1/* vi: set sw=4 ts=4: */ 2/* 3 * ifupdown for busybox 4 * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au> 5 * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org> 6 * 7 * Based on ifupdown v 0.6.4 by Anthony Towns 8 * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au> 9 * 10 * Changes to upstream version 11 * Remove checks for kernel version, assume kernel version 2.2.0 or better. 12 * Lines in the interfaces file cannot wrap. 13 * To adhere to the FHS, the default state file is /var/run/ifstate 14 * (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build 15 * configuration. 16 * 17 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 18 */ 19 20#include <sys/utsname.h> 21#include <fnmatch.h> 22#include <getopt.h> 23 24#include "libbb.h" 25 26#define MAX_OPT_DEPTH 10 27#define EUNBALBRACK 10001 28#define EUNDEFVAR 10002 29#define EUNBALPER 10000 30 31#if ENABLE_FEATURE_IFUPDOWN_MAPPING 32#define MAX_INTERFACE_LENGTH 10 33#endif 34 35#define debug_noise(args...) /*fprintf(stderr, args)*/ 36 37/* Forward declaration */ 38struct interface_defn_t; 39 40typedef int execfn(char *command); 41 42struct method_t { 43 const char *name; 44 int (*up)(struct interface_defn_t *ifd, execfn *e); 45 int (*down)(struct interface_defn_t *ifd, execfn *e); 46}; 47 48struct address_family_t { 49 const char *name; 50 int n_methods; 51 const struct method_t *method; 52}; 53 54struct mapping_defn_t { 55 struct mapping_defn_t *next; 56 57 int max_matches; 58 int n_matches; 59 char **match; 60 61 char *script; 62 63 int max_mappings; 64 int n_mappings; 65 char **mapping; 66}; 67 68struct variable_t { 69 char *name; 70 char *value; 71}; 72 73struct interface_defn_t { 74 const struct address_family_t *address_family; 75 const struct method_t *method; 76 77 char *iface; 78 int max_options; 79 int n_options; 80 struct variable_t *option; 81}; 82 83struct interfaces_file_t { 84 llist_t *autointerfaces; 85 llist_t *ifaces; 86 struct mapping_defn_t *mappings; 87}; 88 89#define OPTION_STR "anvf" USE_FEATURE_IFUPDOWN_MAPPING("m") "i:" 90enum { 91 OPT_do_all = 0x1, 92 OPT_no_act = 0x2, 93 OPT_verbose = 0x4, 94 OPT_force = 0x8, 95 OPT_no_mappings = 0x10, 96}; 97#define DO_ALL (option_mask32 & OPT_do_all) 98#define NO_ACT (option_mask32 & OPT_no_act) 99#define VERBOSE (option_mask32 & OPT_verbose) 100#define FORCE (option_mask32 & OPT_force) 101#define NO_MAPPINGS (option_mask32 & OPT_no_mappings) 102 103static char **my_environ; 104 105static const char *startup_PATH; 106 107#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6 108 109static void addstr(char **bufp, const char *str, size_t str_length) 110{ 111 /* xasprintf trick will be smaller, but we are often 112 * called with str_length == 1 - don't want to have 113 * THAT much of malloc/freeing! */ 114 char *buf = *bufp; 115 int len = (buf ? strlen(buf) : 0); 116 str_length++; 117 buf = xrealloc(buf, len + str_length); 118 /* copies at most str_length-1 chars! */ 119 safe_strncpy(buf + len, str, str_length); 120 *bufp = buf; 121} 122 123static int strncmpz(const char *l, const char *r, size_t llen) 124{ 125 int i = strncmp(l, r, llen); 126 127 if (i == 0) 128 return -r[llen]; 129 return i; 130} 131 132static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd) 133{ 134 int i; 135 136 if (strncmpz(id, "iface", idlen) == 0) { 137 char *result; 138 static char label_buf[20]; 139 safe_strncpy(label_buf, ifd->iface, sizeof(label_buf)); 140 result = strchr(label_buf, ':'); 141 if (result) { 142 *result = '\0'; 143 } 144 return label_buf; 145 } 146 if (strncmpz(id, "label", idlen) == 0) { 147 return ifd->iface; 148 } 149 for (i = 0; i < ifd->n_options; i++) { 150 if (strncmpz(id, ifd->option[i].name, idlen) == 0) { 151 return ifd->option[i].value; 152 } 153 } 154 return NULL; 155} 156 157#if ENABLE_FEATURE_IFUPDOWN_IP 158static int count_netmask_bits(const char *dotted_quad) 159{ 160// int result; 161// unsigned a, b, c, d; 162// /* Found a netmask... Check if it is dotted quad */ 163// if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) 164// return -1; 165// if ((a|b|c|d) >> 8) 166// return -1; /* one of numbers is >= 256 */ 167// d |= (a << 24) | (b << 16) | (c << 8); /* IP */ 168// d = ~d; /* 11110000 -> 00001111 */ 169 170 /* Shorter version */ 171 int result; 172 struct in_addr ip; 173 unsigned d; 174 175 if (inet_aton(dotted_quad, &ip) == 0) 176 return -1; /* malformed dotted IP */ 177 d = ntohl(ip.s_addr); /* IP in host order */ 178 d = ~d; /* 11110000 -> 00001111 */ 179 if (d & (d+1)) /* check that it is in 00001111 form */ 180 return -1; /* no it is not */ 181 result = 32; 182 while (d) { 183 d >>= 1; 184 result--; 185 } 186 return result; 187} 188#endif 189 190static char *parse(const char *command, struct interface_defn_t *ifd) 191{ 192 size_t old_pos[MAX_OPT_DEPTH] = { 0 }; 193 int okay[MAX_OPT_DEPTH] = { 1 }; 194 int opt_depth = 1; 195 char *result = NULL; 196 197 while (*command) { 198 switch (*command) { 199 default: 200 addstr(&result, command, 1); 201 command++; 202 break; 203 case '\\': 204 if (command[1]) { 205 addstr(&result, command + 1, 1); 206 command += 2; 207 } else { 208 addstr(&result, command, 1); 209 command++; 210 } 211 break; 212 case '[': 213 if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) { 214 old_pos[opt_depth] = result ? strlen(result) : 0; 215 okay[opt_depth] = 1; 216 opt_depth++; 217 command += 2; 218 } else { 219 addstr(&result, "[", 1); 220 command++; 221 } 222 break; 223 case ']': 224 if (command[1] == ']' && opt_depth > 1) { 225 opt_depth--; 226 if (!okay[opt_depth]) { 227 result[old_pos[opt_depth]] = '\0'; 228 } 229 command += 2; 230 } else { 231 addstr(&result, "]", 1); 232 command++; 233 } 234 break; 235 case '%': 236 { 237 char *nextpercent; 238 char *varvalue; 239 240 command++; 241 nextpercent = strchr(command, '%'); 242 if (!nextpercent) { 243 errno = EUNBALPER; 244 free(result); 245 return NULL; 246 } 247 248 varvalue = get_var(command, nextpercent - command, ifd); 249 250 if (varvalue) { 251 addstr(&result, varvalue, strlen(varvalue)); 252 } else { 253#if ENABLE_FEATURE_IFUPDOWN_IP 254 /* Sigh... Add a special case for 'ip' to convert from 255 * dotted quad to bit count style netmasks. */ 256 if (strncmp(command, "bnmask", 6) == 0) { 257 unsigned res; 258 varvalue = get_var("netmask", 7, ifd); 259 if (varvalue) { 260 res = count_netmask_bits(varvalue); 261 if (res > 0) { 262 const char *argument = utoa(res); 263 addstr(&result, argument, strlen(argument)); 264 command = nextpercent + 1; 265 break; 266 } 267 } 268 } 269#endif 270 okay[opt_depth - 1] = 0; 271 } 272 273 command = nextpercent + 1; 274 } 275 break; 276 } 277 } 278 279 if (opt_depth > 1) { 280 errno = EUNBALBRACK; 281 free(result); 282 return NULL; 283 } 284 285 if (!okay[0]) { 286 errno = EUNDEFVAR; 287 free(result); 288 return NULL; 289 } 290 291 return result; 292} 293 294/* execute() returns 1 for success and 0 for failure */ 295static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec) 296{ 297 char *out; 298 int ret; 299 300 out = parse(command, ifd); 301 if (!out) { 302 /* parse error? */ 303 return 0; 304 } 305 /* out == "": parsed ok but not all needed variables known, skip */ 306 ret = out[0] ? (*exec)(out) : 1; 307 308 free(out); 309 if (ret != 1) { 310 return 0; 311 } 312 return 1; 313} 314#endif 315 316#if ENABLE_FEATURE_IFUPDOWN_IPV6 317static int loopback_up6(struct interface_defn_t *ifd, execfn *exec) 318{ 319#if ENABLE_FEATURE_IFUPDOWN_IP 320 int result; 321 result = execute("ip addr add ::1 dev %iface%", ifd, exec); 322 result += execute("ip link set %iface% up", ifd, exec); 323 return ((result == 2) ? 2 : 0); 324#else 325 return execute("ifconfig %iface% add ::1", ifd, exec); 326#endif 327} 328 329static int loopback_down6(struct interface_defn_t *ifd, execfn *exec) 330{ 331#if ENABLE_FEATURE_IFUPDOWN_IP 332 return execute("ip link set %iface% down", ifd, exec); 333#else 334 return execute("ifconfig %iface% del ::1", ifd, exec); 335#endif 336} 337 338static int static_up6(struct interface_defn_t *ifd, execfn *exec) 339{ 340 int result; 341#if ENABLE_FEATURE_IFUPDOWN_IP 342 result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec); 343 result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec); 344 /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */ 345 result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); 346#else 347 result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec); 348 result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec); 349 result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec); 350#endif 351 return ((result == 3) ? 3 : 0); 352} 353 354static int static_down6(struct interface_defn_t *ifd, execfn *exec) 355{ 356#if ENABLE_FEATURE_IFUPDOWN_IP 357 return execute("ip link set %iface% down", ifd, exec); 358#else 359 return execute("ifconfig %iface% down", ifd, exec); 360#endif 361} 362 363#if ENABLE_FEATURE_IFUPDOWN_IP 364static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec) 365{ 366 int result; 367 result = execute("ip tunnel add %iface% mode sit remote " 368 "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec); 369 result += execute("ip link set %iface% up", ifd, exec); 370 result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec); 371 result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); 372 return ((result == 4) ? 4 : 0); 373} 374 375static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec) 376{ 377 return execute("ip tunnel del %iface%", ifd, exec); 378} 379#endif 380 381static const struct method_t methods6[] = { 382#if ENABLE_FEATURE_IFUPDOWN_IP 383 { "v4tunnel", v4tunnel_up, v4tunnel_down, }, 384#endif 385 { "static", static_up6, static_down6, }, 386 { "loopback", loopback_up6, loopback_down6, }, 387}; 388 389static const struct address_family_t addr_inet6 = { 390 "inet6", 391 ARRAY_SIZE(methods6), 392 methods6 393}; 394#endif /* FEATURE_IFUPDOWN_IPV6 */ 395 396#if ENABLE_FEATURE_IFUPDOWN_IPV4 397static int loopback_up(struct interface_defn_t *ifd, execfn *exec) 398{ 399#if ENABLE_FEATURE_IFUPDOWN_IP 400 int result; 401 result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec); 402 result += execute("ip link set %iface% up", ifd, exec); 403 return ((result == 2) ? 2 : 0); 404#else 405 return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec); 406#endif 407} 408 409static int loopback_down(struct interface_defn_t *ifd, execfn *exec) 410{ 411#if ENABLE_FEATURE_IFUPDOWN_IP 412 int result; 413 result = execute("ip addr flush dev %iface%", ifd, exec); 414 result += execute("ip link set %iface% down", ifd, exec); 415 return ((result == 2) ? 2 : 0); 416#else 417 return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec); 418#endif 419} 420 421static int static_up(struct interface_defn_t *ifd, execfn *exec) 422{ 423 int result; 424#if ENABLE_FEATURE_IFUPDOWN_IP 425 result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] " 426 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec); 427 result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec); 428 result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec); 429 return ((result == 3) ? 3 : 0); 430#else 431 /* ifconfig said to set iface up before it processes hw %hwaddress%, 432 * which then of course fails. Thus we run two separate ifconfig */ 433 result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up", 434 ifd, exec); 435 result += execute("ifconfig %iface% %address% netmask %netmask%" 436 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ", 437 ifd, exec); 438 result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec); 439 return ((result == 3) ? 3 : 0); 440#endif 441} 442 443static int static_down(struct interface_defn_t *ifd, execfn *exec) 444{ 445 int result; 446#if ENABLE_FEATURE_IFUPDOWN_IP 447 result = execute("ip addr flush dev %iface%", ifd, exec); 448 result += execute("ip link set %iface% down", ifd, exec); 449#else 450 result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); 451 result += execute("ifconfig %iface% down", ifd, exec); 452#endif 453 return ((result == 2) ? 2 : 0); 454} 455 456#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 457struct dhcp_client_t 458{ 459 const char *name; 460 const char *startcmd; 461 const char *stopcmd; 462}; 463 464static const struct dhcp_client_t ext_dhcp_clients[] = { 465 { "dhcpcd", 466 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%", 467 "dhcpcd -k %iface%", 468 }, 469 { "dhclient", 470 "dhclient -pf /var/run/dhclient.%iface%.pid %iface%", 471 "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", 472 }, 473 { "pump", 474 "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]", 475 "pump -i %iface% -k", 476 }, 477 { "udhcpc", 478 "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]", 479 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", 480 }, 481}; 482#endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */ 483 484static int dhcp_up(struct interface_defn_t *ifd, execfn *exec) 485{ 486#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 487 int i; 488#if ENABLE_FEATURE_IFUPDOWN_IP 489 /* ip doesn't up iface when it configures it (unlike ifconfig) */ 490 if (!execute("ip link set %iface% up", ifd, exec)) 491 return 0; 492#endif 493 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { 494 if (exists_execable(ext_dhcp_clients[i].name)) 495 return execute(ext_dhcp_clients[i].startcmd, ifd, exec); 496 } 497 bb_error_msg("no dhcp clients found"); 498 return 0; 499#elif ENABLE_APP_UDHCPC 500#if ENABLE_FEATURE_IFUPDOWN_IP 501 /* ip doesn't up iface when it configures it (unlike ifconfig) */ 502 if (!execute("ip link set %iface% up", ifd, exec)) 503 return 0; 504#endif 505 return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid " 506 "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]", 507 ifd, exec); 508#else 509 return 0; /* no dhcp support */ 510#endif 511} 512 513static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) 514{ 515#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 516 int i; 517 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { 518 if (exists_execable(ext_dhcp_clients[i].name)) 519 return execute(ext_dhcp_clients[i].stopcmd, ifd, exec); 520 } 521 bb_error_msg("no dhcp clients found, using static interface shutdown"); 522 return static_down(ifd, exec); 523#elif ENABLE_APP_UDHCPC 524 return execute("kill " 525 "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); 526#else 527 return 0; /* no dhcp support */ 528#endif 529} 530 531static int manual_up_down(struct interface_defn_t *ifd, execfn *exec) 532{ 533 return 1; 534} 535 536static int bootp_up(struct interface_defn_t *ifd, execfn *exec) 537{ 538 return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%" 539 "[[ --server %server%]][[ --hwaddr %hwaddr%]]" 540 " --returniffail --serverbcast", ifd, exec); 541} 542 543static int ppp_up(struct interface_defn_t *ifd, execfn *exec) 544{ 545 return execute("pon[[ %provider%]]", ifd, exec); 546} 547 548static int ppp_down(struct interface_defn_t *ifd, execfn *exec) 549{ 550 return execute("poff[[ %provider%]]", ifd, exec); 551} 552 553static int wvdial_up(struct interface_defn_t *ifd, execfn *exec) 554{ 555 return execute("start-stop-daemon --start -x wvdial " 556 "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec); 557} 558 559static int wvdial_down(struct interface_defn_t *ifd, execfn *exec) 560{ 561 return execute("start-stop-daemon --stop -x wvdial " 562 "-p /var/run/wvdial.%iface% -s 2", ifd, exec); 563} 564 565static const struct method_t methods[] = { 566 { "manual", manual_up_down, manual_up_down, }, 567 { "wvdial", wvdial_up, wvdial_down, }, 568 { "ppp", ppp_up, ppp_down, }, 569 { "static", static_up, static_down, }, 570 { "bootp", bootp_up, static_down, }, 571 { "dhcp", dhcp_up, dhcp_down, }, 572 { "loopback", loopback_up, loopback_down, }, 573}; 574 575static const struct address_family_t addr_inet = { 576 "inet", 577 ARRAY_SIZE(methods), 578 methods 579}; 580 581#endif /* if ENABLE_FEATURE_IFUPDOWN_IPV4 */ 582 583static char *next_word(char **buf) 584{ 585 unsigned short length; 586 char *word; 587 588 if (!buf || !*buf || !**buf) { 589 return NULL; 590 } 591 592 /* Skip over leading whitespace */ 593 word = skip_whitespace(*buf); 594 595 /* Skip over comments */ 596 if (*word == '#') { 597 return NULL; 598 } 599 600 /* Find the length of this word */ 601 length = strcspn(word, " \t\n"); 602 if (length == 0) { 603 return NULL; 604 } 605 *buf = word + length; 606 /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */ 607 if (**buf) { 608 **buf = '\0'; 609 (*buf)++; 610 } 611 612 return word; 613} 614 615static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name) 616{ 617 int i; 618 619 if (!name) 620 return NULL; 621 622 for (i = 0; af[i]; i++) { 623 if (strcmp(af[i]->name, name) == 0) { 624 return af[i]; 625 } 626 } 627 return NULL; 628} 629 630static const struct method_t *get_method(const struct address_family_t *af, char *name) 631{ 632 int i; 633 634 if (!name) 635 return NULL; 636 637 for (i = 0; i < af->n_methods; i++) { 638 if (strcmp(af->method[i].name, name) == 0) { 639 return &af->method[i]; 640 } 641 } 642 return NULL; 643} 644 645static const llist_t *find_list_string(const llist_t *list, const char *string) 646{ 647 if (string == NULL) 648 return NULL; 649 650 while (list) { 651 if (strcmp(list->data, string) == 0) { 652 return list; 653 } 654 list = list->link; 655 } 656 return NULL; 657} 658 659static struct interfaces_file_t *read_interfaces(const char *filename) 660{ 661#if ENABLE_FEATURE_IFUPDOWN_MAPPING 662 struct mapping_defn_t *currmap = NULL; 663#endif 664 struct interface_defn_t *currif = NULL; 665 struct interfaces_file_t *defn; 666 FILE *f; 667 char *firstword; 668 char *buf; 669 670 enum { NONE, IFACE, MAPPING } currently_processing = NONE; 671 672 defn = xzalloc(sizeof(struct interfaces_file_t)); 673 674 f = xfopen(filename, "r"); 675 676 while ((buf = xmalloc_getline(f)) != NULL) { 677 char *buf_ptr = buf; 678 679 firstword = next_word(&buf_ptr); 680 if (firstword == NULL) { 681 free(buf); 682 continue; /* blank line */ 683 } 684 685 if (strcmp(firstword, "mapping") == 0) { 686#if ENABLE_FEATURE_IFUPDOWN_MAPPING 687 currmap = xzalloc(sizeof(struct mapping_defn_t)); 688 689 while ((firstword = next_word(&buf_ptr)) != NULL) { 690 if (currmap->max_matches == currmap->n_matches) { 691 currmap->max_matches = currmap->max_matches * 2 + 1; 692 currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches); 693 } 694 695 currmap->match[currmap->n_matches++] = xstrdup(firstword); 696 } 697 currmap->max_mappings = 0; 698 currmap->n_mappings = 0; 699 currmap->mapping = NULL; 700 currmap->script = NULL; 701 { 702 struct mapping_defn_t **where = &defn->mappings; 703 while (*where != NULL) { 704 where = &(*where)->next; 705 } 706 *where = currmap; 707 currmap->next = NULL; 708 } 709 debug_noise("Added mapping\n"); 710#endif 711 currently_processing = MAPPING; 712 } else if (strcmp(firstword, "iface") == 0) { 713 static const struct address_family_t *const addr_fams[] = { 714#if ENABLE_FEATURE_IFUPDOWN_IPV4 715 &addr_inet, 716#endif 717#if ENABLE_FEATURE_IFUPDOWN_IPV6 718 &addr_inet6, 719#endif 720 NULL 721 }; 722 723 char *iface_name; 724 char *address_family_name; 725 char *method_name; 726 llist_t *iface_list; 727 728 currif = xzalloc(sizeof(struct interface_defn_t)); 729 iface_name = next_word(&buf_ptr); 730 address_family_name = next_word(&buf_ptr); 731 method_name = next_word(&buf_ptr); 732 733 if (buf_ptr == NULL) { 734 bb_error_msg("too few parameters for line \"%s\"", buf); 735 return NULL; 736 } 737 738 /* ship any trailing whitespace */ 739 buf_ptr = skip_whitespace(buf_ptr); 740 741 if (buf_ptr[0] != '\0') { 742 bb_error_msg("too many parameters \"%s\"", buf); 743 return NULL; 744 } 745 746 currif->iface = xstrdup(iface_name); 747 748 currif->address_family = get_address_family(addr_fams, address_family_name); 749 if (!currif->address_family) { 750 bb_error_msg("unknown address type \"%s\"", address_family_name); 751 return NULL; 752 } 753 754 currif->method = get_method(currif->address_family, method_name); 755 if (!currif->method) { 756 bb_error_msg("unknown method \"%s\"", method_name); 757 return NULL; 758 } 759 760 for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) { 761 struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data; 762 if ((strcmp(tmp->iface, currif->iface) == 0) && 763 (tmp->address_family == currif->address_family)) { 764 bb_error_msg("duplicate interface \"%s\"", tmp->iface); 765 return NULL; 766 } 767 } 768 llist_add_to_end(&(defn->ifaces), (char*)currif); 769 770 debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name); 771 currently_processing = IFACE; 772 } else if (strcmp(firstword, "auto") == 0) { 773 while ((firstword = next_word(&buf_ptr)) != NULL) { 774 775 /* Check the interface isnt already listed */ 776 if (find_list_string(defn->autointerfaces, firstword)) { 777 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf); 778 } 779 780 /* Add the interface to the list */ 781 llist_add_to_end(&(defn->autointerfaces), xstrdup(firstword)); 782 debug_noise("\nauto %s\n", firstword); 783 } 784 currently_processing = NONE; 785 } else { 786 switch (currently_processing) { 787 case IFACE: 788 { 789 int i; 790 791 if (strlen(buf_ptr) == 0) { 792 bb_error_msg("option with empty value \"%s\"", buf); 793 return NULL; 794 } 795 796 if (strcmp(firstword, "up") != 0 797 && strcmp(firstword, "down") != 0 798 && strcmp(firstword, "pre-up") != 0 799 && strcmp(firstword, "post-down") != 0) { 800 for (i = 0; i < currif->n_options; i++) { 801 if (strcmp(currif->option[i].name, firstword) == 0) { 802 bb_error_msg("duplicate option \"%s\"", buf); 803 return NULL; 804 } 805 } 806 } 807 } 808 if (currif->n_options >= currif->max_options) { 809 struct variable_t *opt; 810 811 currif->max_options = currif->max_options + 10; 812 opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options); 813 currif->option = opt; 814 } 815 currif->option[currif->n_options].name = xstrdup(firstword); 816 currif->option[currif->n_options].value = xstrdup(buf_ptr); 817 if (!currif->option[currif->n_options].name) { 818 perror(filename); 819 return NULL; 820 } 821 if (!currif->option[currif->n_options].value) { 822 perror(filename); 823 return NULL; 824 } 825 debug_noise("\t%s=%s\n", currif->option[currif->n_options].name, 826 currif->option[currif->n_options].value); 827 currif->n_options++; 828 break; 829 case MAPPING: 830#if ENABLE_FEATURE_IFUPDOWN_MAPPING 831 if (strcmp(firstword, "script") == 0) { 832 if (currmap->script != NULL) { 833 bb_error_msg("duplicate script in mapping \"%s\"", buf); 834 return NULL; 835 } else { 836 currmap->script = xstrdup(next_word(&buf_ptr)); 837 } 838 } else if (strcmp(firstword, "map") == 0) { 839 if (currmap->max_mappings == currmap->n_mappings) { 840 currmap->max_mappings = currmap->max_mappings * 2 + 1; 841 currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings); 842 } 843 currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&buf_ptr)); 844 currmap->n_mappings++; 845 } else { 846 bb_error_msg("misplaced option \"%s\"", buf); 847 return NULL; 848 } 849#endif 850 break; 851 case NONE: 852 default: 853 bb_error_msg("misplaced option \"%s\"", buf); 854 return NULL; 855 } 856 } 857 free(buf); 858 } 859 if (ferror(f) != 0) { 860 /* ferror does NOT set errno! */ 861 bb_error_msg_and_die("%s: I/O error", filename); 862 } 863 fclose(f); 864 865 return defn; 866} 867 868static char *setlocalenv(const char *format, const char *name, const char *value) 869{ 870 char *result; 871 char *here; 872 char *there; 873 874 result = xasprintf(format, name, value); 875 876 for (here = there = result; *there != '=' && *there; there++) { 877 if (*there == '-') 878 *there = '_'; 879 if (isalpha(*there)) 880 *there = toupper(*there); 881 882 if (isalnum(*there) || *there == '_') { 883 *here = *there; 884 here++; 885 } 886 } 887 memmove(here, there, strlen(there) + 1); 888 889 return result; 890} 891 892static void set_environ(struct interface_defn_t *iface, const char *mode) 893{ 894 char **environend; 895 int i; 896 const int n_env_entries = iface->n_options + 5; 897 char **ppch; 898 899 if (my_environ != NULL) { 900 for (ppch = my_environ; *ppch; ppch++) { 901 free(*ppch); 902 *ppch = NULL; 903 } 904 free(my_environ); 905 } 906 my_environ = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ )); 907 environend = my_environ; 908 909 for (i = 0; i < iface->n_options; i++) { 910 if (strcmp(iface->option[i].name, "up") == 0 911 || strcmp(iface->option[i].name, "down") == 0 912 || strcmp(iface->option[i].name, "pre-up") == 0 913 || strcmp(iface->option[i].name, "post-down") == 0 914 ) { 915 continue; 916 } 917 *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); 918 } 919 920 *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface); 921 *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); 922 *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name); 923 *(environend++) = setlocalenv("%s=%s", "MODE", mode); 924 *(environend++) = setlocalenv("%s=%s", "PATH", startup_PATH); 925} 926 927static int doit(char *str) 928{ 929 if (option_mask32 & (OPT_no_act|OPT_verbose)) { 930 puts(str); 931 } 932 if (!(option_mask32 & OPT_no_act)) { 933 pid_t child; 934 int status; 935 936 fflush(NULL); 937 child = fork(); 938 switch (child) { 939 case -1: /* failure */ 940 return 0; 941 case 0: /* child */ 942 execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, my_environ); 943 exit(127); 944 } 945 waitpid(child, &status, 0); 946 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 947 return 0; 948 } 949 } 950 return 1; 951} 952 953static int execute_all(struct interface_defn_t *ifd, const char *opt) 954{ 955 int i; 956 char *buf; 957 for (i = 0; i < ifd->n_options; i++) { 958 if (strcmp(ifd->option[i].name, opt) == 0) { 959 if (!doit(ifd->option[i].value)) { 960 return 0; 961 } 962 } 963 } 964 965 buf = xasprintf("run-parts /etc/network/if-%s.d", opt); 966 /* heh, we don't bother free'ing it */ 967 return doit(buf); 968} 969 970static int check(char *str) 971{ 972 return str != NULL; 973} 974 975static int iface_up(struct interface_defn_t *iface) 976{ 977 if (!iface->method->up(iface, check)) return -1; 978 set_environ(iface, "start"); 979 if (!execute_all(iface, "pre-up")) return 0; 980 if (!iface->method->up(iface, doit)) return 0; 981 if (!execute_all(iface, "up")) return 0; 982 return 1; 983} 984 985static int iface_down(struct interface_defn_t *iface) 986{ 987 if (!iface->method->down(iface,check)) return -1; 988 set_environ(iface, "stop"); 989 if (!execute_all(iface, "down")) return 0; 990 if (!iface->method->down(iface, doit)) return 0; 991 if (!execute_all(iface, "post-down")) return 0; 992 return 1; 993} 994 995#if ENABLE_FEATURE_IFUPDOWN_MAPPING 996static int popen2(FILE **in, FILE **out, char *command, ...) 997{ 998 va_list ap; 999 char *argv[11] = { command }; 1000 int argc; 1001 int infd[2], outfd[2]; 1002 pid_t pid; 1003 1004 argc = 1; 1005 va_start(ap, command); 1006 while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) { 1007 argc++; 1008 } 1009 argv[argc] = NULL; /* make sure */ 1010 va_end(ap); 1011 1012 if (pipe(infd) != 0) { 1013 return 0; 1014 } 1015 1016 if (pipe(outfd) != 0) { 1017 close(infd[0]); 1018 close(infd[1]); 1019 return 0; 1020 } 1021 1022 fflush(NULL); 1023 switch (pid = fork()) { 1024 case -1: /* failure */ 1025 close(infd[0]); 1026 close(infd[1]); 1027 close(outfd[0]); 1028 close(outfd[1]); 1029 return 0; 1030 case 0: /* child */ 1031 dup2(infd[0], 0); 1032 dup2(outfd[1], 1); 1033 close(infd[0]); 1034 close(infd[1]); 1035 close(outfd[0]); 1036 close(outfd[1]); 1037 BB_EXECVP(command, argv); 1038 exit(127); 1039 default: /* parent */ 1040 *in = fdopen(infd[1], "w"); 1041 *out = fdopen(outfd[0], "r"); 1042 close(infd[0]); 1043 close(outfd[1]); 1044 return pid; 1045 } 1046 /* unreached */ 1047} 1048 1049static char *run_mapping(char *physical, struct mapping_defn_t * map) 1050{ 1051 FILE *in, *out; 1052 int i, status; 1053 pid_t pid; 1054 1055 char *logical = xstrdup(physical); 1056 1057 /* Run the mapping script. */ 1058 pid = popen2(&in, &out, map->script, physical, NULL); 1059 1060 /* popen2() returns 0 on failure. */ 1061 if (pid == 0) 1062 return logical; 1063 1064 /* Write mappings to stdin of mapping script. */ 1065 for (i = 0; i < map->n_mappings; i++) { 1066 fprintf(in, "%s\n", map->mapping[i]); 1067 } 1068 fclose(in); 1069 waitpid(pid, &status, 0); 1070 1071 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 1072 /* If the mapping script exited successfully, try to 1073 * grab a line of output and use that as the name of the 1074 * logical interface. */ 1075 char *new_logical = xmalloc(MAX_INTERFACE_LENGTH); 1076 1077 if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) { 1078 /* If we are able to read a line of output from the script, 1079 * remove any trailing whitespace and use this value 1080 * as the name of the logical interface. */ 1081 char *pch = new_logical + strlen(new_logical) - 1; 1082 1083 while (pch >= new_logical && isspace(*pch)) 1084 *(pch--) = '\0'; 1085 1086 free(logical); 1087 logical = new_logical; 1088 } else { 1089 /* If we are UNABLE to read a line of output, discard our 1090 * freshly allocated memory. */ 1091 free(new_logical); 1092 } 1093 } 1094 1095 fclose(out); 1096 1097 return logical; 1098} 1099#endif /* FEATURE_IFUPDOWN_MAPPING */ 1100 1101static llist_t *find_iface_state(llist_t *state_list, const char *iface) 1102{ 1103 unsigned short iface_len = strlen(iface); 1104 llist_t *search = state_list; 1105 1106 while (search) { 1107 if ((strncmp(search->data, iface, iface_len) == 0) 1108 && (search->data[iface_len] == '=')) { 1109 return search; 1110 } 1111 search = search->link; 1112 } 1113 return NULL; 1114} 1115 1116/* read the previous state from the state file */ 1117static llist_t *read_iface_state(void) 1118{ 1119 llist_t *state_list = NULL; 1120 FILE *state_fp = fopen(CONFIG_IFUPDOWN_IFSTATE_PATH, "r"); 1121 1122 if (state_fp) { 1123 char *start, *end_ptr; 1124 while ((start = xmalloc_fgets(state_fp)) != NULL) { 1125 /* We should only need to check for a single character */ 1126 end_ptr = start + strcspn(start, " \t\n"); 1127 *end_ptr = '\0'; 1128 llist_add_to(&state_list, start); 1129 } 1130 fclose(state_fp); 1131 } 1132 return state_list; 1133} 1134 1135 1136int ifupdown_main(int argc, char **argv); 1137int ifupdown_main(int argc, char **argv) 1138{ 1139 int (*cmds)(struct interface_defn_t *) = NULL; 1140 struct interfaces_file_t *defn; 1141 llist_t *target_list = NULL; 1142 const char *interfaces = "/etc/network/interfaces"; 1143 bool any_failures = 0; 1144 1145 cmds = iface_down; 1146 if (applet_name[2] == 'u') { 1147 /* ifup command */ 1148 cmds = iface_up; 1149 } 1150 1151 getopt32(argv, OPTION_STR, &interfaces); 1152 if (argc - optind > 0) { 1153 if (DO_ALL) bb_show_usage(); 1154 } else { 1155 if (!DO_ALL) bb_show_usage(); 1156 } 1157 1158 debug_noise("reading %s file:\n", interfaces); 1159 defn = read_interfaces(interfaces); 1160 debug_noise("\ndone reading %s\n\n", interfaces); 1161 1162 if (!defn) { 1163 return EXIT_FAILURE; 1164 } 1165 1166 startup_PATH = getenv("PATH"); 1167 if (!startup_PATH) startup_PATH = ""; 1168 1169 /* Create a list of interfaces to work on */ 1170 if (DO_ALL) { 1171 target_list = defn->autointerfaces; 1172 } else { 1173 llist_add_to_end(&target_list, argv[optind]); 1174 } 1175 1176 /* Update the interfaces */ 1177 while (target_list) { 1178 llist_t *iface_list; 1179 struct interface_defn_t *currif; 1180 char *iface; 1181 char *liface; 1182 char *pch; 1183 bool okay = 0; 1184 unsigned cmds_ret; 1185 1186 iface = xstrdup(target_list->data); 1187 target_list = target_list->link; 1188 1189 pch = strchr(iface, '='); 1190 if (pch) { 1191 *pch = '\0'; 1192 liface = xstrdup(pch + 1); 1193 } else { 1194 liface = xstrdup(iface); 1195 } 1196 1197 if (!FORCE) { 1198 llist_t *state_list = read_iface_state(); 1199 const llist_t *iface_state = find_iface_state(state_list, iface); 1200 1201 if (cmds == iface_up) { 1202 /* ifup */ 1203 if (iface_state) { 1204 bb_error_msg("interface %s already configured", iface); 1205 continue; 1206 } 1207 } else { 1208 /* ifdown */ 1209 if (!iface_state) { 1210 bb_error_msg("interface %s not configured", iface); 1211 continue; 1212 } 1213 } 1214 llist_free(state_list, free); 1215 } 1216 1217#if ENABLE_FEATURE_IFUPDOWN_MAPPING 1218 if ((cmds == iface_up) && !NO_MAPPINGS) { 1219 struct mapping_defn_t *currmap; 1220 1221 for (currmap = defn->mappings; currmap; currmap = currmap->next) { 1222 int i; 1223 for (i = 0; i < currmap->n_matches; i++) { 1224 if (fnmatch(currmap->match[i], liface, 0) != 0) 1225 continue; 1226 if (VERBOSE) { 1227 printf("Running mapping script %s on %s\n", currmap->script, liface); 1228 } 1229 liface = run_mapping(iface, currmap); 1230 break; 1231 } 1232 } 1233 } 1234#endif 1235 1236 iface_list = defn->ifaces; 1237 while (iface_list) { 1238 currif = (struct interface_defn_t *) iface_list->data; 1239 if (strcmp(liface, currif->iface) == 0) { 1240 char *oldiface = currif->iface; 1241 1242 okay = 1; 1243 currif->iface = iface; 1244 1245 debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name); 1246 1247 /* Call the cmds function pointer, does either iface_up() or iface_down() */ 1248 cmds_ret = cmds(currif); 1249 if (cmds_ret == -1) { 1250 bb_error_msg("don't seem to have all the variables for %s/%s", 1251 liface, currif->address_family->name); 1252 any_failures = 1; 1253 } else if (cmds_ret == 0) { 1254 any_failures = 1; 1255 } 1256 1257 currif->iface = oldiface; 1258 } 1259 iface_list = iface_list->link; 1260 } 1261 if (VERBOSE) { 1262 puts(""); 1263 } 1264 1265 if (!okay && !FORCE) { 1266 bb_error_msg("ignoring unknown interface %s", liface); 1267 any_failures = 1; 1268 } else if (!NO_ACT) { 1269 /* update the state file */ 1270 FILE *state_fp; 1271 llist_t *state; 1272 llist_t *state_list = read_iface_state(); 1273 llist_t *iface_state = find_iface_state(state_list, iface); 1274 1275 if (cmds == iface_up) { 1276 char * const newiface = xasprintf("%s=%s", iface, liface); 1277 if (iface_state == NULL) { 1278 llist_add_to_end(&state_list, newiface); 1279 } else { 1280 free(iface_state->data); 1281 iface_state->data = newiface; 1282 } 1283 } else { 1284 /* Remove an interface from state_list */ 1285 llist_unlink(&state_list, iface_state); 1286 free(llist_pop(&iface_state)); 1287 } 1288 1289 /* Actually write the new state */ 1290 state_fp = xfopen(CONFIG_IFUPDOWN_IFSTATE_PATH, "w"); 1291 state = state_list; 1292 while (state) { 1293 if (state->data) { 1294 fprintf(state_fp, "%s\n", state->data); 1295 } 1296 state = state->link; 1297 } 1298 fclose(state_fp); 1299 llist_free(state_list, free); 1300 } 1301 } 1302 1303 return any_failures; 1304} 1305