]> Pileus Git - ~andy/linux/blobdiff - net/sctp/socket.c
Merge branch 'for-linus' of git://github.com/gregungerer/m68knommu
[~andy/linux] / net / sctp / socket.c
index d3ccf7973c597402ba6e0783ef583f40d87a39ca..836aa63ee121977d820e4f648135e65e3dd2f5d8 100644 (file)
@@ -476,7 +476,7 @@ static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                /* The list may contain either IPv4 or IPv6 address;
                 * determine the address length for walking thru the list.
                 */
-               sa_addr = (struct sockaddr *)addr_buf;
+               sa_addr = addr_buf;
                af = sctp_get_af_specific(sa_addr->sa_family);
                if (!af) {
                        retval = -EINVAL;
@@ -555,7 +555,7 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
                 */
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
-                       addr = (union sctp_addr *)addr_buf;
+                       addr = addr_buf;
                        af = sctp_get_af_specific(addr->v4.sin_family);
                        if (!af) {
                                retval = -EINVAL;
@@ -583,22 +583,35 @@ static int sctp_send_asconf_add_ip(struct sock            *sk,
                        goto out;
                }
 
-               retval = sctp_send_asconf(asoc, chunk);
-               if (retval)
-                       goto out;
-
                /* Add the new addresses to the bind address list with
                 * use_as_src set to 0.
                 */
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
-                       addr = (union sctp_addr *)addr_buf;
+                       addr = addr_buf;
                        af = sctp_get_af_specific(addr->v4.sin_family);
                        memcpy(&saveaddr, addr, af->sockaddr_len);
                        retval = sctp_add_bind_addr(bp, &saveaddr,
                                                    SCTP_ADDR_NEW, GFP_ATOMIC);
                        addr_buf += af->sockaddr_len;
                }
+               if (asoc->src_out_of_asoc_ok) {
+                       struct sctp_transport *trans;
+
+                       list_for_each_entry(trans,
+                           &asoc->peer.transport_addr_list, transports) {
+                               /* Clear the source and route cache */
+                               dst_release(trans->dst);
+                               trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
+                                   2*asoc->pathmtu, 4380));
+                               trans->ssthresh = asoc->peer.i.a_rwnd;
+                               trans->rto = asoc->rto_initial;
+                               trans->rtt = trans->srtt = trans->rttvar = 0;
+                               sctp_transport_route(trans, NULL,
+                                   sctp_sk(asoc->base.sk));
+                       }
+               }
+               retval = sctp_send_asconf(asoc, chunk);
        }
 
 out:
@@ -646,7 +659,7 @@ static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                        goto err_bindx_rem;
                }
 
-               sa_addr = (union sctp_addr *)addr_buf;
+               sa_addr = addr_buf;
                af = sctp_get_af_specific(sa_addr->sa.sa_family);
                if (!af) {
                        retval = -EINVAL;
@@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
        struct sctp_sockaddr_entry *saddr;
        int                     i;
        int                     retval = 0;
+       int                     stored = 0;
 
+       chunk = NULL;
        if (!sctp_addip_enable)
                return retval;
 
@@ -743,7 +758,7 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
                 */
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
-                       laddr = (union sctp_addr *)addr_buf;
+                       laddr = addr_buf;
                        af = sctp_get_af_specific(laddr->v4.sin_family);
                        if (!af) {
                                retval = -EINVAL;
@@ -766,8 +781,37 @@ static int sctp_send_asconf_del_ip(struct sock             *sk,
                bp = &asoc->base.bind_addr;
                laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
                                               addrcnt, sp);
-               if (!laddr)
-                       continue;
+               if ((laddr == NULL) && (addrcnt == 1)) {
+                       if (asoc->asconf_addr_del_pending)
+                               continue;
+                       asoc->asconf_addr_del_pending =
+                           kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
+                       if (asoc->asconf_addr_del_pending == NULL) {
+                               retval = -ENOMEM;
+                               goto out;
+                       }
+                       asoc->asconf_addr_del_pending->sa.sa_family =
+                                   addrs->sa_family;
+                       asoc->asconf_addr_del_pending->v4.sin_port =
+                                   htons(bp->port);
+                       if (addrs->sa_family == AF_INET) {
+                               struct sockaddr_in *sin;
+
+                               sin = (struct sockaddr_in *)addrs;
+                               asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
+                       } else if (addrs->sa_family == AF_INET6) {
+                               struct sockaddr_in6 *sin6;
+
+                               sin6 = (struct sockaddr_in6 *)addrs;
+                               ipv6_addr_copy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr);
+                       }
+                       SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ",
+                           " at %p\n", asoc, asoc->asconf_addr_del_pending,
+                           asoc->asconf_addr_del_pending);
+                       asoc->src_out_of_asoc_ok = 1;
+                       stored = 1;
+                       goto skip_mkasconf;
+               }
 
                /* We do not need RCU protection throughout this loop
                 * because this is done under a socket lock from the
@@ -780,12 +824,13 @@ static int sctp_send_asconf_del_ip(struct sock            *sk,
                        goto out;
                }
 
+skip_mkasconf:
                /* Reset use_as_src flag for the addresses in the bind address
                 * list that are to be deleted.
                 */
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
-                       laddr = (union sctp_addr *)addr_buf;
+                       laddr = addr_buf;
                        af = sctp_get_af_specific(laddr->v4.sin_family);
                        list_for_each_entry(saddr, &bp->address_list, list) {
                                if (sctp_cmp_addr_exact(&saddr->a, laddr))
@@ -805,12 +850,37 @@ static int sctp_send_asconf_del_ip(struct sock            *sk,
                                             sctp_sk(asoc->base.sk));
                }
 
+               if (stored)
+                       /* We don't need to transmit ASCONF */
+                       continue;
                retval = sctp_send_asconf(asoc, chunk);
        }
 out:
        return retval;
 }
 
+/* set addr events to assocs in the endpoint.  ep and addr_wq must be locked */
+int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
+{
+       struct sock *sk = sctp_opt2sk(sp);
+       union sctp_addr *addr;
+       struct sctp_af *af;
+
+       /* It is safe to write port space in caller. */
+       addr = &addrw->a;
+       addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
+       af = sctp_get_af_specific(addr->sa.sa_family);
+       if (!af)
+               return -EINVAL;
+       if (sctp_verify_addr(sk, addr, af->sockaddr_len))
+               return -EINVAL;
+
+       if (addrw->state == SCTP_ADDR_NEW)
+               return sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1);
+       else
+               return sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1);
+}
+
 /* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
  *
  * API 8.1
@@ -927,7 +997,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
                        return -EINVAL;
                }
 
-               sa_addr = (struct sockaddr *)addr_buf;
+               sa_addr = addr_buf;
                af = sctp_get_af_specific(sa_addr->sa_family);
 
                /* If the address family is not supported or if this address
@@ -1018,7 +1088,7 @@ static int __sctp_connect(struct sock* sk,
                        goto out_free;
                }
 
-               sa_addr = (union sctp_addr *)addr_buf;
+               sa_addr = addr_buf;
                af = sctp_get_af_specific(sa_addr->sa.sa_family);
 
                /* If the address family is not supported or if this address
@@ -3213,11 +3283,11 @@ static int sctp_setsockopt_auth_chunk(struct sock *sk,
                return -EFAULT;
 
        switch (val.sauth_chunk) {
-               case SCTP_CID_INIT:
-               case SCTP_CID_INIT_ACK:
-               case SCTP_CID_SHUTDOWN_COMPLETE:
-               case SCTP_CID_AUTH:
-                       return -EINVAL;
+       case SCTP_CID_INIT:
+       case SCTP_CID_INIT_ACK:
+       case SCTP_CID_SHUTDOWN_COMPLETE:
+       case SCTP_CID_AUTH:
+               return -EINVAL;
        }
 
        /* add this chunk id to the endpoint */
@@ -3360,6 +3430,46 @@ static int sctp_setsockopt_del_key(struct sock *sk,
 
 }
 
+/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ *
+ * This option will enable or disable the use of the automatic generation of
+ * ASCONF chunks to add and delete addresses to an existing association.  Note
+ * that this option has two caveats namely: a) it only affects sockets that
+ * are bound to all addresses available to the SCTP stack, and b) the system
+ * administrator may have an overriding control that turns the ASCONF feature
+ * off no matter what setting the socket option may have.
+ * This option expects an integer boolean flag, where a non-zero value turns on
+ * the option, and a zero value turns off the option.
+ * Note. In this implementation, socket operation overrides default parameter
+ * being set by sysctl as well as FreeBSD implementation
+ */
+static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
+                                       unsigned int optlen)
+{
+       int val;
+       struct sctp_sock *sp = sctp_sk(sk);
+
+       if (optlen < sizeof(int))
+               return -EINVAL;
+       if (get_user(val, (int __user *)optval))
+               return -EFAULT;
+       if (!sctp_is_ep_boundall(sk) && val)
+               return -EINVAL;
+       if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf))
+               return 0;
+
+       if (val == 0 && sp->do_auto_asconf) {
+               list_del(&sp->auto_asconf_list);
+               sp->do_auto_asconf = 0;
+       } else if (val && !sp->do_auto_asconf) {
+               list_add_tail(&sp->auto_asconf_list,
+                   &sctp_auto_asconf_splist);
+               sp->do_auto_asconf = 1;
+       }
+       return 0;
+}
+
 
 /* API 6.2 setsockopt(), getsockopt()
  *
@@ -3507,6 +3617,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_AUTH_DELETE_KEY:
                retval = sctp_setsockopt_del_key(sk, optval, optlen);
                break;
+       case SCTP_AUTO_ASCONF:
+               retval = sctp_setsockopt_auto_asconf(sk, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -3789,6 +3902,12 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        local_bh_disable();
        percpu_counter_inc(&sctp_sockets_allocated);
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+       if (sctp_default_auto_asconf) {
+               list_add_tail(&sp->auto_asconf_list,
+                   &sctp_auto_asconf_splist);
+               sp->do_auto_asconf = 1;
+       } else
+               sp->do_auto_asconf = 0;
        local_bh_enable();
 
        return 0;
@@ -3797,13 +3916,17 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
 /* Cleanup any SCTP per socket resources.  */
 SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
 {
-       struct sctp_endpoint *ep;
+       struct sctp_sock *sp;
 
        SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
 
        /* Release our hold on the endpoint. */
-       ep = sctp_sk(sk)->ep;
-       sctp_endpoint_free(ep);
+       sp = sctp_sk(sk);
+       if (sp->do_auto_asconf) {
+               sp->do_auto_asconf = 0;
+               list_del(&sp->auto_asconf_list);
+       }
+       sctp_endpoint_free(sp->ep);
        local_bh_disable();
        percpu_counter_dec(&sctp_sockets_allocated);
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
@@ -5302,6 +5425,28 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len,
        return 0;
 }
 
+/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ * See the corresponding setsockopt entry as description
+ */
+static int sctp_getsockopt_auto_asconf(struct sock *sk, int len,
+                                  char __user *optval, int __user *optlen)
+{
+       int val = 0;
+
+       if (len < sizeof(int))
+               return -EINVAL;
+
+       len = sizeof(int);
+       if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk))
+               val = 1;
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &val, len))
+               return -EFAULT;
+       return 0;
+}
+
 /*
  * 8.2.6. Get the Current Identifiers of Associations
  *        (SCTP_GET_ASSOC_ID_LIST)
@@ -5486,6 +5631,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
        case SCTP_GET_ASSOC_ID_LIST:
                retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen);
                break;
+       case SCTP_AUTO_ASCONF:
+               retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -6538,6 +6686,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        struct sk_buff *skb, *tmp;
        struct sctp_ulpevent *event;
        struct sctp_bind_hashbucket *head;
+       struct list_head tmplist;
 
        /* Migrate socket buffer sizes and all the socket level options to the
         * new socket.
@@ -6545,7 +6694,12 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        newsk->sk_sndbuf = oldsk->sk_sndbuf;
        newsk->sk_rcvbuf = oldsk->sk_rcvbuf;
        /* Brute force copy old sctp opt. */
-       inet_sk_copy_descendant(newsk, oldsk);
+       if (oldsp->do_auto_asconf) {
+               memcpy(&tmplist, &newsp->auto_asconf_list, sizeof(tmplist));
+               inet_sk_copy_descendant(newsk, oldsk);
+               memcpy(&newsp->auto_asconf_list, &tmplist, sizeof(tmplist));
+       } else
+               inet_sk_copy_descendant(newsk, oldsk);
 
        /* Restore the ep value that was overwritten with the above structure
         * copy.