1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141#include <net/if.h>
142#include "libbb.h"
143#include "common_bufsiz.h"
144
145#include <sys/utsname.h>
146#include <fnmatch.h>
147
148#define MAX_OPT_DEPTH 10
149
150#if ENABLE_FEATURE_IFUPDOWN_MAPPING
151#define MAX_INTERFACE_LENGTH 10
152#endif
153
154#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
155#define IFSTATE_FILE_PATH CONFIG_IFUPDOWN_IFSTATE_PATH
156
157#define debug_noise(args...)
158
159
160struct interface_defn_t;
161
162typedef int execfn(char *command);
163
164struct method_t {
165 const char *name;
166 int (*up)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
167 int (*down)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
168};
169
170struct address_family_t {
171 const char *name;
172 int n_methods;
173 const struct method_t *method;
174};
175
176struct mapping_defn_t {
177 struct mapping_defn_t *next;
178
179 int max_matches;
180 int n_matches;
181 char **match;
182
183 char *script;
184
185 int n_mappings;
186 char **mapping;
187};
188
189struct variable_t {
190 char *name;
191 char *value;
192};
193
194struct interface_defn_t {
195 const struct address_family_t *address_family;
196 const struct method_t *method;
197
198 char *iface;
199 int n_options;
200 struct variable_t *option;
201};
202
203struct interfaces_file_t {
204 llist_t *autointerfaces;
205 llist_t *ifaces;
206 struct mapping_defn_t *mappings;
207};
208
209
210#define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:"
211enum {
212 OPT_do_all = 0x1,
213 OPT_no_act = 0x2,
214 OPT_verbose = 0x4,
215 OPT_force = 0x8,
216 OPT_no_mappings = 0x10,
217};
218#define DO_ALL (option_mask32 & OPT_do_all)
219#define NO_ACT (option_mask32 & OPT_no_act)
220#define VERBOSE (option_mask32 & OPT_verbose)
221#define FORCE (option_mask32 & OPT_force)
222#define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
223
224
225struct globals {
226 char **my_environ;
227 const char *startup_PATH;
228 char *shell;
229} FIX_ALIASING;
230#define G (*(struct globals*)bb_common_bufsiz1)
231#define INIT_G() do { setup_common_bufsiz(); } while (0)
232
233
234static const char keywords_up_down[] ALIGN1 =
235 "up\0"
236 "down\0"
237 "pre-up\0"
238 "post-down\0"
239;
240
241
242#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
243
244static void addstr(char **bufp, const char *str, size_t str_length)
245{
246
247
248
249 char *buf = *bufp;
250 int len = (buf ? strlen(buf) : 0);
251 str_length++;
252 buf = xrealloc(buf, len + str_length);
253
254 safe_strncpy(buf + len, str, str_length);
255 *bufp = buf;
256}
257
258static int strncmpz(const char *l, const char *r, size_t llen)
259{
260 int i = strncmp(l, r, llen);
261
262 if (i == 0)
263 return - (unsigned char)r[llen];
264 return i;
265}
266
267static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
268{
269 int i;
270
271 if (strncmpz(id, "iface", idlen) == 0) {
272
273
274
275
276
277
278
279
280
281
282 return ifd->iface;
283 }
284 if (strncmpz(id, "label", idlen) == 0) {
285 return ifd->iface;
286 }
287 for (i = 0; i < ifd->n_options; i++) {
288 if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
289 return ifd->option[i].value;
290 }
291 }
292 return NULL;
293}
294
295# if ENABLE_FEATURE_IFUPDOWN_IP
296static int count_netmask_bits(const char *dotted_quad)
297{
298
299
300
301
302
303
304
305
306
307
308
309 int result;
310 struct in_addr ip;
311 unsigned d;
312
313 if (inet_aton(dotted_quad, &ip) == 0)
314 return -1;
315 d = ntohl(ip.s_addr);
316 d = ~d;
317 if (d & (d+1))
318 return -1;
319 result = 32;
320 while (d) {
321 d >>= 1;
322 result--;
323 }
324 return result;
325}
326# endif
327
328static char *parse(const char *command, struct interface_defn_t *ifd)
329{
330 size_t old_pos[MAX_OPT_DEPTH] = { 0 };
331 smallint okay[MAX_OPT_DEPTH] = { 1 };
332 int opt_depth = 1;
333 char *result = NULL;
334
335 while (*command) {
336 switch (*command) {
337 default:
338 addstr(&result, command, 1);
339 command++;
340 break;
341 case '\\':
342 if (command[1])
343 command++;
344 addstr(&result, command, 1);
345 command++;
346 break;
347 case '[':
348 if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
349 old_pos[opt_depth] = result ? strlen(result) : 0;
350 okay[opt_depth] = 1;
351 opt_depth++;
352 command += 2;
353 } else {
354 addstr(&result, command, 1);
355 command++;
356 }
357 break;
358 case ']':
359 if (command[1] == ']' && opt_depth > 1) {
360 opt_depth--;
361 if (!okay[opt_depth]) {
362 result[old_pos[opt_depth]] = '\0';
363 }
364 command += 2;
365 } else {
366 addstr(&result, command, 1);
367 command++;
368 }
369 break;
370 case '%':
371 {
372 char *nextpercent;
373 char *varvalue;
374
375 command++;
376 nextpercent = strchr(command, '%');
377 if (!nextpercent) {
378
379 free(result);
380 return NULL;
381 }
382
383 varvalue = get_var(command, nextpercent - command, ifd);
384
385 if (varvalue) {
386# if ENABLE_FEATURE_IFUPDOWN_IP
387
388
389
390 if (is_prefixed_with(command, "hwaddress")) {
391 varvalue = skip_whitespace(skip_non_whitespace(varvalue));
392 }
393# endif
394 addstr(&result, varvalue, strlen(varvalue));
395 } else {
396# if ENABLE_FEATURE_IFUPDOWN_IP
397
398
399 if (is_prefixed_with(command, "bnmask")) {
400 unsigned res;
401 varvalue = get_var("netmask", 7, ifd);
402 if (varvalue) {
403 res = count_netmask_bits(varvalue);
404 if (res > 0) {
405 const char *argument = utoa(res);
406 addstr(&result, argument, strlen(argument));
407 command = nextpercent + 1;
408 break;
409 }
410 }
411 }
412# endif
413 okay[opt_depth - 1] = 0;
414 }
415
416 command = nextpercent + 1;
417 }
418 break;
419 }
420 }
421
422 if (opt_depth > 1) {
423
424 free(result);
425 return NULL;
426 }
427
428 if (!okay[0]) {
429
430 free(result);
431 return NULL;
432 }
433
434 return result;
435}
436
437
438static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec)
439{
440 char *out;
441 int ret;
442
443 out = parse(command, ifd);
444 if (!out) {
445
446 return 0;
447 }
448
449 ret = out[0] ? (*exec)(out) : 1;
450
451 free(out);
452 if (ret != 1) {
453 return 0;
454 }
455 return 1;
456}
457
458#endif
459
460
461#if ENABLE_FEATURE_IFUPDOWN_IPV6
462
463static int FAST_FUNC loopback_up6(struct interface_defn_t *ifd, execfn *exec)
464{
465# if ENABLE_FEATURE_IFUPDOWN_IP
466 int result;
467 result = execute("ip addr add ::1 dev %iface%", ifd, exec);
468 result += execute("ip link set %iface% up", ifd, exec);
469 return ((result == 2) ? 2 : 0);
470# else
471 return execute("ifconfig %iface% add ::1", ifd, exec);
472# endif
473}
474
475static int FAST_FUNC loopback_down6(struct interface_defn_t *ifd, execfn *exec)
476{
477# if ENABLE_FEATURE_IFUPDOWN_IP
478 return execute("ip link set %iface% down", ifd, exec);
479# else
480 return execute("ifconfig %iface% del ::1", ifd, exec);
481# endif
482}
483
484static int FAST_FUNC manual_up_down6(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
485{
486 return 1;
487}
488
489static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec)
490{
491 int result;
492# if ENABLE_FEATURE_IFUPDOWN_IP
493 result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
494 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
495
496 result += execute("[[ip route add ::/0 via %gateway% dev %iface%]][[ metric %metric%]]", ifd, exec);
497# else
498 result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
499 result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
500 result += execute("[[route -A inet6 add ::/0 gw %gateway%[[ metric %metric%]]]]", ifd, exec);
501# endif
502 return ((result == 3) ? 3 : 0);
503}
504
505static int FAST_FUNC static_down6(struct interface_defn_t *ifd, execfn *exec)
506{
507 if (!if_nametoindex(ifd->iface))
508 return 1;
509# if ENABLE_FEATURE_IFUPDOWN_IP
510 return execute("ip link set %iface% down", ifd, exec);
511# else
512 return execute("ifconfig %iface% down", ifd, exec);
513# endif
514}
515
516# if ENABLE_FEATURE_IFUPDOWN_IP
517static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
518{
519 int result;
520 result = execute("ip tunnel add %iface% mode sit remote "
521 "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec);
522 result += execute("ip link set %iface% up", ifd, exec);
523 result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
524
525 result += execute("[[ip route add ::/0 via %gateway% dev %iface%]]", ifd, exec);
526 return ((result == 4) ? 4 : 0);
527}
528
529static int FAST_FUNC v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
530{
531 return execute("ip tunnel del %iface%", ifd, exec);
532}
533# endif
534
535static const struct method_t methods6[] = {
536# if ENABLE_FEATURE_IFUPDOWN_IP
537 { "v4tunnel" , v4tunnel_up , v4tunnel_down , },
538# endif
539 { "static" , static_up6 , static_down6 , },
540 { "manual" , manual_up_down6 , manual_up_down6 , },
541 { "loopback" , loopback_up6 , loopback_down6 , },
542};
543
544static const struct address_family_t addr_inet6 = {
545 "inet6",
546 ARRAY_SIZE(methods6),
547 methods6
548};
549
550#endif
551
552
553#if ENABLE_FEATURE_IFUPDOWN_IPV4
554
555static int FAST_FUNC loopback_up(struct interface_defn_t *ifd, execfn *exec)
556{
557# if ENABLE_FEATURE_IFUPDOWN_IP
558 int result;
559 result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
560 result += execute("ip link set %iface% up", ifd, exec);
561 return ((result == 2) ? 2 : 0);
562# else
563 return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
564# endif
565}
566
567static int FAST_FUNC loopback_down(struct interface_defn_t *ifd, execfn *exec)
568{
569# if ENABLE_FEATURE_IFUPDOWN_IP
570 int result;
571 result = execute("ip addr flush dev %iface%", ifd, exec);
572 result += execute("ip link set %iface% down", ifd, exec);
573 return ((result == 2) ? 2 : 0);
574# else
575 return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec);
576# endif
577}
578
579static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
580{
581 int result;
582# if ENABLE_FEATURE_IFUPDOWN_IP
583 result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
584 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
585 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
586 result += execute("[[ip route add default via %gateway% dev %iface%[[ metric %metric%]]]]", ifd, exec);
587 return ((result == 3) ? 3 : 0);
588# else
589
590
591 result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
592 ifd, exec);
593 result += execute("ifconfig %iface% %address% netmask %netmask%"
594 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]]",
595 ifd, exec);
596 result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec);
597 return ((result == 3) ? 3 : 0);
598# endif
599}
600
601static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec)
602{
603 int result;
604
605 if (!if_nametoindex(ifd->iface))
606 return 2;
607# if ENABLE_FEATURE_IFUPDOWN_IP
608
609
610
611 result = execute("ip addr flush dev %iface%[[ label %label%]]", ifd, exec);
612 result += execute("ip link set %iface% down", ifd, exec);
613# else
614
615
616
617 result = 1;
618 result += execute("ifconfig %iface% down", ifd, exec);
619# endif
620 return ((result == 2) ? 2 : 0);
621}
622
623# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
624struct dhcp_client_t {
625 const char *name;
626 const char *startcmd;
627 const char *stopcmd;
628};
629
630static const struct dhcp_client_t ext_dhcp_clients[] = {
631 { "dhcpcd",
632 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
633 "dhcpcd -k %iface%",
634 },
635 { "dhclient",
636 "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
637 "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
638 },
639 { "pump",
640 "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
641 "pump -i %iface% -k",
642 },
643 { "udhcpc",
644 "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -x hostname:%hostname%]][[ -c %client%]]"
645 "[[ -s %script%]][[ %udhcpc_opts%]]",
646 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
647 },
648};
649# endif
650
651# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
652static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
653{
654 unsigned i;
655# if ENABLE_FEATURE_IFUPDOWN_IP
656
657 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
658 return 0;
659# else
660
661 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
662 return 0;
663# endif
664 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
665 if (executable_exists(ext_dhcp_clients[i].name))
666 return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
667 }
668 bb_simple_error_msg("no dhcp clients found");
669 return 0;
670}
671# elif ENABLE_UDHCPC
672static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
673{
674# if ENABLE_FEATURE_IFUPDOWN_IP
675
676 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
677 return 0;
678# else
679
680 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
681 return 0;
682# endif
683 return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
684 "-i %iface%[[ -x hostname:%hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
685 ifd, exec);
686}
687# else
688static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM,
689 execfn *exec UNUSED_PARAM)
690{
691 return 0;
692}
693# endif
694
695# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
696static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
697{
698 int result = 0;
699 unsigned i;
700
701 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
702 if (executable_exists(ext_dhcp_clients[i].name)) {
703 result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
704 if (result)
705 break;
706 }
707 }
708
709 if (!result)
710 bb_simple_error_msg("warning: no dhcp clients found and stopped");
711
712
713
714 usleep(100000);
715 result += static_down(ifd, exec);
716 return ((result == 3) ? 3 : 0);
717}
718# elif ENABLE_UDHCPC
719static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
720{
721 int result;
722 result = execute(
723 "test -f /var/run/udhcpc.%iface%.pid && "
724 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
725 ifd, exec);
726
727
728
729
730
731 usleep(100000);
732 result += static_down(ifd, exec);
733 return ((result == 3) ? 3 : 0);
734}
735# else
736static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM,
737 execfn *exec UNUSED_PARAM)
738{
739 return 0;
740}
741# endif
742
743static int FAST_FUNC manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
744{
745 return 1;
746}
747
748static int FAST_FUNC bootp_up(struct interface_defn_t *ifd, execfn *exec)
749{
750 return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
751 "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
752 " --returniffail --serverbcast", ifd, exec);
753}
754
755static int FAST_FUNC ppp_up(struct interface_defn_t *ifd, execfn *exec)
756{
757 return execute("pon[[ %provider%]]", ifd, exec);
758}
759
760static int FAST_FUNC ppp_down(struct interface_defn_t *ifd, execfn *exec)
761{
762 return execute("poff[[ %provider%]]", ifd, exec);
763}
764
765static int FAST_FUNC wvdial_up(struct interface_defn_t *ifd, execfn *exec)
766{
767 return execute("start-stop-daemon --start -x wvdial "
768 "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec);
769}
770
771static int FAST_FUNC wvdial_down(struct interface_defn_t *ifd, execfn *exec)
772{
773 return execute("start-stop-daemon --stop -x wvdial "
774 "-p /var/run/wvdial.%iface% -s 2", ifd, exec);
775}
776
777static const struct method_t methods[] = {
778 { "manual" , manual_up_down, manual_up_down, },
779 { "wvdial" , wvdial_up , wvdial_down , },
780 { "ppp" , ppp_up , ppp_down , },
781 { "static" , static_up , static_down , },
782 { "bootp" , bootp_up , static_down , },
783 { "dhcp" , dhcp_up , dhcp_down , },
784 { "loopback", loopback_up , loopback_down , },
785};
786
787static const struct address_family_t addr_inet = {
788 "inet",
789 ARRAY_SIZE(methods),
790 methods
791};
792
793#endif
794
795static int FAST_FUNC link_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
796{
797 return 1;
798}
799
800static const struct method_t link_methods[] = {
801 { "none", link_up_down, link_up_down }
802};
803
804static const struct address_family_t addr_link = {
805 "link", ARRAY_SIZE(link_methods), link_methods
806};
807
808
809
810
811static char *next_word(char **buf)
812{
813 unsigned length;
814 char *word;
815
816
817 word = skip_whitespace(*buf);
818
819
820 if (*word == '\0')
821 return NULL;
822
823
824 length = strcspn(word, " \t\n");
825
826
827 if (word[length] != '\0')
828 word[length++] = '\0';
829
830 *buf = skip_whitespace(word + length);
831
832 return word;
833}
834
835static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name)
836{
837 int i;
838
839 if (!name)
840 return NULL;
841
842 for (i = 0; af[i]; i++) {
843 if (strcmp(af[i]->name, name) == 0) {
844 return af[i];
845 }
846 }
847 return NULL;
848}
849
850static const struct method_t *get_method(const struct address_family_t *af, char *name)
851{
852 int i;
853
854 if (!name)
855 return NULL;
856
857 for (i = 0; i < af->n_methods; i++) {
858 if (strcmp(af->method[i].name, name) == 0) {
859 return &af->method[i];
860 }
861 }
862 return NULL;
863}
864
865static struct interfaces_file_t *read_interfaces(const char *filename, struct interfaces_file_t *defn)
866{
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885#if ENABLE_FEATURE_IFUPDOWN_MAPPING
886 struct mapping_defn_t *currmap = NULL;
887#endif
888 struct interface_defn_t *currif = NULL;
889 FILE *f;
890 char *buf;
891 char *first_word;
892 char *rest_of_line;
893 enum { NONE, IFACE, MAPPING } currently_processing = NONE;
894
895 if (!defn)
896 defn = xzalloc(sizeof(*defn));
897
898 debug_noise("reading %s file:\n", filename);
899 f = xfopen_for_read(filename);
900
901 while ((buf = xmalloc_fgetline(f)) != NULL) {
902#if ENABLE_DESKTOP
903
904 char *p;
905 while ((p = last_char_is(buf, '\\')) != NULL) {
906 *p = '\0';
907 rest_of_line = xmalloc_fgetline(f);
908 if (!rest_of_line)
909 break;
910 p = xasprintf("%s%s", buf, rest_of_line);
911 free(buf);
912 free(rest_of_line);
913 buf = p;
914 }
915#endif
916 rest_of_line = buf;
917 first_word = next_word(&rest_of_line);
918 if (!first_word || *first_word == '#') {
919 free(buf);
920 continue;
921 }
922
923 if (strcmp(first_word, "mapping") == 0) {
924#if ENABLE_FEATURE_IFUPDOWN_MAPPING
925 currmap = xzalloc(sizeof(*currmap));
926
927 while ((first_word = next_word(&rest_of_line)) != NULL) {
928 currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches);
929 currmap->match[currmap->n_matches++] = xstrdup(first_word);
930 }
931
932
933
934 {
935 struct mapping_defn_t **where = &defn->mappings;
936 while (*where != NULL) {
937 where = &(*where)->next;
938 }
939 *where = currmap;
940
941 }
942 debug_noise("Added mapping\n");
943#endif
944 currently_processing = MAPPING;
945 } else if (strcmp(first_word, "iface") == 0) {
946 static const struct address_family_t *const addr_fams[] = {
947#if ENABLE_FEATURE_IFUPDOWN_IPV4
948 &addr_inet,
949#endif
950#if ENABLE_FEATURE_IFUPDOWN_IPV6
951 &addr_inet6,
952#endif
953 &addr_link,
954 NULL
955 };
956 char *iface_name;
957 char *address_family_name;
958 char *method_name;
959
960 currif = xzalloc(sizeof(*currif));
961 iface_name = next_word(&rest_of_line);
962 address_family_name = next_word(&rest_of_line);
963 method_name = next_word(&rest_of_line);
964
965 if (method_name == NULL)
966 bb_error_msg_and_die("too few parameters for line \"%s\"", buf);
967
968
969 rest_of_line = skip_whitespace(rest_of_line);
970
971 if (rest_of_line[0] != '\0' )
972 bb_error_msg_and_die("too many parameters \"%s\"", buf);
973
974 currif->iface = xstrdup(iface_name);
975
976 currif->address_family = get_address_family(addr_fams, address_family_name);
977 if (!currif->address_family)
978 bb_error_msg_and_die("unknown address type \"%s\"", address_family_name);
979
980 currif->method = get_method(currif->address_family, method_name);
981 if (!currif->method)
982 bb_error_msg_and_die("unknown method \"%s\"", method_name);
983#if 0
984
985
986
987
988
989
990
991
992
993
994
995
996 llist_t *iface_list;
997 for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
998 struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
999 if ((strcmp(tmp->iface, currif->iface) == 0)
1000 && (tmp->address_family == currif->address_family)
1001 ) {
1002 bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface);
1003 }
1004 }
1005#endif
1006 llist_add_to_end(&(defn->ifaces), (char*)currif);
1007
1008 debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
1009 currently_processing = IFACE;
1010 } else if (strcmp(first_word, "auto") == 0) {
1011 while ((first_word = next_word(&rest_of_line)) != NULL) {
1012
1013
1014 if (llist_find_str(defn->autointerfaces, first_word)) {
1015 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
1016 }
1017
1018
1019 llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word));
1020 debug_noise("\nauto %s\n", first_word);
1021 }
1022 currently_processing = NONE;
1023 } else if (strcmp(first_word, "source") == 0) {
1024 read_interfaces(next_word(&rest_of_line), defn);
1025 } else if (is_prefixed_with(first_word, "source-dir")) {
1026 const char *dirpath;
1027 DIR *dir;
1028 struct dirent *entry;
1029
1030 dirpath = next_word(&rest_of_line);
1031 dir = xopendir(dirpath);
1032 while ((entry = readdir(dir)) != NULL) {
1033 char *path;
1034 if (entry->d_name[0] == '.')
1035 continue;
1036 path = concat_path_file(dirpath, entry->d_name);
1037 read_interfaces(path, defn);
1038 free(path);
1039 }
1040 closedir(dir);
1041 } else {
1042 switch (currently_processing) {
1043 case IFACE:
1044 if (rest_of_line[0] == '\0')
1045 bb_error_msg_and_die("option with empty value \"%s\"", buf);
1046
1047 if (strcmp(first_word, "post-up") == 0)
1048 first_word += 5;
1049 else if (strcmp(first_word, "pre-down") == 0)
1050 first_word += 4;
1051
1052
1053 if (index_in_strings(keywords_up_down, first_word) < 0) {
1054 int i;
1055 for (i = 0; i < currif->n_options; i++) {
1056 if (strcmp(currif->option[i].name, first_word) == 0)
1057 bb_error_msg_and_die("duplicate option \"%s\"", buf);
1058 }
1059 }
1060 debug_noise("\t%s=%s\n", first_word, rest_of_line);
1061 currif->option = xrealloc_vector(currif->option, 4, currif->n_options);
1062 currif->option[currif->n_options].name = xstrdup(first_word);
1063 currif->option[currif->n_options].value = xstrdup(rest_of_line);
1064 currif->n_options++;
1065 break;
1066 case MAPPING:
1067#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1068 if (strcmp(first_word, "script") == 0) {
1069 if (currmap->script != NULL)
1070 bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf);
1071 currmap->script = xstrdup(next_word(&rest_of_line));
1072 } else if (strcmp(first_word, "map") == 0) {
1073 currmap->mapping = xrealloc_vector(currmap->mapping, 2, currmap->n_mappings);
1074 currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line));
1075 currmap->n_mappings++;
1076 } else {
1077 bb_error_msg_and_die("misplaced option \"%s\"", buf);
1078 }
1079#endif
1080 break;
1081 case NONE:
1082 default:
1083 bb_error_msg_and_die("misplaced option \"%s\"", buf);
1084 }
1085 }
1086 free(buf);
1087 }
1088
1089 if (ferror(f) != 0) {
1090
1091 bb_error_msg_and_die("%s: I/O error", filename);
1092 }
1093 fclose(f);
1094 debug_noise("\ndone reading %s\n\n", filename);
1095
1096 return defn;
1097}
1098
1099static char *setlocalenv(const char *format, const char *name, const char *value)
1100{
1101 char *result;
1102 char *dst;
1103 char *src;
1104 char c;
1105
1106 result = xasprintf(format, name, value);
1107
1108 for (dst = src = result; (c = *src) != '=' && c; src++) {
1109 if (c == '-')
1110 c = '_';
1111 if (c >= 'a' && c <= 'z')
1112 c -= ('a' - 'A');
1113 if (isalnum(c) || c == '_')
1114 *dst++ = c;
1115 }
1116 overlapping_strcpy(dst, src);
1117
1118 return result;
1119}
1120
1121static void set_environ(struct interface_defn_t *iface, const char *mode, const char *opt)
1122{
1123 int i;
1124 char **pp;
1125
1126 if (G.my_environ != NULL) {
1127 for (pp = G.my_environ; *pp; pp++) {
1128 free(*pp);
1129 }
1130 free(G.my_environ);
1131 }
1132
1133
1134 G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 7));
1135 pp = G.my_environ;
1136
1137 for (i = 0; i < iface->n_options; i++) {
1138 if (index_in_strings(keywords_up_down, iface->option[i].name) >= 0) {
1139 continue;
1140 }
1141 *pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
1142 }
1143
1144 *pp++ = setlocalenv("%s=%s", "IFACE", iface->iface);
1145 *pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
1146 *pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name);
1147 *pp++ = setlocalenv("%s=%s", "MODE", mode);
1148 *pp++ = setlocalenv("%s=%s", "PHASE", opt);
1149 if (G.startup_PATH)
1150 *pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH);
1151}
1152
1153static int doit(char *str)
1154{
1155 if (option_mask32 & (OPT_no_act|OPT_verbose)) {
1156 puts(str);
1157 }
1158 if (!(option_mask32 & OPT_no_act)) {
1159 pid_t child;
1160 int status;
1161
1162 fflush_all();
1163 child = vfork();
1164 if (child < 0)
1165 return 0;
1166 if (child == 0) {
1167 execle(G.shell, G.shell, "-c", str, (char *) NULL, G.my_environ);
1168 _exit(127);
1169 }
1170 safe_waitpid(child, &status, 0);
1171 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1172 return 0;
1173 }
1174 }
1175 return 1;
1176}
1177
1178static int execute_all(struct interface_defn_t *ifd, const char *opt)
1179{
1180
1181
1182
1183 char buf[sizeof("run-parts /etc/network/if-%s.d")
1184 + sizeof("post-down")
1185 + 8
1186 ];
1187 int i;
1188
1189 for (i = 0; i < ifd->n_options; i++) {
1190 if (strcmp(ifd->option[i].name, opt) == 0) {
1191 if (!doit(ifd->option[i].value)) {
1192 return 0;
1193 }
1194 }
1195 }
1196
1197
1198
1199
1200
1201
1202 sprintf(buf, "run-parts /etc/network/if-%s.d", opt);
1203 return doit(buf);
1204}
1205
1206static int check(char *str)
1207{
1208 return str != NULL;
1209}
1210
1211static int iface_up(struct interface_defn_t *iface)
1212{
1213 if (!iface->method->up(iface, check)) return -1;
1214 set_environ(iface, "start", "pre-up");
1215 if (!execute_all(iface, "pre-up")) return 0;
1216 if (!iface->method->up(iface, doit)) return 0;
1217 set_environ(iface, "start", "post-up");
1218 if (!execute_all(iface, "up")) return 0;
1219 return 1;
1220}
1221
1222static int iface_down(struct interface_defn_t *iface)
1223{
1224 if (!iface->method->down(iface, check)) return -1;
1225 set_environ(iface, "stop", "pre-down");
1226 if (!execute_all(iface, "down")) return 0;
1227 if (!iface->method->down(iface, doit)) return 0;
1228 set_environ(iface, "stop", "post-down");
1229 if (!execute_all(iface, "post-down")) return 0;
1230 return 1;
1231}
1232
1233#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1234static int popen2(FILE **in, FILE **out, char *command, char *param)
1235{
1236 char *argv[3] = { command, param, NULL };
1237 struct fd_pair infd, outfd;
1238 pid_t pid;
1239
1240 xpiped_pair(infd);
1241 xpiped_pair(outfd);
1242
1243 fflush_all();
1244 pid = xvfork();
1245
1246 if (pid == 0) {
1247
1248
1249 close(infd.wr);
1250 close(outfd.rd);
1251 xmove_fd(infd.rd, 0);
1252 xmove_fd(outfd.wr, 1);
1253 BB_EXECVP_or_die(argv);
1254 }
1255
1256 close(infd.rd);
1257 close(outfd.wr);
1258 *in = xfdopen_for_write(infd.wr);
1259 *out = xfdopen_for_read(outfd.rd);
1260 return pid;
1261}
1262
1263static char *run_mapping(char *physical, struct mapping_defn_t *map)
1264{
1265 FILE *in, *out;
1266 int i, status;
1267 pid_t pid;
1268
1269 char *logical = xstrdup(physical);
1270
1271
1272 pid = popen2(&in, &out, map->script, physical);
1273
1274
1275 for (i = 0; i < map->n_mappings; i++) {
1276 fprintf(in, "%s\n", map->mapping[i]);
1277 }
1278 fclose(in);
1279 safe_waitpid(pid, &status, 0);
1280
1281 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
1282
1283
1284
1285 char *new_logical = xmalloc_fgetline(out);
1286
1287 if (new_logical) {
1288
1289
1290
1291 char *pch = new_logical + strlen(new_logical) - 1;
1292
1293 while (pch >= new_logical && isspace(*pch))
1294 *(pch--) = '\0';
1295
1296 free(logical);
1297 logical = new_logical;
1298 }
1299 }
1300
1301 fclose(out);
1302
1303 return logical;
1304}
1305#endif
1306
1307static llist_t *find_iface_state(llist_t *state_list, const char *iface)
1308{
1309 llist_t *search = state_list;
1310
1311 while (search) {
1312 char *after_iface = is_prefixed_with(search->data, iface);
1313 if (after_iface
1314 && *after_iface == '='
1315 ) {
1316 return search;
1317 }
1318 search = search->link;
1319 }
1320 return NULL;
1321}
1322
1323
1324static llist_t *read_iface_state(void)
1325{
1326 llist_t *state_list = NULL;
1327 FILE *state_fp = fopen_for_read(IFSTATE_FILE_PATH);
1328
1329 if (state_fp) {
1330 char *start, *end_ptr;
1331 while ((start = xmalloc_fgets(state_fp)) != NULL) {
1332
1333 end_ptr = start + strcspn(start, " \t\n");
1334 *end_ptr = '\0';
1335 llist_add_to(&state_list, start);
1336 }
1337 fclose(state_fp);
1338 }
1339 return state_list;
1340}
1341
1342
1343static FILE *open_new_state_file(void)
1344{
1345 int fd, flags, cnt;
1346
1347 cnt = 0;
1348 flags = (O_WRONLY | O_CREAT | O_EXCL);
1349 for (;;) {
1350 fd = open(IFSTATE_FILE_PATH".new", flags, 0666);
1351 if (fd >= 0)
1352 break;
1353 if (errno != EEXIST
1354 || flags == (O_WRONLY | O_CREAT | O_TRUNC)
1355 ) {
1356 bb_perror_msg_and_die("can't open '%s'",
1357 IFSTATE_FILE_PATH".new");
1358 }
1359
1360 if (cnt > 30) {
1361
1362
1363
1364 flags = (O_WRONLY | O_CREAT | O_TRUNC);
1365 continue;
1366 }
1367 msleep(cnt);
1368 cnt++;
1369 }
1370
1371 return xfdopen_for_write(fd);
1372}
1373
1374int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1375int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1376{
1377 int (*cmds)(struct interface_defn_t *);
1378 struct interfaces_file_t *defn;
1379 llist_t *target_list = NULL;
1380 const char *interfaces = "/etc/network/interfaces";
1381 bool any_failures = 0;
1382
1383 INIT_G();
1384
1385 G.startup_PATH = getenv("PATH");
1386 G.shell = xstrdup(get_shell_name());
1387
1388 if (ENABLE_IFUP
1389 && (!ENABLE_IFDOWN || applet_name[2] == 'u')
1390 ) {
1391
1392 cmds = iface_up;
1393 } else {
1394 cmds = iface_down;
1395 }
1396
1397 getopt32(argv, OPTION_STR, &interfaces);
1398 argv += optind;
1399 if (argv[0]) {
1400 if (DO_ALL) bb_show_usage();
1401 } else {
1402 if (!DO_ALL) bb_show_usage();
1403 }
1404
1405 defn = read_interfaces(interfaces, NULL);
1406
1407
1408 if (DO_ALL) {
1409 target_list = defn->autointerfaces;
1410 } else {
1411 llist_add_to_end(&target_list, argv[0]);
1412 }
1413
1414
1415 while (target_list) {
1416 llist_t *iface_list;
1417 struct interface_defn_t *currif;
1418 char *iface;
1419 char *liface;
1420 char *pch;
1421 bool okay = 0;
1422 int cmds_ret;
1423 bool curr_failure = 0;
1424
1425 iface = xstrdup(target_list->data);
1426 target_list = target_list->link;
1427
1428 pch = strchr(iface, '=');
1429 if (pch) {
1430 *pch = '\0';
1431 liface = xstrdup(pch + 1);
1432 } else {
1433 liface = xstrdup(iface);
1434 }
1435
1436 if (!FORCE) {
1437 llist_t *state_list = read_iface_state();
1438 const llist_t *iface_state = find_iface_state(state_list, iface);
1439
1440 if (cmds == iface_up) {
1441
1442 if (iface_state) {
1443 bb_error_msg("interface %s already configured", iface);
1444 goto next;
1445 }
1446 } else {
1447
1448 if (!iface_state) {
1449 bb_error_msg("interface %s not configured", iface);
1450 goto next;
1451 }
1452 }
1453 llist_free(state_list, free);
1454 }
1455
1456#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1457 if ((cmds == iface_up) && !NO_MAPPINGS) {
1458 struct mapping_defn_t *currmap;
1459
1460 for (currmap = defn->mappings; currmap; currmap = currmap->next) {
1461 int i;
1462 for (i = 0; i < currmap->n_matches; i++) {
1463 if (fnmatch(currmap->match[i], liface, 0) != 0)
1464 continue;
1465 if (VERBOSE) {
1466 printf("Running mapping script %s on %s\n", currmap->script, liface);
1467 }
1468 liface = run_mapping(iface, currmap);
1469 break;
1470 }
1471 }
1472 }
1473#endif
1474
1475 iface_list = defn->ifaces;
1476 while (iface_list) {
1477 currif = (struct interface_defn_t *) iface_list->data;
1478 if (strcmp(liface, currif->iface) == 0) {
1479 char *oldiface = currif->iface;
1480
1481 okay = 1;
1482 currif->iface = iface;
1483
1484 debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
1485
1486
1487 cmds_ret = cmds(currif);
1488 if (cmds_ret == -1) {
1489 bb_error_msg("don't have all variables for %s/%s",
1490 liface, currif->address_family->name);
1491 any_failures = curr_failure = 1;
1492 } else if (cmds_ret == 0) {
1493 any_failures = curr_failure = 1;
1494 }
1495
1496 currif->iface = oldiface;
1497 }
1498 iface_list = iface_list->link;
1499 }
1500 if (VERBOSE) {
1501 bb_putchar('\n');
1502 }
1503
1504 if (!okay && !FORCE) {
1505 bb_error_msg("ignoring unknown interface %s", liface);
1506 any_failures = 1;
1507 } else if (!NO_ACT) {
1508
1509 FILE *new_state_fp = open_new_state_file();
1510 llist_t *state;
1511 llist_t *state_list = read_iface_state();
1512 llist_t *iface_state = find_iface_state(state_list, iface);
1513
1514 if (cmds == iface_up && !curr_failure) {
1515 char *newiface = xasprintf("%s=%s", iface, liface);
1516 if (!iface_state) {
1517 llist_add_to_end(&state_list, newiface);
1518 } else {
1519 free(iface_state->data);
1520 iface_state->data = newiface;
1521 }
1522 } else {
1523
1524 llist_unlink(&state_list, iface_state);
1525 free(llist_pop(&iface_state));
1526 }
1527
1528
1529 state = state_list;
1530 while (state) {
1531 if (state->data) {
1532 fprintf(new_state_fp, "%s\n", state->data);
1533 }
1534 state = state->link;
1535 }
1536 fclose(new_state_fp);
1537 xrename(IFSTATE_FILE_PATH".new", IFSTATE_FILE_PATH);
1538 llist_free(state_list, free);
1539 }
1540 next:
1541 free(iface);
1542 free(liface);
1543 }
1544
1545 return any_failures;
1546}
1547