| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Alexey Dobriyan | 7f927fc | 2006-03-28 01:56:53 -0800 | [diff] [blame] | 90 | *	   simplify things, it is now handled separately. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | */ | 
| Neil Horman | e6d184e | 2005-11-22 14:56:32 -0800 | [diff] [blame] | 696 | set_if_addr(master_ifname, slave_ifname); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Francis Galiegue | a33f322 | 2010-04-23 00:08:02 +0200 | [diff] [blame] | 759 | * down before setting its hwaddr | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1082 | } | 
|  | 1083 |  | 
| Randy Dunlap | b3784a7 | 2008-08-12 15:09:08 -0700 | [diff] [blame] | 1084 | ipaddr = (unsigned char *)ifr.ifr_addr.sa_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  |