]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/sctp/socket.c
Merge branch 'for-linus' of git://brick.kernel.dk/data/git/linux-2.6-block
[mv-sheeva.git] / net / sctp / socket.c
index 9f34dec6ff8ec0cd70acfb72e594f016c60a36a8..bdd8bd428b64f3de8ae4fff0c9414fce324ddf30 100644 (file)
@@ -107,7 +107,7 @@ static void sctp_sock_migrate(struct sock *, struct sock *,
                              struct sctp_association *, sctp_socket_type_t);
 static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
 
-extern kmem_cache_t *sctp_bucket_cachep;
+extern struct kmem_cache *sctp_bucket_cachep;
 
 /* Get the sndbuf space available at the time on the association.  */
 static inline int sctp_wspace(struct sctp_association *asoc)
@@ -229,11 +229,9 @@ static struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
        struct sctp_transport *transport;
        union sctp_addr *laddr = (union sctp_addr *)addr;
 
-       laddr->v4.sin_port = ntohs(laddr->v4.sin_port);
        addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep,
-                                              (union sctp_addr *)addr,
+                                              laddr,
                                               &transport);
-       laddr->v4.sin_port = htons(laddr->v4.sin_port);
 
        if (!addr_asoc)
                return NULL;
@@ -368,9 +366,7 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        sctp_write_lock(&ep->base.addr_lock);
 
        /* Use GFP_ATOMIC since BHs are disabled.  */
-       addr->v4.sin_port = ntohs(addr->v4.sin_port);
        ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC);
-       addr->v4.sin_port = htons(addr->v4.sin_port);
        sctp_write_unlock(&ep->base.addr_lock);
        sctp_local_bh_enable();
 
@@ -572,7 +568,6 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
                        addr = (union sctp_addr *)addr_buf;
                        af = sctp_get_af_specific(addr->v4.sin_family);
                        memcpy(&saveaddr, addr, af->sockaddr_len);
-                       saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
                        retval = sctp_add_bind_addr(bp, &saveaddr, 0,
                                                    GFP_ATOMIC);
                        addr_buf += af->sockaddr_len;
@@ -607,9 +602,8 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
        int cnt;
        struct sctp_bind_addr *bp = &ep->base.bind_addr;
        int retval = 0;
-       union sctp_addr saveaddr;
        void *addr_buf;
-       struct sockaddr *sa_addr;
+       union sctp_addr *sa_addr;
        struct sctp_af *af;
 
        SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",
@@ -627,19 +621,13 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                        goto err_bindx_rem;
                }
 
-               /* The list may contain either IPv4 or IPv6 address;
-                * determine the address length to copy the address to
-                * saveaddr. 
-                */
-               sa_addr = (struct sockaddr *)addr_buf;
-               af = sctp_get_af_specific(sa_addr->sa_family);
+               sa_addr = (union sctp_addr *)addr_buf;
+               af = sctp_get_af_specific(sa_addr->sa.sa_family);
                if (!af) {
                        retval = -EINVAL;
                        goto err_bindx_rem;
                }
