diff options
-rw-r--r-- | include/linux/udp.h | 2 | ||||
-rw-r--r-- | include/net/sock.h | 6 | ||||
-rw-r--r-- | net/ipv4/udp.c | 25 | ||||
-rw-r--r-- | net/ipv6/udp.c | 27 |
4 files changed, 51 insertions, 9 deletions
diff --git a/include/linux/udp.h b/include/linux/udp.h index 832361e3e596..5b4b5274e683 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h | |||
@@ -55,6 +55,8 @@ static inline int udp_hashfn(struct net *net, unsigned num, unsigned mask) | |||
55 | struct udp_sock { | 55 | struct udp_sock { |
56 | /* inet_sock has to be the first member */ | 56 | /* inet_sock has to be the first member */ |
57 | struct inet_sock inet; | 57 | struct inet_sock inet; |
58 | #define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0] | ||
59 | #define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1] | ||
58 | int pending; /* Any pending frames ? */ | 60 | int pending; /* Any pending frames ? */ |
59 | unsigned int corkflag; /* Cork is required */ | 61 | unsigned int corkflag; /* Cork is required */ |
60 | __u16 encap_type; /* Is this an Encapsulation socket? */ | 62 | __u16 encap_type; /* Is this an Encapsulation socket? */ |
diff --git a/include/net/sock.h b/include/net/sock.h index 55de3bd719a5..827366b62680 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -109,6 +109,7 @@ struct net; | |||
109 | * @skc_refcnt: reference count | 109 | * @skc_refcnt: reference count |
110 | * @skc_tx_queue_mapping: tx queue number for this connection | 110 | * @skc_tx_queue_mapping: tx queue number for this connection |
111 | * @skc_hash: hash value used with various protocol lookup tables | 111 | * @skc_hash: hash value used with various protocol lookup tables |
112 | * @skc_u16hashes: two u16 hash values used by UDP lookup tables | ||
112 | * @skc_family: network address family | 113 | * @skc_family: network address family |
113 | * @skc_state: Connection state | 114 | * @skc_state: Connection state |
114 | * @skc_reuse: %SO_REUSEADDR setting | 115 | * @skc_reuse: %SO_REUSEADDR setting |
@@ -131,7 +132,10 @@ struct sock_common { | |||
131 | atomic_t skc_refcnt; | 132 | atomic_t skc_refcnt; |
132 | int skc_tx_queue_mapping; | 133 | int skc_tx_queue_mapping; |
133 | 134 | ||
134 | unsigned int skc_hash; | 135 | union { |
136 | unsigned int skc_hash; | ||
137 | __u16 skc_u16hashes[2]; | ||
138 | }; | ||
135 | unsigned short skc_family; | 139 | unsigned short skc_family; |
136 | volatile unsigned char skc_state; | 140 | volatile unsigned char skc_state; |
137 | unsigned char skc_reuse; | 141 | unsigned char skc_reuse; |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index ffc837643a04..af72de1c8690 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -138,13 +138,14 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, | |||
138 | sk_nulls_for_each(sk2, node, &hslot->head) | 138 | sk_nulls_for_each(sk2, node, &hslot->head) |
139 | if (net_eq(sock_net(sk2), net) && | 139 | if (net_eq(sock_net(sk2), net) && |
140 | sk2 != sk && | 140 | sk2 != sk && |
141 | (bitmap || sk2->sk_hash == num) && | 141 | (bitmap || udp_sk(sk2)->udp_port_hash == num) && |
142 | (!sk2->sk_reuse || !sk->sk_reuse) && | 142 | (!sk2->sk_reuse || !sk->sk_reuse) && |
143 | (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if | 143 | (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if |
144 | || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && | 144 | || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && |
145 | (*saddr_comp)(sk, sk2)) { | 145 | (*saddr_comp)(sk, sk2)) { |
146 | if (bitmap) | 146 | if (bitmap) |
147 | __set_bit(sk2->sk_hash >> log, bitmap); | 147 | __set_bit(udp_sk(sk2)->udp_port_hash >> log, |
148 | bitmap); | ||
148 | else | 149 | else |
149 | return 1; | 150 | return 1; |
150 | } | 151 | } |
@@ -215,7 +216,8 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, | |||
215 | } | 216 | } |
216 | found: | 217 | found: |
217 | inet_sk(sk)->inet_num = snum; | 218 | inet_sk(sk)->inet_num = snum; |
218 | sk->sk_hash = snum; | 219 | udp_sk(sk)->udp_port_hash = snum; |
220 | udp_sk(sk)->udp_portaddr_hash ^= snum; | ||
219 | if (sk_unhashed(sk)) { | 221 | if (sk_unhashed(sk)) { |
220 | sk_nulls_add_node_rcu(sk, &hslot->head); | 222 | sk_nulls_add_node_rcu(sk, &hslot->head); |
221 | hslot->count++; | 223 | hslot->count++; |
@@ -238,8 +240,19 @@ static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) | |||
238 | inet1->inet_rcv_saddr == inet2->inet_rcv_saddr)); | 240 | inet1->inet_rcv_saddr == inet2->inet_rcv_saddr)); |
239 | } | 241 | } |
240 | 242 | ||
243 | static unsigned int udp4_portaddr_hash(struct net *net, __be32 saddr, | ||
244 | unsigned int port) | ||
245 | { | ||
246 | return jhash_1word(saddr, net_hash_mix(net)) ^ port; | ||
247 | } | ||
248 | |||
241 | int udp_v4_get_port(struct sock *sk, unsigned short snum) | 249 | int udp_v4_get_port(struct sock *sk, unsigned short snum) |
242 | { | 250 | { |
251 | /* precompute partial secondary hash */ | ||
252 | udp_sk(sk)->udp_portaddr_hash = | ||
253 | udp4_portaddr_hash(sock_net(sk), | ||
254 | inet_sk(sk)->inet_rcv_saddr, | ||
255 | 0); | ||
243 | return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal); | 256 | return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal); |
244 | } | 257 | } |
245 | 258 | ||
@@ -249,7 +262,7 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, | |||
249 | { | 262 | { |
250 | int score = -1; | 263 | int score = -1; |
251 | 264 | ||
252 | if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && | 265 | if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum && |
253 | !ipv6_only_sock(sk)) { | 266 | !ipv6_only_sock(sk)) { |
254 | struct inet_sock *inet = inet_sk(sk); | 267 | struct inet_sock *inet = inet_sk(sk); |
255 | 268 | ||
@@ -360,7 +373,7 @@ static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk, | |||
360 | struct inet_sock *inet = inet_sk(s); | 373 | struct inet_sock *inet = inet_sk(s); |
361 | 374 | ||
362 | if (!net_eq(sock_net(s), net) || | 375 | if (!net_eq(sock_net(s), net) || |
363 | s->sk_hash != hnum || | 376 | udp_sk(s)->udp_port_hash != hnum || |
364 | (inet->inet_daddr && inet->inet_daddr != rmt_addr) || | 377 | (inet->inet_daddr && inet->inet_daddr != rmt_addr) || |
365 | (inet->inet_dport != rmt_port && inet->inet_dport) || | 378 | (inet->inet_dport != rmt_port && inet->inet_dport) || |
366 | (inet->inet_rcv_saddr && | 379 | (inet->inet_rcv_saddr && |
@@ -1050,7 +1063,7 @@ void udp_lib_unhash(struct sock *sk) | |||
1050 | if (sk_hashed(sk)) { | 1063 | if (sk_hashed(sk)) { |
1051 | struct udp_table *udptable = sk->sk_prot->h.udp_table; | 1064 | struct udp_table *udptable = sk->sk_prot->h.udp_table; |
1052 | struct udp_hslot *hslot = udp_hashslot(udptable, sock_net(sk), | 1065 | struct udp_hslot *hslot = udp_hashslot(udptable, sock_net(sk), |
1053 | sk->sk_hash); | 1066 | udp_sk(sk)->udp_port_hash); |
1054 | 1067 | ||
1055 | spin_lock_bh(&hslot->lock); | 1068 | spin_lock_bh(&hslot->lock); |
1056 | if (sk_nulls_del_node_init_rcu(sk)) { | 1069 | if (sk_nulls_del_node_init_rcu(sk)) { |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 5bc7cdbf030a..1e5fadd997b7 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -81,8 +81,30 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) | |||
81 | return 0; | 81 | return 0; |
82 | } | 82 | } |
83 | 83 | ||
84 | static unsigned int udp6_portaddr_hash(struct net *net, | ||
85 | const struct in6_addr *addr6, | ||
86 | unsigned int port) | ||
87 | { | ||
88 | unsigned int hash, mix = net_hash_mix(net); | ||
89 | |||
90 | if (ipv6_addr_any(addr6)) | ||
91 | hash = jhash_1word(0, mix); | ||
92 | else if (ipv6_addr_type(addr6) == IPV6_ADDR_MAPPED) | ||
93 | hash = jhash_1word(addr6->s6_addr32[3], mix); | ||
94 | else | ||
95 | hash = jhash2(addr6->s6_addr32, 4, mix); | ||
96 | |||
97 | return hash ^ port; | ||
98 | } | ||
99 | |||
100 | |||
84 | int udp_v6_get_port(struct sock *sk, unsigned short snum) | 101 | int udp_v6_get_port(struct sock *sk, unsigned short snum) |
85 | { | 102 | { |
103 | /* precompute partial secondary hash */ | ||
104 | udp_sk(sk)->udp_portaddr_hash = | ||
105 | udp6_portaddr_hash(sock_net(sk), | ||
106 | &inet6_sk(sk)->rcv_saddr, | ||
107 | 0); | ||
86 | return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal); | 108 | return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal); |
87 | } | 109 | } |
88 | 110 | ||
@@ -94,7 +116,7 @@ static inline int compute_score(struct sock *sk, struct net *net, | |||
94 | { | 116 | { |
95 | int score = -1; | 117 | int score = -1; |
96 | 118 | ||
97 | if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && | 119 | if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum && |
98 | sk->sk_family == PF_INET6) { | 120 | sk->sk_family == PF_INET6) { |
99 | struct ipv6_pinfo *np = inet6_sk(sk); | 121 | struct ipv6_pinfo *np = inet6_sk(sk); |
100 | struct inet_sock *inet = inet_sk(sk); | 122 | struct inet_sock *inet = inet_sk(sk); |
@@ -415,7 +437,8 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk, | |||
415 | if (!net_eq(sock_net(s), net)) | 437 | if (!net_eq(sock_net(s), net)) |
416 | continue; | 438 | continue; |
417 | 439 | ||
418 | if (s->sk_hash == num && s->sk_family == PF_INET6) { | 440 | if (udp_sk(s)->udp_port_hash == num && |
441 | s->sk_family == PF_INET6) { | ||
419 | struct ipv6_pinfo *np = inet6_sk(s); | 442 | struct ipv6_pinfo *np = inet6_sk(s); |
420 | if (inet->inet_dport) { | 443 | if (inet->inet_dport) { |
421 | if (inet->inet_dport != rmt_port) | 444 | if (inet->inet_dport != rmt_port) |