* [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).