-               memcpy(&saveaddr, sa_addr, af->sockaddr_len); 
-               saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
-               if (saveaddr.v4.sin_port != bp->port) {
+               if (sa_addr->v4.sin_port != htons(bp->port)) {
                        retval = -EINVAL;
                        goto err_bindx_rem;
                }
@@ -654,7 +642,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                sctp_local_bh_disable();
                sctp_write_lock(&ep->base.addr_lock);
 
-               retval = sctp_del_bind_addr(bp, &saveaddr);
+               retval = sctp_del_bind_addr(bp, sa_addr);
 
                sctp_write_unlock(&ep->base.addr_lock);
                sctp_local_bh_enable();
@@ -693,7 +681,6 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
        struct sctp_bind_addr   *bp;
        struct sctp_chunk       *chunk;
        union sctp_addr         *laddr;
-       union sctp_addr         saveaddr;
        void                    *addr_buf;
        struct sctp_af          *af;
        struct list_head        *pos, *pos1;
@@ -773,13 +760,11 @@ static int sctp_send_asconf_del_ip(struct sock            *sk,
                for (i = 0; i < addrcnt; i++) {
                        laddr = (union sctp_addr *)addr_buf;
                        af = sctp_get_af_specific(laddr->v4.sin_family);
-                       memcpy(&saveaddr, laddr, af->sockaddr_len);
-                       saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
                        list_for_each(pos1, &bp->address_list) {
                                saddr = list_entry(pos1,
                                                   struct sctp_sockaddr_entry,
                                                   list);
-                               if (sctp_cmp_addr_exact(&saddr->a, &saveaddr))
+                               if (sctp_cmp_addr_exact(&saddr->a, laddr))
                                        saddr->use_as_src = 0;
                        }
                        addr_buf += af->sockaddr_len;
@@ -979,7 +964,7 @@ static int __sctp_connect(struct sock* sk,
        int err = 0;
        int addrcnt = 0;
        int walk_size = 0;
-       struct sockaddr *sa_addr;
+       union sctp_addr *sa_addr;
        void *addr_buf;
 
        sp = sctp_sk(sk);
@@ -999,8 +984,8 @@ static int __sctp_connect(struct sock* sk,
        /* Walk through the addrs buffer and count the number of addresses. */
        addr_buf = kaddrs;
        while (walk_size < addrs_size) {
-               sa_addr = (struct sockaddr *)addr_buf;
-               af = sctp_get_af_specific(sa_addr->sa_family);
+               sa_addr = (union sctp_addr *)addr_buf;
+               af = sctp_get_af_specific(sa_addr->sa.sa_family);
 
                /* If the address family is not supported or if this address
                 * causes the address buffer to overflow return EINVAL.
@@ -1010,18 +995,16 @@ static int __sctp_connect(struct sock* sk,
                        goto out_free;
                }
 
-               err = sctp_verify_addr(sk, (union sctp_addr *)sa_addr,
-                                      af->sockaddr_len);
+               err = sctp_verify_addr(sk, sa_addr, af->sockaddr_len);
                if (err)
                        goto out_free;
 
                memcpy(&to, sa_addr, af->sockaddr_len);
-               to.v4.sin_port = ntohs(to.v4.sin_port);
 
                /* Check if there already is a matching association on the
                 * endpoint (other than the one created here).
                 */
-               asoc2 = sctp_endpoint_lookup_assoc(ep, &to, &transport);
+               asoc2 = sctp_endpoint_lookup_assoc(ep, sa_addr, &transport);
                if (asoc2 && asoc2 != asoc) {
                        if (asoc2->state >= SCTP_STATE_ESTABLISHED)
                                err = -EISCONN;
@@ -1034,7 +1017,7 @@ static int __sctp_connect(struct sock* sk,
                 * make sure that there is no peeled-off association matching
                 * the peer address even on another socket.
                 */
-               if (sctp_endpoint_is_peeled_off(ep, &to)) {
+               if (sctp_endpoint_is_peeled_off(ep, sa_addr)) {
                        err = -EADDRNOTAVAIL;
                        goto out_free;
                }
@@ -1065,7 +1048,7 @@ static int __sctp_connect(struct sock* sk,
                                }
                        }
 
-                       scope = sctp_scope(&to);
+                       scope = sctp_scope(sa_addr);
                        asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
                        if (!asoc) {
                                err = -ENOMEM;
@@ -1074,7 +1057,7 @@ static int __sctp_connect(struct sock* sk,
                }
 
                /* Prime the peer's transport structures.  */
-               transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL,
+               transport = sctp_assoc_add_peer(asoc, sa_addr, GFP_KERNEL,
                                                SCTP_UNKNOWN);
                if (!transport) {
                        err = -ENOMEM;
@@ -1427,11 +1410,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                if (msg_namelen > sizeof(to))
                        msg_namelen = sizeof(to);
                memcpy(&to, msg->msg_name, msg_namelen);
-               SCTP_DEBUG_PRINTK("Just memcpy'd. msg_name is "
-                                 "0x%x:%u.\n",
-                                 to.v4.sin_addr.s_addr, to.v4.sin_port);
-
-               to.v4.sin_port = ntohs(to.v4.sin_port);
                msg_name = msg->msg_name;
        }
 
@@ -2768,6 +2746,46 @@ static int sctp_setsockopt_adaption_layer(struct sock *sk, char __user *optval,
        return 0;
 }
 
+/*
+ * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
+ *
+ * The context field in the sctp_sndrcvinfo structure is normally only
+ * used when a failed message is retrieved holding the value that was
+ * sent down on the actual send call.  This option allows the setting of
+ * a default context on an association basis that will be received on
+ * reading messages from the peer.  This is especially helpful in the
+ * one-2-many model for an application to keep some reference to an
+ * internal state machine that is processing messages on the
+ * association.  Note that the setting of this value only effects
+ * received messages from the peer and does not effect the value that is
+ * saved with outbound messages.
+ */
+static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
+                                  int optlen)
+{
+       struct sctp_assoc_value params;
+       struct sctp_sock *sp;
+       struct sctp_association *asoc;
+
+       if (optlen != sizeof(struct sctp_assoc_value))
+               return -EINVAL;
+       if (copy_from_user(&params, optval, optlen))
+               return -EFAULT;
+
+       sp = sctp_sk(sk);
+
+       if (params.assoc_id != 0) {
+               asoc = sctp_id2assoc(sk, params.assoc_id);
+               if (!asoc)
+                       return -EINVAL;
+               asoc->default_rcv_context = params.assoc_value;
+       } else {
+               sp->default_rcv_context = params.assoc_value;
+       }
+
+       return 0;
+}
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -2879,6 +2897,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_ADAPTION_LAYER:
                retval = sctp_setsockopt_adaption_layer(sk, optval, optlen);
                break;
+       case SCTP_CONTEXT:
+               retval = sctp_setsockopt_context(sk, optval, optlen);
+               break;
 
        default:
                retval = -ENOPROTOOPT;
@@ -3038,6 +3059,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->default_context = 0;
        sp->default_timetolive = 0;
 
+       sp->default_rcv_context = 0;
+
        /* Initialize default setup parameters. These parameters
         * can be modified with the SCTP_INITMSG socket option or
         * overridden by the SCTP_INIT CMSG.
@@ -3217,8 +3240,8 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
        status.sstat_outstrms = asoc->c.sinit_num_ostreams;
        status.sstat_fragmentation_point = asoc->frag_point;
        status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
-       memcpy(&status.sstat_primary.spinfo_address,
-              &(transport->ipaddr), sizeof(union sctp_addr));
+       memcpy(&status.sstat_primary.spinfo_address, &transport->ipaddr,
+                       transport->af_specific->sockaddr_len);
        /* Map ipv4 address into v4-mapped-on-v6 address.  */
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
                (union sctp_addr *)&status.sstat_primary.spinfo_address);
@@ -3372,6 +3395,7 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
 {
        struct sock *sk = asoc->base.sk;
        struct socket *sock;
+       struct inet_sock *inetsk;
        int err = 0;
 
        /* An association cannot be branched off from an already peeled-off
@@ -3389,6 +3413,14 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
         * asoc to the newsk.
         */
        sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
+
+       /* Make peeled-off sockets more like 1-1 accepted sockets.
+        * Set the daddr and initialize id to something more random
+        */
+       inetsk = inet_sk(sock->sk);
+       inetsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
+       inetsk->id = asoc->next_tsn ^ jiffies;
+
        *sockp = sock;
 
        return err;
@@ -3761,7 +3793,6 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
                memcpy(&temp, &from->ipaddr, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
-               temp.v4.sin_port = htons(temp.v4.sin_port);
                if (copy_to_user(to, &temp, addrlen))
                        return -EFAULT;
                to += addrlen ;
@@ -3812,7 +3843,6 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
                addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
                if(space_left < addrlen)
                        return -ENOMEM;
-               temp.v4.sin_port = htons(temp.v4.sin_port);
                if (copy_to_user(to, &temp, addrlen))
                        return -EFAULT;
                to += addrlen;
@@ -3836,10 +3866,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
        sctp_assoc_t id;
        struct sctp_bind_addr *bp;
        struct sctp_association *asoc;
-       struct list_head *pos;
+       struct list_head *pos, *temp;
        struct sctp_sockaddr_entry *addr;
        rwlock_t *addr_lock;
-       unsigned long flags;
        int cnt = 0;
 
        if (len != sizeof(sctp_assoc_t))
@@ -3874,18 +3903,15 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
                addr = list_entry(bp->address_list.next,
                                  struct sctp_sockaddr_entry, list);
                if (sctp_is_any(&addr->a)) {
-                       sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
-                       list_for_each(pos, &sctp_local_addr_list) {
+                       list_for_each_safe(pos, temp, &sctp_local_addr_list) {
                                addr = list_entry(pos,
                                                  struct sctp_sockaddr_entry,
                                                  list);
                                if ((PF_INET == sk->sk_family) && 
-                                   (AF_INET6 == addr->a.sa.sa_family)) 
+                                   (AF_INET6 == addr->a.sa.sa_family))
                                        continue;
                                cnt++;
                        }
-                       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
-                                                   flags);
                } else {
                        cnt = 1;
                }
@@ -3907,15 +3933,13 @@ done:
 static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs,
                                        void __user *to)
 {
-       struct list_head *pos;
+       struct list_head *pos, *next;
        struct sctp_sockaddr_entry *addr;
-       unsigned long flags;
        union sctp_addr temp;
        int cnt = 0;
        int addrlen;
 
-       sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
-       list_for_each(pos, &sctp_local_addr_list) {
+       list_for_each_safe(pos, next, &sctp_local_addr_list) {
                addr = list_entry(pos, struct sctp_sockaddr_entry, list);
                if ((PF_INET == sk->sk_family) && 
                    (AF_INET6 == addr->a.sa.sa_family))
@@ -3924,17 +3948,13 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
                                                                &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               temp.v4.sin_port = htons(port);
-               if (copy_to_user(to, &temp, addrlen)) {
-                       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
-                                                   flags);
+               if (copy_to_user(to, &temp, addrlen))
                        return -EFAULT;
-               }
+
                to += addrlen;
                cnt ++;
                if (cnt >= max_addrs) break;
        }
-       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
 
        return cnt;
 }
@@ -3942,15 +3962,13 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add
 static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
                                    void __user **to, size_t space_left)
 {
-       struct list_head *pos;
+       struct list_head *pos, *next;
        struct sctp_sockaddr_entry *addr;
-       unsigned long flags;
        union sctp_addr temp;
        int cnt = 0;
        int addrlen;
 
-       sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
-       list_for_each(pos, &sctp_local_addr_list) {
+       list_for_each_safe(pos, next, &sctp_local_addr_list) {
                addr = list_entry(pos, struct sctp_sockaddr_entry, list);
                if ((PF_INET == sk->sk_family) && 
                    (AF_INET6 == addr->a.sa.sa_family))
@@ -3961,17 +3979,13 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
                if(space_left<addrlen)
                        return -ENOMEM;
-               temp.v4.sin_port = htons(port);
-               if (copy_to_user(*to, &temp, addrlen)) {
-                       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
-                                                   flags);
+               if (copy_to_user(*to, &temp, addrlen))
                        return -EFAULT;
-               }
+
                *to += addrlen;
                cnt ++;
                space_left -= addrlen;
        }
-       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
 
        return cnt;
 }
@@ -4046,7 +4060,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
                memcpy(&temp, &addr->a, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               temp.v4.sin_port = htons(temp.v4.sin_port);
                if (copy_to_user(to, &temp, addrlen)) {
                        err = -EFAULT;
                        goto unlock;
@@ -4137,7 +4150,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
                if(space_left < addrlen)
                        return -ENOMEM; /*fixme: right error?*/
-               temp.v4.sin_port = htons(temp.v4.sin_port);
                if (copy_to_user(to, &temp, addrlen)) {
                        err = -EFAULT;
                        goto unlock;
@@ -4185,12 +4197,8 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        if (!asoc->peer.primary_path)
                return -ENOTCONN;
        
-       asoc->peer.primary_path->ipaddr.v4.sin_port =
-               htons(asoc->peer.primary_path->ipaddr.v4.sin_port);
        memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
-              sizeof(union sctp_addr));
-       asoc->peer.primary_path->ipaddr.v4.sin_port =
-               ntohs(asoc->peer.primary_path->ipaddr.v4.sin_port);
+               asoc->peer.primary_path->af_specific->sockaddr_len);
 
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
                        (union sctp_addr *)&prim.ssp_addr);
@@ -4457,6 +4465,42 @@ static int sctp_getsockopt_mappedv4(struct sock *sk, int len,
        return 0;
 }
 
+/*
+ * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
+ * (chapter and verse is quoted at sctp_setsockopt_context())
+ */
+static int sctp_getsockopt_context(struct sock *sk, int len,
+                                  char __user *optval, int __user *optlen)
+{
+       struct sctp_assoc_value params;
+       struct sctp_sock *sp;
+       struct sctp_association *asoc;
+
+       if (len != sizeof(struct sctp_assoc_value))
+               return -EINVAL;
+
+       if (copy_from_user(&params, optval, len))
+               return -EFAULT;
+
+       sp = sctp_sk(sk);
+
+       if (params.assoc_id != 0) {
+               asoc = sctp_id2assoc(sk, params.assoc_id);
+               if (!asoc)
+                       return -EINVAL;
+               params.assoc_value = asoc->default_rcv_context;
+       } else {
+               params.assoc_value = sp->default_rcv_context;
+       }
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &params, len))
+               return -EFAULT;
+
+       return 0;
+}
+
 /*
  * 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
  *
@@ -4595,6 +4639,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
                retval = sctp_getsockopt_adaption_layer(sk, len, optval,
                                                        optlen);
                break;
+       case SCTP_CONTEXT:
+               retval = sctp_getsockopt_context(sk, len, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -4636,9 +4683,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
        unsigned short snum;
        int ret;
 
-       /* NOTE:  Remember to put this back to net order. */
-       addr->v4.sin_port = ntohs(addr->v4.sin_port);
-       snum = addr->v4.sin_port;
+       snum = ntohs(addr->v4.sin_port);
 
        SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum);
        sctp_local_bh_disable();
@@ -4775,7 +4820,6 @@ fail_unlock:
 
 fail:
        sctp_local_bh_enable();
-       addr->v4.sin_port = htons(addr->v4.sin_port);
        return ret;
 }
 
@@ -5015,7 +5059,7 @@ static struct sctp_bind_bucket *sctp_bucket_create(
 {
        struct sctp_bind_bucket *pp;
 
-       pp = kmem_cache_alloc(sctp_bucket_cachep, SLAB_ATOMIC);
+       pp = kmem_cache_alloc(sctp_bucket_cachep, GFP_ATOMIC);
        SCTP_DBG_OBJCNT_INC(bind_bucket);
        if (pp) {
                pp->port = snum;
@@ -5074,7 +5118,7 @@ static int sctp_autobind(struct sock *sk)
 {
        union sctp_addr autoaddr;
        struct sctp_af *af;
-       unsigned short port;
+       __be16 port;
 
        /* Initialize a local sockaddr structure to INADDR_ANY. */
        af = sctp_sk(sk)->pf->af;