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

Commit 87c48fa3 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

ipv6: make fragment identifications less predictable



IPv6 fragment identification generation is way beyond what we use for
IPv4 : It uses a single generator. Its not scalable and allows DOS
attacks.

Now inetpeer is IPv6 aware, we can use it to provide a more secure and
scalable frag ident generator (per destination, instead of system wide)

This patch :
1) defines a new secure_ipv6_id() helper
2) extends inet_getid() to provide 32bit results
3) extends ipv6_select_ident() with a new dest parameter

Reported-by: default avatarFernando Gont <fernando@gont.com.ar>
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 21efcfa0
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -1523,6 +1523,21 @@ __u32 secure_ip_id(__be32 daddr)
	return half_md4_transform(hash, keyptr->secret);
}

__u32 secure_ipv6_id(const __be32 daddr[4])
{
	const struct keydata *keyptr;
	__u32 hash[4];

	keyptr = get_keyptr();

	hash[0] = (__force __u32)daddr[0];
	hash[1] = (__force __u32)daddr[1];
	hash[2] = (__force __u32)daddr[2];
	hash[3] = (__force __u32)daddr[3];

	return half_md4_transform(hash, keyptr->secret);
}

#ifdef CONFIG_INET

__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ extern void get_random_bytes(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]);

extern __u32 secure_ip_id(__be32 daddr);
extern __u32 secure_ipv6_id(const __be32 daddr[4]);
extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
				      __be16 dport);
+10 −3
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ static inline bool inet_metrics_new(const struct inet_peer *p)
}

/* can be called with or without local BH being disabled */
struct inet_peer	*inet_getpeer(struct inetpeer_addr *daddr, int create);
struct inet_peer	*inet_getpeer(const struct inetpeer_addr *daddr, int create);

static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create)
{
@@ -106,11 +106,18 @@ static inline void inet_peer_refcheck(const struct inet_peer *p)


/* can be called with or without local BH being disabled */
static inline __u16	inet_getid(struct inet_peer *p, int more)
static inline int inet_getid(struct inet_peer *p, int more)
{
	int old, new;
	more++;
	inet_peer_refcheck(p);
	return atomic_add_return(more, &p->ip_id_count) - more;
	do {
		old = atomic_read(&p->ip_id_count);
		new = old + more;
		if (!new)
			new = 1;
	} while (atomic_cmpxchg(&p->ip_id_count, old, new) != old);
	return new;
}

#endif /* _NET_INETPEER_H */
+1 −11
Original line number Diff line number Diff line
@@ -463,17 +463,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add
	return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr));
}

static __inline__ void ipv6_select_ident(struct frag_hdr *fhdr)
{
	static u32 ipv6_fragmentation_id = 1;
	static DEFINE_SPINLOCK(ip6_id_lock);

	spin_lock_bh(&ip6_id_lock);
	fhdr->identification = htonl(ipv6_fragmentation_id);
	if (++ipv6_fragmentation_id == 0)
		ipv6_fragmentation_id = 1;
	spin_unlock_bh(&ip6_id_lock);
}
extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);

/*
 *	Prototypes exported by ipv6
+5 −2
Original line number Diff line number Diff line
@@ -391,7 +391,7 @@ static int inet_peer_gc(struct inet_peer_base *base,
	return cnt;
}

struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create)
struct inet_peer *inet_getpeer(const struct inetpeer_addr *daddr, int create)
{
	struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr;
	struct inet_peer_base *base = family_to_base(daddr->family);
@@ -436,7 +436,10 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create)
		p->daddr = *daddr;
		atomic_set(&p->refcnt, 1);
		atomic_set(&p->rid, 0);
		atomic_set(&p->ip_id_count, secure_ip_id(daddr->addr.a4));
		atomic_set(&p->ip_id_count,
				(daddr->family == AF_INET) ?
					secure_ip_id(daddr->addr.a4) :
					secure_ipv6_id(daddr->addr.a6));
		p->tcp_ts_stamp = 0;
		p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
		p->rate_tokens = 0;
Loading