Skip to content

Commit 305d552

Browse files
Brian HaleyJeff Garzik
authored andcommitted
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices, especially when in active-backup mode and there are only IPv6 addresses configured, as reported by Alex Sidorenko. - Creates a new file, net/drivers/bonding/bond_ipv6.c, for the IPv6-specific routines. Both regular bonds and VLANs over bonds are supported. - Adds a new tunable, num_unsol_na, to limit the number of unsolicited IPv6 Neighbor Advertisements that are sent on a failover event. Default is 1. - Creates two new IPv6 neighbor discovery functions: ndisc_build_skb() ndisc_send_skb() These were required to support VLANs since we have to be able to add the VLAN id to the skb since ndisc_send_na() and friends shouldn't be asked to do this. These two routines are basically __ndisc_send() split into two pieces, in a slightly different order. - Updates Documentation/networking/bonding.txt and bumps the rev of bond support to 3.4.0. On failover, this new code will generate one packet: - An unsolicited IPv6 Neighbor Advertisement, which helps the switch learn that the address has moved to the new slave. Testing has shown that sending just the NA results in pretty good behavior when in active-back mode, I saw no lost ping packets for example. Signed-off-by: Brian Haley <[email protected]> Signed-off-by: Jay Vosburgh <[email protected]> Signed-off-by: Jeff Garzik <[email protected]>
1 parent 61c9eaf commit 305d552

File tree

9 files changed

+416
-31
lines changed

9 files changed

+416
-31
lines changed

Documentation/networking/bonding.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,16 @@ num_grat_arp
551551
affects only the active-backup mode. This option was added for
552552
bonding version 3.3.0.
553553

554+
num_unsol_na
555+
556+
Specifies the number of unsolicited IPv6 Neighbor Advertisements
557+
to be issued after a failover event. One unsolicited NA is issued
558+
immediately after the failover.
559+
560+
The valid range is 0 - 255; the default value is 1. This option
561+
affects only the active-backup mode. This option was added for
562+
bonding version 3.4.0.
563+
554564
primary
555565

556566
A string (eth0, eth2, etc) specifying which slave is the

drivers/net/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ config DUMMY
6161
config BONDING
6262
tristate "Bonding driver support"
6363
depends on INET
64+
depends on IPV6 || IPV6=n
6465
---help---
6566
Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
6667
Channels together. This is called 'Etherchannel' by Cisco,

drivers/net/bonding/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ obj-$(CONFIG_BONDING) += bonding.o
66

77
bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o
88

9+
ipv6-$(subst m,y,$(CONFIG_IPV6)) += bond_ipv6.o
10+
bonding-objs += $(ipv6-y)
11+

