#include #include #include #include #include #include #include #include "netconf.h" #include "internal.h" #include #include "netconf.m" #include "hostinfo.h" #include "../askrunlevel/askrunlevel.h" /* #Specification: devices / management netconf knows how to load a devices module (probing) and how to configure it (ifconfig). It also knows how to update the different routes. */ # class NETDEV_STATUS { public: SSTRING up; SSTRINGS &reconf; SSTRING down; NETDEV_STATUS (SSTRINGS &_reconf) : reconf(_reconf) { }; }; /* Programme a network interface (ifconfig) only if needed, Compare the current configuration with the expected configuration. If there is a mismatch (or the interface is not configure), then it is reconfigure with the expected values. Return -1 if any error. */ static int devices_ifconfig( const char *device, const char *expected_hst, const char *expected_msk, const char *expected_bcast, bool per_dev_hint, NETDEV_STATUS &status) { IFCONFIG_INFO info; int ret = ifconfig_getinfo (device,info); if (per_dev_hint){ net_hint ("DEVICE",device); net_hint ("IPADDR",expected_hst); net_hint ("NETMASK",expected_msk); net_hint ("BROADCAST",expected_bcast); } if (ret == -1 && !simul_ison()){ xconf_error (MSG_U(E_CANTPROBE ,"Can't probe current setup of network device %s\n" "No way to activate the network\n") ,device); }else{ if (strcmp(info.ip_addr,expected_hst)==0 && strcmp(info.netmask,expected_msk)==0 && strcmp(info.bcast,expected_bcast)==0 && info.flags & IFF_UP){ ret = 0; if (per_dev_hint) net_hint ("ACTION",""); }else{ char cmd[300]; /* #Specification: netconf / ifconfig / down We always force an interface DOWN before reconfiguring it. It has the side effect of cleaning the route table for that device. */ snprintf (cmd,sizeof(cmd)-1,"%s down",device); netconf_system_if ("ifconfig",cmd); if (strcmp(expected_hst, "0.0.0.0") == 0) { snprintf (cmd,sizeof(cmd)-1,"%s %s" ,device ,expected_hst); } else { snprintf (cmd,sizeof(cmd)-1,"%s %s netmask %s broadcast %s" ,device ,expected_hst ,expected_msk ,expected_bcast); } ret = netconf_system_if ("ifconfig",cmd); bool was_down = (info.flags & IFF_UP) == 0; if (per_dev_hint){ net_hint ("ACTION",was_down ? "up" : "reconf"); }else if (was_down){ status.up.append (device); status.up.append (" "); }else{ status.reconf.add (new SSTRING(device)); } } } return ret; } /* Configure the loopback device. Return -1 if any error. */ static int netconf_setloopback( NETDEV_STATUS &status, bool per_dev_hint) { /* #Specification: loopback / strategy As far as I know, there is only a way to configure the loopback device. /sbin/ifconfig lo 127.0.0.1 /sbin/route add -net loopback */ int ret = devices_ifconfig ("lo","127.0.0.1","255.0.0.0" ,"127.255.255.255",per_dev_hint,status); if (ret == 0){ char gateway[PATH_MAX]; if (!route_isactive("127.0.0.0",gateway)){ ret = netconf_system_if ("route","add -net 127.0.0.0 netmask 255.0.0.0 dev lo"); } } return ret == 0 ? 0 : -1; } /* Configure the loopback device. Return -1 if any error. */ int netconf_setloopback() { SSTRINGS reconf; NETDEV_STATUS status(reconf); return netconf_setloopback(status,false); } #if 0 /* Perform a gethostbyname with error reporting. Return -1 if not found. */ static int devices_gethostbyname( const char *host, const char *device, char *ip_num, // Will contain the formatted IP address int silent) // Ok if not set, no error message { struct hostent *ent = gethostbyname(host); int ret = -1; if (ent == NULL){ if (!silent){ xconf_error ("%s is not defined\n" "Can't configured device %s\n",host,device); } }else{ ret = 0; ipnum_ip2a (ent,ip_num); } return ret; } /* Perform a getnetbyname with error reporting. Return -1 if not found. */ static int devices_getnetbyname( const char *net, const char *device, char *ip_num, // Will contain the formatted IP address int silent) // Ok if not set, no error message { struct netent *ent = getnetbyname(net); int ret = -1; if (ent == NULL){ if (!silent){ xconf_error ("%s is not defined\n" "Can't configured device %s\n",net,device); } }else{ ret = 0; ipnum_ip2a (ent,ip_num); } return ret; } #endif struct IFACE_NUMS{ char hst[LEN_IP_ASC]; char net[LEN_IP_ASC]; char msk[LEN_IP_ASC]; char bcast[LEN_IP_ASC]; }; void device_setstdnetmask( const char *hostip, // IP number of a HOST char *def_msk) // Default netmask { int num4[4]; ipnum_aip24 (hostip,num4); if (num4[0] == 0){ strcpy (def_msk,"0.0.0.0"); }else if ((num4[0] & 192) == 192){ strcpy (def_msk,"255.255.255.0"); }else if ((num4[0] & 128) == 128){ strcpy (def_msk,"255.255.0.0"); }else{ strcpy (def_msk,"255.0.0.0"); } } static int device_copystr (char *dst, SSTRING &src) { src.copy (dst); return src.is_empty() ? -1 : 0; } /* Get the IP number assigned to an interface (host,network and mask) Fill default values if some information is not fully specified. Return -1 if to much is missing. */ static int device_ifaceinfo ( const char *device, // Name of the device (for error report) INTER_INFO &itf, IFACE_NUMS &nums, bool showerr) { int ret = -1; int h_ok = device_copystr (nums.hst,itf.ipaddr); int n_ok = device_copystr (nums.net,itf.network); int m_ok = device_copystr (nums.msk,itf.netmask); int b_ok = device_copystr (nums.bcast,itf.bcast); if (h_ok != -1){ /* #Specification: ifconfig / eth devices / 127.0.0.1 Linuxconf won't configure an ethernet interface to 127.0.0.1. It will simply ignore it. This case happen when a properly configured linux system without an ethernet adaptor get one. */ if (strcmp(nums.hst,"127.0.0.1")!=0){ /* #Specification: ifconfig / netmask and network / optionnal The netmask and network and broadcast address of a network specification are optionnal. The kernel (or ifconfig or route, I don't know) should compute suitable defaults. Given it is not the case, linuxconf is computing the defaults. If you only give a host IP number, the netmask and the net number and broadcast will be computed. If you supply the IP and the netmask, the net number and the broadcast will be computed. The logic of linuxconf goes like this. If you supply only the left part, linuxconf compute the right part # IP number -> netmask, net num, broacast IP number + netmask -> net num broadcast IP number + netmask + net num -> broadcast # */ ret = 0; char def_msk[20]; device_setstdnetmask(nums.hst,def_msk); bool ok_ip = strcmp(nums.hst,"0.0.0.0") == 0 || ipnum_validip(nums.hst,true); bool ok_msk = true; bool ok_net = true; bool ok_bcast = true; if (m_ok == -1){ strcpy (nums.msk,def_msk); if (n_ok != -1 && showerr){ xconf_error (MSG_U(E_NETMASKNEEDED ,"A netmask is needed for interface %s\n" "because you have supplied a network number") ,device); ok_msk = false; } } ok_msk &= ipnum_validip(nums.msk,false); unsigned long numip = ipnum_aip2l(nums.hst); unsigned long nummsk = ipnum_aip2l(nums.msk); unsigned long numnet = numip & nummsk; if (n_ok == -1){ ipnum_ip2a (numnet,nums.net); } else { numnet = ipnum_aip2l(nums.net); if ((numip & nummsk) != numnet){ ok_net = false; } } ok_net &= ipnum_validip(nums.net,false); if (b_ok == -1){ unsigned long n_nummsk = ~nummsk; unsigned long numbcast = nummsk == 0 ? 0 : numnet | (n_nummsk & 0xffffffffl); ipnum_ip2a (numbcast,nums.bcast); } else { unsigned long numbcast = ipnum_aip2l(nums.bcast); if ((numbcast & nummsk) != numnet){ ok_bcast = false; } } ok_bcast &= ipnum_validip(nums.bcast,false); if ((!ok_ip || !ok_net || !ok_msk || !ok_bcast) && showerr){ ret = -1; xconf_error (MSG_U(E_IVLIPINTER, "Invalid IP configuration for interface %s\n" "IP address\t%s\t%s\n" "Net address\t%s\t%s\n" "Netmask\t%s\t%s\n" "Broadcast\t%s\t%s\n") ,device ,nums.hst ,(ok_ip ? MSG_U(E_OK,"Ok") : MSG_U(E_INV,"Invalid")) ,nums.net ,(ok_net ? MSG_R(E_OK) : MSG_R(E_INV)) ,nums.msk ,(ok_msk ? MSG_R(E_OK) : MSG_R(E_INV)) ,nums.bcast ,(ok_bcast ? MSG_R(E_OK) : MSG_R(E_INV))); } } } return ret; } /* Load a network device module. Return -1 if any error */ int device_insmod (const char *devname) { return netconf_system_if ("modprobe",devname); } /* Initialise a network device and set the basic route to it. */ static int device_set ( INTER_INFO &itf, bool per_dev_hint, NETDEV_STATUS &status) { int ret = -1; const char *device = itf.device.get(); // Name of the device // Is the device configured in the kernel ? bool dev_exist = devlist_devexist(device); IFACE_NUMS nums; bool ip_needed = itf.confmode == INTER_BOOTP || itf.confmode == INTER_DHCP;; if (device_ifaceinfo (device,itf,nums ,itf.confmode == INTER_MANUAL) != -1) ip_needed = true; if (ip_needed){ /* #Specification: netconf / devices / modules If a device (eth?) is not available in the kernel linuxconf does a "modprobe device" to load the module. */ if (!dev_exist && !itf.pcmcia){ if (per_dev_hint) net_hint ("INSMOD",device); dev_exist = device_insmod (device) != -1; } if (dev_exist){ ret = 0; if (itf.confmode != INTER_MANUAL){ if (itf.confmode == INTER_BOOTP){ ret = bootp_probe (itf); }else if (itf.confmode == INTER_DHCP){ IFCONFIG_INFO info; if (ifconfig_getinfo (device,info)==-1 || (info.flags & IFF_UP) == 0){ status.up.append (device); status.up.append (" "); if (per_dev_hint){ net_hint ("ACTION","up"); } } ret = dhcp_probe (itf); } if (ret == 0 && itf.confmode != INTER_DHCP){ ret = device_ifaceinfo (device,itf,nums ,!simul_ison()); } } if (ret == 0 && itf.confmode != INTER_DHCP){ ret = devices_ifconfig(device,nums.hst,nums.msk,nums.bcast ,per_dev_hint,status); if (ret == 0){ char cmd[300]; char gateway[PATH_MAX]; int statusnet = route_isactive(nums.net,gateway); if (statusnet && strcmp(gateway,device)!=0){ snprintf (cmd,sizeof(cmd)-1,"del -net %s",nums.net); ret = netconf_system_if ("route",cmd); statusnet = 0; } if (!statusnet){ snprintf (cmd,sizeof(cmd)-1,"add -net %s %s",nums.net,device); ret = netconf_system_if ("route",cmd); } } } }else if (!itf.pcmcia){ xconf_error (MSG_U(E_NODEVICE ,"Networking was configured\n" "for the interface %s\n" "but this device is not configured\n" "in the kernel\n") ,device); } }else if (!dev_exist){ // No configuration (IP or IPX) for this device // but it is not there anyway ret = 0; } return ret; } /* Configure the main communication devices (ethernet) and set the basic route. Return -1 if any error. If dev != NULL, only this device is configured */ int netconf_setdevices( const char *dev, SSTRINGS &reconf) // Will contain the list of device to reconfigure // (Usefule for hint mode) { /* #Specification: network devices / lookup netconf finds out which permanent network devices do exist by reading /proc/net/dev. Slip devices are ignored because they are not managed properply by netconf right now. In fact, netconf deal only with ETHx devices. */ int ret = 0; HOSTINFO info; bool doneone = false; NETDEV_STATUS status (reconf); if (dev == NULL || strcmp(dev,"lo")==0){ netconf_setloopback(status,dev != NULL); } if (netconf_loadinfos(info) != -1){ for (int i=0; idevice.get(); if (pti->enable && simul_ishint() && !devlist_devexist(ptdev)){ /* #Specification: hinting / netdev / no device When doing the hinting for network devices, we check if the device is available in the kernel. If not, we check if the device is configured in /etc/modules.conf. If so, we output a line to say that this device must be configured. A network driver may be of 3 types Linked in the kernel. Compiled as a module, specified in /etc/modules.conf. Compiled as a module, as a PCMCIA device. So a device is either there, or configured in modules.conf or else, we don't care. A PCMCIA will configure on the fly. */ SSTRING kdev,io,irq; modules_getsetting (ptdev,kdev,io,irq); if (!kdev.is_empty()){ status.up.append (ptdev); status.up.append (" "); } continue; } if (dev == NULL || strcmp(ptdev,dev)==0){ doneone = true; if (pti->enable){ ret |= device_set (*pti,dev != NULL,status); }else{ IFCONFIG_INFO info; int ret = ifconfig_getinfo (ptdev,info); if (ret != -1 && (info.flags & IFF_UP) != 0){ char cmd[100]; snprintf (cmd,sizeof(cmd)-1,"%s down",ptdev); netconf_system_if ("ifconfig",cmd); status.down.append (ptdev); status.down.append (" "); if (dev != NULL) net_hint ("ACTION","down"); }else if (dev != NULL){ net_hint ("ACTION",""); } } } } } if (dev != NULL){ if (!doneone) ret = -1; }else{ net_hint ("DEV_UP",status.up.get()); // net_hint ("DEV_RECONF",status.reconf.get()); // DEV_RECONF is merged with IPX reconf output in start.cc net_hint ("DEV_DOWN",status.down.get()); } return ret; } /* Locate the device associate with a network/netmask pair or a host Return -1 if no device matches. */ static int netconf_finddevfrom( const char *hostip, // Host IP number or NULL const char *network, // Network/netmask or NULL const char *netmask, char *dev) { dev[0] = '\0'; int ret = -1; HOSTINFO info; unsigned long hostipl = hostip == NULL ? 0 : ipnum_aip2l(hostip); if (netconf_loadinfos(info) != -1){ for (int i=0; ienable && device_ifaceinfo ("",*pti,nums,false)!=-1){ if (network != NULL){ if (strcmp(network,nums.net)==0 && strcmp(netmask,nums.msk)==0){ pti->device.copy (dev); ret = 0; break; } }else{ unsigned long net = ipnum_aip2l (nums.net); unsigned long msk = ipnum_aip2l (nums.msk); if ((hostipl & msk) == net){ pti->device.copy (dev); ret = 0; break; } } } } } return ret; } int netconf_finddevfromnet( const char *network, const char *netmask, char *dev) { return netconf_finddevfrom (NULL,network,netmask,dev); } /* Locate the device associate with a host Return -1 if no device matches. */ int netconf_finddevfromhost( const char *hostip, char *dev) { int ret = -1; if (strcmp(hostip,"127.0.0.1")==0){ strcpy (dev,"lo"); ret = 0; }else{ ret = netconf_finddevfrom (hostip,NULL,NULL,dev); } return ret; } /* Compute the default netmask for an IP address */ void netconf_computemsk (const char ip[], char msk[]) { int num4[4]; ipnum_aip24 (ip,num4); if (num4[3] != 0){ strcpy (msk,"255.255.255.255"); }else{ device_setstdnetmask(ip,msk); } } /* try to convert something into an IP number Return -1 if it can't. */ int netconf_convert ( const char *str, // An IP number, a host name or an interface // name (eth0) char ip[], char msk[]) // Corresponding (computed) netmask // or extracted from the device { int ret = 0; struct hostent *hent; struct netent *nent; IFCONFIG_INFO info; const char *pt = str; // gethostbyname accepts invalid ipv4 quad dot notation and translates // them (2.2.2 -> 2.2.0.2) oddly // So we make sure we have something like a IP number // If this is the case, this is further tested using validip bool isdot = true; while (*pt != '\0'){ if (!isdigit(*pt) && *pt != '.'){ isdot = false; break; } pt++; } if (isdot){ if (ipnum_validip(str,false)){ strcpy (ip,str); netconf_computemsk (str,msk); }else{ ret = -1; } }else if (ifconfig_getinfo(str,info)!=-1){ strcpy (ip,info.ip_addr); strcpy (msk,info.netmask); }else if ((hent = gethostbyname(str)) != NULL){ ipnum_ip2a (hent,ip); netconf_computemsk (ip,msk); }else if ((nent = getnetbyname(str)) != NULL){ ipnum_ip2a (nent,ip); netconf_computemsk (ip,msk); }else{ ret = -1; } return ret; }