]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/sctp/socket.c
be2net: support multiple TX queues
[mirror_ubuntu-bionic-kernel.git] / net / sctp / socket.c
index 6766913a53e626279a7a2f4eb543e1f9fa7b5263..60038fef3ba14e982ea5317b9f30b41697bc20fc 100644 (file)
@@ -583,10 +583,6 @@ 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.
                 */
@@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock             *sk,
                                                    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:
@@ -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;
 
@@ -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,6 +824,7 @@ 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.
                 */
@@ -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
@@ -3334,6 +3404,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()
  *
@@ -3481,6 +3591,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;
@@ -3763,6 +3876,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;
@@ -3771,13 +3890,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);
@@ -5276,6 +5399,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)
@@ -5460,6 +5605,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;
@@ -6512,6 +6660,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.
@@ -6519,7 +6668,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.