diff --git a/debian/changelog b/debian/changelog index b7b34bf9a..0e68f9029 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ linux-2.6 (2.6.32-9) UNRELEASED; urgency=low * [x86] Enable USB IP drivers (Closes: #568903) * Ignore failure of lsusb when gathering information for bug reports (Closes: #569725) + * macvlan: Add bridge, VEPA and private modes (Closes: #568756) [ maximilian attems] * Postinst don't refercence k-p related manpage. (Closes: #542208) diff --git a/debian/patches/features/all/macvlan-Copy-functions-from-2.6.33-net-core-dev-c.patch b/debian/patches/features/all/macvlan-Copy-functions-from-2.6.33-net-core-dev-c.patch new file mode 100644 index 000000000..69c901984 --- /dev/null +++ b/debian/patches/features/all/macvlan-Copy-functions-from-2.6.33-net-core-dev-c.patch @@ -0,0 +1,64 @@ +From 2b071e9effc16db91cd17577a35b0e61e9fcee9d Mon Sep 17 00:00:00 2001 +From: Ben Hutchings +Date: Tue, 16 Feb 2010 03:06:44 +0000 +Subject: [PATCH] macvlan: Copy necessary helper functions from 2.6.33 net/core/dev.c + +--- + drivers/net/macvlan.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 41 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c +index 2490aa3..cb4a16e 100644 +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -46,6 +46,47 @@ struct macvlan_dev { + struct net_device *lowerdev; + }; + ++/* From 2.6.33 net/core/dev.c */ ++static int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) ++{ ++ skb_orphan(skb); ++ ++ if (!(dev->flags & IFF_UP)) ++ return NET_RX_DROP; ++ ++ if (skb->len > (dev->mtu + dev->hard_header_len)) ++ return NET_RX_DROP; ++ ++ skb_dst_drop(skb); ++ skb->tstamp.tv64 = 0; ++ skb->pkt_type = PACKET_HOST; ++ skb->protocol = eth_type_trans(skb, dev); ++ skb->mark = 0; ++ secpath_reset(skb); ++ nf_reset(skb); ++ return netif_rx(skb); ++} ++ ++/* From 2.6.33 net/core/dev.c */ ++static void dev_txq_stats_fold(const struct net_device *dev, ++ struct net_device_stats *stats) ++{ ++ unsigned long tx_bytes = 0, tx_packets = 0, tx_dropped = 0; ++ unsigned int i; ++ struct netdev_queue *txq; ++ ++ for (i = 0; i < dev->num_tx_queues; i++) { ++ txq = netdev_get_tx_queue(dev, i); ++ tx_bytes += txq->tx_bytes; ++ tx_packets += txq->tx_packets; ++ tx_dropped += txq->tx_dropped; ++ } ++ if (tx_bytes || tx_packets || tx_dropped) { ++ stats->tx_bytes = tx_bytes; ++ stats->tx_packets = tx_packets; ++ stats->tx_dropped = tx_dropped; ++ } ++} + + static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port, + const unsigned char *addr) +-- +1.6.6.2 + diff --git a/debian/patches/features/all/macvlan-Precise-RX-stats-accounting.patch b/debian/patches/features/all/macvlan-Precise-RX-stats-accounting.patch new file mode 100644 index 000000000..7f8a50318 --- /dev/null +++ b/debian/patches/features/all/macvlan-Precise-RX-stats-accounting.patch @@ -0,0 +1,183 @@ +From fccaf71011b171883efee5bae321eac4760584d1 Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Tue, 17 Nov 2009 08:53:49 +0000 +Subject: [PATCH 1/4] macvlan: Precise RX stats accounting + +With multi queue devices, its possible that several cpus call +macvlan RX routines simultaneously for the same macvlan device. + +We update RX stats counter without any locking, so we can +get slightly wrong counters. + +One possible fix is to use percpu counters, to get precise +accounting and also get guarantee of no cache line ping pongs +between cpus. + +Note: this adds 16 bytes (32 bytes on 64bit arches) of percpu +data per macvlan device. + +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +--- + drivers/net/macvlan.c | 76 ++++++++++++++++++++++++++++++++++++++++++------ + 1 files changed, 66 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c +index 271aa7e..ae2b5c7 100644 +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -38,12 +38,27 @@ struct macvlan_port { + struct list_head vlans; + }; + ++/** ++ * struct macvlan_rx_stats - MACVLAN percpu rx stats ++ * @rx_packets: number of received packets ++ * @rx_bytes: number of received bytes ++ * @multicast: number of received multicast packets ++ * @rx_errors: number of errors ++ */ ++struct macvlan_rx_stats { ++ unsigned long rx_packets; ++ unsigned long rx_bytes; ++ unsigned long multicast; ++ unsigned long rx_errors; ++}; ++ + struct macvlan_dev { + struct net_device *dev; + struct list_head list; + struct hlist_node hlist; + struct macvlan_port *port; + struct net_device *lowerdev; ++ struct macvlan_rx_stats *rx_stats; + }; + + +@@ -110,6 +125,7 @@ static void macvlan_broadcast(struct sk_buff *skb, + struct net_device *dev; + struct sk_buff *nskb; + unsigned int i; ++ struct macvlan_rx_stats *rx_stats; + + if (skb->protocol == htons(ETH_P_PAUSE)) + return; +@@ -117,17 +133,17 @@ static void macvlan_broadcast(struct sk_buff *skb, + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { + hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) { + dev = vlan->dev; ++ rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id()); + + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb == NULL) { +- dev->stats.rx_errors++; +- dev->stats.rx_dropped++; ++ rx_stats->rx_errors++; + continue; + } + +- dev->stats.rx_bytes += skb->len + ETH_HLEN; +- dev->stats.rx_packets++; +- dev->stats.multicast++; ++ rx_stats->rx_bytes += skb->len + ETH_HLEN; ++ rx_stats->rx_packets++; ++ rx_stats->multicast++; + + nskb->dev = dev; + if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast)) +@@ -147,6 +163,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) + const struct macvlan_port *port; + const struct macvlan_dev *vlan; + struct net_device *dev; ++ struct macvlan_rx_stats *rx_stats; + + port = rcu_dereference(skb->dev->macvlan_port); + if (port == NULL) +@@ -166,16 +183,15 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) + kfree_skb(skb); + return NULL; + } +- ++ rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id()); + skb = skb_share_check(skb, GFP_ATOMIC); + if (skb == NULL) { +- dev->stats.rx_errors++; +- dev->stats.rx_dropped++; ++ rx_stats->rx_errors++; + return NULL; + } + +- dev->stats.rx_bytes += skb->len + ETH_HLEN; +- dev->stats.rx_packets++; ++ rx_stats->rx_bytes += skb->len + ETH_HLEN; ++ rx_stats->rx_packets++; + + skb->dev = dev; + skb->pkt_type = PACKET_HOST; +@@ -365,9 +381,47 @@ static int macvlan_init(struct net_device *dev) + + macvlan_set_lockdep_class(dev); + ++ vlan->rx_stats = alloc_percpu(struct macvlan_rx_stats); ++ if (!vlan->rx_stats) ++ return -ENOMEM; ++ + return 0; + } + ++static void macvlan_uninit(struct net_device *dev) ++{ ++ struct macvlan_dev *vlan = netdev_priv(dev); ++ ++ free_percpu(vlan->rx_stats); ++} ++ ++static struct net_device_stats *macvlan_dev_get_stats(struct net_device *dev) ++{ ++ struct net_device_stats *stats = &dev->stats; ++ struct macvlan_dev *vlan = netdev_priv(dev); ++ ++ dev_txq_stats_fold(dev, stats); ++ ++ if (vlan->rx_stats) { ++ struct macvlan_rx_stats *p, rx = {0}; ++ int i; ++ ++ for_each_possible_cpu(i) { ++ p = per_cpu_ptr(vlan->rx_stats, i); ++ rx.rx_packets += p->rx_packets; ++ rx.rx_bytes += p->rx_bytes; ++ rx.rx_errors += p->rx_errors; ++ rx.multicast += p->multicast; ++ } ++ stats->rx_packets = rx.rx_packets; ++ stats->rx_bytes = rx.rx_bytes; ++ stats->rx_errors = rx.rx_errors; ++ stats->rx_dropped = rx.rx_errors; ++ stats->multicast = rx.multicast; ++ } ++ return stats; ++} ++ + static void macvlan_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) + { +@@ -404,6 +458,7 @@ static const struct ethtool_ops macvlan_ethtool_ops = { + + static const struct net_device_ops macvlan_netdev_ops = { + .ndo_init = macvlan_init, ++ .ndo_uninit = macvlan_uninit, + .ndo_open = macvlan_open, + .ndo_stop = macvlan_stop, + .ndo_start_xmit = macvlan_start_xmit, +@@ -411,6 +466,7 @@ static const struct net_device_ops macvlan_netdev_ops = { + .ndo_change_rx_flags = macvlan_change_rx_flags, + .ndo_set_mac_address = macvlan_set_mac_address, + .ndo_set_multicast_list = macvlan_set_multicast_list, ++ .ndo_get_stats = macvlan_dev_get_stats, + .ndo_validate_addr = eth_validate_addr, + }; + +-- +1.6.6.2 + diff --git a/debian/patches/features/all/macvlan-cleanup-rx-statistics.patch b/debian/patches/features/all/macvlan-cleanup-rx-statistics.patch new file mode 100644 index 000000000..bd5b8b4f9 --- /dev/null +++ b/debian/patches/features/all/macvlan-cleanup-rx-statistics.patch @@ -0,0 +1,134 @@ +From a1e514c5d0397b5581721aad9b303f7df83b103d Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Thu, 26 Nov 2009 06:07:09 +0000 +Subject: [PATCH 2/4] macvlan: cleanup rx statistics + +We have very similar code for rx statistics in +two places in the macvlan driver, with a third +one being added in the next patch. + +Consolidate them into one function to improve +overall readability of the driver. + +Signed-off-by: Arnd Bergmann +Acked-by: Patrick McHardy +Signed-off-by: David S. Miller +--- + drivers/net/macvlan.c | 70 ++++++++++++++++++++++++++++-------------------- + 1 files changed, 41 insertions(+), 29 deletions(-) + +diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c +index ae2b5c7..1e7faf9 100644 +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -116,42 +116,58 @@ static int macvlan_addr_busy(const struct macvlan_port *port, + return 0; + } + ++static inline void macvlan_count_rx(const struct macvlan_dev *vlan, ++ unsigned int len, bool success, ++ bool multicast) ++{ ++ struct macvlan_rx_stats *rx_stats; ++ ++ rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id()); ++ if (likely(success)) { ++ rx_stats->rx_packets++;; ++ rx_stats->rx_bytes += len; ++ if (multicast) ++ rx_stats->multicast++; ++ } else { ++ rx_stats->rx_errors++; ++ } ++} ++ ++static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev, ++ const struct ethhdr *eth) ++{ ++ if (!skb) ++ return NET_RX_DROP; ++ ++ skb->dev = dev; ++ if (!compare_ether_addr_64bits(eth->h_dest, ++ dev->broadcast)) ++ skb->pkt_type = PACKET_BROADCAST; ++ else ++ skb->pkt_type = PACKET_MULTICAST; ++ ++ return netif_rx(skb); ++} ++ + static void macvlan_broadcast(struct sk_buff *skb, + const struct macvlan_port *port) + { + const struct ethhdr *eth = eth_hdr(skb); + const struct macvlan_dev *vlan; + struct hlist_node *n; +- struct net_device *dev; + struct sk_buff *nskb; + unsigned int i; +- struct macvlan_rx_stats *rx_stats; ++ int err; + + if (skb->protocol == htons(ETH_P_PAUSE)) + return; + + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { + hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) { +- dev = vlan->dev; +- rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id()); +- + nskb = skb_clone(skb, GFP_ATOMIC); +- if (nskb == NULL) { +- rx_stats->rx_errors++; +- continue; +- } +- +- rx_stats->rx_bytes += skb->len + ETH_HLEN; +- rx_stats->rx_packets++; +- rx_stats->multicast++; +- +- nskb->dev = dev; +- if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast)) +- nskb->pkt_type = PACKET_BROADCAST; +- else +- nskb->pkt_type = PACKET_MULTICAST; +- +- netif_rx(nskb); ++ err = macvlan_broadcast_one(nskb, vlan->dev, eth); ++ macvlan_count_rx(vlan, skb->len + ETH_HLEN, ++ err == NET_RX_SUCCESS, 1); + } + } + } +@@ -163,7 +179,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) + const struct macvlan_port *port; + const struct macvlan_dev *vlan; + struct net_device *dev; +- struct macvlan_rx_stats *rx_stats; ++ unsigned int len; + + port = rcu_dereference(skb->dev->macvlan_port); + if (port == NULL) +@@ -183,15 +199,11 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) + kfree_skb(skb); + return NULL; + } +- rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id()); ++ len = skb->len + ETH_HLEN; + skb = skb_share_check(skb, GFP_ATOMIC); +- if (skb == NULL) { +- rx_stats->rx_errors++; ++ macvlan_count_rx(vlan, len, skb != NULL, 0); ++ if (!skb) + return NULL; +- } +- +- rx_stats->rx_bytes += skb->len + ETH_HLEN; +- rx_stats->rx_packets++; + + skb->dev = dev; + skb->pkt_type = PACKET_HOST; +-- +1.6.6.2 + diff --git a/debian/patches/features/all/macvlan-export-macvlan-mode-through-netlink.patch b/debian/patches/features/all/macvlan-export-macvlan-mode-through-netlink.patch new file mode 100644 index 000000000..ba7d60cf1 --- /dev/null +++ b/debian/patches/features/all/macvlan-export-macvlan-mode-through-netlink.patch @@ -0,0 +1,143 @@ +From 27c0b1a850cdea6298f573d835782f3337be913c Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Thu, 26 Nov 2009 06:07:11 +0000 +Subject: [PATCH 4/4] macvlan: export macvlan mode through netlink + +In order to support all three modes of macvlan at +runtime, extend the existing netlink protocol +to allow choosing the mode per macvlan slave +interface. + +This depends on a matching patch to iproute2 +in order to become accessible in user land. + +Signed-off-by: Arnd Bergmann +Acked-by: Patrick McHardy +Signed-off-by: David S. Miller +--- + drivers/net/macvlan.c | 56 +++++++++++++++++++++++++++++++++++++++++----- + include/linux/if_link.h | 15 ++++++++++++ + 2 files changed, 65 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c +index d6bd843..322112c 100644 +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -33,12 +33,6 @@ + + #define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) + +-enum macvlan_mode { +- MACVLAN_MODE_PRIVATE = 1, +- MACVLAN_MODE_VEPA = 2, +- MACVLAN_MODE_BRIDGE = 4, +-}; +- + struct macvlan_port { + struct net_device *dev; + struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; +@@ -614,6 +608,17 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } ++ ++ if (data && data[IFLA_MACVLAN_MODE]) { ++ switch (nla_get_u32(data[IFLA_MACVLAN_MODE])) { ++ case MACVLAN_MODE_PRIVATE: ++ case MACVLAN_MODE_VEPA: ++ case MACVLAN_MODE_BRIDGE: ++ break; ++ default: ++ return -EINVAL; ++ } ++ } + return 0; + } + +@@ -678,6 +683,10 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev, + vlan->dev = dev; + vlan->port = port; + ++ vlan->mode = MACVLAN_MODE_VEPA; ++ if (data && data[IFLA_MACVLAN_MODE]) ++ vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); ++ + err = register_netdevice(dev); + if (err < 0) + return err; +@@ -699,6 +708,36 @@ static void macvlan_dellink(struct net_device *dev, struct list_head *head) + macvlan_port_destroy(port->dev); + } + ++static int macvlan_changelink(struct net_device *dev, ++ struct nlattr *tb[], struct nlattr *data[]) ++{ ++ struct macvlan_dev *vlan = netdev_priv(dev); ++ if (data && data[IFLA_MACVLAN_MODE]) ++ vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); ++ return 0; ++} ++ ++static size_t macvlan_get_size(const struct net_device *dev) ++{ ++ return nla_total_size(4); ++} ++ ++static int macvlan_fill_info(struct sk_buff *skb, ++ const struct net_device *dev) ++{ ++ struct macvlan_dev *vlan = netdev_priv(dev); ++ ++ NLA_PUT_U32(skb, IFLA_MACVLAN_MODE, vlan->mode); ++ return 0; ++ ++nla_put_failure: ++ return -EMSGSIZE; ++} ++ ++static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = { ++ [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, ++}; ++ + static struct rtnl_link_ops macvlan_link_ops __read_mostly = { + .kind = "macvlan", + .priv_size = sizeof(struct macvlan_dev), +@@ -707,6 +746,11 @@ static struct rtnl_link_ops macvlan_link_ops __read_mostly = { + .validate = macvlan_validate, + .newlink = macvlan_newlink, + .dellink = macvlan_dellink, ++ .maxtype = IFLA_MACVLAN_MAX, ++ .policy = macvlan_policy, ++ .changelink = macvlan_changelink, ++ .get_size = macvlan_get_size, ++ .fill_info = macvlan_fill_info, + }; + + static int macvlan_device_event(struct notifier_block *unused, +diff --git a/include/linux/if_link.h b/include/linux/if_link.h +index 1d3b242..6674791 100644 +--- a/include/linux/if_link.h ++++ b/include/linux/if_link.h +@@ -181,4 +181,19 @@ struct ifla_vlan_qos_mapping { + __u32 to; + }; + ++/* MACVLAN section */ ++enum { ++ IFLA_MACVLAN_UNSPEC, ++ IFLA_MACVLAN_MODE, ++ __IFLA_MACVLAN_MAX, ++}; ++ ++#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) ++ ++enum macvlan_mode { ++ MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ ++ MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ ++ MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ ++}; ++ + #endif /* _LINUX_IF_LINK_H */ +-- +1.6.6.2 + diff --git a/debian/patches/features/all/macvlan-implement-bridge-VEPA-and-private-mode.patch b/debian/patches/features/all/macvlan-implement-bridge-VEPA-and-private-mode.patch new file mode 100644 index 000000000..4dcdec0b8 --- /dev/null +++ b/debian/patches/features/all/macvlan-implement-bridge-VEPA-and-private-mode.patch @@ -0,0 +1,206 @@ +From 618e1b7482f7a8a4c6c6e8ccbe140e4c331df4e9 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Thu, 26 Nov 2009 06:07:10 +0000 +Subject: [PATCH 3/4] macvlan: implement bridge, VEPA and private mode + +This allows each macvlan slave device to be in one +of three modes, depending on the use case: + +MACVLAN_PRIVATE: + The device never communicates with any other device + on the same upper_dev. This even includes frames + coming back from a reflective relay, where supported + by the adjacent bridge. + +MACVLAN_VEPA: + The new Virtual Ethernet Port Aggregator (VEPA) mode, + we assume that the adjacent bridge returns all frames + where both source and destination are local to the + macvlan port, i.e. the bridge is set up as a reflective + relay. + Broadcast frames coming in from the upper_dev get + flooded to all macvlan interfaces in VEPA mode. + We never deliver any frames locally. + +MACVLAN_BRIDGE: + We provide the behavior of a simple bridge between + different macvlan interfaces on the same port. Frames + from one interface to another one get delivered directly + and are not sent out externally. Broadcast frames get + flooded to all other bridge ports and to the external + interface, but when they come back from a reflective + relay, we don't deliver them again. + Since we know all the MAC addresses, the macvlan bridge + mode does not require learning or STP like the bridge + module does. + +Based on an earlier patch "macvlan: Reflect macvlan packets +meant for other macvlan devices" by Eric Biederman. + +Signed-off-by: Arnd Bergmann +Acked-by: Patrick McHardy +Cc: Eric Biederman +Signed-off-by: David S. Miller +--- + drivers/net/macvlan.c | 80 ++++++++++++++++++++++++++++++++++++++++++++----- + 1 files changed, 72 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c +index 1e7faf9..d6bd843 100644 +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -29,9 +29,16 @@ + #include + #include + #include ++#include + + #define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) + ++enum macvlan_mode { ++ MACVLAN_MODE_PRIVATE = 1, ++ MACVLAN_MODE_VEPA = 2, ++ MACVLAN_MODE_BRIDGE = 4, ++}; ++ + struct macvlan_port { + struct net_device *dev; + struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; +@@ -59,6 +66,7 @@ struct macvlan_dev { + struct macvlan_port *port; + struct net_device *lowerdev; + struct macvlan_rx_stats *rx_stats; ++ enum macvlan_mode mode; + }; + + +@@ -134,11 +142,14 @@ static inline void macvlan_count_rx(const struct macvlan_dev *vlan, + } + + static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev, +- const struct ethhdr *eth) ++ const struct ethhdr *eth, bool local) + { + if (!skb) + return NET_RX_DROP; + ++ if (local) ++ return dev_forward_skb(dev, skb); ++ + skb->dev = dev; + if (!compare_ether_addr_64bits(eth->h_dest, + dev->broadcast)) +@@ -150,7 +161,9 @@ static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev, + } + + static void macvlan_broadcast(struct sk_buff *skb, +- const struct macvlan_port *port) ++ const struct macvlan_port *port, ++ struct net_device *src, ++ enum macvlan_mode mode) + { + const struct ethhdr *eth = eth_hdr(skb); + const struct macvlan_dev *vlan; +@@ -164,8 +177,12 @@ static void macvlan_broadcast(struct sk_buff *skb, + + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { + hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) { ++ if (vlan->dev == src || !(vlan->mode & mode)) ++ continue; ++ + nskb = skb_clone(skb, GFP_ATOMIC); +- err = macvlan_broadcast_one(nskb, vlan->dev, eth); ++ err = macvlan_broadcast_one(nskb, vlan->dev, eth, ++ mode == MACVLAN_MODE_BRIDGE); + macvlan_count_rx(vlan, skb->len + ETH_HLEN, + err == NET_RX_SUCCESS, 1); + } +@@ -178,6 +195,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) + const struct ethhdr *eth = eth_hdr(skb); + const struct macvlan_port *port; + const struct macvlan_dev *vlan; ++ const struct macvlan_dev *src; + struct net_device *dev; + unsigned int len; + +@@ -186,7 +204,25 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) + return skb; + + if (is_multicast_ether_addr(eth->h_dest)) { +- macvlan_broadcast(skb, port); ++ src = macvlan_hash_lookup(port, eth->h_source); ++ if (!src) ++ /* frame comes from an external address */ ++ macvlan_broadcast(skb, port, NULL, ++ MACVLAN_MODE_PRIVATE | ++ MACVLAN_MODE_VEPA | ++ MACVLAN_MODE_BRIDGE); ++ else if (src->mode == MACVLAN_MODE_VEPA) ++ /* flood to everyone except source */ ++ macvlan_broadcast(skb, port, src->dev, ++ MACVLAN_MODE_VEPA | ++ MACVLAN_MODE_BRIDGE); ++ else if (src->mode == MACVLAN_MODE_BRIDGE) ++ /* ++ * flood only to VEPA ports, bridge ports ++ * already saw the frame on the way out. ++ */ ++ macvlan_broadcast(skb, port, src->dev, ++ MACVLAN_MODE_VEPA); + return skb; + } + +@@ -212,18 +248,46 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) + return NULL; + } + ++static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ const struct macvlan_dev *vlan = netdev_priv(dev); ++ const struct macvlan_port *port = vlan->port; ++ const struct macvlan_dev *dest; ++ ++ if (vlan->mode == MACVLAN_MODE_BRIDGE) { ++ const struct ethhdr *eth = (void *)skb->data; ++ ++ /* send to other bridge ports directly */ ++ if (is_multicast_ether_addr(eth->h_dest)) { ++ macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE); ++ goto xmit_world; ++ } ++ ++ dest = macvlan_hash_lookup(port, eth->h_dest); ++ if (dest && dest->mode == MACVLAN_MODE_BRIDGE) { ++ unsigned int length = skb->len + ETH_HLEN; ++ int ret = dev_forward_skb(dest->dev, skb); ++ macvlan_count_rx(dest, length, ++ ret == NET_RX_SUCCESS, 0); ++ ++ return NET_XMIT_SUCCESS; ++ } ++ } ++ ++xmit_world: ++ skb->dev = vlan->lowerdev; ++ return dev_queue_xmit(skb); ++} ++ + static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, + struct net_device *dev) + { + int i = skb_get_queue_mapping(skb); + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); +- const struct macvlan_dev *vlan = netdev_priv(dev); + unsigned int len = skb->len; + int ret; + +- skb->dev = vlan->lowerdev; +- ret = dev_queue_xmit(skb); +- ++ ret = macvlan_queue_xmit(skb, dev); + if (likely(ret == NET_XMIT_SUCCESS)) { + txq->tx_packets++; + txq->tx_bytes += len; +-- +1.6.6.2 + diff --git a/debian/patches/series/9 b/debian/patches/series/9 index e97973a63..413e7f192 100644 --- a/debian/patches/series/9 +++ b/debian/patches/series/9 @@ -1,2 +1,7 @@ + bugfix/all/cxusb-dont-select-lgs8gl5.patch + debian/sysrq-mask.patch ++ features/all/macvlan-Copy-functions-from-2.6.33-net-core-dev-c.patch ++ features/all/macvlan-Precise-RX-stats-accounting.patch ++ features/all/macvlan-cleanup-rx-statistics.patch ++ features/all/macvlan-implement-bridge-VEPA-and-private-mode.patch ++ features/all/macvlan-export-macvlan-mode-through-netlink.patch