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
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)
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.
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.
28 #include <lib/internal.h>
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
44 #define ENSC_WRAPPERS_PREFIX "naddress: "
45 #define ENSC_WRAPPERS_IO 1
46 #define ENSC_WRAPPERS_UNISTD 1
47 #define ENSC_WRAPPERS_VSERVER 1
50 #define CMD_HELP 0x1000
51 #define CMD_VERSION 0x1001
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
59 #define CMD_BCAST 0x2011
60 #define CMD_MASK 0x2012
61 #define CMD_RANGE 0x2013
62 #define CMD_LBACK 0x2014
64 int wrapper_exit_code = 255;
67 static struct option const
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 },
99 showHelp(int fd, char const *cmd, int res)
101 WRITE_MSG(fd, "Usage:\n ");
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");
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);
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.
128 Return != 0 if the device exist.
131 existsDevice(char const *dev_raw)
133 size_t buf_size=8192;
134 char dev[strlen(dev_raw)+2];
136 strcpy(dev, dev_raw);
142 int fd=open("/proc/net/dev", O_RDONLY);
144 if (fd==-1) return false;
145 too_small = EreadAll(fd, buf, buf_size);
153 pos = strstr(buf, dev);
154 return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
158 static int ifconfig_ioctl(
164 strcpy(ifr->ifr_name, ifname);
165 return ioctl(fd, cmd, ifr);
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.
173 int ifconfig_getaddr (
180 if (existsDevice(ifname)){
181 int skfd = socket(AF_INET, SOCK_DGRAM, 0);
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;
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;
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;
206 convertAddress(const char *str, uint16_t *type, void *dst)
209 if (type) *type = VC_NXA_TYPE_IPV4;
210 ret = inet_pton(AF_INET, str, dst);
212 if (type) *type = VC_NXA_TYPE_IPV6;
213 ret = inet_pton(AF_INET6, str, dst);
215 return ret > 0 ? 0 : -1;
219 ipv6PrefixToMask(struct in6_addr *mask, int prefix)
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;
226 if ((i << 3) > prefix)
227 mask->s6_addr[i-1] = ~((1 << (prefix & 0x07)) - 1);
231 maskToPrefix(void *data, int limit)
233 uint8_t *mask = data;
235 for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
241 parseIPFormat(char const *str_c, struct vc_ips **ips,
244 size_t len = strlen(str_c);
246 char str[len + 1], *ptr = str;
251 /* XXX: condition at the bottom */
252 for (fc = format; ; fc += 2) {
255 unsigned long limit = 0;
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;
266 if ((sep = memchr(ptr, *(fc + 1), len)) == NULL)
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;
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...
279 if (*fc == 'm' && strchr(ptr, ':') == NULL && strchr(ptr, '.') == NULL) {
280 /* This is a prefix, not a netmask */
283 if (!isNumberUnsigned(ptr, &sz, true) || sz > limit) {
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));
293 case VC_NXA_TYPE_IPV6:
294 ipv6PrefixToMask(&(*ips)->a.vna_v6_mask, sz);
299 if (convertAddress(ptr, &(*ips)->a.vna_type, dst) == -1) {
303 else if (*fc == 'm') {
304 /* Got a mask, set the prefix */
305 (*ips)->a.vna_prefix = maskToPrefix(&(*ips)->a.s.mask, limit);
313 if (*(fc + 1) == '\0')
322 readIP(char const *str, struct vc_ips **ips, uint16_t type)
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 '");
329 exit(wrapper_exit_code);
333 (*ips)->a.vna_type = VC_NXA_TYPE_IPV4;
334 (*ips)->a.vna_type |= type;
336 (*ips)->next = calloc(1, sizeof(struct vc_ips));
341 readBcast(char const *str, struct vc_ips **ips)
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);
349 exit(wrapper_exit_code);
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));
359 readRange(char const *str, struct vc_ips **ips)
361 if (parseIPFormat(str, ips, "1-2/m") < 2) {
362 WRITE_MSG(2, "Invalid range '");
365 exit(wrapper_exit_code);
367 (*ips)->a.vna_type |= VC_NXA_TYPE_RANGE;
368 (*ips)->next = calloc(1, sizeof(struct vc_ips));
373 tellAddress(struct vc_net_addr *addr, bool silent)
380 switch (addr->vna_type & (VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_IPV6)) {
381 case VC_NXA_TYPE_IPV4:
383 address = &addr->vna_v4_ip.s_addr;
385 case VC_NXA_TYPE_IPV6:
387 address = addr->vna_v6_ip.s6_addr32;
390 WRITE_MSG(1, " <unknown address type>");
393 if (inet_ntop(af, address, buf, sizeof(buf)) == NULL) {
394 WRITE_MSG(1, " <conversion failed>");
402 doit(struct Arguments *args)
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);
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)
422 perror(ENSC_WRAPPERS_PREFIX "vc_net_add()");
423 exit(wrapper_exit_code);
426 if (!args->is_silent)
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)
437 perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
438 exit(wrapper_exit_code);
441 if (!args->is_silent)
446 int main (int argc, char *argv[])
448 struct Arguments args = {
454 .head = { .next = NULL },
456 struct vc_ips *ips = &args.head;
459 int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
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;
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);
484 if (args.nid == VC_NOCTX) args.nid = Evc_get_task_nid(0);
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);
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);
498 Eexecvp (argv[optind],argv+optind);
502 #ifdef ENSC_TESTSUITE
507 struct vc_ip_mask_pair ip;
511 readIP("1.2.3.4", &ip, &bcast);
512 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
514 readIP("1.2.3.4/8", &ip, &bcast);
515 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
517 readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
518 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
520 readIP("localhost", &ip, &bcast);
521 assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==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));