1
0
Fork 0

net/packet: fix packet receive on L3 devices without visible hard header

In the patchset merged by commit b9fcf0a0d8
("Merge branch 'support-AF_PACKET-for-layer-3-devices'") L3 devices which
did not have header_ops were given one for the purpose of protocol parsing
on af_packet transmit path.

That change made af_packet receive path regard these devices as having a
visible L3 header and therefore aligned incoming skb->data to point to the
skb's mac_header. Some devices, such as ipip, xfrmi, and others, do not
reset their mac_header prior to ingress and therefore their incoming
packets became malformed.

Ideally these devices would reset their mac headers, or af_packet would be
able to rely on dev->hard_header_len being 0 for such cases, but it seems
this is not the case.

Fix by changing af_packet RX ll visibility criteria to include the
existence of a '.create()' header operation, which is used when creating
a device hard header - via dev_hard_header() - by upper layers, and does
not exist in these L3 devices.

As this predicate may be useful in other situations, add it as a common
dev_has_header() helper in netdevice.h.

Fixes: b9fcf0a0d8 ("Merge branch 'support-AF_PACKET-for-layer-3-devices'")
Signed-off-by: Eyal Birger <eyal.birger@gmail.com>
Acked-by: Jason A. Donenfeld <Jason@zx2c4.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Link: https://lore.kernel.org/r/20201121062817.3178900-1-eyal.birger@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
zero-sugar-mainline-defconfig
Eyal Birger 2020-11-21 08:28:17 +02:00 committed by Jakub Kicinski
parent 2980cbd4dc
commit d549699048
2 changed files with 14 additions and 9 deletions

View File

@ -3137,6 +3137,11 @@ static inline bool dev_validate_header(const struct net_device *dev,
return false; return false;
} }
static inline bool dev_has_header(const struct net_device *dev)
{
return dev->header_ops && dev->header_ops->create;
}
typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr,
int len, int size); int len, int size);
int register_gifconf(unsigned int family, gifconf_func_t *gifconf); int register_gifconf(unsigned int family, gifconf_func_t *gifconf);

View File

@ -93,8 +93,8 @@
/* /*
Assumptions: Assumptions:
- If the device has no dev->header_ops, there is no LL header visible - If the device has no dev->header_ops->create, there is no LL header
above the device. In this case, its hard_header_len should be 0. visible above the device. In this case, its hard_header_len should be 0.
The device may prepend its own header internally. In this case, its The device may prepend its own header internally. In this case, its
needed_headroom should be set to the space needed for it to add its needed_headroom should be set to the space needed for it to add its
internal header. internal header.
@ -108,26 +108,26 @@
On receive: On receive:
----------- -----------
Incoming, dev->header_ops != NULL Incoming, dev_has_header(dev) == true
mac_header -> ll header mac_header -> ll header
data -> data data -> data
Outgoing, dev->header_ops != NULL Outgoing, dev_has_header(dev) == true
mac_header -> ll header mac_header -> ll header
data -> ll header data -> ll header
Incoming, dev->header_ops == NULL Incoming, dev_has_header(dev) == false
mac_header -> data mac_header -> data
However drivers often make it point to the ll header. However drivers often make it point to the ll header.
This is incorrect because the ll header should be invisible to us. This is incorrect because the ll header should be invisible to us.
data -> data data -> data
Outgoing, dev->header_ops == NULL Outgoing, dev_has_header(dev) == false
mac_header -> data. ll header is invisible to us. mac_header -> data. ll header is invisible to us.
data -> data data -> data
Resume Resume
If dev->header_ops == NULL we are unable to restore the ll header, If dev_has_header(dev) == false we are unable to restore the ll header,
because it is invisible to us. because it is invisible to us.
@ -2069,7 +2069,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
skb->dev = dev; skb->dev = dev;
if (dev->header_ops) { if (dev_has_header(dev)) {
/* The device has an explicit notion of ll header, /* The device has an explicit notion of ll header,
* exported to higher levels. * exported to higher levels.
* *
@ -2198,7 +2198,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
if (!net_eq(dev_net(dev), sock_net(sk))) if (!net_eq(dev_net(dev), sock_net(sk)))
goto drop; goto drop;
if (dev->header_ops) { if (dev_has_header(dev)) {
if (sk->sk_type != SOCK_DGRAM) if (sk->sk_type != SOCK_DGRAM)
skb_push(skb, skb->data - skb_mac_header(skb)); skb_push(skb, skb->data - skb_mac_header(skb));
else if (skb->pkt_type == PACKET_OUTGOING) { else if (skb->pkt_type == PACKET_OUTGOING) {