aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/inet_hashtables.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/inet_hashtables.c')
-rw-r--r--net/ipv4/inet_hashtables.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 06cbc6f689c5..88fcba05b7d6 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -121,3 +121,44 @@ void inet_listen_wlock(struct inet_hashinfo *hashinfo)
121} 121}
122 122
123EXPORT_SYMBOL(inet_listen_wlock); 123EXPORT_SYMBOL(inet_listen_wlock);
124
125/*
126 * Don't inline this cruft. Here are some nice properties to exploit here. The
127 * BSD API does not allow a listening sock to specify the remote port nor the
128 * remote address for the connection. So always assume those are both
129 * wildcarded during the search since they can never be otherwise.
130 */
131struct sock *__inet_lookup_listener(const struct hlist_head *head, const u32 daddr,
132 const unsigned short hnum, const int dif)
133{
134 struct sock *result = NULL, *sk;
135 const struct hlist_node *node;
136 int hiscore = -1;
137
138 sk_for_each(sk, node, head) {
139 const struct inet_sock *inet = inet_sk(sk);
140
141 if (inet->num == hnum && !ipv6_only_sock(sk)) {
142 const __u32 rcv_saddr = inet->rcv_saddr;
143 int score = sk->sk_family == PF_INET ? 1 : 0;
144
145 if (rcv_saddr) {
146 if (rcv_saddr != daddr)
147 continue;
148 score += 2;
149 }
150 if (sk->sk_bound_dev_if) {
151 if (sk->sk_bound_dev_if != dif)
152 continue;
153 score += 2;
154 }
155 if (score == 5)
156 return sk;
157 if (score > hiscore) {
158 hiscore = score;
159 result = sk;
160 }
161 }
162 }
163 return result;
164}