[PATCH] Use libnl and dhclient in nash for network configuration.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch modifies nash to use dhclient when the 'network'
command requests dhcp.  Instead of firing off dhclient-script,
nash reads the leases file directly and extracts the information.
The nashSetupInterface() function is then called (which is also
the case for manual network configuration) to configure the
interface.  nashSetupInterface() uses libnl to add the address
and set the default gateway.

Added nashSetMTU() to set the interface MTU using libnl.

Modified writeResolvConf() to not use pumpNetIntf.
---
 nash/Makefile  |    4 +-
 nash/network.c |  429 +++++++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 335 insertions(+), 98 deletions(-)

diff --git a/nash/Makefile b/nash/Makefile
index 1d03c3f..f1cceb7 100644
--- a/nash/Makefile
+++ b/nash/Makefile
@@ -32,14 +32,14 @@ libnash_OBJECTS = lib.o hotplug.o wrap.o list.o dict.o block.o uevent.o dm.o \
 include ../Makefile.inc
 
 CFLAGS += -I$(TOPDIR)/nash/include -I$(TOPDIR)/bdevid/include
-CFLAGS += $(shell pkg-config --cflags libdhcp)
+CFLAGS += $(shell pkg-config --cflags libnl-1)
 
 LDFLAGS += -Wl,-rpath-link,$(TOPDIR)/bdevid:$(TOPDIR)/nash
 
 nash_LIBPATHS = -L$(TOPDIR)/nash -L$(TOPDIR)/bdevid -L/$(LIB)
 nash_LIBS += -ldevmapper -lparted -lblkid -lselinux -lsepol
 nash_LIBS += -luuid -lpopt -lresolv -ldl -lelf
-nash_LIBS += $(shell pkg-config --libs libdhcp) -lm
+nash_LIBS += $(shell pkg-config --libs libnl-1) -lm
 # We need to link against libgcc_s directly, or it'll /dlopen() it during
 # backtrace()!  This is not teh way!!!1!!!one!!!.
 nash_LIBS += -lgcc_s
diff --git a/nash/network.c b/nash/network.c
index 5d017c3..09a1cbf 100644
--- a/nash/network.c
+++ b/nash/network.c
@@ -2,10 +2,8 @@
  * nash-network.c
  *
  * Simple network bring-up code for nash.
- * It currently uses libpump and looks a lot like the network code in
- * anaconda
  *
- * Copyright 2006-2008 Red Hat, Inc.  All rights reserved.
+ * Copyright 2006, 2007, 2008  Red Hat, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +19,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  * Author(s): Jeremy Katz <katzj@xxxxxxxxxx>
+ *            David Cantrell <dcantrell@xxxxxxxxxx>
  */
 
 #include <errno.h>
@@ -29,13 +28,28 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
 
 #include <popt.h>
-#include <pump.h>
+
+#include <netlink/netlink.h>
+#include <netlink/socket.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/route.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/link.h>
 
 #include <nash.h>
 #include "util.h"
 
+#define DHCLIENT       "/sbin/dhclient"
+#define RESOLVCONF     "/etc/resolv.conf"
+#define DHCLIENTLEASES "/var/lib/dhclient/dhclient.leases"
+#define BUFSIZ         4096
+
 /* returns 1 for link, 0 for no link, -1 for unknown */
 int get_link_status(char *ifname);
 
@@ -60,29 +74,55 @@ static int waitForLink(char * dev) {
     return 1;
 }
 