drivers/net/bonding/bond_ipv6.c

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* Copyright(c) 2008 Hewlett-Packard Development Company, L.P.
3+
*
4+
* This program is free software; you can redistribute it and/or modify it
5+
* under the terms of the GNU General Public License as published by the
6+
* Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but
10+
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along
15+
* with this program; if not, write to the Free Software Foundation, Inc.,
16+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*
18+
* The full GNU General Public License is included in this distribution in the
19+
* file called LICENSE.
20+
*
21+
*/
22+
23+
//#define BONDING_DEBUG 1
24+
25+
#include <linux/types.h>
26+
#include <linux/if_vlan.h>
27+
#include <net/ipv6.h>
28+
#include <net/ndisc.h>
29+
#include <net/addrconf.h>
30+
#include "bonding.h"
31+
32+
/*
33+
* Assign bond->master_ipv6 to the next IPv6 address in the list, or
34+
* zero it out if there are none.
35+
*/
36+
static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr)
37+
{
38+
struct inet6_dev *idev;
39+
struct inet6_ifaddr *ifa;
40+
41+
if (!dev)
42+
return;
43+
44+
idev = in6_dev_get(dev);
45+
if (!idev)
46+
return;
47+
48+
read_lock_bh(&idev->lock);
49+
ifa = idev->addr_list;
50+
if (ifa)
51+
ipv6_addr_copy(addr, &ifa->addr);
52+
else
53+
ipv6_addr_set(addr, 0, 0, 0, 0);
54+
55+
read_unlock_bh(&idev->lock);
56+
57+
in6_dev_put(idev);
58+
}
59+
60+
static void bond_na_send(struct net_device *slave_dev,
61+
struct in6_addr *daddr,
62+
int router,
63+
unsigned short vlan_id)
64+
{
65+
struct in6_addr mcaddr;
66+
struct icmp6hdr icmp6h = {
67+
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
68+
};
69+
struct sk_buff *skb;
70+
71+
icmp6h.icmp6_router = router;
72+
icmp6h.icmp6_solicited = 0;
73+
icmp6h.icmp6_override = 1;
74+
75+
addrconf_addr_solict_mult(daddr, &mcaddr);
76+
77+
dprintk("ipv6 na on slave %s: dest %pI6, src %pI6\n",
78+
slave->name, &mcaddr, daddr);
79+
80+
skb = ndisc_build_skb(slave_dev, &mcaddr, daddr, &icmp6h, daddr,
81+
ND_OPT_TARGET_LL_ADDR);
82+
83+
if (!skb) {
84+
printk(KERN_ERR DRV_NAME ": NA packet allocation failed\n");
85+
return;
86+
}
87+
88+
if (vlan_id) {
89+
skb = vlan_put_tag(skb, vlan_id);
90+
if (!skb) {
91+
printk(KERN_ERR DRV_NAME ": failed to insert VLAN tag\n");
92+
return;
93+
}
94+
}
95+
96+
ndisc_send_skb(skb, slave_dev, NULL, &mcaddr, daddr, &icmp6h);
97+
}
98+
99+
/*
100+
* Kick out an unsolicited Neighbor Advertisement for an IPv6 address on
101+
* the bonding master. This will help the switch learn our address
102+
* if in active-backup mode.
103+
*
104+
* Caller must hold curr_slave_lock for read or better
105+
*/
106+
void bond_send_unsolicited_na(struct bonding *bond)
107+
{
108+
struct slave *slave = bond->curr_active_slave;
109+
struct vlan_entry *vlan;
110+
struct inet6_dev *idev;
111+
int is_router;
112+
113+
dprintk("bond_send_unsol_na: bond %s slave %s\n", bond->dev->name,
114+
slave ? slave->dev->name : "NULL");
115+
116+
if (!slave || !bond->send_unsol_na ||
117+
test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
118+
return;
119+
120+
bond->send_unsol_na--;
121+
122+
idev = in6_dev_get(bond->dev);
123+
if (!idev)
124+
return;
125+
126+
is_router = !!idev->cnf.forwarding;
127+
128+
in6_dev_put(idev);
129+
130+
if (!ipv6_addr_any(&bond->master_ipv6))
131+
bond_na_send(slave->dev, &bond->master_ipv6, is_router, 0);
132+
133+
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
134+
if (!ipv6_addr_any(&vlan->vlan_ipv6)) {
135+
bond_na_send(slave->dev, &vlan->vlan_ipv6, is_router,
136+
vlan->vlan_id);
137+
}
138+
}
139+
}
140+
141+
/*
142+
* bond_inet6addr_event: handle inet6addr notifier chain events.
143+
*
144+
* We keep track of device IPv6 addresses primarily to use as source
145+
* addresses in NS probes.
146+
*
147+
* We track one IPv6 for the main device (if it has one).
148+
*/
149+
static int bond_inet6addr_event(struct notifier_block *this,
150+
unsigned long event,
151+
void *ptr)
152+
{
153+
struct inet6_ifaddr *ifa = ptr;
154+
struct net_device *vlan_dev, *event_dev = ifa->idev->dev;
155+
struct bonding *bond;
156+
struct vlan_entry *vlan;
157+
158+
if (dev_net(event_dev) != &init_net)
159+
return NOTIFY_DONE;
160+
161+
list_for_each_entry(bond, &bond_dev_list, bond_list) {
162+
if (bond->dev == event_dev) {
163+
switch (event) {
164+
case NETDEV_UP:
165+
if (ipv6_addr_any(&bond->master_ipv6))
166+
ipv6_addr_copy(&bond->master_ipv6,
167+
&ifa->addr);
168+
return NOTIFY_OK;
169+
case NETDEV_DOWN:
170+
if (ipv6_addr_equal(&bond->master_ipv6,
171+
&ifa->addr))
172+
bond_glean_dev_ipv6(bond->dev,
173+
&bond->master_ipv6);
174+
return NOTIFY_OK;
175+
default:
176+
return NOTIFY_DONE;
177+
}
178+
}
179+
180+
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
181+
vlan_dev = vlan_group_get_device(bond->vlgrp,
182+
vlan->vlan_id);
183+
if (vlan_dev == event_dev) {
184+
switch (event) {
185+
case NETDEV_UP:
186+
if (ipv6_addr_any(&vlan->vlan_ipv6))
187+
ipv6_addr_copy(&vlan->vlan_ipv6,
188+
&ifa->addr);
189+
return NOTIFY_OK;
190+
case NETDEV_DOWN:
191+
if (ipv6_addr_equal(&vlan->vlan_ipv6,
192+
&ifa->addr))
193+
bond_glean_dev_ipv6(vlan_dev,
194+
&vlan->vlan_ipv6);
195+
return NOTIFY_OK;
196+
default:
197+
return NOTIFY_DONE;
198+
}
199+
}
200+
}
201+
}
202+
return NOTIFY_DONE;
203+
}
204+
205+
static struct notifier_block bond_inet6addr_notifier = {
206+
.notifier_call = bond_inet6addr_event,
207+
};
208+
209+
void bond_register_ipv6_notifier(void)
210+
{
211+
register_inet6addr_notifier(&bond_inet6addr_notifier);
212+
}
213+
214+
void bond_unregister_ipv6_notifier(void)
215+
{
216+
unregister_inet6addr_notifier(&bond_inet6addr_notifier);
217+
}
218+

