]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.10.53/ip-make-ip-identifiers-less-predictable.patch
5.1-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.10.53 / ip-make-ip-identifiers-less-predictable.patch
1 From foo@baz Fri Aug 8 08:48:58 PDT 2014
2 From: Eric Dumazet <edumazet@google.com>
3 Date: Sat, 26 Jul 2014 08:58:10 +0200
4 Subject: ip: make IP identifiers less predictable
5
6 From: Eric Dumazet <edumazet@google.com>
7
8 [ Upstream commit 04ca6973f7c1a0d8537f2d9906a0cf8e69886d75 ]
9
10 In "Counting Packets Sent Between Arbitrary Internet Hosts", Jeffrey and
11 Jedidiah describe ways exploiting linux IP identifier generation to
12 infer whether two machines are exchanging packets.
13
14 With commit 73f156a6e8c1 ("inetpeer: get rid of ip_id_count"), we
15 changed IP id generation, but this does not really prevent this
16 side-channel technique.
17
18 This patch adds a random amount of perturbation so that IP identifiers
19 for a given destination [1] are no longer monotonically increasing after
20 an idle period.
21
22 Note that prandom_u32_max(1) returns 0, so if generator is used at most
23 once per jiffy, this patch inserts no hole in the ID suite and do not
24 increase collision probability.
25
26 This is jiffies based, so in the worst case (HZ=1000), the id can
27 rollover after ~65 seconds of idle time, which should be fine.
28
29 We also change the hash used in __ip_select_ident() to not only hash
30 on daddr, but also saddr and protocol, so that ICMP probes can not be
31 used to infer information for other protocols.
32
33 For IPv6, adds saddr into the hash as well, but not nexthdr.
34
35 If I ping the patched target, we can see ID are now hard to predict.
36
37 21:57:11.008086 IP (...)
38 A > target: ICMP echo request, seq 1, length 64
39 21:57:11.010752 IP (... id 2081 ...)
40 target > A: ICMP echo reply, seq 1, length 64
41
42 21:57:12.013133 IP (...)
43 A > target: ICMP echo request, seq 2, length 64
44 21:57:12.015737 IP (... id 3039 ...)
45 target > A: ICMP echo reply, seq 2, length 64
46
47 21:57:13.016580 IP (...)
48 A > target: ICMP echo request, seq 3, length 64
49 21:57:13.019251 IP (... id 3437 ...)
50 target > A: ICMP echo reply, seq 3, length 64
51
52 [1] TCP sessions uses a per flow ID generator not changed by this patch.
53
54 Signed-off-by: Eric Dumazet <edumazet@google.com>
55 Reported-by: Jeffrey Knockel <jeffk@cs.unm.edu>
56 Reported-by: Jedidiah R. Crandall <crandall@cs.unm.edu>
57 Cc: Willy Tarreau <w@1wt.eu>
58 Cc: Hannes Frederic Sowa <hannes@redhat.com>
59 Signed-off-by: David S. Miller <davem@davemloft.net>
60 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
61 ---
62 include/net/ip.h | 11 +----------
63 net/ipv4/route.c | 36 +++++++++++++++++++++++++++++++++---
64 net/ipv6/ip6_output.c | 2 ++
65 3 files changed, 36 insertions(+), 13 deletions(-)
66
67 --- a/include/net/ip.h
68 +++ b/include/net/ip.h
69 @@ -252,16 +252,7 @@ int ip_dont_fragment(struct sock *sk, st
70 !(dst_metric_locked(dst, RTAX_MTU)));
71 }
72
73 -#define IP_IDENTS_SZ 2048u
74 -extern atomic_t *ip_idents;
75 -
76 -static inline u32 ip_idents_reserve(u32 hash, int segs)
77 -{
78 - atomic_t *id_ptr = ip_idents + hash % IP_IDENTS_SZ;
79 -
80 - return atomic_add_return(segs, id_ptr) - segs;
81 -}
82 -
83 +u32 ip_idents_reserve(u32 hash, int segs);
84 void __ip_select_ident(struct iphdr *iph, int segs);
85
86 static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs)
87 --- a/net/ipv4/route.c
88 +++ b/net/ipv4/route.c
89 @@ -465,8 +465,35 @@ static struct neighbour *ipv4_neigh_look
90 return neigh_create(&arp_tbl, pkey, dev);
91 }
92
93 -atomic_t *ip_idents __read_mostly;
94 -EXPORT_SYMBOL(ip_idents);
95 +#define IP_IDENTS_SZ 2048u
96 +struct ip_ident_bucket {
97 + atomic_t id;
98 + u32 stamp32;
99 +};
100 +
101 +static struct ip_ident_bucket *ip_idents __read_mostly;
102 +
103 +/* In order to protect privacy, we add a perturbation to identifiers
104 + * if one generator is seldom used. This makes hard for an attacker
105 + * to infer how many packets were sent between two points in time.
106 + */
107 +u32 ip_idents_reserve(u32 hash, int segs)
108 +{
109 + struct ip_ident_bucket *bucket = ip_idents + hash % IP_IDENTS_SZ;
110 + u32 old = ACCESS_ONCE(bucket->stamp32);
111 + u32 now = (u32)jiffies;
112 + u32 delta = 0;
113 +
114 + if (old != now && cmpxchg(&bucket->stamp32, old, now) == old) {
115 + u64 x = prandom_u32();
116 +
117 + x *= (now - old);
118 + delta = (u32)(x >> 32);
119 + }
120 +
121 + return atomic_add_return(segs + delta, &bucket->id) - segs;
122 +}
123 +EXPORT_SYMBOL(ip_idents_reserve);
124
125 void __ip_select_ident(struct iphdr *iph, int segs)
126 {
127 @@ -479,7 +506,10 @@ void __ip_select_ident(struct iphdr *iph
128 get_random_bytes(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd));
129 }
130
131 - hash = jhash_1word((__force u32)iph->daddr, ip_idents_hashrnd);
132 + hash = jhash_3words((__force u32)iph->daddr,
133 + (__force u32)iph->saddr,
134 + iph->protocol,
135 + ip_idents_hashrnd);
136 id = ip_idents_reserve(hash, segs);
137 iph->id = htons(id);
138 }
139 --- a/net/ipv6/ip6_output.c
140 +++ b/net/ipv6/ip6_output.c
141 @@ -551,6 +551,8 @@ static void ipv6_select_ident(struct fra
142 get_random_bytes(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
143 }
144 hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd);
145 + hash = __ipv6_addr_jhash(&rt->rt6i_src.addr, hash);
146 +
147 id = ip_idents_reserve(hash, 1);
148 fhdr->identification = htonl(id);
149 }