diff options
Diffstat (limited to 'net/ipv4/inet_hashtables.c')
-rw-r--r-- | net/ipv4/inet_hashtables.c | 41 |
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 | ||
123 | EXPORT_SYMBOL(inet_listen_wlock); | 123 | EXPORT_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 | */ | ||
131 | struct 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 | } | ||