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: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-02-22 12:55:32 -08:00
commit d29d1c4957
10 changed files with 56 additions and 39 deletions

View File

@ -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)
{
struct flow_keys keys;
if (skb_transport_header_was_set(skb))
return skb_transport_offset(skb);
else if (skb_flow_dissect_flow_keys(skb, &keys, 0))
return keys.control.thoff;
else
return mlx5e_skb_l2_header_offset(skb);
}
@ -172,15 +168,8 @@ static inline u16 mlx5e_calc_min_inline(enum mlx5_inline_modes mode,
hlen += VLAN_HLEN;
break;
case MLX5_INLINE_MODE_IP:
/* When transport header is set to zero, it means no transport
* header. When transport header is set to 0xff's, it means
* transport header wasn't set.
*/
if (skb_transport_offset(skb)) {
hlen = mlx5e_skb_l3_header_offset(skb);
break;
}
/* fall through */
hlen = mlx5e_skb_l3_header_offset(skb);
break;
case MLX5_INLINE_MODE_L2:
default:
hlen = mlx5e_skb_l2_header_offset(skb);

View File

@ -712,7 +712,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
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 */
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)
tap = rcu_dereference(q->tap);
if (tap) {
skb->dev = tap->dev;
skb_probe_transport_header(skb, ETH_HLEN);
skb_probe_transport_header(skb);
dev_queue_xmit(skb);
} else {
kfree_skb(skb);

View File

@ -1929,7 +1929,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
}
skb_reset_network_header(skb);
skb_probe_transport_header(skb, 0);
skb_probe_transport_header(skb);
if (skb_xdp) {
struct bpf_prog *xdp_prog;
@ -2482,7 +2482,7 @@ static int tun_xdp_one(struct tun_struct *tun,
skb->protocol = eth_type_trans(skb, tun->dev);
skb_reset_network_header(skb);
skb_probe_transport_header(skb, 0);
skb_probe_transport_header(skb);
if (skb_xdp) {
err = do_xdp_generic(xdp_prog, skb);

View File

@ -1169,15 +1169,24 @@ static int xenvif_tx_submit(struct xenvif_queue *queue)
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
* transport header offset in checksum_setup so it's now
* straightforward to calculate gso_segs.
*/
if (skb_is_gso(skb)) {
int mss = skb_shinfo(skb)->gso_size;
int hdrlen = skb_transport_header(skb) -
int mss, hdrlen;
/* 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) +
tcp_hdrlen(skb);

View File

@ -44,6 +44,7 @@ int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh,
__be16 type);
void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev,
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);
void eth_commit_mac_addr_change(struct net_device *dev, void *p);
int eth_mac_addr(struct net_device *dev, void *p);

View File

@ -274,6 +274,7 @@ struct header_ops {
const struct net_device *dev,
const unsigned char *haddr);
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
@ -2939,6 +2940,15 @@ static inline int dev_parse_header(const struct sk_buff *skb,
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 */
static inline bool dev_validate_header(const struct net_device *dev,
char *ll_header, int len)

View File

@ -2429,8 +2429,7 @@ static inline void skb_pop_mac_header(struct sk_buff *skb)
skb->mac_header = skb->network_header;
}
static inline void skb_probe_transport_header(struct sk_buff *skb,
const int offset_hint)
static inline void skb_probe_transport_header(struct sk_buff *skb)
{
struct flow_keys_basic keys;
@ -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))
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)

View File

@ -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.
*/
if (gso_type) {
skb_probe_transport_header(skb, -1);
skb_probe_transport_header(skb);
if (!skb_transport_header_was_set(skb))
return -EINVAL;
}

View File

@ -264,6 +264,18 @@ void eth_header_cache_update(struct hh_cache *hh,
}
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
* @dev: network device
@ -346,6 +358,7 @@ const struct header_ops eth_header_ops ____cacheline_aligned = {
.parse = eth_header_parse,
.cache = eth_header_cache,
.cache_update = eth_header_cache_update,
.parse_protocol = eth_header_parse_protocol,
};
/**

View File

@ -1850,6 +1850,15 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev,
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
@ -1970,7 +1979,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
if (unlikely(extra_len == 4))
skb->no_fcs = 1;
skb_probe_transport_header(skb, 0);
packet_parse_headers(skb, sock);
dev_queue_xmit(skb);
rcu_read_unlock();
@ -2404,15 +2413,6 @@ static void tpacket_destruct_skb(struct sk_buff *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)
{
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,
return err;
if (!dev_validate_header(dev, skb->data, hdrlen))
return -EINVAL;
if (!skb->protocol)
tpacket_set_protocol(dev, skb);
data += hdrlen;
to_write -= hdrlen;
@ -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);
}
skb_probe_transport_header(skb, 0);
packet_parse_headers(skb, sock);
return tp_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);
}
skb_probe_transport_header(skb, reserve);
packet_parse_headers(skb, sock);
if (unlikely(extra_len == 4))
skb->no_fcs = 1;