]> xenbits.xen.org Git - xenclient/kernel.git/commitdiff
imported patch bonding-balance-slb-fixes.patch git-3201e656ce56ed02e9501906c18ffe16ae350a52-bonding-use-after-free
authort_jeang <devnull@localhost>
Tue, 6 Jan 2009 12:06:05 +0000 (12:06 +0000)
committert_jeang <devnull@localhost>
Tue, 6 Jan 2009 12:06:05 +0000 (12:06 +0000)
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bonding.h
net/bridge/br_fdb.c
net/bridge/br_if.c
net/bridge/br_input.c
net/bridge/br_private.h

index c6ecb4c7a8d0fd3e741ddeaebbd9e990e27c3df3..4dcfa2733b4e692a6640b97e9040faaa3449bbc2 100644 (file)
@@ -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; i<VLAN_GROUP_ARRAY_LEN; i++) {
+                       if (bond->vlgrp->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);
index d7c4a1f7b51e19e8b37d1309024926b16a385a40..7f0bde1a8c8a4de782b5c6e5fe3d1b2211dad4f5 100644 (file)
@@ -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;
index b401426b89dec9dead2c06b571df7be4634100a2..13ef723bf8ee8d24a83d56fa42ab23ccd777e63e 100644 (file)
@@ -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))
 
 /*
index 3a73b8c94271c94beaf3159a078a4fa46c2bb6b0..7d51fda5bdb49dd898e2a19b3840ba51d2099fa9 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/jhash.h>
+#include <linux/if_arp.h>
 #include <asm/atomic.h>
 #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;
 }
index 5b7e35848272394646cd825736a012038b07cf5a..7c5c81cf1425655fd43b7f40658b79b6a6db1c61 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
+#include <linux/pkt_sched.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <linux/if_arp.h>
@@ -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);
index bfa4d8c333f7b727946e40c4c33e159d4fdbfe2f..3dadc895952004246caa324845f35dce91161192 100644 (file)
@@ -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 */
 }
index 1c911bf55fe4bb337c8d562795710f58ffe57172..dcdeb6d2dd6c8deb08d5af1b8ed30ff767600543 100644 (file)
@@ -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,