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

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
...@@ -1523,6 +1523,21 @@ __u32 secure_ip_id(__be32 daddr) ...@@ -1523,6 +1523,21 @@ __u32 secure_ip_id(__be32 daddr)
return half_md4_transform(hash, keyptr->secret); 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 #ifdef CONFIG_INET
__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
......
...@@ -58,6 +58,7 @@ extern void get_random_bytes(void *buf, int nbytes); ...@@ -58,6 +58,7 @@ extern void get_random_bytes(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]); void generate_random_uuid(unsigned char uuid_out[16]);
extern __u32 secure_ip_id(__be32 daddr); 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_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
__be16 dport); __be16 dport);
......
...@@ -71,7 +71,7 @@ static inline bool inet_metrics_new(const struct inet_peer *p) ...@@ -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 */ /* 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) 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) ...@@ -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 */ /* 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++; more++;
inet_peer_refcheck(p); 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 */ #endif /* _NET_INETPEER_H */
...@@ -463,17 +463,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add ...@@ -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)); return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr));
} }
static __inline__ void ipv6_select_ident(struct frag_hdr *fhdr) extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
{
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);
}
/* /*
* Prototypes exported by ipv6 * Prototypes exported by ipv6
......
...@@ -391,7 +391,7 @@ static int inet_peer_gc(struct inet_peer_base *base, ...@@ -391,7 +391,7 @@ static int inet_peer_gc(struct inet_peer_base *base,
return cnt; 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 __rcu **stack[PEER_MAXDEPTH], ***stackptr;
struct inet_peer_base *base = family_to_base(daddr->family); 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) ...@@ -436,7 +436,10 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create)
p->daddr = *daddr; p->daddr = *daddr;
atomic_set(&p->refcnt, 1); atomic_set(&p->refcnt, 1);
atomic_set(&p->rid, 0); 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->tcp_ts_stamp = 0;
p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
p->rate_tokens = 0; p->rate_tokens = 0;
......
...@@ -596,6 +596,31 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) ...@@ -596,6 +596,31 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
return offset; return offset;
} }
void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
{
static atomic_t ipv6_fragmentation_id;
int old, new;
if (rt) {
struct inet_peer *peer;
if (!rt->rt6i_peer)
rt6_bind_peer(rt, 1);
peer = rt->rt6i_peer;
if (peer) {
fhdr->identification = htonl(inet_getid(peer, 0));
return;
}
}
do {
old = atomic_read(&ipv6_fragmentation_id);
new = old + 1;
if (!new)
new = 1;
} while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
fhdr->identification = htonl(new);
}
int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
{ {
struct sk_buff *frag; struct sk_buff *frag;
...@@ -680,7 +705,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ...@@ -680,7 +705,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
skb_reset_network_header(skb); skb_reset_network_header(skb);
memcpy(skb_network_header(skb), tmp_hdr, hlen); memcpy(skb_network_header(skb), tmp_hdr, hlen);
ipv6_select_ident(fh); ipv6_select_ident(fh, rt);
fh->nexthdr = nexthdr; fh->nexthdr = nexthdr;
fh->reserved = 0; fh->reserved = 0;
fh->frag_off = htons(IP6_MF); fh->frag_off = htons(IP6_MF);
...@@ -826,7 +851,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ...@@ -826,7 +851,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
fh->nexthdr = nexthdr; fh->nexthdr = nexthdr;
fh->reserved = 0; fh->reserved = 0;
if (!frag_id) { if (!frag_id) {
ipv6_select_ident(fh); ipv6_select_ident(fh, rt);
frag_id = fh->identification; frag_id = fh->identification;
} else } else
fh->identification = frag_id; fh->identification = frag_id;
...@@ -1076,7 +1101,8 @@ static inline int ip6_ufo_append_data(struct sock *sk, ...@@ -1076,7 +1101,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
int getfrag(void *from, char *to, int offset, int len, int getfrag(void *from, char *to, int offset, int len,
int odd, struct sk_buff *skb), int odd, struct sk_buff *skb),
void *from, int length, int hh_len, int fragheaderlen, void *from, int length, int hh_len, int fragheaderlen,
int transhdrlen, int mtu,unsigned int flags) int transhdrlen, int mtu,unsigned int flags,
struct rt6_info *rt)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -1120,7 +1146,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, ...@@ -1120,7 +1146,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
sizeof(struct frag_hdr)) & ~7; sizeof(struct frag_hdr)) & ~7;
skb_shinfo(skb)->gso_type = SKB_GSO_UDP; skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
ipv6_select_ident(&fhdr); ipv6_select_ident(&fhdr, rt);
skb_shinfo(skb)->ip6_frag_id = fhdr.identification; skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
__skb_queue_tail(&sk->sk_write_queue, skb); __skb_queue_tail(&sk->sk_write_queue, skb);
...@@ -1286,7 +1312,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1286,7 +1312,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
err = ip6_ufo_append_data(sk, getfrag, from, length, err = ip6_ufo_append_data(sk, getfrag, from, length,
hh_len, fragheaderlen, hh_len, fragheaderlen,
transhdrlen, mtu, flags); transhdrlen, mtu, flags, rt);
if (err) if (err)
goto error; goto error;
return 0; return 0;
......
...@@ -1359,7 +1359,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, u32 features) ...@@ -1359,7 +1359,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, u32 features)
fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
fptr->nexthdr = nexthdr; fptr->nexthdr = nexthdr;
fptr->reserved = 0; fptr->reserved = 0;
ipv6_select_ident(fptr); ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
/* Fragment the skb. ipv6 header and the remaining fields of the /* Fragment the skb. ipv6 header and the remaining fields of the
* fragment header are updated in ipv6_gso_segment() * fragment header are updated in ipv6_gso_segment()
......
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