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