Commit d29d1c49 authored by David S. Miller's avatar David S. Miller

Merge branch 'AF_PACKET-transport_offset-fix'

Maxim Mikityanskiy says:

====================
AF_PACKET transport_offset fix

This patch series contains the implementation of the RFC that was posted
on this mailing list previously:
https://www.spinics.net/lists/netdev/msg541709.html

It fixes having incorrect skb->transport_header values in cases when
dissect fails. Having correct values set by the kernel fixes mlx5
operation and allows to remove some unnecessary code flows in mlx5.

v2 changes:

- Rebase against the fresh net-next.
- Don't return bool from skb_probe_transport_header (and don't rename
  the function).
- WARN_ON_ONCE and error path in case of GSO without the L4 header.
====================
Acked-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5328b633 41f5f63c
...@@ -148,12 +148,8 @@ static inline int mlx5e_skb_l2_header_offset(struct sk_buff *skb) ...@@ -148,12 +148,8 @@ static inline int mlx5e_skb_l2_header_offset(struct sk_buff *skb)
static inline int mlx5e_skb_l3_header_offset(struct sk_buff *skb) static inline int mlx5e_skb_l3_header_offset(struct sk_buff *skb)
{ {
struct flow_keys keys;
if (skb_transport_header_was_set(skb)) if (skb_transport_header_was_set(skb))
return skb_transport_offset(skb); return skb_transport_offset(skb);
else if (skb_flow_dissect_flow_keys(skb, &keys, 0))
return keys.control.thoff;
else else
return mlx5e_skb_l2_header_offset(skb); return mlx5e_skb_l2_header_offset(skb);
} }
...@@ -172,15 +168,8 @@ static inline u16 mlx5e_calc_min_inline(enum mlx5_inline_modes mode, ...@@ -172,15 +168,8 @@ static inline u16 mlx5e_calc_min_inline(enum mlx5_inline_modes mode,
hlen += VLAN_HLEN; hlen += VLAN_HLEN;
break; break;
case MLX5_INLINE_MODE_IP: case MLX5_INLINE_MODE_IP:
/* When transport header is set to zero, it means no transport hlen = mlx5e_skb_l3_header_offset(skb);
* header. When transport header is set to 0xff's, it means break;
* transport header wasn't set.
*/
if (skb_transport_offset(skb)) {
hlen = mlx5e_skb_l3_header_offset(skb);
break;
}
/* fall through */
case MLX5_INLINE_MODE_L2: case MLX5_INLINE_MODE_L2:
default: default:
hlen = mlx5e_skb_l2_header_offset(skb); hlen = mlx5e_skb_l2_header_offset(skb);
......
...@@ -712,7 +712,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, ...@@ -712,7 +712,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
goto err_kfree; goto err_kfree;
} }
skb_probe_transport_header(skb, ETH_HLEN); skb_probe_transport_header(skb);
/* Move network header to the right position for VLAN tagged packets */ /* Move network header to the right position for VLAN tagged packets */
if ((skb->protocol == htons(ETH_P_8021Q) || if ((skb->protocol == htons(ETH_P_8021Q) ||
...@@ -1187,7 +1187,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) ...@@ -1187,7 +1187,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
tap = rcu_dereference(q->tap); tap = rcu_dereference(q->tap);
if (tap) { if (tap) {
skb->dev = tap->dev; skb->dev = tap->dev;
skb_probe_transport_header(skb, ETH_HLEN); skb_probe_transport_header(skb);
dev_queue_xmit(skb); dev_queue_xmit(skb);
} else { } else {
kfree_skb(skb); kfree_skb(skb);
......
...@@ -1929,7 +1929,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1929,7 +1929,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
} }
skb_reset_network_header(skb); skb_reset_network_header(skb);
skb_probe_transport_header(skb, 0); skb_probe_transport_header(skb);
if (skb_xdp) { if (skb_xdp) {
struct bpf_prog *xdp_prog; struct bpf_prog *xdp_prog;
...@@ -2482,7 +2482,7 @@ static int tun_xdp_one(struct tun_struct *tun, ...@@ -2482,7 +2482,7 @@ static int tun_xdp_one(struct tun_struct *tun,
skb->protocol = eth_type_trans(skb, tun->dev); skb->protocol = eth_type_trans(skb, tun->dev);
skb_reset_network_header(skb); skb_reset_network_header(skb);
skb_probe_transport_header(skb, 0); skb_probe_transport_header(skb);
if (skb_xdp) { if (skb_xdp) {
err = do_xdp_generic(xdp_prog, skb); err = do_xdp_generic(xdp_prog, skb);
......
...@@ -1169,15 +1169,24 @@ static int xenvif_tx_submit(struct xenvif_queue *queue) ...@@ -1169,15 +1169,24 @@ static int xenvif_tx_submit(struct xenvif_queue *queue)
continue; continue;
} }
skb_probe_transport_header(skb, 0); skb_probe_transport_header(skb);
/* If the packet is GSO then we will have just set up the /* If the packet is GSO then we will have just set up the
* transport header offset in checksum_setup so it's now * transport header offset in checksum_setup so it's now
* straightforward to calculate gso_segs. * straightforward to calculate gso_segs.
*/ */
if (skb_is_gso(skb)) { if (skb_is_gso(skb)) {
int mss = skb_shinfo(skb)->gso_size; int mss, hdrlen;
int hdrlen = skb_transport_header(skb) -
/* GSO implies having the L4 header. */
WARN_ON_ONCE(!skb_transport_header_was_set(skb));
if (unlikely(!skb_transport_header_was_set(skb))) {
kfree_skb(skb);
continue;
}
mss = skb_shinfo(skb)->gso_size;
hdrlen = skb_transport_header(skb) -
skb_mac_header(skb) + skb_mac_header(skb) +
tcp_hdrlen(skb); tcp_hdrlen(skb);
......
...@@ -44,6 +44,7 @@ int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, ...@@ -44,6 +44,7 @@ int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh,
__be16 type); __be16 type);
void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev, void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev,
const unsigned char *haddr); const unsigned char *haddr);
__be16 eth_header_parse_protocol(const struct sk_buff *skb);
int eth_prepare_mac_addr_change(struct net_device *dev, void *p); int eth_prepare_mac_addr_change(struct net_device *dev, void *p);
void eth_commit_mac_addr_change(struct net_device *dev, void *p); void eth_commit_mac_addr_change(struct net_device *dev, void *p);
int eth_mac_addr(struct net_device *dev, void *p); int eth_mac_addr(struct net_device *dev, void *p);
......
...@@ -274,6 +274,7 @@ struct header_ops { ...@@ -274,6 +274,7 @@ struct header_ops {
const struct net_device *dev, const struct net_device *dev,
const unsigned char *haddr); const unsigned char *haddr);
bool (*validate)(const char *ll_header, unsigned int len); bool (*validate)(const char *ll_header, unsigned int len);
__be16 (*parse_protocol)(const struct sk_buff *skb);
}; };
/* These flag bits are private to the generic network queueing /* These flag bits are private to the generic network queueing
...@@ -2939,6 +2940,15 @@ static inline int dev_parse_header(const struct sk_buff *skb, ...@@ -2939,6 +2940,15 @@ static inline int dev_parse_header(const struct sk_buff *skb,
return dev->header_ops->parse(skb, haddr); return dev->header_ops->parse(skb, haddr);
} }
static inline __be16 dev_parse_header_protocol(const struct sk_buff *skb)
{
const struct net_device *dev = skb->dev;
if (!dev->header_ops || !dev->header_ops->parse_protocol)
return 0;
return dev->header_ops->parse_protocol(skb);
}
/* ll_header must have at least hard_header_len allocated */ /* ll_header must have at least hard_header_len allocated */
static inline bool dev_validate_header(const struct net_device *dev, static inline bool dev_validate_header(const struct net_device *dev,
char *ll_header, int len) char *ll_header, int len)
......
...@@ -2429,8 +2429,7 @@ static inline void skb_pop_mac_header(struct sk_buff *skb) ...@@ -2429,8 +2429,7 @@ static inline void skb_pop_mac_header(struct sk_buff *skb)
skb->mac_header = skb->network_header; skb->mac_header = skb->network_header;
} }
static inline void skb_probe_transport_header(struct sk_buff *skb, static inline void skb_probe_transport_header(struct sk_buff *skb)
const int offset_hint)
{ {
struct flow_keys_basic keys; struct flow_keys_basic keys;
...@@ -2439,8 +2438,6 @@ static inline void skb_probe_transport_header(struct sk_buff *skb, ...@@ -2439,8 +2438,6 @@ static inline void skb_probe_transport_header(struct sk_buff *skb,
if (skb_flow_dissect_flow_keys_basic(skb, &keys, NULL, 0, 0, 0, 0)) if (skb_flow_dissect_flow_keys_basic(skb, &keys, NULL, 0, 0, 0, 0))
skb_set_transport_header(skb, keys.control.thoff); skb_set_transport_header(skb, keys.control.thoff);
else if (offset_hint >= 0)
skb_set_transport_header(skb, offset_hint);
} }
static inline void skb_mac_header_rebuild(struct sk_buff *skb) static inline void skb_mac_header_rebuild(struct sk_buff *skb)
......
...@@ -62,7 +62,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, ...@@ -62,7 +62,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
* probe and drop if does not match one of the above types. * probe and drop if does not match one of the above types.
*/ */
if (gso_type) { if (gso_type) {
skb_probe_transport_header(skb, -1); skb_probe_transport_header(skb);
if (!skb_transport_header_was_set(skb)) if (!skb_transport_header_was_set(skb))
return -EINVAL; return -EINVAL;
} }
......
...@@ -264,6 +264,18 @@ void eth_header_cache_update(struct hh_cache *hh, ...@@ -264,6 +264,18 @@ void eth_header_cache_update(struct hh_cache *hh,
} }
EXPORT_SYMBOL(eth_header_cache_update); EXPORT_SYMBOL(eth_header_cache_update);
/**
* eth_header_parser_protocol - extract protocol from L2 header
* @skb: packet to extract protocol from
*/
__be16 eth_header_parse_protocol(const struct sk_buff *skb)
{
const struct ethhdr *eth = eth_hdr(skb);
return eth->h_proto;
}
EXPORT_SYMBOL(eth_header_parse_protocol);
/** /**
* eth_prepare_mac_addr_change - prepare for mac change * eth_prepare_mac_addr_change - prepare for mac change
* @dev: network device * @dev: network device
...@@ -346,6 +358,7 @@ const struct header_ops eth_header_ops ____cacheline_aligned = { ...@@ -346,6 +358,7 @@ const struct header_ops eth_header_ops ____cacheline_aligned = {
.parse = eth_header_parse, .parse = eth_header_parse,
.cache = eth_header_cache, .cache = eth_header_cache,
.cache_update = eth_header_cache_update, .cache_update = eth_header_cache_update,
.parse_protocol = eth_header_parse_protocol,
}; };
/** /**
......
...@@ -1850,6 +1850,15 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, ...@@ -1850,6 +1850,15 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev,
return 0; return 0;
} }
static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)
{
if (!skb->protocol && sock->type == SOCK_RAW) {
skb_reset_mac_header(skb);
skb->protocol = dev_parse_header_protocol(skb);
}
skb_probe_transport_header(skb);
}
/* /*
* Output a raw packet to a device layer. This bypasses all the other * Output a raw packet to a device layer. This bypasses all the other
...@@ -1970,7 +1979,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, ...@@ -1970,7 +1979,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
if (unlikely(extra_len == 4)) if (unlikely(extra_len == 4))
skb->no_fcs = 1; skb->no_fcs = 1;
skb_probe_transport_header(skb, 0); packet_parse_headers(skb, sock);
dev_queue_xmit(skb); dev_queue_xmit(skb);
rcu_read_unlock(); rcu_read_unlock();
...@@ -2404,15 +2413,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb) ...@@ -2404,15 +2413,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
sock_wfree(skb); sock_wfree(skb);
} }
static void tpacket_set_protocol(const struct net_device *dev,
struct sk_buff *skb)
{
if (dev->type == ARPHRD_ETHER) {
skb_reset_mac_header(skb);
skb->protocol = eth_hdr(skb)->h_proto;
}
}
static int __packet_snd_vnet_parse(struct virtio_net_hdr *vnet_hdr, size_t len) static int __packet_snd_vnet_parse(struct virtio_net_hdr *vnet_hdr, size_t len)
{ {
if ((vnet_hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && if ((vnet_hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
...@@ -2483,8 +2483,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, ...@@ -2483,8 +2483,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
return err; return err;
if (!dev_validate_header(dev, skb->data, hdrlen)) if (!dev_validate_header(dev, skb->data, hdrlen))
return -EINVAL; return -EINVAL;
if (!skb->protocol)
tpacket_set_protocol(dev, skb);
data += hdrlen; data += hdrlen;
to_write -= hdrlen; to_write -= hdrlen;
...@@ -2519,7 +2517,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, ...@@ -2519,7 +2517,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
len = ((to_write > len_max) ? len_max : to_write); len = ((to_write > len_max) ? len_max : to_write);
} }
skb_probe_transport_header(skb, 0); packet_parse_headers(skb, sock);
return tp_len; return tp_len;
} }
...@@ -2925,7 +2923,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) ...@@ -2925,7 +2923,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
virtio_net_hdr_set_proto(skb, &vnet_hdr); virtio_net_hdr_set_proto(skb, &vnet_hdr);
} }
skb_probe_transport_header(skb, reserve); packet_parse_headers(skb, sock);
if (unlikely(extra_len == 4)) if (unlikely(extra_len == 4))
skb->no_fcs = 1; skb->no_fcs = 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment