Commit ef012690 authored by Sridhar Samudrala's avatar Sridhar Samudrala

sctp: Fixes a couple of sctp_peeloff() issues.

* data/notifications waiting in the parent socket's receive queue
  should be moved to the peeled-off socket's receive queue.
* sendmsg() to a peer already on an association that has been
  peeled off should fail and not create a new association.
parent 6d727be7
...@@ -1044,6 +1044,9 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, ...@@ -1044,6 +1044,9 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const sockaddr_storage_t *); const sockaddr_storage_t *);
int sctp_has_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr);
int sctp_verify_init(const sctp_association_t *asoc, int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid, sctp_cid_t cid,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
......
...@@ -522,9 +522,9 @@ void __sctp_unhash_established(sctp_association_t *asoc) ...@@ -522,9 +522,9 @@ void __sctp_unhash_established(sctp_association_t *asoc)
} }
/* Look up an association. */ /* Look up an association. */
sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *laddr, sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr, const sockaddr_storage_t *paddr,
sctp_transport_t **transportp) sctp_transport_t **transportp)
{ {
sctp_hashbucket_t *head; sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb; sctp_endpoint_common_t *epb;
...@@ -557,6 +557,36 @@ sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *ladd ...@@ -557,6 +557,36 @@ sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *ladd
return asoc; return asoc;
} }
/* Look up an association. BH-safe. */
sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr,
sctp_transport_t **transportp)
{
sctp_association_t *asoc;
sctp_local_bh_disable();
asoc = __sctp_lookup_association(laddr, paddr, transportp);
sctp_local_bh_enable();
return asoc;
}
/* Is there an association matching the given local and peer addresses? */
int sctp_has_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr)
{
sctp_association_t *asoc;
sctp_transport_t *transport;
if (asoc = sctp_lookup_association(laddr, paddr, &transport)) {
sock_put(asoc->base.sk);
sctp_association_put(asoc);
return 1;
}
return 0;
}
/* /*
* SCTP Implementors Guide, 2.18 Handling of address * SCTP Implementors Guide, 2.18 Handling of address
* parameters within the INIT or INIT-ACK. * parameters within the INIT or INIT-ACK.
...@@ -628,7 +658,7 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb, ...@@ -628,7 +658,7 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
sctp_param2sockaddr(paddr, parm, ntohs(sh->source)); sctp_param2sockaddr(paddr, parm, ntohs(sh->source));
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp); asoc = __sctp_lookup_association(laddr, paddr, transportp);
if (asoc) if (asoc)
return asoc; return asoc;
} }
...@@ -644,7 +674,7 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, ...@@ -644,7 +674,7 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp); asoc = __sctp_lookup_association(laddr, paddr, transportp);
/* Further lookup for INIT-ACK packet. /* Further lookup for INIT-ACK packet.
* SCTP Implementors Guide, 2.18 Handling of address * SCTP Implementors Guide, 2.18 Handling of address
......
...@@ -820,7 +820,32 @@ SCTP_STATIC int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size) ...@@ -820,7 +820,32 @@ SCTP_STATIC int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
/* If a msg_name has been specified, assume this is to be used. */ /* If a msg_name has been specified, assume this is to be used. */
if (msg_name) { if (msg_name) {
/* Look for a matching association on the endpoint. */
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (!asoc) {
struct list_head *pos;
struct sockaddr_storage_list *addr;
sctp_bind_addr_t *bp = &ep->base.bind_addr;
sctp_read_lock(&ep->base.addr_lock);
/* If we could not find a matching association on
* the endpoint, make sure that there is no peeled-
* off association.
*/
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos,
struct sockaddr_storage_list,
list);
if (sctp_has_association(&addr->a, &to)) {
err = -EINVAL;
sctp_read_unlock(&ep->base.addr_lock);
goto out_unlock;
}
}
sctp_read_unlock(&ep->base.addr_lock);
}
} else { } else {
/* For a peeled-off socket, ignore any associd specified by /* For a peeled-off socket, ignore any associd specified by
* the user with SNDRCVINFO. * the user with SNDRCVINFO.
...@@ -1583,6 +1608,8 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1583,6 +1608,8 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
sctp_endpoint_t *newep; sctp_endpoint_t *newep;
sctp_opt_t *oldsp = sctp_sk(oldsk); sctp_opt_t *oldsp = sctp_sk(oldsk);
sctp_opt_t *newsp; sctp_opt_t *newsp;
struct sk_buff *skb, *tmp;
sctp_ulpevent_t *event;
int err = 0; int err = 0;
/* An association cannot be branched off from an already peeled-off /* An association cannot be branched off from an already peeled-off
...@@ -1612,6 +1639,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1612,6 +1639,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
*/ */
newsp->ep = newep; newsp->ep = newep;
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
*/
sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) {
event = (sctp_ulpevent_t *)skb->cb;
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(&newsk->receive_queue, skb);
}
}
/* Set the type of socket to indicate that it is peeled off from the /* Set the type of socket to indicate that it is peeled off from the
* original socket. * original socket.
*/ */
...@@ -1629,39 +1667,46 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval ...@@ -1629,39 +1667,46 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
{ {
sctp_peeloff_arg_t peeloff; sctp_peeloff_arg_t peeloff;
struct socket *newsock; struct socket *newsock;
int err, sd; int retval = 0;
sctp_association_t *assoc; sctp_association_t *assoc;
if (len != sizeof(sctp_peeloff_arg_t)) if (len != sizeof(sctp_peeloff_arg_t))
return -EINVAL; return -EINVAL;
if (copy_from_user(&peeloff, optval, len)) if (copy_from_user(&peeloff, optval, len))
return -EFAULT; return -EFAULT;
sctp_lock_sock(sk);
assoc = sctp_id2assoc(sk, peeloff.associd); assoc = sctp_id2assoc(sk, peeloff.associd);
if (NULL == assoc) if (NULL == assoc) {
return -EINVAL; retval = -EINVAL;
goto out_unlock;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc); SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc);
err = sctp_do_peeloff(assoc, &newsock); retval = sctp_do_peeloff(assoc, &newsock);
if (err < 0) if (retval < 0)
return err; goto out_unlock;
/* Map the socket to an unused fd that can be returned to the user. */ /* Map the socket to an unused fd that can be returned to the user. */
sd = sock_map_fd(newsock); retval = sock_map_fd(newsock);
if (sd < 0) { if (retval < 0) {
sock_release(newsock); sock_release(newsock);
return sd; goto out_unlock;
} }
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n", SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n",
__FUNCTION__, sk, assoc, newsock->sk, sd); __FUNCTION__, sk, assoc, newsock->sk, retval);
/* Return the fd mapped to the new socket. */ /* Return the fd mapped to the new socket. */
peeloff.sd = sd; peeloff.sd = retval;
if (copy_to_user(optval, &peeloff, len)) if (copy_to_user(optval, &peeloff, len))
return -EFAULT; retval = -EFAULT;
return 0; out_unlock:
sctp_release_sock(sk);
return retval;
} }
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
......
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