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#include "libbb.h"
138
139
140#include <linux/if.h>
141
142#include <linux/if_bonding.h>
143#include <linux/sockios.h>
144#include "fix_u32.h"
145#include <linux/ethtool.h>
146#ifndef BOND_ABI_VERSION
147# define BOND_ABI_VERSION 2
148#endif
149#ifndef IFNAMSIZ
150# define IFNAMSIZ 16
151#endif
152
153
154struct dev_data {
155 struct ifreq mtu, flags, hwaddr;
156};
157
158
159enum { skfd = 3 };
160struct globals {
161 unsigned abi_ver;
162 smallint hwaddr_set;
163 struct dev_data master;
164 struct dev_data slave;
165};
166#define G (*ptr_to_globals)
167#define abi_ver (G.abi_ver )
168#define hwaddr_set (G.hwaddr_set)
169#define master (G.master )
170#define slave (G.slave )
171#define INIT_G() do { \
172 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
173} while (0)
174
175
176
177
178static int ioctl_on_skfd(unsigned request, struct ifreq *ifr)
179{
180 return ioctl(skfd, request, ifr);
181}
182
183static int set_ifrname_and_do_ioctl(unsigned request, struct ifreq *ifr, const char *ifname)
184{
185 strncpy_IFNAMSIZ(ifr->ifr_name, ifname);
186 return ioctl_on_skfd(request, ifr);
187}
188
189static int get_if_settings(char *ifname, struct dev_data *dd)
190{
191 int res;
192
193 res = set_ifrname_and_do_ioctl(SIOCGIFMTU, &dd->mtu, ifname);
194 res |= set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &dd->flags, ifname);
195 res |= set_ifrname_and_do_ioctl(SIOCGIFHWADDR, &dd->hwaddr, ifname);
196
197 return res;
198}
199
200static int get_slave_flags(char *slave_ifname)
201{
202 return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname);
203}
204
205static int set_hwaddr(char *ifname, struct sockaddr *hwaddr)
206{
207 struct ifreq ifr;
208
209 memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr));
210 return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname);
211}
212
213static int set_mtu(char *ifname, int mtu)
214{
215 struct ifreq ifr;
216
217 ifr.ifr_mtu = mtu;
218 return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname);
219}
220
221static int set_if_flags(char *ifname, int flags)
222{
223 struct ifreq ifr;
224
225 ifr.ifr_flags = flags;
226 return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname);
227}
228
229static int set_if_up(char *ifname, int flags)
230{
231 int res = set_if_flags(ifname, flags | IFF_UP);
232 if (res)
233 bb_perror_msg("%s: can't up", ifname);
234 return res;
235}
236
237static int set_if_down(char *ifname, int flags)
238{
239 int res = set_if_flags(ifname, flags & ~IFF_UP);
240 if (res)
241 bb_perror_msg("%s: can't down", ifname);
242 return res;
243}
244
245static int clear_if_addr(char *ifname)
246{
247 struct ifreq ifr;
248
249 ifr.ifr_addr.sa_family = AF_INET;
250 memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data));
251 return set_ifrname_and_do_ioctl(SIOCSIFADDR, &ifr, ifname);
252}
253
254static int set_if_addr(char *master_ifname, char *slave_ifname)
255{
256#if (SIOCGIFADDR | SIOCSIFADDR \
257 | SIOCGIFDSTADDR | SIOCSIFDSTADDR \
258 | SIOCGIFBRDADDR | SIOCSIFBRDADDR \
259 | SIOCGIFNETMASK | SIOCSIFNETMASK) <= 0xffff
260#define INT uint16_t
261#else
262#define INT int
263#endif
264 static const struct {
265 INT g_ioctl;
266 INT s_ioctl;
267 } ifra[] = {
268 { SIOCGIFADDR, SIOCSIFADDR },
269 { SIOCGIFDSTADDR, SIOCSIFDSTADDR },
270 { SIOCGIFBRDADDR, SIOCSIFBRDADDR },
271 { SIOCGIFNETMASK, SIOCSIFNETMASK },
272 };
273
274 struct ifreq ifr;
275 int res;
276 unsigned i;
277
278 for (i = 0; i < ARRAY_SIZE(ifra); i++) {
279 res = set_ifrname_and_do_ioctl(ifra[i].g_ioctl, &ifr, master_ifname);
280 if (res < 0) {
281 ifr.ifr_addr.sa_family = AF_INET;
282 memset(ifr.ifr_addr.sa_data, 0,
283 sizeof(ifr.ifr_addr.sa_data));
284 }
285
286 res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname);
287 if (res < 0)
288 return res;
289 }
290
291 return 0;
292}
293
294static void change_active(char *master_ifname, char *slave_ifname)
295{
296 struct ifreq ifr;
297
298 if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
299 bb_error_msg_and_die("%s is not a slave", slave_ifname);
300 }
301
302 strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
303 if (set_ifrname_and_do_ioctl(SIOCBONDCHANGEACTIVE, &ifr, master_ifname)
304 && ioctl_on_skfd(BOND_CHANGE_ACTIVE_OLD, &ifr)
305 ) {
306 bb_perror_msg_and_die(
307 "master %s, slave %s: can't "
308 "change active",
309 master_ifname, slave_ifname);
310 }
311}
312
313static NOINLINE int enslave(char *master_ifname, char *slave_ifname)
314{
315 struct ifreq ifr;
316 int res;
317
318 if (slave.flags.ifr_flags & IFF_SLAVE) {
319 bb_error_msg(
320 "%s is already a slave",
321 slave_ifname);
322 return 1;
323 }
324
325 res = set_if_down(slave_ifname, slave.flags.ifr_flags);
326 if (res)
327 return res;
328
329 if (abi_ver < 2) {
330
331
332
333 res = set_if_addr(master_ifname, slave_ifname);
334 if (res) {
335 bb_perror_msg("%s: can't set address", slave_ifname);
336 return res;
337 }
338 } else {
339 res = clear_if_addr(slave_ifname);
340 if (res) {
341 bb_perror_msg("%s: can't clear address", slave_ifname);
342 return res;
343 }
344 }
345
346 if (master.mtu.ifr_mtu != slave.mtu.ifr_mtu) {
347 res = set_mtu(slave_ifname, master.mtu.ifr_mtu);
348 if (res) {
349 bb_perror_msg("%s: can't set MTU", slave_ifname);
350 return res;
351 }
352 }
353
354 if (hwaddr_set) {
355
356
357
358 if (abi_ver < 1) {
359
360
361
362
363 if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) {
364 bb_perror_msg("%s: can't set hw address",
365 slave_ifname);
366 goto undo_mtu;
367 }
368
369
370
371
372 if (set_if_up(slave_ifname, slave.flags.ifr_flags))
373 goto undo_slave_mac;
374 }
375
376
377
378
379
380 } else {
381
382
383
384 if (abi_ver < 1) {
385
386
387
388 if (set_if_down(master_ifname, master.flags.ifr_flags))
389 goto undo_mtu;
390 }
391
392 if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) {
393 bb_error_msg("%s: can't set hw address",
394 master_ifname);
395 goto undo_mtu;
396 }
397
398 if (abi_ver < 1) {
399
400
401
402 if (set_if_up(master_ifname, master.flags.ifr_flags))
403 goto undo_master_mac;
404 }
405
406 hwaddr_set = 1;
407 }
408
409
410 strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
411 if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname)
412 && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr)
413 ) {
414 goto undo_master_mac;
415 }
416
417 return 0;
418
419
420 undo_master_mac:
421 set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr));
422 hwaddr_set = 0;
423 goto undo_mtu;
424
425 undo_slave_mac:
426 set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr));
427 undo_mtu:
428 set_mtu(slave_ifname, slave.mtu.ifr_mtu);
429 return 1;
430}
431
432static int release(char *master_ifname, char *slave_ifname)
433{
434 struct ifreq ifr;
435 int res = 0;
436
437 if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
438 bb_error_msg("%s is not a slave", slave_ifname);
439 return 1;
440 }
441
442 strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
443 if (set_ifrname_and_do_ioctl(SIOCBONDRELEASE, &ifr, master_ifname) < 0
444 && ioctl_on_skfd(BOND_RELEASE_OLD, &ifr) < 0
445 ) {
446 return 1;
447 }
448 if (abi_ver < 1) {
449
450
451
452 res = set_if_down(slave_ifname, slave.flags.ifr_flags);
453 }
454
455
456 set_mtu(slave_ifname, 1500);
457
458 return res;
459}
460
461static NOINLINE void get_drv_info(char *master_ifname)
462{
463 struct ifreq ifr;
464 struct ethtool_drvinfo info;
465
466 memset(&ifr, 0, sizeof(ifr));
467 ifr.ifr_data = (caddr_t)&info;
468 info.cmd = ETHTOOL_GDRVINFO;
469
470 strcpy(info.driver, "ifenslave");
471 strcpy(info.fw_version, utoa(BOND_ABI_VERSION));
472 if (set_ifrname_and_do_ioctl(SIOCETHTOOL, &ifr, master_ifname) < 0) {
473 if (errno == EOPNOTSUPP)
474 return;
475 bb_perror_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
476 }
477
478 abi_ver = bb_strtou(info.fw_version, NULL, 0);
479 if (errno)
480 bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
481}
482
483int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
484int ifenslave_main(int argc UNUSED_PARAM, char **argv)
485{
486 char *master_ifname, *slave_ifname;
487 int rv;
488 int res;
489 unsigned opt;
490 enum {
491 OPT_c = (1 << 0),
492 OPT_d = (1 << 1),
493 OPT_f = (1 << 2),
494 };
495
496 INIT_G();
497
498 opt = getopt32long(argv, "cdfa",
499 "change-active\0" No_argument "c"
500 "detach\0" No_argument "d"
501 "force\0" No_argument "f"
502
503 );
504 argv += optind;
505 if (opt & (opt-1))
506 bb_show_usage();
507
508 master_ifname = *argv++;
509
510
511 if (!master_ifname) {
512 display_interfaces(NULL);
513 return EXIT_SUCCESS;
514 }
515
516
517 xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), skfd);
518
519
520 get_drv_info(master_ifname);
521
522 slave_ifname = *argv++;
523 if (!slave_ifname) {
524 if (opt & (OPT_d|OPT_c)) {
525
526
527 display_interfaces(slave_ifname );
528 return 2;
529 }
530
531
532
533 display_interfaces(master_ifname);
534 return EXIT_SUCCESS;
535 }
536
537 if (get_if_settings(master_ifname, &master)) {
538
539 bb_perror_msg_and_die("%s: can't get settings", master_ifname);
540 }
541
542
543
544
545 if (!(master.flags.ifr_flags & IFF_MASTER))
546 bb_error_msg_and_die("%s is not a master", master_ifname);
547
548
549 if (!(master.flags.ifr_flags & IFF_UP))
550 bb_error_msg_and_die("%s is not up", master_ifname);
551
552#ifdef WHY_BOTHER
553
554
555 if (!(opt & (OPT_d|OPT_c|OPT_f))) {
556
557 if (master.hwaddr.ifr_hwaddr.sa_family != 1) {
558 bb_error_msg_and_die(
559 "%s is not ethernet-like (-f overrides)",
560 master_ifname);
561 }
562 }
563#endif
564
565
566 if (opt & OPT_c) {
567
568 if (get_slave_flags(slave_ifname)) {
569 bb_perror_msg_and_die(
570 "%s: can't get flags", slave_ifname);
571 }
572 change_active(master_ifname, slave_ifname);
573 return EXIT_SUCCESS;
574 }
575
576
577 res = 0;
578 do {
579 if (opt & OPT_d) {
580
581 rv = get_slave_flags(slave_ifname);
582 if (rv) {
583
584
585 bb_perror_msg(
586 "skipping %s: can't get %s",
587 slave_ifname, "flags");
588 res = rv;
589 continue;
590 }
591 rv = release(master_ifname, slave_ifname);
592 if (rv) {
593 bb_perror_msg("can't release %s from %s",
594 slave_ifname, master_ifname);
595 res = rv;
596 }
597 } else {
598
599 rv = get_if_settings(slave_ifname, &slave);
600 if (rv) {
601
602
603 bb_perror_msg(
604 "skipping %s: can't get %s",
605 slave_ifname, "settings");
606 res = rv;
607 continue;
608 }
609 rv = enslave(master_ifname, slave_ifname);
610 if (rv) {
611 bb_perror_msg("can't enslave %s to %s",
612 slave_ifname, master_ifname);
613 res = rv;
614 }
615 }
616 } while ((slave_ifname = *argv++) != NULL);
617
618 if (ENABLE_FEATURE_CLEAN_UP) {
619 close(skfd);
620 }
621
622 return res;
623}
624