aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r--net/ipv4/udp.c96
1 files changed, 60 insertions, 36 deletions
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;
118struct hlist_head udp_hash[UDP_HTABLE_SIZE]; 118struct hlist_head udp_hash[UDP_HTABLE_SIZE];
119DEFINE_RWLOCK(udp_hash_lock); 119DEFINE_RWLOCK(udp_hash_lock);
120 120
121/* Shared by v4/v6 udp. */ 121/* Shared by v4/v6 udp_get_port */
122int udp_port_rover; 122int udp_port_rover;
123 123
124static int udp_v4_get_port(struct sock *sk, unsigned short snum) 124static 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 */
142int 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)
170gotit: 188gotit:
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
200fail: 209fail:
201 write_unlock_bh(&udp_hash_lock); 210 write_unlock_bh(&udp_hash_lock);
202 return 1; 211 return error;
212}
213
214static 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
223static 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
205static void udp_v4_hash(struct sock *sk) 229static void udp_v4_hash(struct sock *sk)
206{ 230{
207 BUG(); 231 BUG();
@@ -1596,7 +1620,7 @@ EXPORT_SYMBOL(udp_disconnect);
1596EXPORT_SYMBOL(udp_hash); 1620EXPORT_SYMBOL(udp_hash);
1597EXPORT_SYMBOL(udp_hash_lock); 1621EXPORT_SYMBOL(udp_hash_lock);
1598EXPORT_SYMBOL(udp_ioctl); 1622EXPORT_SYMBOL(udp_ioctl);
1599EXPORT_SYMBOL(udp_port_rover); 1623EXPORT_SYMBOL(udp_get_port);
1600EXPORT_SYMBOL(udp_prot); 1624EXPORT_SYMBOL(udp_prot);
1601EXPORT_SYMBOL(udp_sendmsg); 1625EXPORT_SYMBOL(udp_sendmsg);
1602EXPORT_SYMBOL(udp_poll); 1626EXPORT_SYMBOL(udp_poll);