From: t_jeang Date: Tue, 6 Jan 2009 12:06:05 +0000 (+0000) Subject: imported patch bonding-balance-slb-fixes.patch X-Git-Tag: git-3201e656ce56ed02e9501906c18ffe16ae350a52-bonding-use-after-free X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=f4e1a199b13b84568b490595fe753f77db381e61;p=xenclient%2Fkernel.git imported patch bonding-balance-slb-fixes.patch --- diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index c6ecb4c7..4dcfa273 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -107,6 +107,7 @@ struct arp_pkt { /* Forward declaration */ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]); +static void slb_send_learning_packets(struct bonding *bond); static inline u8 _simple_hash(u8 *hash_start, int hash_size) { @@ -300,6 +301,21 @@ static struct slave *tlb_choose_channel(struct bonding *bond, u32 hash_index, u3 /*********************** slb specific functions ***************************/ +static void slb_send_learning_packets(struct bonding *bond) +{ + int i; + + br_send_gratuitous_switch_learning_packet(bond->dev); + + if (bond->vlgrp) { + for(i=0; ivlgrp->vlan_devices[i]) { + br_send_gratuitous_switch_learning_packet(bond->vlgrp->vlan_devices[i]); + } + } + } +} + void bond_info_show_slb(struct seq_file *seq) { struct bonding *bond = seq->private; @@ -994,7 +1010,8 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct /* fasten the change in the switch */ if (SLAVE_IS_OK(slave1)) { - alb_send_learning_packets(slave1, slave1->dev->dev_addr); + if (!bond->alb_info.slb_enabled) + alb_send_learning_packets(slave1, slave1->dev->dev_addr); if (bond->alb_info.rlb_enabled) { /* inform the clients that the mac address * has changed @@ -1006,7 +1023,8 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct } if (SLAVE_IS_OK(slave2)) { - alb_send_learning_packets(slave2, slave2->dev->dev_addr); + if (!bond->alb_info.slb_enabled) + alb_send_learning_packets(slave2, slave2->dev->dev_addr); if (bond->alb_info.rlb_enabled) { /* inform the clients that the mac address * has changed @@ -1017,6 +1035,9 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct disabled_slave = slave2; } + if (bond->alb_info.slb_enabled) + slb_send_learning_packets(bond); + if (bond->alb_info.rlb_enabled && slaves_state_differ) { /* A disabled slave was assigned an active mac addr */ rlb_teach_disabled_mac_on_primary(bond, @@ -1378,16 +1399,8 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) /* unbalanced or unassigned, send through primary */ tx_slave = bond->curr_active_slave; bond_info->unbalanced_load += skb->len; - printk(KERN_ERR "No slave for %02x:%02x:%02x:%02x:%02x:%02x.\n", - eth_data->h_source[0], eth_data->h_source[1], - eth_data->h_source[2], eth_data->h_source[3], - eth_data->h_source[4], eth_data->h_source[5]); - if (tx_slave) { - printk(KERN_ERR "Sending via primary %s (hash_index %x, length %x)\n", - tx_slave->dev->name, hash_index, hash_size); - } else { + if (!tx_slave) printk(KERN_ERR "No primary interface found\n"); - } } if (tx_slave && SLAVE_IS_OK(tx_slave)) { @@ -1437,7 +1450,7 @@ void bond_alb_monitor(struct bonding *bond) bond_info->lp_counter++; /* send learning packets */ - if (bond_info->lp_counter >= BOND_ALB_LP_TICKS) { + if (!bond->alb_info.slb_enabled && bond_info->lp_counter >= BOND_ALB_LP_TICKS) { /* change of curr_active_slave involves swapping of mac addresses. * in order to avoid this swapping from happening while * sending the learning packets, the curr_slave_lock must be held for @@ -1467,14 +1480,11 @@ void bond_alb_monitor(struct bonding *bond) BOND_TLB_REBALANCE_INTERVAL; bond_info->unbalanced_load = 0; } - /* - * No need for ARP in the SLB case since the - * RX path remains valid, although we may - * shortly be choosing a different TX path - * which will cause RX to change too. - */ } + if (bond->alb_info.slb_enabled) + slb_send_learning_packets(bond); + read_unlock(&bond->curr_slave_lock); bond_info->tx_rebalance_counter = 0; @@ -1600,7 +1610,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char * gone away. Send a gratuitous packet which * will cause the switch to update its tables. */ - br_send_gratuitous_switch_learning_packet(bond->dev); + slb_send_learning_packets(bond); } } else if (link == BOND_LINK_UP) { /* order a rebalance ASAP */ @@ -1672,7 +1682,10 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr, bond->alb_info.rlb_enabled); /* fasten bond mac on new current slave */ - alb_send_learning_packets(new_slave, bond->dev->dev_addr); + if (bond->alb_info.slb_enabled) + slb_send_learning_packets(bond); + else + alb_send_learning_packets(new_slave, bond->dev->dev_addr); } } @@ -1718,7 +1731,10 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr, bond->alb_info.rlb_enabled); - alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr); + if (bond->alb_info.slb_enabled) + slb_send_learning_packets(bond); + else + alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr); if (bond->alb_info.rlb_enabled) { /* inform clients mac address has changed */ rlb_req_update_slave_clients(bond, bond->curr_active_slave); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index d7c4a1f7..7f0bde1a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -794,7 +794,8 @@ static struct dev_mc_list *bond_mc_list_find_dmi(struct dev_mc_list *dmi, struct */ static void bond_set_promiscuity(struct bonding *bond, int inc) { - if (USES_PRIMARY(bond->params.mode)) { + + if (USES_PRIMARY(bond->params.mode) && bond->params.mode != BOND_MODE_SLB) { /* write lock already acquired */ if (bond->curr_active_slave) { dev_set_promiscuity(bond->curr_active_slave->dev, inc); @@ -1452,6 +1453,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) { dev_mc_add (slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); } + } else if (bond->params.mode == BOND_MODE_SLB) { + /* set promiscuity level to new slave */ + if (bond_dev->flags & IFF_PROMISC) { + dev_set_promiscuity(slave_dev, 1); + } } if (bond->params.mode == BOND_MODE_8023AD) { @@ -4242,9 +4248,9 @@ void bond_set_mode_ops(struct bonding *bond, int mode) bond->xmit_hash_policy = bond_xmit_hash_policy_l2; break; case BOND_MODE_ALB: + case BOND_MODE_SLB: bond_set_master_alb_flags(bond); /* FALLTHRU */ - case BOND_MODE_SLB: case BOND_MODE_TLB: bond_dev->hard_start_xmit = bond_alb_xmit; bond_dev->set_mac_address = bond_alb_set_mac_address; diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index b401426b..13ef723b 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -65,7 +65,7 @@ #define USES_PRIMARY(mode) \ (((mode) == BOND_MODE_ACTIVEBACKUP) || \ ((mode) == BOND_MODE_TLB) || \ - ((mode) == BOND_MODE_ALB) || \ + ((mode) == BOND_MODE_ALB) || \ ((mode) == BOND_MODE_SLB)) /* diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 3a73b8c9..7d51fda5 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "br_private.h" @@ -331,21 +332,66 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, return ret; } -void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr) +int br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, + struct sk_buff *skb) { + const unsigned char *addr = eth_hdr(skb)->h_source; struct hlist_head *head = &br->hash[br_mac_hash(addr)]; struct net_bridge_fdb_entry *fdb; + static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* some users want to always flood. */ if (hold_time(br) == 0) - return; + return 1; fdb = fdb_find(head, addr); if (likely(fdb)) { + /* + * If this is an address arriving on the physical port + * which we have previously seen on a non-physical + * port then ignore it. + * + * _Unless_ it is a broadcast ARP reply in which case + * the guest in question has migrated. + */ + extern struct net_bridge_port *br_locate_physical_port(struct net_bridge *br); + struct net_bridge_port *phys_port = br_locate_physical_port(br); + if (phys_port && phys_port != fdb->dst && phys_port == source) { +#pragma pack(1) + struct arp_pkt { + u16 hw_addr_space; + u16 prot_addr_space; + u8 hw_addr_len; + u8 prot_addr_len; + u16 op_code; + u8 mac_src[ETH_ALEN]; /* sender hardware address */ + u32 ip_src; /* sender IP address */ + u8 mac_dst[ETH_ALEN]; /* target hardware address */ + u32 ip_dst; /* target IP address */ + }; +#pragma pack() + struct arp_pkt *arp = (struct arp_pkt *)skb->data; + + if (compare_ether_addr(bcast, addr) != 0) + return 0; + + if (!arp) + return 0; + + if (skb->len < sizeof(struct arp_pkt)) + return 0; + + if (eth_hdr(skb)->h_proto != htons(ETH_P_ARP)) + return 0; + + if (arp->op_code != htons(ARPOP_REPLY)) + return 0; + } + /* attempt to update an entry for a local interface */ if (unlikely(fdb->is_local)) { - if (net_ratelimit()) + return 0; + if (net_ratelimit()) printk(KERN_WARNING "%s: received packet with " " own address as source address\n", source->dev->name); @@ -363,4 +409,6 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, */ spin_unlock(&br->hash_lock); } + + return 1; } diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 5b7e3584..7c5c81cf 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -216,6 +217,7 @@ static struct net_device *new_bridge_dev(const char *name) br->topology_change = 0; br->topology_change_detected = 0; br->ageing_time = 300 * HZ; + br->phys_port = NULL; INIT_LIST_HEAD(&br->age_list); br_stp_timer_init(br); @@ -280,6 +282,20 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, return p; } +struct net_bridge_port *br_locate_physical_port(struct net_bridge *br) +{ + struct net_bridge_port *p; + if (!br->phys_port) { + list_for_each_entry(p, &br->port_list, list) { + if (!compare_ether_addr(br->dev->dev_addr, p->dev->dev_addr)) { + br->phys_port = p; + break; + } + } + } + return br->phys_port; +} + struct net_device *br_locate_physical_device(struct net_device *dev) { struct net_bridge *br; @@ -289,55 +305,54 @@ struct net_device *br_locate_physical_device(struct net_device *dev) return dev; br = netdev_priv(dev); + p = br_locate_physical_port(br); - list_for_each_entry(p, &br->port_list, list) { - if (!compare_ether_addr(dev->dev_addr, p->dev->dev_addr)) - return p->dev; - } - return dev; + return p ? p->dev : dev; } EXPORT_SYMBOL(br_locate_physical_device); static struct sk_buff *create_switch_learning_packet(struct net_device *dev, unsigned char *src_hw) { +#pragma pack(1) + struct learning_pkt { + u8 mac_dst[ETH_ALEN]; + u8 mac_src[ETH_ALEN]; + u16 type; + u8 padding[ETH_ZLEN - ETH_HLEN]; + }; +#pragma pack() struct sk_buff *skb; - unsigned char *data; - - /* - * Xen OUI is 00-16-3E therefore multicast address is 01-16-3E. - * Use the first of these addresses as our destination address with protocol type 0. - * Include the physical interface's MAC address as the payload. - */ - unsigned char dest_hw[ETH_ALEN] = {0x01, 0x16, 0x3e, 0x00, 0x00, 0x00}; - - skb = alloc_skb(ETH_ALEN + LL_RESERVED_SPACE(dev), GFP_ATOMIC); - if (skb == NULL) + struct learning_pkt pkt; + int size = sizeof(struct learning_pkt); + char *data; + const unsigned char dest_hw[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + memset(&pkt, 0, size); + memcpy(pkt.mac_dst, dest_hw, ETH_ALEN); + memcpy(pkt.mac_src, src_hw, ETH_ALEN); + pkt.type = __constant_htons(ETH_P_LOOP); + + skb = dev_alloc_skb(size); + if (!skb) return NULL; - skb_reserve(skb, LL_RESERVED_SPACE(dev)); - skb->nh.raw = skb->data; - data = (unsigned char *) skb_put(skb, ETH_ALEN); + data = skb_put(skb, size); + memcpy(data, &pkt, size); + skb->mac.raw = data; + skb->nh.raw = data + ETH_HLEN; + skb->protocol = pkt.type; + skb->priority = TC_PRIO_CONTROL; skb->dev = dev; - skb->protocol = 0; - - if (dev->hard_header && - dev->hard_header(skb,dev,0,&dest_hw,src_hw,skb->len) < 0) - goto out; - - memcpy(data, dev->dev_addr, ETH_ALEN); return skb; - -out: - kfree_skb(skb); - return NULL; } void br_send_gratuitous_switch_learning_packet(struct net_device *dev) { struct net_bridge *br; struct net_device *phys; + struct sk_buff *skb; int i; if (!dev->br_port) @@ -357,16 +372,22 @@ void br_send_gratuitous_switch_learning_packet(struct net_device *dev) if (f->dst != phys->br_port && f->dst->dev->addr_len == ETH_ALEN && memcmp(&f->dst->dev->dev_addr[0], &f->addr.addr[0], ETH_ALEN) != 0) { - struct sk_buff *skb; skb = create_switch_learning_packet(dev, f->addr.addr); if (skb == NULL) goto out; dev_queue_xmit(skb); + + f->ageing_timer = jiffies; } } } + + skb = create_switch_learning_packet(dev, dev->dev_addr); + if (skb) + dev_queue_xmit(skb); + out: spin_unlock_bh(&br->hash_lock); } @@ -552,6 +573,9 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) if (!p || p->br != br) return -EINVAL; + if ( p == br->phys_port ) + br->phys_port = NULL; + del_nbp(p); spin_lock_bh(&br->lock); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index bfa4d8c3..3dadc895 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -50,7 +50,8 @@ int br_handle_frame_finish(struct sk_buff *skb) /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; - br_fdb_update(br, p, eth_hdr(skb)->h_source); + if (!br_fdb_update(br, p, skb)) + goto drop; if (p->state == BR_STATE_LEARNING) goto drop; @@ -101,8 +102,12 @@ static int br_handle_local_finish(struct sk_buff *skb) { struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); - if (p && p->state != BR_STATE_DISABLED) - br_fdb_update(p->br, p, eth_hdr(skb)->h_source); + if (p && p->state != BR_STATE_DISABLED) { + if (!br_fdb_update(p->br, p, skb)) { + kfree_skb(skb); + return 1; + } + } return 0; /* process further */ } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1c911bf5..dcdeb6d2 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -91,6 +91,7 @@ struct net_bridge struct list_head port_list; struct list_head promiscuous_list; struct net_device *dev; + struct net_bridge_port *phys_port; /* One of our ports will contains the route to the physical world */ struct net_device_stats statistics; spinlock_t hash_lock; struct hlist_head hash[BR_HASH_SIZE]; @@ -154,9 +155,9 @@ extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, extern int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr); -extern void br_fdb_update(struct net_bridge *br, - struct net_bridge_port *source, - const unsigned char *addr); +extern int br_fdb_update(struct net_bridge *br, + struct net_bridge_port *source, + struct sk_buff *skb); /* br_forward.c */ extern void br_deliver(const struct net_bridge_port *to,