* [RFC] macvlan: proper multicast support
[not found] ` <49F1D5EF.3020306@trash.net>
@ 2009-04-24 16:48 ` Stephen Hemminger
2009-04-27 9:56 ` David Miller
0 siblings, 1 reply; 4+ messages in thread
From: Stephen Hemminger @ 2009-04-24 16:48 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netdev
When using macvlan's multicast packets don't get properly passed between
macvlan's which should be logically sharing the network. Any multicast
packet sent on a macvlan should show up lower device and all other macvlan's.
Likewise a multicast packet sent on lower device should be received
by all macvlan's.
The following is one way to do it; build tested only.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
---
drivers/net/macvlan.c | 69 +++++++++++++++++++++++++++++++--------------
include/linux/if_macvlan.h | 1
net/core/dev.c | 11 +++++++
3 files changed, 60 insertions(+), 21 deletions(-)
--- a/drivers/net/macvlan.c 2009-04-24 08:56:54.832616816 -0700
+++ b/drivers/net/macvlan.c 2009-04-24 09:47:31.617996161 -0700
@@ -101,14 +101,29 @@ static int macvlan_addr_busy(const struc
return 0;
}
+static int macvlan_clone(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sk_buff *nskb;
+
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ return -ENOMEM;
+
+ nskb->dev = dev;
+ nskb->pkt_type = is_broadcast_ether_addr(eth_hdr(nskb)->h_dest)
+ ? PACKET_BROADCAST : PACKET_MULTICAST;
+
+ netif_rx(nskb);
+ return 0;
+}
+
+
static void macvlan_broadcast(struct sk_buff *skb,
- const struct macvlan_port *port)
+ const struct macvlan_port *port,
+ const struct macvlan_dev *originator)
{
- 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;
if (skb->protocol == htons(ETH_P_PAUSE))
@@ -116,26 +131,18 @@ static void macvlan_broadcast(struct sk_
for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) {
- dev = vlan->dev;
+ struct net_device *dev = vlan->dev;
+ if (vlan == originator)
+ continue;
- nskb = skb_clone(skb, GFP_ATOMIC);
- if (nskb == NULL) {
+ if (macvlan_clone(skb, dev)) {
dev->stats.rx_errors++;
dev->stats.rx_dropped++;
- continue;
+ } else {
+ dev->stats.rx_bytes += skb->len + ETH_HLEN;
+ dev->stats.rx_packets++;
+ dev->stats.multicast++;
}
-
- dev->stats.rx_bytes += skb->len + ETH_HLEN;
- dev->stats.rx_packets++;
- dev->stats.multicast++;
-
- nskb->dev = dev;
- if (!compare_ether_addr(eth->h_dest, dev->broadcast))
- nskb->pkt_type = PACKET_BROADCAST;
- else
- nskb->pkt_type = PACKET_MULTICAST;
-
- netif_rx(nskb);
}
}
}
@@ -153,7 +160,7 @@ static struct sk_buff *macvlan_handle_fr
return skb;
if (is_multicast_ether_addr(eth->h_dest)) {
- macvlan_broadcast(skb, port);
+ macvlan_broadcast(skb, port, NULL);
return skb;
}
@@ -184,13 +191,31 @@ static struct sk_buff *macvlan_handle_fr
return NULL;
}
+/* called from dev_start_xmit if multicast is sent on lower device */
+static void macvlan_transmit_multicast(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ const struct ethhdr *eth = eth_hdr(skb);
+ const struct macvlan_port *port;
+
+ port = rcu_dereference(skb->dev->macvlan_port);
+ if (port && is_multicast_ether_addr(eth->h_dest))
+ macvlan_broadcast(skb, port, NULL);
+}
+
static int macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
const struct macvlan_dev *vlan = netdev_priv(dev);
+ const struct ethhdr *eth = eth_hdr(skb);
unsigned int len = skb->len;
int ret;
skb->dev = vlan->lowerdev;
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ macvlan_broadcast(skb, vlan->port, vlan);
+ macvlan_clone(skb, vlan->lowerdev);
+ }
+
ret = dev_queue_xmit(skb);
if (likely(ret == NET_XMIT_SUCCESS)) {
@@ -605,6 +630,7 @@ static int __init macvlan_init_module(vo
register_netdevice_notifier(&macvlan_notifier_block);
macvlan_handle_frame_hook = macvlan_handle_frame;
+ macvlan_transmit_hook = macvlan_transmit_multicast;
err = rtnl_link_register(&macvlan_link_ops);
if (err < 0)
@@ -620,6 +646,7 @@ static void __exit macvlan_cleanup_modul
{
rtnl_link_unregister(&macvlan_link_ops);
macvlan_handle_frame_hook = NULL;
+ macvlan_transmit_hook = NULL;
unregister_netdevice_notifier(&macvlan_notifier_block);
}
--- a/include/linux/if_macvlan.h 2009-04-24 09:10:45.056475851 -0700
+++ b/include/linux/if_macvlan.h 2009-04-24 09:11:06.884508032 -0700
@@ -2,5 +2,6 @@
#define _LINUX_IF_MACVLAN_H
extern struct sk_buff *(*macvlan_handle_frame_hook)(struct sk_buff *);
+extern void (*macvlan_transmit_hook)(struct net_device *, struct sk_buff *);
#endif /* _LINUX_IF_MACVLAN_H */
--- a/net/core/dev.c 2009-04-24 08:59:30.185453953 -0700
+++ b/net/core/dev.c 2009-04-24 09:47:25.371638613 -0700
@@ -1758,6 +1758,12 @@ static struct netdev_queue *dev_pick_tx(
return netdev_get_tx_queue(dev, queue_index);
}
+#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
+void (*macvlan_transmit_hook)(struct net_device *, struct sk_buff *)
+ __read_mostly;
+EXPORT_SYMBOL_GPL(macvlan_transmit_hook);
+#endif
+
/**
* dev_queue_xmit - transmit a buffer
* @skb: buffer to transmit
@@ -1818,6 +1824,11 @@ int dev_queue_xmit(struct sk_buff *skb)
goto out_kfree_skb;
}
+#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
+ if (unlikely(skb->dev->macvlan_port))
+ macvlan_transmit_hook(dev, skb);
+#endif
+
gso:
/* Disable soft irqs for various locks below. Also
* stops preemption for RCU.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [RFC] macvlan: proper multicast support
2009-04-24 16:48 ` [RFC] macvlan: proper multicast support Stephen Hemminger
@ 2009-04-27 9:56 ` David Miller
2009-05-05 13:15 ` Patrick McHardy
0 siblings, 1 reply; 4+ messages in thread
From: David Miller @ 2009-04-27 9:56 UTC (permalink / raw)
To: shemminger; +Cc: kaber, netdev
From: Stephen Hemminger <shemminger@vyatta.com>
Date: Fri, 24 Apr 2009 09:48:08 -0700
> When using macvlan's multicast packets don't get properly passed between
> macvlan's which should be logically sharing the network. Any multicast
> packet sent on a macvlan should show up lower device and all other macvlan's.
> Likewise a multicast packet sent on lower device should be received
> by all macvlan's.
>
> The following is one way to do it; build tested only.
>
> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
I definitely want to see what Patrick thinks of this.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [RFC] macvlan: proper multicast support
2009-04-27 9:56 ` David Miller
@ 2009-05-05 13:15 ` Patrick McHardy
2009-05-05 18:00 ` Stephen Hemminger
0 siblings, 1 reply; 4+ messages in thread
From: Patrick McHardy @ 2009-05-05 13:15 UTC (permalink / raw)
To: David Miller; +Cc: shemminger, netdev
David Miller wrote:
> From: Stephen Hemminger <shemminger@vyatta.com>
> Date: Fri, 24 Apr 2009 09:48:08 -0700
>
>> When using macvlan's multicast packets don't get properly passed between
>> macvlan's which should be logically sharing the network. Any multicast
>> packet sent on a macvlan should show up lower device and all other macvlan's.
>> Likewise a multicast packet sent on lower device should be received
>> by all macvlan's.
>>
>> The following is one way to do it; build tested only.
>>
>> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
>
> I definitely want to see what Patrick thinks of this.
From a functional POV the patch looks mostly fine to me, it seems
to simulate the behaviour of a real network accurately to the point
that you could probably create a loop by bridging two macvlans on
the same physical network.
This part looks incorrect however:
>> static int macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
>> {
>> const struct macvlan_dev *vlan = netdev_priv(dev);
>> + const struct ethhdr *eth = eth_hdr(skb);
>> unsigned int len = skb->len;
>> int ret;
>>
>> skb->dev = vlan->lowerdev;
>> + if (is_multicast_ether_addr(eth->h_dest)) {
>> + macvlan_broadcast(skb, vlan->port, vlan);
>> + macvlan_clone(skb, vlan->lowerdev);
>> + }
>> +
>> ret = dev_queue_xmit(skb);
>>
macvlan_clone() will netif_rx() the packet on the lower dev,
which means it will get passed back to the macvlan receive
hook for the same lower device. This will do two things:
- it will deliver the packet to all other macvlan devices on
the same physical device - which is fine but done twice with
the manual delivery
- it will deliver the packet to the originating macvlan device,
which is wrong
The first point is actually a good thing in my opinion, we need
less special handling and ordering of the reception events is
automatically correct.
The delivery to the originating device looks a bit harder, we
don't know the originating macvlan device when the packet is
delivered to the receive handler. I can't think of an easy way
to fix this right now.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [RFC] macvlan: proper multicast support
2009-05-05 13:15 ` Patrick McHardy
@ 2009-05-05 18:00 ` Stephen Hemminger
0 siblings, 0 replies; 4+ messages in thread
From: Stephen Hemminger @ 2009-05-05 18:00 UTC (permalink / raw)
To: Patrick McHardy; +Cc: David Miller, netdev
On Tue, 05 May 2009 15:15:41 +0200
Patrick McHardy <kaber@trash.net> wrote:
> David Miller wrote:
> > From: Stephen Hemminger <shemminger@vyatta.com>
> > Date: Fri, 24 Apr 2009 09:48:08 -0700
> >
> >> When using macvlan's multicast packets don't get properly passed between
> >> macvlan's which should be logically sharing the network. Any multicast
> >> packet sent on a macvlan should show up lower device and all other macvlan's.
> >> Likewise a multicast packet sent on lower device should be received
> >> by all macvlan's.
> >>
> >> The following is one way to do it; build tested only.
> >>
> >> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
> >
> > I definitely want to see what Patrick thinks of this.
>
> From a functional POV the patch looks mostly fine to me, it seems
> to simulate the behaviour of a real network accurately to the point
> that you could probably create a loop by bridging two macvlans on
> the same physical network.
>
> This part looks incorrect however:
>
> >> static int macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
> >> {
> >> const struct macvlan_dev *vlan = netdev_priv(dev);
> >> + const struct ethhdr *eth = eth_hdr(skb);
> >> unsigned int len = skb->len;
> >> int ret;
> >>
> >> skb->dev = vlan->lowerdev;
> >> + if (is_multicast_ether_addr(eth->h_dest)) {
> >> + macvlan_broadcast(skb, vlan->port, vlan);
> >> + macvlan_clone(skb, vlan->lowerdev);
> >> + }
> >> +
> >> ret = dev_queue_xmit(skb);
> >>
>
> macvlan_clone() will netif_rx() the packet on the lower dev,
> which means it will get passed back to the macvlan receive
> hook for the same lower device. This will do two things:
>
> - it will deliver the packet to all other macvlan devices on
> the same physical device - which is fine but done twice with
> the manual delivery
>
> - it will deliver the packet to the originating macvlan device,
> which is wrong
>
> The first point is actually a good thing in my opinion, we need
> less special handling and ordering of the reception events is
> automatically correct.
>
> The delivery to the originating device looks a bit harder, we
> don't know the originating macvlan device when the packet is
> delivered to the receive handler. I can't think of an easy way
> to fix this right now.
>
Okay, I'll go back and fix that. Also forwarding between
macvlan's is needed if it is to be useful in full container mode.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2009-05-05 18:00 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20090423091425.73ed544c@s6510>
[not found] ` <49F1D5EF.3020306@trash.net>
2009-04-24 16:48 ` [RFC] macvlan: proper multicast support Stephen Hemminger
2009-04-27 9:56 ` David Miller
2009-05-05 13:15 ` Patrick McHardy
2009-05-05 18:00 ` Stephen Hemminger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).