diff options
| author | Sridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com> | 2002-10-13 21:01:52 -0700 |
|---|---|---|
| committer | Sridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com> | 2002-10-13 21:01:52 -0700 |
| commit | d7b59481637b5650d58b57483dd00a1824f0a0ee (patch) | |
| tree | dab0ff4a1e69dd0278c8256236b2d4476224ce86 | |
| parent | c6b7c07005b1e30086af760da23ad890f64152e6 (diff) | |
| parent | ef012690c07245bc6b7fc6d126ee7d9d2f796ac9 (diff) | |
Merge
| -rw-r--r-- | include/net/sctp/structs.h | 3 | ||||
| -rw-r--r-- | net/sctp/input.c | 40 | ||||
| -rw-r--r-- | net/sctp/socket.c | 71 |
3 files changed, 96 insertions, 18 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 4581ed653252..04288b3a7814 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -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 *, 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, sctp_cid_t cid, sctp_init_chunk_t *peer_init, diff --git a/net/sctp/input.c b/net/sctp/input.c index deec255627c4..7a83c77dbdf9 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -522,9 +522,9 @@ void __sctp_unhash_established(sctp_association_t *asoc) } /* Look up an association. */ -sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *laddr, - const sockaddr_storage_t *paddr, - sctp_transport_t **transportp) +sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr, + const sockaddr_storage_t *paddr, + sctp_transport_t **transportp) { sctp_hashbucket_t *head; sctp_endpoint_common_t *epb; @@ -557,6 +557,36 @@ hit: 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 * parameters within the INIT or INIT-ACK. @@ -633,7 +663,7 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb, sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm, ntohs(sh->source)); - asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp); + asoc = __sctp_lookup_association(laddr, paddr, transportp); if (asoc) return asoc; } @@ -649,7 +679,7 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, { 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. * SCTP Implementors Guide, 2.18 Handling of address diff --git a/net/sctp/socket.c b/net/sctp/socket.c index bcdffb131a30..b2d3ca856385 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -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 (msg_name) { + /* Look for a matching association on the endpoint. */ 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 { /* For a peeled-off socket, ignore any associd specified by * the user with SNDRCVINFO. @@ -1583,6 +1608,8 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso sctp_endpoint_t *newep; sctp_opt_t *oldsp = sctp_sk(oldsk); sctp_opt_t *newsp; + struct sk_buff *skb, *tmp; + sctp_ulpevent_t *event; int err = 0; /* 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 */ 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 * original socket. */ @@ -1629,39 +1667,46 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval { sctp_peeloff_arg_t peeloff; struct socket *newsock; - int err, sd; + int retval = 0; sctp_association_t *assoc; if (len != sizeof(sctp_peeloff_arg_t)) return -EINVAL; if (copy_from_user(&peeloff, optval, len)) return -EFAULT; + + sctp_lock_sock(sk); + assoc = sctp_id2assoc(sk, peeloff.associd); - if (NULL == assoc) - return -EINVAL; + if (NULL == assoc) { + retval = -EINVAL; + goto out_unlock; + } SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc); - err = sctp_do_peeloff(assoc, &newsock); - if (err < 0) - return err; + retval = sctp_do_peeloff(assoc, &newsock); + if (retval < 0) + goto out_unlock; /* Map the socket to an unused fd that can be returned to the user. */ - sd = sock_map_fd(newsock); - if (sd < 0) { + retval = sock_map_fd(newsock); + if (retval < 0) { sock_release(newsock); - return sd; + goto out_unlock; } 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. */ - peeloff.sd = sd; + peeloff.sd = retval; 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, |
