|
@@ -101,19 +101,43 @@ void inet_put_port(struct sock *sk)
|
|
|
}
|
|
|
EXPORT_SYMBOL(inet_put_port);
|
|
|
|
|
|
-void __inet_inherit_port(struct sock *sk, struct sock *child)
|
|
|
+int __inet_inherit_port(struct sock *sk, struct sock *child)
|
|
|
{
|
|
|
struct inet_hashinfo *table = sk->sk_prot->h.hashinfo;
|
|
|
- const int bhash = inet_bhashfn(sock_net(sk), inet_sk(child)->inet_num,
|
|
|
+ unsigned short port = inet_sk(child)->inet_num;
|
|
|
+ const int bhash = inet_bhashfn(sock_net(sk), port,
|
|
|
table->bhash_size);
|
|
|
struct inet_bind_hashbucket *head = &table->bhash[bhash];
|
|
|
struct inet_bind_bucket *tb;
|
|
|
|
|
|
spin_lock(&head->lock);
|
|
|
tb = inet_csk(sk)->icsk_bind_hash;
|
|
|
+ if (tb->port != port) {
|
|
|
+ /* NOTE: using tproxy and redirecting skbs to a proxy
|
|
|
+ * on a different listener port breaks the assumption
|
|
|
+ * that the listener socket's icsk_bind_hash is the same
|
|
|
+ * as that of the child socket. We have to look up or
|
|
|
+ * create a new bind bucket for the child here. */
|
|
|
+ struct hlist_node *node;
|
|
|
+ inet_bind_bucket_for_each(tb, node, &head->chain) {
|
|
|
+ if (net_eq(ib_net(tb), sock_net(sk)) &&
|
|
|
+ tb->port == port)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (!node) {
|
|
|
+ tb = inet_bind_bucket_create(table->bind_bucket_cachep,
|
|
|
+ sock_net(sk), head, port);
|
|
|
+ if (!tb) {
|
|
|
+ spin_unlock(&head->lock);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
sk_add_bind_node(child, &tb->owners);
|
|
|
inet_csk(child)->icsk_bind_hash = tb;
|
|
|
spin_unlock(&head->lock);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__inet_inherit_port);
|
|
|
|