1 // $Id: naddress.c 2584 2007-08-10 15:28:42Z dhozac $
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 readIP(char const *str, struct vc_ips **ips, uint16_t type)
243 if (ifconfig_getaddr(str, &(*ips)->a.vna_v4_ip.s_addr, &(*ips)->a.vna_v4_mask.s_addr, NULL)==-1) {
245 char tmpopt[strlen(str)+1];
248 pt = strchr(tmpopt,'/');
252 if (convertAddress(tmpopt, &(*ips)->a.vna_type, &(*ips)->a.vna_v4_ip.s_addr) == -1) {
253 WRITE_MSG(2, "Invalid IP number '");
254 WRITE_STR(2, tmpopt);
256 exit(wrapper_exit_code);
260 switch ((*ips)->a.vna_type) {
261 case VC_NXA_TYPE_IPV4:
262 (*ips)->a.vna_v4_mask.s_addr = htonl(0xffffff00);
263 (*ips)->a.vna_prefix = 24;
265 case VC_NXA_TYPE_IPV6:
266 (*ips)->a.vna_prefix = 64;
267 (*ips)->a.vna_v6_mask.s6_addr32[0] = (*ips)->a.vna_v6_mask.s6_addr32[1] = 0xffffffff;
268 (*ips)->a.vna_v6_mask.s6_addr32[2] = (*ips)->a.vna_v6_mask.s6_addr32[3] = 0x00000000;
274 // Ok, we have a network size, not a netmask
275 if (strchr(pt,'.')==0 && strchr(pt,':')==0) {
276 unsigned long sz, limit = 0;
278 switch ((*ips)->a.vna_type) {
279 case VC_NXA_TYPE_IPV4: limit = 32; break;
280 case VC_NXA_TYPE_IPV6: limit = 128; break;
284 if (!isNumberUnsigned(pt, &sz, true) || sz > limit) {
285 WRITE_MSG(2, "Invalid prefix '");
288 exit(wrapper_exit_code);
291 (*ips)->a.vna_prefix = sz;
292 switch ((*ips)->a.vna_type) {
293 case VC_NXA_TYPE_IPV4:
294 (*ips)->a.vna_v4_mask.s_addr = htonl(~((1 << (32 - sz)) - 1));
296 case VC_NXA_TYPE_IPV6:
297 ipv6PrefixToMask(&(*ips)->a.vna_v6_mask, (*ips)->a.vna_prefix);
305 switch ((*ips)->a.vna_type) {
306 case VC_NXA_TYPE_IPV4:
308 mask = &(*ips)->a.vna_v4_mask.s_addr;
311 case VC_NXA_TYPE_IPV6:
313 mask = (*ips)->a.vna_v6_mask.s6_addr32;
319 if (inet_pton(af, pt, mask) < 0) {
320 WRITE_MSG(2, "Invalid netmask '");
323 exit(wrapper_exit_code);
325 (*ips)->a.vna_prefix = maskToPrefix(mask, limit);
330 (*ips)->a.vna_type = VC_NXA_TYPE_IPV4;
331 (*ips)->a.vna_type |= type;
333 (*ips)->next = calloc(1, sizeof(struct vc_ips));
338 readBcast(char const *str, struct vc_ips **ips)
341 if (ifconfig_getaddr(str, NULL, NULL, &bcast)==-1){
342 if (inet_pton(AF_INET, str, &bcast) < 0) {
343 WRITE_MSG(2, "Invalid broadcast number '");
344 WRITE_STR(2, optarg);
346 exit(wrapper_exit_code);
349 (*ips)->a.vna_v4_ip.s_addr = bcast;
350 (*ips)->a.vna_type = VC_NXA_TYPE_IPV4 | VC_NXA_MOD_BCAST | VC_NXA_TYPE_ADDR;
351 (*ips)->next = calloc(1, sizeof(struct vc_ips));
356 tellAddress(struct vc_net_addr *addr, bool silent)
363 switch (addr->vna_type & (VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_IPV6)) {
364 case VC_NXA_TYPE_IPV4:
366 address = &addr->vna_v4_ip.s_addr;
368 case VC_NXA_TYPE_IPV6:
370 address = addr->vna_v6_ip.s6_addr32;
373 WRITE_MSG(1, " <unknown address type>");
376 if (inet_ntop(af, address, buf, sizeof(buf)) == NULL) {
377 WRITE_MSG(1, " <conversion failed>");
385 doit(struct Arguments *args)
390 struct vc_net_addr remove = { .vna_type = VC_NXA_TYPE_ANY };
391 if (vc_net_remove(args->nid, &remove) == -1) {
392 perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
393 exit(wrapper_exit_code);
397 if (args->do_add || args->do_set) {
398 if (!args->is_silent)
399 WRITE_MSG(1, "Adding");
400 for (ips = &args->head; ips->next; ips = ips->next) {
401 tellAddress(&ips->a, args->is_silent);
402 if (vc_net_add(args->nid, &ips->a) == -1) {
403 if (!args->is_silent)
405 if (!args->is_silent)
407 perror(ENSC_WRAPPERS_PREFIX "vc_net_add()");
408 exit(wrapper_exit_code);
411 if (!args->is_silent)
414 else if (args->do_remove) {
415 if (!args->is_silent)
416 WRITE_MSG(1, "Removing");
417 for (ips = &args->head; ips->next; ips = ips->next) {
418 tellAddress(&ips->a, args->is_silent);
419 if (vc_net_remove(args->nid, &ips->a) == -1) {
420 if (!args->is_silent)
422 perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
423 exit(wrapper_exit_code);
426 if (!args->is_silent)
431 int main (int argc, char *argv[])
433 struct Arguments args = {
439 .head = { .next = NULL },
441 struct vc_ips *ips = &args.head;
444 int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
448 case CMD_HELP : showHelp(1, argv[0], 0);
449 case CMD_VERSION : showVersion();
450 case CMD_SILENT : args.is_silent = true; break;
451 case CMD_NID : args.nid = Evc_nidopt2nid(optarg,true); break;
452 case CMD_ADD : args.do_add = true; break;
453 case CMD_REMOVE : args.do_remove = true; break;
454 case CMD_SET : args.do_set = true; break;
455 case CMD_IP : readIP(optarg, &ips, VC_NXA_TYPE_ADDR); break;
456 case CMD_MASK : readIP(optarg, &ips, VC_NXA_TYPE_MASK); break;
457 case CMD_RANGE : readIP(optarg, &ips, VC_NXA_TYPE_RANGE); break;
458 case CMD_BCAST : readBcast(optarg, &ips); break;
459 case CMD_LBACK : readIP(optarg, &ips, VC_NXA_TYPE_ADDR | VC_NXA_MOD_LBACK); break;
461 WRITE_MSG(2, "Try '");
462 WRITE_STR(2, argv[0]);
463 WRITE_MSG(2, " --help' for more information.\n");
464 exit(wrapper_exit_code);
469 if (args.nid == VC_NOCTX) args.nid = Evc_get_task_nid(0);
471 if (!args.do_add && !args.do_remove && !args.do_set) {
472 WRITE_MSG(2, "No operation specified; try '--help' for more information\n");
473 exit(wrapper_exit_code);
475 else if (((args.do_add ? 1 : 0) + (args.do_remove ? 1 : 0) + (args.do_set ? 1 : 0)) > 1) {
476 WRITE_MSG(2, "Multiple operations specified; try '--help' for more information\n");
477 exit(wrapper_exit_code);
483 Eexecvp (argv[optind],argv+optind);
487 #ifdef ENSC_TESTSUITE
492 struct vc_ip_mask_pair ip;
496 readIP("1.2.3.4", &ip, &bcast);
497 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
499 readIP("1.2.3.4/8", &ip, &bcast);
500 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
502 readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
503 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
505 readIP("localhost", &ip, &bcast);
506 assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
509 if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
510 readIP("lo", &ip, &bcast);
511 assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));