Break up the old chbind into ncontext, nattribute, and naddress.
[util-vserver.git] / src / naddress.c
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_IP          0x2010
58 #define CMD_BCAST       0x2011
59
60 int wrapper_exit_code = 255;
61
62
63 static struct option const
64 CMDLINE_OPTIONS[] = {
65   { "help",     no_argument,  0, CMD_HELP },
66   { "version",  no_argument,  0, CMD_VERSION },
67   { "silent",   no_argument,  0, CMD_SILENT },
68   { "add",      no_argument,  0, CMD_ADD },
69   { "remove",   no_argument,  0, CMD_REMOVE },
70   { "nid",      required_argument, 0, CMD_NID },
71   { "ip",       required_argument, 0, CMD_IP },
72   { "bcast",    required_argument, 0, CMD_BCAST },
73   { 0,0,0,0 }
74 };
75
76 struct vc_ips {
77   struct vc_net_nx a;
78   struct vc_ips *next;
79 };
80
81 struct Arguments {
82   nid_t         nid;
83   struct vc_ips head;
84   bool          is_silent;
85   bool          do_add;
86   bool          do_remove;
87 };
88
89 static void
90 showHelp(int fd, char const *cmd, int res)
91 {
92   WRITE_MSG(fd, "Usage:\n  ");
93   WRITE_STR(fd, cmd);
94   WRITE_MSG(fd,
95             " [--silent] [--nid <nid>] [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
96             "Please report bugs to " PACKAGE_BUGREPORT "\n");
97
98   exit(res);
99 }
100
101 static void
102 showVersion()
103 {
104   WRITE_MSG(1,
105             "naddress " VERSION " -- bind to an ip and execute a program\n"
106             "This program is part of " PACKAGE_STRING "\n\n"
107             "Copyright (C) 2003,2004 Enrico Scholz\n"
108             "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
109             VERSION_COPYRIGHT_DISCLAIMER);
110   exit(0);
111 }
112
113 /*
114         Check if a network device exist in /proc/net/dev.
115         This is used because ifconfig_ioctl triggers modprobe if requesting
116         information about non existant devices.
117
118         Return != 0 if the device exist.
119 */
120 static bool
121 existsDevice(char const *dev_raw)
122 {
123   size_t        buf_size=8192;
124   char          dev[strlen(dev_raw)+2];
125
126   strcpy(dev, dev_raw);
127   strcat(dev, ":");
128   for (;;) {
129     char        buf[buf_size];
130     char *      pos;
131     bool        too_small;
132     int         fd=open("/proc/net/dev", O_RDONLY);
133     
134     if (fd==-1) return false;
135     too_small = EreadAll(fd, buf, buf_size);
136     close(fd);
137
138     if (too_small) {
139       buf_size *= 2;
140       continue;
141     }
142
143     pos = strstr(buf, dev);
144     return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
145   }
146 }
147
148 static int ifconfig_ioctl(
149         int fd,
150         const char *ifname,
151         int cmd,
152         struct ifreq *ifr)
153 {
154         strcpy(ifr->ifr_name, ifname);
155         return ioctl(fd, cmd, ifr);
156 }
157
158 /*
159         Fetch the IP number of an interface from the kernel.
160         Assume the device is already available in the kernel
161         Return -1 if any error.
162 */
163 int ifconfig_getaddr (
164         const char *ifname,
165         uint32_t *addr,
166         uint32_t *mask,
167         uint32_t *bcast)
168 {
169         int ret = -1;
170         if (existsDevice(ifname)){
171                 int skfd = socket(AF_INET, SOCK_DGRAM, 0);
172                 if (skfd != -1){
173                         struct ifreq ifr;
174                         if (addr != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
175                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
176                                 *addr = sin->sin_addr.s_addr;
177                                 ret = 0;
178                         }
179                         if (mask != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
180                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
181                                 *mask = sin->sin_addr.s_addr;
182                                 ret = 0;
183                         }
184                         if (bcast != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
185                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
186                                 *bcast = sin->sin_addr.s_addr;
187                                 ret = 0;
188                         }
189                         close (skfd);
190                 }
191         }
192         return ret;
193 }
194
195 static int
196 convertAddress(const char *str, vc_net_nx_type *type, void *dst)
197 {
198   int   ret;
199   if (type) *type = vcNET_IPV4;
200   ret = inet_pton(AF_INET, str, dst);
201   if (ret==0) {
202     if (type) *type = vcNET_IPV6;
203     ret = inet_pton(AF_INET6, str, dst);
204   }
205   return ret > 0 ? 0 : -1;
206 }
207
208 static void
209 readIP(char const *str, struct vc_ips **ips)
210 {
211   if (ifconfig_getaddr(str, &(*ips)->a.ip[0], &(*ips)->a.mask[0], NULL)==-1) {
212     char                *pt;
213     char                tmpopt[strlen(str)+1];
214     uint32_t            *mask = (*ips)->a.mask;
215
216     strcpy(tmpopt,str);
217     pt = strchr(tmpopt,'/');
218     if (pt)
219       *pt++ = '\0';
220
221     if (convertAddress(tmpopt, &(*ips)->a.type, (*ips)->a.ip) == -1) {
222       WRITE_MSG(2, "Invalid IP number '");
223       WRITE_STR(2, tmpopt);
224       WRITE_MSG(2, "'\n");
225       exit(wrapper_exit_code);
226     }
227
228     if (pt==0) {
229       switch ((*ips)->a.type) {
230         case vcNET_IPV4:
231           mask[0] = htonl(0xffffff00);
232           break;
233         case vcNET_IPV6:
234           mask[0] = 64;
235           break;
236         default: break;
237       }
238     }
239     else {
240       // Ok, we have a network size, not a netmask
241       if (strchr(pt,'.')==0 && strchr(pt,':')==0) {
242         int             sz = atoi(pt);
243         switch ((*ips)->a.type) {
244           case vcNET_IPV4:
245             mask[0] = htonl((1 << sz) - 1);
246             break;
247           case vcNET_IPV6:
248             mask[0] = sz;
249             break;
250           default: break;
251         }
252       }
253       else { 
254         if (convertAddress(pt, NULL, &(*ips)->a.mask) == -1) {
255           WRITE_MSG(2, "Invalid netmask '");
256           WRITE_STR(2, pt);
257           WRITE_MSG(2, "'\n");
258           exit(wrapper_exit_code);
259         }
260       }
261     }
262   }
263   else
264     (*ips)->a.type = vcNET_IPV4;
265
266   (*ips)->a.count = 1;
267   (*ips)->next = calloc(1, sizeof(struct vc_ips));
268   *ips = (*ips)->next;
269 }
270
271 static void
272 readBcast(char const *str, struct vc_ips **ips)
273 {
274   uint32_t bcast;
275   if (ifconfig_getaddr(str, NULL, NULL, &bcast)==-1){
276     if (convertAddress(str, NULL, &bcast) == -1) {
277       WRITE_MSG(2, "Invalid broadcast number '");
278       WRITE_STR(2, optarg);
279       WRITE_MSG(2, "'\n");
280       exit(wrapper_exit_code);
281     }
282   }
283   (*ips)->a.ip[0] = bcast;
284   (*ips)->a.count = 1;
285   (*ips)->a.type = vcNET_IPV4B;
286   (*ips)->next = calloc(1, sizeof(struct vc_ips));
287   *ips = (*ips)->next;
288 }
289
290 static void
291 tellAddress(struct vc_net_nx *addr, bool silent)
292 {
293   char buf[41];
294   if (silent)
295     return;
296   if (inet_ntop(addr->type == vcNET_IPV6 ? AF_INET6 : AF_INET,
297                 &addr->ip, buf, sizeof(buf)) == NULL) {
298     WRITE_MSG(1, " <conversion failed>");
299     return;
300   }
301   WRITE_MSG(1, " ");
302   WRITE_STR(1, buf);
303 }
304
305 static inline void
306 doit(struct Arguments *args)
307 {
308   struct vc_ips *ips;
309   if (args->do_add) {
310     if (!args->is_silent)
311       WRITE_MSG(1, "Adding");
312     for (ips = &args->head; ips->next; ips = ips->next) {
313       tellAddress(&ips->a, args->is_silent);
314       if (vc_net_add(args->nid, &ips->a) != (int)ips->a.count) {
315         perror(ENSC_WRAPPERS_PREFIX "vc_net_add()");
316         exit(wrapper_exit_code);
317       }
318     }
319     if (!args->is_silent)
320       WRITE_MSG(1, "\n");
321   }
322   else if (args->do_remove) {
323     if (!args->is_silent)
324       WRITE_MSG(1, "Removing");
325     for (ips = &args->head; ips->next; ips = ips->next) {
326       tellAddress(&ips->a, args->is_silent);
327       if (vc_net_remove(args->nid, &ips->a) != (int)ips->a.count) {
328         perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
329         exit(wrapper_exit_code);
330       }
331     }
332     if (!args->is_silent)
333       WRITE_MSG(1, "\n");
334   }
335 }
336
337 int main (int argc, char *argv[])
338 {
339   struct Arguments args = {
340     .nid        = VC_NOCTX,
341     .is_silent  = false,
342     .do_add     = false,
343     .do_remove  = false,
344     .head       = { .next = NULL },
345   };
346   struct vc_ips *ips = &args.head;
347   
348   while (1) {
349     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
350     if (c==-1) break;
351
352     switch (c) {
353       case CMD_HELP             :  showHelp(1, argv[0], 0);
354       case CMD_VERSION          :  showVersion();
355       case CMD_SILENT           :  args.is_silent = true; break;
356       case CMD_BCAST            :  readBcast(optarg, &ips); break;
357       case CMD_NID              :  args.nid = Evc_nidopt2nid(optarg,true); break;
358       case CMD_ADD              :  args.do_add = true; break;
359       case CMD_REMOVE           :  args.do_remove = true; break;
360       case CMD_IP               :  readIP(optarg, &ips); break;
361       default           :
362         WRITE_MSG(2, "Try '");
363         WRITE_STR(2, argv[0]);
364         WRITE_MSG(2, " --help\" for more information.\n");
365         exit(wrapper_exit_code);
366         break;
367     }
368   }
369
370   if (args.nid == VC_NOCTX) args.nid = Evc_get_task_nid(0);
371
372   if (!args.do_add && !args.do_remove) {
373     WRITE_MSG(2, "No operation specified; try '--help' for more information\n");
374     exit(wrapper_exit_code);
375   }
376   else if (args.do_add && args.do_remove) {
377     WRITE_MSG(2, "Multiple operations specified; try '--help' for more information\n");
378     exit(wrapper_exit_code);
379   }
380
381   doit(&args);
382
383   if (optind != argc)
384     Eexecvp (argv[optind],argv+optind);
385   return EXIT_SUCCESS;
386 }
387
388 #ifdef ENSC_TESTSUITE
389 #include <assert.h>
390
391 void test()
392 {
393   struct vc_ip_mask_pair        ip;
394   uint32_t                      bcast;
395
396   bcast = 0;
397   readIP("1.2.3.4", &ip, &bcast);
398   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
399
400   readIP("1.2.3.4/8", &ip, &bcast);
401   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
402
403   readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
404   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
405
406   readIP("localhost", &ip, &bcast);
407   assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
408
409 #if 0
410   if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
411     readIP("lo", &ip, &bcast);
412     assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));
413   }
414 #endif
415 }
416 #endif