aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2013-12-31 16:23:35 -0500
committerDavid S. Miller <davem@davemloft.net>2013-12-31 16:23:35 -0500
commit2205369a314e12fcec4781cc73ac9c08fc2b47de (patch)
tree5daa8338d99f62013631fc9c2ab1a6e6896ef0cd
parent71ce176ee6ed1735b9a1160a5704a915d13849b1 (diff)
vlan: Fix header ops passthru when doing TX VLAN offload.
When the vlan code detects that the real device can do TX VLAN offloads in hardware, it tries to arrange for the real device's header_ops to be invoked directly. But it does so illegally, by simply hooking the real device's header_ops up to the VLAN device. This doesn't work because we will end up invoking a set of header_ops routines which expect a device type which matches the real device, but will see a VLAN device instead. Fix this by providing a pass-thru set of header_ops which will arrange to pass the proper real device instead. To facilitate this add a dev_rebuild_header(). There are implementations which provide a ->cache and ->create but not a ->rebuild (f.e. PLIP). So we need a helper function just like dev_hard_header() to avoid crashes. Use this helper in the one existing place where the header_ops->rebuild was being invoked, the neighbour code. With lots of help from Florian Westphal. Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/netdevice.h9
-rw-r--r--net/8021q/vlan_dev.c19
-rw-r--r--net/core/neighbour.c2
3 files changed, 28 insertions, 2 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d9a550bf3e8e..7514b9c37a39 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1912,6 +1912,15 @@ static inline int dev_parse_header(const struct sk_buff *skb,
1912 return dev->header_ops->parse(skb, haddr); 1912 return dev->header_ops->parse(skb, haddr);
1913} 1913}
1914 1914
1915static inline int dev_rebuild_header(struct sk_buff *skb)
1916{
1917 const struct net_device *dev = skb->dev;
1918
1919 if (!dev->header_ops || !dev->header_ops->rebuild)
1920 return 0;
1921 return dev->header_ops->rebuild(skb);
1922}
1923
1915typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len); 1924typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len);
1916int register_gifconf(unsigned int family, gifconf_func_t *gifconf); 1925int register_gifconf(unsigned int family, gifconf_func_t *gifconf);
1917static inline int unregister_gifconf(unsigned int family) 1926static inline int unregister_gifconf(unsigned int family)
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 762896ebfcf5..47c908f1f626 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -530,6 +530,23 @@ static const struct header_ops vlan_header_ops = {
530 .parse = eth_header_parse, 530 .parse = eth_header_parse,
531}; 531};
532 532
533static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev,
534 unsigned short type,
535 const void *daddr, const void *saddr,
536 unsigned int len)
537{
538 struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
539 struct net_device *real_dev = vlan->real_dev;
540
541 return dev_hard_header(skb, real_dev, type, daddr, saddr, len);
542}
543
544static const struct header_ops vlan_passthru_header_ops = {
545 .create = vlan_passthru_hard_header,
546 .rebuild = dev_rebuild_header,
547 .parse = eth_header_parse,
548};
549
533static struct device_type vlan_type = { 550static struct device_type vlan_type = {
534 .name = "vlan", 551 .name = "vlan",
535}; 552};
@@ -573,7 +590,7 @@ static int vlan_dev_init(struct net_device *dev)
573 590
574 dev->needed_headroom = real_dev->needed_headroom; 591 dev->needed_headroom = real_dev->needed_headroom;
575 if (real_dev->features & NETIF_F_HW_VLAN_CTAG_TX) { 592 if (real_dev->features & NETIF_F_HW_VLAN_CTAG_TX) {
576 dev->header_ops = real_dev->header_ops; 593 dev->header_ops = &vlan_passthru_header_ops;
577 dev->hard_header_len = real_dev->hard_header_len; 594 dev->hard_header_len = real_dev->hard_header_len;
578 } else { 595 } else {
579 dev->header_ops = &vlan_header_ops; 596 dev->header_ops = &vlan_header_ops;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 36b1443f9ae4..932c6d7cf666 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1275,7 +1275,7 @@ int neigh_compat_output(struct neighbour *neigh, struct sk_buff *skb)
1275 1275
1276 if (dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, 1276 if (dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL,
1277 skb->len) < 0 && 1277 skb->len) < 0 &&
1278 dev->header_ops->rebuild(skb)) 1278 dev_rebuild_header(skb))
1279 return 0; 1279 return 0;
1280 1280
1281 return dev_queue_xmit(skb); 1281 return dev_queue_xmit(skb);