aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-07-22 00:25:58 -0400
committerDavid S. Miller <davem@davemloft.net>2011-07-22 00:25:58 -0400
commit87c48fa3b4630905f98268dde838ee43626a060c (patch)
tree1374b52ed0514682f836cfa0a6a683eb549c9613 /net/ipv6
parent21efcfa0ff27776902a8a15e810147be4d937d69 (diff)
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: Fernando Gont <fernando@gont.com.ar> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_output.c36
-rw-r--r--net/ipv6/udp.c2
2 files changed, 32 insertions, 6 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 8db0e4875ad8..32e5339db0c8 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -596,6 +596,31 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
596 return offset; 596 return offset;
597} 597}
598 598
599void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
600{
601 static atomic_t ipv6_fragmentation_id;
602 int old, new;
603
604 if (rt) {
605 struct inet_peer *peer;
606
607 if (!rt->rt6i_peer)
608 rt6_bind_peer(rt, 1);
609 peer = rt->rt6i_peer;
610 if (peer) {
611 fhdr->identification = htonl(inet_getid(peer, 0));
612 return;
613 }
614 }
615 do {
616 old = atomic_read(&ipv6_fragmentation_id);
617 new = old + 1;
618 if (!new)
619 new = 1;
620 } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
621 fhdr->identification = htonl(new);
622}
623
599int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) 624int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
600{ 625{
601 struct sk_buff *frag; 626 struct sk_buff *frag;
@@ -680,7 +705,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
680 skb_reset_network_header(skb); 705 skb_reset_network_header(skb);
681 memcpy(skb_network_header(skb), tmp_hdr, hlen); 706 memcpy(skb_network_header(skb), tmp_hdr, hlen);
682 707
683 ipv6_select_ident(fh); 708 ipv6_select_ident(fh, rt);
684 fh->nexthdr = nexthdr; 709 fh->nexthdr = nexthdr;
685 fh->reserved = 0; 710 fh->reserved = 0;
686 fh->frag_off = htons(IP6_MF); 711 fh->frag_off = htons(IP6_MF);
@@ -826,7 +851,7 @@ slow_path:
826 fh->nexthdr = nexthdr; 851 fh->nexthdr = nexthdr;
827 fh->reserved = 0; 852 fh->reserved = 0;
828 if (!frag_id) { 853 if (!frag_id) {
829 ipv6_select_ident(fh); 854 ipv6_select_ident(fh, rt);
830 frag_id = fh->identification; 855 frag_id = fh->identification;
831 } else 856 } else
832 fh->identification = frag_id; 857 fh->identification = frag_id;
@@ -1076,7 +1101,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
1076 int getfrag(void *from, char *to, int offset, int len, 1101 int getfrag(void *from, char *to, int offset, int len,
1077 int odd, struct sk_buff *skb), 1102 int odd, struct sk_buff *skb),
1078 void *from, int length, int hh_len, int fragheaderlen, 1103 void *from, int length, int hh_len, int fragheaderlen,
1079 int transhdrlen, int mtu,unsigned int flags) 1104 int transhdrlen, int mtu,unsigned int flags,
1105 struct rt6_info *rt)
1080 1106
1081{ 1107{
1082 struct sk_buff *skb; 1108 struct sk_buff *skb;
@@ -1120,7 +1146,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
1120 skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - 1146 skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
1121 sizeof(struct frag_hdr)) & ~7; 1147 sizeof(struct frag_hdr)) & ~7;
1122 skb_shinfo(skb)->gso_type = SKB_GSO_UDP; 1148 skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
1123 ipv6_select_ident(&fhdr); 1149 ipv6_select_ident(&fhdr, rt);
1124 skb_shinfo(skb)->ip6_frag_id = fhdr.identification; 1150 skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
1125 __skb_queue_tail(&sk->sk_write_queue, skb); 1151 __skb_queue_tail(&sk->sk_write_queue, skb);
1126 1152
@@ -1286,7 +1312,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1286 1312
1287 err = ip6_ufo_append_data(sk, getfrag, from, length, 1313 err = ip6_ufo_append_data(sk, getfrag, from, length,
1288 hh_len, fragheaderlen, 1314 hh_len, fragheaderlen,
1289 transhdrlen, mtu, flags); 1315 transhdrlen, mtu, flags, rt);
1290 if (err) 1316 if (err)
1291 goto error; 1317 goto error;
1292 return 0; 1318 return 0;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 328985c40883..29213b51c499 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()