diff --git a/networking/ifenslave.c b/networking/ifenslave.c index 071cac722..44839868a 100644 --- a/networking/ifenslave.c +++ b/networking/ifenslave.c @@ -133,19 +133,7 @@ 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_hwaddr(char *ifname, struct sockaddr *hwaddr); -static int set_mtu(char *ifname, int mtu); -static int set_if_flags(char *ifname, int flags); -static int set_if_up(char *ifname, int flags); -static int set_if_down(char *ifname, int 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) */ static void strncpy_IFNAMSIZ(char *dst, const char *src) { @@ -163,6 +151,302 @@ static int set_ifrname_and_do_ioctl(unsigned request, struct ifreq *ifr, const c return ioctl_on_skfd(request, ifr); } +static int get_if_settings(char *ifname, struct dev_data *dd) +{ + int res; + + 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); + + return res; +} + +static int get_slave_flags(char *slave_ifname) +{ + return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname); +} + +static int set_hwaddr(char *ifname, struct sockaddr *hwaddr) +{ + struct ifreq ifr; + + memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr)); + return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname); +} + +static int set_mtu(char *ifname, int mtu) +{ + struct ifreq ifr; + + ifr.ifr_mtu = mtu; + return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname); +} + +static int set_if_flags(char *ifname, int flags) +{ + struct ifreq ifr; + + ifr.ifr_flags = flags; + return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname); +} + +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 int clear_if_addr(char *ifname) +{ + struct ifreq ifr; + + 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); +} + +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 }, + }; + + struct ifreq ifr; + int res; + unsigned i; + + 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)); + } + + 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) +{ + struct ifreq ifr; + + if (!(slave.flags.ifr_flags & IFF_SLAVE)) { + bb_error_msg_and_die( + "%s is not a slave", + slave_ifname); + } + + 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 " + "change active", + master_ifname, slave_ifname); + } +} + +static NOINLINE int enslave(char *master_ifname, char *slave_ifname) +{ + struct ifreq ifr; + int res; + + if (slave.flags.ifr_flags & IFF_SLAVE) { + bb_error_msg( + "%s is already a slave", + slave_ifname); + return 1; + } + + res = set_if_down(slave_ifname, slave.flags.ifr_flags); + if (res) + return res; + + if (abi_ver < 2) { + /* Older bonding versions would panic if the slave has no IP + * address, so get the IP setting from the master. + */ + res = set_if_addr(master_ifname, slave_ifname); + if (res) { + bb_perror_msg("%s: can't set address", slave_ifname); + return res; + } + } else { + res = clear_if_addr(slave_ifname); + if (res) { + bb_perror_msg("%s: can't clear address", slave_ifname); + return res; + } + } + + if (master.mtu.ifr_mtu != slave.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; + } + } + + if (hwaddr_set) { + /* Master already has an hwaddr + * so set it's hwaddr to the slave + */ + if (abi_ver < 1) { + /* The driver is using an old ABI, so + * the application sets the slave's + * hwaddr + */ + if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) { + bb_perror_msg("%s: can't set hw address", + slave_ifname); + goto undo_mtu; + } + + /* For old ABI the application needs to bring the + * slave back up + */ + if (set_if_up(slave_ifname, slave.flags.ifr_flags)) + goto undo_slave_mac; + } + /* The driver is using a new ABI, + * so the driver takes care of setting + * the slave's hwaddr and bringing + * it up again + */ + } else { + /* No hwaddr for master yet, so + * set the slave's hwaddr to it + */ + if (abi_ver < 1) { + /* For old ABI, the master needs to be + * down before setting it's hwaddr + */ + if (set_if_down(master_ifname, master.flags.ifr_flags)) + goto undo_mtu; + } + + if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) { + bb_error_msg("%s: can't set hw address", + master_ifname); + goto undo_mtu; + } + + if (abi_ver < 1) { + /* For old ABI, bring the master + * back up + */ + if (set_if_up(master_ifname, master.flags.ifr_flags)) + goto undo_master_mac; + } + + hwaddr_set = 1; + } + + /* Do the real thing */ + strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); + if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname) + && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr) + ) { + goto undo_master_mac; + } + + return 0; + +/* rollback (best effort) */ + undo_master_mac: + set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr)); + hwaddr_set = 0; + goto undo_mtu; + + undo_slave_mac: + set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr)); + undo_mtu: + set_mtu(slave_ifname, slave.mtu.ifr_mtu); + return 1; +} + +static int release(char *master_ifname, char *slave_ifname) +{ + struct ifreq ifr; + int res = 0; + + if (!(slave.flags.ifr_flags & IFF_SLAVE)) { + bb_error_msg("%s is not a slave", + slave_ifname); + return 1; + } + + 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; + } + if (abi_ver < 1) { + /* The driver is using an old ABI, so we'll set the interface + * down to avoid any conflicts due to same MAC/IP + */ + res = set_if_down(slave_ifname, slave.flags.ifr_flags); + } + + /* set to default mtu */ + set_mtu(slave_ifname, 1500); + + return res; +} + +static NOINLINE void get_drv_info(char *master_ifname) +{ + struct ifreq ifr; + struct ethtool_drvinfo info; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_data = (caddr_t)&info; + info.cmd = ETHTOOL_GDRVINFO; + strncpy(info.driver, "ifenslave", 32); + snprintf(info.fw_version, 32, "%d", 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); + } + + abi_ver = bb_strtou(info.fw_version, NULL, 0); + if (errno) + bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname); +} + int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ifenslave_main(int argc ATTRIBUTE_UNUSED, char **argv) { @@ -310,299 +594,3 @@ int ifenslave_main(int argc ATTRIBUTE_UNUSED, char **argv) return res; } - -static void get_drv_info(char *master_ifname) -{ - struct ifreq ifr; - struct ethtool_drvinfo info; - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_data = (caddr_t)&info; - info.cmd = ETHTOOL_GDRVINFO; - strncpy(info.driver, "ifenslave", 32); - snprintf(info.fw_version, 32, "%d", 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); - } - - abi_ver = bb_strtou(info.fw_version, NULL, 0); - if (errno) - bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname); -} - -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); - } - - 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 " - "change active", - master_ifname, slave_ifname); - } -} - -static int enslave(char *master_ifname, char *slave_ifname) -{ - struct ifreq ifr; - int res; - - if (slave.flags.ifr_flags & IFF_SLAVE) { - bb_error_msg( - "%s is already a slave", - slave_ifname); - return 1; - } - - res = set_if_down(slave_ifname, slave.flags.ifr_flags); - if (res) - return res; - - if (abi_ver < 2) { - /* Older bonding versions would panic if the slave has no IP - * address, so get the IP setting from the master. - */ - res = set_if_addr(master_ifname, slave_ifname); - if (res) { - bb_perror_msg("%s: can't set address", slave_ifname); - return res; - } - } else { - res = clear_if_addr(slave_ifname); - if (res) { - bb_perror_msg("%s: can't clear address", slave_ifname); - return res; - } - } - - if (master.mtu.ifr_mtu != slave.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; - } - } - - if (hwaddr_set) { - /* Master already has an hwaddr - * so set it's hwaddr to the slave - */ - if (abi_ver < 1) { - /* The driver is using an old ABI, so - * the application sets the slave's - * hwaddr - */ - if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) { - bb_perror_msg("%s: can't set hw address", - slave_ifname); - goto undo_mtu; - } - - /* For old ABI the application needs to bring the - * slave back up - */ - if (set_if_up(slave_ifname, slave.flags.ifr_flags)) - goto undo_slave_mac; - } - /* The driver is using a new ABI, - * so the driver takes care of setting - * the slave's hwaddr and bringing - * it up again - */ - } else { - /* No hwaddr for master yet, so - * set the slave's hwaddr to it - */ - if (abi_ver < 1) { - /* For old ABI, the master needs to be - * down before setting it's hwaddr - */ - if (set_if_down(master_ifname, master.flags.ifr_flags)) - goto undo_mtu; - } - - if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) { - bb_error_msg("%s: can't set hw address", - master_ifname); - goto undo_mtu; - } - - if (abi_ver < 1) { - /* For old ABI, bring the master - * back up - */ - if (set_if_up(master_ifname, master.flags.ifr_flags)) - goto undo_master_mac; - } - - hwaddr_set = 1; - } - - /* Do the real thing */ - strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname); - if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname) - && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr) - ) { - goto undo_master_mac; - } - - return 0; - -/* rollback (best effort) */ - undo_master_mac: - set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr)); - hwaddr_set = 0; - goto undo_mtu; - - undo_slave_mac: - set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr)); - undo_mtu: - set_mtu(slave_ifname, slave.mtu.ifr_mtu); - return 1; -} - -static int release(char *master_ifname, char *slave_ifname) -{ - struct ifreq ifr; - int res = 0; - - if (!(slave.flags.ifr_flags & IFF_SLAVE)) { - bb_error_msg("%s is not a slave", - slave_ifname); - return 1; - } - - 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; - } - if (abi_ver < 1) { - /* The driver is using an old ABI, so we'll set the interface - * down to avoid any conflicts due to same MAC/IP - */ - res = set_if_down(slave_ifname, slave.flags.ifr_flags); - } - - /* set to default mtu */ - set_mtu(slave_ifname, 1500); - - return res; -} - -static int get_if_settings(char *ifname, struct dev_data *dd) -{ - int res; - - 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); - - return res; -} - -static int get_slave_flags(char *slave_ifname) -{ - return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname); -} - -static int set_hwaddr(char *ifname, struct sockaddr *hwaddr) -{ - struct ifreq ifr; - - memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr)); - return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname); -} - -static int set_mtu(char *ifname, int mtu) -{ - struct ifreq ifr; - - ifr.ifr_mtu = mtu; - return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname); -} - -static int set_if_flags(char *ifname, int flags) -{ - struct ifreq ifr; - - ifr.ifr_flags = flags; - return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname); -} - -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 int clear_if_addr(char *ifname) -{ - struct ifreq ifr; - - 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); -} - -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 }, - }; - - struct ifreq ifr; - int res; - unsigned i; - - 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)); - } - - res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname); - if (res < 0) - return res; - } - - return 0; -}