aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-08-09 02:44:00 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-08-15 21:31:37 -0400
commitef81bb40bf15f350fe865f31fa42f1082772a576 (patch)
treef89c3e9616127d234207a84e108bf147cc393215 /net/ipv6
parenteb473dd5ad8785d35142966cdcd6896d8dff5a22 (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>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/ip6_output.c40
-rw-r--r--net/ipv6/udp.c2
3 files changed, 38 insertions, 6 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 3b5669a2582..559123644e5 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -1078,6 +1078,8 @@ static int __init inet6_init(void)
1078 goto out; 1078 goto out;
1079 } 1079 }
1080 1080
1081 initialize_hashidentrnd();
1082
1081 err = proto_register(&tcpv6_prot, 1); 1083 err = proto_register(&tcpv6_prot, 1);
1082 if (err) 1084 if (err)
1083 goto out; 1085 goto out;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 9d4b165837d..16612968aaa 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -596,6 +596,35 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
596 return offset; 596 return offset;
597} 597}
598 598
599static u32 hashidentrnd __read_mostly;
600#define FID_HASH_SZ 16
601static u32 ipv6_fragmentation_id[FID_HASH_SZ];
602
603void __init initialize_hashidentrnd(void)
604{
605 get_random_bytes(&hashidentrnd, sizeof(hashidentrnd));
606}
607
608static u32 __ipv6_select_ident(const struct in6_addr *addr)
609{
610 u32 newid, oldid, hash = jhash2((u32 *)addr, 4, hashidentrnd);
611 u32 *pid = &ipv6_fragmentation_id[hash % FID_HASH_SZ];
612
613 do {
614 oldid = *pid;
615 newid = oldid + 1;
616 if (!(hash + newid))
617 newid++;
618 } while (cmpxchg(pid, oldid, newid) != oldid);
619
620 return hash + newid;
621}
622
623void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
624{
625 fhdr->identification = htonl(__ipv6_select_ident(&rt->rt6i_dst.addr));
626}
627
599int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) 628int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
600{ 629{
601 struct sk_buff *frag; 630 struct sk_buff *frag;
@@ -680,7 +709,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
680 skb_reset_network_header(skb); 709 skb_reset_network_header(skb);
681 memcpy(skb_network_header(skb), tmp_hdr, hlen); 710 memcpy(skb_network_header(skb), tmp_hdr, hlen);
682 711
683 ipv6_select_ident(fh); 712 ipv6_select_ident(fh, rt);
684 fh->nexthdr = nexthdr; 713 fh->nexthdr = nexthdr;
685 fh->reserved = 0; 714 fh->reserved = 0;
686 fh->frag_off = htons(IP6_MF); 715 fh->frag_off = htons(IP6_MF);
@@ -826,7 +855,7 @@ slow_path:
826 fh->nexthdr = nexthdr; 855 fh->nexthdr = nexthdr;
827 fh->reserved = 0; 856 fh->reserved = 0;
828 if (!frag_id) { 857 if (!frag_id) {
829 ipv6_select_ident(fh); 858 ipv6_select_ident(fh, rt);
830 frag_id = fh->identification; 859 frag_id = fh->identification;
831 } else 860 } else
832 fh->identification = frag_id; 861 fh->identification = frag_id;
@@ -1072,7 +1101,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
1072 int getfrag(void *from, char *to, int offset, int len, 1101 int getfrag(void *from, char *to, int offset, int len,
1073 int odd, struct sk_buff *skb), 1102 int odd, struct sk_buff *skb),
1074 void *from, int length, int hh_len, int fragheaderlen, 1103 void *from, int length, int hh_len, int fragheaderlen,
1075 int transhdrlen, int mtu,unsigned int flags) 1104 int transhdrlen, int mtu,unsigned int flags,
1105 struct rt6_info *rt)
1076 1106
1077{ 1107{
1078 struct sk_buff *skb; 1108 struct sk_buff *skb;
@@ -1116,7 +1146,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
1116 skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - 1146 skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
1117 sizeof(struct frag_hdr)) & ~7; 1147 sizeof(struct frag_hdr)) & ~7;
1118 skb_shinfo(skb)->gso_type = SKB_GSO_UDP; 1148 skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
1119 ipv6_select_ident(&fhdr); 1149 ipv6_select_ident(&fhdr, rt);
1120 skb_shinfo(skb)->ip6_frag_id = fhdr.identification; 1150 skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
1121 __skb_queue_tail(&sk->sk_write_queue, skb); 1151 __skb_queue_tail(&sk->sk_write_queue, skb);
1122 1152
@@ -1282,7 +1312,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1282 1312
1283 err = ip6_ufo_append_data(sk, getfrag, from, length, 1313 err = ip6_ufo_append_data(sk, getfrag, from, length,
1284 hh_len, fragheaderlen, 1314 hh_len, fragheaderlen,
1285 transhdrlen, mtu, flags); 1315 transhdrlen, mtu, flags, rt);
1286 if (err) 1316 if (err)
1287 goto error; 1317 goto error;
1288 return 0; 1318 return 0;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 328985c4088..29213b51c49 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1359,7 +1359,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, u32 features)
1359 fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); 1359 fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
1360 fptr->nexthdr = nexthdr; 1360 fptr->nexthdr = nexthdr;
1361 fptr->reserved = 0; 1361 fptr->reserved = 0;
1362 ipv6_select_ident(fptr); 1362 ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
1363 1363
1364 /* Fragment the skb. ipv6 header and the remaining fields of the 1364 /* Fragment the skb. ipv6 header and the remaining fields of the
1365 * fragment header are updated in ipv6_gso_segment() 1365 * fragment header are updated in ipv6_gso_segment()