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

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
Loading
Loading
Loading
Loading
+97 −45
Original line number Diff line number Diff line
@@ -623,6 +623,32 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
	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,
					  unsigned int off,
					  struct vxlanhdr *vh, size_t hdrlen,
@@ -649,26 +675,24 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
	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 sk_buff *skb)
						  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));

@@ -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)) {
		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 */
@@ -698,12 +722,48 @@ static struct sk_buff *vxlan_gro_receive(struct sock *sk,
		}
	}

	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;

	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;
}

@@ -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));
}

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)
@@ -1525,35 +1600,6 @@ static void vxlan_parse_gbp_hdr(struct vxlanhdr *unparsed,
	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,
			  struct vxlan_sock *vs,
			  struct sk_buff *skb, __be32 vni)
@@ -1655,8 +1701,9 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
	 * used by VXLAN extensions if explicitly requested.
	 */
	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;
		unparsed.vx_flags &= ~VXLAN_GPE_USED_BITS;
		raw_proto = true;
	}

@@ -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_err_lookup = vxlan_err_lookup;
	tunnel_cfg.encap_destroy = NULL;
	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);