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