Commit 22117b3a authored by David S. Miller's avatar David S. Miller

Merge branch 'vxlan-gro-fixes'

Jiri Benc says:

====================
vxlan: fix GRO with VXLAN-GPE

The first patch generalizes code for the second patch, which is a fix for
broken VXLAN-GPE GRO. Thanks to Paolo for noticing the bug.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8d01da0a b0b672c4
...@@ -623,6 +623,32 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, ...@@ -623,6 +623,32 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
return 1; return 1;
} }
static bool vxlan_parse_gpe_proto(struct vxlanhdr *hdr, __be16 *protocol)
{
struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)hdr;
/* Need to have Next Protocol set for interfaces in GPE mode. */
if (!gpe->np_applied)
return false;
/* "The initial version is 0. If a receiver does not support the
* version indicated it MUST drop the packet.
*/
if (gpe->version != 0)
return false;
/* "When the O bit is set to 1, the packet is an OAM packet and OAM
* processing MUST occur." However, we don't implement OAM
* processing, thus drop the packet.
*/
if (gpe->oam_flag)
return false;
*protocol = tun_p_to_eth_p(gpe->next_protocol);
if (!*protocol)
return false;
return true;
}
static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
unsigned int off, unsigned int off,
struct vxlanhdr *vh, size_t hdrlen, struct vxlanhdr *vh, size_t hdrlen,
...@@ -649,26 +675,24 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, ...@@ -649,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));
...@@ -676,12 +700,12 @@ static struct sk_buff *vxlan_gro_receive(struct sock *sk, ...@@ -676,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 */
...@@ -698,12 +722,48 @@ static struct sk_buff *vxlan_gro_receive(struct sock *sk, ...@@ -698,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; }
out: 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); 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; return pp;
} }
...@@ -715,6 +775,21 @@ static int vxlan_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) ...@@ -715,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)
...@@ -1525,35 +1600,6 @@ static void vxlan_parse_gbp_hdr(struct vxlanhdr *unparsed, ...@@ -1525,35 +1600,6 @@ static void vxlan_parse_gbp_hdr(struct vxlanhdr *unparsed,
unparsed->vx_flags &= ~VXLAN_GBP_USED_BITS; unparsed->vx_flags &= ~VXLAN_GBP_USED_BITS;
} }
static bool vxlan_parse_gpe_hdr(struct vxlanhdr *unparsed,
__be16 *protocol,
struct sk_buff *skb, u32 vxflags)
{
struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)unparsed;
/* Need to have Next Protocol set for interfaces in GPE mode. */
if (!gpe->np_applied)
return false;
/* "The initial version is 0. If a receiver does not support the
* version indicated it MUST drop the packet.
*/
if (gpe->version != 0)
return false;
/* "When the O bit is set to 1, the packet is an OAM packet and OAM
* processing MUST occur." However, we don't implement OAM
* processing, thus drop the packet.
*/
if (gpe->oam_flag)
return false;
*protocol = tun_p_to_eth_p(gpe->next_protocol);
if (!*protocol)
return false;
unparsed->vx_flags &= ~VXLAN_GPE_USED_BITS;
return true;
}
static bool vxlan_set_mac(struct vxlan_dev *vxlan, static bool vxlan_set_mac(struct vxlan_dev *vxlan,
struct vxlan_sock *vs, struct vxlan_sock *vs,
struct sk_buff *skb, __be32 vni) struct sk_buff *skb, __be32 vni)
...@@ -1655,8 +1701,9 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -1655,8 +1701,9 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
* used by VXLAN extensions if explicitly requested. * used by VXLAN extensions if explicitly requested.
*/ */
if (vs->flags & VXLAN_F_GPE) { if (vs->flags & VXLAN_F_GPE) {
if (!vxlan_parse_gpe_hdr(&unparsed, &protocol, skb, vs->flags)) if (!vxlan_parse_gpe_proto(&unparsed, &protocol))
goto drop; goto drop;
unparsed.vx_flags &= ~VXLAN_GPE_USED_BITS;
raw_proto = true; raw_proto = true;
} }
...@@ -3378,8 +3425,13 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, ...@@ -3378,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);
......
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