]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Mode: C; |
2 | * ifenslave.c: Configure network interfaces for parallel routing. | |
3 | * | |
4 | * This program controls the Linux implementation of running multiple | |
5 | * network interfaces in parallel. | |
6 | * | |
7 | * Author: Donald Becker <becker@cesdis.gsfc.nasa.gov> | |
8 | * Copyright 1994-1996 Donald Becker | |
9 | * | |
10 | * This program is free software; you can redistribute it | |
11 | * and/or modify it under the terms of the GNU General Public | |
12 | * License as published by the Free Software Foundation. | |
13 | * | |
14 | * The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O | |
15 | * Center of Excellence in Space Data and Information Sciences | |
16 | * Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 | |
17 | * | |
18 | * Changes : | |
19 | * - 2000/10/02 Willy Tarreau <willy at meta-x.org> : | |
20 | * - few fixes. Master's MAC address is now correctly taken from | |
21 | * the first device when not previously set ; | |
22 | * - detach support : call BOND_RELEASE to detach an enslaved interface. | |
23 | * - give a mini-howto from command-line help : # ifenslave -h | |
24 | * | |
25 | * - 2001/02/16 Chad N. Tindel <ctindel at ieee dot org> : | |
26 | * - Master is now brought down before setting the MAC address. In | |
27 | * the 2.4 kernel you can't change the MAC address while the device is | |
28 | * up because you get EBUSY. | |
29 | * | |
30 | * - 2001/09/13 Takao Indoh <indou dot takao at jp dot fujitsu dot com> | |
31 | * - Added the ability to change the active interface on a mode 1 bond | |
32 | * at runtime. | |
33 | * | |
34 | * - 2001/10/23 Chad N. Tindel <ctindel at ieee dot org> : | |
35 | * - No longer set the MAC address of the master. The bond device will | |
36 | * take care of this itself | |
37 | * - Try the SIOC*** versions of the bonding ioctls before using the | |
38 | * old versions | |
39 | * - 2002/02/18 Erik Habbinga <erik_habbinga @ hp dot com> : | |
40 | * - ifr2.ifr_flags was not initialized in the hwaddr_notset case, | |
41 | * SIOCGIFFLAGS now called before hwaddr_notset test | |
42 | * | |
43 | * - 2002/10/31 Tony Cureington <tony.cureington * hp_com> : | |
44 | * - If the master does not have a hardware address when the first slave | |
45 | * is enslaved, the master is assigned the hardware address of that | |
46 | * slave - there is a comment in bonding.c stating "ifenslave takes | |
47 | * care of this now." This corrects the problem of slaves having | |
48 | * different hardware addresses in active-backup mode when | |
49 | * multiple interfaces are specified on a single ifenslave command | |
50 | * (ifenslave bond0 eth0 eth1). | |
51 | * | |
52 | * - 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and | |
53 | * Shmulik Hen <shmulik.hen at intel dot com> | |
54 | * - Moved setting the slave's mac address and openning it, from | |
55 | * the application to the driver. This enables support of modes | |
56 | * that need to use the unique mac address of each slave. | |
57 | * The driver also takes care of closing the slave and restoring its | |
58 | * original mac address upon release. | |
59 | * In addition, block possibility of enslaving before the master is up. | |
60 | * This prevents putting the system in an undefined state. | |
61 | * | |
62 | * - 2003/05/01 - Amir Noam <amir.noam at intel dot com> | |
63 | * - Added ABI version control to restore compatibility between | |
64 | * new/old ifenslave and new/old bonding. | |
65 | * - Prevent adding an adapter that is already a slave. | |
66 | * Fixes the problem of stalling the transmission and leaving | |
67 | * the slave in a down state. | |
68 | * | |
69 | * - 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com> | |
70 | * - Prevent enslaving if the bond device is down. | |
71 | * Fixes the problem of leaving the system in unstable state and | |
72 | * halting when trying to remove the module. | |
73 | * - Close socket on all abnormal exists. | |
74 | * - Add versioning scheme that follows that of the bonding driver. | |
75 | * current version is 1.0.0 as a base line. | |
76 | * | |
77 | * - 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com> | |
78 | * - ifenslave -c was broken; it's now fixed | |
79 | * - Fixed problem with routes vanishing from master during enslave | |
80 | * processing. | |
81 | * | |
82 | * - 2003/05/27 - Amir Noam <amir.noam at intel dot com> | |
83 | * - Fix backward compatibility issues: | |
84 | * For drivers not using ABI versions, slave was set down while | |
85 | * it should be left up before enslaving. | |
86 | * Also, master was not set down and the default set_mac_address() | |
87 | * would fail and generate an error message in the system log. | |
88 | * - For opt_c: slave should not be set to the master's setting | |
89 | * while it is running. It was already set during enslave. To | |
7f927fcc | 90 | * simplify things, it is now handled separately. |
1da177e4 LT |
91 | * |
92 | * - 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com> | |
93 | * - Code cleanup and style changes | |
94 | * set version to 1.1.0 | |
95 | */ | |
96 | ||
97 | #define APP_VERSION "1.1.0" | |
98 | #define APP_RELDATE "December 1, 2003" | |
99 | #define APP_NAME "ifenslave" | |
100 | ||
101 | static char *version = | |
102 | APP_NAME ".c:v" APP_VERSION " (" APP_RELDATE ")\n" | |
103 | "o Donald Becker (becker@cesdis.gsfc.nasa.gov).\n" | |
104 | "o Detach support added on 2000/10/02 by Willy Tarreau (willy at meta-x.org).\n" | |
105 | "o 2.4 kernel support added on 2001/02/16 by Chad N. Tindel\n" | |
106 | " (ctindel at ieee dot org).\n"; | |
107 | ||
108 | static const char *usage_msg = | |
109 | "Usage: ifenslave [-f] <master-if> <slave-if> [<slave-if>...]\n" | |
110 | " ifenslave -d <master-if> <slave-if> [<slave-if>...]\n" | |
111 | " ifenslave -c <master-if> <slave-if>\n" | |
112 | " ifenslave --help\n"; | |
113 | ||
114 | static const char *help_msg = | |
115 | "\n" | |
116 | " To create a bond device, simply follow these three steps :\n" | |
117 | " - ensure that the required drivers are properly loaded :\n" | |
118 | " # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" | |
119 | " - assign an IP address to the bond device :\n" | |
120 | " # ifconfig bond0 <addr> netmask <mask> broadcast <bcast>\n" | |
121 | " - attach all the interfaces you need to the bond device :\n" | |
122 | " # ifenslave [{-f|--force}] bond0 eth0 [eth1 [eth2]...]\n" | |
123 | " If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" | |
124 | " interfaces attached AFTER this assignment will get the same MAC addr.\n" | |
125 | " (except for ALB/TLB modes)\n" | |
126 | "\n" | |
127 | " To set the bond device down and automatically release all the slaves :\n" | |
128 | " # ifconfig bond0 down\n" | |
129 | "\n" | |
130 | " To detach a dead interface without setting the bond device down :\n" | |
131 | " # ifenslave {-d|--detach} bond0 eth0 [eth1 [eth2]...]\n" | |
132 | "\n" | |
133 | " To change active slave :\n" | |
134 | " # ifenslave {-c|--change-active} bond0 eth0\n" | |
135 | "\n" | |
136 | " To show master interface info\n" | |
137 | " # ifenslave bond0\n" | |
138 | "\n" | |
139 | " To show all interfaces info\n" | |
140 | " # ifenslave {-a|--all-interfaces}\n" | |
141 | "\n" | |
142 | " To be more verbose\n" | |
143 | " # ifenslave {-v|--verbose} ...\n" | |
144 | "\n" | |
145 | " # ifenslave {-u|--usage} Show usage\n" | |
146 | " # ifenslave {-V|--version} Show version\n" | |
147 | " # ifenslave {-h|--help} This message\n" | |
148 | "\n"; | |
149 | ||
150 | #include <unistd.h> | |
151 | #include <stdlib.h> | |
152 | #include <stdio.h> | |
153 | #include <ctype.h> | |
154 | #include <string.h> | |
155 | #include <errno.h> | |
156 | #include <fcntl.h> | |
157 | #include <getopt.h> | |
158 | #include <sys/types.h> | |
159 | #include <sys/socket.h> | |
160 | #include <sys/ioctl.h> | |
161 | #include <linux/if.h> | |
162 | #include <net/if_arp.h> | |
163 | #include <linux/if_ether.h> | |
164 | #include <linux/if_bonding.h> | |
165 | #include <linux/sockios.h> | |
166 | ||
167 | typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */ | |
168 | typedef __uint32_t u32; /* ditto */ | |
169 | typedef __uint16_t u16; /* ditto */ | |
170 | typedef __uint8_t u8; /* ditto */ | |
171 | #include <linux/ethtool.h> | |
172 | ||
173 | struct option longopts[] = { | |
174 | /* { name has_arg *flag val } */ | |
175 | {"all-interfaces", 0, 0, 'a'}, /* Show all interfaces. */ | |
176 | {"change-active", 0, 0, 'c'}, /* Change the active slave. */ | |
177 | {"detach", 0, 0, 'd'}, /* Detach a slave interface. */ | |
178 | {"force", 0, 0, 'f'}, /* Force the operation. */ | |
179 | {"help", 0, 0, 'h'}, /* Give help */ | |
180 | {"usage", 0, 0, 'u'}, /* Give usage */ | |
181 | {"verbose", 0, 0, 'v'}, /* Report each action taken. */ | |
182 | {"version", 0, 0, 'V'}, /* Emit version information. */ | |
183 | { 0, 0, 0, 0} | |
184 | }; | |
185 | ||
186 | /* Command-line flags. */ | |
187 | unsigned int | |
188 | opt_a = 0, /* Show-all-interfaces flag. */ | |
189 | opt_c = 0, /* Change-active-slave flag. */ | |
190 | opt_d = 0, /* Detach a slave interface. */ | |
191 | opt_f = 0, /* Force the operation. */ | |
192 | opt_h = 0, /* Help */ | |
193 | opt_u = 0, /* Usage */ | |
194 | opt_v = 0, /* Verbose flag. */ | |
195 | opt_V = 0; /* Version */ | |
196 | ||
197 | int skfd = -1; /* AF_INET socket for ioctl() calls.*/ | |
198 | int abi_ver = 0; /* userland - kernel ABI version */ | |
199 | int hwaddr_set = 0; /* Master's hwaddr is set */ | |
200 | int saved_errno; | |
201 | ||
202 | struct ifreq master_mtu, master_flags, master_hwaddr; | |
203 | struct ifreq slave_mtu, slave_flags, slave_hwaddr; | |
204 | ||
205 | struct dev_ifr { | |
206 | struct ifreq *req_ifr; | |
207 | char *req_name; | |
208 | int req_type; | |
209 | }; | |
210 | ||
211 | struct dev_ifr master_ifra[] = { | |
212 | {&master_mtu, "SIOCGIFMTU", SIOCGIFMTU}, | |
213 | {&master_flags, "SIOCGIFFLAGS", SIOCGIFFLAGS}, | |
214 | {&master_hwaddr, "SIOCGIFHWADDR", SIOCGIFHWADDR}, | |
215 | {NULL, "", 0} | |
216 | }; | |
217 | ||
218 | struct dev_ifr slave_ifra[] = { | |
219 | {&slave_mtu, "SIOCGIFMTU", SIOCGIFMTU}, | |
220 | {&slave_flags, "SIOCGIFFLAGS", SIOCGIFFLAGS}, | |
221 | {&slave_hwaddr, "SIOCGIFHWADDR", SIOCGIFHWADDR}, | |
222 | {NULL, "", 0} | |
223 | }; | |
224 | ||
225 | static void if_print(char *ifname); | |
226 | static int get_drv_info(char *master_ifname); | |
227 | static int get_if_settings(char *ifname, struct dev_ifr ifra[]); | |
228 | static int get_slave_flags(char *slave_ifname); | |
229 | static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr); | |
230 | static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr); | |
231 | static int set_slave_mtu(char *slave_ifname, int mtu); | |
232 | static int set_if_flags(char *ifname, short flags); | |
233 | static int set_if_up(char *ifname, short flags); | |
234 | static int set_if_down(char *ifname, short flags); | |
235 | static int clear_if_addr(char *ifname); | |
236 | static int set_if_addr(char *master_ifname, char *slave_ifname); | |
237 | static int change_active(char *master_ifname, char *slave_ifname); | |
238 | static int enslave(char *master_ifname, char *slave_ifname); | |
239 | static int release(char *master_ifname, char *slave_ifname); | |
240 | #define v_print(fmt, args...) \ | |
241 | if (opt_v) \ | |
242 | fprintf(stderr, fmt, ## args ) | |
243 | ||
244 | int main(int argc, char *argv[]) | |
245 | { | |
246 | char **spp, *master_ifname, *slave_ifname; | |
247 | int c, i, rv; | |
248 | int res = 0; | |
249 | int exclusive = 0; | |
250 | ||
251 | while ((c = getopt_long(argc, argv, "acdfhuvV", longopts, 0)) != EOF) { | |
252 | switch (c) { | |
253 | case 'a': opt_a++; exclusive++; break; | |
254 | case 'c': opt_c++; exclusive++; break; | |
255 | case 'd': opt_d++; exclusive++; break; | |
256 | case 'f': opt_f++; exclusive++; break; | |
257 | case 'h': opt_h++; exclusive++; break; | |
258 | case 'u': opt_u++; exclusive++; break; | |
259 | case 'v': opt_v++; break; | |
260 | case 'V': opt_V++; exclusive++; break; | |
261 | ||
262 | case '?': | |
263 | fprintf(stderr, usage_msg); | |
264 | res = 2; | |
265 | goto out; | |
266 | } | |
267 | } | |
268 | ||
269 | /* options check */ | |
270 | if (exclusive > 1) { | |
271 | fprintf(stderr, usage_msg); | |
272 | res = 2; | |
273 | goto out; | |
274 | } | |
275 | ||
276 | if (opt_v || opt_V) { | |
277 | printf(version); | |
278 | if (opt_V) { | |
279 | res = 0; | |
280 | goto out; | |
281 | } | |
282 | } | |
283 | ||
284 | if (opt_u) { | |
285 | printf(usage_msg); | |
286 | res = 0; | |
287 | goto out; | |
288 | } | |
289 | ||
290 | if (opt_h) { | |
291 | printf(usage_msg); | |
292 | printf(help_msg); | |
293 | res = 0; | |
294 | goto out; | |
295 | } | |
296 | ||
297 | /* Open a basic socket */ | |
298 | if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
299 | perror("socket"); | |
300 | res = 1; | |
301 | goto out; | |
302 | } | |
303 | ||
304 | if (opt_a) { | |
305 | if (optind == argc) { | |
306 | /* No remaining args */ | |
307 | /* show all interfaces */ | |
308 | if_print((char *)NULL); | |
309 | goto out; | |
310 | } else { | |
311 | /* Just show usage */ | |
312 | fprintf(stderr, usage_msg); | |
313 | res = 2; | |
314 | goto out; | |
315 | } | |
316 | } | |
317 | ||
318 | /* Copy the interface name */ | |
319 | spp = argv + optind; | |
320 | master_ifname = *spp++; | |
321 | ||
322 | if (master_ifname == NULL) { | |
323 | fprintf(stderr, usage_msg); | |
324 | res = 2; | |
325 | goto out; | |
326 | } | |
327 | ||
328 | /* exchange abi version with bonding module */ | |
329 | res = get_drv_info(master_ifname); | |
330 | if (res) { | |
331 | fprintf(stderr, | |
332 | "Master '%s': Error: handshake with driver failed. " | |
333 | "Aborting\n", | |
334 | master_ifname); | |
335 | goto out; | |
336 | } | |
337 | ||
338 | slave_ifname = *spp++; | |
339 | ||
340 | if (slave_ifname == NULL) { | |
341 | if (opt_d || opt_c) { | |
342 | fprintf(stderr, usage_msg); | |
343 | res = 2; | |
344 | goto out; | |
345 | } | |
346 | ||
347 | /* A single arg means show the | |
348 | * configuration for this interface | |
349 | */ | |
350 | if_print(master_ifname); | |
351 | goto out; | |
352 | } | |
353 | ||
354 | res = get_if_settings(master_ifname, master_ifra); | |
355 | if (res) { | |
356 | /* Probably a good reason not to go on */ | |
357 | fprintf(stderr, | |
358 | "Master '%s': Error: get settings failed: %s. " | |
359 | "Aborting\n", | |
360 | master_ifname, strerror(res)); | |
361 | goto out; | |
362 | } | |
363 | ||
364 | /* check if master is indeed a master; | |
365 | * if not then fail any operation | |
366 | */ | |
367 | if (!(master_flags.ifr_flags & IFF_MASTER)) { | |
368 | fprintf(stderr, | |
369 | "Illegal operation; the specified interface '%s' " | |
370 | "is not a master. Aborting\n", | |
371 | master_ifname); | |
372 | res = 1; | |
373 | goto out; | |
374 | } | |
375 | ||
376 | /* check if master is up; if not then fail any operation */ | |
377 | if (!(master_flags.ifr_flags & IFF_UP)) { | |
378 | fprintf(stderr, | |
379 | "Illegal operation; the specified master interface " | |
380 | "'%s' is not up.\n", | |
381 | master_ifname); | |
382 | res = 1; | |
383 | goto out; | |
384 | } | |
385 | ||
386 | /* Only for enslaving */ | |
387 | if (!opt_c && !opt_d) { | |
388 | sa_family_t master_family = master_hwaddr.ifr_hwaddr.sa_family; | |
389 | unsigned char *hwaddr = | |
390 | (unsigned char *)master_hwaddr.ifr_hwaddr.sa_data; | |
391 | ||
392 | /* The family '1' is ARPHRD_ETHER for ethernet. */ | |
393 | if (master_family != 1 && !opt_f) { | |
394 | fprintf(stderr, | |
395 | "Illegal operation: The specified master " | |
396 | "interface '%s' is not ethernet-like.\n " | |
397 | "This program is designed to work with " | |
398 | "ethernet-like network interfaces.\n " | |
399 | "Use the '-f' option to force the " | |
400 | "operation.\n", | |
401 | master_ifname); | |
402 | res = 1; | |
403 | goto out; | |
404 | } | |
405 | ||
406 | /* Check master's hw addr */ | |
407 | for (i = 0; i < 6; i++) { | |
408 | if (hwaddr[i] != 0) { | |
409 | hwaddr_set = 1; | |
410 | break; | |
411 | } | |
412 | } | |
413 | ||
414 | if (hwaddr_set) { | |
415 | v_print("current hardware address of master '%s' " | |
416 | "is %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " | |
417 | "type %d\n", | |
418 | master_ifname, | |
419 | hwaddr[0], hwaddr[1], | |
420 | hwaddr[2], hwaddr[3], | |
421 | hwaddr[4], hwaddr[5], | |
422 | master_family); | |
423 | } | |
424 | } | |
425 | ||
426 | /* Accepts only one slave */ | |
427 | if (opt_c) { | |
428 | /* change active slave */ | |
429 | res = get_slave_flags(slave_ifname); | |
430 | if (res) { | |
431 | fprintf(stderr, | |
432 | "Slave '%s': Error: get flags failed. " | |
433 | "Aborting\n", | |
434 | slave_ifname); | |
435 | goto out; | |
436 | } | |
437 | res = change_active(master_ifname, slave_ifname); | |
438 | if (res) { | |
439 | fprintf(stderr, | |
440 | "Master '%s', Slave '%s': Error: " | |
441 | "Change active failed\n", | |
442 | master_ifname, slave_ifname); | |
443 | } | |
444 | } else { | |
445 | /* Accept multiple slaves */ | |
446 | do { | |
447 | if (opt_d) { | |
448 | /* detach a slave interface from the master */ | |
449 | rv = get_slave_flags(slave_ifname); | |
450 | if (rv) { | |
451 | /* Can't work with this slave. */ | |
452 | /* remember the error and skip it*/ | |
453 | fprintf(stderr, | |
454 | "Slave '%s': Error: get flags " | |
455 | "failed. Skipping\n", | |
456 | slave_ifname); | |
457 | res = rv; | |
458 | continue; | |
459 | } | |
460 | rv = release(master_ifname, slave_ifname); | |
461 | if (rv) { | |
462 | fprintf(stderr, | |
463 | "Master '%s', Slave '%s': Error: " | |
464 | "Release failed\n", | |
465 | master_ifname, slave_ifname); | |
466 | res = rv; | |
467 | } | |
468 | } else { | |
469 | /* attach a slave interface to the master */ | |
470 | rv = get_if_settings(slave_ifname, slave_ifra); | |
471 | if (rv) { | |
472 | /* Can't work with this slave. */ | |
473 | /* remember the error and skip it*/ | |
474 | fprintf(stderr, | |
475 | "Slave '%s': Error: get " | |
476 | "settings failed: %s. " | |
477 | "Skipping\n", | |
478 | slave_ifname, strerror(rv)); | |
479 | res = rv; | |
480 | continue; | |
481 | } | |
482 | rv = enslave(master_ifname, slave_ifname); | |
483 | if (rv) { | |
484 | fprintf(stderr, | |
485 | "Master '%s', Slave '%s': Error: " | |
486 | "Enslave failed\n", | |
487 | master_ifname, slave_ifname); | |
488 | res = rv; | |
489 | } | |
490 | } | |
491 | } while ((slave_ifname = *spp++) != NULL); | |
492 | } | |
493 | ||
494 | out: | |
495 | if (skfd >= 0) { | |
496 | close(skfd); | |
497 | } | |
498 | ||
499 | return res; | |
500 | } | |
501 | ||
502 | static short mif_flags; | |
503 | ||
504 | /* Get the inteface configuration from the kernel. */ | |
505 | static int if_getconfig(char *ifname) | |
506 | { | |
507 | struct ifreq ifr; | |
508 | int metric, mtu; /* Parameters of the master interface. */ | |
509 | struct sockaddr dstaddr, broadaddr, netmask; | |
510 | unsigned char *hwaddr; | |
511 | ||
512 | strcpy(ifr.ifr_name, ifname); | |
513 | if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) | |
514 | return -1; | |
515 | mif_flags = ifr.ifr_flags; | |
516 | printf("The result of SIOCGIFFLAGS on %s is %x.\n", | |
517 | ifname, ifr.ifr_flags); | |
518 | ||
519 | strcpy(ifr.ifr_name, ifname); | |
520 | if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0) | |
521 | return -1; | |
522 | printf("The result of SIOCGIFADDR is %2.2x.%2.2x.%2.2x.%2.2x.\n", | |
523 | ifr.ifr_addr.sa_data[0], ifr.ifr_addr.sa_data[1], | |
524 | ifr.ifr_addr.sa_data[2], ifr.ifr_addr.sa_data[3]); | |
525 | ||
526 | strcpy(ifr.ifr_name, ifname); | |
527 | if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) | |
528 | return -1; | |
529 | ||
530 | /* Gotta convert from 'char' to unsigned for printf(). */ | |
531 | hwaddr = (unsigned char *)ifr.ifr_hwaddr.sa_data; | |
532 | printf("The result of SIOCGIFHWADDR is type %d " | |
533 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", | |
534 | ifr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], | |
535 | hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); | |
536 | ||
537 | strcpy(ifr.ifr_name, ifname); | |
538 | if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) { | |
539 | metric = 0; | |
540 | } else | |
541 | metric = ifr.ifr_metric; | |
542 | ||
543 | strcpy(ifr.ifr_name, ifname); | |
544 | if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) | |
545 | mtu = 0; | |
546 | else | |
547 | mtu = ifr.ifr_mtu; | |
548 | ||
549 | strcpy(ifr.ifr_name, ifname); | |
550 | if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) < 0) { | |
551 | memset(&dstaddr, 0, sizeof(struct sockaddr)); | |
552 | } else | |
553 | dstaddr = ifr.ifr_dstaddr; | |
554 | ||
555 | strcpy(ifr.ifr_name, ifname); | |
556 | if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0) { | |
557 | memset(&broadaddr, 0, sizeof(struct sockaddr)); | |
558 | } else | |
559 | broadaddr = ifr.ifr_broadaddr; | |
560 | ||
561 | strcpy(ifr.ifr_name, ifname); | |
562 | if (ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0) { | |
563 | memset(&netmask, 0, sizeof(struct sockaddr)); | |
564 | } else | |
565 | netmask = ifr.ifr_netmask; | |
566 | ||
567 | return 0; | |
568 | } | |
569 | ||
570 | static void if_print(char *ifname) | |
571 | { | |
572 | char buff[1024]; | |
573 | struct ifconf ifc; | |
574 | struct ifreq *ifr; | |
575 | int i; | |
576 | ||
577 | if (ifname == (char *)NULL) { | |
578 | ifc.ifc_len = sizeof(buff); | |
579 | ifc.ifc_buf = buff; | |
580 | if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { | |
581 | perror("SIOCGIFCONF failed"); | |
582 | return; | |
583 | } | |
584 | ||
585 | ifr = ifc.ifc_req; | |
586 | for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { | |
587 | if (if_getconfig(ifr->ifr_name) < 0) { | |
588 | fprintf(stderr, | |
589 | "%s: unknown interface.\n", | |
590 | ifr->ifr_name); | |
591 | continue; | |
592 | } | |
593 | ||
594 | if (((mif_flags & IFF_UP) == 0) && !opt_a) continue; | |
595 | /*ife_print(&ife);*/ | |
596 | } | |
597 | } else { | |
598 | if (if_getconfig(ifname) < 0) { | |
599 | fprintf(stderr, | |
600 | "%s: unknown interface.\n", ifname); | |
601 | } | |
602 | } | |
603 | } | |
604 | ||
605 | static int get_drv_info(char *master_ifname) | |
606 | { | |
607 | struct ifreq ifr; | |
608 | struct ethtool_drvinfo info; | |
609 | char *endptr; | |
610 | ||
611 | memset(&ifr, 0, sizeof(ifr)); | |
612 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
613 | ifr.ifr_data = (caddr_t)&info; | |
614 | ||
615 | info.cmd = ETHTOOL_GDRVINFO; | |
616 | strncpy(info.driver, "ifenslave", 32); | |
617 | snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION); | |
618 | ||
619 | if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) { | |
620 | if (errno == EOPNOTSUPP) { | |
621 | goto out; | |
622 | } | |
623 | ||
624 | saved_errno = errno; | |
625 | v_print("Master '%s': Error: get bonding info failed %s\n", | |
626 | master_ifname, strerror(saved_errno)); | |
627 | return 1; | |
628 | } | |
629 | ||
630 | abi_ver = strtoul(info.fw_version, &endptr, 0); | |
631 | if (*endptr) { | |
632 | v_print("Master '%s': Error: got invalid string as an ABI " | |
633 | "version from the bonding module\n", | |
634 | master_ifname); | |
635 | return 1; | |
636 | } | |
637 | ||
638 | out: | |
639 | v_print("ABI ver is %d\n", abi_ver); | |
640 | ||
641 | return 0; | |
642 | } | |
643 | ||
644 | static int change_active(char *master_ifname, char *slave_ifname) | |
645 | { | |
646 | struct ifreq ifr; | |
647 | int res = 0; | |
648 | ||
649 | if (!(slave_flags.ifr_flags & IFF_SLAVE)) { | |
650 | fprintf(stderr, | |
651 | "Illegal operation: The specified slave interface " | |
652 | "'%s' is not a slave\n", | |
653 | slave_ifname); | |
654 | return 1; | |
655 | } | |
656 | ||
657 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
658 | strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); | |
659 | if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0) && | |
660 | (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0)) { | |
661 | saved_errno = errno; | |
662 | v_print("Master '%s': Error: SIOCBONDCHANGEACTIVE failed: " | |
663 | "%s\n", | |
664 | master_ifname, strerror(saved_errno)); | |
665 | res = 1; | |
666 | } | |
667 | ||
668 | return res; | |
669 | } | |
670 | ||
671 | static int enslave(char *master_ifname, char *slave_ifname) | |
672 | { | |
673 | struct ifreq ifr; | |
674 | int res = 0; | |
675 | ||
676 | if (slave_flags.ifr_flags & IFF_SLAVE) { | |
677 | fprintf(stderr, | |
678 | "Illegal operation: The specified slave interface " | |
679 | "'%s' is already a slave\n", | |
680 | slave_ifname); | |
681 | return 1; | |
682 | } | |
683 | ||
684 | res = set_if_down(slave_ifname, slave_flags.ifr_flags); | |
685 | if (res) { | |
686 | fprintf(stderr, | |
687 | "Slave '%s': Error: bring interface down failed\n", | |
688 | slave_ifname); | |
689 | return res; | |
690 | } | |
691 | ||
692 | if (abi_ver < 2) { | |
693 | /* Older bonding versions would panic if the slave has no IP | |
694 | * address, so get the IP setting from the master. | |
695 | */ | |
e6d184e3 | 696 | set_if_addr(master_ifname, slave_ifname); |
1da177e4 LT |
697 | } else { |
698 | res = clear_if_addr(slave_ifname); | |
699 | if (res) { | |
700 | fprintf(stderr, | |
701 | "Slave '%s': Error: clear address failed\n", | |
702 | slave_ifname); | |
703 | return res; | |
704 | } | |
705 | } | |
706 | ||
707 | if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) { | |
708 | res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu); | |
709 | if (res) { | |
710 | fprintf(stderr, | |
711 | "Slave '%s': Error: set MTU failed\n", | |
712 | slave_ifname); | |
713 | return res; | |
714 | } | |
715 | } | |
716 | ||
717 | if (hwaddr_set) { | |
718 | /* Master already has an hwaddr | |
719 | * so set it's hwaddr to the slave | |
720 | */ | |
721 | if (abi_ver < 1) { | |
722 | /* The driver is using an old ABI, so | |
723 | * the application sets the slave's | |
724 | * hwaddr | |
725 | */ | |
726 | res = set_slave_hwaddr(slave_ifname, | |
727 | &(master_hwaddr.ifr_hwaddr)); | |
728 | if (res) { | |
729 | fprintf(stderr, | |
730 | "Slave '%s': Error: set hw address " | |
731 | "failed\n", | |
732 | slave_ifname); | |
733 | goto undo_mtu; | |
734 | } | |
735 | ||
736 | /* For old ABI the application needs to bring the | |
737 | * slave back up | |
738 | */ | |
739 | res = set_if_up(slave_ifname, slave_flags.ifr_flags); | |
740 | if (res) { | |
741 | fprintf(stderr, | |
742 | "Slave '%s': Error: bring interface " | |
743 | "down failed\n", | |
744 | slave_ifname); | |
745 | goto undo_slave_mac; | |
746 | } | |
747 | } | |
748 | /* The driver is using a new ABI, | |
749 | * so the driver takes care of setting | |
750 | * the slave's hwaddr and bringing | |
751 | * it up again | |
752 | */ | |
753 | } else { | |
754 | /* No hwaddr for master yet, so | |
755 | * set the slave's hwaddr to it | |
756 | */ | |
757 | if (abi_ver < 1) { | |
758 | /* For old ABI, the master needs to be | |
759 | * down before setting it's hwaddr | |
760 | */ | |
761 | res = set_if_down(master_ifname, master_flags.ifr_flags); | |
762 | if (res) { | |
763 | fprintf(stderr, | |
764 | "Master '%s': Error: bring interface " | |
765 | "down failed\n", | |
766 | master_ifname); | |
767 | goto undo_mtu; | |
768 | } | |
769 | } | |
770 | ||
771 | res = set_master_hwaddr(master_ifname, | |
772 | &(slave_hwaddr.ifr_hwaddr)); | |
773 | if (res) { | |
774 | fprintf(stderr, | |
775 | "Master '%s': Error: set hw address " | |
776 | "failed\n", | |
777 | master_ifname); | |
778 | goto undo_mtu; | |
779 | } | |
780 | ||
781 | if (abi_ver < 1) { | |
782 | /* For old ABI, bring the master | |
783 | * back up | |
784 | */ | |
785 | res = set_if_up(master_ifname, master_flags.ifr_flags); | |
786 | if (res) { | |
787 | fprintf(stderr, | |
788 | "Master '%s': Error: bring interface " | |
789 | "up failed\n", | |
790 | master_ifname); | |
791 | goto undo_master_mac; | |
792 | } | |
793 | } | |
794 | ||
795 | hwaddr_set = 1; | |
796 | } | |
797 | ||
798 | /* Do the real thing */ | |
799 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
800 | strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); | |
801 | if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) && | |
802 | (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) { | |
803 | saved_errno = errno; | |
804 | v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n", | |
805 | master_ifname, strerror(saved_errno)); | |
806 | res = 1; | |
807 | } | |
808 | ||
809 | if (res) { | |
810 | goto undo_master_mac; | |
811 | } | |
812 | ||
813 | return 0; | |
814 | ||
815 | /* rollback (best effort) */ | |
816 | undo_master_mac: | |
817 | set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr)); | |
818 | hwaddr_set = 0; | |
819 | goto undo_mtu; | |
820 | undo_slave_mac: | |
821 | set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr)); | |
822 | undo_mtu: | |
823 | set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu); | |
824 | return res; | |
825 | } | |
826 | ||
827 | static int release(char *master_ifname, char *slave_ifname) | |
828 | { | |
829 | struct ifreq ifr; | |
830 | int res = 0; | |
831 | ||
832 | if (!(slave_flags.ifr_flags & IFF_SLAVE)) { | |
833 | fprintf(stderr, | |
834 | "Illegal operation: The specified slave interface " | |
835 | "'%s' is not a slave\n", | |
836 | slave_ifname); | |
837 | return 1; | |
838 | } | |
839 | ||
840 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
841 | strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); | |
842 | if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) && | |
843 | (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) { | |
844 | saved_errno = errno; | |
845 | v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n", | |
846 | master_ifname, strerror(saved_errno)); | |
847 | return 1; | |
848 | } else if (abi_ver < 1) { | |
849 | /* The driver is using an old ABI, so we'll set the interface | |
850 | * down to avoid any conflicts due to same MAC/IP | |
851 | */ | |
852 | res = set_if_down(slave_ifname, slave_flags.ifr_flags); | |
853 | if (res) { | |
854 | fprintf(stderr, | |
855 | "Slave '%s': Error: bring interface " | |
856 | "down failed\n", | |
857 | slave_ifname); | |
858 | } | |
859 | } | |
860 | ||
861 | /* set to default mtu */ | |
862 | set_slave_mtu(slave_ifname, 1500); | |
863 | ||
864 | return res; | |
865 | } | |
866 | ||
867 | static int get_if_settings(char *ifname, struct dev_ifr ifra[]) | |
868 | { | |
869 | int i; | |
870 | int res = 0; | |
871 | ||
872 | for (i = 0; ifra[i].req_ifr; i++) { | |
873 | strncpy(ifra[i].req_ifr->ifr_name, ifname, IFNAMSIZ); | |
874 | res = ioctl(skfd, ifra[i].req_type, ifra[i].req_ifr); | |
875 | if (res < 0) { | |
876 | saved_errno = errno; | |
877 | v_print("Interface '%s': Error: %s failed: %s\n", | |
878 | ifname, ifra[i].req_name, | |
879 | strerror(saved_errno)); | |
880 | ||
881 | return saved_errno; | |
882 | } | |
883 | } | |
884 | ||
885 | return 0; | |
886 | } | |
887 | ||
888 | static int get_slave_flags(char *slave_ifname) | |
889 | { | |
890 | int res = 0; | |
891 | ||
892 | strncpy(slave_flags.ifr_name, slave_ifname, IFNAMSIZ); | |
893 | res = ioctl(skfd, SIOCGIFFLAGS, &slave_flags); | |
894 | if (res < 0) { | |
895 | saved_errno = errno; | |
896 | v_print("Slave '%s': Error: SIOCGIFFLAGS failed: %s\n", | |
897 | slave_ifname, strerror(saved_errno)); | |
898 | } else { | |
899 | v_print("Slave %s: flags %04X.\n", | |
900 | slave_ifname, slave_flags.ifr_flags); | |
901 | } | |
902 | ||
903 | return res; | |
904 | } | |
905 | ||
906 | static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr) | |
907 | { | |
908 | unsigned char *addr = (unsigned char *)hwaddr->sa_data; | |
909 | struct ifreq ifr; | |
910 | int res = 0; | |
911 | ||
912 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
913 | memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); | |
914 | res = ioctl(skfd, SIOCSIFHWADDR, &ifr); | |
915 | if (res < 0) { | |
916 | saved_errno = errno; | |
917 | v_print("Master '%s': Error: SIOCSIFHWADDR failed: %s\n", | |
918 | master_ifname, strerror(saved_errno)); | |
919 | return res; | |
920 | } else { | |
921 | v_print("Master '%s': hardware address set to " | |
922 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", | |
923 | master_ifname, addr[0], addr[1], addr[2], | |
924 | addr[3], addr[4], addr[5]); | |
925 | } | |
926 | ||
927 | return res; | |
928 | } | |
929 | ||
930 | static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr) | |
931 | { | |
932 | unsigned char *addr = (unsigned char *)hwaddr->sa_data; | |
933 | struct ifreq ifr; | |
934 | int res = 0; | |
935 | ||
936 | strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); | |
937 | memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); | |
938 | res = ioctl(skfd, SIOCSIFHWADDR, &ifr); | |
939 | if (res < 0) { | |
940 | saved_errno = errno; | |
941 | ||
942 | v_print("Slave '%s': Error: SIOCSIFHWADDR failed: %s\n", | |
943 | slave_ifname, strerror(saved_errno)); | |
944 | ||
945 | if (saved_errno == EBUSY) { | |
946 | v_print(" The device is busy: it must be idle " | |
947 | "before running this command.\n"); | |
948 | } else if (saved_errno == EOPNOTSUPP) { | |
949 | v_print(" The device does not support setting " | |
950 | "the MAC address.\n" | |
951 | " Your kernel likely does not support slave " | |
952 | "devices.\n"); | |
953 | } else if (saved_errno == EINVAL) { | |
954 | v_print(" The device's address type does not match " | |
955 | "the master's address type.\n"); | |
956 | } | |
957 | return res; | |
958 | } else { | |
959 | v_print("Slave '%s': hardware address set to " | |
960 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", | |
961 | slave_ifname, addr[0], addr[1], addr[2], | |
962 | addr[3], addr[4], addr[5]); | |
963 | } | |
964 | ||
965 | return res; | |
966 | } | |
967 | ||
968 | static int set_slave_mtu(char *slave_ifname, int mtu) | |
969 | { | |
970 | struct ifreq ifr; | |
971 | int res = 0; | |
972 | ||
973 | ifr.ifr_mtu = mtu; | |
974 | strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); | |
975 | ||
976 | res = ioctl(skfd, SIOCSIFMTU, &ifr); | |
977 | if (res < 0) { | |
978 | saved_errno = errno; | |
979 | v_print("Slave '%s': Error: SIOCSIFMTU failed: %s\n", | |
980 | slave_ifname, strerror(saved_errno)); | |
981 | } else { | |
982 | v_print("Slave '%s': MTU set to %d.\n", slave_ifname, mtu); | |
983 | } | |
984 | ||
985 | return res; | |
986 | } | |
987 | ||
988 | static int set_if_flags(char *ifname, short flags) | |
989 | { | |
990 | struct ifreq ifr; | |
991 | int res = 0; | |
992 | ||
993 | ifr.ifr_flags = flags; | |
994 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
995 | ||
996 | res = ioctl(skfd, SIOCSIFFLAGS, &ifr); | |
997 | if (res < 0) { | |
998 | saved_errno = errno; | |
999 | v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n", | |
1000 | ifname, strerror(saved_errno)); | |
1001 | } else { | |
1002 | v_print("Interface '%s': flags set to %04X.\n", ifname, flags); | |
1003 | } | |
1004 | ||
1005 | return res; | |
1006 | } | |
1007 | ||
1008 | static int set_if_up(char *ifname, short flags) | |
1009 | { | |
1010 | return set_if_flags(ifname, flags | IFF_UP); | |
1011 | } | |
1012 | ||
1013 | static int set_if_down(char *ifname, short flags) | |
1014 | { | |
1015 | return set_if_flags(ifname, flags & ~IFF_UP); | |
1016 | } | |
1017 | ||
1018 | static int clear_if_addr(char *ifname) | |
1019 | { | |
1020 | struct ifreq ifr; | |
1021 | int res = 0; | |
1022 | ||
1023 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
1024 | ifr.ifr_addr.sa_family = AF_INET; | |
1025 | memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); | |
1026 | ||
1027 | res = ioctl(skfd, SIOCSIFADDR, &ifr); | |
1028 | if (res < 0) { | |
1029 | saved_errno = errno; | |
1030 | v_print("Interface '%s': Error: SIOCSIFADDR failed: %s\n", | |
1031 | ifname, strerror(saved_errno)); | |
1032 | } else { | |
1033 | v_print("Interface '%s': address cleared\n", ifname); | |
1034 | } | |
1035 | ||
1036 | return res; | |
1037 | } | |
1038 | ||
1039 | static int set_if_addr(char *master_ifname, char *slave_ifname) | |
1040 | { | |
1041 | struct ifreq ifr; | |
1042 | int res; | |
1043 | unsigned char *ipaddr; | |
1044 | int i; | |
1045 | struct { | |
1046 | char *req_name; | |
1047 | char *desc; | |
1048 | int g_ioctl; | |
1049 | int s_ioctl; | |
1050 | } ifra[] = { | |
1051 | {"IFADDR", "addr", SIOCGIFADDR, SIOCSIFADDR}, | |
1052 | {"DSTADDR", "destination addr", SIOCGIFDSTADDR, SIOCSIFDSTADDR}, | |
1053 | {"BRDADDR", "broadcast addr", SIOCGIFBRDADDR, SIOCSIFBRDADDR}, | |
1054 | {"NETMASK", "netmask", SIOCGIFNETMASK, SIOCSIFNETMASK}, | |
1055 | {NULL, NULL, 0, 0}, | |
1056 | }; | |
1057 | ||
1058 | for (i = 0; ifra[i].req_name; i++) { | |
1059 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
1060 | res = ioctl(skfd, ifra[i].g_ioctl, &ifr); | |
1061 | if (res < 0) { | |
1062 | int saved_errno = errno; | |
1063 | ||
1064 | v_print("Interface '%s': Error: SIOCG%s failed: %s\n", | |
1065 | master_ifname, ifra[i].req_name, | |
1066 | strerror(saved_errno)); | |
1067 | ||
1068 | ifr.ifr_addr.sa_family = AF_INET; | |
1069 | memset(ifr.ifr_addr.sa_data, 0, | |
1070 | sizeof(ifr.ifr_addr.sa_data)); | |
1071 | } | |
1072 | ||
1073 | strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); | |
1074 | res = ioctl(skfd, ifra[i].s_ioctl, &ifr); | |
1075 | if (res < 0) { | |
1076 | int saved_errno = errno; | |
1077 | ||
1078 | v_print("Interface '%s': Error: SIOCS%s failed: %s\n", | |
1079 | slave_ifname, ifra[i].req_name, | |
1080 | strerror(saved_errno)); | |
1081 | ||
1da177e4 LT |
1082 | } |
1083 | ||
1084 | ipaddr = ifr.ifr_addr.sa_data; | |
1085 | v_print("Interface '%s': set IP %s to %d.%d.%d.%d\n", | |
1086 | slave_ifname, ifra[i].desc, | |
1087 | ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); | |
1088 | } | |
1089 | ||
1090 | return 0; | |
1091 | } | |
1092 | ||
1093 | /* | |
1094 | * Local variables: | |
1095 | * version-control: t | |
1096 | * kept-new-versions: 5 | |
1097 | * c-indent-level: 4 | |
1098 | * c-basic-offset: 4 | |
1099 | * tab-width: 4 | |
1100 | * compile-command: "gcc -Wall -Wstrict-prototypes -O -I/usr/src/linux/include ifenslave.c -o ifenslave" | |
1101 | * End: | |
1102 | */ | |
1103 |