Commit 87c48fa3b4630905f98268dde838ee43626a060c

Authored by Eric Dumazet
Committed by David S. Miller
1 parent 21efcfa0ff

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>

Showing 7 changed files with 64 additions and 22 deletions Side-by-side Diff

drivers/char/random.c
... ... @@ -1523,6 +1523,21 @@
1523 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 1541 #ifdef CONFIG_INET
1527 1542  
1528 1543 __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
include/linux/random.h
... ... @@ -58,6 +58,7 @@
58 58 void generate_random_uuid(unsigned char uuid_out[16]);
59 59  
60 60 extern __u32 secure_ip_id(__be32 daddr);
  61 +extern __u32 secure_ipv6_id(const __be32 daddr[4]);
61 62 extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
62 63 extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
63 64 __be16 dport);
include/net/inetpeer.h
... ... @@ -71,7 +71,7 @@
71 71 }
72 72  
73 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 76 static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create)
77 77 {
78 78  
79 79  
... ... @@ -106,11 +106,18 @@
106 106  
107 107  
108 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 112 more++;
112 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 123 #endif /* _NET_INETPEER_H */
... ... @@ -463,17 +463,7 @@
463 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)
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   -}
  466 +extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
477 467  
478 468 /*
479 469 * Prototypes exported by ipv6
... ... @@ -391,7 +391,7 @@
391 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 396 struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr;
397 397 struct inet_peer_base *base = family_to_base(daddr->family);
... ... @@ -436,7 +436,10 @@
436 436 p->daddr = *daddr;
437 437 atomic_set(&p->refcnt, 1);
438 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 443 p->tcp_ts_stamp = 0;
441 444 p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
442 445 p->rate_tokens = 0;
net/ipv6/ip6_output.c
... ... @@ -596,6 +596,31 @@
596 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 624 int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
600 625 {
601 626 struct sk_buff *frag;
... ... @@ -680,7 +705,7 @@
680 705 skb_reset_network_header(skb);
681 706 memcpy(skb_network_header(skb), tmp_hdr, hlen);
682 707  
683   - ipv6_select_ident(fh);
  708 + ipv6_select_ident(fh, rt);
684 709 fh->nexthdr = nexthdr;
685 710 fh->reserved = 0;
686 711 fh->frag_off = htons(IP6_MF);
... ... @@ -826,7 +851,7 @@
826 851 fh->nexthdr = nexthdr;
827 852 fh->reserved = 0;
828 853 if (!frag_id) {
829   - ipv6_select_ident(fh);
  854 + ipv6_select_ident(fh, rt);
830 855 frag_id = fh->identification;
831 856 } else
832 857 fh->identification = frag_id;
... ... @@ -1076,7 +1101,8 @@
1076 1101 int getfrag(void *from, char *to, int offset, int len,
1077 1102 int odd, struct sk_buff *skb),
1078 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 1108 struct sk_buff *skb;
... ... @@ -1120,7 +1146,7 @@
1120 1146 skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
1121 1147 sizeof(struct frag_hdr)) & ~7;
1122 1148 skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
1123   - ipv6_select_ident(&fhdr);
  1149 + ipv6_select_ident(&fhdr, rt);
1124 1150 skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
1125 1151 __skb_queue_tail(&sk->sk_write_queue, skb);
1126 1152  
... ... @@ -1286,7 +1312,7 @@
1286 1312  
1287 1313 err = ip6_ufo_append_data(sk, getfrag, from, length,
1288 1314 hh_len, fragheaderlen,
1289   - transhdrlen, mtu, flags);
  1315 + transhdrlen, mtu, flags, rt);
1290 1316 if (err)
1291 1317 goto error;
1292 1318 return 0;
... ... @@ -1359,7 +1359,7 @@
1359 1359 fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
1360 1360 fptr->nexthdr = nexthdr;
1361 1361 fptr->reserved = 0;
1362   - ipv6_select_ident(fptr);
  1362 + ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
1363 1363  
1364 1364 /* Fragment the skb. ipv6 header and the remaining fields of the
1365 1365 * fragment header are updated in ipv6_gso_segment()