source: MondoRescue/branches/3.3/mindi-busybox/networking/ifenslave.c@ 3723

Last change on this file since 3723 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 18.0 KB
Line 
1/* Mode: C;
2 *
3 * Mini ifenslave implementation for busybox
4 * Copyright (C) 2005 by Marc Leeman <marc.leeman@barco.com>
5 *
6 * ifenslave.c: Configure network interfaces for parallel routing.
7 *
8 * This program controls the Linux implementation of running multiple
9 * network interfaces in parallel.
10 *
11 * Author: Donald Becker <becker@cesdis.gsfc.nasa.gov>
12 * Copyright 1994-1996 Donald Becker
13 *
14 * This program is free software; you can redistribute it
15 * and/or modify it under the terms of the GNU General Public
16 * License as published by the Free Software Foundation.
17 *
18 * The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
19 * Center of Excellence in Space Data and Information Sciences
20 * Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
21 *
22 * Changes :
23 * - 2000/10/02 Willy Tarreau <willy at meta-x.org> :
24 * - few fixes. Master's MAC address is now correctly taken from
25 * the first device when not previously set ;
26 * - detach support : call BOND_RELEASE to detach an enslaved interface.
27 * - give a mini-howto from command-line help : # ifenslave -h
28 *
29 * - 2001/02/16 Chad N. Tindel <ctindel at ieee dot org> :
30 * - Master is now brought down before setting the MAC address. In
31 * the 2.4 kernel you can't change the MAC address while the device is
32 * up because you get EBUSY.
33 *
34 * - 2001/09/13 Takao Indoh <indou dot takao at jp dot fujitsu dot com>
35 * - Added the ability to change the active interface on a mode 1 bond
36 * at runtime.
37 *
38 * - 2001/10/23 Chad N. Tindel <ctindel at ieee dot org> :
39 * - No longer set the MAC address of the master. The bond device will
40 * take care of this itself
41 * - Try the SIOC*** versions of the bonding ioctls before using the
42 * old versions
43 * - 2002/02/18 Erik Habbinga <erik_habbinga @ hp dot com> :
44 * - ifr2.ifr_flags was not initialized in the hwaddr_notset case,
45 * SIOCGIFFLAGS now called before hwaddr_notset test
46 *
47 * - 2002/10/31 Tony Cureington <tony.cureington * hp_com> :
48 * - If the master does not have a hardware address when the first slave
49 * is enslaved, the master is assigned the hardware address of that
50 * slave - there is a comment in bonding.c stating "ifenslave takes
51 * care of this now." This corrects the problem of slaves having
52 * different hardware addresses in active-backup mode when
53 * multiple interfaces are specified on a single ifenslave command
54 * (ifenslave bond0 eth0 eth1).
55 *
56 * - 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
57 * Shmulik Hen <shmulik.hen at intel dot com>
58 * - Moved setting the slave's mac address and openning it, from
59 * the application to the driver. This enables support of modes
60 * that need to use the unique mac address of each slave.
61 * The driver also takes care of closing the slave and restoring its
62 * original mac address upon release.
63 * In addition, block possibility of enslaving before the master is up.
64 * This prevents putting the system in an undefined state.
65 *
66 * - 2003/05/01 - Amir Noam <amir.noam at intel dot com>
67 * - Added ABI version control to restore compatibility between
68 * new/old ifenslave and new/old bonding.
69 * - Prevent adding an adapter that is already a slave.
70 * Fixes the problem of stalling the transmission and leaving
71 * the slave in a down state.
72 *
73 * - 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
74 * - Prevent enslaving if the bond device is down.
75 * Fixes the problem of leaving the system in unstable state and
76 * halting when trying to remove the module.
77 * - Close socket on all abnormal exists.
78 * - Add versioning scheme that follows that of the bonding driver.
79 * current version is 1.0.0 as a base line.
80 *
81 * - 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com>
82 * - ifenslave -c was broken; it's now fixed
83 * - Fixed problem with routes vanishing from master during enslave
84 * processing.
85 *
86 * - 2003/05/27 - Amir Noam <amir.noam at intel dot com>
87 * - Fix backward compatibility issues:
88 * For drivers not using ABI versions, slave was set down while
89 * it should be left up before enslaving.
90 * Also, master was not set down and the default set_mac_address()
91 * would fail and generate an error message in the system log.
92 * - For opt_c: slave should not be set to the master's setting
93 * while it is running. It was already set during enslave. To
94 * simplify things, it is now handeled separately.
95 *
96 * - 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
97 * - Code cleanup and style changes
98 * set version to 1.1.0
99 */
100
101//usage:#define ifenslave_trivial_usage
102//usage: "[-cdf] MASTER_IFACE SLAVE_IFACE..."
103//usage:#define ifenslave_full_usage "\n\n"
104//usage: "Configure network interfaces for parallel routing\n"
105//usage: "\n -c,--change-active Change active slave"
106//usage: "\n -d,--detach Remove slave interface from bonding device"
107//usage: "\n -f,--force Force, even if interface is not Ethernet"
108/* //usage: "\n -r,--receive-slave Create a receive-only slave" */
109//usage:
110//usage:#define ifenslave_example_usage
111//usage: "To create a bond device, simply follow these three steps:\n"
112//usage: "- ensure that the required drivers are properly loaded:\n"
113//usage: " # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n"
114//usage: "- assign an IP address to the bond device:\n"
115//usage: " # ifconfig bond0 <addr> netmask <mask> broadcast <bcast>\n"
116//usage: "- attach all the interfaces you need to the bond device:\n"
117//usage: " # ifenslave bond0 eth0 eth1 eth2\n"
118//usage: " If bond0 didn't have a MAC address, it will take eth0's. Then, all\n"
119//usage: " interfaces attached AFTER this assignment will get the same MAC addr.\n\n"
120//usage: " To detach a dead interface without setting the bond device down:\n"
121//usage: " # ifenslave -d bond0 eth1\n\n"
122//usage: " To set the bond device down and automatically release all the slaves:\n"
123//usage: " # ifconfig bond0 down\n\n"
124//usage: " To change active slave:\n"
125//usage: " # ifenslave -c bond0 eth0\n"
126
127#include "libbb.h"
128
129/* #include <net/if.h> - no. linux/if_bonding.h pulls in linux/if.h */
130#include <linux/if.h>
131//#include <net/if_arp.h> - not needed?
132#include <linux/if_bonding.h>
133#include <linux/sockios.h>
134#include "fix_u32.h" /* hack, so we may include kernel's ethtool.h */
135#include <linux/ethtool.h>
136#ifndef BOND_ABI_VERSION
137# define BOND_ABI_VERSION 2
138#endif
139#ifndef IFNAMSIZ
140# define IFNAMSIZ 16
141#endif
142
143
144struct dev_data {
145 struct ifreq mtu, flags, hwaddr;
146};
147
148
149enum { skfd = 3 }; /* AF_INET socket for ioctl() calls. */
150struct globals {
151 unsigned abi_ver; /* userland - kernel ABI version */
152 smallint hwaddr_set; /* Master's hwaddr is set */
153 struct dev_data master;
154 struct dev_data slave;
155};
156#define G (*ptr_to_globals)
157#define abi_ver (G.abi_ver )
158#define hwaddr_set (G.hwaddr_set)
159#define master (G.master )
160#define slave (G.slave )
161#define INIT_G() do { \
162 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
163} while (0)
164
165
166/* NOINLINEs are placed where it results in smaller code (gcc 4.3.1) */
167
168static int ioctl_on_skfd(unsigned request, struct ifreq *ifr)
169{
170 return ioctl(skfd, request, ifr);
171}
172
173static int set_ifrname_and_do_ioctl(unsigned request, struct ifreq *ifr, const char *ifname)
174{
175 strncpy_IFNAMSIZ(ifr->ifr_name, ifname);
176 return ioctl_on_skfd(request, ifr);
177}
178
179static int get_if_settings(char *ifname, struct dev_data *dd)
180{
181 int res;
182
183 res = set_ifrname_and_do_ioctl(SIOCGIFMTU, &dd->mtu, ifname);
184 res |= set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &dd->flags, ifname);
185 res |= set_ifrname_and_do_ioctl(SIOCGIFHWADDR, &dd->hwaddr, ifname);
186
187 return res;
188}
189
190static int get_slave_flags(char *slave_ifname)
191{
192 return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname);
193}
194
195static int set_hwaddr(char *ifname, struct sockaddr *hwaddr)
196{
197 struct ifreq ifr;
198
199 memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr));
200 return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname);
201}
202
203static int set_mtu(char *ifname, int mtu)
204{
205 struct ifreq ifr;
206
207 ifr.ifr_mtu = mtu;
208 return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname);
209}
210
211static int set_if_flags(char *ifname, int flags)
212{
213 struct ifreq ifr;
214
215 ifr.ifr_flags = flags;
216 return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname);
217}
218
219static int set_if_up(char *ifname, int flags)
220{
221 int res = set_if_flags(ifname, flags | IFF_UP);
222 if (res)
223 bb_perror_msg("%s: can't up", ifname);
224 return res;
225}
226
227static int set_if_down(char *ifname, int flags)
228{
229 int res = set_if_flags(ifname, flags & ~IFF_UP);
230 if (res)
231 bb_perror_msg("%s: can't down", ifname);
232 return res;
233}
234
235static int clear_if_addr(char *ifname)
236{
237 struct ifreq ifr;
238
239 ifr.ifr_addr.sa_family = AF_INET;
240 memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data));
241 return set_ifrname_and_do_ioctl(SIOCSIFADDR, &ifr, ifname);
242}
243
244static int set_if_addr(char *master_ifname, char *slave_ifname)
245{
246#if (SIOCGIFADDR | SIOCSIFADDR \
247 | SIOCGIFDSTADDR | SIOCSIFDSTADDR \
248 | SIOCGIFBRDADDR | SIOCSIFBRDADDR \
249 | SIOCGIFNETMASK | SIOCSIFNETMASK) <= 0xffff
250#define INT uint16_t
251#else
252#define INT int
253#endif
254 static const struct {
255 INT g_ioctl;
256 INT s_ioctl;
257 } ifra[] = {
258 { SIOCGIFADDR, SIOCSIFADDR },
259 { SIOCGIFDSTADDR, SIOCSIFDSTADDR },
260 { SIOCGIFBRDADDR, SIOCSIFBRDADDR },
261 { SIOCGIFNETMASK, SIOCSIFNETMASK },
262 };
263
264 struct ifreq ifr;
265 int res;
266 unsigned i;
267
268 for (i = 0; i < ARRAY_SIZE(ifra); i++) {
269 res = set_ifrname_and_do_ioctl(ifra[i].g_ioctl, &ifr, master_ifname);
270 if (res < 0) {
271 ifr.ifr_addr.sa_family = AF_INET;
272 memset(ifr.ifr_addr.sa_data, 0,
273 sizeof(ifr.ifr_addr.sa_data));
274 }
275
276 res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname);
277 if (res < 0)
278 return res;
279 }
280
281 return 0;
282}
283
284static void change_active(char *master_ifname, char *slave_ifname)
285{
286 struct ifreq ifr;
287
288 if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
289 bb_error_msg_and_die("%s is not a slave", slave_ifname);
290 }
291
292 strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
293 if (set_ifrname_and_do_ioctl(SIOCBONDCHANGEACTIVE, &ifr, master_ifname)
294 && ioctl_on_skfd(BOND_CHANGE_ACTIVE_OLD, &ifr)
295 ) {
296 bb_perror_msg_and_die(
297 "master %s, slave %s: can't "
298 "change active",
299 master_ifname, slave_ifname);
300 }
301}
302
303static NOINLINE int enslave(char *master_ifname, char *slave_ifname)
304{
305 struct ifreq ifr;
306 int res;
307
308 if (slave.flags.ifr_flags & IFF_SLAVE) {
309 bb_error_msg(
310 "%s is already a slave",
311 slave_ifname);
312 return 1;
313 }
314
315 res = set_if_down(slave_ifname, slave.flags.ifr_flags);
316 if (res)
317 return res;
318
319 if (abi_ver < 2) {
320 /* Older bonding versions would panic if the slave has no IP
321 * address, so get the IP setting from the master.
322 */
323 res = set_if_addr(master_ifname, slave_ifname);
324 if (res) {
325 bb_perror_msg("%s: can't set address", slave_ifname);
326 return res;
327 }
328 } else {
329 res = clear_if_addr(slave_ifname);
330 if (res) {
331 bb_perror_msg("%s: can't clear address", slave_ifname);
332 return res;
333 }
334 }
335
336 if (master.mtu.ifr_mtu != slave.mtu.ifr_mtu) {
337 res = set_mtu(slave_ifname, master.mtu.ifr_mtu);
338 if (res) {
339 bb_perror_msg("%s: can't set MTU", slave_ifname);
340 return res;
341 }
342 }
343
344 if (hwaddr_set) {
345 /* Master already has an hwaddr
346 * so set it's hwaddr to the slave
347 */
348 if (abi_ver < 1) {
349 /* The driver is using an old ABI, so
350 * the application sets the slave's
351 * hwaddr
352 */
353 if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) {
354 bb_perror_msg("%s: can't set hw address",
355 slave_ifname);
356 goto undo_mtu;
357 }
358
359 /* For old ABI the application needs to bring the
360 * slave back up
361 */
362 if (set_if_up(slave_ifname, slave.flags.ifr_flags))
363 goto undo_slave_mac;
364 }
365 /* The driver is using a new ABI,
366 * so the driver takes care of setting
367 * the slave's hwaddr and bringing
368 * it up again
369 */
370 } else {
371 /* No hwaddr for master yet, so
372 * set the slave's hwaddr to it
373 */
374 if (abi_ver < 1) {
375 /* For old ABI, the master needs to be
376 * down before setting it's hwaddr
377 */
378 if (set_if_down(master_ifname, master.flags.ifr_flags))
379 goto undo_mtu;
380 }
381
382 if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) {
383 bb_error_msg("%s: can't set hw address",
384 master_ifname);
385 goto undo_mtu;
386 }
387
388 if (abi_ver < 1) {
389 /* For old ABI, bring the master
390 * back up
391 */
392 if (set_if_up(master_ifname, master.flags.ifr_flags))
393 goto undo_master_mac;
394 }
395
396 hwaddr_set = 1;
397 }
398
399 /* Do the real thing */
400 strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
401 if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname)
402 && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr)
403 ) {
404 goto undo_master_mac;
405 }
406
407 return 0;
408
409/* rollback (best effort) */
410 undo_master_mac:
411 set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr));
412 hwaddr_set = 0;
413 goto undo_mtu;
414
415 undo_slave_mac:
416 set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr));
417 undo_mtu:
418 set_mtu(slave_ifname, slave.mtu.ifr_mtu);
419 return 1;
420}
421
422static int release(char *master_ifname, char *slave_ifname)
423{
424 struct ifreq ifr;
425 int res = 0;
426
427 if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
428 bb_error_msg("%s is not a slave", slave_ifname);
429 return 1;
430 }
431
432 strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
433 if (set_ifrname_and_do_ioctl(SIOCBONDRELEASE, &ifr, master_ifname) < 0
434 && ioctl_on_skfd(BOND_RELEASE_OLD, &ifr) < 0
435 ) {
436 return 1;
437 }
438 if (abi_ver < 1) {
439 /* The driver is using an old ABI, so we'll set the interface
440 * down to avoid any conflicts due to same MAC/IP
441 */
442 res = set_if_down(slave_ifname, slave.flags.ifr_flags);
443 }
444
445 /* set to default mtu */
446 set_mtu(slave_ifname, 1500);
447
448 return res;
449}
450
451static NOINLINE void get_drv_info(char *master_ifname)
452{
453 struct ifreq ifr;
454 struct ethtool_drvinfo info;
455
456 memset(&ifr, 0, sizeof(ifr));
457 ifr.ifr_data = (caddr_t)&info;
458 info.cmd = ETHTOOL_GDRVINFO;
459 /* both fields are 32 bytes long (long enough) */
460 strcpy(info.driver, "ifenslave");
461 strcpy(info.fw_version, utoa(BOND_ABI_VERSION));
462 if (set_ifrname_and_do_ioctl(SIOCETHTOOL, &ifr, master_ifname) < 0) {
463 if (errno == EOPNOTSUPP)
464 return;
465 bb_perror_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
466 }
467
468 abi_ver = bb_strtou(info.fw_version, NULL, 0);
469 if (errno)
470 bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
471}
472
473int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
474int ifenslave_main(int argc UNUSED_PARAM, char **argv)
475{
476 char *master_ifname, *slave_ifname;
477 int rv;
478 int res;
479 unsigned opt;
480 enum {
481 OPT_c = (1 << 0),
482 OPT_d = (1 << 1),
483 OPT_f = (1 << 2),
484 };
485#if ENABLE_LONG_OPTS
486 static const char ifenslave_longopts[] ALIGN1 =
487 "change-active\0" No_argument "c"
488 "detach\0" No_argument "d"
489 "force\0" No_argument "f"
490 /* "all-interfaces\0" No_argument "a" */
491 ;
492
493 applet_long_options = ifenslave_longopts;
494#endif
495 INIT_G();
496
497 opt = getopt32(argv, "cdfa");
498 argv += optind;
499 if (opt & (opt-1)) /* Only one option can be given */
500 bb_show_usage();
501
502 master_ifname = *argv++;
503
504 /* No interface names - show all interfaces. */
505 if (!master_ifname) {
506 display_interfaces(NULL);
507 return EXIT_SUCCESS;
508 }
509
510 /* Open a basic socket */
511 xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), skfd);
512
513 /* Exchange abi version with bonding module */
514 get_drv_info(master_ifname);
515
516 slave_ifname = *argv++;
517 if (!slave_ifname) {
518 if (opt & (OPT_d|OPT_c)) {
519 /* --change or --detach, and no slaves given -
520 * show all interfaces. */
521 display_interfaces(slave_ifname /* == NULL */);
522 return 2; /* why 2? */
523 }
524 /* A single arg means show the
525 * configuration for this interface
526 */
527 display_interfaces(master_ifname);
528 return EXIT_SUCCESS;
529 }
530
531 if (get_if_settings(master_ifname, &master)) {
532 /* Probably a good reason not to go on */
533 bb_perror_msg_and_die("%s: can't get settings", master_ifname);
534 }
535
536 /* Check if master is indeed a master;
537 * if not then fail any operation
538 */
539 if (!(master.flags.ifr_flags & IFF_MASTER))
540 bb_error_msg_and_die("%s is not a master", master_ifname);
541
542 /* Check if master is up; if not then fail any operation */
543 if (!(master.flags.ifr_flags & IFF_UP))
544 bb_error_msg_and_die("%s is not up", master_ifname);
545
546#ifdef WHY_BOTHER
547 /* Neither -c[hange] nor -d[etach] -> it's "enslave" then;
548 * and -f[orce] is not there too. Check that it's ethernet. */
549 if (!(opt & (OPT_d|OPT_c|OPT_f))) {
550 /* The family '1' is ARPHRD_ETHER for ethernet. */
551 if (master.hwaddr.ifr_hwaddr.sa_family != 1) {
552 bb_error_msg_and_die(
553 "%s is not ethernet-like (-f overrides)",
554 master_ifname);
555 }
556 }
557#endif
558
559 /* Accepts only one slave */
560 if (opt & OPT_c) {
561 /* Change active slave */
562 if (get_slave_flags(slave_ifname)) {
563 bb_perror_msg_and_die(
564 "%s: can't get flags", slave_ifname);
565 }
566 change_active(master_ifname, slave_ifname);
567 return EXIT_SUCCESS;
568 }
569
570 /* Accepts multiple slaves */
571 res = 0;
572 do {
573 if (opt & OPT_d) {
574 /* Detach a slave interface from the master */
575 rv = get_slave_flags(slave_ifname);
576 if (rv) {
577 /* Can't work with this slave, */
578 /* remember the error and skip it */
579 bb_perror_msg(
580 "skipping %s: can't get %s",
581 slave_ifname, "flags");
582 res = rv;
583 continue;
584 }
585 rv = release(master_ifname, slave_ifname);
586 if (rv) {
587 bb_perror_msg("can't release %s from %s",
588 slave_ifname, master_ifname);
589 res = rv;
590 }
591 } else {
592 /* Attach a slave interface to the master */
593 rv = get_if_settings(slave_ifname, &slave);
594 if (rv) {
595 /* Can't work with this slave, */
596 /* remember the error and skip it */
597 bb_perror_msg(
598 "skipping %s: can't get %s",
599 slave_ifname, "settings");
600 res = rv;
601 continue;
602 }
603 rv = enslave(master_ifname, slave_ifname);
604 if (rv) {
605 bb_perror_msg("can't enslave %s to %s",
606 slave_ifname, master_ifname);
607 res = rv;
608 }
609 }
610 } while ((slave_ifname = *argv++) != NULL);
611
612 if (ENABLE_FEATURE_CLEAN_UP) {
613 close(skfd);
614 }
615
616 return res;
617}
Note: See TracBrowser for help on using the repository browser.