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