aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/char/random.c15
-rw-r--r--include/linux/random.h1
-rw-r--r--include/net/inetpeer.h13
-rw-r--r--include/net/ipv6.h12
-rw-r--r--net/ipv4/inetpeer.c7
-rw-r--r--net/ipv6/ip6_output.c36
-rw-r--r--net/ipv6/udp.c2
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);
58void generate_random_uuid(unsigned char uuid_out[16]); 58void generate_random_uuid(unsigned char uuid_out[16]);
59 59
60extern __u32 secure_ip_id(__be32 daddr); 60extern __u32 secure_ip_id(__be32 daddr);
61extern __u32 secure_ipv6_id(const __be32 daddr[4]);
61extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); 62extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
62extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, 63extern 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 */
74struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create); 74struct inet_peer *inet_getpeer(const struct inetpeer_addr *daddr, int create);
75 75
76static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create) 76static 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 */
109static inline __u16 inet_getid(struct inet_peer *p, int more) 109static 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
466static __inline__ void ipv6_select_ident(struct frag_hdr *fhdr) 466extern 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
394struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) 394struct 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
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()