X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Fifenslave.c;h=1cb765e230d83756ac85531e864f45b9d0e8a865;hb=c6725b0af68238a456690146adec763c04f66c82;hp=b11951758415fbee1e2072a762af06698dacecaf;hpb=2570b2e5759e8ac32b0922d71056bd426caae9f1;p=oweals%2Fbusybox.git diff --git a/networking/ifenslave.c b/networking/ifenslave.c index b11951758..1cb765e23 100644 --- a/networking/ifenslave.c +++ b/networking/ifenslave.c @@ -97,18 +97,59 @@ * - Code cleanup and style changes * set version to 1.1.0 */ +//config:config IFENSLAVE +//config: bool "ifenslave" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Userspace application to bind several interfaces +//config: to a logical interface (use with kernel bonding driver). + +//applet:IF_IFENSLAVE(APPLET(ifenslave, BB_DIR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o + +//usage:#define ifenslave_trivial_usage +//usage: "[-cdf] MASTER_IFACE SLAVE_IFACE..." +//usage:#define ifenslave_full_usage "\n\n" +//usage: "Configure network interfaces for parallel routing\n" +//usage: "\n -c,--change-active Change active slave" +//usage: "\n -d,--detach Remove slave interface from bonding device" +//usage: "\n -f,--force Force, even if interface is not Ethernet" +/* //usage: "\n -r,--receive-slave Create a receive-only slave" */ +//usage: +//usage:#define ifenslave_example_usage +//usage: "To create a bond device, simply follow these three steps:\n" +//usage: "- ensure that the required drivers are properly loaded:\n" +//usage: " # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" +//usage: "- assign an IP address to the bond device:\n" +//usage: " # ifconfig bond0 netmask broadcast \n" +//usage: "- attach all the interfaces you need to the bond device:\n" +//usage: " # ifenslave bond0 eth0 eth1 eth2\n" +//usage: " If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" +//usage: " interfaces attached AFTER this assignment will get the same MAC addr.\n\n" +//usage: " To detach a dead interface without setting the bond device down:\n" +//usage: " # ifenslave -d bond0 eth1\n\n" +//usage: " To set the bond device down and automatically release all the slaves:\n" +//usage: " # ifconfig bond0 down\n\n" +//usage: " To change active slave:\n" +//usage: " # ifenslave -c bond0 eth0\n" #include "libbb.h" -#include +/* #include - no. linux/if_bonding.h pulls in linux/if.h */ +#include +//#include - not needed? #include #include - -typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */ -typedef uint32_t u32; /* ditto */ -typedef uint16_t u16; /* ditto */ -typedef uint8_t u8; /* ditto */ +#include "fix_u32.h" /* hack, so we may include kernel's ethtool.h */ #include +#ifndef BOND_ABI_VERSION +# define BOND_ABI_VERSION 2 +#endif +#ifndef IFNAMSIZ +# define IFNAMSIZ 16 +#endif struct dev_data { @@ -116,7 +157,7 @@ struct dev_data { }; -enum { skfd = 3 }; /* AF_INET socket for ioctl() calls.*/ +enum { skfd = 3 }; /* AF_INET socket for ioctl() calls. */ struct globals { unsigned abi_ver; /* userland - kernel ABI version */ smallint hwaddr_set; /* Master's hwaddr is set */ @@ -133,193 +174,122 @@ struct globals { } while (0) -static void get_drv_info(char *master_ifname); -static int get_if_settings(char *ifname, struct dev_data *dd); -static int get_slave_flags(char *slave_ifname); -static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr); -static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr); -static int set_slave_mtu(char *slave_ifname, int mtu); -static int set_if_flags(char *ifname, short flags); -static int set_if_up(char *ifname, short flags); -static int set_if_down(char *ifname, short flags); -static int clear_if_addr(char *ifname); -static int set_if_addr(char *master_ifname, char *slave_ifname); -static void change_active(char *master_ifname, char *slave_ifname); -static int enslave(char *master_ifname, char *slave_ifname); -static int release(char *master_ifname, char *slave_ifname); +/* NOINLINEs are placed where it results in smaller code (gcc 4.3.1) */ - -int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int ifenslave_main(int argc ATTRIBUTE_UNUSED, char **argv) +static int ioctl_on_skfd(unsigned request, struct ifreq *ifr) { - char *master_ifname, *slave_ifname; - int rv; - int res; - unsigned opt; - enum { - OPT_c = (1 << 0), - OPT_d = (1 << 1), - OPT_f = (1 << 2), - }; -#if ENABLE_GETOPT_LONG - static const char ifenslave_longopts[] ALIGN1 = - "change-active" No_argument "c" - "detach" No_argument "d" - "force" No_argument "f" - ; - - applet_long_options = ifenslave_longopts; -#endif - opt = getopt32(argv, "cdf"); - argv += optind; - if (opt & (opt-1)) /* options check */ - bb_show_usage(); - - /* Copy the interface name */ - master_ifname = *argv++; + return ioctl(skfd, request, ifr); +} - /* No remaining args means show all interfaces. */ - if (!master_ifname) { - display_interfaces(NULL); - return EXIT_SUCCESS; - } +static int set_ifrname_and_do_ioctl(unsigned request, struct ifreq *ifr, const char *ifname) +{ + strncpy_IFNAMSIZ(ifr->ifr_name, ifname); + return ioctl_on_skfd(request, ifr); +} - /* Open a basic socket */ - xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), skfd); +static int get_if_settings(char *ifname, struct dev_data *dd) +{ + int res; - /* exchange abi version with bonding module */ - get_drv_info(master_ifname); + res = set_ifrname_and_do_ioctl(SIOCGIFMTU, &dd->mtu, ifname); + res |= set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &dd->flags, ifname); + res |= set_ifrname_and_do_ioctl(SIOCGIFHWADDR, &dd->hwaddr, ifname); - slave_ifname = *argv++; - if (!slave_ifname) { - if (opt & (OPT_d|OPT_c)) { - display_interfaces(slave_ifname); - return 2; /* why? */ - } - /* A single arg means show the - * configuration for this interface - */ - display_interfaces(master_ifname); - return EXIT_SUCCESS; - } + return res; +} - res = get_if_settings(master_ifname, &master); - if (res) { - /* Probably a good reason not to go on */ - bb_perror_msg_and_die("%s: can't get settings", master_ifname); - } +static int get_slave_flags(char *slave_ifname) +{ + return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname); +} - /* check if master is indeed a master; - * if not then fail any operation - */ - if (!(master.flags.ifr_flags & IFF_MASTER)) - bb_error_msg_and_die("%s is not a master", master_ifname); +static int set_hwaddr(char *ifname, struct sockaddr *hwaddr) +{ + struct ifreq ifr; - /* check if master is up; if not then fail any operation */ - if (!(master.flags.ifr_flags & IFF_UP)) - bb_error_msg_and_die("%s is not up", master_ifname); + memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr)); + return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname); +} - /* Only for enslaving */ - if (!(opt & (OPT_c|OPT_d))) { - sa_family_t master_family = master.hwaddr.ifr_hwaddr.sa_family; +static int set_mtu(char *ifname, int mtu) +{ + struct ifreq ifr; - /* The family '1' is ARPHRD_ETHER for ethernet. */ - if (master_family != 1 && !(opt & OPT_f)) { - bb_error_msg_and_die( - "%s is not ethernet-like (-f overrides)", - master_ifname); - } - } + ifr.ifr_mtu = mtu; + return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname); +} - /* Accepts only one slave */ - if (opt & OPT_c) { - /* change active slave */ - res = get_slave_flags(slave_ifname); - if (res) { - bb_perror_msg_and_die( - "%s: can't get flags", slave_ifname); - } - change_active(master_ifname, slave_ifname); - return EXIT_SUCCESS; - } +static int set_if_flags(char *ifname, int flags) +{ + struct ifreq ifr; - /* Accept multiple slaves */ - res = 0; - do { - if (opt & OPT_d) { - /* detach a slave interface from the master */ - rv = get_slave_flags(slave_ifname); - if (rv) { - /* Can't work with this slave. */ - /* remember the error and skip it*/ - bb_perror_msg( - "skipping %s: can't get flags", - slave_ifname); - res = rv; - continue; - } - rv = release(master_ifname, slave_ifname); - if (rv) { - bb_perror_msg( - "master %s, slave %s: " - "can't release", - master_ifname, slave_ifname); - res = rv; - } - } else { - /* attach a slave interface to the master */ - rv = get_if_settings(slave_ifname, &slave); - if (rv) { - /* Can't work with this slave. */ - /* remember the error and skip it*/ - bb_perror_msg( - "skipping %s: can't get settings", - slave_ifname); - res = rv; - continue; - } - rv = enslave(master_ifname, slave_ifname); - if (rv) { - bb_perror_msg( - "master %s, slave %s: " - "can't enslave", - master_ifname, slave_ifname); - res = rv; - } - } - } while ((slave_ifname = *argv++) != NULL); + ifr.ifr_flags = flags; + return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname); +} - if (ENABLE_FEATURE_CLEAN_UP) { - close(skfd); - } +static int set_if_up(char *ifname, int flags) +{ + int res = set_if_flags(ifname, flags | IFF_UP); + if (res) + bb_perror_msg("%s: can't up", ifname); + return res; +} +static int set_if_down(char *ifname, int flags) +{ + int res = set_if_flags(ifname, flags & ~IFF_UP); + if (res) + bb_perror_msg("%s: can't down", ifname); return res; } -static void get_drv_info(char *master_ifname) +static int clear_if_addr(char *ifname) { struct ifreq ifr; - struct ethtool_drvinfo info; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - ifr.ifr_data = (caddr_t)&info; + ifr.ifr_addr.sa_family = AF_INET; + memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); + return set_ifrname_and_do_ioctl(SIOCSIFADDR, &ifr, ifname); +} - info.cmd = ETHTOOL_GDRVINFO; - strncpy(info.driver, "ifenslave", 32); - snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION); +static int set_if_addr(char *master_ifname, char *slave_ifname) +{ +#if (SIOCGIFADDR | SIOCSIFADDR \ + | SIOCGIFDSTADDR | SIOCSIFDSTADDR \ + | SIOCGIFBRDADDR | SIOCSIFBRDADDR \ + | SIOCGIFNETMASK | SIOCSIFNETMASK) <= 0xffff +#define INT uint16_t +#else +#define INT int +#endif + static const struct { + INT g_ioctl; + INT s_ioctl; + } ifra[] = { + { SIOCGIFADDR, SIOCSIFADDR }, + { SIOCGIFDSTADDR, SIOCSIFDSTADDR }, + { SIOCGIFBRDADDR, SIOCSIFBRDADDR }, + { SIOCGIFNETMASK, SIOCSIFNETMASK }, + }; - if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) { - if (errno == EOPNOTSUPP) - return; - bb_perror_msg_and_die("%s: SIOCETHTOOL error", master_ifname); - } + struct ifreq ifr; + int res; + unsigned i; - abi_ver = bb_strtou(info.fw_version, NULL, 0); - if (errno) - bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname); + for (i = 0; i < ARRAY_SIZE(ifra); i++) { + res = set_ifrname_and_do_ioctl(ifra[i].g_ioctl, &ifr, master_ifname); + if (res < 0) { + ifr.ifr_addr.sa_family = AF_INET; + memset(ifr.ifr_addr.sa_data, 0, + sizeof(ifr.ifr_addr.sa_data)); + } - return; + res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname); + if (res < 0) + return res; + } + + return 0; } static void change_active(char *master_ifname, char *slave_ifname) @@ -327,15 +297,12 @@ static void change_active(char *master_ifname, char *slave_ifname) struct ifreq ifr; if (!(slave.flags.ifr_flags & IFF_SLAVE)) { - bb_error_msg_and_die( - "%s is not a slave", - slave_ifname); + bb_error_msg_and_die("%s is not a slave", slave_ifname); } - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); - if (ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0 - && ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0 + strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); + if (set_ifrname_and_do_ioctl(SIOCBONDCHANGEACTIVE, &ifr, master_ifname) + && ioctl_on_skfd(BOND_CHANGE_ACTIVE_OLD, &ifr) ) { bb_perror_msg_and_die( "master %s, slave %s: can't " @@ -344,7 +311,7 @@ static void change_active(char *master_ifname, char *slave_ifname) } } -static int enslave(char *master_ifname, char *slave_ifname) +static NOINLINE int enslave(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res; @@ -378,7 +345,7 @@ static int enslave(char *master_ifname, char *slave_ifname) } if (master.mtu.ifr_mtu != slave.mtu.ifr_mtu) { - res = set_slave_mtu(slave_ifname, master.mtu.ifr_mtu); + res = set_mtu(slave_ifname, master.mtu.ifr_mtu); if (res) { bb_perror_msg("%s: can't set MTU", slave_ifname); return res; @@ -394,9 +361,7 @@ static int enslave(char *master_ifname, char *slave_ifname) * the application sets the slave's * hwaddr */ - res = set_slave_hwaddr(slave_ifname, - &(master.hwaddr.ifr_hwaddr)); - if (res) { + if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) { bb_perror_msg("%s: can't set hw address", slave_ifname); goto undo_mtu; @@ -405,8 +370,7 @@ static int enslave(char *master_ifname, char *slave_ifname) /* For old ABI the application needs to bring the * slave back up */ - res = set_if_up(slave_ifname, slave.flags.ifr_flags); - if (res) + if (set_if_up(slave_ifname, slave.flags.ifr_flags)) goto undo_slave_mac; } /* The driver is using a new ABI, @@ -422,14 +386,11 @@ static int enslave(char *master_ifname, char *slave_ifname) /* For old ABI, the master needs to be * down before setting it's hwaddr */ - res = set_if_down(master_ifname, master.flags.ifr_flags); - if (res) + if (set_if_down(master_ifname, master.flags.ifr_flags)) goto undo_mtu; } - res = set_master_hwaddr(master_ifname, - &(slave.hwaddr.ifr_hwaddr)); - if (res) { + if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) { bb_error_msg("%s: can't set hw address", master_ifname); goto undo_mtu; @@ -439,8 +400,7 @@ static int enslave(char *master_ifname, char *slave_ifname) /* For old ABI, bring the master * back up */ - res = set_if_up(master_ifname, master.flags.ifr_flags); - if (res) + if (set_if_up(master_ifname, master.flags.ifr_flags)) goto undo_master_mac; } @@ -448,29 +408,26 @@ static int enslave(char *master_ifname, char *slave_ifname) } /* Do the real thing */ - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); - if (ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0 - && ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0 + strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); + if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname) + && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr) ) { - res = 1; - } - - if (res) goto undo_master_mac; + } return 0; /* rollback (best effort) */ undo_master_mac: - set_master_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr)); + set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr)); hwaddr_set = 0; goto undo_mtu; + undo_slave_mac: - set_slave_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr)); + set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr)); undo_mtu: - set_slave_mtu(slave_ifname, slave.mtu.ifr_mtu); - return res; + set_mtu(slave_ifname, slave.mtu.ifr_mtu); + return 1; } static int release(char *master_ifname, char *slave_ifname) @@ -479,15 +436,13 @@ static int release(char *master_ifname, char *slave_ifname) int res = 0; if (!(slave.flags.ifr_flags & IFF_SLAVE)) { - bb_error_msg("%s is not a slave", - slave_ifname); + bb_error_msg("%s is not a slave", slave_ifname); return 1; } - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); - if (ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0 - && ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0 + strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); + if (set_ifrname_and_do_ioctl(SIOCBONDRELEASE, &ifr, master_ifname) < 0 + && ioctl_on_skfd(BOND_RELEASE_OLD, &ifr) < 0 ) { return 1; } @@ -499,123 +454,175 @@ static int release(char *master_ifname, char *slave_ifname) } /* set to default mtu */ - set_slave_mtu(slave_ifname, 1500); + set_mtu(slave_ifname, 1500); return res; } -static int get_if_settings(char *ifname, struct dev_data *dd) +static NOINLINE void get_drv_info(char *master_ifname) { - int res; - - strncpy(dd->mtu.ifr_name, ifname, IFNAMSIZ); - res = ioctl(skfd, SIOCGIFMTU, &dd->mtu); - strncpy(dd->flags.ifr_name, ifname, IFNAMSIZ); - res |= ioctl(skfd, SIOCGIFFLAGS, &dd->flags); - strncpy(dd->hwaddr.ifr_name, ifname, IFNAMSIZ); - res |= ioctl(skfd, SIOCGIFHWADDR, &dd->hwaddr); + struct ifreq ifr; + struct ethtool_drvinfo info; - return res; -} + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_data = (caddr_t)&info; + info.cmd = ETHTOOL_GDRVINFO; + /* both fields are 32 bytes long (long enough) */ + strcpy(info.driver, "ifenslave"); + strcpy(info.fw_version, utoa(BOND_ABI_VERSION)); + if (set_ifrname_and_do_ioctl(SIOCETHTOOL, &ifr, master_ifname) < 0) { + if (errno == EOPNOTSUPP) + return; + bb_perror_msg_and_die("%s: SIOCETHTOOL error", master_ifname); + } -static int get_slave_flags(char *slave_ifname) -{ - strncpy(slave.flags.ifr_name, slave_ifname, IFNAMSIZ); - return ioctl(skfd, SIOCGIFFLAGS, &slave.flags); + abi_ver = bb_strtou(info.fw_version, NULL, 0); + if (errno) + bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname); } -static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr) +int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ifenslave_main(int argc UNUSED_PARAM, char **argv) { - struct ifreq ifr; - - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); - return ioctl(skfd, SIOCSIFHWADDR, &ifr); -} + char *master_ifname, *slave_ifname; + int rv; + int res; + unsigned opt; + enum { + OPT_c = (1 << 0), + OPT_d = (1 << 1), + OPT_f = (1 << 2), + }; +#if ENABLE_LONG_OPTS + static const char ifenslave_longopts[] ALIGN1 = + "change-active\0" No_argument "c" + "detach\0" No_argument "d" + "force\0" No_argument "f" + /* "all-interfaces\0" No_argument "a" */ + ; -static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr) -{ - struct ifreq ifr; + applet_long_options = ifenslave_longopts; +#endif + INIT_G(); - strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); - memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); - return ioctl(skfd, SIOCSIFHWADDR, &ifr); -} + opt = getopt32(argv, "cdfa"); + argv += optind; + if (opt & (opt-1)) /* Only one option can be given */ + bb_show_usage(); -static int set_slave_mtu(char *slave_ifname, int mtu) -{ - struct ifreq ifr; + master_ifname = *argv++; - ifr.ifr_mtu = mtu; - strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); - return ioctl(skfd, SIOCSIFMTU, &ifr); -} + /* No interface names - show all interfaces. */ + if (!master_ifname) { + display_interfaces(NULL); + return EXIT_SUCCESS; + } -static int set_if_flags(char *ifname, short flags) -{ - struct ifreq ifr; + /* Open a basic socket */ + xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), skfd); - ifr.ifr_flags = flags; - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - return ioctl(skfd, SIOCSIFFLAGS, &ifr); -} + /* Exchange abi version with bonding module */ + get_drv_info(master_ifname); -static int set_if_up(char *ifname, short flags) -{ - int res = set_if_flags(ifname, flags | IFF_UP); - if (res) - bb_perror_msg("%s: can't up", ifname); - return res; -} + slave_ifname = *argv++; + if (!slave_ifname) { + if (opt & (OPT_d|OPT_c)) { + /* --change or --detach, and no slaves given - + * show all interfaces. */ + display_interfaces(slave_ifname /* == NULL */); + return 2; /* why 2? */ + } + /* A single arg means show the + * configuration for this interface + */ + display_interfaces(master_ifname); + return EXIT_SUCCESS; + } -static int set_if_down(char *ifname, short flags) -{ - int res = set_if_flags(ifname, flags & ~IFF_UP); - if (res) - bb_perror_msg("%s: can't down", ifname); - return res; -} + if (get_if_settings(master_ifname, &master)) { + /* Probably a good reason not to go on */ + bb_perror_msg_and_die("%s: can't get settings", master_ifname); + } -static int clear_if_addr(char *ifname) -{ - struct ifreq ifr; + /* Check if master is indeed a master; + * if not then fail any operation + */ + if (!(master.flags.ifr_flags & IFF_MASTER)) + bb_error_msg_and_die("%s is not a master", master_ifname); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_addr.sa_family = AF_INET; - memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); - return ioctl(skfd, SIOCSIFADDR, &ifr); -} + /* Check if master is up; if not then fail any operation */ + if (!(master.flags.ifr_flags & IFF_UP)) + bb_error_msg_and_die("%s is not up", master_ifname); -static int set_if_addr(char *master_ifname, char *slave_ifname) -{ - static const struct { - int g_ioctl; - int s_ioctl; - } ifra[] = { - { SIOCGIFADDR, SIOCSIFADDR }, - { SIOCGIFDSTADDR, SIOCSIFDSTADDR }, - { SIOCGIFBRDADDR, SIOCSIFBRDADDR }, - { SIOCGIFNETMASK, SIOCSIFNETMASK }, - }; +#ifdef WHY_BOTHER + /* Neither -c[hange] nor -d[etach] -> it's "enslave" then; + * and -f[orce] is not there too. Check that it's ethernet. */ + if (!(opt & (OPT_d|OPT_c|OPT_f))) { + /* The family '1' is ARPHRD_ETHER for ethernet. */ + if (master.hwaddr.ifr_hwaddr.sa_family != 1) { + bb_error_msg_and_die( + "%s is not ethernet-like (-f overrides)", + master_ifname); + } + } +#endif - struct ifreq ifr; - int res; - int i; + /* Accepts only one slave */ + if (opt & OPT_c) { + /* Change active slave */ + if (get_slave_flags(slave_ifname)) { + bb_perror_msg_and_die( + "%s: can't get flags", slave_ifname); + } + change_active(master_ifname, slave_ifname); + return EXIT_SUCCESS; + } - for (i = 0; i < ARRAY_SIZE(ifra); i++) { - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - res = ioctl(skfd, ifra[i].g_ioctl, &ifr); - if (res < 0) { - ifr.ifr_addr.sa_family = AF_INET; - memset(ifr.ifr_addr.sa_data, 0, - sizeof(ifr.ifr_addr.sa_data)); + /* Accepts multiple slaves */ + res = 0; + do { + if (opt & OPT_d) { + /* Detach a slave interface from the master */ + rv = get_slave_flags(slave_ifname); + if (rv) { + /* Can't work with this slave, */ + /* remember the error and skip it */ + bb_perror_msg( + "skipping %s: can't get %s", + slave_ifname, "flags"); + res = rv; + continue; + } + rv = release(master_ifname, slave_ifname); + if (rv) { + bb_perror_msg("can't release %s from %s", + slave_ifname, master_ifname); + res = rv; + } + } else { + /* Attach a slave interface to the master */ + rv = get_if_settings(slave_ifname, &slave); + if (rv) { + /* Can't work with this slave, */ + /* remember the error and skip it */ + bb_perror_msg( + "skipping %s: can't get %s", + slave_ifname, "settings"); + res = rv; + continue; + } + rv = enslave(master_ifname, slave_ifname); + if (rv) { + bb_perror_msg("can't enslave %s to %s", + slave_ifname, master_ifname); + res = rv; + } } + } while ((slave_ifname = *argv++) != NULL); - strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); - res = ioctl(skfd, ifra[i].s_ioctl, &ifr); - if (res < 0) - return res; + if (ENABLE_FEATURE_CLEAN_UP) { + close(skfd); } - return 0; + return res; }