drivers/net/bonding/bond_main.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989

9090
static int max_bonds = BOND_DEFAULT_MAX_BONDS;
9191
static int num_grat_arp = 1;
92+
static int num_unsol_na = 1;
9293
static int miimon = BOND_LINK_MON_INTERV;
9394
static int updelay = 0;
9495
static int downdelay = 0;
@@ -107,6 +108,8 @@ module_param(max_bonds, int, 0);
107108
MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
108109
module_param(num_grat_arp, int, 0644);
109110
MODULE_PARM_DESC(num_grat_arp, "Number of gratuitous ARP packets to send on failover event");
111+
module_param(num_unsol_na, int, 0644);
112+
MODULE_PARM_DESC(num_unsol_na, "Number of unsolicited IPv6 Neighbor Advertisements packets to send on failover event");
110113
module_param(miimon, int, 0);
111114
MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
112115
module_param(updelay, int, 0);
@@ -242,14 +245,13 @@ static int bond_add_vlan(struct bonding *bond, unsigned short vlan_id)
242245
dprintk("bond: %s, vlan id %d\n",
243246
(bond ? bond->dev->name: "None"), vlan_id);
244247

245-
vlan = kmalloc(sizeof(struct vlan_entry), GFP_KERNEL);
248+
vlan = kzalloc(sizeof(struct vlan_entry), GFP_KERNEL);
246249
if (!vlan) {
247250
return -ENOMEM;
248251
}
249252

250253
INIT_LIST_HEAD(&vlan->vlan_list);
251254
vlan->vlan_id = vlan_id;
252-
vlan->vlan_ip = 0;
253255

254256
write_lock_bh(&bond->lock);
255257

@@ -1208,6 +1210,9 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
12081210
bond->send_grat_arp = bond->params.num_grat_arp;
12091211
bond_send_gratuitous_arp(bond);
12101212

