]>
Commit | Line | Data |
---|---|---|
963efa89 GKH |
1 | From foo@baz Mon Jan 13 09:28:30 PST 2014 |
2 | From: "David S. Miller" <davem@davemloft.net> | |
3 | Date: Tue, 31 Dec 2013 16:23:35 -0500 | |
4 | Subject: vlan: Fix header ops passthru when doing TX VLAN offload. | |
5 | ||
6 | From: "David S. Miller" <davem@davemloft.net> | |
7 | ||
8 | [ Upstream commit 2205369a314e12fcec4781cc73ac9c08fc2b47de ] | |
9 | ||
10 | When the vlan code detects that the real device can do TX VLAN offloads | |
11 | in hardware, it tries to arrange for the real device's header_ops to | |
12 | be invoked directly. | |
13 | ||
14 | But it does so illegally, by simply hooking the real device's | |
15 | header_ops up to the VLAN device. | |
16 | ||
17 | This doesn't work because we will end up invoking a set of header_ops | |
18 | routines which expect a device type which matches the real device, but | |
19 | will see a VLAN device instead. | |
20 | ||
21 | Fix this by providing a pass-thru set of header_ops which will arrange | |
22 | to pass the proper real device instead. | |
23 | ||
24 | To facilitate this add a dev_rebuild_header(). There are | |
25 | implementations which provide a ->cache and ->create but not a | |
26 | ->rebuild (f.e. PLIP). So we need a helper function just like | |
27 | dev_hard_header() to avoid crashes. | |
28 | ||
29 | Use this helper in the one existing place where the | |
30 | header_ops->rebuild was being invoked, the neighbour code. | |
31 | ||
32 | With lots of help from Florian Westphal. | |
33 | ||
34 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
35 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
36 | --- | |
37 | include/linux/netdevice.h | 9 +++++++++ | |
38 | net/8021q/vlan_dev.c | 19 ++++++++++++++++++- | |
39 | 2 files changed, 27 insertions(+), 1 deletion(-) | |
40 | ||
41 | --- a/include/linux/netdevice.h | |
42 | +++ b/include/linux/netdevice.h | |
43 | @@ -1702,6 +1702,15 @@ static inline int dev_parse_header(const | |
44 | return dev->header_ops->parse(skb, haddr); | |
45 | } | |
46 | ||
47 | +static inline int dev_rebuild_header(struct sk_buff *skb) | |
48 | +{ | |
49 | + const struct net_device *dev = skb->dev; | |
50 | + | |
51 | + if (!dev->header_ops || !dev->header_ops->rebuild) | |
52 | + return 0; | |
53 | + return dev->header_ops->rebuild(skb); | |
54 | +} | |
55 | + | |
56 | typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len); | |
57 | extern int register_gifconf(unsigned int family, gifconf_func_t * gifconf); | |
58 | static inline int unregister_gifconf(unsigned int family) | |
59 | --- a/net/8021q/vlan_dev.c | |
60 | +++ b/net/8021q/vlan_dev.c | |
61 | @@ -525,6 +525,23 @@ static const struct header_ops vlan_head | |
62 | .parse = eth_header_parse, | |
63 | }; | |
64 | ||
65 | +static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev, | |
66 | + unsigned short type, | |
67 | + const void *daddr, const void *saddr, | |
68 | + unsigned int len) | |
69 | +{ | |
70 | + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | |
71 | + struct net_device *real_dev = vlan->real_dev; | |
72 | + | |
73 | + return dev_hard_header(skb, real_dev, type, daddr, saddr, len); | |
74 | +} | |
75 | + | |
76 | +static const struct header_ops vlan_passthru_header_ops = { | |
77 | + .create = vlan_passthru_hard_header, | |
78 | + .rebuild = dev_rebuild_header, | |
79 | + .parse = eth_header_parse, | |
80 | +}; | |
81 | + | |
82 | static const struct net_device_ops vlan_netdev_ops; | |
83 | ||
84 | static int vlan_dev_init(struct net_device *dev) | |
85 | @@ -564,7 +581,7 @@ static int vlan_dev_init(struct net_devi | |
86 | ||
87 | dev->needed_headroom = real_dev->needed_headroom; | |
88 | if (real_dev->features & NETIF_F_HW_VLAN_TX) { | |
89 | - dev->header_ops = real_dev->header_ops; | |
90 | + dev->header_ops = &vlan_passthru_header_ops; | |
91 | dev->hard_header_len = real_dev->hard_header_len; | |
92 | } else { | |
93 | dev->header_ops = &vlan_header_ops; |