Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eec2e618 authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by David S. Miller
Browse files

ipv6: implement RFC3168 5.3 (ecn protection) for ipv6 fragmentation handling



Hello!

After patch 1 got accepted to net-next I will also send a patch to
netfilter-devel to make the corresponding changes to the netfilter
reassembly logic.

Thanks,

  Hannes

-- >8 --
[PATCH 2/2] ipv6: implement RFC3168 5.3 (ecn protection) for ipv6 fragmentation handling

This patch also ensures that INET_ECN_CE is propagated if one fragment
had the codepoint set.

Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Jesper Dangaard Brouer <jbrouer@redhat.com>
Cc: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: default avatarYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent be991971
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -478,6 +478,7 @@ struct ip6_create_arg {
	u32 user;
	u32 user;
	const struct in6_addr *src;
	const struct in6_addr *src;
	const struct in6_addr *dst;
	const struct in6_addr *dst;
	u8 ecn;
};
};


void ip6_frag_init(struct inet_frag_queue *q, void *a);
void ip6_frag_init(struct inet_frag_queue *q, void *a);
@@ -497,6 +498,7 @@ struct frag_queue {
	int			iif;
	int			iif;
	unsigned int		csum;
	unsigned int		csum;
	__u16			nhoffset;
	__u16			nhoffset;
	u8			ecn;
};
};


void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,
void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,
+21 −2
Original line number Original line Diff line number Diff line
@@ -58,6 +58,7 @@
#include <net/ndisc.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
#include <net/addrconf.h>
#include <net/inet_frag.h>
#include <net/inet_frag.h>
#include <net/inet_ecn.h>


struct ip6frag_skb_cb
struct ip6frag_skb_cb
{
{
@@ -67,6 +68,10 @@ struct ip6frag_skb_cb


#define FRAG6_CB(skb)	((struct ip6frag_skb_cb*)((skb)->cb))
#define FRAG6_CB(skb)	((struct ip6frag_skb_cb*)((skb)->cb))


static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
{
	return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
}


static struct inet_frags ip6_frags;
static struct inet_frags ip6_frags;


@@ -119,6 +124,7 @@ void ip6_frag_init(struct inet_frag_queue *q, void *a)
	fq->user = arg->user;
	fq->user = arg->user;
	fq->saddr = *arg->src;
	fq->saddr = *arg->src;
	fq->daddr = *arg->dst;
	fq->daddr = *arg->dst;
	fq->ecn = arg->ecn;
}
}
EXPORT_SYMBOL(ip6_frag_init);
EXPORT_SYMBOL(ip6_frag_init);


@@ -173,7 +179,8 @@ static void ip6_frag_expire(unsigned long data)
}
}


static __inline__ struct frag_queue *
static __inline__ struct frag_queue *
fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6_addr *dst)
fq_find(struct net *net, __be32 id, const struct in6_addr *src,
	const struct in6_addr *dst, u8 ecn)
{
{
	struct inet_frag_queue *q;
	struct inet_frag_queue *q;
	struct ip6_create_arg arg;
	struct ip6_create_arg arg;
@@ -183,6 +190,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6
	arg.user = IP6_DEFRAG_LOCAL_DELIVER;
	arg.user = IP6_DEFRAG_LOCAL_DELIVER;
	arg.src = src;
	arg.src = src;
	arg.dst = dst;
	arg.dst = dst;
	arg.ecn = ecn;


	read_lock(&ip6_frags.lock);
	read_lock(&ip6_frags.lock);
	hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
	hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
@@ -202,6 +210,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
	struct net_device *dev;
	struct net_device *dev;
	int offset, end;
	int offset, end;
	struct net *net = dev_net(skb_dst(skb)->dev);
	struct net *net = dev_net(skb_dst(skb)->dev);
	u8 ecn;


	if (fq->q.last_in & INET_FRAG_COMPLETE)
	if (fq->q.last_in & INET_FRAG_COMPLETE)
		goto err;
		goto err;
@@ -219,6 +228,8 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
		return -1;
		return -1;
	}
	}


	ecn = ip6_frag_ecn(ipv6_hdr(skb));

	if (skb->ip_summed == CHECKSUM_COMPLETE) {
	if (skb->ip_summed == CHECKSUM_COMPLETE) {
		const unsigned char *nh = skb_network_header(skb);
		const unsigned char *nh = skb_network_header(skb);
		skb->csum = csum_sub(skb->csum,
		skb->csum = csum_sub(skb->csum,
@@ -319,6 +330,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
	}
	}
	fq->q.stamp = skb->tstamp;
	fq->q.stamp = skb->tstamp;
	fq->q.meat += skb->len;
	fq->q.meat += skb->len;
	fq->ecn |= ecn;
	add_frag_mem_limit(&fq->q, skb->truesize);
	add_frag_mem_limit(&fq->q, skb->truesize);


	/* The first fragment.
	/* The first fragment.
@@ -362,9 +374,14 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
	int    payload_len;
	int    payload_len;
	unsigned int nhoff;
	unsigned int nhoff;
	int sum_truesize;
	int sum_truesize;
	u8 ecn;


	inet_frag_kill(&fq->q, &ip6_frags);
	inet_frag_kill(&fq->q, &ip6_frags);


	ecn = ip_frag_ecn_table[fq->ecn];
	if (unlikely(ecn == 0xff))
		goto out_fail;

	/* Make the one we just received the head. */
	/* Make the one we just received the head. */
	if (prev) {
	if (prev) {
		head = prev->next;
		head = prev->next;
@@ -463,6 +480,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
	head->dev = dev;
	head->dev = dev;
	head->tstamp = fq->q.stamp;
	head->tstamp = fq->q.stamp;
	ipv6_hdr(head)->payload_len = htons(payload_len);
	ipv6_hdr(head)->payload_len = htons(payload_len);
	ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
	IP6CB(head)->nhoff = nhoff;
	IP6CB(head)->nhoff = nhoff;


	/* Yes, and fold redundant checksum back. 8) */
	/* Yes, and fold redundant checksum back. 8) */
@@ -526,7 +544,8 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
		IP6_ADD_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
		IP6_ADD_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
				 IPSTATS_MIB_REASMFAILS, evicted);
				 IPSTATS_MIB_REASMFAILS, evicted);


	fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr);
	fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr,
		     ip6_frag_ecn(hdr));
	if (fq != NULL) {
	if (fq != NULL) {
		int ret;
		int ret;