diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-07-22 00:25:58 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-07-22 00:25:58 -0400 |
commit | 87c48fa3b4630905f98268dde838ee43626a060c (patch) | |
tree | 1374b52ed0514682f836cfa0a6a683eb549c9613 | |
parent | 21efcfa0ff27776902a8a15e810147be4d937d69 (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>
-rw-r--r-- | drivers/char/random.c | 15 | ||||
-rw-r--r-- | include/linux/random.h | 1 | ||||
-rw-r--r-- | include/net/inetpeer.h | 13 | ||||
-rw-r--r-- | include/net/ipv6.h | 12 | ||||
-rw-r--r-- | net/ipv4/inetpeer.c | 7 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 36 | ||||
-rw-r--r-- | net/ipv6/udp.c | 2 |
7 files changed, 64 insertions, 22 deletions
diff --git a/drivers/char/random.c b/drivers/char/random.c index d4ddeba56682..729281961f22 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c | |||
@@ -1523,6 +1523,21 @@ __u32 secure_ip_id(__be32 daddr) | |||
1523 | return half_md4_transform(hash, keyptr->secret); | 1523 | return half_md4_transform(hash, keyptr->secret); |
1524 | } | 1524 | } |
1525 | 1525 | ||
1526 | __u32 secure_ipv6_id(const __be32 daddr[4]) | ||
1527 | { | ||
1528 | const struct keydata *keyptr; | ||
1529 | __u32 hash[4]; | ||
1530 | |||
1531 | keyptr = get_keyptr(); | ||
1532 | |||
1533 | hash[0] = (__force __u32)daddr[0]; | ||
1534 | hash[1] = (__force __u32)daddr[1]; | ||
1535 | hash[2] = (__force __u32)daddr[2]; | ||
1536 | hash[3] = (__force __u32)daddr[3]; | ||
1537 | |||
1538 | return half_md4_transform(hash, keyptr->secret); | ||
1539 | } | ||
1540 | |||
1526 | #ifdef CONFIG_INET | 1541 | #ifdef CONFIG_INET |
1527 | 1542 | ||
1528 | __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, | 1543 | __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, |
diff --git a/include/linux/random.h b/include/linux/random.h index fb7ab9de5f36..ce29a040c8dc 100644 --- a/include/linux/random.h +++ b/include/linux/random.h | |||
@@ -58,6 +58,7 @@ extern void get_random_bytes(void *buf, int nbytes); | |||
58 | void generate_random_uuid(unsigned char uuid_out[16]); | 58 | void generate_random_uuid(unsigned char uuid_out[16]); |
59 | 59 | ||
60 | extern __u32 secure_ip_id(__be32 daddr); | 60 | extern __u32 secure_ip_id(__be32 daddr); |
61 | extern __u32 secure_ipv6_id(const __be32 daddr[4]); | ||
61 | extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); | 62 | extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); |
62 | extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, | 63 | extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, |
63 | __be16 dport); | 64 | __be16 dport); |
diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 39d123081e7e..4233e6f9841d 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h | |||
@@ -71,7 +71,7 @@ static inline bool inet_metrics_new(const struct inet_peer *p) | |||
71 | } | 71 | } |
72 | 72 | ||
73 | /* can be called with or without local BH being disabled */ | 73 | /* can be called with or without local BH being disabled */ |
74 | struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create); | 74 | struct inet_peer *inet_getpeer(const struct inetpeer_addr *daddr, int create); |
75 | 75 | ||
76 | static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create) | 76 | static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create) |
77 | { | 77 | { |
@@ -106,11 +106,18 @@ static inline void inet_peer_refcheck(const struct inet_peer *p) | |||
106 | 106 | ||
107 | 107 | ||
108 | /* can be called with or without local BH being disabled */ | 108 | /* can be called with or without local BH being disabled */ |
109 | static inline __u16 inet_getid(struct inet_peer *p, int more) | 109 | static inline int inet_getid(struct inet_peer *p, int more) |
110 | { | 110 | { |
111 | int old, new; | ||
111 | more++; | 112 | more++; |
112 | inet_peer_refcheck(p); | 113 | inet_peer_refcheck(p); |
113 | return atomic_add_return(more, &p->ip_id_count) - more; | 114 | do { |
115 | old = atomic_read(&p->ip_id_count); | ||
116 | new = old + more; | ||
117 | if (!new) | ||
118 | new = 1; | ||
119 | } while (atomic_cmpxchg(&p->ip_id_count, old, new) != old); | ||
120 | return new; | ||
114 | } | 121 | } |
115 | 122 | ||
116 | #endif /* _NET_INETPEER_H */ | 123 | #endif /* _NET_INETPEER_H */ |
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index c033ed00df7d..3b5ac1fbff39 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -463,17 +463,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add | |||
463 | return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); | 463 | return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); |
464 | } | 464 | } |
465 | 465 | ||
466 | static __inline__ void ipv6_select_ident(struct frag_hdr *fhdr) | 466 | extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); |
467 | { | ||
468 | static u32 ipv6_fragmentation_id = 1; | ||
469 | static DEFINE_SPINLOCK(ip6_id_lock); | ||
470 | |||
471 | spin_lock_bh(&ip6_id_lock); | ||
472 | fhdr->identification = htonl(ipv6_fragmentation_id); | ||
473 | if (++ipv6_fragmentation_id == 0) | ||
474 | ipv6_fragmentation_id = 1; | ||
475 | spin_unlock_bh(&ip6_id_lock); | ||
476 | } | ||
477 | 467 | ||
478 | /* | 468 | /* |
479 | * Prototypes exported by ipv6 | 469 | * Prototypes exported by ipv6 |
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 90c5f0d1bcf3..e38213817d0a 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c | |||
@@ -391,7 +391,7 @@ static int inet_peer_gc(struct inet_peer_base *base, | |||
391 | return cnt; | 391 | return cnt; |
392 | } | 392 | } |
393 | 393 | ||
394 | struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) | 394 | struct inet_peer *inet_getpeer(const struct inetpeer_addr *daddr, int create) |
395 | { | 395 | { |
396 | struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; | 396 | struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; |
397 | struct inet_peer_base *base = family_to_base(daddr->family); | 397 | struct inet_peer_base *base = family_to_base(daddr->family); |
@@ -436,7 +436,10 @@ relookup: | |||
436 | p->daddr = *daddr; | 436 | p->daddr = *daddr; |
437 | atomic_set(&p->refcnt, 1); | 437 | atomic_set(&p->refcnt, 1); |
438 | atomic_set(&p->rid, 0); | 438 | atomic_set(&p->rid, 0); |
439 | atomic_set(&p->ip_id_count, secure_ip_id(daddr->addr.a4)); | 439 | atomic_set(&p->ip_id_count, |
440 | (daddr->family == AF_INET) ? | ||
441 | secure_ip_id(daddr->addr.a4) : | ||
442 | secure_ipv6_id(daddr->addr.a6)); | ||
440 | p->tcp_ts_stamp = 0; | 443 | p->tcp_ts_stamp = 0; |
441 | p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; | 444 | p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; |
442 | p->rate_tokens = 0; | 445 | p->rate_tokens = 0; |
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 | ||
599 | void 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 | |||
599 | int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | 624 | int 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() |