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