Add forgotten svn:keywords.
[util-vserver.git] / src / chbind.c
1 // $Id$
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on chbind.cc by Jacques Gelinas
5 //  
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2, or (at your option)
9 // any later version.
10 //  
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //  
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include "vserver.h"
25 #include "util.h"
26
27 #include <lib/internal.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <netdb.h>
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <net/if.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <getopt.h>
39 #include <fcntl.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42
43 #define ENSC_WRAPPERS_PREFIX    "chbind: "
44 #define ENSC_WRAPPERS_IO        1
45 #define ENSC_WRAPPERS_UNISTD    1
46 #define ENSC_WRAPPERS_VSERVER   1
47 #include "wrappers.h"
48
49 #define CMD_HELP        0x1000
50 #define CMD_VERSION     0x1001
51
52 #define CMD_SILENT      0x2000
53 #define CMD_IP          0x2001
54 #define CMD_BCAST       0x2002
55 #define CMD_NID         0x2003
56
57 int wrapper_exit_code = 255;
58
59
60 static struct option const
61 CMDLINE_OPTIONS[] = {
62   { "help",     no_argument,  0, CMD_HELP },
63   { "version",  no_argument,  0, CMD_VERSION },
64   { "silent",   no_argument,  0, CMD_SILENT },
65   { "ip",       required_argument, 0, CMD_IP },
66   { "bcast",    required_argument, 0, CMD_BCAST },
67   { "nid",      required_argument, 0, CMD_NID },
68   { 0,0,0,0 }
69 };
70
71 static void
72 showHelp(int fd, char const *cmd, int res)
73 {
74   WRITE_MSG(fd, "Usage:\n  ");
75   WRITE_STR(fd, cmd);
76   WRITE_MSG(fd,
77             " [--silent] [--nid <nid>] [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
78             "Please report bugs to " PACKAGE_BUGREPORT "\n");
79
80   exit(res);
81 }
82
83 static void
84 showVersion()
85 {
86   WRITE_MSG(1,
87             "chbind " VERSION " -- bind to an ip and execute a program\n"
88             "This program is part of " PACKAGE_STRING "\n\n"
89             "Copyright (C) 2003,2004 Enrico Scholz\n"
90             VERSION_COPYRIGHT_DISCLAIMER);
91   exit(0);
92 }
93
94 /*
95         Check if a network device exist in /proc/net/dev.
96         This is used because ifconfig_ioctl triggers modprobe if requesting
97         information about non existant devices.
98
99         Return != 0 if the device exist.
100 */
101 static bool
102 existsDevice(char const *dev_raw)
103 {
104   size_t        buf_size=8192;
105   char          dev[strlen(dev_raw)+2];
106
107   strcpy(dev, dev_raw);
108   strcat(dev, ":");
109   for (;;) {
110     char        buf[buf_size];
111     char *      pos;
112     bool        too_small;
113     int         fd=open("/proc/net/dev", O_RDONLY);
114     
115     if (fd==-1) return false;
116     too_small = EreadAll(fd, buf, buf_size);
117     close(fd);
118
119     if (too_small) {
120       buf_size *= 2;
121       continue;
122     }
123
124     pos = strstr(buf, dev);
125     return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
126   }
127 }
128
129 static int ifconfig_ioctl(
130         int fd,
131         const char *ifname,
132         int cmd,
133         struct ifreq *ifr)
134 {
135         strcpy(ifr->ifr_name, ifname);
136         return ioctl(fd, cmd,ifr);
137 }
138
139 /*
140         Fetch the IP number of an interface from the kernel.
141         Assume the device is already available in the kernel
142         Return -1 if any error.
143 */
144 int ifconfig_getaddr (
145         const char *ifname,
146         uint32_t *addr,
147         uint32_t *mask,
148         uint32_t *bcast)
149 {
150         int ret = -1;
151         if (existsDevice(ifname)){
152                 int skfd = socket(AF_INET, SOCK_DGRAM, 0);
153                 *addr = 0;
154                 *bcast = 0xffffffff;
155                 if (skfd != -1){
156                         struct ifreq ifr;
157                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
158                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
159                                 *addr = sin->sin_addr.s_addr;
160                                 ret = 0;
161                         }
162                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
163                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
164                                 *mask = sin->sin_addr.s_addr;
165                                 ret = 0;
166                         }
167                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
168                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
169                                 *bcast = sin->sin_addr.s_addr;
170                                 ret = 0;
171                         }
172                         close (skfd);
173                 }
174         }
175         return ret;
176 }
177
178 static void
179 readIP(char const *str, struct vc_ip_mask_pair *ip, uint32_t *bcast)
180 {
181   if (ifconfig_getaddr(str, &ip->ip, &ip->mask, bcast)==-1) {
182     char                *pt;
183     char                tmpopt[strlen(str)+1];
184     struct hostent      *h;
185
186     strcpy(tmpopt,str);
187     pt = strchr(tmpopt,'/');
188     
189     if (pt==0)
190       ip->mask = ntohl(0xffffff00);
191     else {
192       *pt++ = '\0';
193
194       // Ok, we have a network size, not a netmask
195       if (strchr(pt,'.')==0) {
196         int             sz = atoi(pt);
197         ;
198         for (ip->mask = 0; sz>0; --sz) {
199           ip->mask >>= 1;
200           ip->mask  |= 0x80000000;
201         }
202         ip->mask = ntohl(ip->mask);
203       }
204       else { 
205         struct hostent *h = gethostbyname (pt);
206         if (h==0) {
207           WRITE_MSG(2, "Invalid netmask '");
208           WRITE_STR(2, pt);
209           WRITE_MSG(2, "'\n");
210           exit(wrapper_exit_code);
211         }
212
213         memcpy (&ip->mask, h->h_addr, sizeof(ip->mask));
214       }
215     }
216
217     h = gethostbyname (tmpopt);
218     if (h==0) {
219       WRITE_MSG(2, "Invalid IP number or host name '");
220       WRITE_STR(2, tmpopt);
221       WRITE_MSG(2, "'\n");
222       exit(wrapper_exit_code);
223     }
224
225     memcpy (&ip->ip, h->h_addr,sizeof(ip->ip));
226   }
227 }
228
229 static void
230 readBcast(char const *str, uint32_t *bcast)
231 {
232   uint32_t      tmp;
233   if (ifconfig_getaddr(str, &tmp, &tmp, bcast)==-1){
234     struct hostent *h = gethostbyname (str);
235     if (h==0){
236       WRITE_MSG(2, "Invalid broadcast number '");
237       WRITE_STR(2, optarg);
238       WRITE_MSG(2, "'\n");
239       exit(wrapper_exit_code);
240     }
241     memcpy (bcast, h->h_addr,sizeof(*bcast));
242   }
243 }
244
245 #if defined(VC_ENABLE_API_NET)
246 static void
247 make_nx(nid_t nid, uint32_t bcast, size_t nbaddrs, struct vc_ip_mask_pair *ips)
248 {
249   size_t i;
250   struct vc_net_nx addr;
251
252   if (nid == VC_DYNAMIC_NID) {
253     nid = vc_net_create(VC_DYNAMIC_NID);
254     if (nid == (nid_t) -1) {
255       perror("chbind: vc_net_create()");
256       exit(wrapper_exit_code);
257     }
258   }
259   else {
260     if (vc_net_create(nid) == (nid_t) -1) {
261       if (errno == EEXIST) {
262         if (vc_net_migrate(nid) != 0) {
263           perror("chbind: vc_net_migrate()");
264           exit(wrapper_exit_code);
265         }
266         else
267           return;
268       }
269       else {
270         perror("chbind: vc_net_create()");
271         exit(wrapper_exit_code);
272       }
273     }
274   }
275
276   addr.type = vcNET_IPV4B;
277   addr.count = 1;
278   addr.ip[0] = bcast;
279   addr.mask[0] = 0;
280
281   if (vc_net_add(nid, &addr) != 1) {
282     perror("chbind: vc_net_add()");
283     exit(wrapper_exit_code);
284   }
285
286   for (i = 0; i < nbaddrs; i++) {
287     addr.type = vcNET_IPV4;
288     addr.count = 1;
289     addr.ip[0] = ips[i].ip;
290     addr.mask[0] = ips[i].mask;
291
292     if (vc_net_add(nid, &addr) != 1) {
293       perror("chbind: vc_net_add()");
294       exit(wrapper_exit_code);
295     }
296   }
297 }
298 #endif
299
300 int main (int argc, char *argv[])
301 {
302   size_t const                  nb_ipv4root = vc_get_nb_ipv4root();
303   bool                          is_silent   = false;
304   struct vc_ip_mask_pair        ips[nb_ipv4root];
305   size_t                        nbaddrs = 0;
306   uint32_t                      bcast   = 0xffffffff;
307   nid_t                         nid     = VC_DYNAMIC_NID;
308   
309   while (1) {
310     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
311     if (c==-1) break;
312
313     switch (c) {
314       case CMD_HELP             :  showHelp(1, argv[0], 0);
315       case CMD_VERSION          :  showVersion();
316       case CMD_SILENT           :  is_silent = true; break;
317       case CMD_BCAST            :  readBcast(optarg, &bcast); break;
318 #if defined(VC_ENABLE_API_NET)
319       case CMD_NID              :  nid = Evc_xidopt2xid(optarg,true); break;
320 #else
321       case CMD_NID              :  WRITE_MSG(2, "WARNING: --nid is not supported by this version\n"); break;
322 #endif
323       case CMD_IP               :
324         if (nbaddrs>=nb_ipv4root) {
325           WRITE_MSG(2, "Too many IP numbers, max 16\n");
326           exit(wrapper_exit_code);
327         }
328         readIP(optarg, ips+nbaddrs, &bcast);
329         ++nbaddrs;
330         break;
331       default           :
332         WRITE_MSG(2, "Try '");
333         WRITE_STR(2, argv[0]);
334         WRITE_MSG(2, " --help\" for more information.\n");
335         exit(wrapper_exit_code);
336         break;
337     }
338   }
339
340   if (optind==argc) {
341     WRITE_MSG(2, "No command given; try '--help' for more information\n");
342     exit(wrapper_exit_code);
343   }
344   
345 #if !defined(VC_ENABLE_API_NET) && !defined(VC_ENABLE_API_COMPAT) && !defined(VC_ENABLE_API_LEGACY)
346 #  error can not build 'chbind' without network virtualization API
347 #endif
348   
349 #if defined(VC_ENABLE_API_NET)
350   if (vc_isSupported(vcFEATURE_VNET)) {
351     make_nx(nid, bcast, nbaddrs, ips);
352   }
353   else
354 #endif
355 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY)
356   if (vc_set_ipv4root(bcast,nbaddrs,ips)!=0) {
357     perror("chbind: vc_set_ipv4root()");
358     exit(wrapper_exit_code);
359   }
360 #else
361   {
362     WRITE_MSG(2, "chbind: kernel does not provide network virtualization\n");
363     exit(wrapper_exit_code);
364   }
365 #endif
366
367   if (!is_silent) {
368     size_t              i;
369     
370     WRITE_MSG(1, "ipv4root is now");
371     for (i=0; i<nbaddrs; ++i) {
372       WRITE_MSG(1, " ");
373       WRITE_STR(1, inet_ntoa(*reinterpret_cast(struct in_addr *)(&ips[i].ip)));
374     }
375     WRITE_MSG(1, "\n");
376   }
377
378   Eexecvp (argv[optind],argv+optind);
379   return EXIT_SUCCESS;
380 }
381
382 #ifdef ENSC_TESTSUITE
383 #include <assert.h>
384
385 void test()
386 {
387   struct vc_ip_mask_pair        ip;
388   uint32_t                      bcast;
389
390   bcast = 0;
391   readIP("1.2.3.4", &ip, &bcast);
392   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
393
394   readIP("1.2.3.4/8", &ip, &bcast);
395   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
396
397   readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
398   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
399
400   readIP("localhost", &ip, &bcast);
401   assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
402
403 #if 0
404   if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
405     readIP("lo", &ip, &bcast);
406     assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));
407   }
408 #endif
409 }
410 #endif