diff options
-rw-r--r-- | include/net/ip.h | 11 | ||||
-rw-r--r-- | net/ipv4/route.c | 32 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 2 |
3 files changed, 32 insertions, 13 deletions
diff --git a/include/net/ip.h b/include/net/ip.h index 0e795df05ec9..7596eb22e1ce 100644 --- a/include/net/ip.h +++ b/include/net/ip.h | |||
@@ -309,16 +309,7 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb) | |||
309 | } | 309 | } |
310 | } | 310 | } |
311 | 311 | ||
312 | #define IP_IDENTS_SZ 2048u | 312 | u32 ip_idents_reserve(u32 hash, int segs); |
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 | |||
322 | void __ip_select_ident(struct iphdr *iph, int segs); | 313 | void __ip_select_ident(struct iphdr *iph, int segs); |
323 | 314 | ||
324 | static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs) | 315 | static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs) |
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3162ea923ded..190199851c9a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -457,8 +457,31 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, | |||
457 | return neigh_create(&arp_tbl, pkey, dev); | 457 | return neigh_create(&arp_tbl, pkey, dev); |
458 | } | 458 | } |
459 | 459 | ||
460 | atomic_t *ip_idents __read_mostly; | 460 | #define IP_IDENTS_SZ 2048u |
461 | EXPORT_SYMBOL(ip_idents); | 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); | ||
462 | 485 | ||
463 | void __ip_select_ident(struct iphdr *iph, int segs) | 486 | void __ip_select_ident(struct iphdr *iph, int segs) |
464 | { | 487 | { |
@@ -467,7 +490,10 @@ void __ip_select_ident(struct iphdr *iph, int segs) | |||
467 | 490 | ||
468 | net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd)); | 491 | net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd)); |
469 | 492 | ||
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); | ||
471 | id = ip_idents_reserve(hash, segs); | 497 | id = ip_idents_reserve(hash, segs); |
472 | iph->id = htons(id); | 498 | iph->id = htons(id); |
473 | } | 499 | } |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index cb9df0eb4023..45702b8cd141 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -545,6 +545,8 @@ static void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) | |||
545 | net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); | 545 | net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); |
546 | 546 | ||
547 | hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd); | 547 | hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd); |
548 | hash = __ipv6_addr_jhash(&rt->rt6i_src.addr, hash); | ||
549 | |||
548 | id = ip_idents_reserve(hash, 1); | 550 | id = ip_idents_reserve(hash, 1); |
549 | fhdr->identification = htonl(id); | 551 | fhdr->identification = htonl(id); |
550 | } | 552 | } |