summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-10-13 21:01:52 -0700
committerSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-10-13 21:01:52 -0700
commitd7b59481637b5650d58b57483dd00a1824f0a0ee (patch)
treedab0ff4a1e69dd0278c8256236b2d4476224ce86
parentc6b7c07005b1e30086af760da23ad890f64152e6 (diff)
parentef012690c07245bc6b7fc6d126ee7d9d2f796ac9 (diff)
Merge
-rw-r--r--include/net/sctp/structs.h3
-rw-r--r--net/sctp/input.c40
-rw-r--r--net/sctp/socket.c71
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,