Skip to content

Commit 04ca697

Browse files
edumazetdavem330
authored andcommitted
ip: make IP identifiers less predictable
In "Counting Packets Sent Between Arbitrary Internet Hosts", Jeffrey and Jedidiah describe ways exploiting linux IP identifier generation to infer whether two machines are exchanging packets. With commit 73f156a ("inetpeer: get rid of ip_id_count"), we changed IP id generation, but this does not really prevent this side-channel technique. This patch adds a random amount of perturbation so that IP identifiers for a given destination [1] are no longer monotonically increasing after an idle period. Note that prandom_u32_max(1) returns 0, so if generator is used at most once per jiffy, this patch inserts no hole in the ID suite and do not increase collision probability. This is jiffies based, so in the worst case (HZ=1000), the id can rollover after ~65 seconds of idle time, which should be fine. We also change the hash used in __ip_select_ident() to not only hash on daddr, but also saddr and protocol, so that ICMP probes can not be used to infer information for other protocols. For IPv6, adds saddr into the hash as well, but not nexthdr. If I ping the patched target, we can see ID are now hard to predict. 21:57:11.008086 IP (...) A > target: ICMP echo request, seq 1, length 64 21:57:11.010752 IP (... id 2081 ...) target > A: ICMP echo reply, seq 1, length 64 21:57:12.013133 IP (...) A > target: ICMP echo request, seq 2, length 64 21:57:12.015737 IP (... id 3039 ...) target > A: ICMP echo reply, seq 2, length 64 21:57:13.016580 IP (...) A > target: ICMP echo request, seq 3, length 64 21:57:13.019251 IP (... id 3437 ...) target > A: ICMP echo reply, seq 3, length 64 [1] TCP sessions uses a per flow ID generator not changed by this patch. Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Jeffrey Knockel <jeffk@cs.unm.edu> Reported-by: Jedidiah R. Crandall <crandall@cs.unm.edu> Cc: Willy Tarreau <w@1wt.eu> Cc: Hannes Frederic Sowa <hannes@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 545469f commit 04ca697

File tree

3 files changed

+32
-13
lines changed

3 files changed

+32
-13
lines changed

include/net/ip.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -309,16 +309,7 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb)
309309
}
310310
}
311311

312-
#define IP_IDENTS_SZ 2048u
313-
extern atomic_t *ip_idents;
314-
315-
static inline u32 ip_idents_reserve(u32 hash, int segs)
316-
{
317-
atomic_t *id_ptr = ip_idents + hash % IP_IDENTS_SZ;
318-
319-
return atomic_add_return(segs, id_ptr) - segs;
320-
}
321-
312+
u32 ip_idents_reserve(u32 hash, int segs);
322313
void __ip_select_ident(struct iphdr *iph, int segs);
323314

324315
static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs)

net/ipv4/route.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,31 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
457457
return neigh_create(&arp_tbl, pkey, dev);
458458
}
459459

460-
atomic_t *ip_idents __read_mostly;
461-
EXPORT_SYMBOL(ip_idents);
460+
#define IP_IDENTS_SZ 2048u
461+
struct ip_ident_bucket {
462+
atomic_t id;
463+
u32 stamp32;
464+
};
465+
466+
static struct ip_ident_bucket *ip_idents __read_mostly;
467+
468+
/* In order to protect privacy, we add a perturbation to identifiers
469+
* if one generator is seldom used. This makes hard for an attacker
470+
* to infer how many packets were sent between two points in time.
471+
*/
472+
u32 ip_idents_reserve(u32 hash, int segs)
473+
{
474+
struct ip_ident_bucket *bucket = ip_idents + hash % IP_IDENTS_SZ;
475+
u32 old = ACCESS_ONCE(bucket->stamp32);
476+
u32 now = (u32)jiffies;
477+
u32 delta = 0;
478+
479+
if (old != now && cmpxchg(&bucket->stamp32, old, now) == old)
480+
delta = prandom_u32_max(now - old);
481+
482+
return atomic_add_return(segs + delta, &bucket->id) - segs;
483+
}
484+
EXPORT_SYMBOL(ip_idents_reserve);
462485

463486
void __ip_select_ident(struct iphdr *iph, int segs)
464487
{
@@ -467,7 +490,10 @@ void __ip_select_ident(struct iphdr *iph, int segs)
467490

468491
net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd));
469492

470-
hash = jhash_1word((__force u32)iph->daddr, ip_idents_hashrnd);
493+
hash = jhash_3words((__force u32)iph->daddr,
494+
(__force u32)iph->saddr,
495+
iph->protocol,
496+
ip_idents_hashrnd);
471497
id = ip_idents_reserve(hash, segs);
472498
iph->id = htons(id);
473499
}

net/ipv6/ip6_output.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ static void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
545545
net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
546546

547547
hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd);
548+
hash = __ipv6_addr_jhash(&rt->rt6i_src.addr, hash);
549+
548550
id = ip_idents_reserve(hash, 1);
549551
fhdr->identification = htonl(id);
550552
}

0 commit comments

Comments
 (0)