On Tue, Jul 23, 2019 at 01:37:58AM +0800, Xin Long wrote:
> __sctp_connect is doing quit similar things as sctp_sendmsg_new_asoc.
> To factor out common functions, this patch is to clean up their code
> to make them look more similar:
> 
>   1. create the asoc and add a peer with the 1st addr.
>   2. add peers with the other addrs into this asoc one by one.
> 
> Signed-off-by: Xin Long <lucien....@gmail.com>
> ---
>  net/sctp/socket.c | 211 
> +++++++++++++++++++-----------------------------------
>  1 file changed, 75 insertions(+), 136 deletions(-)
> 
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index 5f92e4a..49837e9 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -1049,154 +1049,108 @@ static int sctp_setsockopt_bindx(struct sock *sk,
>   * Common routine for handling connect() and sctp_connectx().
>   * Connect will come in with just a single address.
>   */
> -static int __sctp_connect(struct sock *sk,
> -                       struct sockaddr *kaddrs,
> -                       int addrs_size, int flags,
> -                       sctp_assoc_t *assoc_id)
> +static int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs,
> +                       int addrs_size, int flags, sctp_assoc_t *assoc_id)
>  {
> -     struct net *net = sock_net(sk);
> -     struct sctp_sock *sp;
> -     struct sctp_endpoint *ep;
> -     struct sctp_association *asoc = NULL;
> -     struct sctp_association *asoc2;
> +     struct sctp_association *old, *asoc;
> +     struct sctp_sock *sp = sctp_sk(sk);
> +     struct sctp_endpoint *ep = sp->ep;
>       struct sctp_transport *transport;
> -     union sctp_addr to;
> +     struct net *net = sock_net(sk);
> +     int addrcnt, walk_size, err;
> +     void *addr_buf = kaddrs;
> +     union sctp_addr *daddr;
>       enum sctp_scope scope;
> +     struct sctp_af *af;
>       long timeo;
> -     int err = 0;
> -     int addrcnt = 0;
> -     int walk_size = 0;
> -     union sctp_addr *sa_addr = NULL;
> -     void *addr_buf;
> -     unsigned short port;
>  
> -     sp = sctp_sk(sk);
> -     ep = sp->ep;
> -
> -     /* connect() cannot be done on a socket that is already in ESTABLISHED
> -      * state - UDP-style peeled off socket or a TCP-style socket that
> -      * is already connected.
> -      * It cannot be done even on a TCP-style listening socket.
> -      */
>       if (sctp_sstate(sk, ESTABLISHED) || sctp_sstate(sk, CLOSING) ||
> -         (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))) {
> -             err = -EISCONN;
> -             goto out_free;
> +         (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)))
> +             return -EISCONN;
> +
> +     daddr = addr_buf;
> +     af = sctp_get_af_specific(daddr->sa.sa_family);
> +     if (!af || af->sockaddr_len > addrs_size)
> +             return -EINVAL;
> +
> +     err = sctp_verify_addr(sk, daddr, af->sockaddr_len);
> +     if (err)
> +             return err;
> +
> +     asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
> +     if (asoc)
> +             return asoc->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
> +                                                          : -EALREADY;
> +
> +     if (sctp_endpoint_is_peeled_off(ep, daddr))
> +             return -EADDRNOTAVAIL;
> +
> +     if (!ep->base.bind_addr.port) {
> +             if (sctp_autobind(sk))
> +                     return -EAGAIN;
> +     } else {
> +             if (ep->base.bind_addr.port < inet_prot_sock(net) &&
> +                 !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
> +                     return -EACCES;
>       }
>  
> -     /* Walk through the addrs buffer and count the number of addresses. */
> -     addr_buf = kaddrs;
> -     while (walk_size < addrs_size) {
> -             struct sctp_af *af;
> +     scope = sctp_scope(daddr);
> +     asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
> +     if (!asoc)
> +             return -ENOMEM;
>  
> -             if (walk_size + sizeof(sa_family_t) > addrs_size) {
> -                     err = -EINVAL;
> -                     goto out_free;
> -             }
> +     err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, GFP_KERNEL);
> +     if (err < 0)
> +             goto out_free;
>  
> -             sa_addr = addr_buf;
> -             af = sctp_get_af_specific(sa_addr->sa.sa_family);
> +     transport = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
> +     if (!transport) {
> +             err = -ENOMEM;
> +             goto out_free;
> +     }
>  
> -             /* If the address family is not supported or if this address
> -              * causes the address buffer to overflow return EINVAL.
> -              */
> -             if (!af || (walk_size + af->sockaddr_len) > addrs_size) {
> -                     err = -EINVAL;
> +     addr_buf += af->sockaddr_len;
> +     walk_size = af->sockaddr_len;
> +     addrcnt = 1;

This variable can be removed (in a follow-up patch). It's only
incremented and never used other than that.

> +     while (walk_size < addrs_size) {
> +             err = -EINVAL;
> +             if (walk_size + sizeof(sa_family_t) > addrs_size)
>                       goto out_free;
> -             }
> -
> -             port = ntohs(sa_addr->v4.sin_port);
>  
> -             /* Save current address so we can work with it */
> -             memcpy(&to, sa_addr, af->sockaddr_len);
> +             daddr = addr_buf;
> +             af = sctp_get_af_specific(daddr->sa.sa_family);
> +             if (!af || af->sockaddr_len + walk_size > addrs_size)
> +                     goto out_free;
>  
> -             err = sctp_verify_addr(sk, &to, af->sockaddr_len);
> -             if (err)
> +             if (asoc->peer.port != ntohs(daddr->v4.sin_port))
>                       goto out_free;
>  
> -             /* Make sure the destination port is correctly set
> -              * in all addresses.
> -              */
> -             if (asoc && asoc->peer.port && asoc->peer.port != port) {
> -                     err = -EINVAL;
> +             err = sctp_verify_addr(sk, daddr, af->sockaddr_len);
> +             if (err)
>                       goto out_free;
> -             }
>  
> -             /* 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);
> -             if (asoc2 && asoc2 != asoc) {
> -                     if (asoc2->state >= SCTP_STATE_ESTABLISHED)
> -                             err = -EISCONN;
> -                     else
> -                             err = -EALREADY;
> +             old = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
> +             if (old && old != asoc) {
> +                     err = old->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
> +                                                                : -EALREADY;
>                       goto out_free;
>               }
>  
> -             /* If we could not find a matching association on the endpoint,
> -              * 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, daddr)) {
>                       err = -EADDRNOTAVAIL;
>                       goto out_free;
>               }
>  
> -             if (!asoc) {
> -                     /* If a bind() or sctp_bindx() is not called prior to
> -                      * an sctp_connectx() call, the system picks an
> -                      * ephemeral port and will choose an address set
> -                      * equivalent to binding with a wildcard address.
> -                      */
> -                     if (!ep->base.bind_addr.port) {
> -                             if (sctp_autobind(sk)) {
> -                                     err = -EAGAIN;
> -                                     goto out_free;
> -                             }
> -                     } else {
> -                             /*
> -                              * If an unprivileged user inherits a 1-many
> -                              * style socket with open associations on a
> -                              * privileged port, it MAY be permitted to
> -                              * accept new associations, but it SHOULD NOT
> -                              * be permitted to open new associations.
> -                              */
> -                             if (ep->base.bind_addr.port <
> -                                 inet_prot_sock(net) &&
> -                                 !ns_capable(net->user_ns,
> -                                 CAP_NET_BIND_SERVICE)) {
> -                                     err = -EACCES;
> -                                     goto out_free;
> -                             }
> -                     }
> -
> -                     scope = sctp_scope(&to);
> -                     asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
> -                     if (!asoc) {
> -                             err = -ENOMEM;
> -                             goto out_free;
> -                     }
> -
> -                     err = sctp_assoc_set_bind_addr_from_ep(asoc, scope,
> -                                                           GFP_KERNEL);
> -                     if (err < 0) {
> -                             goto out_free;
> -                     }
> -
> -             }
> -
> -             /* Prime the peer's transport structures.  */
> -             transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL,
> +             transport = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL,
>                                               SCTP_UNKNOWN);
>               if (!transport) {
>                       err = -ENOMEM;
>                       goto out_free;
>               }
>  
> -             addrcnt++;
> -             addr_buf += af->sockaddr_len;
> +             addr_buf  += af->sockaddr_len;
>               walk_size += af->sockaddr_len;
> +             addrcnt++;
>       }
>  
>       /* In case the user of sctp_connectx() wants an association
> @@ -1209,39 +1163,24 @@ static int __sctp_connect(struct sock *sk,
>       }
>  
>       err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
> -     if (err < 0) {
> +     if (err < 0)
>               goto out_free;
> -     }
>  
>       /* Initialize sk's dport and daddr for getpeername() */
>       inet_sk(sk)->inet_dport = htons(asoc->peer.port);
> -     sp->pf->to_sk_daddr(sa_addr, sk);
> +     sp->pf->to_sk_daddr(daddr, sk);
>       sk->sk_err = 0;
>  
> -     timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
> -
>       if (assoc_id)
>               *assoc_id = asoc->assoc_id;
>  
> -     err = sctp_wait_for_connect(asoc, &timeo);
> -     /* Note: the asoc may be freed after the return of
> -      * sctp_wait_for_connect.
> -      */
> -
> -     /* Don't free association on exit. */
> -     asoc = NULL;
> +     timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
> +     return sctp_wait_for_connect(asoc, &timeo);
>  
>  out_free:
>       pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
>                __func__, asoc, kaddrs, err);
> -
> -     if (asoc) {
> -             /* sctp_primitive_ASSOCIATE may have added this association
> -              * To the hash table, try to unhash it, just in case, its a noop
> -              * if it wasn't hashed so we're safe
> -              */
> -             sctp_association_free(asoc);
> -     }
> +     sctp_association_free(asoc);
>       return err;
>  }
>  
> -- 
> 2.1.0
> 

Reply via email to