1213+
bond->send_unsol_na = bond->params.num_unsol_na;
1214+
bond_send_unsolicited_na(bond);
1215+
12111216
write_unlock_bh(&bond->curr_slave_lock);
12121217
read_unlock(&bond->lock);
12131218

@@ -2463,6 +2468,12 @@ void bond_mii_monitor(struct work_struct *work)
24632468
read_unlock(&bond->curr_slave_lock);
24642469
}
24652470

2471+
if (bond->send_unsol_na) {
2472+
read_lock(&bond->curr_slave_lock);
2473+
bond_send_unsolicited_na(bond);
2474+
read_unlock(&bond->curr_slave_lock);
2475+
}
2476+
24662477
if (bond_miimon_inspect(bond)) {
24672478
read_unlock(&bond->lock);
24682479
rtnl_lock();
@@ -3158,6 +3169,12 @@ void bond_activebackup_arp_mon(struct work_struct *work)
31583169
read_unlock(&bond->curr_slave_lock);
31593170
}
31603171

3172+
if (bond->send_unsol_na) {
3173+
read_lock(&bond->curr_slave_lock);
3174+
bond_send_unsolicited_na(bond);
3175+
read_unlock(&bond->curr_slave_lock);
3176+
}
3177+
31613178
if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
31623179
read_unlock(&bond->lock);
31633180
rtnl_lock();
@@ -3827,6 +3844,7 @@ static int bond_close(struct net_device *bond_dev)
38273844
write_lock_bh(&bond->lock);
38283845

38293846
bond->send_grat_arp = 0;
3847+
bond->send_unsol_na = 0;
38303848

38313849
/* signal timers not to re-arm */
38323850
bond->kill_timers = 1;
@@ -4542,6 +4560,7 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
45424560
bond->primary_slave = NULL;
45434561
bond->dev = bond_dev;
45444562
bond->send_grat_arp = 0;
4563+
bond->send_unsol_na = 0;
45454564
bond->setup_by_slave = 0;
45464565
INIT_LIST_HEAD(&bond->vlan_list);
45474566

@@ -4791,6 +4810,13 @@ static int bond_check_params(struct bond_params *params)
47914810
num_grat_arp = 1;
47924811
}
47934812

4813+
if (num_unsol_na < 0 || num_unsol_na > 255) {
4814+
printk(KERN_WARNING DRV_NAME
4815+
": Warning: num_unsol_na (%d) not in range 0-255 so it "
4816+
"was reset to 1 \n", num_unsol_na);
4817+
num_unsol_na = 1;
4818+
}
4819+
47944820
/* reset values for 802.3ad */
47954821
if (bond_mode == BOND_MODE_8023AD) {
47964822
if (!miimon) {
@@ -4992,6 +5018,7 @@ static int bond_check_params(struct bond_params *params)
49925018
params->xmit_policy = xmit_hashtype;
49935019
params->miimon = miimon;
49945020
params->num_grat_arp = num_grat_arp;
5021+
params->num_unsol_na = num_unsol_na;
49955022
params->arp_interval = arp_interval;
49965023
params->arp_validate = arp_validate_value;
49975024
params->updelay = updelay;
@@ -5144,6 +5171,7 @@ static int __init bonding_init(void)
51445171

51455172
register_netdevice_notifier(&bond_netdev_notifier);
51465173
register_inetaddr_notifier(&bond_inetaddr_notifier);
5174+
bond_register_ipv6_notifier();
51475175

51485176
goto out;
51495177
err:
@@ -5166,6 +5194,7 @@ static void __exit bonding_exit(void)
51665194
{
51675195
unregister_netdevice_notifier(&bond_netdev_notifier);
51685196
unregister_inetaddr_notifier(&bond_inetaddr_notifier);
5197+
bond_unregister_ipv6_notifier();
51695198

51705199
bond_destroy_sysfs();
51715200

0 commit comments

Comments
 (0)