diff options
| author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-08-09 02:44:00 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-15 21:31:37 -0400 |
| commit | ef81bb40bf15f350fe865f31fa42f1082772a576 (patch) | |
| tree | f89c3e9616127d234207a84e108bf147cc393215 /net/ipv6 | |
| parent | eb473dd5ad8785d35142966cdcd6896d8dff5a22 (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.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 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 | ||
| 599 | static u32 hashidentrnd __read_mostly; | ||
| 600 | #define FID_HASH_SZ 16 | ||
| 601 | static u32 ipv6_fragmentation_id[FID_HASH_SZ]; | ||
| 602 | |||
| 603 | void __init initialize_hashidentrnd(void) | ||
| 604 | { | ||
| 605 | get_random_bytes(&hashidentrnd, sizeof(hashidentrnd)); | ||
| 606 | } | ||
| 607 | |||
| 608 | static 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 | |||
| 623 | void 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 | |||
| 599 | int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | 628 | int 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() |
