naddress.c (13661B)
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_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