diff options
39 files changed, 4015 insertions, 291 deletions
diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index d733a428eff6..5ede7473b425 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -194,6 +194,48 @@ or, for backwards compatibility, the option value. E.g., The parameters are as follows: +ad_select + + Specifies the 802.3ad aggregation selection logic to use. The + possible values and their effects are: + + stable or 0 + + The active aggregator is chosen by largest aggregate + bandwidth. + + Reselection of the active aggregator occurs only when all + slaves of the active aggregator are down or the active + aggregator has no slaves. + + This is the default value. + + bandwidth or 1 + + The active aggregator is chosen by largest aggregate + bandwidth. Reselection occurs if: + + - A slave is added to or removed from the bond + + - Any slave's link state changes + + - Any slave's 802.3ad association state changes + + - The bond's adminstrative state changes to up + + count or 2 + + The active aggregator is chosen by the largest number of + ports (slaves). Reselection occurs as described under the + "bandwidth" setting, above. + + The bandwidth and count selection policies permit failover of + 802.3ad aggregations when partial failure of the active aggregator + occurs. This keeps the aggregator with the highest availability + (either in bandwidth or in number of ports) active at all times. + + This option was added in bonding version 3.4.0. + arp_interval Specifies the ARP link monitoring frequency in milliseconds. @@ -551,6 +593,16 @@ num_grat_arp affects only the active-backup mode. This option was added for bonding version 3.3.0. +num_unsol_na + + Specifies the number of unsolicited IPv6 Neighbor Advertisements + to be issued after a failover event. One unsolicited NA is issued + immediately after the failover. + + The valid range is 0 - 255; the default value is 1. This option + affects only the active-backup mode. This option was added for + bonding version 3.4.0. + primary A string (eth0, eth2, etc) specifying which slave is the diff --git a/MAINTAINERS b/MAINTAINERS index ca96939b7969..425e50e6b4c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3853,6 +3853,12 @@ M: mhoffman@lightlink.com L: lm-sensors@lm-sensors.org S: Maintained +SMSC911x ETHERNET DRIVER +P: Steve Glendinning +M: steve.glendinning@smsc.com +L: netdev@vger.kernel.org +S: Supported + SMX UIO Interface P: Ben Nizette M: bn@niasdigital.com diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 1757bb3f6e08..732ea834d50d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -61,6 +61,7 @@ config DUMMY config BONDING tristate "Bonding driver support" depends on INET + depends on IPV6 || IPV6=n ---help--- Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet Channels together. This is called 'Etherchannel' by Cisco, @@ -978,6 +979,20 @@ config SMC911X called smc911x. If you want to compile it as a module, say M here and read <file:Documentation/kbuild/modules.txt> +config SMSC911X + tristate "SMSC LAN911x/LAN921x families embedded ethernet support" + depends on ARM || SUPERH + select CRC32 + select MII + select PHYLIB + ---help--- + Say Y here if you want support for SMSC LAN911x and LAN921x families + of ethernet controllers. + + To compile this driver as a module, choose M here and read + <file:Documentation/networking/net-modules.txt>. The module + will be called smsc911x. + config NET_VENDOR_RACAL bool "Racal-Interlan (Micom) NI cards" depends on ISA diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 657c47b1a6b6..e06829aa75b0 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -219,6 +219,7 @@ obj-$(CONFIG_S2IO) += s2io.o obj-$(CONFIG_MYRI10GE) += myri10ge/ obj-$(CONFIG_SMC91X) += smc91x.o obj-$(CONFIG_SMC911X) += smc911x.o +obj-$(CONFIG_SMSC911X) += smsc911x.o obj-$(CONFIG_BFIN_MAC) += bfin_mac.o obj-$(CONFIG_DM9000) += dm9000.o obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile index 5cdae2bc055a..6f9c6faef24c 100644 --- a/drivers/net/bonding/Makefile +++ b/drivers/net/bonding/Makefile @@ -6,3 +6,6 @@ obj-$(CONFIG_BONDING) += bonding.o bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o +ipv6-$(subst m,y,$(CONFIG_IPV6)) += bond_ipv6.o +bonding-objs += $(ipv6-y) + diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 6106660a4a44..ba1372f2f144 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -27,6 +27,7 @@ #include <linux/netdevice.h> #include <linux/spinlock.h> #include <linux/ethtool.h> +#include <linux/etherdevice.h> #include <linux/if_bonding.h> #include <linux/pkt_sched.h> #include <net/net_namespace.h> @@ -236,6 +237,17 @@ static inline struct aggregator *__get_next_agg(struct aggregator *aggregator) return &(SLAVE_AD_INFO(slave->next).aggregator); } +/* + * __agg_has_partner + * + * Return nonzero if aggregator has a partner (denoted by a non-zero ether + * address for the partner). Return 0 if not. + */ +static inline int __agg_has_partner(struct aggregator *agg) +{ + return !is_zero_ether_addr(agg->partner_system.mac_addr_value); +} + /** * __disable_port - disable the port's slave * @port: the port we're looking at @@ -274,14 +286,14 @@ static inline int __port_is_enabled(struct port *port) * __get_agg_selection_mode - get the aggregator selection mode * @port: the port we're looking at * - * Get the aggregator selection mode. Can be %BANDWIDTH or %COUNT. + * Get the aggregator selection mode. Can be %STABLE, %BANDWIDTH or %COUNT. */ static inline u32 __get_agg_selection_mode(struct port *port) { struct bonding *bond = __get_bond_by_port(port); if (bond == NULL) { - return AD_BANDWIDTH; + return BOND_AD_STABLE; } return BOND_AD_INFO(bond).agg_select_mode; @@ -1414,9 +1426,82 @@ static void ad_port_selection_logic(struct port *port) // else set ready=FALSE in all aggregator's ports __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); - if (!__check_agg_selection_timer(port) && (aggregator = __get_first_agg(port))) { - ad_agg_selection_logic(aggregator); + aggregator = __get_first_agg(port); + ad_agg_selection_logic(aggregator); +} + +/* + * Decide if "agg" is a better choice for the new active aggregator that + * the current best, according to the ad_select policy. + */ +static struct aggregator *ad_agg_selection_test(struct aggregator *best, + struct aggregator *curr) +{ + /* + * 0. If no best, select current. + * + * 1. If the current agg is not individual, and the best is + * individual, select current. + * + * 2. If current agg is individual and the best is not, keep best. + * + * 3. Therefore, current and best are both individual or both not + * individual, so: + * + * 3a. If current agg partner replied, and best agg partner did not, + * select current. + * + * 3b. If current agg partner did not reply and best agg partner + * did reply, keep best. + * + * 4. Therefore, current and best both have partner replies or + * both do not, so perform selection policy: + * + * BOND_AD_COUNT: Select by count of ports. If count is equal, + * select by bandwidth. + * + * BOND_AD_STABLE, BOND_AD_BANDWIDTH: Select by bandwidth. + */ + if (!best) + return curr; + + if (!curr->is_individual && best->is_individual) + return curr; + + if (curr->is_individual && !best->is_individual) + return best; + + if (__agg_has_partner(curr) && !__agg_has_partner(best)) + return curr; + + if (!__agg_has_partner(curr) && __agg_has_partner(best)) + return best; + + switch (__get_agg_selection_mode(curr->lag_ports)) { + case BOND_AD_COUNT: + if (curr->num_of_ports > best->num_of_ports) + return curr; + + if (curr->num_of_ports < best->num_of_ports) + return best; + + /*FALLTHROUGH*/ + case BOND_AD_STABLE: + case BOND_AD_BANDWIDTH: + if (__get_agg_bandwidth(curr) > __get_agg_bandwidth(best)) + return curr; + + break; + + default: + printk(KERN_WARNING DRV_NAME + ": %s: Impossible agg select mode %d\n", + curr->slave->dev->master->name, + __get_agg_selection_mode(curr->lag_ports)); + break; } + + return best; } /** @@ -1424,156 +1509,138 @@ static void ad_port_selection_logic(struct port *port) * @aggregator: the aggregator we're looking at * * It is assumed that only one aggregator may be selected for a team. - * The logic of this function is to select (at first time) the aggregator with - * the most ports attached to it, and to reselect the active aggregator only if - * the previous aggregator has no more ports related to it. + * + * The logic of this function is to select the aggregator according to + * the ad_select policy: + * + * BOND_AD_STABLE: select the aggregator with the most ports attached to + * it, and to reselect the active aggregator only if the previous + * aggregator has no more ports related to it. + * + * BOND_AD_BANDWIDTH: select the aggregator with the highest total + * bandwidth, and reselect whenever a link state change takes place or the + * set of slaves in the bond changes. + * + * BOND_AD_COUNT: select the aggregator with largest number of ports + * (slaves), and reselect whenever a link state change takes place or the + * set of slaves in the bond changes. * * FIXME: this function MUST be called with the first agg in the bond, or * __get_active_agg() won't work correctly. This function should be better * called with the bond itself, and retrieve the first agg from it. */ -static void ad_agg_selection_logic(struct aggregator *aggregator) +static void ad_agg_selection_logic(struct aggregator *agg) { - struct aggregator *best_aggregator = NULL, *active_aggregator = NULL; - struct aggregator *last_active_aggregator = NULL, *origin_aggregator; + struct aggregator *best, *active, *origin; struct port *port; - u16 num_of_aggs=0; - origin_aggregator = aggregator; + origin = agg; - //get current active aggregator - last_active_aggregator = __get_active_agg(aggregator); + active = __get_active_agg(agg); + best = active; - // search for the aggregator with the most ports attached to it. do { - // count how many candidate lag's we have - if (aggregator->lag_ports) { - num_of_aggs++; - } - if (aggregator->is_active && !aggregator->is_individual && // if current aggregator is the active aggregator - MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr))) { // and partner answers to 802.3ad PDUs - if (aggregator->num_of_ports) { // if any ports attached to the current aggregator - best_aggregator=NULL; // disregard the best aggregator that was chosen by now - break; // stop the selection of other aggregator if there are any ports attached to this active aggregator - } else { // no ports attached to this active aggregator - aggregator->is_active = 0; // mark this aggregator as not active anymore + agg->is_active = 0; + + if (agg->num_of_ports) + best = ad_agg_selection_test(best, agg); + + } while ((agg = __get_next_agg(agg))); + + if (best && + __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) { + /* + * For the STABLE policy, don't replace the old active + * aggregator if it's still active (it has an answering + * partner) or if both the best and active don't have an + * answering partner. + */ + if (active && active->lag_ports && + active->lag_ports->is_enabled && + (__agg_has_partner(active) || + (!__agg_has_partner(active) && !__agg_has_partner(best)))) { + if (!(!active->actor_oper_aggregator_key && + best->actor_oper_aggregator_key)) { + best = NULL; + active->is_active = 1; } } - if (aggregator->num_of_ports) { // if any ports attached - if (best_aggregator) { // if there is a candidte aggregator - //The reasons for choosing new best aggregator: - // 1. if current agg is NOT individual and the best agg chosen so far is individual OR - // current and best aggs are both individual or both not individual, AND - // 2a. current agg partner reply but best agg partner do not reply OR - // 2b. current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply AND - // current has more ports/bandwidth, or same amount of ports but current has faster ports, THEN - // current agg become best agg so far - - //if current agg is NOT individual and the best agg chosen so far is individual change best_aggregator - if (!aggregator->is_individual && best_aggregator->is_individual) { - best_aggregator=aggregator; - } - // current and best aggs are both individual or both not individual - else if ((aggregator->is_individual && best_aggregator->is_individual) || - (!aggregator->is_individual && !best_aggregator->is_individual)) { - // current and best aggs are both individual or both not individual AND - // current agg partner reply but best agg partner do not reply - if ((MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) && - !MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) { - best_aggregator=aggregator; - } - // current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply - else if (! (!MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) && - MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) { - if ((__get_agg_selection_mode(aggregator->lag_ports) == AD_BANDWIDTH)&& - (__get_agg_bandwidth(aggregator) > __get_agg_bandwidth(best_aggregator))) { - best_aggregator=aggregator; - } else if (__get_agg_selection_mode(aggregator->lag_ports) == AD_COUNT) { - if (((aggregator->num_of_ports > best_aggregator->num_of_ports) && - (aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS))|| - ((aggregator->num_of_ports == best_aggregator->num_of_ports) && - ((u16)(aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS) > - (u16)(best_aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS)))) { - best_aggregator=aggregator; - } - } - } - } - } else { - best_aggregator=aggregator; - } - } - aggregator->is_active = 0; // mark all aggregators as not active anymore - } while ((aggregator = __get_next_agg(aggregator))); - - // if we have new aggregator selected, don't replace the old aggregator if it has an answering partner, - // or if both old aggregator and new aggregator don't have answering partner - if (best_aggregator) { - if (last_active_aggregator && last_active_aggregator->lag_ports && last_active_aggregator->lag_ports->is_enabled && - (MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) || // partner answers OR - (!MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) && // both old and new - !MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) // partner do not answer - ) { - // if new aggregator has link, and old aggregator does not, replace old aggregator.(do nothing) - // -> don't replace otherwise. - if (!(!last_active_aggregator->actor_oper_aggregator_key && best_aggregator->actor_oper_aggregator_key)) { - best_aggregator=NULL; - last_active_aggregator->is_active = 1; // don't replace good old aggregator + } - } - } + if (best && (best == active)) { + best = NULL; + active->is_active = 1; } // if there is new best aggregator, activate it - if (best_aggregator) { - for (aggregator = __get_first_agg(best_aggregator->lag_ports); - aggregator; - aggregator = __get_next_agg(aggregator)) { - - dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n", - aggregator->aggregator_identifier, aggregator->num_of_ports, - aggregator->actor_oper_aggregator_key, aggregator->partner_oper_aggregator_key, - aggregator->is_individual, aggregator->is_active); + if (best) { + dprintk("best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", + best->aggregator_identifier, best->num_of_ports, + best->actor_oper_aggregator_key, + best->partner_oper_aggregator_key, + best->is_individual, best->is_active); + dprintk("best ports %p slave %p %s\n", + best->lag_ports, best->slave, + best->slave ? best->slave->dev->name : "NULL"); + + for (agg = __get_first_agg(best->lag_ports); agg; + agg = __get_next_agg(agg)) { + + dprintk("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", + agg->aggregator_identifier, agg->num_of_ports, + agg->actor_oper_aggregator_key, + agg->partner_oper_aggregator_key, + agg->is_individual, agg->is_active); } // check if any partner replys - if (best_aggregator->is_individual) { - printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad response from " - "the link partner for any adapters in the bond\n", - best_aggregator->slave->dev->master->name); - } - - // check if there are more than one aggregator - if (num_of_aggs > 1) { - dprintk("Warning: More than one Link Aggregation Group was " - "found in the bond. Only one group will function in the bond\n"); + if (best->is_individual) { + printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad" + " response from the link partner for any" + " adapters in the bond\n", + best->slave->dev->master->name); } - best_aggregator->is_active = 1; - dprintk("LAG %d choosed as the active LAG\n", best_aggregator->aggregator_identifier); - dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n", - best_aggregator->aggregator_identifier, best_aggregator->num_of_ports, - best_aggregator->actor_oper_aggregator_key, best_aggregator->partner_oper_aggregator_key, - best_aggregator->is_individual, best_aggregator->is_active); + best->is_active = 1; + dprintk("LAG %d chosen as the active LAG\n", + best->aggregator_identifier); + dprintk("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", + best->aggregator_identifier, best->num_of_ports, + best->actor_oper_aggregator_key, + best->partner_oper_aggregator_key, + best->is_individual, best->is_active); // disable the ports that were related to the former active_aggregator - if (last_active_aggregator) { - for (port=last_active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) { + if (active) { + for (port = active->lag_ports; port; + port = port->next_port_in_aggregator) { __disable_port(port); } } } - // if the selected aggregator is of join individuals(partner_system is NULL), enable their ports - active_aggregator = __get_active_agg(origin_aggregator); + /* + * if the selected aggregator is of join individuals + * (partner_system is NULL), enable their ports + */ + active = __get_active_agg(origin); - if (active_aggregator) { - if (!MAC_ADDRESS_COMPARE(&(active_aggregator->partner_system), &(null_mac_addr))) { - for (port=active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) { + if (active) { + if (!__agg_has_partner(active)) { + for (port = active->lag_ports; port; + port = port->next_port_in_aggregator) { __enable_port(port); } } } + + if (origin->slave) { + struct bonding *bond; + + bond = bond_get_bond_by_slave(origin->slave); + if (bond) + bond_3ad_set_carrier(bond); + } } /** @@ -1830,6 +1897,19 @@ static void ad_initialize_lacpdu(struct lacpdu *lacpdu) // Check aggregators status in team every T seconds #define AD_AGGREGATOR_SELECTION_TIMER 8 +/* + * bond_3ad_initiate_agg_selection(struct bonding *bond) + * + * Set the aggregation selection timer, to initiate an agg selection in + * the very near future. Called during first initialization, and during + * any down to up transitions of the bond. + */ +void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout) +{ + BOND_AD_INFO(bond).agg_select_timer = timeout; + BOND_AD_INFO(bond).agg_select_mode = bond->params.ad_select; +} + static u16 aggregator_identifier; /** @@ -1854,9 +1934,9 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas // initialize how many times this module is called in one second(should be about every 100ms) ad_ticks_per_sec = tick_resolution; - // initialize the aggregator selection timer(to activate an aggregation selection after initialize) - BOND_AD_INFO(bond).agg_select_timer = (AD_AGGREGATOR_SELECTION_TIMER * ad_ticks_per_sec); - BOND_AD_INFO(bond).agg_select_mode = AD_BANDWIDTH; + bond_3ad_initiate_agg_selection(bond, + AD_AGGREGATOR_SELECTION_TIMER * + ad_ticks_per_sec); } } diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h index b5ee45f6d55a..a803fe05f63e 100644 --- a/drivers/net/bonding/bond_3ad.h +++ b/drivers/net/bonding/bond_3ad.h @@ -42,10 +42,11 @@ typedef struct mac_addr { u8 mac_addr_value[ETH_ALEN]; } mac_addr_t; -typedef enum { - AD_BANDWIDTH = 0, - AD_COUNT -} agg_selection_t; +enum { + BOND_AD_STABLE = 0, + BOND_AD_BANDWIDTH = 1, + BOND_AD_COUNT = 2, +}; // rx machine states(43.4.11 in the 802.3ad standard) typedef enum { @@ -277,6 +278,7 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas int bond_3ad_bind_slave(struct slave *slave); void bond_3ad_unbind_slave(struct slave *slave); void bond_3ad_state_machine_handler(struct work_struct *); +void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout); void bond_3ad_adapter_speed_changed(struct slave *slave); void bond_3ad_adapter_duplex_changed(struct slave *slave); void bond_3ad_handle_link_change(struct slave *slave, char link); diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 87437c788476..e170fa2fa1a1 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -346,14 +346,18 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp) static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct packet_type *ptype, struct net_device *orig_dev) { - struct bonding *bond = bond_dev->priv; + struct bonding *bond; struct arp_pkt *arp = (struct arp_pkt *)skb->data; int res = NET_RX_DROP; if (dev_net(bond_dev) != &init_net) goto out; - if (!(bond_dev->flags & IFF_MASTER)) + while (bond_dev->priv_flags & IFF_802_1Q_VLAN) + bond_dev = vlan_dev_real_dev(bond_dev); + + if (!(bond_dev->priv_flags & IFF_BONDING) || + !(bond_dev->flags & IFF_MASTER)) goto out; if (!arp) { @@ -368,6 +372,9 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct if (arp->op_code == htons(ARPOP_REPLY)) { /* update rx hash table for this ARP */ + printk("rar: update orig %s bond_dev %s\n", orig_dev->name, + bond_dev->name); + bond = bond_dev->priv; rlb_update_entry_from_arp(bond, arp); dprintk("Server received an ARP Reply from client\n"); } @@ -818,7 +825,7 @@ static int rlb_initialize(struct bonding *bond) /*initialize packet type*/ pk_type->type = __constant_htons(ETH_P_ARP); - pk_type->dev = bond->dev; + pk_type->dev = NULL; pk_type->func = rlb_arp_recv; /* register to receive ARPs */ diff --git a/drivers/net/bonding/bond_ipv6.c b/drivers/net/bonding/bond_ipv6.c new file mode 100644 index 000000000000..7c78b7bf671c --- /dev/null +++ b/drivers/net/bonding/bond_ipv6.c @@ -0,0 +1,218 @@ +/* + * Copyright(c) 2008 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + */ + +//#define BONDING_DEBUG 1 + +#include <linux/types.h> +#include <linux/if_vlan.h> +#include <net/ipv6.h> +#include <net/ndisc.h> +#include <net/addrconf.h> +#include "bonding.h" + +/* + * Assign bond->master_ipv6 to the next IPv6 address in the list, or + * zero it out if there are none. + */ +static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr) +{ + struct inet6_dev *idev; + struct inet6_ifaddr *ifa; + + if (!dev) + return; + + idev = in6_dev_get(dev); + if (!idev) + return; + + read_lock_bh(&idev->lock); + ifa = idev->addr_list; + if (ifa) + ipv6_addr_copy(addr, &ifa->addr); + else + ipv6_addr_set(addr, 0, 0, 0, 0); + + read_unlock_bh(&idev->lock); + + in6_dev_put(idev); +} + +static void bond_na_send(struct net_device *slave_dev, + struct in6_addr *daddr, + int router, + unsigned short vlan_id) +{ + struct in6_addr mcaddr; + struct icmp6hdr icmp6h = { + .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, + }; + struct sk_buff *skb; + + icmp6h.icmp6_router = router; + icmp6h.icmp6_solicited = 0; + icmp6h.icmp6_override = 1; + + addrconf_addr_solict_mult(daddr, &mcaddr); + + dprintk("ipv6 na on slave %s: dest %pI6, src %pI6\n", + slave->name, &mcaddr, daddr); + + skb = ndisc_build_skb(slave_dev, &mcaddr, daddr, &icmp6h, daddr, + ND_OPT_TARGET_LL_ADDR); + + if (!skb) { + printk(KERN_ERR DRV_NAME ": NA packet allocation failed\n"); + return; + } + + if (vlan_id) { + skb = vlan_put_tag(skb, vlan_id); + if (!skb) { + printk(KERN_ERR DRV_NAME ": failed to insert VLAN tag\n"); + return; + } + } + + ndisc_send_skb(skb, slave_dev, NULL, &mcaddr, daddr, &icmp6h); +} + +/* + * Kick out an unsolicited Neighbor Advertisement for an IPv6 address on + * the bonding master. This will help the switch learn our address + * if in active-backup mode. + * + * Caller must hold curr_slave_lock for read or better + */ +void bond_send_unsolicited_na(struct bonding *bond) +{ + struct slave *slave = bond->curr_active_slave; + struct vlan_entry *vlan; + struct inet6_dev *idev; |