-static void writeResolvConf(struct pumpNetIntf intf) {
-    char * filename = "/etc/resolv.conf";
+static void writeResolvConf(char *dns, char *nameserver, char *domain) {
     FILE * f;
-    char buf[INET6_ADDRSTRLEN+1];
-    ip_addr_t *addr;
-    int i;
+    char * tmpdns = NULL, * c = NULL;
+    struct in_addr addr;
+    struct in6_addr addr6;
 
-    if (!(intf.set & PUMP_NETINFO_HAS_DOMAIN) && !intf.numDns)
+    if (dns == NULL && nameserver == NULL && domain == NULL)
         return;
 
-    f = fopen(filename, "w");
+    f = fopen(RESOLVCONF, "w");
     if (!f) {
-        eprintf("Cannot create %s: %s\n", filename, strerror(errno));
+        eprintf("Cannot create %s: %s\n", RESOLVCONF, strerror(errno));
         return;
     }
 
-    if (intf.set & PUMP_NETINFO_HAS_DOMAIN)
-        fprintf(f, "search %s\n", intf.domain);
+    if (domain != NULL)
+        fprintf(f, "search %s\n", domain);
+
+    if (nameserver != NULL) {
+        if (inet_pton(AF_INET, nameserver, &addr) > 0 ||
+            inet_pton(AF_INET6, nameserver, &addr6) > 0) {
+            fprintf(f, "nameserver %s\n", nameserver);
+        }
+    }
 
-    for (i = 0; i < intf.numDns; i++) {
-        addr = &(intf.dnsServers[i]);
-        inet_ntop(addr->sa_family, IP_ADDR(addr), buf, INET6_ADDRSTRLEN);
-        fprintf(f, "nameserver %s\n", buf);
+    /* dns is a command delimited list of name IP addresses
+     * can be IPv4 or IPv6 addresses, just make sure they
+     * satisfy input to inet_pton()
+     */
+    if (dns && *dns) {
+        if ((tmpdns = strdup(dns)) == NULL) {
+            eprintf("Failure reading dns servers: %s\n", strerror(errno));
+            fclose(f);
+            return;
+        }
+
+        c = strtok(tmpdns, ",");
+        while (c != NULL) {
+            memset(&addr, 0, sizeof(addr));
+            memset(&addr6, 0, sizeof(addr6));
+
+            if (inet_pton(AF_INET, c, &addr) > 0 ||
+                inet_pton(AF_INET6, c, &addr) > 0)
+                fprintf(f, "nameserver %s\n", c);
+            else
+                eprintf("Invalid nameserver address %s: %s\n", c, strerror(errno));
+
+            c = strtok(NULL, ",");
+        }
     }
 
     fclose(f);
@@ -92,45 +132,201 @@ static void writeResolvConf(struct pumpNetIntf intf) {
     return;
 }
 
-static void nashNetLogger(void * arg, int priority, char * fmt, va_list va) {
-    nash_log_level loglevel = NASH_NOTICE;
+static int nashSetMTU(char * dev, int mtu) {
+    struct nl_handle *handle = NULL;
+    struct nl_cache *cache = NULL;
+    struct rtnl_link *link = NULL;
+    struct rtnl_link *request = NULL;
 
-    if (priority <= LOG_ERR)
-        loglevel = NASH_ERROR;
-    else if (priority <= LOG_WARNING)
-        loglevel = NASH_WARNING;
+    if (dev == NULL || mtu <= 0)
+        return 1;
 
-    nashLoggerV(_nash_context, loglevel, fmt, va);
-    nashLogger(_nash_context, loglevel, "\n");
-}
+    if ((handle = nl_handle_alloc()) == NULL)
+        return 1;
 
-static inline int nashPton(char * addr_str, ip_addr_t *ret) {
-    struct in_addr addr;
-    struct in6_addr addr6;
+    if (nl_connect(handle, NETLINK_ROUTE)) {
+        nl_handle_destroy(handle);
+        return 1;
+    }
 
-    if (!addr_str)
-        return 0;
+    if ((cache = rtnl_link_alloc_cache(handle)) == NULL) {
+        nl_close(handle);
+        nl_handle_destroy(handle);
+        return 1;
+    }
 
-    if (inet_pton(AF_INET, addr_str, &addr)) {
-	*ret = ip_addr_in(&addr);
-	return 1;
-    } else if (inet_pton(AF_INET6, addr_str, &addr6)) {
-	*ret = ip_addr_in6(&addr6);
-	return 1;
+    if ((link = rtnl_link_get_by_name(cache, dev)) == NULL) {
+        nl_close(handle);
+        nl_handle_destroy(handle);
+        return 1;
+    }
+
+    request = rtnl_link_alloc();
+    rtnl_link_set_mtu(request, mtu);
+
+    if (rtnl_link_change(handle, link, request, 0)) {
+        rtnl_link_put(link);
+        nl_close(handle);
+        nl_handle_destroy(handle);
+        return 1;
     }
 
     return 0;
 }
 
+static char *dhclientLeaseLineParse(char *linebuf, char *key) {
+    char *b = NULL, *e = NULL;
+
+    if ((b = index(strstr(linebuf, key), ' ')) == NULL)
+        return NULL;
+    b++;
+
+    while (*b == '"')
+        b++;
+
+    if ((e = rindex(b, ';')) == NULL)
+        return NULL;
+    *e = '\0';
+    e--;
+
+    while (*e == '"') {
+        *e = '\0';
+        e--;
+    }
+
+    return b;
+}
+
+static void nashSetHostname(char *hostname, char *domain) {
+    int hfailed = 0;
+    char *tmp = NULL;
+
+    if (hostname == NULL)
+        return;
+
+    if (domain != NULL) {
+        if (asprintf(&tmp, "%s.%s", hostname, domain) != -1) {
+            if (sethostname(tmp, strlen(tmp)) == -1) {
+                hfailed = 1;
+            }
+
+            free(tmp);
+        }
+    } else {
+        if (sethostname(hostname, strlen(hostname)) == -1) {
+            return;
+        }
+    }
+
+    if (hfailed == 1) {
+        if (sethostname(hostname, strlen(hostname)) == -1) {
+            return;
+        }
+    }
+
+    return;
+}
+
+static char *nashSetupInterface(char *dev, char *ip, char *netmask, char *broadcast, char *gateway) {
+    int ifindex = 0;
+    int prefix = -1;
+    struct nl_handle *handle = NULL;
+    struct nl_cache *cache = NULL;
+    struct rtnl_addr *addr = NULL;
+    struct rtnl_route *route = NULL;
+    struct nl_addr *nl_ip = NULL;
+    struct nl_addr *nl_bc = NULL;
+    struct nl_addr *nl_gw = NULL;
+    struct in_addr mask;
+
+    if (dev == NULL || ip == NULL)
+        return "dev and ip are empty";
+
+    if ((handle = nl_handle_alloc()) == NULL)
+        return "failed to get a Netlink handle";
+
+    if (nl_connect(handle, NETLINK_ROUTE)) {
+        nl_handle_destroy(handle);
+        return "failed to connect to NETLINK_ROUTE";
+    }
+
+    if ((cache = rtnl_link_alloc_cache(handle)) == NULL) {
+        nl_close(handle);
+        nl_handle_destroy(handle);
+        return "failed to get a link cache";
+    }
+
+    /* configure the IP address, netmask, broadcast */
+    addr = rtnl_addr_alloc();
+    ifindex = rtnl_link_name2i(cache, dev);
+    rtnl_addr_set_ifindex(addr, ifindex);
+
+    nl_ip = nl_addr_parse(ip, AF_UNSPEC);
+    rtnl_addr_set_local(addr, nl_ip);
+
+    if (broadcast != NULL) {
+        nl_bc = nl_addr_parse(broadcast, AF_UNSPEC);
+        rtnl_addr_set_broadcast(addr, nl_bc);
+    }
+
+    if (netmask != NULL) {
+        if (inet_pton(AF_INET, netmask, &addr) > 0) {
+            while (mask.s_addr != 0) {
+                mask.s_addr = mask.s_addr >> 1;
+                prefix++;
+            }
+        }
+
+        if (prefix != -1) {
+            rtnl_addr_set_prefixlen(addr, prefix);
+        }
+    }
+
+    rtnl_addr_add(handle, addr, 0);
+    rtnl_addr_put(addr);
+
+    if (nl_ip != NULL) {
+        nl_addr_destroy(nl_ip);
+    }
+
+    if (nl_bc != NULL) {
+        nl_addr_destroy(nl_bc);
+    }
+
+    /* configure the gateway */
+    if (gateway != NULL) {
+        route = rtnl_route_alloc();
+        nl_gw = nl_addr_parse(gateway, AF_UNSPEC);
+        rtnl_route_set_gateway(route, nl_gw);
+
+        rtnl_route_add(handle, route, 0);
+        rtnl_route_put(route);
+
+        if (nl_gw != NULL) {
+            nl_addr_destroy(nl_gw);
+        }
+    }
+
+    /* success */
+    nl_close(handle);
+    nl_handle_destroy(handle);
+    return NULL;
+}
+
 int nashNetworkCommand(char * cmd) {
     int argc;
     char ** argv;
     char * bootProto = NULL, * dev = NULL, * dhcpclass = NULL, * ethtool = NULL, * hostname = NULL;
     char * gateway = NULL, * ip = NULL, * nameserver = NULL, * netmask = NULL, * dns = NULL, * domain = NULL;
-    int mtu = 0, rc;
+    char * broadcast = NULL;
+    int mtu = 0, rc, status;
     char * err = NULL;
-    struct pumpNetIntf intf;
-    struct in_addr addr;
+    char * val = NULL;
+    pid_t pid;
+    FILE *f = NULL;
+    int in_lease = 0;
+    int in_interface = 0;
+    char linebuf[BUFSIZ];
     poptContext optCon;
     struct poptOption netOptions[] = {
         { "bootproto", '\0', POPT_ARG_STRING, &bootProto, 0, NULL, NULL },
@@ -147,14 +343,14 @@ int nashNetworkCommand(char * cmd) {
         { "hostname", '\0', POPT_ARG_STRING, &hostname, 0, NULL, NULL },
         { NULL, 0, 0, NULL, 0, NULL, NULL }
     };
-    
+
     if (poptParseArgvString(cmd, &argc, (const char ***) &argv) || !argc) {
         eprintf("ERROR: Invalid options to network command\n");
         return 1;
     }
-    
-    optCon = poptGetContext(NULL, argc, (const char **) argv, 
-                            netOptions, 0);    
+
+    optCon = poptGetContext(NULL, argc, (const char **) argv,
+                            netOptions, 0);
 
     while ((rc = poptGetNextOpt(optCon)) > 0) {}
     if (rc < -1) {
@@ -167,73 +363,114 @@ int nashNetworkCommand(char * cmd) {
         nashLogger(_nash_context, NASH_WARNING, "WARNING: ethtool options not currently handled\n");
     }
 
-    memset(&intf,'\0', sizeof(intf));
+    if (dev == NULL)
+        dev = strdup("eth0");
 
     if (mtu) {
-        intf.mtu = mtu;
-        intf.set |= PUMP_INTFINFO_HAS_MTU;
-    }
-
-    if (hostname != NULL) {
-        intf.hostname = hostname;
-        intf.set |= PUMP_NETINFO_HAS_HOSTNAME;
-    }
-    
-    if (domain != NULL) {
-        intf.domain = domain;
-        intf.set |= PUMP_NETINFO_HAS_DOMAIN;
-    }
-
-    if (dns) {
-        char *c, *buf = strdup(dns);
-        
-        c = strtok(buf, ",");
-        while ((intf.numDns < MAXNS) && (c != NULL)) {
-            if (nashPton(c, &intf.dnsServers[intf.numDns]))
-                intf.numDns++;
-            c = strtok(NULL, ",");
+        if (nashSetMTU(dev, mtu)) {
+            nashLogger(_nash_context, NASH_WARNING, "WARNING: unable to set MTU on %s to %d\n", dev, mtu);
         }
-        if (intf.numDns)
-            intf.set |= PUMP_NETINFO_HAS_DNS;
     }
 
-    if (dev == NULL)
-        dev = strdup("eth0");
+    if (hostname)
+        nashSetHostname(hostname, domain);
 
-    strncpy(intf.device, dev, 9);
+    if (dns || nameserver)
+        writeResolvConf(dns, nameserver, domain);
 
     if ((bootProto != NULL) && (!strncmp(bootProto, "dhcp", 4))) {
-        waitForLink(dev);
         nashLogger(_nash_context, NASH_NOTICE, "Sending request for IP information through %s\n", dev);
-        pumpDhcpClassRun(&intf, NULL,
-                         dhcpclass ? dhcpclass : "nash",
-                         DHCPv6_DISABLE, DHCP_USE_LEASE_DATABASE,
-                         45, nashNetLogger, LOG_INFO);
-    } else { /* static IP.  hope enough is specified! */
-        if (nashPton(ip, &intf.ip))
-            intf.set |= PUMP_INTFINFO_HAS_IP;
-        if (nashPton(netmask, &intf.netmask))
-            intf.set |= PUMP_INTFINFO_HAS_NETMASK;
-        if (nashPton(gateway, &intf.gateway))
-            intf.set |= PUMP_NETINFO_HAS_GATEWAY;
-
-        /* FIXME: what about IPv6 ? */
-        if (intf.set & PUMP_INTFINFO_HAS_NETMASK &&
-            intf.netmask.sa_family == AF_INET) {
-            addr.s_addr = IP_ADDR_IN(&intf.ip)->s_addr & IP_ADDR_IN(&intf.netmask)->s_addr;
-            intf.network = ip_addr_in(&addr);
-            addr.s_addr = IP_ADDR_IN(&intf.network)->s_addr | ~IP_ADDR_IN(&intf.netmask)->s_addr;
-            intf.broadcast = ip_addr_in(&addr);
-            intf.set |= PUMP_INTFINFO_HAS_NETWORK | PUMP_INTFINFO_HAS_BROADCAST;
+
+        waitForLink(dev);
+        pid = fork();
+
+        /* start dhclient */
+        if (pid == 0) {
+            /* dhclient returns code 2 for failure, do the same */
+            if (setpgrp() == -1)
+                exit(2);
+
+            if (execl(DHCLIENT, DHCLIENT, "-4", "-1", "-n", NULL) == -1)
+                exit(2);
+        } else if (pid == -1) {
+            eprintf("Failed to start dhclient.\n");
+            return 1;
+        } else {
+            if (waitpid(pid, &status, 0) == -1) {
+                return 1;
+            }
+        }
+
+        /* collect lease information */
+        if ((f = fopen(DHCLIENTLEASES, "r")) == NULL)
+            return 1;
+
+        while (fgets(linebuf, BUFSIZ, f) != NULL) {
+            linebuf[strlen(linebuf) - 1] = '\0';
+
+            if (in_lease == 0) {
+                if (strstr(linebuf, "lease {") != NULL) {
+                    in_lease = 1;
+                }
+            } else if (in_lease == 1) {
+                if (strstr(linebuf, "interface") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "interface");
+
+                    if (!strcmp(dev, val)) {
+                        in_lease = 2;
+                        in_interface = 1;
+                    }
+                }
+            } else if (in_interface == 1) {
+                if (strstr(linebuf, "fixed-address") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "fixed-address");
+                    ip = strdup(val);
+                } else if (strstr(linebuf, "subnet-mask") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "subnet-mask");
+                    netmask = strdup(val);
+                } else if (strstr(linebuf, "domain-name-servers") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "domain-name-servers");
+                    dns = strdup(val);
+                    val = dns;
+
+                    while (*val != '\0') {
+                        if (*val == ' ')
+                            *val = ',';
+                        val++;
+                    }
+                } else if (strstr(linebuf, "broadcast-address") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "broadcast-address");
+                    broadcast = strdup(val);
+                } else if (strstr(linebuf, "routers") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "routers");
+                    gateway = strdup(val);
+                } else if (strstr(linebuf, "host-name") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "host-name");
+                    hostname = strdup(val);
+                } else if (strstr(linebuf, "domain-name") != NULL) {
+                    val = dhclientLeaseLineParse(linebuf, "domain-name");
+                    domain = strdup(val);
+                }
+
+                if (strstr(linebuf, "}") != NULL) {
+                    in_lease = 0;
+                    in_interface = 0;
+                }
+            }
         }
+
+        fclose(f);
+
+        if (hostname)
+            nashSetHostname(hostname, domain);
     }
 
-    err =  pumpSetupInterface(&intf);
+    err = nashSetupInterface(dev, ip, netmask, broadcast, gateway);
     if (err) {
         nashLogger(_nash_context, NASH_ERROR, "ERROR: Interface setup failed: %s\n", err);
         return 1;
     }
-    writeResolvConf(intf);
+    writeResolvConf(dns, nameserver, domain);
 
     sleep(2);
     return 0;
-- 
1.6.0.3

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list

[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux