Skip to content

Commit

Permalink
bonding: Improve IGMP join processing
Browse files Browse the repository at this point in the history
	In active-backup mode, the current bonding code duplicates IGMP
traffic to all slaves, so that switches are up to date in case of a
failover from an active to a backup interface.  If bonding then fails
back to the original active interface, it is likely that the "active
slave" switch's IGMP forwarding for the port will be out of date until
some event occurs to refresh the switch (e.g., a membership query).

	This patch alters the behavior of bonding to no longer flood
IGMP to all ports, and to issue IGMP JOINs to the newly active port at
the time of a failover.  This insures that switches are kept up to date
for all cases.

	"GOELLESCH Niels" <niels.goellesch@eurocontrol.int> originally
reported this problem, and included a patch.  His original patch was
modified by Jay Vosburgh to additionally remove the existing IGMP flood
behavior, use RCU, streamline code paths, fix trailing white space, and
adjust for style.

Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
  • Loading branch information
Jay Vosburgh authored and Jeff Garzik committed Mar 6, 2007
1 parent e245cb7 commit a816c7c
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 51 deletions.
76 changes: 25 additions & 51 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
Expand Down Expand Up @@ -861,6 +862,28 @@ static void bond_mc_delete(struct bonding *bond, void *addr, int alen)
}
}


/*
* Retrieve the list of registered multicast addresses for the bonding
* device and retransmit an IGMP JOIN request to the current active
* slave.
*/
static void bond_resend_igmp_join_requests(struct bonding *bond)
{
struct in_device *in_dev;
struct ip_mc_list *im;

rcu_read_lock();
in_dev = __in_dev_get_rcu(bond->dev);
if (in_dev) {
for (im = in_dev->mc_list; im; im = im->next) {
ip_mc_rejoin_group(im);
}
}

rcu_read_unlock();
}

/*
* Totally destroys the mc_list in bond
*/
Expand All @@ -874,6 +897,7 @@ static void bond_mc_list_destroy(struct bonding *bond)
kfree(dmi);
dmi = bond->mc_list;
}
bond->mc_list = NULL;
}

/*
Expand Down Expand Up @@ -967,6 +991,7 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, struct
for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next) {
dev_mc_add(new_active->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
}
bond_resend_igmp_join_requests(bond);
}
}

Expand Down Expand Up @@ -4017,42 +4042,6 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
return 0;
}

static void bond_activebackup_xmit_copy(struct sk_buff *skb,
struct bonding *bond,
struct slave *slave)
{
struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
struct ethhdr *eth_data;
u8 *hwaddr;
int res;

if (!skb2) {
printk(KERN_ERR DRV_NAME ": Error: "
"bond_activebackup_xmit_copy(): skb_copy() failed\n");
return;
}

skb2->mac.raw = (unsigned char *)skb2->data;
eth_data = eth_hdr(skb2);

/* Pick an appropriate source MAC address
* -- use slave's perm MAC addr, unless used by bond
* -- otherwise, borrow active slave's perm MAC addr
* since that will not be used
*/
hwaddr = slave->perm_hwaddr;
if (!memcmp(eth_data->h_source, hwaddr, ETH_ALEN))
hwaddr = bond->curr_active_slave->perm_hwaddr;

/* Set source MAC address appropriately */
memcpy(eth_data->h_source, hwaddr, ETH_ALEN);

res = bond_dev_queue_xmit(bond, skb2, slave->dev);
if (res)
dev_kfree_skb(skb2);

return;
}

/*
* in active-backup mode, we know that bond->curr_active_slave is always valid if
Expand All @@ -4073,21 +4062,6 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
if (!bond->curr_active_slave)
goto out;

/* Xmit IGMP frames on all slaves to ensure rapid fail-over
for multicast traffic on snooping switches */
if (skb->protocol == __constant_htons(ETH_P_IP) &&
skb->nh.iph->protocol == IPPROTO_IGMP) {
struct slave *slave, *active_slave;
int i;

active_slave = bond->curr_active_slave;
bond_for_each_slave_from_to(bond, slave, i, active_slave->next,
active_slave->prev)
if (IS_UP(slave->dev) &&
(slave->link == BOND_LINK_UP))
bond_activebackup_xmit_copy(skb, bond, slave);
}

res = bond_dev_queue_xmit(bond, skb, bond->curr_active_slave->dev);

out:
Expand Down
2 changes: 2 additions & 0 deletions include/linux/igmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,7 @@ extern void ip_mc_up(struct in_device *);
extern void ip_mc_down(struct in_device *);
extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr);
extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr);
extern void ip_mc_rejoin_group(struct ip_mc_list *im);

#endif
#endif
23 changes: 23 additions & 0 deletions net/ipv4/igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,28 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
return;
}

/*
* Resend IGMP JOIN report; used for bonding.
*/
void ip_mc_rejoin_group(struct ip_mc_list *im)
{
struct in_device *in_dev = im->interface;

#ifdef CONFIG_IP_MULTICAST
if (im->multiaddr == IGMP_ALL_HOSTS)
return;

if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) {
igmp_mod_timer(im, IGMP_Initial_Report_Delay);
return;
}
/* else, v3 */
im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
IGMP_Unsolicited_Report_Count;
igmp_ifc_event(in_dev);
#endif
}

/*
* A socket has left a multicast group on device dev
*/
Expand Down Expand Up @@ -2596,3 +2618,4 @@ int __init igmp_mc_proc_init(void)
EXPORT_SYMBOL(ip_mc_dec_group);
EXPORT_SYMBOL(ip_mc_inc_group);
EXPORT_SYMBOL(ip_mc_join_group);
EXPORT_SYMBOL(ip_mc_rejoin_group);

0 comments on commit a816c7c

Please sign in to comment.