vshost-util-vserver

Build script and sources for util-vserver.
git clone https://ccx.te2000.cz/git/vshost-util-vserver
Log | Files | Refs

naddress.c (13661B)


      1 // $Id$
      2 
      3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
      4 // Copyright (C) 2006 Daniel Hokka Zakrisson <daniel@hozac.com>
      5 // based on chbind.cc by Jacques Gelinas
      6 //  
      7 // This program is free software; you can redistribute it and/or modify
      8 // it under the terms of the GNU General Public License as published by
      9 // the Free Software Foundation; either version 2, or (at your option)
     10 // any later version.
     11 //  
     12 // This program is distributed in the hope that it will be useful,
     13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 // GNU General Public License for more details.
     16 //  
     17 // You should have received a copy of the GNU General Public License
     18 // along with this program; if not, write to the Free Software
     19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     20 
     21 #ifdef HAVE_CONFIG_H
     22 #  include <config.h>
     23 #endif
     24 
     25 #include "vserver.h"
     26 #include "util.h"
     27 
     28 #include <lib/internal.h>
     29 
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <netdb.h>
     34 #include <sys/socket.h>
     35 #include <sys/ioctl.h>
     36 #include <net/if.h>
     37 #include <unistd.h>
     38 #include <errno.h>
     39 #include <getopt.h>
     40 #include <fcntl.h>
     41 #include <netinet/in.h>
     42 #include <arpa/inet.h>
     43 
     44 #define ENSC_WRAPPERS_PREFIX	"naddress: "
     45 #define ENSC_WRAPPERS_IO	1
     46 #define ENSC_WRAPPERS_UNISTD	1
     47 #define ENSC_WRAPPERS_VSERVER	1
     48 #include "wrappers.h"
     49 
     50 #define CMD_HELP	0x1000
     51 #define CMD_VERSION	0x1001
     52 
     53 #define CMD_SILENT	0x2000
     54 #define CMD_NID		0x2001
     55 #define CMD_ADD		0x2002
     56 #define CMD_REMOVE	0x2003
     57 #define CMD_SET		0x2004
     58 #define CMD_IP		0x2010
     59 #define CMD_BCAST	0x2011
     60 #define CMD_MASK	0x2012
     61 #define CMD_RANGE	0x2013
     62 #define CMD_LBACK	0x2014
     63 
     64 int wrapper_exit_code = 255;
     65 
     66 
     67 static struct option const
     68 CMDLINE_OPTIONS[] = {
     69   { "help",     no_argument,  0, CMD_HELP },
     70   { "version",  no_argument,  0, CMD_VERSION },
     71   { "silent",   no_argument,  0, CMD_SILENT },
     72   { "add",      no_argument,  0, CMD_ADD },
     73   { "remove",   no_argument,  0, CMD_REMOVE },
     74   { "set",      no_argument,  0, CMD_SET },
     75   { "nid",      required_argument, 0, CMD_NID },
     76   { "ip",       required_argument, 0, CMD_IP },
     77   { "mask",     required_argument, 0, CMD_MASK },
     78   { "range",    required_argument, 0, CMD_RANGE },
     79   { "bcast",    required_argument, 0, CMD_BCAST },
     80   { "lback",    required_argument, 0, CMD_LBACK },
     81   { 0,0,0,0 }
     82 };
     83 
     84 struct vc_ips {
     85   struct vc_net_addr a;
     86   struct vc_ips *next;
     87 };
     88 
     89 struct Arguments {
     90   nid_t		nid;
     91   struct vc_ips	head;
     92   bool		is_silent;
     93   bool		do_add;
     94   bool		do_remove;
     95   bool		do_set;
     96 };
     97 
     98 static void
     99 showHelp(int fd, char const *cmd, int res)
    100 {
    101   WRITE_MSG(fd, "Usage:\n  ");
    102   WRITE_STR(fd, cmd);
    103   WRITE_MSG(fd,
    104 	    " (--add|--remove|--set) [--silent] [--nid <nid>]\n"
    105 	    "    [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
    106 	    "Please report bugs to " PACKAGE_BUGREPORT "\n");
    107 
    108   exit(res);
    109 }
    110 
    111 static void
    112 showVersion()
    113 {
    114   WRITE_MSG(1,
    115 	    "naddress " VERSION " -- bind to an ip and execute a program\n"
    116 	    "This program is part of " PACKAGE_STRING "\n\n"
    117 	    "Copyright (C) 2003,2004 Enrico Scholz\n"
    118 	    "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
    119 	    VERSION_COPYRIGHT_DISCLAIMER);
    120   exit(0);
    121 }
    122 
    123 /*
    124 	Check if a network device exist in /proc/net/dev.
    125 	This is used because ifconfig_ioctl triggers modprobe if requesting
    126 	information about non existant devices.
    127 
    128 	Return != 0 if the device exist.
    129 */
    130 static bool
    131 existsDevice(char const *dev_raw)
    132 {
    133   size_t	buf_size=8192;
    134   char		dev[strlen(dev_raw)+2];
    135 
    136   strcpy(dev, dev_raw);
    137   strcat(dev, ":");
    138   for (;;) {
    139     char	buf[buf_size];
    140     char *	pos;
    141     bool	too_small;
    142     int		fd=open("/proc/net/dev", O_RDONLY);
    143     
    144     if (fd==-1) return false;
    145     too_small = EreadAll(fd, buf, buf_size);
    146     close(fd);
    147 
    148     if (too_small) {
    149       buf_size *= 2;
    150       continue;
    151     }
    152 
    153     pos = strstr(buf, dev);
    154     return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
    155   }
    156 }
    157 
    158 static int ifconfig_ioctl(
    159 	int fd,
    160 	const char *ifname,
    161 	int cmd,
    162 	struct ifreq *ifr)
    163 {
    164 	strcpy(ifr->ifr_name, ifname);
    165 	return ioctl(fd, cmd, ifr);
    166 }
    167 
    168 /*
    169 	Fetch the IP number of an interface from the kernel.
    170 	Assume the device is already available in the kernel
    171 	Return -1 if any error.
    172 */
    173 int ifconfig_getaddr (
    174 	const char *ifname,
    175 	uint32_t *addr,
    176 	uint32_t *mask,
    177 	uint32_t *bcast)
    178 {
    179 	int ret = -1;
    180 	if (existsDevice(ifname)){
    181 		int skfd = socket(AF_INET, SOCK_DGRAM, 0);
    182 		if (skfd != -1){
    183 			struct ifreq ifr;
    184 			if (addr != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
    185 				struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
    186 				*addr = sin->sin_addr.s_addr;
    187 				ret = 0;
    188 			}
    189 			if (mask != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
    190 				struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
    191 				*mask = sin->sin_addr.s_addr;
    192 				ret = 0;
    193 			}
    194 			if (bcast != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
    195 				struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
    196 				*bcast = sin->sin_addr.s_addr;
    197 				ret = 0;
    198 			}
    199 			close (skfd);
    200 		}
    201 	}
    202 	return ret;
    203 }
    204 
    205 static int
    206 convertAddress(const char *str, uint16_t *type, void *dst)
    207 {
    208   int	ret;
    209   if (type) *type = VC_NXA_TYPE_IPV4;
    210   ret = inet_pton(AF_INET, str, dst);
    211   if (ret==0) {
    212     if (type) *type = VC_NXA_TYPE_IPV6;
    213     ret = inet_pton(AF_INET6, str, dst);
    214   }
    215   return ret > 0 ? 0 : -1;
    216 }
    217 
    218 static void
    219 ipv6PrefixToMask(struct in6_addr *mask, int prefix)
    220 {
    221   int i;
    222   mask->s6_addr32[0] = mask->s6_addr32[1] = mask->s6_addr32[2] = mask->s6_addr32[3] = 0;
    223   for (i = 0; (i << 3) < prefix; i++) {
    224     mask->s6_addr[i] = 0xff;
    225   }
    226   if ((i << 3) > prefix)
    227     mask->s6_addr[i-1] = ~((1 << (prefix & 0x07)) - 1);
    228 }
    229 
    230 static int
    231 maskToPrefix(void *data, int limit)
    232 {
    233   uint8_t *mask = data;
    234   int prefix;
    235   for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
    236     ;
    237   return prefix;
    238 }
    239 
    240 static int
    241 parseIPFormat(char const *str_c, struct vc_ips **ips,
    242 	     char const *format)
    243 {
    244   size_t len = strlen(str_c);
    245   char const *fc;
    246   char str[len + 1], *ptr = str;
    247   int ret = 0;
    248 
    249   strcpy(str, str_c);
    250 
    251   /* XXX: condition at the bottom */
    252   for (fc = format; ; fc += 2) {
    253     char *sep;
    254     void *dst;
    255     unsigned long limit = 0;
    256 
    257     switch (*fc) {
    258       case '1': dst = &(*ips)->a.s.ip;		break;
    259       case '2': dst = &(*ips)->a.s.ip2;		break;
    260       case 'm':	dst = &(*ips)->a.s.mask;	break;
    261       default:					goto out;
    262     }
    263 
    264     if (len == 0)
    265       goto out;
    266     if ((sep = memchr(ptr, *(fc + 1), len)) == NULL)
    267       sep = ptr + len;
    268     *sep = '\0';
    269 
    270     /* This is ugly, and means that m cannot be first */
    271     switch ((*ips)->a.vna_type) {
    272       case VC_NXA_TYPE_IPV4: limit =  32;	break;
    273       case VC_NXA_TYPE_IPV6: limit = 128;	break;
    274     }
    275 
    276     /* This is required due to the dain-bramage that is inet_pton in dietlibc.
    277      * Essentially any number will be parsed as a valid IPv4 address...
    278      */
    279     if (*fc == 'm' && strchr(ptr, ':') == NULL && strchr(ptr, '.') == NULL) {
    280       /* This is a prefix, not a netmask */
    281       unsigned long	sz;
    282 
    283       if (!isNumberUnsigned(ptr, &sz, true) || sz > limit) {
    284 	ret = -1;
    285 	goto out;
    286       }
    287 
    288       (*ips)->a.vna_prefix = sz;
    289       switch ((*ips)->a.vna_type) {
    290 	case VC_NXA_TYPE_IPV4:
    291 	  (*ips)->a.vna_v4_mask.s_addr = htonl(~((1 << (32 - sz)) - 1));
    292 	  break;
    293 	case VC_NXA_TYPE_IPV6:
    294 	  ipv6PrefixToMask(&(*ips)->a.vna_v6_mask, sz);
    295 	  break;
    296       }
    297     }
    298     else {
    299       if (convertAddress(ptr, &(*ips)->a.vna_type, dst) == -1) {
    300 	ret = -1;
    301 	goto out;
    302       }
    303       else if (*fc == 'm') {
    304 	/* Got a mask, set the prefix */
    305 	(*ips)->a.vna_prefix = maskToPrefix(&(*ips)->a.s.mask, limit);
    306       }
    307     }
    308 
    309     ret++;
    310     len -= (sep - ptr);
    311     ptr = sep + 1;
    312 
    313     if (*(fc + 1) == '\0')
    314       break;
    315   }
    316 
    317 out:
    318   return ret;
    319 }
    320 
    321 static void
    322 readIP(char const *str, struct vc_ips **ips, uint16_t type)
    323 {
    324   if (ifconfig_getaddr(str, &(*ips)->a.vna_v4_ip.s_addr, &(*ips)->a.vna_v4_mask.s_addr, NULL)==-1) {
    325     if (parseIPFormat(str, ips, "1/m") < 1) {
    326       WRITE_MSG(2, "Invalid IP number '");
    327       WRITE_STR(2, str);
    328       WRITE_MSG(2, "'\n");
    329       exit(wrapper_exit_code);
    330     }
    331   }
    332   else
    333     (*ips)->a.vna_type = VC_NXA_TYPE_IPV4;
    334   (*ips)->a.vna_type |= type;
    335 
    336   (*ips)->next = calloc(1, sizeof(struct vc_ips));
    337   *ips = (*ips)->next;
    338 }
    339 
    340 static void
    341 readBcast(char const *str, struct vc_ips **ips)
    342 {
    343   uint32_t bcast;
    344   if (ifconfig_getaddr(str, NULL, NULL, &bcast)==-1){
    345     if (inet_pton(AF_INET, str, &bcast) < 0) {
    346       WRITE_MSG(2, "Invalid broadcast number '");
    347       WRITE_STR(2, optarg);
    348       WRITE_MSG(2, "'\n");
    349       exit(wrapper_exit_code);
    350     }
    351   }
    352   (*ips)->a.vna_v4_ip.s_addr = bcast;
    353   (*ips)->a.vna_type = VC_NXA_TYPE_IPV4 | VC_NXA_MOD_BCAST | VC_NXA_TYPE_ADDR;
    354   (*ips)->next = calloc(1, sizeof(struct vc_ips));
    355   *ips = (*ips)->next;
    356 }
    357 
    358 static void
    359 readRange(char const *str, struct vc_ips **ips)
    360 {
    361   if (parseIPFormat(str, ips, "1-2/m") < 2) {
    362     WRITE_MSG(2, "Invalid range '");
    363     WRITE_STR(2, str);
    364     WRITE_MSG(2, "'\n");
    365     exit(wrapper_exit_code);
    366   }
    367   (*ips)->a.vna_type |= VC_NXA_TYPE_RANGE;
    368   (*ips)->next = calloc(1, sizeof(struct vc_ips));
    369   *ips = (*ips)->next;
    370 }
    371 
    372 static void
    373 tellAddress(struct vc_net_addr *addr, bool silent)
    374 {
    375   char buf[41];
    376   int af;
    377   void *address;
    378   if (silent)
    379     return;
    380   switch (addr->vna_type & (VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_IPV6)) {
    381     case VC_NXA_TYPE_IPV4:
    382       af = AF_INET;
    383       address = &addr->vna_v4_ip.s_addr;
    384       break;
    385     case VC_NXA_TYPE_IPV6:
    386       af = AF_INET6;
    387       address = addr->vna_v6_ip.s6_addr32;
    388       break;
    389     default:
    390       WRITE_MSG(1, " <unknown address type>");
    391       return;
    392   }
    393   if (inet_ntop(af, address, buf, sizeof(buf)) == NULL) {
    394     WRITE_MSG(1, " <conversion failed>");
    395     return;
    396   }
    397   WRITE_MSG(1, " ");
    398   WRITE_STR(1, buf);
    399 }
    400 
    401 static inline void
    402 doit(struct Arguments *args)
    403 {
    404   struct vc_ips *ips;
    405 
    406   if (args->do_set) {
    407     struct vc_net_addr remove = { .vna_type = VC_NXA_TYPE_ANY };
    408     if (vc_net_remove(args->nid, &remove) == -1) {
    409       perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
    410       exit(wrapper_exit_code);
    411     }
    412   }
    413 
    414   if (args->do_add || args->do_set) {
    415     if (!args->is_silent)
    416       WRITE_MSG(1, "Adding");
    417     for (ips = &args->head; ips->next; ips = ips->next) {
    418       tellAddress(&ips->a, args->is_silent);
    419       if (vc_net_add(args->nid, &ips->a) == -1) {
    420 	if (!args->is_silent)
    421 	  WRITE_MSG(1, "\n");
    422 	perror(ENSC_WRAPPERS_PREFIX "vc_net_add()");
    423 	exit(wrapper_exit_code);
    424       }
    425     }
    426     if (!args->is_silent)
    427       WRITE_MSG(1, "\n");
    428   }
    429   else if (args->do_remove) {
    430     if (!args->is_silent)
    431       WRITE_MSG(1, "Removing");
    432     for (ips = &args->head; ips->next; ips = ips->next) {
    433       tellAddress(&ips->a, args->is_silent);
    434       if (vc_net_remove(args->nid, &ips->a) == -1) {
    435 	if (!args->is_silent)
    436 	  WRITE_MSG(1, "\n");
    437 	perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
    438 	exit(wrapper_exit_code);
    439       }
    440     }
    441     if (!args->is_silent)
    442       WRITE_MSG(1, "\n");
    443   }
    444 }
    445 
    446 int main (int argc, char *argv[])
    447 {
    448   struct Arguments args = {
    449     .nid	= VC_NOCTX,
    450     .is_silent	= false,
    451     .do_add	= false,
    452     .do_remove	= false,
    453     .do_set	= false,
    454     .head	= { .next = NULL },
    455   };
    456   struct vc_ips *ips = &args.head;
    457   
    458   while (1) {
    459     int		c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
    460     if (c==-1) break;
    461 
    462     switch (c) {
    463       case CMD_HELP	:  showHelp(1, argv[0], 0);
    464       case CMD_VERSION	:  showVersion();
    465       case CMD_SILENT	:  args.is_silent = true; break;
    466       case CMD_NID	:  args.nid       = Evc_nidopt2nid(optarg,true); break;
    467       case CMD_ADD	:  args.do_add    = true; break;
    468       case CMD_REMOVE	:  args.do_remove = true; break;
    469       case CMD_SET	:  args.do_set    = true; break;
    470       case CMD_IP	:  readIP(optarg, &ips, VC_NXA_TYPE_ADDR);  break;
    471       case CMD_MASK	:  readIP(optarg, &ips, VC_NXA_TYPE_MASK);  break;
    472       case CMD_RANGE	:  readRange(optarg, &ips); break;
    473       case CMD_BCAST	:  readBcast(optarg, &ips); break;
    474       case CMD_LBACK	:  readIP(optarg, &ips, VC_NXA_TYPE_ADDR | VC_NXA_MOD_LBACK); break;
    475       default		:
    476 	WRITE_MSG(2, "Try '");
    477 	WRITE_STR(2, argv[0]);
    478 	WRITE_MSG(2, " --help' for more information.\n");
    479 	exit(wrapper_exit_code);
    480 	break;
    481     }
    482   }
    483 
    484   if (args.nid == VC_NOCTX) args.nid = Evc_get_task_nid(0);
    485 
    486   if (!args.do_add && !args.do_remove && !args.do_set) {
    487     WRITE_MSG(2, "No operation specified; try '--help' for more information\n");
    488     exit(wrapper_exit_code);
    489   }
    490   else if (((args.do_add ? 1 : 0) + (args.do_remove ? 1 : 0) + (args.do_set ? 1 : 0)) > 1) {
    491     WRITE_MSG(2, "Multiple operations specified; try '--help' for more information\n");
    492     exit(wrapper_exit_code);
    493   }
    494 
    495   doit(&args);
    496 
    497   if (optind != argc)
    498     Eexecvp (argv[optind],argv+optind);
    499   return EXIT_SUCCESS;
    500 }
    501 
    502 #ifdef ENSC_TESTSUITE
    503 #include <assert.h>
    504 
    505 void test()
    506 {
    507   struct vc_ip_mask_pair	ip;
    508   uint32_t			bcast;
    509 
    510   bcast = 0;
    511   readIP("1.2.3.4", &ip, &bcast);
    512   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
    513 
    514   readIP("1.2.3.4/8", &ip, &bcast);
    515   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
    516 
    517   readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
    518   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
    519 
    520   readIP("localhost", &ip, &bcast);
    521   assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
    522 
    523 #if 0
    524   if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
    525     readIP("lo", &ip, &bcast);
    526     assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));
    527   }
    528 #endif
    529 }
    530 #endif