mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-07 02:19:54 +00:00
vxlan: fix GRO with VXLAN-GPE
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: e1e5314de0 ("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>
This commit is contained in:
committed by
David S. Miller
parent
17a0a64448
commit
b0b672c4d0
@@ -675,26 +675,24 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
|
|||||||
return vh;
|
return vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sk_buff *vxlan_gro_receive(struct sock *sk,
|
static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk,
|
||||||
struct list_head *head,
|
struct list_head *head,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb,
|
||||||
|
struct gro_remcsum *grc)
|
||||||
{
|
{
|
||||||
struct sk_buff *pp = NULL;
|
|
||||||
struct sk_buff *p;
|
struct sk_buff *p;
|
||||||
struct vxlanhdr *vh, *vh2;
|
struct vxlanhdr *vh, *vh2;
|
||||||
unsigned int hlen, off_vx;
|
unsigned int hlen, off_vx;
|
||||||
int flush = 1;
|
|
||||||
struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk);
|
struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk);
|
||||||
__be32 flags;
|
__be32 flags;
|
||||||
struct gro_remcsum grc;
|
|
||||||
|
|
||||||
skb_gro_remcsum_init(&grc);
|
skb_gro_remcsum_init(grc);
|
||||||
|
|
||||||
off_vx = skb_gro_offset(skb);
|
off_vx = skb_gro_offset(skb);
|
||||||
hlen = off_vx + sizeof(*vh);
|
hlen = off_vx + sizeof(*vh);
|
||||||
vh = skb_gro_header(skb, hlen, off_vx);
|
vh = skb_gro_header(skb, hlen, off_vx);
|
||||||
if (unlikely(!vh))
|
if (unlikely(!vh))
|
||||||
goto out;
|
return NULL;
|
||||||
|
|
||||||
skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr));
|
skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr));
|
||||||
|
|
||||||
@@ -702,12 +700,12 @@ static struct sk_buff *vxlan_gro_receive(struct sock *sk,
|
|||||||
|
|
||||||
if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) {
|
if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) {
|
||||||
vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr),
|
vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr),
|
||||||
vh->vx_vni, &grc,
|
vh->vx_vni, grc,
|
||||||
!!(vs->flags &
|
!!(vs->flags &
|
||||||
VXLAN_F_REMCSUM_NOPARTIAL));
|
VXLAN_F_REMCSUM_NOPARTIAL));
|
||||||
|
|
||||||
if (!vh)
|
if (!vh)
|
||||||
goto out;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */
|
skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */
|
||||||
@@ -724,12 +722,48 @@ static struct sk_buff *vxlan_gro_receive(struct sock *sk,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pp = call_gro_receive(eth_gro_receive, head, skb);
|
return vh;
|
||||||
flush = 0;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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:
|
out:
|
||||||
skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
|
skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
|
||||||
|
|
||||||
return pp;
|
return pp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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));
|
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,
|
static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan, const u8 *mac,
|
||||||
__u16 state, __be32 src_vni,
|
__u16 state, __be32 src_vni,
|
||||||
__u16 ndm_flags)
|
__u16 ndm_flags)
|
||||||
@@ -3376,8 +3425,13 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
|
|||||||
tunnel_cfg.encap_rcv = vxlan_rcv;
|
tunnel_cfg.encap_rcv = vxlan_rcv;
|
||||||
tunnel_cfg.encap_err_lookup = vxlan_err_lookup;
|
tunnel_cfg.encap_err_lookup = vxlan_err_lookup;
|
||||||
tunnel_cfg.encap_destroy = NULL;
|
tunnel_cfg.encap_destroy = NULL;
|
||||||
tunnel_cfg.gro_receive = vxlan_gro_receive;
|
if (vs->flags & VXLAN_F_GPE) {
|
||||||
tunnel_cfg.gro_complete = vxlan_gro_complete;
|
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);
|
setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user