Skip to content

Commit

Permalink
vxlan: fix GRO with VXLAN-GPE
Browse files Browse the repository at this point in the history
[ Upstream commit b0b672c ]

In VXLAN-GPE, there may not be an Ethernet header following the VXLAN
header. But in GRO, the vxlan driver calls eth_gro_receive
unconditionally, which means the following header is incorrectly parsed
as Ethernet.

Introduce GPE specific GRO handling.

For better performance, do not check for GPE during GRO but rather
install a different set of functions at setup time.

Fixes: e1e5314 ("vxlan: implement GPE")
Reported-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Jiri Benc <jbenc@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Jiri Benc authored and gregkh committed Aug 3, 2023
1 parent c3a497d commit bc1e4f9
Showing 1 changed file with 69 additions and 15 deletions.
84 changes: 69 additions & 15 deletions drivers/net/vxlan/vxlan_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -675,39 +675,37 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
return vh;
}

static struct sk_buff *vxlan_gro_receive(struct sock *sk,
struct list_head *head,
struct sk_buff *skb)
static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk,
struct list_head *head,
struct sk_buff *skb,
struct gro_remcsum *grc)
{
struct sk_buff *pp = NULL;
struct sk_buff *p;
struct vxlanhdr *vh, *vh2;
unsigned int hlen, off_vx;
int flush = 1;
struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk);
__be32 flags;
struct gro_remcsum grc;

skb_gro_remcsum_init(&grc);
skb_gro_remcsum_init(grc);

off_vx = skb_gro_offset(skb);
hlen = off_vx + sizeof(*vh);
vh = skb_gro_header(skb, hlen, off_vx);
if (unlikely(!vh))
goto out;
return NULL;

skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr));

flags = vh->vx_flags;

if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) {
vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr),
vh->vx_vni, &grc,
vh->vx_vni, grc,
!!(vs->flags &
VXLAN_F_REMCSUM_NOPARTIAL));

if (!vh)
goto out;
return NULL;
}

skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */
Expand All @@ -724,12 +722,48 @@ static struct sk_buff *vxlan_gro_receive(struct sock *sk,
}
}

pp = call_gro_receive(eth_gro_receive, head, skb);
flush = 0;
return vh;
}

static struct sk_buff *vxlan_gro_receive(struct sock *sk,
struct list_head *head,
struct sk_buff *skb)
{
struct sk_buff *pp = NULL;
struct gro_remcsum grc;
int flush = 1;

out:
if (vxlan_gro_prepare_receive(sk, head, skb, &grc)) {
pp = call_gro_receive(eth_gro_receive, head, skb);
flush = 0;
}
skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
return pp;
}

static struct sk_buff *vxlan_gpe_gro_receive(struct sock *sk,
struct list_head *head,
struct sk_buff *skb)
{
const struct packet_offload *ptype;
struct sk_buff *pp = NULL;
struct gro_remcsum grc;
struct vxlanhdr *vh;
__be16 protocol;
int flush = 1;

vh = vxlan_gro_prepare_receive(sk, head, skb, &grc);
if (vh) {
if (!vxlan_parse_gpe_proto(vh, &protocol))
goto out;
ptype = gro_find_receive_by_type(protocol);
if (!ptype)
goto out;
pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
flush = 0;
}
out:
skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
return pp;
}

Expand All @@ -741,6 +775,21 @@ static int vxlan_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
return eth_gro_complete(skb, nhoff + sizeof(struct vxlanhdr));
}

static int vxlan_gpe_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
{
struct vxlanhdr *vh = (struct vxlanhdr *)(skb->data + nhoff);
const struct packet_offload *ptype;
int err = -ENOSYS;
__be16 protocol;

if (!vxlan_parse_gpe_proto(vh, &protocol))
return err;
ptype = gro_find_complete_by_type(protocol);
if (ptype)
err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(struct vxlanhdr));
return err;
}

static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan, const u8 *mac,
__u16 state, __be32 src_vni,
__u16 ndm_flags)
Expand Down Expand Up @@ -3373,8 +3422,13 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
tunnel_cfg.encap_rcv = vxlan_rcv;
tunnel_cfg.encap_err_lookup = vxlan_err_lookup;
tunnel_cfg.encap_destroy = NULL;
tunnel_cfg.gro_receive = vxlan_gro_receive;
tunnel_cfg.gro_complete = vxlan_gro_complete;
if (vs->flags & VXLAN_F_GPE) {
tunnel_cfg.gro_receive = vxlan_gpe_gro_receive;
tunnel_cfg.gro_complete = vxlan_gpe_gro_complete;
} else {
tunnel_cfg.gro_receive = vxlan_gro_receive;
tunnel_cfg.gro_complete = vxlan_gro_complete;
}

setup_udp_tunnel_sock(net, sock, &tunnel_cfg);

Expand Down

0 comments on commit bc1e4f9

Please sign in to comment.