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_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_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 {
1026 switch (currently_processing) {
1027 case IFACE:
1028 if (rest_of_line[0] == '\0')
1029 bb_error_msg_and_die("option with empty value \"%s\"", buf);
1030
1031 if (strcmp(first_word, "post-up") == 0)
1032 first_word += 5;
1033 else if (strcmp(first_word, "pre-down") == 0)
1034 first_word += 4;
1035
1036
1037 if (index_in_strings(keywords_up_down, first_word) < 0) {
1038 int i;
1039 for (i = 0; i < currif->n_options; i++) {
1040 if (strcmp(currif->option[i].name, first_word) == 0)
1041 bb_error_msg_and_die("duplicate option \"%s\"", buf);
1042 }
1043 }
1044 debug_noise("\t%s=%s\n", first_word, rest_of_line);
1045 currif->option = xrealloc_vector(currif->option, 4, currif->n_options);
1046 currif->option[currif->n_options].name = xstrdup(first_word);
1047 currif->option[currif->n_options].value = xstrdup(rest_of_line);
1048 currif->n_options++;
1049 break;
1050 case MAPPING:
1051#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1052 if (strcmp(first_word, "script") == 0) {
1053 if (currmap->script != NULL)
1054 bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf);
1055 currmap->script = xstrdup(next_word(&rest_of_line));
1056 } else if (strcmp(first_word, "map") == 0) {
1057 currmap->mapping = xrealloc_vector(currmap->mapping, 2, currmap->n_mappings);
1058 currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line));
1059 currmap->n_mappings++;
1060 } else {
1061 bb_error_msg_and_die("misplaced option \"%s\"", buf);
1062 }
1063#endif
1064 break;
1065 case NONE:
1066 default:
1067 bb_error_msg_and_die("misplaced option \"%s\"", buf);
1068 }
1069 }
1070 free(buf);
1071 }
1072
1073 if (ferror(f) != 0) {
1074
1075 bb_error_msg_and_die("%s: I/O error", filename);
1076 }
1077 fclose(f);
1078 debug_noise("\ndone reading %s\n\n", filename);
1079
1080 return defn;
1081}
1082
1083static char *setlocalenv(const char *format, const char *name, const char *value)
1084{
1085 char *result;
1086 char *dst;
1087 char *src;
1088 char c;
1089
1090 result = xasprintf(format, name, value);
1091
1092 for (dst = src = result; (c = *src) != '=' && c; src++) {
1093 if (c == '-')
1094 c = '_';
1095 if (c >= 'a' && c <= 'z')
1096 c -= ('a' - 'A');
1097 if (isalnum(c) || c == '_')
1098 *dst++ = c;
1099 }
1100 overlapping_strcpy(dst, src);
1101
1102 return result;
1103}
1104
1105static void set_environ(struct interface_defn_t *iface, const char *mode, const char *opt)
1106{
1107 int i;
1108 char **pp;
1109
1110 if (G.my_environ != NULL) {
1111 for (pp = G.my_environ; *pp; pp++) {
1112 free(*pp);
1113 }
1114 free(G.my_environ);
1115 }
1116
1117
1118 G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 7));
1119 pp = G.my_environ;
1120
1121 for (i = 0; i < iface->n_options; i++) {
1122 if (index_in_strings(keywords_up_down, iface->option[i].name) >= 0) {
1123 continue;
1124 }
1125 *pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
1126 }
1127
1128 *pp++ = setlocalenv("%s=%s", "IFACE", iface->iface);
1129 *pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
1130 *pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name);
1131 *pp++ = setlocalenv("%s=%s", "MODE", mode);
1132 *pp++ = setlocalenv("%s=%s", "PHASE", opt);
1133 if (G.startup_PATH)
1134 *pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH);
1135}
1136
1137static int doit(char *str)
1138{
1139 if (option_mask32 & (OPT_no_act|OPT_verbose)) {
1140 puts(str);
1141 }
1142 if (!(option_mask32 & OPT_no_act)) {
1143 pid_t child;
1144 int status;
1145
1146 fflush_all();
1147 child = vfork();
1148 if (child < 0)
1149 return 0;
1150 if (child == 0) {
1151 execle(G.shell, G.shell, "-c", str, (char *) NULL, G.my_environ);
1152 _exit(127);
1153 }
1154 safe_waitpid(child, &status, 0);
1155 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1156 return 0;
1157 }
1158 }
1159 return 1;
1160}
1161
1162static int execute_all(struct interface_defn_t *ifd, const char *opt)
1163{
1164 int i;
1165 char *buf;
1166 for (i = 0; i < ifd->n_options; i++) {
1167 if (strcmp(ifd->option[i].name, opt) == 0) {
1168 if (!doit(ifd->option[i].value)) {
1169 return 0;
1170 }
1171 }
1172 }
1173
1174
1175
1176
1177
1178
1179 buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
1180
1181 return doit(buf);
1182}
1183
1184static int check(char *str)
1185{
1186 return str != NULL;
1187}
1188
1189static int iface_up(struct interface_defn_t *iface)
1190{
1191 if (!iface->method->up(iface, check)) return -1;
1192 set_environ(iface, "start", "pre-up");
1193 if (!execute_all(iface, "pre-up")) return 0;
1194 if (!iface->method->up(iface, doit)) return 0;
1195 set_environ(iface, "start", "post-up");
1196 if (!execute_all(iface, "up")) return 0;
1197 return 1;
1198}
1199
1200static int iface_down(struct interface_defn_t *iface)
1201{
1202 if (!iface->method->down(iface, check)) return -1;
1203 set_environ(iface, "stop", "pre-down");
1204 if (!execute_all(iface, "down")) return 0;
1205 if (!iface->method->down(iface, doit)) return 0;
1206 set_environ(iface, "stop", "post-down");
1207 if (!execute_all(iface, "post-down")) return 0;
1208 return 1;
1209}
1210
1211#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1212static int popen2(FILE **in, FILE **out, char *command, char *param)
1213{
1214 char *argv[3] = { command, param, NULL };
1215 struct fd_pair infd, outfd;
1216 pid_t pid;
1217
1218 xpiped_pair(infd);
1219 xpiped_pair(outfd);
1220
1221 fflush_all();
1222 pid = xvfork();
1223
1224 if (pid == 0) {
1225
1226
1227 close(infd.wr);
1228 close(outfd.rd);
1229 xmove_fd(infd.rd, 0);
1230 xmove_fd(outfd.wr, 1);
1231 BB_EXECVP_or_die(argv);
1232 }
1233
1234 close(infd.rd);
1235 close(outfd.wr);
1236 *in = xfdopen_for_write(infd.wr);
1237 *out = xfdopen_for_read(outfd.rd);
1238 return pid;
1239}
1240
1241static char *run_mapping(char *physical, struct mapping_defn_t *map)
1242{
1243 FILE *in, *out;
1244 int i, status;
1245 pid_t pid;
1246
1247 char *logical = xstrdup(physical);
1248
1249
1250 pid = popen2(&in, &out, map->script, physical);
1251
1252
1253 for (i = 0; i < map->n_mappings; i++) {
1254 fprintf(in, "%s\n", map->mapping[i]);
1255 }
1256 fclose(in);
1257 safe_waitpid(pid, &status, 0);
1258
1259 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
1260
1261
1262
1263 char *new_logical = xmalloc_fgetline(out);
1264
1265 if (new_logical) {
1266
1267
1268
1269 char *pch = new_logical + strlen(new_logical) - 1;
1270
1271 while (pch >= new_logical && isspace(*pch))
1272 *(pch--) = '\0';
1273
1274 free(logical);
1275 logical = new_logical;
1276 }
1277 }
1278
1279 fclose(out);
1280
1281 return logical;
1282}
1283#endif
1284
1285static llist_t *find_iface_state(llist_t *state_list, const char *iface)
1286{
1287 llist_t *search = state_list;
1288
1289 while (search) {
1290 char *after_iface = is_prefixed_with(search->data, iface);
1291 if (after_iface
1292 && *after_iface == '='
1293 ) {
1294 return search;
1295 }
1296 search = search->link;
1297 }
1298 return NULL;
1299}
1300
1301
1302static llist_t *read_iface_state(void)
1303{
1304 llist_t *state_list = NULL;
1305 FILE *state_fp = fopen_for_read(IFSTATE_FILE_PATH);
1306
1307 if (state_fp) {
1308 char *start, *end_ptr;
1309 while ((start = xmalloc_fgets(state_fp)) != NULL) {
1310
1311 end_ptr = start + strcspn(start, " \t\n");
1312 *end_ptr = '\0';
1313 llist_add_to(&state_list, start);
1314 }
1315 fclose(state_fp);
1316 }
1317 return state_list;
1318}
1319
1320
1321static FILE *open_new_state_file(void)
1322{
1323 int fd, flags, cnt;
1324
1325 cnt = 0;
1326 flags = (O_WRONLY | O_CREAT | O_EXCL);
1327 for (;;) {
1328 fd = open(IFSTATE_FILE_PATH".new", flags, 0666);
1329 if (fd >= 0)
1330 break;
1331 if (errno != EEXIST
1332 || flags == (O_WRONLY | O_CREAT | O_TRUNC)
1333 ) {
1334 bb_perror_msg_and_die("can't open '%s'",
1335 IFSTATE_FILE_PATH".new");
1336 }
1337
1338 if (cnt > 30 * 1000) {
1339
1340
1341
1342 flags = (O_WRONLY | O_CREAT | O_TRUNC);
1343 continue;
1344 }
1345 usleep(cnt);
1346 cnt += 1000;
1347 }
1348
1349 return xfdopen_for_write(fd);
1350}
1351
1352int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1353int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1354{
1355 int (*cmds)(struct interface_defn_t *);
1356 struct interfaces_file_t *defn;
1357 llist_t *target_list = NULL;
1358 const char *interfaces = "/etc/network/interfaces";
1359 bool any_failures = 0;
1360
1361 INIT_G();
1362
1363 G.startup_PATH = getenv("PATH");
1364 G.shell = xstrdup(get_shell_name());
1365
1366 if (ENABLE_IFUP
1367 && (!ENABLE_IFDOWN || applet_name[2] == 'u')
1368 ) {
1369
1370 cmds = iface_up;
1371 } else {
1372 cmds = iface_down;
1373 }
1374
1375 getopt32(argv, OPTION_STR, &interfaces);
1376 argv += optind;
1377 if (argv[0]) {
1378 if (DO_ALL) bb_show_usage();
1379 } else {
1380 if (!DO_ALL) bb_show_usage();
1381 }
1382
1383 defn = read_interfaces(interfaces, NULL);
1384
1385
1386 if (DO_ALL) {
1387 target_list = defn->autointerfaces;
1388 } else {
1389 llist_add_to_end(&target_list, argv[0]);
1390 }
1391
1392
1393 while (target_list) {
1394 llist_t *iface_list;
1395 struct interface_defn_t *currif;
1396 char *iface;
1397 char *liface;
1398 char *pch;
1399 bool okay = 0;
1400 int cmds_ret;
1401 bool curr_failure = 0;
1402
1403 iface = xstrdup(target_list->data);
1404 target_list = target_list->link;
1405
1406 pch = strchr(iface, '=');
1407 if (pch) {
1408 *pch = '\0';
1409 liface = xstrdup(pch + 1);
1410 } else {
1411 liface = xstrdup(iface);
1412 }
1413
1414 if (!FORCE) {
1415 llist_t *state_list = read_iface_state();
1416 const llist_t *iface_state = find_iface_state(state_list, iface);
1417
1418 if (cmds == iface_up) {
1419
1420 if (iface_state) {
1421 bb_error_msg("interface %s already configured", iface);
1422 goto next;
1423 }
1424 } else {
1425
1426 if (!iface_state) {
1427 bb_error_msg("interface %s not configured", iface);
1428 goto next;
1429 }
1430 }
1431 llist_free(state_list, free);
1432 }
1433
1434#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1435 if ((cmds == iface_up) && !NO_MAPPINGS) {
1436 struct mapping_defn_t *currmap;
1437
1438 for (currmap = defn->mappings; currmap; currmap = currmap->next) {
1439 int i;
1440 for (i = 0; i < currmap->n_matches; i++) {
1441 if (fnmatch(currmap->match[i], liface, 0) != 0)
1442 continue;
1443 if (VERBOSE) {
1444 printf("Running mapping script %s on %s\n", currmap->script, liface);
1445 }
1446 liface = run_mapping(iface, currmap);
1447 break;
1448 }
1449 }
1450 }
1451#endif
1452
1453 iface_list = defn->ifaces;
1454 while (iface_list) {
1455 currif = (struct interface_defn_t *) iface_list->data;
1456 if (strcmp(liface, currif->iface) == 0) {
1457 char *oldiface = currif->iface;
1458
1459 okay = 1;
1460 currif->iface = iface;
1461
1462 debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
1463
1464
1465 cmds_ret = cmds(currif);
1466 if (cmds_ret == -1) {
1467 bb_error_msg("don't have all variables for %s/%s",
1468 liface, currif->address_family->name);
1469 any_failures = curr_failure = 1;
1470 } else if (cmds_ret == 0) {
1471 any_failures = curr_failure = 1;
1472 }
1473
1474 currif->iface = oldiface;
1475 }
1476 iface_list = iface_list->link;
1477 }
1478 if (VERBOSE) {
1479 bb_putchar('\n');
1480 }
1481
1482 if (!okay && !FORCE) {
1483 bb_error_msg("ignoring unknown interface %s", liface);
1484 any_failures = 1;
1485 } else if (!NO_ACT) {
1486
1487 FILE *new_state_fp = open_new_state_file();
1488 llist_t *state;
1489 llist_t *state_list = read_iface_state();
1490 llist_t *iface_state = find_iface_state(state_list, iface);
1491
1492 if (cmds == iface_up && !curr_failure) {
1493 char *newiface = xasprintf("%s=%s", iface, liface);
1494 if (!iface_state) {
1495 llist_add_to_end(&state_list, newiface);
1496 } else {
1497 free(iface_state->data);
1498 iface_state->data = newiface;
1499 }
1500 } else {
1501
1502 llist_unlink(&state_list, iface_state);
1503 free(llist_pop(&iface_state));
1504 }
1505
1506
1507 state = state_list;
1508 while (state) {
1509 if (state->data) {
1510 fprintf(new_state_fp, "%s\n", state->data);
1511 }
1512 state = state->link;
1513 }
1514 fclose(new_state_fp);
1515 xrename(IFSTATE_FILE_PATH".new", IFSTATE_FILE_PATH);
1516 llist_free(state_list, free);
1517 }
1518 next:
1519 free(iface);
1520 free(liface);
1521 }
1522
1523 return any_failures;
1524}
1525