diff options
Diffstat (limited to 'net/ipv4/route.c')
| -rw-r--r-- | net/ipv4/route.c | 47 |
1 files changed, 37 insertions, 10 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 082239ffe34a..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 | } |
| @@ -1010,7 +1036,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) | |||
| 1010 | const struct iphdr *iph = (const struct iphdr *) skb->data; | 1036 | const struct iphdr *iph = (const struct iphdr *) skb->data; |
| 1011 | struct flowi4 fl4; | 1037 | struct flowi4 fl4; |
| 1012 | struct rtable *rt; | 1038 | struct rtable *rt; |
| 1013 | struct dst_entry *dst; | 1039 | struct dst_entry *odst = NULL; |
| 1014 | bool new = false; | 1040 | bool new = false; |
| 1015 | 1041 | ||
| 1016 | bh_lock_sock(sk); | 1042 | bh_lock_sock(sk); |
| @@ -1018,16 +1044,17 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) | |||
| 1018 | if (!ip_sk_accept_pmtu(sk)) | 1044 | if (!ip_sk_accept_pmtu(sk)) |
| 1019 | goto out; | 1045 | goto out; |
| 1020 | 1046 | ||
| 1021 | rt = (struct rtable *) __sk_dst_get(sk); | 1047 | odst = sk_dst_get(sk); |
| 1022 | 1048 | ||
| 1023 | if (sock_owned_by_user(sk) || !rt) { | 1049 | if (sock_owned_by_user(sk) || !odst) { |
| 1024 | __ipv4_sk_update_pmtu(skb, sk, mtu); | 1050 | __ipv4_sk_update_pmtu(skb, sk, mtu); |
| 1025 | goto out; | 1051 | goto out; |
| 1026 | } | 1052 | } |
| 1027 | 1053 | ||
| 1028 | __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); | 1054 | __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); |
| 1029 | 1055 | ||
| 1030 | if (!__sk_dst_check(sk, 0)) { | 1056 | rt = (struct rtable *)odst; |
| 1057 | if (odst->obsolete && odst->ops->check(odst, 0) == NULL) { | ||
| 1031 | rt = ip_route_output_flow(sock_net(sk), &fl4, sk); | 1058 | rt = ip_route_output_flow(sock_net(sk), &fl4, sk); |
| 1032 | if (IS_ERR(rt)) | 1059 | if (IS_ERR(rt)) |
| 1033 | goto out; | 1060 | goto out; |
| @@ -1037,8 +1064,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) | |||
| 1037 | 1064 | ||
| 1038 | __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu); | 1065 | __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu); |
| 1039 | 1066 | ||
| 1040 | dst = dst_check(&rt->dst, 0); | 1067 | if (!dst_check(&rt->dst, 0)) { |
| 1041 | if (!dst) { | ||
| 1042 | if (new) | 1068 | if (new) |
| 1043 | dst_release(&rt->dst); | 1069 | dst_release(&rt->dst); |
| 1044 | 1070 | ||
| @@ -1050,10 +1076,11 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) | |||
| 1050 | } | 1076 | } |
| 1051 | 1077 | ||
| 1052 | if (new) | 1078 | if (new) |
| 1053 | __sk_dst_set(sk, &rt->dst); | 1079 | sk_dst_set(sk, &rt->dst); |
| 1054 | 1080 | ||
| 1055 | out: | 1081 | out: |
| 1056 | bh_unlock_sock(sk); | 1082 | bh_unlock_sock(sk); |
| 1083 | dst_release(odst); | ||
| 1057 | } | 1084 | } |
| 1058 | EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); | 1085 | EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); |
| 1059 | 1086 | ||
