#include #include #include #include #include #include #include #include "netconf.h" #include "hostinfo.h" #include #include "internal.h" #include #include #include "../paths.h" #include "netconf.m" #include #include #include static NETCONF_HELP_FILE help_aliases ("ip_aliases"); static CONFIG_FILE f_aliases (PROC_NET_ALIASES ,help_aliases ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE f_alias_types (PROC_NET_ALIAS_TYPES ,help_aliases ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE f_max_aliases (PROC_SYS_NET_MAX_ALIAS ,help_aliases ,CONFIGF_OPTIONAL|CONFIGF_MANAGED); PRIVATE void IP_ALIAS::init (const char *_id, const char *_ip, const char *_mask) { id.setfrom (_id); ip.setfrom (_ip); mask.setfrom (_mask[0] == '\0' ? "255.255.255.255" : _mask); } /* #Specification: netconf / aliases / strategy linuxconf will automaticly manage/assign/remove all ip_alias based on the definitions it received. It will select by itself the alias number (ethx:NUMBER) by monitoring the currently allocated ones. */ PUBLIC IP_ALIAS::IP_ALIAS(const char *_id, const char *_ip, const char *_mask) { init (_id,_ip,_mask); } PUBLIC IP_ALIAS::IP_ALIAS(const char *_ip, const char *_mask) { init ("",_ip,_mask); } PUBLIC int IP_ALIAS::unset (const char *devname) { char cmd[100],devali[20]; snprintf (devali,sizeof(devali)-1,"%s:%s",devname,id.get()); if (kernel_newer (2,2,0)){ snprintf (cmd,sizeof(cmd)-1,"%s down",devali); }else{ snprintf (cmd,sizeof(cmd)-1,"%s- 0.0.0.0",devali); } net_printhint ("del %s\n",devali); return netconf_system_if ("ifconfig",cmd); } /* Return the numeric ID of an alias (eth0:N -> 0). Return -1 if the ID is not numerical */ PUBLIC int IP_ALIAS::getnumid() { return hastextid() ? -1 : id.getval(); } /* Return true if the IP alias has a textual (non numeric ID). */ PUBLIC bool IP_ALIAS::hastextid() { bool ret = false; const char *s = id.get(); if (*s == '\0'){ ret = true; }else{ while (isdigit(*s)) s++; if (*s != '\0') ret = true; } return ret; } PUBLIC int IP_ALIAS::set (int _num, const char *devname) { char cmd[100],bcast[LEN_IP_ASC]; { unsigned long nummsk = ipnum_aip2l(mask.get()); unsigned long numnet = ipnum_aip2l (ip.get()); numnet &= nummsk; unsigned long n_nummsk = ~nummsk; unsigned long numbcast = numnet | (n_nummsk & 0xffffffffl); ipnum_ip2a (numbcast,bcast); } snprintf (cmd,sizeof(cmd)-1,"%s:%d %s netmask %s broadcast %s",devname,_num,ip.get() ,mask.get(),bcast); int ret = netconf_system_if ("ifconfig",cmd); if (mask.is_empty()){ // To keep compatibility with old ifup-aliases from redhat-5.1 // when there were no netmask for IP aliases. snprintf (cmd,sizeof(cmd)-1,"%s:%d %s",devname,_num,ip.get()); }else{ snprintf (cmd,sizeof(cmd)-1,"%s:%d %s %s %s",devname,_num,ip.get() ,mask.get(),bcast); } net_printhint ("add %s\n",cmd); if (ret == 0){ snprintf (cmd,sizeof(cmd)-1,"add %s %s:%d",ip.get(),devname,_num); ret = netconf_system_if ("route",cmd); } return ret; } PUBLIC IP_ALIAS *IP_ALIASES::getitem(int no) { return (IP_ALIAS*)ARRAY::getitem(no); } /* Locate one alias from its IP number and netmask */ PUBLIC IP_ALIAS *IP_ALIASES::getitem(const char *ip, const char *mask) { IP_ALIAS *ret = NULL; int n = getnb(); for (int i=0; iip.cmp(ip)==0){ // A small optimisation. If we have found the IP, then // another entry with the same IP can't exist. But if the // mask does not fit, then we return NULL if (a->mask.cmp(mask)==0){ ret = a; } break; } } return ret; } /* Read the aliases currently configured for a device (eth0 for example) */ PUBLIC void IP_ALIASES::readproc (const char *devname) { int len = strlen(devname); FILE_CFG *fin = f_aliases.fopen ("r"); if (fin != NULL){ char buf[300]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ if (strncmp(buf,devname,len)==0 && buf[len] == ':'){ char id[100]; int family; char ip[100]; sscanf (buf+len+1,"%s %d %s",id,&family,ip); /* #Specification: netconf / aliases / 0.0.0.0 The file /proc/net/aliases contains entries with an IP number of 0.0.0.0. Theses entries are "downed" interface. I suspect they should be left out of the table. Anyway, they are ignored by linuxconf. */ if (strcmp(ip,"0.0.0.0")!=0){ char alidev[20]; snprintf (alidev,sizeof(alidev)-1,"%s:%s",devname,id); IFCONFIG_INFO info; if (ifconfig_getinfo_nocheck(alidev,info)!=-1){ add (new IP_ALIAS(id,ip,info.netmask)); } } } } fclose (fin); }else{ SSTRINGS list; int nb = devlist_read (list,false,true); for (int i=0; iget(); if (strncmp(devali,devname,len)==0 && devali[len] == ':'){ IFCONFIG_INFO info; if (ifconfig_getinfo_nocheck(devali,info)!=-1){ if (info.ip_addr[0] != '\0'){ add (new IP_ALIAS(devali+len+1,info.ip_addr,info.netmask)); } } } } } } /* Return the number of the last allocated alias numer (ethx:N) */ PUBLIC int IP_ALIASES::getmaxnum() { int ret = 0; int n = getnb(); for (int i=0; igetnumid(); if (num > ret) ret = num; } return ret; } /* Record the aliases definition. The function setup has to be called after setting all the definition */ PUBLIC int IP_ALIASES::setnew ( const char *, // devip, // Ip number of the device const char *ips, // A bunch of IP alias to allocate const char *mask, // Associated netmask (common to all ips in the range) char *err) // Collect error messages { /* #Specification: netconf / aliases / defining many The user interface of linuxconf allows you to define several IP aliases for a device. These aliases are generally used for virtual host httpd server. Many installation will required a fair amount of IP number, one for each domain they serve. To help setup those aliases, linuxconf do support different syntax. # -You can define menu IP number on a single line. -You can define a range of IP number like this x.y.z.a-b. This will effectivly define one alias per number from a to b. -You can use names (fqdn) to setup aliases. # */ /* #Specification: netconf / aliases / defining many / rejected syntax One syntax look useful at first. Here it is # -You can define an alias just by specifying the suffix of the ip number. For example, an ethernet device (eth0) may have the IP 192.168.1.1. By defining the alias 2 3 4 5, you are effectivly defining: eth0:1 192.168.1.2 eth0:2 192.168.1.3 eth0:3 192.168.1.4 eth0:4 192.168.1.5 -The abreviated syntax define in the previous paragraph also work with ranges. The previous example could be written 2-5. # It has been removed from linuxconf because it was too error prone. An incomplete IP number could turn into a strange alias. */ int ret = 0; while (1){ ips = str_skip(ips); if (*ips == '\0') break; char word[200]; ips = str_copyword (word,ips,sizeof(word)); if (isdigit (word[0])){ if (ipnum_validip(word,true)){ add (new IP_ALIAS (word,mask)); }else{ IPMAP map(word); if (map.setup()==-1){ strcat (err,MSG_U(E_RANGESYN,"Invalid range syntax")); strcat (err," "); strcat (err,word); strcat (err,"\n"); ret = -1; }else{ do{ add (new IP_ALIAS (map.getcur(),mask)); }while (map.next()!= -1); #if 0 // Old validation char *pt = strchr(word,'-'); int start=0,end=0; if (pt != NULL){ *pt++ = '\0'; end = atoi(pt); } char *first = strrchr(word,'.'); if (first != NULL){ *first++ = '\0'; start = atoi(first); }else{ start = atoi(word); word[0] = '\0'; } if (end < start) end = start; int tbdev[4]; ipalias_evalip(devip,tbdev); int tbspec[4]; int nbspec = ipalias_evalip(word,tbspec); memcpy (tbdev+(3-nbspec),tbspec,nbspec*sizeof(int)); for (int i=start; i<=end; i++){ snprintf (word,sizeof(word)-1,"%d.%d.%d.%d",tbdev[0],tbdev[1],tbdev[2],i); add (new IP_ALIAS (word,mask)); } #endif } } }else{ struct hostent *ent = gethostbyname(word); if (ent != NULL){ ipnum_ip2a (ent,word); add (new IP_ALIAS(word,mask)); }else{ strcat (err,MSG_U(E_UNKHOST,"Unknown host name")); strcat (err," "); strcat (err,word); strcat (err,"\n"); ret = -1; } } } return ret; } static bool ipalias_available() { bool ret = false; if (kernel_newer(2,1,0)){ /* #Specification: IP aliases / checking availability / kernel 2.1 On kernel 2.1 and above, I do not know how to tell if the IP aliases are available. For now, I assume this is always true. As far as I understand, aliasing is not modular anymore and can be activated on a per device basis. */ ret = true; }else{ /* #Specification: IP aliases / checking availability / kernel 2.0 Linuxconf checks if the kernel supports IP aliases by opening the file /proc/net/alias_types. If the file is missing then, aliasing is not supported in the kernel. If the file is there, it looks for a line starting with the ID AF_INET (a number). If it is not there, it calls modprobe to load the module ip_alias. */ FILE_CFG *fin = f_alias_types.fopen("r"); if (fin == NULL){ xconf_error (MSG_U(E_NOALIAS,"No kernel support for devices aliasing")); }else{ char buf[300]; if (fgets(buf,sizeof(buf)-1,fin)!=NULL){ while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ int id = atoi(buf); if (id == AF_INET){ ret = 1; break; } } } fclose (fin); if (!ret){ ret = netconf_system_if ("modprobe","ip_alias") == 0; } } } return ret; } PUBLIC int IP_ALIASES::unsetall (const char *devname) { int ret = 0; int n = getnb(); for (int i=0; iunset (devname); } return ret; } /* Install the proper aliases in the kernel. Cleanup the one which are not needed anymore. Return -1 if any errors. */ PUBLIC int IP_ALIASES::setup (const char *devname) { int max_kernel_aliases = 10000000; /* #Specification: IP aliases / maximum / kernel 2.2 On kernel older than 2.2, linuxconf checks the maximum number of aliases and reconfigure the kernel accordingly. On Linux 2.2, there is no limit. Nothing to do for linuxconf. */ if (!kernel_newer (2,2,0)){ max_kernel_aliases = 256; FILE_CFG *fin = f_max_aliases.fopen ("r"); if (fin != NULL){ fscanf (fin,"%d\n",&max_kernel_aliases); fclose (fin); } } IP_ALIASES proc; proc.readproc(devname); int maxnum = proc.getmaxnum(); int n = getnb(); int len = maxnum+n+1; int ret = -1; char lookup[len]; memset (lookup,0,sizeof(char)*len); IP_ALIASES toadd; // Will contain the items that must be added toadd.neverdelete(); int i; for (i=0; iip.get(),a->mask.get()); if (ap != NULL){ // Alias is already installed lookup[ap->getnumid()] = 1; }else{ toadd.add(a); } } ret = 0; n = proc.getnb(); for (i=0; ihastextid()){ IP_ALIAS *ap = getitem(a->ip.get(),a->mask.get()); if (ap == NULL){ // Alias is already installed and must be removed ret |= a->unset (devname); } } } n = toadd.getnb(); if (n > 0){ if (ipalias_available()){ int total_needed = n; for (i=0; i max_kernel_aliases){ /* #Specification: IP aliases / max number Kernel before 2.0.31 were limited to 256 aliases. With 2.0.31, they is a soft limit of 256 per devices. This limit may be changed by setting it in /proc/sys/net/core/net_alias_max. But this limit take action when the first alias is defined. If we later want to put more aliases than the current limit, we must remove all aliases definitions, change the limit and redo them. This is what linuxconf does. */ if (kernel_newer(2,0,30)){ int old_max = max_kernel_aliases; max_kernel_aliases = total_needed+100; if (maxnum > 0){ // There are already some aliases. They // must be removed net_prtlog (NETLOG_CMD,MSG_U(I_RESETFORNEW ,"Unsetting aliases to grow the kernel table from %d to %d entries\n") ,old_max,max_kernel_aliases); proc.unsetall(devname); proc.remove_all(); } if (simul_ishint()){ net_printhint ("reload %d\n",max_kernel_aliases); }else if (simul_ison()){ net_prtlog (NETLOG_CMD,MSG_U(I_NEWMAXALIASES ,"Raising kernel max aliases per device limit to %d\n") ,max_kernel_aliases); }else{ FILE_CFG *fout = f_max_aliases.fopen ("w"); if (fout != NULL){ max_kernel_aliases = total_needed+100; net_prtlog (NETLOG_CMD,MSG_R(I_NEWMAXALIASES) ,max_kernel_aliases); fprintf (fout,"%d\n",max_kernel_aliases); fclose (fout); }else{ max_kernel_aliases = old_max; } } } } if (total_needed > max_kernel_aliases){ xconf_error (MSG_U(E_TOOALIASES,"Too many IP aliases, maximum %d") ,max_kernel_aliases); }else{ int allocnum = 0; for (i=0; iset (allocnum,devname); allocnum++; } } }else{ ret = -1; } } return ret; } static char IPALIAS[]="ipalias"; static int (*ipalias_fct_load)(const char *devname, SSTRINGS &, SSTRINGS &); static int (*ipalias_fct_save)(const char *devname, const SSTRINGS &, const SSTRINGS &); void ipalias_sethook ( int (*fct_load)(const char *devname, SSTRINGS &, SSTRINGS &), int (*fct_save)(const char *devname, const SSTRINGS &, const SSTRINGS &)) { ipalias_fct_load = fct_load; ipalias_fct_save = fct_save; } static int ipalias_load( const char *devname, SSTRINGS &tbip, // IP aliases SSTRINGS &tbmasks) // Corresponding netmasks { SSTRINGS tb; int ret = linuxconf_getall (IPALIAS,devname,tb,1); if (ret == 0 && ipalias_fct_load != NULL){ ret = (*ipalias_fct_load)(devname,tbip,tbmasks); }else{ for (int i=0; iget(); int len = strlen(buf)+1; char buf1[len],buf2[len]; buf1[0] = buf2[0] = '\0'; sscanf (buf,"%s %s",buf1,buf2); tbip.add (new SSTRING (buf1)); tbmasks.add (new SSTRING (buf2)); } } return ret; } static int ipalias_save( const char *devname, const SSTRINGS &tbip, // IP aliases const SSTRINGS &tbmasks) // Corresponding netmasks { int ret = -1; if (ipalias_fct_save != NULL){ ret = (*ipalias_fct_save)(devname,tbip,tbmasks); if (ret != -1){ linuxconf_removeall (IPALIAS,devname); ret = linuxconf_save(); } }else{ SSTRINGS tb; for (int i=0; igetlen()+1+mk->getlen()+1]; snprintf (buf,sizeof(buf)-1,"%s %s",ip->get(),mk->get()); tb.add (new SSTRING(buf)); } linuxconf_replace (IPALIAS,devname,tb); ret = linuxconf_save(); } return ret; } int ipalias_setup(const char *devname) { int ret = -1; IFCONFIG_INFO info; if (ifconfig_getinfo(devname,info)!=-1){ IP_ALIASES alias; SSTRINGS ips,masks; ipalias_load (devname,ips,masks); ret = 0; char err[10000]; err[0] = '\0'; for (int i=0; iget(),m->get(),err); } if (ret == 0){ ret = alias.setup(devname); }else if (err[0] != '\0'){ xconf_error (MSG_U(E_SETALIAS ,"Invalid alias setup for device %s\n%s") ,devname,err); } } return ret; } /* Install the aliases for all devices */ int ipalias_setup () { int ret = -1; SSTRINGS list; HOSTS hosts; NETWORKS networks; HOSTINFO info; if (netconf_loadinfos(hosts,networks,info) != -1 && devlist_read (list) != -1){ SSTRING devreconf; int n = list.getnb(); ret = 0; for (int i=0; iget(); INTER_INFO *ptdev = info.a; // We must make sure the device is enabled // If the device is not configured through linuxconf, then // we don't know if it is enable or not, so we give it a try bool ok = true; for (int j=0; jdevice.cmp(dev)==0){ ok = ptdev->enable != 0; break; } } if (ok){ ret |= ipalias_setup (dev); if (net_washints()){ if (!devreconf.is_empty()) devreconf.append (" "); devreconf.append (dev); } } net_enableprinthint (true); } net_hint ("DEV_RECONF_ALIASES",devreconf.get()); } return ret; } static void alias_checkspace ( const SSTRINGS &tb, int entry, int line, SSTRING &msg, int &new_nof) { const char *p = tb.getitem(entry)->get(); if (strchr(p,' ')!=NULL){ msg.appendf (MSG_U(E_NOSPACE,"No space allowed in line %d: %s\n") ,line,p); if (new_nof != -1) new_nof = line; } } static void alias_edit(const char *devname) { SSTRINGS ips,masks; ipalias_load (devname,ips,masks); for (int i=0; i<2; i++){ ips.add(new SSTRING); masks.add(new SSTRING); } int nof = 0; DIALOG dia; for (int i=0; iis_filled()){ SSTRING *m = masks.getitem(i); IP_ALIASES als; char err[10000]; err[0] = '\0'; if (als.setnew(info.ip_addr,s->get(),m->get() ,err)==-1){ xconf_error ("%s",err); nof = i; one_err = true; break; } } } } if (!one_err){ linuxconf_setcursys (subsys_stationid); for (int i=0; iis_empty()){ ips.remove_del (s); masks.remove_del (i); i--; } } ipalias_save (devname,ips,masks); break; } } } } } void alias_edit() { SSTRINGS lst; if (devlist_read(lst)!=-1){ int sel = 0; DIALOG_RECORDS dia; int n = lst.getnb(); lst.sort(); dia.newf_head ("",MSG_U(H_NETDEV,"Network device")); for (int i=0; iget(),""); } while (1){ MENU_STATUS code = dia.editmenu ( MSG_U(T_IPALIASES,"Edit IP aliases configurations") ,MSG_U(I_SYSTEMS,"Each network device may have several\n" "IP number. Alternate ones are called alias and are\n" "entered here.\n") ,help_aliases ,sel,0); if (code == MENU_OK){ if (n > 0){ SSTRING *s = lst.getitem(sel); alias_edit (s->get()); } }else if (code == MENU_ESCAPE || code == MENU_QUIT){ break; } } } }