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