Commit 87611822 authored by Jon Grimm's avatar Jon Grimm

[SCTP] Peeled off/accepted sockets not in the right bind_bucket.

hlist changes caused the peeloff testcase to fail. Investigation shows
that the peeloff sockets is not bound into bind_bucket, so the
bucket has gone away (original socket closed).  Fixing this, shows
a problem that inet->num wasn't set on peeled off sockets, so autobind 
kicks in creating a new bind_bucket.   Ugh.   One bug had been 
hiding the other one all this time. 

Fix 1) peeledoff/accepted sockets need to have their own socket woven 
into the bind_bucket->owner list.  2) Set inet->num, so autobind 
doesn't think it needs to kick in.  
parent da799759
...@@ -99,7 +99,7 @@ struct sctp_bind_bucket { ...@@ -99,7 +99,7 @@ struct sctp_bind_bucket {
unsigned short fastreuse; unsigned short fastreuse;
struct sctp_bind_bucket *next; struct sctp_bind_bucket *next;
struct sctp_bind_bucket **pprev; struct sctp_bind_bucket **pprev;
struct hlist_head sk_list; struct hlist_head owner;
}; };
struct sctp_bind_hashbucket { struct sctp_bind_hashbucket {
......
...@@ -102,8 +102,6 @@ static void sctp_sock_migrate(struct sock *, struct sock *, ...@@ -102,8 +102,6 @@ static void sctp_sock_migrate(struct sock *, struct sock *,
static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG; static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
extern kmem_cache_t *sctp_bucket_cachep; extern kmem_cache_t *sctp_bucket_cachep;
extern int sctp_assoc_valid(struct sock *sk, struct sctp_association *asoc); extern int sctp_assoc_valid(struct sock *sk, struct sctp_association *asoc);
/* Look up the association by its id. If this is not a UDP-style /* Look up the association by its id. If this is not a UDP-style
...@@ -1620,7 +1618,7 @@ static int sctp_setsockopt_nodelay(struct sock *sk, char *optval, ...@@ -1620,7 +1618,7 @@ static int sctp_setsockopt_nodelay(struct sock *sk, char *optval,
sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1; sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
return 0; return 0;
} }
/* /*
* *
* 7.1.1 SCTP_RTOINFO * 7.1.1 SCTP_RTOINFO
...@@ -1705,10 +1703,10 @@ static int sctp_setsockopt_assocrtx(struct sock *sk, char *optval, ...@@ -1705,10 +1703,10 @@ static int sctp_setsockopt_assocrtx(struct sock *sk, char *optval,
if (assocparams.sasoc_asocmaxrxt != 0) if (assocparams.sasoc_asocmaxrxt != 0)
asoc->max_retrans = assocparams.sasoc_asocmaxrxt; asoc->max_retrans = assocparams.sasoc_asocmaxrxt;
if (assocparams.sasoc_cookie_life != 0) { if (assocparams.sasoc_cookie_life != 0) {
asoc->cookie_life.tv_sec = asoc->cookie_life.tv_sec =
assocparams.sasoc_cookie_life / 1000; assocparams.sasoc_cookie_life / 1000;
asoc->cookie_life.tv_usec = asoc->cookie_life.tv_usec =
(assocparams.sasoc_cookie_life % 1000) (assocparams.sasoc_cookie_life % 1000)
* 1000; * 1000;
} }
} else { } else {
...@@ -1716,9 +1714,9 @@ static int sctp_setsockopt_assocrtx(struct sock *sk, char *optval, ...@@ -1716,9 +1714,9 @@ static int sctp_setsockopt_assocrtx(struct sock *sk, char *optval,
struct sctp_opt *sp = sctp_sk(sk); struct sctp_opt *sp = sctp_sk(sk);
if (assocparams.sasoc_asocmaxrxt != 0) if (assocparams.sasoc_asocmaxrxt != 0)
sp->assocparams.sasoc_asocmaxrxt = sp->assocparams.sasoc_asocmaxrxt =
assocparams.sasoc_asocmaxrxt; assocparams.sasoc_asocmaxrxt;
if (assocparams.sasoc_cookie_life != 0) if (assocparams.sasoc_cookie_life != 0)
sp->assocparams.sasoc_cookie_life = sp->assocparams.sasoc_cookie_life =
assocparams.sasoc_cookie_life; assocparams.sasoc_cookie_life;
} }
...@@ -2151,7 +2149,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) ...@@ -2151,7 +2149,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp->assocparams.sasoc_number_peer_destinations = 0; sp->assocparams.sasoc_number_peer_destinations = 0;
sp->assocparams.sasoc_peer_rwnd = 0; sp->assocparams.sasoc_peer_rwnd = 0;
sp->assocparams.sasoc_local_rwnd = 0; sp->assocparams.sasoc_local_rwnd = 0;
sp->assocparams.sasoc_cookie_life = (sctp_valid_cookie_life / HZ) sp->assocparams.sasoc_cookie_life = (sctp_valid_cookie_life / HZ)
* 1000; * 1000;
/* Initialize default event subscriptions. /* Initialize default event subscriptions.
...@@ -3197,7 +3195,6 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3197,7 +3195,6 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
snum = addr->v4.sin_port; snum = addr->v4.sin_port;
SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum); SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum);
sctp_local_bh_disable(); sctp_local_bh_disable();
if (snum == 0) { if (snum == 0) {
...@@ -3244,7 +3241,6 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3244,7 +3241,6 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
* mutex. * mutex.
*/ */
snum = rover; snum = rover;
pp = NULL;
} else { } else {
/* We are given an specific port number; we verify /* We are given an specific port number; we verify
* that it is not being used. If it is used, we will * that it is not being used. If it is used, we will
...@@ -3256,23 +3252,23 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3256,23 +3252,23 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
sctp_spin_lock(&head->lock); sctp_spin_lock(&head->lock);
for (pp = head->chain; pp; pp = pp->next) { for (pp = head->chain; pp; pp = pp->next) {
if (pp->port == snum) if (pp->port == snum)
break; goto pp_found;
} }
} }
pp = NULL;
goto pp_not_found;
if (pp && !hlist_empty(&pp->sk_list)) { pp_found:
if (!hlist_empty(&pp->owner)) {
/* We had a port hash table hit - there is an /* We had a port hash table hit - there is an
* available port (pp != NULL) and it is being * available port (pp != NULL) and it is being
* used by other socket (pp->sk_list not empty); that other * used by other socket (pp->owner not empty); that other
* socket is going to be sk2. * socket is going to be sk2.
*/ */
int reuse = sk->sk_reuse; int reuse = sk->sk_reuse;
struct sock *sk2; struct sock *sk2;
struct hlist_node *node; struct hlist_node *node;
SCTP_DEBUG_PRINTK("sctp_get_port() found a " SCTP_DEBUG_PRINTK("sctp_get_port() found a possible match\n");
"possible match\n");
if (pp->fastreuse && sk->sk_reuse) if (pp->fastreuse && sk->sk_reuse)
goto success; goto success;
...@@ -3286,7 +3282,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3286,7 +3282,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
* that this port/socket (sk) combination are already * that this port/socket (sk) combination are already
* in an endpoint. * in an endpoint.
*/ */
sk_for_each_bound(sk2, node, &pp->sk_list) { sk_for_each_bound(sk2, node, &pp->owner) {
struct sctp_endpoint *ep2; struct sctp_endpoint *ep2;
ep2 = sctp_sk(sk2)->ep; ep2 = sctp_sk(sk2)->ep;
...@@ -3301,10 +3297,9 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3301,10 +3297,9 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
} }
SCTP_DEBUG_PRINTK("sctp_get_port(): Found a match\n"); SCTP_DEBUG_PRINTK("sctp_get_port(): Found a match\n");
} }
pp_not_found:
/* If there was a hash table miss, create a new port. */ /* If there was a hash table miss, create a new port. */
ret = 1; ret = 1;
if (!pp && !(pp = sctp_bucket_create(head, snum))) if (!pp && !(pp = sctp_bucket_create(head, snum)))
goto fail_unlock; goto fail_unlock;
...@@ -3312,7 +3307,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3312,7 +3307,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
* if sk->sk_reuse is too (that is, if the caller requested * if sk->sk_reuse is too (that is, if the caller requested
* SO_REUSEADDR on this socket -sk-). * SO_REUSEADDR on this socket -sk-).
*/ */
if (hlist_empty(&pp->sk_list)) if (hlist_empty(&pp->owner))
pp->fastreuse = sk->sk_reuse ? 1 : 0; pp->fastreuse = sk->sk_reuse ? 1 : 0;
else if (pp->fastreuse && !sk->sk_reuse) else if (pp->fastreuse && !sk->sk_reuse)
pp->fastreuse = 0; pp->fastreuse = 0;
...@@ -3324,7 +3319,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3324,7 +3319,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
success: success:
inet_sk(sk)->num = snum; inet_sk(sk)->num = snum;
if (!sctp_sk(sk)->bind_hash) { if (!sctp_sk(sk)->bind_hash) {
sk_add_bind_node(sk, &pp->sk_list); sk_add_bind_node(sk, &pp->owner);
sctp_sk(sk)->bind_hash = pp; sctp_sk(sk)->bind_hash = pp;
} }
ret = 0; ret = 0;
...@@ -3334,8 +3329,6 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -3334,8 +3329,6 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
fail: fail:
sctp_local_bh_enable(); sctp_local_bh_enable();
SCTP_DEBUG_PRINTK("sctp_get_port() ends, ret=%d\n", ret);
addr->v4.sin_port = htons(addr->v4.sin_port); addr->v4.sin_port = htons(addr->v4.sin_port);
return ret; return ret;
} }
...@@ -3561,7 +3554,7 @@ static struct sctp_bind_bucket *sctp_bucket_create( ...@@ -3561,7 +3554,7 @@ static struct sctp_bind_bucket *sctp_bucket_create(
if (pp) { if (pp) {
pp->port = snum; pp->port = snum;
pp->fastreuse = 0; pp->fastreuse = 0;
INIT_HLIST_HEAD(&pp->sk_list); INIT_HLIST_HEAD(&pp->owner);
if ((pp->next = head->chain) != NULL) if ((pp->next = head->chain) != NULL)
pp->next->pprev = &pp->next; pp->next->pprev = &pp->next;
head->chain = pp; head->chain = pp;
...@@ -3573,7 +3566,7 @@ static struct sctp_bind_bucket *sctp_bucket_create( ...@@ -3573,7 +3566,7 @@ static struct sctp_bind_bucket *sctp_bucket_create(
/* Caller must hold hashbucket lock for this tb with local BH disabled */ /* Caller must hold hashbucket lock for this tb with local BH disabled */
static void sctp_bucket_destroy(struct sctp_bind_bucket *pp) static void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
{ {
if (hlist_empty(&pp->sk_list)) { if (hlist_empty(&pp->owner)) {
if (pp->next) if (pp->next)
pp->next->pprev = pp->pprev; pp->next->pprev = pp->pprev;
*(pp->pprev) = pp->next; *(pp->pprev) = pp->next;
...@@ -3582,8 +3575,8 @@ static void sctp_bucket_destroy(struct sctp_bind_bucket *pp) ...@@ -3582,8 +3575,8 @@ static void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
} }
} }
/* FIXME: Comments! */ /* Release this socket's reference to a local port. */
static __inline__ void __sctp_put_port(struct sock *sk) static inline void __sctp_put_port(struct sock *sk)
{ {
struct sctp_bind_hashbucket *head = struct sctp_bind_hashbucket *head =
&sctp_port_hashtable[sctp_phashfn(inet_sk(sk)->num)]; &sctp_port_hashtable[sctp_phashfn(inet_sk(sk)->num)];
...@@ -4187,6 +4180,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, ...@@ -4187,6 +4180,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
{ {
struct sctp_opt *oldsp = sctp_sk(oldsk); struct sctp_opt *oldsp = sctp_sk(oldsk);
struct sctp_opt *newsp = sctp_sk(newsk); struct sctp_opt *newsp = sctp_sk(newsk);
struct sctp_bind_bucket *pp; /* hash list port iterator */
struct sctp_endpoint *newep = newsp->ep; struct sctp_endpoint *newep = newsp->ep;
struct sk_buff *skb, *tmp; struct sk_buff *skb, *tmp;
struct sctp_ulpevent *event; struct sctp_ulpevent *event;
...@@ -4196,7 +4190,8 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, ...@@ -4196,7 +4190,8 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
*/ */
newsk->sk_sndbuf = oldsk->sk_sndbuf; newsk->sk_sndbuf = oldsk->sk_sndbuf;
newsk->sk_rcvbuf = oldsk->sk_rcvbuf; newsk->sk_rcvbuf = oldsk->sk_rcvbuf;
*newsp = *oldsp; /* Brute force copy old sctp opt. */
memcpy(newsp, oldsp, sizeof(struct sctp_opt));
/* Restore the ep value that was overwritten with the above structure /* Restore the ep value that was overwritten with the above structure
* copy. * copy.
...@@ -4204,6 +4199,12 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, ...@@ -4204,6 +4199,12 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
newsp->ep = newep; newsp->ep = newep;
newsp->hmac = NULL; newsp->hmac = NULL;
/* Hook this new socket in to the bind_hash list. */
pp = sctp_sk(oldsk)->bind_hash;
sk_add_bind_node(newsk, &pp->owner);
sctp_sk(newsk)->bind_hash = pp;
inet_sk(newsk)->num = inet_sk(oldsk)->num;
/* Move any messages in the old socket's receive queue that are for the /* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue. * peeled off association to the new socket's receive queue.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment