diff options
author | David Cantrell <dcantrell@redhat.com> | 2008-08-25 17:13:37 -1000 |
---|---|---|
committer | David Cantrell <dcantrell@redhat.com> | 2008-08-25 17:13:37 -1000 |
commit | 80713e3f73e48856221c667f32b94b0a023ebecc (patch) | |
tree | aff4d9170fc24d2f1acc238a2d8908159a71d3dd /loader/net.c | |
parent | ef5fbf7bc72572f3a6326b12f9187a5438e58e4c (diff) | |
download | anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.tar.gz anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.tar.xz anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.zip |
Renamed loader2 subdirectory to loader (hooray for git)
Diffstat (limited to 'loader/net.c')
-rw-r--r-- | loader/net.c | 2090 |
1 files changed, 2090 insertions, 0 deletions
diff --git a/loader/net.c b/loader/net.c new file mode 100644 index 000000000..0acbe83c9 --- /dev/null +++ b/loader/net.c @@ -0,0 +1,2090 @@ +/* + * net.c + * + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + * 2006, 2007, 2008 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author(s): David Cantrell <dcantrell@redhat.com> + */ + +#include <netdb.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/utsname.h> +#include <arpa/inet.h> +#include <errno.h> +#include <popt.h> +#include <resolv.h> +#include <net/if.h> +#include <newt.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <dbus/dbus.h> +#include <NetworkManager.h> + +#include "../isys/isys.h" +#include "../isys/ethtool.h" +#include "../isys/iface.h" +#include "../isys/str.h" + +#include "lang.h" +#include "loader.h" +#include "loadermisc.h" +#include "log.h" +#include "method.h" +#include "net.h" +#include "windows.h" + +/* boot flags */ +extern uint64_t flags; + +/** + * Callback function for the CIDR entry boxes on the manual TCP/IP + * configuration window. + * + * @param co The entry field that triggered the callback. + * @param dptr Pointer to intfconfig_s data structure for this field. + * @see intfconfig_s + */ +static void cidrCallback(newtComponent co, void * dptr) { + struct intfconfig_s * data = dptr; + int cidr, upper = 0; + struct in_addr addr; + + if (co == data->cidr4Entry) { + if (data->cidr4 == NULL && data->ipv4 == NULL) + return; + + if (inet_pton(AF_INET, data->cidr4, &addr) >= 1) + return; + + errno = 0; + cidr = strtol(data->cidr4, NULL, 10); + if ((errno == ERANGE && (cidr == LONG_MIN || cidr == LONG_MAX)) || + (errno != 0 && cidr == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (strcmp(data->ipv4, "")) + upper = 32; + } else if (co == data->cidr6Entry) { + if (data->cidr6 == NULL && data->ipv6 == NULL) + return; + + errno = 0; + cidr = strtol(data->cidr6, NULL, 10); + if ((errno == ERANGE && (cidr == LONG_MIN || cidr == LONG_MAX)) || + (errno != 0 && cidr == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (strcmp(data->ipv6, "")) + upper = 128; + } + + if (upper != 0) { + if (cidr < 1 || cidr > upper) { + newtWinMessage(_("Invalid Prefix"), _("Retry"), + _("Prefix must be between 1 and 32 " + "for IPv4 networks or between 1 and 128 " + "for IPv6 networks")); + } + } +} + +static void ipCallback(newtComponent co, void * dptr) { + int i; + char *buf, *octet; + struct intfconfig_s * data = dptr; + + if (co == data->ipv4Entry) { + /* do we need to guess a netmask for the user? */ + if (data->cidr4 == NULL && data->ipv4 != NULL) { + buf = strdup(data->ipv4); + octet = strtok(buf, "."); + errno = 0; + i = strtol(octet, NULL, 10); + + if ((errno == ERANGE && (i == LONG_MIN || i == LONG_MAX)) || + (errno != 0 && i == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + free(buf); + free(octet); + + if (i >= 0 && i <= 127) + newtEntrySet(data->cidr4Entry, "8", 1); + else if (i >= 128 && i <= 191) + newtEntrySet(data->cidr4Entry, "16", 1); + else if (i >= 192 && i <= 222) + newtEntrySet(data->cidr4Entry, "24", 1); + } + + return; + } else if (co == data->ipv6Entry) { + /* users must provide a mask, we can't guess for ipv6 */ + return; + } +} + +static void setMethodSensitivity(void *dptr, int radio_button_count) { + int i = 0; + + for (i = 0; i < radio_button_count; i++) { + newtCheckboxSetFlags(*((newtComponent *) dptr), NEWT_FLAG_DISABLED, + NEWT_FLAGS_TOGGLE); + dptr += sizeof (newtComponent); + } + + return; +} + +static void v4MethodCallback(newtComponent co, void *dptr) { + setMethodSensitivity(dptr, 2); + return; +} + +static void v6MethodCallback(newtComponent co, void *dptr) { + setMethodSensitivity(dptr, 3); + return; +} + +static void parseEthtoolSettings(struct loaderData_s * loaderData) { + char * option, * buf; + ethtool_duplex duplex = ETHTOOL_DUPLEX_UNSPEC; + ethtool_speed speed = ETHTOOL_SPEED_UNSPEC; + + buf = strdup(loaderData->ethtool); + option = strtok(buf, " "); + while (option) { + if (option[strlen(option) - 1] == '\"') + option[strlen(option) - 1] = '\0'; + if (option[0] == '\"') + option++; + if (!strncmp(option, "duplex", 6)) { + if (!strncmp(option + 7, "full", 4)) + duplex = ETHTOOL_DUPLEX_FULL; + else if (!strncmp(option + 7, "half", 4)) + duplex = ETHTOOL_DUPLEX_HALF; + else + logMessage(WARNING, "Unknown duplex setting: %s", option + 7); + option = strtok(NULL, " "); + } else if (!strncmp("speed", option, 5)) { + if (!strncmp(option + 6, "1000", 4)) + speed = ETHTOOL_SPEED_1000; + else if (!strncmp(option + 6, "100", 3)) + speed = ETHTOOL_SPEED_100; + else if (!strncmp(option + 6, "10", 2)) + speed = ETHTOOL_SPEED_10; + else + logMessage(WARNING, "Unknown speed setting: %s", option + 6); + option = strtok(NULL, " "); + } else { + logMessage(WARNING, "Unknown ethtool setting: %s", option); + } + option = strtok(NULL, " "); + } + setEthtoolSettings(loaderData->netDev, speed, duplex); + free(buf); +} + +/* XXX: make this get DNS servers via NM +static int getDnsServers(iface_t * iface) { + int rc; + struct in_addr addr; + struct in6_addr addr6; + char * ns = ""; + struct newtWinEntry entry[] = { { N_("Nameserver IP"), &ns, 0 }, + { NULL, NULL, 0 } }; + + do { + rc = newtWinEntries(_("Missing Nameserver"), + _("Your IP address request returned configuration " + "information, but it did not include a nameserver address. " + "If you do not have this information, you can leave " + "the field blank and the install will continue."), + 61, 0, 0, 45, entry, _("OK"), _("Back"), NULL); + + if (rc == 2) return LOADER_BACK; + + rc = 0; + if (!ns || !*ns) { + iface->numdns = 0; + break; + } else { + if ((inet_pton(AF_INET, ns, &addr) >= 1) || + (inet_pton(AF_INET6, ns, &addr6) >= 1)) { + iface->dns[0] = strdup(ns); + } else { + rc = 2; + } + } + + if (rc) { + newtWinMessage(_("Invalid IP Information"), _("Retry"), + _("You entered an invalid IP address.")); + } else { + iface->numdns = 1; + } + } while (rc == 2); + + return LOADER_OK; +} +*/ + +void printLoaderDataIPINFO(struct loaderData_s *loaderData) { + logMessage(DEBUGLVL, "loaderData->ipinfo_set = |%d|", loaderData->ipinfo_set); + logMessage(DEBUGLVL, "loaderData->ipv4 = |%s|", loaderData->ipv4); + logMessage(DEBUGLVL, "loaderData->ipv6info_set = |%d|", loaderData->ipv6info_set); + logMessage(DEBUGLVL, "loaderData->ipv6 = |%s|", loaderData->ipv6); + logMessage(DEBUGLVL, "loaderData->dhcpTimeout = |%d|", loaderData->dhcpTimeout); + logMessage(DEBUGLVL, "loaderData->netmask = |%s|", loaderData->netmask); + logMessage(DEBUGLVL, "loaderData->gateway = |%s|", loaderData->gateway); + logMessage(DEBUGLVL, "loaderData->dns = |%s|", loaderData->dns); + logMessage(DEBUGLVL, "loaderData->hostname = |%s|", loaderData->hostname); + logMessage(DEBUGLVL, "loaderData->noDns = |%d|", loaderData->noDns); + logMessage(DEBUGLVL, "loaderData->netDev_set = |%d|", loaderData->netDev_set); + logMessage(DEBUGLVL, "loaderData->netDev = |%s|", loaderData->netDev); + logMessage(DEBUGLVL, "loaderData->netCls_set = |%d|", loaderData->netCls_set); + logMessage(DEBUGLVL, "loaderData->netCls = |%s|", loaderData->netCls); +} + +/* given loader data from kickstart, populate network configuration struct */ +void setupNetworkDeviceConfig(iface_t * iface, + struct loaderData_s * loaderData) { + int err; + struct in_addr addr; + struct in6_addr addr6; + char * c; + + /* set to 1 to get ks network struct logged */ +#if 0 + printLoaderDataIPINFO(loaderData); +#endif + + if (loaderData->ethtool) { + parseEthtoolSettings(loaderData); + } + + if (loaderData->netCls_set) { + iface->vendorclass = loaderData->netCls; + } else { + iface->vendorclass = NULL; + } + + if (loaderData->ipinfo_set) { + /* this is how we specify dhcp */ + if (!strncmp(loaderData->ipv4, "dhcp", 4)) { + int ret = 0; + + /* JKFIXME: this soooo doesn't belong here. and it needs to + * be broken out into a function too */ + logMessage(INFO, "sending dhcp request through device %s", + loaderData->netDev); + + if (!FL_TESTING(flags)) { + if (loaderData->noDns) { + iface->flags |= IFACE_FLAGS_NO_WRITE_RESOLV_CONF; + } + + iface->dhcptimeout = loaderData->dhcpTimeout; + + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, + "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return; + } + + ret = get_connection(iface); + newtPopWindow(); + } + + if (ret) { + logMessage(ERROR, "failed to start NetworkManager (%d)", ret); + return; + } + + iface->flags |= IFACE_FLAGS_IS_DYNAMIC | IFACE_FLAGS_IS_PRESET; + } else if (loaderData->ipv4) { + if (inet_pton(AF_INET, loaderData->ipv4, &addr) >= 1) { + iface->ipaddr = addr; + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + iface->flags |= IFACE_FLAGS_IS_PRESET; + } + } else if (loaderData->ipv6) { + if (inet_pton(AF_INET6, loaderData->ipv6, &addr6) >= 1) { + memcpy(&iface->ip6addr, &addr6, sizeof(struct in6_addr)); + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + iface->flags |= IFACE_FLAGS_IS_PRESET; + } + } else { /* invalid ip information, disable the setting of ip info */ + loaderData->ipinfo_set = 0; + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + loaderData->ipv4 = NULL; + loaderData->ipv6 = NULL; + } + } + + if (loaderData->netmask) { + if (inet_pton(AF_INET, loaderData->netmask, &iface->netmask) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (loaderData->gateway) { + if (inet_pton(AF_INET, loaderData->gateway, &iface->gateway) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + /* FIXME: add support for loaderData->gateway6 */ + + if (loaderData->dns) { + char * buf; + char ret[INET6_ADDRSTRLEN+1]; + buf = strdup(loaderData->dns); + + /* Scan the dns parameter for multiple comma-separated IP addresses */ + c = strtok(buf, ","); + while ((iface->numdns < MAXNS) && (c != NULL)) { + if (inet_pton(AF_INET, c, &addr) >= 1) { + iface->dns[iface->numdns] = strdup(c); + iface->numdns++; + + if (inet_ntop(AF_INET, &addr, ret, INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, strerror(errno)); + } else { + logMessage(DEBUGLVL, "adding dns4 %s", ret); + c = strtok(NULL, ","); + } + } else if (inet_pton(AF_INET6, c, &addr6) >= 1) { + iface->dns[iface->numdns] = strdup(c); + iface->numdns++; + + if (inet_ntop(AF_INET6, &addr6, ret, INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, strerror(errno)); + } else { + logMessage(DEBUGLVL, "adding dns6 %s", ret); + c = strtok(NULL, ","); + } + } + } + + logMessage(INFO, "dnsservers is %s", loaderData->dns); + } + + if (loaderData->hostname) { + logMessage(INFO, "setting specified hostname of %s", + loaderData->hostname); + iface->hostname = strdup(loaderData->hostname); + } + + if (loaderData->mtu) { + iface->mtu = loaderData->mtu; + } + + if (loaderData->peerid) { + iface->peerid = strdup(loaderData->peerid); + } + + if (loaderData->subchannels) { + iface->subchannels = strdup(loaderData->subchannels); + } + + if (loaderData->ctcprot) { + iface->ctcprot = strdup(loaderData->ctcprot); + } + + if (loaderData->portname) { + iface->portname = strdup(loaderData->portname); + } + + if (loaderData->nettype) { + iface->nettype = strdup(loaderData->nettype); + } + + if (loaderData->ethtool) { + parseEthtoolSettings(loaderData); + } + + if (loaderData->noDns) { + iface->flags |= IFACE_FLAGS_NO_WRITE_RESOLV_CONF; + } + + iface->dhcptimeout = loaderData->dhcpTimeout; +} + +int readNetConfig(char * device, iface_t * iface, + char * dhcpclass, int methodNum) { + int err; + int ret; + int i = 0; + struct netconfopts opts; + struct in_addr addr; + struct intfconfig_s ipcomps; + + /* ipcomps contains the user interface components */ + ipcomps.ipv4 = NULL; + ipcomps.ipv6 = NULL; + ipcomps.cidr4 = NULL; + ipcomps.cidr6 = NULL; + ipcomps.gw = NULL; + ipcomps.gw6 = NULL; + ipcomps.ns = NULL; + + /* init opts */ + opts.ipv4Choice = 0; + opts.ipv6Choice = 0; + + /* JKFIXME: we really need a way to override this and be able to change + * our network config */ + if (!FL_TESTING(flags) && IFACE_IS_PRESET(iface->flags)) { + logMessage(INFO, "doing kickstart... setting it up"); + + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return LOADER_BACK; + } + + i = get_connection(iface); + newtPopWindow(); + + if (i > 0) { + newtWinMessage(_("Network Error"), _("Retry"), + _("There was an error configuring your network " + "interface.")); + return LOADER_BACK; + } + + return LOADER_NOOP; + } + + /* dhcp/manual network configuration loop */ + i = 1; + while (i == 1) { + ret = configureTCPIP(device, iface, &opts, methodNum); + + if (ret == LOADER_NOOP) { + /* dhcp selected, proceed */ + i = 0; + } else if (ret == LOADER_OK) { + /* do manual configuration */ + ret = manualNetConfig(device, iface, &ipcomps, &opts); + + if (ret == LOADER_BACK) { + continue; + } else if (ret == LOADER_OK) { + i = 0; + } + } else if (ret == LOADER_BACK) { + return LOADER_BACK; + } + } + +/* + if (ipcomps.gw && *ipcomps.gw) { + if (inet_pton(AF_INET, ipcomps.gw, &iface->gateway) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else if (inet_pton(AF_INET6, ipcomps.gw, &iface->gateway6) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } +*/ + + /* calculate any missing IPv4 pieces */ + if (opts.ipv4Choice == '*') { + memset(&addr, 0, sizeof(addr)); + addr.s_addr = (iface->ipaddr.s_addr) & (iface->netmask.s_addr); + + if (iface->broadcast.s_addr == 0) { + iface->broadcast.s_addr = addr.s_addr | ~(iface->netmask.s_addr); + } + } + + /* dump some network debugging info */ + debugNetworkInfo(iface); + + /* bring up the interface */ + if (!FL_TESTING(flags)) { + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return LOADER_BACK; + } + + i = get_connection(iface); + newtPopWindow(); + + if (i > 0) { + newtWinMessage(_("Network Error"), _("Retry"), + _("There was an error configuring your network " + "interface.")); + return LOADER_BACK; + } + } + + return LOADER_OK; +} + +int configureTCPIP(char * device, iface_t * iface, + struct netconfopts * opts, int methodNum) { + int i = 0, z = 0, skipForm = 0, dret = 0, err; + newtComponent f, okay, back, answer; + newtComponent ipv4Checkbox, ipv6Checkbox, v4Method[2], v6Method[3]; + newtGrid grid, checkgrid, buttons; + + /* UI WINDOW 1: ask for ipv4 choice, ipv6 choice, and conf methods */ + + /* IPv4 checkbox */ + if (!opts->ipv4Choice) { + if (FL_NOIPV4(flags) && !FL_IP_PARAM(flags)) + opts->ipv4Choice = ' '; + else + opts->ipv4Choice = '*'; + } + + ipv4Checkbox = newtCheckbox(-1, -1, _("Enable IPv4 support"), + opts->ipv4Choice, NULL, &(opts->ipv4Choice)); + v4Method[0] = newtRadiobutton(-1, -1, DHCP_METHOD_STR, 1, NULL); + v4Method[1] = newtRadiobutton(-1, -1, MANUAL_METHOD_STR, 0, v4Method[0]); + + /* IPv6 checkbox */ + if (!opts->ipv6Choice) { + if (FL_NOIPV6(flags) && !FL_IPV6_PARAM(flags)) + opts->ipv6Choice = ' '; + else + opts->ipv6Choice = '*'; + } + + ipv6Checkbox = newtCheckbox(-1, -1, _("Enable IPv6 support"), + opts->ipv6Choice, NULL, &(opts->ipv6Choice)); + v6Method[0] = newtRadiobutton(-1, -1, AUTO_METHOD_STR, 1, NULL); + v6Method[1] = newtRadiobutton(-1, -1, DHCPV6_METHOD_STR, 0, v6Method[0]); + v6Method[2] = newtRadiobutton(-1, -1, MANUAL_METHOD_STR, 0, v6Method[1]); + + /* button bar at the bottom of the window */ + buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL); + + /* checkgrid contains the toggle options for net configuration */ + checkgrid = newtCreateGrid(1, 8); + + newtGridSetField(checkgrid, 0, 0, NEWT_GRID_COMPONENT, ipv4Checkbox, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + for (i = 1; i < 3; i++) + newtGridSetField(checkgrid, 0, i, NEWT_GRID_COMPONENT, v4Method[i-1], + 7, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtGridSetField(checkgrid, 0, 4, NEWT_GRID_COMPONENT, ipv6Checkbox, + 0, 1, 0, 0, NEWT_ANCHOR_LEFT, 0); + for (i = 5; i < 8; i++) + newtGridSetField(checkgrid, 0, i, NEWT_GRID_COMPONENT, v6Method[i-5], + 7, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + /* main window layout */ + grid = newtCreateGrid(1, 2); + newtGridSetField(grid, 0, 0, NEWT_GRID_SUBGRID, checkgrid, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + f = newtForm(NULL, NULL, 0); + newtGridAddComponentsToForm(grid, f, 1); + newtGridWrappedWindow(grid, _("Configure TCP/IP")); + newtGridFree(grid, 1); + + /* callbacks */ + newtComponentAddCallback(ipv4Checkbox, v4MethodCallback, &v4Method); + newtComponentAddCallback(ipv6Checkbox, v6MethodCallback, &v6Method); + + /* match radio button sensitivity to initial checkbox choices */ + if (opts->ipv4Choice == ' ') + setMethodSensitivity(&v4Method, 2); + + if (opts->ipv6Choice == ' ') + setMethodSensitivity(&v6Method, 3); + + /* If the user provided any of the following boot paramters, skip + * prompting for network configuration information: + * ip=<val> ipv6=<val> + * noipv4 noipv6 + * ip=<val> noipv6 + * ipv6=<val> noipv4 + * we also skip this form for anyone doing a kickstart install + */ + if ((FL_IP_PARAM(flags) && FL_IPV6_PARAM(flags)) || + (FL_IP_PARAM(flags) && FL_NOIPV6(flags)) || + (FL_IPV6_PARAM(flags) && FL_NOIPV4(flags)) || + (FL_NOIPV4(flags) && FL_NOIPV6(flags)) || + (FL_IS_KICKSTART(flags))) { + skipForm = 1; + newtPopWindow(); + } + + /* run the form */ + do { + if (!skipForm) { + answer = newtRunForm(f); + + if (answer == back) { + newtFormDestroy(f); + newtPopWindow(); + return LOADER_BACK; + } + + /* need at least one stack */ + if (opts->ipv4Choice == ' ' && opts->ipv6Choice == ' ') { + newtWinMessage(_("Missing Protocol"), _("Retry"), + _("You must select at least one protocol (IPv4 " + "or IPv6).")); + continue; + } + + /* NFS only works over IPv4 */ + if (opts->ipv4Choice == ' ' && methodNum == METHOD_NFS) { + newtWinMessage(_("IPv4 Needed for NFS"), _("Retry"), + _("NFS installation method requires IPv4 support.")); + continue; + } + } + + /* what TCP/IP stacks do we use? what conf methods? */ + if (opts->ipv4Choice == '*') { + flags &= ~LOADER_FLAGS_NOIPV4; + for (z = IPV4_FIRST_METHOD; z <= IPV4_LAST_METHOD; z++) + if (newtRadioGetCurrent(v4Method[0]) == v4Method[z-1]) + iface->ipv4method = z; + } else { + flags |= LOADER_FLAGS_NOIPV4; + } + + if (opts->ipv6Choice == '*') { + flags &= ~LOADER_FLAGS_NOIPV6; + for (z = IPV6_FIRST_METHOD; z <= IPV6_LAST_METHOD; z++) + if (newtRadioGetCurrent(v6Method[0]) == v6Method[z-1]) + iface->ipv6method = z; + } else { + flags |= LOADER_FLAGS_NOIPV6; + } + + /* do interface configuration (call DHCP here, or return for manual) */ + if ((!FL_NOIPV4(flags) && iface->ipv4method == IPV4_DHCP_METHOD) || + (!FL_NOIPV6(flags) && (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD))) { + /* do DHCP if selected */ + if (!FL_TESTING(flags)) { + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, + "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return LOADER_BACK; + } + + dret = get_connection(iface); + newtPopWindow(); + } + + if (!dret) { + iface->flags |= IFACE_FLAGS_IS_DYNAMIC; + +/* XXX: if we don't have working DNS lookups, ask for a nameserver, + * but be friendly to NM. we should ask NM if it knows about a + * nameserver and then ask the user for one if NM isn't in the know. + */ +/* + if (iface->numdns == 0) { + logMessage(WARNING, + "dhcp worked, but did not return a DNS server"); +*/ + /* + * prompt for a nameserver IP address when: + * - DHCP for IPv4, DHCP/AUTO for IPv6 and both enabled + * - IPv4 disabled and DHCP/AUTO for IPv6 + * - IPv6 disabled and DHCP for IPv4 + */ +/* + if ((iface->ipv4method == IPV4_DHCP_METHOD + && (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD)) + || (iface->ipv4method == IPV4_DHCP_METHOD + && FL_NOIPV6(flags)) + || (FL_NOIPV4(flags) + && (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD))) { + i = getDnsServers(iface); + i = i ? 0 : 1; + } else { + i = 1; + } + } else { + i = 1; + } +*/ + i = 1; + } else { + logMessage(DEBUGLVL, "get_connection() failed, returned %d", dret); + i = 0; + } + } else { + /* manual IP configuration for IPv4 and IPv6 */ + newtFormDestroy(f); + newtPopWindow(); + return LOADER_OK; + } + } while (i != 1); + + newtFormDestroy(f); + newtPopWindow(); + + if ((!FL_NOIPV4(flags) && iface->ipv4method == IPV4_MANUAL_METHOD) || + (!FL_NOIPV6(flags) && iface->ipv6method == IPV6_MANUAL_METHOD)) + return LOADER_OK; + else + return LOADER_NOOP; +} + +int manualNetConfig(char * device, iface_t * iface, + struct intfconfig_s * ipcomps, struct netconfopts * opts) { + int i, rows, pos, prefix, cidr, have[2], stack[2]; + char *buf = NULL; + char ret[48]; + struct in_addr addr; + struct in6_addr addr6; + struct in_addr *tmpaddr = NULL; + newtComponent f, okay, back, answer; + newtGrid egrid = NULL; + newtGrid qgrid = NULL; + newtGrid rgrid = NULL; + newtGrid buttons, grid; + newtComponent text = NULL; + + memset(ret, '\0', INET6_ADDRSTRLEN+1); + + /* so we don't perform this test over and over */ + stack[IPV4] = opts->ipv4Choice == '*' && + iface->ipv4method == IPV4_MANUAL_METHOD; + stack[IPV6] = opts->ipv6Choice == '*' && + iface->ipv6method == IPV6_MANUAL_METHOD; + + /* UI WINDOW 2 (optional): manual IP config for non-DHCP installs */ + rows = 2; + for (i = 0; i < 2; i++) { + if (stack[i]) { + rows++; + } + } + egrid = newtCreateGrid(4, rows); + + pos = 0; + + /* IPv4 entry items */ + if (stack[IPV4]) { + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("IPv4 address:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + ipcomps->ipv4Entry = newtEntry(-1, -1, NULL, 16, &ipcomps->ipv4, 0); + ipcomps->cidr4Entry = newtEntry(-1, -1, NULL, 16, &ipcomps->cidr4, 0); + + /* use a nested grid for ipv4 addr & netmask */ + qgrid = newtCreateGrid(3, 1); + + newtGridSetField(qgrid, 0, 0, NEWT_GRID_COMPONENT, + ipcomps->ipv4Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(qgrid, 1, 0, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("/")), + 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(qgrid, 2, 0, NEWT_GRID_COMPONENT, + ipcomps->cidr4Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtGridSetField(egrid, 1, pos, NEWT_GRID_SUBGRID, qgrid, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtComponentAddCallback(ipcomps->ipv4Entry, ipCallback, ipcomps); + newtComponentAddCallback(ipcomps->cidr4Entry, cidrCallback, ipcomps); + + /* populate fields if we have data already */ + if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } else if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (*ret) { + newtEntrySet(ipcomps->ipv4Entry, ret, 1); + } + + if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->netmask, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } else if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->netmask, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (*ret) { + newtEntrySet(ipcomps->cidr4Entry, ret, 1); + } + + pos++; + } + + /* IPv6 entry items */ + if (stack[IPV6]) { + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("IPv6 address:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + ipcomps->ipv6Entry = newtEntry(-1, -1, NULL, 41, &ipcomps->ipv6, 0); + ipcomps->cidr6Entry = newtEntry(-1, -1, NULL, 4, &ipcomps->cidr6, 0); + + /* use a nested grid for ipv6 addr & netmask */ + rgrid = newtCreateGrid(3, 1); + + newtGridSetField(rgrid, 0, 0, NEWT_GRID_COMPONENT, + ipcomps->ipv6Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(rgrid, 1, 0, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("/")), + 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(rgrid, 2, 0, NEWT_GRID_COMPONENT, + ipcomps->cidr6Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtGridSetField(egrid, 1, pos, NEWT_GRID_SUBGRID, rgrid, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtComponentAddCallback(ipcomps->ipv6Entry, ipCallback, ipcomps); + newtComponentAddCallback(ipcomps->cidr6Entry, cidrCallback, ipcomps); + + /* populate fields if we have data already */ + if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, ret, + INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } else if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, ret, + INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (*ret) { + newtEntrySet(ipcomps->ipv6Entry, ret, 1); + } + + if (iface->ip6prefix) { + if (asprintf(&buf, "%d", iface->ip6prefix) == -1) { + buf = NULL; + } + } else if (iface->ip6prefix) { + if (asprintf(&buf, "%d", iface->ip6prefix) == -1) { + buf = NULL; + } + } + + if (buf != NULL) { + newtEntrySet(ipcomps->cidr6Entry, buf, 1); + free(buf); + } + + pos++; + } + + /* common entry items */ + ipcomps->gwEntry = newtEntry(-1, -1, NULL, 41, &ipcomps->gw, 0); + ipcomps->nsEntry = newtEntry(-1, -1, NULL, 41, &ipcomps->ns, 0); + + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("Gateway:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(egrid, 1, pos, NEWT_GRID_COMPONENT, + ipcomps->gwEntry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + pos++; + + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("Name Server:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(egrid, 1, pos, NEWT_GRID_COMPONENT, + ipcomps->nsEntry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + newtEntrySet(ipcomps->gwEntry, ret, 1); + } + } else if (iface_have_in6_addr(&iface->gateway6)) { + if (inet_ntop(AF_INET6, &iface->gateway6, ret, + INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + newtEntrySet(ipcomps->gwEntry, ret, 1); + } + } + + if (iface->numdns) { + newtEntrySet(ipcomps->nsEntry, iface->dns[0], 1); + } else if (iface->numdns) { + newtEntrySet(ipcomps->nsEntry, iface->dns[0], 1); + } + + newtComponentAddCallback(ipcomps->gwEntry, ipCallback, ipcomps); + newtComponentAddCallback(ipcomps->nsEntry, ipCallback, ipcomps); + + /* button bar at the bottom of the window */ + buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL); + + /* main window layout */ + grid = newtCreateGrid(1, 3); + + if (asprintf(&buf, + _("Enter the IPv4 and/or the IPv6 address and prefix " + "(address / prefix). For IPv4, the dotted-quad " + "netmask or the CIDR-style prefix are acceptable. " + "The gateway and name server fields must be valid IPv4 " + "or IPv6 addresses.")) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + text = newtTextboxReflowed(-1, -1, buf, 52, 0, 10, 0); + + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 1, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, egrid, + 0, 0, 0, 1, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + f = newtForm(NULL, NULL, 0); + newtGridAddComponentsToForm(grid, f, 1); + newtGridWrappedWindow(grid, _("Manual TCP/IP Configuration")); + newtGridFree(grid, 1); + + /* run the form */ + while ((have[IPV4] != 2) || (have[IPV6] != 2)) { + have[IPV4] = 0; + have[IPV6] = 0; + + for (i = 0; i < 2; i++) + if (!stack[i]) have[i] = 2; + + answer = newtRunForm(f); + /* memset(newCfg, 0, sizeof(*newCfg)); */ + + /* collect IPv4 data */ + if (stack[IPV4]) { + if (ipcomps->ipv4) { + if (inet_pton(AF_INET, ipcomps->ipv4, &iface->ipaddr) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + have[IPV4]++; + } + } + + if (ipcomps->cidr4) { + if (inet_pton(AF_INET, ipcomps->cidr4, &iface->netmask)>=1) { + have[IPV4]++; + } else { + errno = 0; + cidr = strtol(ipcomps->cidr4, NULL, 10); + + if ((errno == ERANGE && (cidr == LONG_MIN || + cidr == LONG_MAX)) || + (errno != 0 && cidr == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (cidr >= 1 && cidr <= 32) { + tmpaddr = iface_prefix2netmask(cidr); + if (tmpaddr != NULL) { + memcpy(&iface->netmask, tmpaddr, + sizeof(struct in_addr)); + have[IPV4]++; + } else { + iface->netmask.s_addr = 0; + } + } + } + } + } + + /* collect IPv6 data */ + if (stack[IPV6]) { + if (ipcomps->ipv6) { + if (inet_pton(AF_INET6, ipcomps->ipv6, &iface->ip6addr) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + have[IPV6]++; + } + } + + if (ipcomps->cidr6) { + errno = 0; + prefix = strtol(ipcomps->cidr6, NULL, 10); + + if ((errno == ERANGE && (prefix == LONG_MIN || + prefix == LONG_MAX)) || + (errno != 0 && prefix == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (prefix > 0 || prefix <= 128) { + iface->ip6prefix = prefix; + have[IPV6]++; + } + } + } + + /* collect common network settings */ + if (ipcomps->gw) { + if (inet_pton(AF_INET, ipcomps->gw, &iface->gateway) <= 0) { + memset(&iface->gateway, 0, sizeof(iface->gateway)); + + if (inet_pton(AF_INET6, ipcomps->gw, &iface->gateway6) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + memset(&iface->gateway6, 0, sizeof(iface->gateway6)); + } + } + } + + /* gather nameservers */ + if (ipcomps->ns) { + if ((inet_pton(AF_INET, ipcomps->ns, &addr) >= 1) || + (inet_pton(AF_INET6, ipcomps->ns, &addr6) >= 1)) { + iface->dns[0] = strdup(ipcomps->ns); + if (iface->numdns < 1) + iface->numdns = 1; + } + } + + /* user selected back, but we've saved what they entered already */ + if (answer == back) { + newtFormDestroy(f); + newtPopWindow(); + free(buf); + return LOADER_BACK; + } + + /* we might be done now */ + if (have[IPV4] != 2) { + newtWinMessage(_("Missing Information"), _("Retry"), + _("You must enter both a valid IPv4 address and a " + "network mask or CIDR prefix.")); + } + + if (have[IPV6] != 2) { + newtWinMessage(_("Missing Information"), _("Retry"), + _("You must enter both a valid IPv6 address and a " + "CIDR prefix.")); + } + + strcpy(iface->device, device); + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + } + + free(buf); + newtFormDestroy(f); + newtPopWindow(); + + return LOADER_OK; +} + +void debugNetworkInfo(iface_t * iface) { + int i; + char buf[INET6_ADDRSTRLEN]; + + logMessage(DEBUGLVL, "device = %s", iface->device); + + if (iface->macaddr != NULL) { + logMessage(DEBUGLVL, "MAC address = %s", iface->macaddr); + } + + if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, INET_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv4 address = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv4 address = %s", buf); + } + } + + if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->netmask, buf, INET_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv4 netmask = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv4 netmask = %s", buf); + } + } + + if (iface_have_in_addr(&iface->broadcast)) { + if (inet_ntop(AF_INET, &iface->broadcast, buf, + INET_ADDRSTRLEN) == NULL ) { + logMessage(DEBUGLVL, "IPv4 broadcast = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv4 broadcast = %s", buf); + } + } + + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, buf, INET_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "Gateway = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "Gateway = %s", buf); + } + } + + if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, buf, + INET6_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv6 address = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv6 address = %s", buf); + } + } + + if (iface->ip6prefix) { + logMessage(DEBUGLVL, "IPv6 prefix = %d", iface->ip6prefix); + } + + if (iface_have_in6_addr(&iface->gateway6)) { + if (inet_ntop(AF_INET6, &iface->gateway6, buf, + INET6_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv6 Gateway = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv6 Gateway = %s", buf); + } + } + + if (iface->numdns > 0) { + for (i = 0; i < iface->numdns; i++) { + logMessage(DEBUGLVL, "DNS[%d] = %s", i, iface->dns[i]); + } + } + + if (iface->hostname) { + logMessage(DEBUGLVL, "hostname = %s", iface->hostname); + } + + if (iface->domain) { + logMessage(DEBUGLVL, "domain = %s", iface->domain); + } + + if (iface->dhcptimeout) { + logMessage(DEBUGLVL, "DHCP timeout = %d", iface->dhcptimeout); + } + + if (iface->vendorclass) { + logMessage(DEBUGLVL, "DHCP vendor class = %s", iface->vendorclass); + } + + if (iface->ssid) { + logMessage(DEBUGLVL, "SSID = %s", iface->ssid); + } + + if (iface->wepkey) { + logMessage(DEBUGLVL, "WEP key = %s", iface->wepkey); + } + +#if defined(__s390__) || defined(__s390x__) + if (iface->mtu) { + logMessage(DEBUGLVL, "mtu = %d", iface->mtu); + } + + if (iface->subchannels) { + logMessage(DEBUGLVL, "subchannels = %s", iface->subchannels); + } + + if (iface->portname) { + logMessage(DEBUGLVL, "portname = %s", iface->portname); + } + + if (iface->peerid) { + logMessage(DEBUGLVL, "peerid = %s", iface->peerid); + } + + if (iface->nettype) { + logMessage(DEBUGLVL, "nettype = %s", iface->nettype); + } + + if (iface->ctcprot) { + logMessage(DEBUGLVL, "ctcprot = %s", iface->ctcprot); + } +#endif + +/* FIXME: print rest of iface structure */ + + return; +} + +/* + * By default, we disable all network interfaces and then only + * bring up the ones the user wants. + */ +int writeDisabledNetInfo(void) { + int i = 0; + char *ofile = NULL; + FILE *fp = NULL; + struct device **devs = NULL; + + devs = getDevices(DEVICE_NETWORK); + + if (devs == NULL) { + return 1; + } + + for (i = 0; devs[i]; i++) { + if (asprintf(&ofile, "/etc/sysconfig/network-scripts/ifcfg-%s", + devs[i]->device) == -1) { + logMessage(ERROR, "%s (%d): %m", __func__, __LINE__); + abort(); + } + + if ((fp = fopen(ofile, "w")) == NULL) { + free(ofile); + return 2; + } + + fprintf(fp, "DEVICE=%s\n", devs[i]->device); + fprintf(fp, "HWADDR=%s\n", iface_mac2str(devs[i]->device)); + fprintf(fp, "ONBOOT=no\n"); + fprintf(fp, "NM_CONTROLLED=no\n"); + + if (ofile) { + free(ofile); + } + + if (fclose(fp) == EOF) { + return 3; + } + } + + return 0; +} + +/* + * Write out network interface control files: + * /etc/sysconfig/network-scripts/ifcfg-DEVICE + * /etc/sysconfig/network + */ +int writeEnabledNetInfo(iface_t *iface) { + int i = 0; + FILE *fp = NULL; + char buf[INET6_ADDRSTRLEN+1]; + char *ofile = NULL; + + memset(&buf, '\0', sizeof(buf)); + + if (asprintf(&ofile, "/etc/sysconfig/network-scripts/ifcfg-%s", + iface->device) == -1) { + return 1; + } + + if ((fp = fopen(ofile, "w")) == NULL) { + free(ofile); + return 2; + } + + fprintf(fp, "DEVICE=%s\n", iface->device); + fprintf(fp, "HWADDR=%s\n", iface_mac2str(iface->device)); + fprintf(fp, "ONBOOT=yes\n"); + fprintf(fp, "NM_CONTROLLED=yes\n"); + + if (!FL_NOIPV4(flags)) { + if (iface->ipv4method == IPV4_DHCP_METHOD) { + fprintf(fp, "BOOTPROTO=dhcp\n"); + } else if (iface->ipv4method == IPV4_MANUAL_METHOD) { + fprintf(fp, "BOOTPROTO=static\n"); + + if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 3; + } + + fprintf(fp, "IPADDR=%s\n", buf); + } + + if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 4; + } + + fprintf(fp, "NETMASK=%s\n", buf); + } + + if (iface_have_in_addr(&iface->broadcast)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 5; + } + + fprintf(fp, "BROADCAST=%s\n", buf); + } + + /* XXX: this should not be here, but ifcfg-fedora + * in NM does not currently read the global + * /etc/sysconfig/network file. + */ + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 6; + } + + fprintf(fp, "GATEWAY=%s\n", buf); + } + } + } + + if (!FL_NOIPV6(flags)) { + if (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD || + iface->ipv6method == IPV6_MANUAL_METHOD) { + fprintf(fp, "IPV6INIT=yes\n"); + + if (iface->ipv6method == IPV6_AUTO_METHOD) { + fprintf(fp, "IPV6_AUTOCONF=yes\n"); + } else if (iface->ipv6method == IPV6_DHCP_METHOD) { + fprintf(fp, "DHCPV6C=yes\n"); + } else if (iface->ipv6method == IPV6_MANUAL_METHOD) { + if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, buf, + INET6_ADDRSTRLEN) == NULL) { + free(ofile); + return 7; + } + + if (iface->ip6prefix) { + fprintf(fp, "IPV6ADDR=%s/%d\n", buf, iface->ip6prefix); + } else { + fprintf(fp, "IPV6ADDR=%s\n", buf); + } + } + } + } + } + + if (iface->numdns > 0) { + for (i = 0; i < iface->numdns; i++) { + fprintf(fp, "DNS%d=%s\n", i+1, iface->dns[i]); + } + } + + if (iface->hostname) { + fprintf(fp, "HOSTNAME=%s\n", iface->hostname); + } + + if (iface->domain) { + fprintf(fp, "DOMAIN=%s\n", iface->domain); + } + + if (iface->mtu) { + fprintf(fp, "MTU=%d\n", iface->mtu); + } + + if (iface->peerid) { + fprintf(fp, "PEERID=%s\n", iface->peerid); + } + + if (iface->subchannels) { + fprintf(fp, "SUBCHANNELS=%s\n", iface->subchannels); + } + + if (iface->portname) { + fprintf(fp, "PORTNAME=%s\n", iface->portname); + } + + if (iface->nettype) { + fprintf(fp, "NETTYPE=%s\n", iface->nettype); + } + + if (iface->ctcprot) { + fprintf(fp, "CTCPROT=%s\n", iface->ctcprot); + } + + if (ofile) { + free(ofile); + } + + if (fclose(fp) == EOF) { + return 8; + } + + /* Global settings */ + if ((fp = fopen("/etc/sysconfig/network", "w")) == NULL) { + return 9; + } + + if (!FL_NOIPV4(flags)) { + fprintf(fp, "NETWORKING=yes\n"); + } + + if (!FL_NOIPV6(flags)) { + fprintf(fp, "NETWORKING_IPV6=yes\n"); + } + + if (iface->hostname != NULL) { + fprintf(fp, "HOSTNAME=%s\n", iface->hostname); + } + + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, buf, + INET_ADDRSTRLEN) == NULL) { + return 10; + } + + fprintf(fp, "GATEWAY=%s\n", buf); + } + + if (iface_have_in6_addr(&iface->gateway6)) { + if (inet_ntop(AF_INET6, &iface->gateway6, buf, + INET6_ADDRSTRLEN) == NULL) { + return 11; + } + + fprintf(fp, "IPV6_DEFAULTGW=%s\n", buf); + } + + if (fclose(fp) == EOF) { + return 12; + } + + return 0; +} + +void setKickstartNetwork(struct loaderData_s * loaderData, int argc, + char ** argv) { + char * arg, * bootProto = NULL, * device = NULL, *ethtool = NULL, * class = NULL; + char * essid = NULL, * wepkey = NULL, * onboot = NULL; + int mtu = 1500, noipv4 = 0, noipv6 = 0, dhcpTimeout = -1, noDns = 0, noksdev = 0; + int rc; + poptContext optCon; + iface_t iface; + + struct poptOption ksOptions[] = { + { "bootproto", '\0', POPT_ARG_STRING, &bootProto, 0, NULL, NULL }, + { "device", '\0', POPT_ARG_STRING, &device, 0, NULL, NULL }, + { "dhcpclass", '\0', POPT_ARG_STRING, &class, 0, NULL, NULL }, + { "gateway", '\0', POPT_ARG_STRING, NULL, 'g', NULL, NULL }, + { "ip", '\0', POPT_ARG_STRING, NULL, 'i', NULL, NULL }, + { "mtu", '\0', POPT_ARG_INT, &mtu, 0, NULL, NULL }, + { "nameserver", '\0', POPT_ARG_STRING, NULL, 'n', NULL, NULL }, + { "netmask", '\0', POPT_ARG_STRING, NULL, 'm', NULL, NULL }, + { "noipv4", '\0', POPT_ARG_NONE, &noipv4, 0, NULL, NULL }, + { "noipv6", '\0', POPT_ARG_NONE, &noipv6, 0, NULL, NULL }, + { "nodns", '\0', POPT_ARG_NONE, &noDns, 0, NULL, NULL }, + { "hostname", '\0', POPT_ARG_STRING, NULL, 'h', NULL, NULL}, + { "ethtool", '\0', POPT_ARG_STRING, ðtool, 0, NULL, NULL }, + { "essid", '\0', POPT_ARG_STRING, &essid, 0, NULL, NULL }, + { "wepkey", '\0', POPT_ARG_STRING, &wepkey, 0, NULL, NULL }, + { "onboot", '\0', POPT_ARG_STRING, &onboot, 0, NULL, NULL }, + { "notksdevice", '\0', POPT_ARG_NONE, &noksdev, 0, NULL, NULL }, + { "dhcptimeout", '\0', POPT_ARG_INT, &dhcpTimeout, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + iface_init_iface_t(&iface); + + optCon = poptGetContext(NULL, argc, (const char **) argv, + ksOptions, 0); + while ((rc = poptGetNextOpt(optCon)) >= 0) { + arg = (char *) poptGetOptArg(optCon); + + switch (rc) { + case 'g': + loaderData->gateway = strdup(arg); + break; + case 'i': + loaderData->ipv4 = strdup(arg); + break; + case 'n': + loaderData->dns = strdup(arg); + break; + case 'm': + loaderData->netmask = strdup(arg); + break; + case 'h': + if (loaderData->hostname) + free(loaderData->hostname); + loaderData->hostname = strdup(arg); + break; + } + } + + if (rc < -1) { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad argument to kickstart network command %s: %s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + } else { + poptFreeContext(optCon); + } + + /* if they've specified dhcp/bootp or haven't specified anything, + * use dhcp for the interface */ + if ((bootProto && (!strncmp(bootProto, "dhcp", 4) || + !strncmp(bootProto, "bootp", 4))) || + (!bootProto && !loaderData->ipv4)) { + loaderData->ipv4 = strdup("dhcp"); + loaderData->ipinfo_set = 1; + } else if (loaderData->ipv4) { + /* JKFIXME: this assumes a bit... */ + loaderData->ipinfo_set = 1; + } + + /* now make sure the specified bootproto is valid */ + if (bootProto && strcmp(bootProto, "dhcp") && strcmp(bootProto, "bootp") && + strcmp(bootProto, "static") && strcmp(bootProto, "query")) { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad bootproto %s specified in network command"), + bootProto); + } + + if (!noksdev) { + if (device) { + loaderData->netDev = strdup(device); + loaderData->netDev_set = 1; + } + + if (class) { + loaderData->netCls = strdup(class); + loaderData->netCls_set = 1; + } + + if (ethtool) { + if (loaderData->ethtool) + free(loaderData->ethtool); + loaderData->ethtool = strdup(ethtool); + free(ethtool); + } + + if (essid) { + if (loaderData->essid) + free(loaderData->essid); + loaderData->essid = strdup(essid); + free(essid); + } + + if (wepkey) { + if (loaderData->wepkey) + free(loaderData->wepkey); + loaderData->wepkey = strdup(wepkey); + free(wepkey); + } + + if (mtu) { + loaderData->mtu = mtu; + } + + if (noipv4) + flags |= LOADER_FLAGS_NOIPV4; + + if (noipv6) + flags |= LOADER_FLAGS_NOIPV6; + } + + if (noDns) { + loaderData->noDns = 1; + } + + /* Make sure the network is always up if there's a network line in the + * kickstart file, as %post/%pre scripts might require that. + */ + if (loaderData->method != METHOD_NFS && loaderData->method != METHOD_URL) { + if (kickstartNetworkUp(loaderData, &iface)) + logMessage(ERROR, "unable to bring up network"); + } +} + +/* if multiple interfaces get one to use from user. */ +/* NOTE - uses kickstart data available in loaderData */ +int chooseNetworkInterface(struct loaderData_s * loaderData) { + int i, rc, ask, idrc, secs, deviceNums = 0, deviceNum, foundDev = 0; + unsigned int max = 40; + char **devices; + char **deviceNames; + char *ksMacAddr = NULL, *seconds = strdup("10"), *idstr = NULL; + struct device **devs; + struct newtWinEntry entry[] = {{N_("Seconds:"), (char **) &seconds, 0}, + {NULL, NULL, 0 }}; + + devs = getDevices(DEVICE_NETWORK); + if (!devs) { + logMessage(ERROR, "no network devices in choose network device!"); + return LOADER_ERROR; + } + + for (i = 0; devs[i]; i++); + + devices = alloca((i + 1) * sizeof(*devices)); + deviceNames = alloca((i + 1) * sizeof(*devices)); + if (loaderData->netDev && (loaderData->netDev_set) == 1) { + if ((loaderData->bootIf && (loaderData->bootIf_set) == 1) && !strcasecmp(loaderData->netDev, "bootif")) { + ksMacAddr = strdup(loaderData->bootIf); + } else { + ksMacAddr = strdup(loaderData->netDev); + } + + ksMacAddr = str2upper(ksMacAddr); + } + + for (i = 0; devs[i]; i++) { + if (!devs[i]->device) + continue; + + if (devs[i]->description) { + deviceNames[deviceNums] = alloca(strlen(devs[i]->device) + + strlen(devs[i]->description) + 4); + sprintf(deviceNames[deviceNums],"%s - %s", + devs[i]->device, devs[i]->description); + if (strlen(deviceNames[deviceNums]) > max) + max = strlen(deviceNames[deviceNums]); + devices[deviceNums] = devs[i]->device; + } else { + devices[deviceNums] = devs[i]->device; + deviceNames[deviceNums] = devs[i]->device; + } + + deviceNums++; + + /* this device has been set and we don't really need to ask + * about it again... */ + if (loaderData->netDev && (loaderData->netDev_set == 1)) { + if (!strcmp(loaderData->netDev, devs[i]->device)) { + foundDev = 1; + } else if (ksMacAddr != NULL) { + /* maybe it's a mac address */ + char *devmacaddr = NULL; + devmacaddr = iface_mac2str(devs[i]->device); + if ((devmacaddr != NULL) && !strcmp(ksMacAddr, devmacaddr)) { + foundDev = 1; + free(loaderData->netDev); + loaderData->netDev = devs[i]->device; + if (devmacaddr != NULL) + free(devmacaddr); + break; + } + + if (devmacaddr != NULL) + free(devmacaddr); + } + } + } + if (ksMacAddr) + free(ksMacAddr); + if (foundDev == 1) + return LOADER_NOOP; + + devices[deviceNums] = NULL; + deviceNames[deviceNums] = NULL; + qsort(devices, deviceNums, sizeof(*devices), simpleStringCmp); + qsort(deviceNames, deviceNums, sizeof(*devices), simpleStringCmp); + + /* ASSERT: we should *ALWAYS* have a network device when we get here */ + if (!deviceNums) { + logMessage(CRITICAL, "no network device in chooseNetworkInterface"); + return LOADER_ERROR; + } + + /* JKFIXME: if we only have one interface and it doesn't have link, + * do we go ahead? */ + if (deviceNums == 1) { + logMessage(INFO, "only have one network device: %s", devices[0]); + loaderData->netDev = devices[0]; + return LOADER_NOOP; + } + + if ((loaderData->netDev && (loaderData->netDev_set == 1)) && + !strcmp(loaderData->netDev, "link")) { + logMessage(INFO, "looking for first netDev with link"); + for (rc = 0; rc < 5; rc++) { + for (i = 0; i < deviceNums; i++) { + if (get_link_status(devices[i]) == 1) { + loaderData->netDev = devices[i]; + logMessage(INFO, "%s has link, using it", devices[i]); + return LOADER_NOOP; + } + } + sleep(1); + } + logMessage(WARNING, "wanted netdev with link, but none present. prompting"); + } + + startNewt(); + + if (max > 70) + max = 70; + + /* JKFIXME: should display link status */ + deviceNum = 0; + ask = 1; + while (ask) { + rc = newtWinMenu(_("Networking Device"), + _("You have multiple network devices on this system. " + "Which would you like to install through?"), + max, 10, 10, + deviceNums < 6 ? deviceNums : 6, deviceNames, + &deviceNum, _("OK"), _("Identify"), _("Back"), NULL); + + if (rc == 2) { + if (!devices[deviceNum]) { + logMessage(ERROR, "NIC %d contains no device name", deviceNum); + continue; + } + + if (asprintf(&idstr, "%s %s %s", + _("You can identify the physical port for"), + devices[deviceNum], + _("by flashing the LED lights for a number of " + "seconds. Enter a number between 1 and 30 to " + "set the duration to flash the LED port " + "lights.")) == -1) { + logMessage(ERROR, "asprintf() failure in %s: %m", __func__); + abort(); + } + + i = 1; + while (i) { + idrc = newtWinEntries(_("Identify NIC"), idstr, 50, 5, 15, 24, + entry, _("OK"), _("Back"), NULL); + + if (idrc == 0 || idrc == 1) { + errno = 0; + secs = strtol((const char *) seconds, NULL, 10); + if (errno == EINVAL || errno == ERANGE) { + logMessage(ERROR, "strtol() failure in %s: %m", + __func__); + continue; + } + + if (secs <=0 || secs > 30) { + newtWinMessage(_("Invalid Duration"), _("OK"), + _("You must enter the number of " + "seconds as an integer between 1 " + "and 30.")); + continue; + } + + idrc = 41 + strlen(devices[deviceNum]); + if (secs > 9) { + idrc += 1; + } + + winStatus(idrc, 3, NULL, + _("Flashing %s port lights for %d seconds..."), + devices[deviceNum], secs); + + if (identifyNIC(devices[deviceNum], secs)) { + logMessage(ERROR, + "error during physical NIC identification"); + } + + newtPopWindow(); + i = 0; + } else if (idrc == 2) { + i = 0; + } + } + } else if (rc == 3) { + ask = 0; + return LOADER_BACK; + } else { + ask = 0; + } + } + + loaderData->netDev = devices[deviceNum]; + return LOADER_OK; +} + +/* JKFIXME: bad name. this function brings up networking early on a + * kickstart install so that we can do things like grab the ks.cfg from + * the network */ +int kickstartNetworkUp(struct loaderData_s * loaderData, iface_t * iface) { + int rc; + + /* we may have networking already, so return to the caller */ + if ((loaderData->ipinfo_set == 1) || (loaderData->ipv6info_set == 1)) + return 0; + + memset(iface, 0, sizeof(*iface)); + + do { + do { + /* this is smart and does the right thing based on whether or not + * we have ksdevice= specified */ + rc = chooseNetworkInterface(loaderData); + + if (rc == LOADER_ERROR) { + /* JKFIXME: ask for a driver disk? */ + logMessage(ERROR, "no network drivers for doing kickstart"); + return -1; + } else if (rc == LOADER_BACK) { + return -1; + } + + /* insert device into iface structure */ + strcpy(iface->device, loaderData->netDev); + + break; + } while (1); + + /* we don't want to end up asking about interface more than once + * if we're in a kickstart-ish case (#100724) */ + loaderData->netDev_set = 1; + + /* JKFIXME: this is kind of crufty, we depend on the fact that the + * ip is set and then just get the network up. we should probably + * add a way to do asking about static here and not be such a hack */ + if (!loaderData->ipv4) { + loaderData->ipv4 = strdup("dhcp"); + } + loaderData->ipinfo_set = 1; + + setupNetworkDeviceConfig(iface, loaderData); + + rc = readNetConfig(loaderData->netDev, iface, loaderData->netCls, + loaderData->method); + + if (rc == LOADER_ERROR) { + logMessage(ERROR, "unable to setup networking"); + return -1; + } + else if (rc == LOADER_BACK) { + /* Going back to the interface selection screen, so unset anything + * we set before attempting to bring the incorrect interface up. + */ + loaderData->netDev_set = 0; + free(loaderData->ipv4); + loaderData->ipinfo_set = 0; + } + else + break; + } while (1); + + return 0; +} + +void splitHostname (char *str, char **host, char **port) +{ + char *rightbrack = strchr(str, ']'); + + *host = NULL; + *port = NULL; + + if (*str == '[' && rightbrack) { + /* An IPv6 address surrounded by brackets, optionally with a colon and + * port number. + */ + char *colon = strrchr(rightbrack, ':'); + + if (colon) { + *host = strndup(str+1, rightbrack-1-str); + *port = strdup(colon+1); + } + else + *host = strndup(str+1, rightbrack-1-str); + } else if (strcount(str, ':') > 1) { + /* An IPv6 address without brackets. Don't make the user surround the + * address with brackets if there's no port number. + */ + *host = strdup(str); + } else { + /* An IPv4 address, optionally with a colon and port number. */ + char *colon = strrchr(str, ':'); + + if (colon) { + *host = strndup(str, colon-str); + *port = strdup(colon+1); + } + else + *host = strdup(str); + } +} + +/* + * Start NetworkManager and wait for a valid link, return non-zero on error. + */ +int get_connection(iface_t *iface) { + int ret; + DBusConnection *connection = NULL; + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + DBusError error; + DBusMessageIter iter, variant_iter; + dbus_uint32_t state = 0; + char *nm_iface = "org.freedesktop.NetworkManager"; + char *property = "State"; + + if (iface == NULL) { + return 1; + } + + /* display status */ + if (FL_CMDLINE(flags)) { + printf(_("Waiting for NetworkManager to configure %s...\n"), + iface->device); + } else { + winStatus(55, 3, NULL, + _("Waiting for NetworkManager to configure %s...\n"), + iface->device, 0); + } + + /* start NetworkManager for configured interface */ + logMessage(INFO, "starting NetworkManager (%d) for %s", __LINE__, + iface->device); + ret = iface_start_NetworkManager(iface); + if (ret > 0) { + return 2; + } + + dbus_error_init(&error); + connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + return 3; + } + + dbus_error_init(&error); + message = dbus_message_new_method_call(NM_DBUS_SERVICE, + NM_DBUS_PATH, + "org.freedesktop.DBus.Properties", + "Get"); + if (!message) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + return 4; + } + + dbus_error_init(&error); + if (!dbus_message_append_args(message, + DBUS_TYPE_STRING, &nm_iface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID)) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + dbus_message_unref(message); + return 5; + } + + /* send message and block until a reply or error comes back */ + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(connection, + message, -1, + &error); + dbus_message_unref(message); + if (!reply) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + dbus_message_unref(reply); + return 6; + } + + /* extra uint32 'state' property from the returned variant type */ + dbus_message_iter_init(reply, &iter); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + logMessage(DEBUGLVL, "%s (%d): unexpected reply format", + __func__, __LINE__); + dbus_message_unref(reply); + return 7; + } + + /* open the variant */ + dbus_message_iter_recurse(&iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_UINT32) { + logMessage(DEBUGLVL, "%s (%d): unexpected reply format", + __func__, __LINE__); + dbus_message_unref(reply); + return 8; + } + + dbus_message_iter_get_basic(&variant_iter, &state); + if (state == NM_STATE_CONNECTED) { + logMessage(DEBUGLVL, "%s (%d): NetworkManager connected", + __func__, __LINE__); + dbus_message_unref(reply); + return 0; + } + + /* NM is not in NM_STATE_CONNECTED if we get here */ + dbus_message_unref(reply); + return 9; +} + +/* vim:set shiftwidth=4 softtabstop=4: */ |