diff options
-rw-r--r-- | drivers/net/macvlan.c | 33 | ||||
-rw-r--r-- | include/linux/if_link.h | 1 |
2 files changed, 33 insertions, 1 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 93f0ba25c808..6ed577b065df 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c | |||
@@ -38,6 +38,7 @@ struct macvlan_port { | |||
38 | struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; | 38 | struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; |
39 | struct list_head vlans; | 39 | struct list_head vlans; |
40 | struct rcu_head rcu; | 40 | struct rcu_head rcu; |
41 | bool passthru; | ||
41 | }; | 42 | }; |
42 | 43 | ||
43 | #define macvlan_port_get_rcu(dev) \ | 44 | #define macvlan_port_get_rcu(dev) \ |
@@ -169,6 +170,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) | |||
169 | macvlan_broadcast(skb, port, NULL, | 170 | macvlan_broadcast(skb, port, NULL, |
170 | MACVLAN_MODE_PRIVATE | | 171 | MACVLAN_MODE_PRIVATE | |
171 | MACVLAN_MODE_VEPA | | 172 | MACVLAN_MODE_VEPA | |
173 | MACVLAN_MODE_PASSTHRU| | ||
172 | MACVLAN_MODE_BRIDGE); | 174 | MACVLAN_MODE_BRIDGE); |
173 | else if (src->mode == MACVLAN_MODE_VEPA) | 175 | else if (src->mode == MACVLAN_MODE_VEPA) |
174 | /* flood to everyone except source */ | 176 | /* flood to everyone except source */ |
@@ -185,7 +187,10 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) | |||
185 | return skb; | 187 | return skb; |
186 | } | 188 | } |
187 | 189 | ||
188 | vlan = macvlan_hash_lookup(port, eth->h_dest); | 190 | if (port->passthru) |
191 | vlan = list_first_entry(&port->vlans, struct macvlan_dev, list); | ||
192 | else | ||
193 | vlan = macvlan_hash_lookup(port, eth->h_dest); | ||
189 | if (vlan == NULL) | 194 | if (vlan == NULL) |
190 | return skb; | 195 | return skb; |
191 | 196 | ||
@@ -288,6 +293,11 @@ static int macvlan_open(struct net_device *dev) | |||
288 | struct net_device *lowerdev = vlan->lowerdev; | 293 | struct net_device *lowerdev = vlan->lowerdev; |
289 | int err; | 294 | int err; |
290 | 295 | ||
296 | if (vlan->port->passthru) { | ||
297 | dev_set_promiscuity(lowerdev, 1); | ||
298 | goto hash_add; | ||
299 | } | ||
300 | |||
291 | err = -EBUSY; | 301 | err = -EBUSY; |
292 | if (macvlan_addr_busy(vlan->port, dev->dev_addr)) | 302 | if (macvlan_addr_busy(vlan->port, dev->dev_addr)) |
293 | goto out; | 303 | goto out; |
@@ -300,6 +310,8 @@ static int macvlan_open(struct net_device *dev) | |||
300 | if (err < 0) | 310 | if (err < 0) |
301 | goto del_unicast; | 311 | goto del_unicast; |
302 | } | 312 | } |
313 | |||
314 | hash_add: | ||
303 | macvlan_hash_add(vlan); | 315 | macvlan_hash_add(vlan); |
304 | return 0; | 316 | return 0; |
305 | 317 | ||
@@ -314,12 +326,18 @@ static int macvlan_stop(struct net_device *dev) | |||
314 | struct macvlan_dev *vlan = netdev_priv(dev); | 326 | struct macvlan_dev *vlan = netdev_priv(dev); |
315 | struct net_device *lowerdev = vlan->lowerdev; | 327 | struct net_device *lowerdev = vlan->lowerdev; |
316 | 328 | ||
329 | if (vlan->port->passthru) { | ||
330 | dev_set_promiscuity(lowerdev, -1); | ||
331 | goto hash_del; | ||
332 | } | ||
333 | |||
317 | dev_mc_unsync(lowerdev, dev); | 334 | dev_mc_unsync(lowerdev, dev); |
318 | if (dev->flags & IFF_ALLMULTI) | 335 | if (dev->flags & IFF_ALLMULTI) |
319 | dev_set_allmulti(lowerdev, -1); | 336 | dev_set_allmulti(lowerdev, -1); |
320 | 337 | ||
321 | dev_uc_del(lowerdev, dev->dev_addr); | 338 | dev_uc_del(lowerdev, dev->dev_addr); |
322 | 339 | ||
340 | hash_del: | ||
323 | macvlan_hash_del(vlan); | 341 | macvlan_hash_del(vlan); |
324 | return 0; | 342 | return 0; |
325 | } | 343 | } |
@@ -559,6 +577,7 @@ static int macvlan_port_create(struct net_device *dev) | |||
559 | if (port == NULL) | 577 | if (port == NULL) |
560 | return -ENOMEM; | 578 | return -ENOMEM; |
561 | 579 | ||
580 | port->passthru = false; | ||
562 | port->dev = dev; | 581 | port->dev = dev; |
563 | INIT_LIST_HEAD(&port->vlans); | 582 | INIT_LIST_HEAD(&port->vlans); |
564 | for (i = 0; i < MACVLAN_HASH_SIZE; i++) | 583 | for (i = 0; i < MACVLAN_HASH_SIZE; i++) |
@@ -603,6 +622,7 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) | |||
603 | case MACVLAN_MODE_PRIVATE: | 622 | case MACVLAN_MODE_PRIVATE: |
604 | case MACVLAN_MODE_VEPA: | 623 | case MACVLAN_MODE_VEPA: |
605 | case MACVLAN_MODE_BRIDGE: | 624 | case MACVLAN_MODE_BRIDGE: |
625 | case MACVLAN_MODE_PASSTHRU: | ||
606 | break; | 626 | break; |
607 | default: | 627 | default: |
608 | return -EINVAL; | 628 | return -EINVAL; |
@@ -652,6 +672,10 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, | |||
652 | } | 672 | } |
653 | port = macvlan_port_get(lowerdev); | 673 | port = macvlan_port_get(lowerdev); |
654 | 674 | ||
675 | /* Only 1 macvlan device can be created in passthru mode */ | ||
676 | if (port->passthru) | ||
677 | return -EINVAL; | ||
678 | |||
655 | vlan->lowerdev = lowerdev; | 679 | vlan->lowerdev = lowerdev; |
656 | vlan->dev = dev; | 680 | vlan->dev = dev; |
657 | vlan->port = port; | 681 | vlan->port = port; |
@@ -662,6 +686,13 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, | |||
662 | if (data && data[IFLA_MACVLAN_MODE]) | 686 | if (data && data[IFLA_MACVLAN_MODE]) |
663 | vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); | 687 | vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); |
664 | 688 | ||
689 | if (vlan->mode == MACVLAN_MODE_PASSTHRU) { | ||
690 | if (!list_empty(&port->vlans)) | ||
691 | return -EINVAL; | ||
692 | port->passthru = true; | ||
693 | memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN); | ||
694 | } | ||
695 | |||
665 | err = register_netdevice(dev); | 696 | err = register_netdevice(dev); |
666 | if (err < 0) | 697 | if (err < 0) |
667 | goto destroy_port; | 698 | goto destroy_port; |
diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 2e02e4d7b11e..6485d2a89bec 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h | |||
@@ -259,6 +259,7 @@ enum macvlan_mode { | |||
259 | MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ | 259 | MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ |
260 | MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ | 260 | MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ |
261 | MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ | 261 | MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ |
262 | MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ | ||
262 | }; | 263 | }; |
263 | 264 | ||
264 | /* SR-IOV virtual function management section */ | 265 | /* SR-IOV virtual function management section */ |