Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 171945
b: refs/heads/master
c: 618e1b7
h: refs/heads/master
i:
  171943: 47e7c0c
v: v3
  • Loading branch information
Arnd Bergmann authored and David S. Miller committed Nov 26, 2009
1 parent 9629d0f commit bbf7556
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 9 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a1e514c5d0397b5581721aad9b303f7df83b103d
refs/heads/master: 618e1b7482f7a8a4c6c6e8ccbe140e4c331df4e9
80 changes: 72 additions & 8 deletions trunk/drivers/net/macvlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@
#include <linux/if_link.h>
#include <linux/if_macvlan.h>
#include <net/rtnetlink.h>
#include <net/xfrm.h>

#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];
Expand Down Expand Up @@ -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;
};


Expand Down Expand Up @@ -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))
Expand All @@ -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;
Expand All @@ -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);
}
Expand All @@ -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;

Expand All @@ -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;
}

Expand All @@ -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;
Expand Down

0 comments on commit bbf7556

Please sign in to comment.