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 '?': | |
d804c6f2 | 263 | fprintf(stderr, "%s", usage_msg); |
1da177e4 LT |
264 | res = 2; |
265 | goto out; | |
266 | } | |
267 | } | |
268 | ||
269 | /* options check */ | |
270 | if (exclusive > 1) { | |
d804c6f2 | 271 | fprintf(stderr, "%s", usage_msg); |
1da177e4 LT |
272 | res = 2; |
273 | goto out; | |
274 | } | |
275 | ||
276 | if (opt_v || opt_V) { | |
d804c6f2 | 277 | printf("%s", version); |
1da177e4 LT |
278 | if (opt_V) { |
279 | res = 0; | |
280 | goto out; | |
281 | } | |
282 | } | |
283 | ||
284 | if (opt_u) { | |
d804c6f2 | 285 | printf("%s", usage_msg); |
1da177e4 LT |
286 | res = 0; |
287 | goto out; | |
288 | } | |
289 | ||
290 | if (opt_h) { | |
d804c6f2 SW |
291 | printf("%s", usage_msg); |
292 | printf("%s", help_msg); | |
1da177e4 LT |
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 */ | |
d804c6f2 | 312 | fprintf(stderr, "%s", usage_msg); |
1da177e4 LT |
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) { | |
d804c6f2 | 323 | fprintf(stderr, "%s", usage_msg); |
1da177e4 LT |
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) { | |
d804c6f2 | 342 | fprintf(stderr, "%s", usage_msg); |
1da177e4 LT |
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; | |
450faacc | 542 | printf("The result of SIOCGIFMETRIC is %d\n", metric); |
1da177e4 LT |
543 | |
544 | strcpy(ifr.ifr_name, ifname); | |
545 | if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) | |
546 | mtu = 0; | |
547 | else | |
548 | mtu = ifr.ifr_mtu; | |
450faacc | 549 | printf("The result of SIOCGIFMTU is %d\n", mtu); |
1da177e4 LT |
550 | |
551 | strcpy(ifr.ifr_name, ifname); | |
552 | if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) < 0) { | |
553 | memset(&dstaddr, 0, sizeof(struct sockaddr)); | |
554 | } else | |
555 | dstaddr = ifr.ifr_dstaddr; | |
556 | ||
557 | strcpy(ifr.ifr_name, ifname); | |
558 | if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0) { | |
559 | memset(&broadaddr, 0, sizeof(struct sockaddr)); | |
560 | } else | |
561 | broadaddr = ifr.ifr_broadaddr; | |
562 | ||
563 | strcpy(ifr.ifr_name, ifname); | |
564 | if (ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0) { | |
565 | memset(&netmask, 0, sizeof(struct sockaddr)); | |
566 | } else | |
567 | netmask = ifr.ifr_netmask; | |
568 | ||
569 | return 0; | |
570 | } | |
571 | ||
572 | static void if_print(char *ifname) | |
573 | { | |
574 | char buff[1024]; | |
575 | struct ifconf ifc; | |
576 | struct ifreq *ifr; | |
577 | int i; | |
578 | ||
579 | if (ifname == (char *)NULL) { | |
580 | ifc.ifc_len = sizeof(buff); | |
581 | ifc.ifc_buf = buff; | |
582 | if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { | |
583 | perror("SIOCGIFCONF failed"); | |
584 | return; | |
585 | } | |
586 | ||
587 | ifr = ifc.ifc_req; | |
588 | for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { | |
589 | if (if_getconfig(ifr->ifr_name) < 0) { | |
590 | fprintf(stderr, | |
591 | "%s: unknown interface.\n", | |
592 | ifr->ifr_name); | |
593 | continue; | |
594 | } | |
595 | ||
596 | if (((mif_flags & IFF_UP) == 0) && !opt_a) continue; | |
597 | /*ife_print(&ife);*/ | |
598 | } | |
599 | } else { | |
600 | if (if_getconfig(ifname) < 0) { | |
601 | fprintf(stderr, | |
602 | "%s: unknown interface.\n", ifname); | |
603 | } | |
604 | } | |
605 | } | |
606 | ||
607 | static int get_drv_info(char *master_ifname) | |
608 | { | |
609 | struct ifreq ifr; | |
610 | struct ethtool_drvinfo info; | |
611 | char *endptr; | |
612 | ||
613 | memset(&ifr, 0, sizeof(ifr)); | |
614 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
615 | ifr.ifr_data = (caddr_t)&info; | |
616 | ||
617 | info.cmd = ETHTOOL_GDRVINFO; | |
618 | strncpy(info.driver, "ifenslave", 32); | |
619 | snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION); | |
620 | ||
621 | if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) { | |
622 | if (errno == EOPNOTSUPP) { | |
623 | goto out; | |
624 | } | |
625 | ||
626 | saved_errno = errno; | |
627 | v_print("Master '%s': Error: get bonding info failed %s\n", | |
628 | master_ifname, strerror(saved_errno)); | |
629 | return 1; | |
630 | } | |
631 | ||
632 | abi_ver = strtoul(info.fw_version, &endptr, 0); | |
633 | if (*endptr) { | |
634 | v_print("Master '%s': Error: got invalid string as an ABI " | |
635 | "version from the bonding module\n", | |
636 | master_ifname); | |
637 | return 1; | |
638 | } | |
639 | ||
640 | out: | |
641 | v_print("ABI ver is %d\n", abi_ver); | |
642 | ||
643 | return 0; | |
644 | } | |
645 | ||
646 | static int change_active(char *master_ifname, char *slave_ifname) | |
647 | { | |
648 | struct ifreq ifr; | |
649 | int res = 0; | |
650 | ||
651 | if (!(slave_flags.ifr_flags & IFF_SLAVE)) { | |
652 | fprintf(stderr, | |
653 | "Illegal operation: The specified slave interface " | |
654 | "'%s' is not a slave\n", | |
655 | slave_ifname); | |
656 | return 1; | |
657 | } | |
658 | ||
659 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
660 | strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); | |
661 | if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0) && | |
662 | (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0)) { | |
663 | saved_errno = errno; | |
664 | v_print("Master '%s': Error: SIOCBONDCHANGEACTIVE failed: " | |
665 | "%s\n", | |
666 | master_ifname, strerror(saved_errno)); | |
667 | res = 1; | |
668 | } | |
669 | ||
670 | return res; | |
671 | } | |
672 | ||
673 | static int enslave(char *master_ifname, char *slave_ifname) | |
674 | { | |
675 | struct ifreq ifr; | |
676 | int res = 0; | |
677 | ||
678 | if (slave_flags.ifr_flags & IFF_SLAVE) { | |
679 | fprintf(stderr, | |
680 | "Illegal operation: The specified slave interface " | |
681 | "'%s' is already a slave\n", | |
682 | slave_ifname); | |
683 | return 1; | |
684 | } | |
685 | ||
686 | res = set_if_down(slave_ifname, slave_flags.ifr_flags); | |
687 | if (res) { | |
688 | fprintf(stderr, | |
689 | "Slave '%s': Error: bring interface down failed\n", | |
690 | slave_ifname); | |
691 | return res; | |
692 | } | |
693 | ||
694 | if (abi_ver < 2) { | |
695 | /* Older bonding versions would panic if the slave has no IP | |
696 | * address, so get the IP setting from the master. | |
697 | */ | |
e6d184e3 | 698 | set_if_addr(master_ifname, slave_ifname); |
1da177e4 LT |
699 | } else { |
700 | res = clear_if_addr(slave_ifname); | |
701 | if (res) { | |
702 | fprintf(stderr, | |
703 | "Slave '%s': Error: clear address failed\n", | |
704 | slave_ifname); | |
705 | return res; | |
706 | } | |
707 | } | |
708 | ||
709 | if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) { | |
710 | res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu); | |
711 | if (res) { | |
712 | fprintf(stderr, | |
713 | "Slave '%s': Error: set MTU failed\n", | |
714 | slave_ifname); | |
715 | return res; | |
716 | } | |
717 | } | |
718 | ||
719 | if (hwaddr_set) { | |
720 | /* Master already has an hwaddr | |
721 | * so set it's hwaddr to the slave | |
722 | */ | |
723 | if (abi_ver < 1) { | |
724 | /* The driver is using an old ABI, so | |
725 | * the application sets the slave's | |
726 | * hwaddr | |
727 | */ | |
728 | res = set_slave_hwaddr(slave_ifname, | |
729 | &(master_hwaddr.ifr_hwaddr)); | |
730 | if (res) { | |
731 | fprintf(stderr, | |
732 | "Slave '%s': Error: set hw address " | |
733 | "failed\n", | |
734 | slave_ifname); | |
735 | goto undo_mtu; | |
736 | } | |
737 | ||
738 | /* For old ABI the application needs to bring the | |
739 | * slave back up | |
740 | */ | |
741 | res = set_if_up(slave_ifname, slave_flags.ifr_flags); | |
742 | if (res) { | |
743 | fprintf(stderr, | |
744 | "Slave '%s': Error: bring interface " | |
745 | "down failed\n", | |
746 | slave_ifname); | |
747 | goto undo_slave_mac; | |
748 | } | |
749 | } | |
750 | /* The driver is using a new ABI, | |
751 | * so the driver takes care of setting | |
752 | * the slave's hwaddr and bringing | |
753 | * it up again | |
754 | */ | |
755 | } else { | |
756 | /* No hwaddr for master yet, so | |
757 | * set the slave's hwaddr to it | |
758 | */ | |
759 | if (abi_ver < 1) { | |
760 | /* For old ABI, the master needs to be | |
a33f3224 | 761 | * down before setting its hwaddr |
1da177e4 LT |
762 | */ |
763 | res = set_if_down(master_ifname, master_flags.ifr_flags); | |
764 | if (res) { | |
765 | fprintf(stderr, | |
766 | "Master '%s': Error: bring interface " | |
767 | "down failed\n", | |
768 | master_ifname); | |
769 | goto undo_mtu; | |
770 | } | |
771 | } | |
772 | ||
773 | res = set_master_hwaddr(master_ifname, | |
774 | &(slave_hwaddr.ifr_hwaddr)); | |
775 | if (res) { | |
776 | fprintf(stderr, | |
777 | "Master '%s': Error: set hw address " | |
778 | "failed\n", | |
779 | master_ifname); | |
780 | goto undo_mtu; | |
781 | } | |
782 | ||
783 | if (abi_ver < 1) { | |
784 | /* For old ABI, bring the master | |
785 | * back up | |
786 | */ | |
787 | res = set_if_up(master_ifname, master_flags.ifr_flags); | |
788 | if (res) { | |
789 | fprintf(stderr, | |
790 | "Master '%s': Error: bring interface " | |
791 | "up failed\n", | |
792 | master_ifname); | |
793 | goto undo_master_mac; | |
794 | } | |
795 | } | |
796 | ||
797 | hwaddr_set = 1; | |
798 | } | |
799 | ||
800 | /* Do the real thing */ | |
801 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
802 | strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); | |
803 | if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) && | |
804 | (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) { | |
805 | saved_errno = errno; | |
806 | v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n", | |
807 | master_ifname, strerror(saved_errno)); | |
808 | res = 1; | |
809 | } | |
810 | ||
811 | if (res) { | |
812 | goto undo_master_mac; | |
813 | } | |
814 | ||
815 | return 0; | |
816 | ||
817 | /* rollback (best effort) */ | |
818 | undo_master_mac: | |
819 | set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr)); | |
820 | hwaddr_set = 0; | |
821 | goto undo_mtu; | |
822 | undo_slave_mac: | |
823 | set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr)); | |
824 | undo_mtu: | |
825 | set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu); | |
826 | return res; | |
827 | } | |
828 | ||
829 | static int release(char *master_ifname, char *slave_ifname) | |
830 | { | |
831 | struct ifreq ifr; | |
832 | int res = 0; | |
833 | ||
834 | if (!(slave_flags.ifr_flags & IFF_SLAVE)) { | |
835 | fprintf(stderr, | |
836 | "Illegal operation: The specified slave interface " | |
837 | "'%s' is not a slave\n", | |
838 | slave_ifname); | |
839 | return 1; | |
840 | } | |
841 | ||
842 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
843 | strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); | |
844 | if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) && | |
845 | (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) { | |
846 | saved_errno = errno; | |
847 | v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n", | |
848 | master_ifname, strerror(saved_errno)); | |
849 | return 1; | |
850 | } else if (abi_ver < 1) { | |
851 | /* The driver is using an old ABI, so we'll set the interface | |
852 | * down to avoid any conflicts due to same MAC/IP | |
853 | */ | |
854 | res = set_if_down(slave_ifname, slave_flags.ifr_flags); | |
855 | if (res) { | |
856 | fprintf(stderr, | |
857 | "Slave '%s': Error: bring interface " | |
858 | "down failed\n", | |
859 | slave_ifname); | |
860 | } | |
861 | } | |
862 | ||
863 | /* set to default mtu */ | |
864 | set_slave_mtu(slave_ifname, 1500); | |
865 | ||
866 | return res; | |
867 | } | |
868 | ||
869 | static int get_if_settings(char *ifname, struct dev_ifr ifra[]) | |
870 | { | |
871 | int i; | |
872 | int res = 0; | |
873 | ||
874 | for (i = 0; ifra[i].req_ifr; i++) { | |
875 | strncpy(ifra[i].req_ifr->ifr_name, ifname, IFNAMSIZ); | |
876 | res = ioctl(skfd, ifra[i].req_type, ifra[i].req_ifr); | |
877 | if (res < 0) { | |
878 | saved_errno = errno; | |
879 | v_print("Interface '%s': Error: %s failed: %s\n", | |
880 | ifname, ifra[i].req_name, | |
881 | strerror(saved_errno)); | |
882 | ||
883 | return saved_errno; | |
884 | } | |
885 | } | |
886 | ||
887 | return 0; | |
888 | } | |
889 | ||
890 | static int get_slave_flags(char *slave_ifname) | |
891 | { | |
892 | int res = 0; | |
893 | ||
894 | strncpy(slave_flags.ifr_name, slave_ifname, IFNAMSIZ); | |
895 | res = ioctl(skfd, SIOCGIFFLAGS, &slave_flags); | |
896 | if (res < 0) { | |
897 | saved_errno = errno; | |
898 | v_print("Slave '%s': Error: SIOCGIFFLAGS failed: %s\n", | |
899 | slave_ifname, strerror(saved_errno)); | |
900 | } else { | |
901 | v_print("Slave %s: flags %04X.\n", | |
902 | slave_ifname, slave_flags.ifr_flags); | |
903 | } | |
904 | ||
905 | return res; | |
906 | } | |
907 | ||
908 | static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr) | |
909 | { | |
910 | unsigned char *addr = (unsigned char *)hwaddr->sa_data; | |
911 | struct ifreq ifr; | |
912 | int res = 0; | |
913 | ||
914 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
915 | memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); | |
916 | res = ioctl(skfd, SIOCSIFHWADDR, &ifr); | |
917 | if (res < 0) { | |
918 | saved_errno = errno; | |
919 | v_print("Master '%s': Error: SIOCSIFHWADDR failed: %s\n", | |
920 | master_ifname, strerror(saved_errno)); | |
921 | return res; | |
922 | } else { | |
923 | v_print("Master '%s': hardware address set to " | |
924 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", | |
925 | master_ifname, addr[0], addr[1], addr[2], | |
926 | addr[3], addr[4], addr[5]); | |
927 | } | |
928 | ||
929 | return res; | |
930 | } | |
931 | ||
932 | static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr) | |
933 | { | |
934 | unsigned char *addr = (unsigned char *)hwaddr->sa_data; | |
935 | struct ifreq ifr; | |
936 | int res = 0; | |
937 | ||
938 | strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); | |
939 | memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); | |
940 | res = ioctl(skfd, SIOCSIFHWADDR, &ifr); | |
941 | if (res < 0) { | |
942 | saved_errno = errno; | |
943 | ||
944 | v_print("Slave '%s': Error: SIOCSIFHWADDR failed: %s\n", | |
945 | slave_ifname, strerror(saved_errno)); | |
946 | ||
947 | if (saved_errno == EBUSY) { | |
948 | v_print(" The device is busy: it must be idle " | |
949 | "before running this command.\n"); | |
950 | } else if (saved_errno == EOPNOTSUPP) { | |
951 | v_print(" The device does not support setting " | |
952 | "the MAC address.\n" | |
953 | " Your kernel likely does not support slave " | |
954 | "devices.\n"); | |
955 | } else if (saved_errno == EINVAL) { | |
956 | v_print(" The device's address type does not match " | |
957 | "the master's address type.\n"); | |
958 | } | |
959 | return res; | |
960 | } else { | |
961 | v_print("Slave '%s': hardware address set to " | |
962 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", | |
963 | slave_ifname, addr[0], addr[1], addr[2], | |
964 | addr[3], addr[4], addr[5]); | |
965 | } | |
966 | ||
967 | return res; | |
968 | } | |
969 | ||
970 | static int set_slave_mtu(char *slave_ifname, int mtu) | |
971 | { | |
972 | struct ifreq ifr; | |
973 | int res = 0; | |
974 | ||
975 | ifr.ifr_mtu = mtu; | |
976 | strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); | |
977 | ||
978 | res = ioctl(skfd, SIOCSIFMTU, &ifr); | |
979 | if (res < 0) { | |
980 | saved_errno = errno; | |
981 | v_print("Slave '%s': Error: SIOCSIFMTU failed: %s\n", | |
982 | slave_ifname, strerror(saved_errno)); | |
983 | } else { | |
984 | v_print("Slave '%s': MTU set to %d.\n", slave_ifname, mtu); | |
985 | } | |
986 | ||
987 | return res; | |
988 | } | |
989 | ||
990 | static int set_if_flags(char *ifname, short flags) | |
991 | { | |
992 | struct ifreq ifr; | |
993 | int res = 0; | |
994 | ||
995 | ifr.ifr_flags = flags; | |
996 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
997 | ||
998 | res = ioctl(skfd, SIOCSIFFLAGS, &ifr); | |
999 | if (res < 0) { | |
1000 | saved_errno = errno; | |
1001 | v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n", | |
1002 | ifname, strerror(saved_errno)); | |
1003 | } else { | |
1004 | v_print("Interface '%s': flags set to %04X.\n", ifname, flags); | |
1005 | } | |
1006 | ||
1007 | return res; | |
1008 | } | |
1009 | ||
1010 | static int set_if_up(char *ifname, short flags) | |
1011 | { | |
1012 | return set_if_flags(ifname, flags | IFF_UP); | |
1013 | } | |
1014 | ||
1015 | static int set_if_down(char *ifname, short flags) | |
1016 | { | |
1017 | return set_if_flags(ifname, flags & ~IFF_UP); | |
1018 | } | |
1019 | ||
1020 | static int clear_if_addr(char *ifname) | |
1021 | { | |
1022 | struct ifreq ifr; | |
1023 | int res = 0; | |
1024 | ||
1025 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
1026 | ifr.ifr_addr.sa_family = AF_INET; | |
1027 | memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); | |
1028 | ||
1029 | res = ioctl(skfd, SIOCSIFADDR, &ifr); | |
1030 | if (res < 0) { | |
1031 | saved_errno = errno; | |
1032 | v_print("Interface '%s': Error: SIOCSIFADDR failed: %s\n", | |
1033 | ifname, strerror(saved_errno)); | |
1034 | } else { | |
1035 | v_print("Interface '%s': address cleared\n", ifname); | |
1036 | } | |
1037 | ||
1038 | return res; | |
1039 | } | |
1040 | ||
1041 | static int set_if_addr(char *master_ifname, char *slave_ifname) | |
1042 | { | |
1043 | struct ifreq ifr; | |
1044 | int res; | |
1045 | unsigned char *ipaddr; | |
1046 | int i; | |
1047 | struct { | |
1048 | char *req_name; | |
1049 | char *desc; | |
1050 | int g_ioctl; | |
1051 | int s_ioctl; | |
1052 | } ifra[] = { | |
1053 | {"IFADDR", "addr", SIOCGIFADDR, SIOCSIFADDR}, | |
1054 | {"DSTADDR", "destination addr", SIOCGIFDSTADDR, SIOCSIFDSTADDR}, | |
1055 | {"BRDADDR", "broadcast addr", SIOCGIFBRDADDR, SIOCSIFBRDADDR}, | |
1056 | {"NETMASK", "netmask", SIOCGIFNETMASK, SIOCSIFNETMASK}, | |
1057 | {NULL, NULL, 0, 0}, | |
1058 | }; | |
1059 | ||
1060 | for (i = 0; ifra[i].req_name; i++) { | |
1061 | strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); | |
1062 | res = ioctl(skfd, ifra[i].g_ioctl, &ifr); | |
1063 | if (res < 0) { | |
1064 | int saved_errno = errno; | |
1065 | ||
1066 | v_print("Interface '%s': Error: SIOCG%s failed: %s\n", | |
1067 | master_ifname, ifra[i].req_name, | |
1068 | strerror(saved_errno)); | |
1069 | ||
1070 | ifr.ifr_addr.sa_family = AF_INET; | |
1071 | memset(ifr.ifr_addr.sa_data, 0, | |
1072 | sizeof(ifr.ifr_addr.sa_data)); | |
1073 | } | |
1074 | ||
1075 | strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); | |
1076 | res = ioctl(skfd, ifra[i].s_ioctl, &ifr); | |
1077 | if (res < 0) { | |
1078 | int saved_errno = errno; | |
1079 | ||
1080 | v_print("Interface '%s': Error: SIOCS%s failed: %s\n", | |
1081 | slave_ifname, ifra[i].req_name, | |
1082 | strerror(saved_errno)); | |
1083 | ||
1da177e4 LT |
1084 | } |
1085 | ||
b3784a77 | 1086 | ipaddr = (unsigned char *)ifr.ifr_addr.sa_data; |
1da177e4 LT |
1087 | v_print("Interface '%s': set IP %s to %d.%d.%d.%d\n", |
1088 | slave_ifname, ifra[i].desc, | |
1089 | ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); | |
1090 | } | |
1091 | ||
1092 | return 0; | |
1093 | } | |
1094 | ||
1095 | /* | |
1096 | * Local variables: | |
1097 | * version-control: t | |
1098 | * kept-new-versions: 5 | |
1099 | * c-indent-level: 4 | |
1100 | * c-basic-offset: 4 | |
1101 | * tab-width: 4 | |
1102 | * compile-command: "gcc -Wall -Wstrict-prototypes -O -I/usr/src/linux/include ifenslave.c -o ifenslave" | |
1103 | * End: | |
1104 | */ | |
1105 |