Tagging module util-vserver - util-vserver-0.30.215-6
[util-vserver.git] / src / naddress.c
1 // $Id: naddress.c 2668 2008-01-21 14:13:52Z dhozac $
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