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