diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-08-08 23:44:00 -0700 |
---|---|---|
committer | Willy Tarreau <w@1wt.eu> | 2013-06-10 11:43:37 +0200 |
commit | c023a0b45bb91bce7bf3300518592f07fcd35244 (patch) | |
tree | 3bf5739f813f09a31c68ccb1149bcb95bc69bc61 /net | |
parent | b1a1c38d6d1860584ec828b798276fffd2b7eadc (diff) |
ipv6: make fragment identifications less predictable
[ Backport of upstream commit 87c48fa3b4630905f98268dde838ee43626a060c ]
Fernando Gont reported current IPv6 fragment identification generation
was not secure, because using a very predictable system-wide generator,
allowing various attacks.
IPv4 uses inetpeer cache to address this problem and to get good
performance. We'll use this mechanism when IPv6 inetpeer is stable
enough in linux-3.1
For the time being, we use jhash on destination address to provide less
predictable identifications. Also remove a spinlock and use cmpxchg() to
get better SMP performance.
Reported-by: Fernando Gont <fernando@gont.com.ar>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
[bwh: Backport further to 2.6.32]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: Willy Tarreau <w@1wt.eu>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/af_inet6.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 40 | ||||
-rw-r--r-- | net/ipv6/udp.c | 2 |
3 files changed, 38 insertions, 6 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e127a32f954..835590d6887 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -1073,6 +1073,8 @@ static int __init inet6_init(void) goto out; } + initialize_hashidentrnd(); + err = proto_register(&tcpv6_prot, 1); if (err) goto out; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 9ad57923440..6ba0fe27baf 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -604,6 +604,35 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) return offset; } +static u32 hashidentrnd __read_mostly; +#define FID_HASH_SZ 16 +static u32 ipv6_fragmentation_id[FID_HASH_SZ]; + +void __init initialize_hashidentrnd(void) +{ + get_random_bytes(&hashidentrnd, sizeof(hashidentrnd)); +} + +static u32 __ipv6_select_ident(const struct in6_addr *addr) +{ + u32 newid, oldid, hash = jhash2((u32 *)addr, 4, hashidentrnd); + u32 *pid = &ipv6_fragmentation_id[hash % FID_HASH_SZ]; + + do { + oldid = *pid; + newid = oldid + 1; + if (!(hash + newid)) + newid++; + } while (cmpxchg(pid, oldid, newid) != oldid); + + return hash + newid; +} + +void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) +{ + fhdr->identification = htonl(__ipv6_select_ident(&rt->rt6i_dst.addr)); +} + static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) { struct sk_buff *frag; @@ -689,7 +718,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) skb_reset_network_header(skb); memcpy(skb_network_header(skb), tmp_hdr, hlen); - ipv6_select_ident(fh); + ipv6_select_ident(fh, rt); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); @@ -835,7 +864,7 @@ slow_path: fh->nexthdr = nexthdr; fh->reserved = 0; if (!frag_id) { - ipv6_select_ident(fh); + ipv6_select_ident(fh, rt); frag_id = fh->identification; } else fh->identification = frag_id; @@ -1039,7 +1068,8 @@ static inline int ip6_ufo_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), 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; @@ -1084,7 +1114,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - sizeof(struct frag_hdr)) & ~7; 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_queue_tail(&sk->sk_write_queue, skb); @@ -1233,7 +1263,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len, fragheaderlen, transhdrlen, mtu, - flags); + flags, rt); if (err) goto error; return 0; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9cc6289acae..d8c03744007 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1162,7 +1162,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features) fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); fptr->nexthdr = nexthdr; 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 header are updated in ipv6_gso_segment() |