Skip to content

Commit

Permalink
macvlan: Introduce 'passthru' mode to takeover the underlying device
Browse files Browse the repository at this point in the history
With the current default 'vepa' mode, a KVM guest using virtio with
macvtap backend has the following limitations.
- cannot change/add a mac address on the guest virtio-net
- cannot create a vlan device on the guest virtio-net
- cannot enable promiscuous mode on guest virtio-net

To address these limitations, this patch introduces a new mode called
'passthru' when creating a macvlan device which allows takeover of the
underlying device and passing it to a guest using virtio with macvtap
backend.

Only one macvlan device is allowed in passthru mode and it inherits
the mac address from the underlying device and sets it in promiscuous
mode to receive and forward all the packets.

Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>

-------------------------------------------------------------------------
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Sridhar Samudrala authored and davem330 committed Nov 22, 2010
1 parent e5700c7 commit eb06acd
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 1 deletion.
33 changes: 32 additions & 1 deletion drivers/net/macvlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct macvlan_port {
struct hlist_head vlan_hash[MACVLAN_HASH_SIZE];
struct list_head vlans;
struct rcu_head rcu;
bool passthru;
};

#define macvlan_port_get_rcu(dev) \
Expand Down Expand Up @@ -169,6 +170,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
macvlan_broadcast(skb, port, NULL,
MACVLAN_MODE_PRIVATE |
MACVLAN_MODE_VEPA |
MACVLAN_MODE_PASSTHRU|
MACVLAN_MODE_BRIDGE);
else if (src->mode == MACVLAN_MODE_VEPA)
/* flood to everyone except source */
Expand All @@ -185,7 +187,10 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
return skb;
}

vlan = macvlan_hash_lookup(port, eth->h_dest);
if (port->passthru)
vlan = list_first_entry(&port->vlans, struct macvlan_dev, list);
else
vlan = macvlan_hash_lookup(port, eth->h_dest);
if (vlan == NULL)
return skb;

Expand Down Expand Up @@ -288,6 +293,11 @@ static int macvlan_open(struct net_device *dev)
struct net_device *lowerdev = vlan->lowerdev;
int err;

if (vlan->port->passthru) {
dev_set_promiscuity(lowerdev, 1);
goto hash_add;
}

err = -EBUSY;
if (macvlan_addr_busy(vlan->port, dev->dev_addr))
goto out;
Expand All @@ -300,6 +310,8 @@ static int macvlan_open(struct net_device *dev)
if (err < 0)
goto del_unicast;
}

hash_add:
macvlan_hash_add(vlan);
return 0;

Expand All @@ -314,12 +326,18 @@ static int macvlan_stop(struct net_device *dev)
struct macvlan_dev *vlan = netdev_priv(dev);
struct net_device *lowerdev = vlan->lowerdev;

if (vlan->port->passthru) {
dev_set_promiscuity(lowerdev, -1);
goto hash_del;
}

dev_mc_unsync(lowerdev, dev);
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(lowerdev, -1);

dev_uc_del(lowerdev, dev->dev_addr);

hash_del:
macvlan_hash_del(vlan);
return 0;
}
Expand Down Expand Up @@ -559,6 +577,7 @@ static int macvlan_port_create(struct net_device *dev)
if (port == NULL)
return -ENOMEM;

port->passthru = false;
port->dev = dev;
INIT_LIST_HEAD(&port->vlans);
for (i = 0; i < MACVLAN_HASH_SIZE; i++)
Expand Down Expand Up @@ -603,6 +622,7 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
case MACVLAN_MODE_PRIVATE:
case MACVLAN_MODE_VEPA:
case MACVLAN_MODE_BRIDGE:
case MACVLAN_MODE_PASSTHRU:
break;
default:
return -EINVAL;
Expand Down Expand Up @@ -652,6 +672,10 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
}
port = macvlan_port_get(lowerdev);

/* Only 1 macvlan device can be created in passthru mode */
if (port->passthru)
return -EINVAL;

vlan->lowerdev = lowerdev;
vlan->dev = dev;
vlan->port = port;
Expand All @@ -662,6 +686,13 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
if (data && data[IFLA_MACVLAN_MODE])
vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);

if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
if (!list_empty(&port->vlans))
return -EINVAL;
port->passthru = true;
memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN);
}

err = register_netdevice(dev);
if (err < 0)
goto destroy_port;
Expand Down
1 change: 1 addition & 0 deletions include/linux/if_link.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ 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 */
MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
};

/* SR-IOV virtual function management section */
Expand Down

0 comments on commit eb06acd

Please sign in to comment.