diff options
-rw-r--r-- | include/net/udp.h | 18 | ||||
-rw-r--r-- | net/ipv4/udp.c | 96 | ||||
-rw-r--r-- | net/ipv6/udp.c | 76 |
3 files changed, 64 insertions, 126 deletions
diff --git a/include/net/udp.h b/include/net/udp.h index 766fba1369ce..c490a0f662ac 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -30,25 +30,9 @@ | |||
30 | 30 | ||
31 | #define UDP_HTABLE_SIZE 128 | 31 | #define UDP_HTABLE_SIZE 128 |
32 | 32 | ||
33 | /* udp.c: This needs to be shared by v4 and v6 because the lookup | ||
34 | * and hashing code needs to work with different AF's yet | ||
35 | * the port space is shared. | ||
36 | */ | ||
37 | extern struct hlist_head udp_hash[UDP_HTABLE_SIZE]; | 33 | extern struct hlist_head udp_hash[UDP_HTABLE_SIZE]; |
38 | extern rwlock_t udp_hash_lock; | 34 | extern rwlock_t udp_hash_lock; |
39 | 35 | ||
40 | extern int udp_port_rover; | ||
41 | |||
42 | static inline int udp_lport_inuse(u16 num) | ||
43 | { | ||
44 | struct sock *sk; | ||
45 | struct hlist_node *node; | ||
46 | |||
47 | sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)]) | ||
48 | if (inet_sk(sk)->num == num) | ||
49 | return 1; | ||
50 | return 0; | ||
51 | } | ||
52 | 36 | ||
53 | /* Note: this must match 'valbool' in sock_setsockopt */ | 37 | /* Note: this must match 'valbool' in sock_setsockopt */ |
54 | #define UDP_CSUM_NOXMIT 1 | 38 | #define UDP_CSUM_NOXMIT 1 |
@@ -63,6 +47,8 @@ extern struct proto udp_prot; | |||
63 | 47 | ||
64 | struct sk_buff; | 48 | struct sk_buff; |
65 | 49 | ||
50 | extern int udp_get_port(struct sock *sk, unsigned short snum, | ||
51 | int (*saddr_cmp)(struct sock *, struct sock *)); | ||
66 | extern void udp_err(struct sk_buff *, u32); | 52 | extern void udp_err(struct sk_buff *, u32); |
67 | 53 | ||
68 | extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, | 54 | extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 514c1e9ae810..7552b50bcd84 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -118,14 +118,34 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly; | |||
118 | struct hlist_head udp_hash[UDP_HTABLE_SIZE]; | 118 | struct hlist_head udp_hash[UDP_HTABLE_SIZE]; |
119 | DEFINE_RWLOCK(udp_hash_lock); | 119 | DEFINE_RWLOCK(udp_hash_lock); |
120 | 120 | ||
121 | /* Shared by v4/v6 udp. */ | 121 | /* Shared by v4/v6 udp_get_port */ |
122 | int udp_port_rover; | 122 | int udp_port_rover; |
123 | 123 | ||
124 | static int udp_v4_get_port(struct sock *sk, unsigned short snum) | 124 | static inline int udp_lport_inuse(u16 num) |
125 | { | 125 | { |
126 | struct sock *sk; | ||
126 | struct hlist_node *node; | 127 | struct hlist_node *node; |
128 | |||
129 | sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)]) | ||
130 | if (inet_sk(sk)->num == num) | ||
131 | return 1; | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * udp_get_port - common port lookup for IPv4 and IPv6 | ||
137 | * | ||
138 | * @sk: socket struct in question | ||
139 | * @snum: port number to look up | ||
140 | * @saddr_comp: AF-dependent comparison of bound local IP addresses | ||
141 | */ | ||
142 | int udp_get_port(struct sock *sk, unsigned short snum, | ||
143 | int (*saddr_cmp)(struct sock *sk1, struct sock *sk2)) | ||
144 | { | ||
145 | struct hlist_node *node; | ||
146 | struct hlist_head *head; | ||
127 | struct sock *sk2; | 147 | struct sock *sk2; |
128 | struct inet_sock *inet = inet_sk(sk); | 148 | int error = 1; |
129 | 149 | ||
130 | write_lock_bh(&udp_hash_lock); | 150 | write_lock_bh(&udp_hash_lock); |
131 | if (snum == 0) { | 151 | if (snum == 0) { |
@@ -137,11 +157,10 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum) | |||
137 | best_size_so_far = 32767; | 157 | best_size_so_far = 32767; |
138 | best = result = udp_port_rover; | 158 | best = result = udp_port_rover; |
139 | for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { | 159 | for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { |
140 | struct hlist_head *list; | ||
141 | int size; | 160 | int size; |
142 | 161 | ||
143 | list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)]; | 162 | head = &udp_hash[result & (UDP_HTABLE_SIZE - 1)]; |
144 | if (hlist_empty(list)) { | 163 | if (hlist_empty(head)) { |
145 | if (result > sysctl_local_port_range[1]) | 164 | if (result > sysctl_local_port_range[1]) |
146 | result = sysctl_local_port_range[0] + | 165 | result = sysctl_local_port_range[0] + |
147 | ((result - sysctl_local_port_range[0]) & | 166 | ((result - sysctl_local_port_range[0]) & |
@@ -149,12 +168,11 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum) | |||
149 | goto gotit; | 168 | goto gotit; |
150 | } | 169 | } |
151 | size = 0; | 170 | size = 0; |
152 | sk_for_each(sk2, node, list) | 171 | sk_for_each(sk2, node, head) |
153 | if (++size >= best_size_so_far) | 172 | if (++size < best_size_so_far) { |
154 | goto next; | 173 | best_size_so_far = size; |
155 | best_size_so_far = size; | 174 | best = result; |
156 | best = result; | 175 | } |
157 | next:; | ||
158 | } | 176 | } |
159 | result = best; | 177 | result = best; |
160 | for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { | 178 | for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { |
@@ -170,38 +188,44 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum) | |||
170 | gotit: | 188 | gotit: |
171 | udp_port_rover = snum = result; | 189 | udp_port_rover = snum = result; |
172 | } else { | 190 | } else { |
173 | sk_for_each(sk2, node, | 191 | head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; |
174 | &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) { | 192 | |
175 | struct inet_sock *inet2 = inet_sk(sk2); | 193 | sk_for_each(sk2, node, head) |
176 | 194 | if (inet_sk(sk2)->num == snum && | |
177 | if (inet2->num == snum && | 195 | sk2 != sk && |
178 | sk2 != sk && | 196 | (!sk2->sk_reuse || !sk->sk_reuse) && |
179 | !ipv6_only_sock(sk2) && | 197 | (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if |
180 | (!sk2->sk_bound_dev_if || | 198 | || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && |
181 | !sk->sk_bound_dev_if || | 199 | (*saddr_cmp)(sk, sk2) ) |
182 | sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && | ||
183 | (!inet2->rcv_saddr || | ||
184 | !inet->rcv_saddr || | ||
185 | inet2->rcv_saddr == inet->rcv_saddr) && | ||
186 | (!sk2->sk_reuse || !sk->sk_reuse)) | ||
187 | goto fail; | 200 | goto fail; |
188 | } | ||
189 | } | 201 | } |
190 | inet->num = snum; | 202 | inet_sk(sk)->num = snum; |
191 | if (sk_unhashed(sk)) { | 203 | if (sk_unhashed(sk)) { |
192 | struct hlist_head *h = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; | 204 | head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; |
193 | 205 | sk_add_node(sk, head); | |
194 | sk_add_node(sk, h); | ||
195 | sock_prot_inc_use(sk->sk_prot); | 206 | sock_prot_inc_use(sk->sk_prot); |
196 | } | 207 | } |
197 | write_unlock_bh(&udp_hash_lock); | 208 | error = 0; |
198 | return 0; | ||
199 | |||
200 | fail: | 209 | fail: |
201 | write_unlock_bh(&udp_hash_lock); | 210 | write_unlock_bh(&udp_hash_lock); |
202 | return 1; | 211 | return error; |
212 | } | ||
213 | |||
214 | static inline int ipv4_rcv_saddr_equal(struct sock *sk1, struct sock *sk2) | ||
215 | { | ||
216 | struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2); | ||
217 | |||
218 | return ( !ipv6_only_sock(sk2) && | ||
219 | (!inet1->rcv_saddr || !inet2->rcv_saddr || | ||
220 | inet1->rcv_saddr == inet2->rcv_saddr )); | ||
221 | } | ||
222 | |||
223 | static inline int udp_v4_get_port(struct sock *sk, unsigned short snum) | ||
224 | { | ||
225 | return udp_get_port(sk, snum, ipv4_rcv_saddr_equal); | ||
203 | } | 226 | } |
204 | 227 | ||
228 | |||
205 | static void udp_v4_hash(struct sock *sk) | 229 | static void udp_v4_hash(struct sock *sk) |
206 | { | 230 | { |
207 | BUG(); | 231 | BUG(); |
@@ -1596,7 +1620,7 @@ EXPORT_SYMBOL(udp_disconnect); | |||
1596 | EXPORT_SYMBOL(udp_hash); | 1620 | EXPORT_SYMBOL(udp_hash); |
1597 | EXPORT_SYMBOL(udp_hash_lock); | 1621 | EXPORT_SYMBOL(udp_hash_lock); |
1598 | EXPORT_SYMBOL(udp_ioctl); | 1622 | EXPORT_SYMBOL(udp_ioctl); |
1599 | EXPORT_SYMBOL(udp_port_rover); | 1623 | EXPORT_SYMBOL(udp_get_port); |
1600 | EXPORT_SYMBOL(udp_prot); | 1624 | EXPORT_SYMBOL(udp_prot); |
1601 | EXPORT_SYMBOL(udp_sendmsg); | 1625 | EXPORT_SYMBOL(udp_sendmsg); |
1602 | EXPORT_SYMBOL(udp_poll); | 1626 | EXPORT_SYMBOL(udp_poll); |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b9cc55ccb000..9662561701d1 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -61,81 +61,9 @@ | |||
61 | 61 | ||
62 | DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly; | 62 | DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly; |
63 | 63 | ||
64 | /* Grrr, addr_type already calculated by caller, but I don't want | 64 | static inline int udp_v6_get_port(struct sock *sk, unsigned short snum) |
65 | * to add some silly "cookie" argument to this method just for that. | ||
66 | */ | ||
67 | static int udp_v6_get_port(struct sock *sk, unsigned short snum) | ||
68 | { | 65 | { |
69 | struct sock *sk2; | 66 | return udp_get_port(sk, snum, ipv6_rcv_saddr_equal); |
70 | struct hlist_node *node; | ||
71 | |||
72 | write_lock_bh(&udp_hash_lock); | ||
73 | if (snum == 0) { | ||
74 | int best_size_so_far, best, result, i; | ||
75 | |||
76 | if (udp_port_rover > sysctl_local_port_range[1] || | ||
77 | udp_port_rover < sysctl_local_port_range[0]) | ||
78 | udp_port_rover = sysctl_local_port_range[0]; | ||
79 | best_size_so_far = 32767; | ||
80 | best = result = udp_port_rover; | ||
81 | for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { | ||
82 | int size; | ||
83 | struct hlist_head *list; | ||
84 | |||
85 | list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)]; | ||
86 | if (hlist_empty(list)) { | ||
87 | if (result > sysctl_local_port_range[1]) | ||
88 | result = sysctl_local_port_range[0] + | ||
89 | ((result - sysctl_local_port_range[0]) & | ||
90 | (UDP_HTABLE_SIZE - 1)); | ||
91 | goto gotit; | ||
92 | } | ||
93 | size = 0; | ||
94 | sk_for_each(sk2, node, list) | ||
95 | if (++size >= best_size_so_far) | ||
96 | goto next; | ||
97 | best_size_so_far = size; | ||
98 | best = result; | ||
99 | next:; | ||
100 | } | ||
101 | result = best; | ||
102 | for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { | ||
103 | if (result > sysctl_local_port_range[1]) | ||
104 | result = sysctl_local_port_range[0] | ||
105 | + ((result - sysctl_local_port_range[0]) & | ||
106 | (UDP_HTABLE_SIZE - 1)); | ||
107 | if (!udp_lport_inuse(result)) | ||
108 | break; | ||
109 | } | ||
110 | if (i >= (1 << 16) / UDP_HTABLE_SIZE) | ||
111 | goto fail; | ||
112 | gotit: | ||
113 | udp_port_rover = snum = result; | ||
114 | } else { | ||
115 | sk_for_each(sk2, node, | ||
116 | &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) { | ||
117 | if (inet_sk(sk2)->num == snum && | ||
118 | sk2 != sk && | ||
119 | (!sk2->sk_bound_dev_if || | ||
120 | !sk->sk_bound_dev_if || | ||
121 | sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && | ||
122 | (!sk2->sk_reuse || !sk->sk_reuse) && | ||
123 | ipv6_rcv_saddr_equal(sk, sk2)) | ||
124 | goto fail; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | inet_sk(sk)->num = snum; | ||
129 | if (sk_unhashed(sk)) { | ||
130 | sk_add_node(sk, &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]); | ||
131 | sock_prot_inc_use(sk->sk_prot); | ||
132 | } | ||
133 | write_unlock_bh(&udp_hash_lock); | ||
134 | return 0; | ||
135 | |||
136 | fail: | ||
137 | write_unlock_bh(&udp_hash_lock); | ||
138 | return 1; | ||
139 | } | 67 | } |
140 | 68 | ||
141 | static void udp_v6_hash(struct sock *sk) | 69 | static void udp_v6_hash(struct sock *sk) |