Commit dd8378b3 authored by Xin Long's avatar Xin Long Committed by David S. Miller

sctp: clean up __sctp_connect

__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.

while at it, also remove the unused 'addrcnt'.
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f40f1177
...@@ -1049,152 +1049,104 @@ static int sctp_setsockopt_bindx(struct sock *sk, ...@@ -1049,152 +1049,104 @@ static int sctp_setsockopt_bindx(struct sock *sk,
* Common routine for handling connect() and sctp_connectx(). * Common routine for handling connect() and sctp_connectx().
* Connect will come in with just a single address. * Connect will come in with just a single address.
*/ */
static int __sctp_connect(struct sock *sk, static int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs,
struct sockaddr *kaddrs, int addrs_size, int flags, sctp_assoc_t *assoc_id)
int addrs_size, int flags,
sctp_assoc_t *assoc_id)
{ {
struct net *net = sock_net(sk); struct sctp_association *old, *asoc;
struct sctp_sock *sp; struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep; struct sctp_endpoint *ep = sp->ep;
struct sctp_association *asoc = NULL;
struct sctp_association *asoc2;
struct sctp_transport *transport; struct sctp_transport *transport;
union sctp_addr to; struct net *net = sock_net(sk);
void *addr_buf = kaddrs;
union sctp_addr *daddr;
enum sctp_scope scope; enum sctp_scope scope;
struct sctp_af *af;
int walk_size, err;
long timeo; 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) || if (sctp_sstate(sk, ESTABLISHED) || sctp_sstate(sk, CLOSING) ||
(sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))) { (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)))
err = -EISCONN; return -EISCONN;
goto out_free;
}
/* Walk through the addrs buffer and count the number of addresses. */ daddr = addr_buf;
addr_buf = kaddrs; af = sctp_get_af_specific(daddr->sa.sa_family);
while (walk_size < addrs_size) { if (!af || af->sockaddr_len > addrs_size)
struct sctp_af *af; return -EINVAL;
if (walk_size + sizeof(sa_family_t) > addrs_size) { err = sctp_verify_addr(sk, daddr, af->sockaddr_len);
err = -EINVAL; if (err)
goto out_free; return err;
}
sa_addr = addr_buf; asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
af = sctp_get_af_specific(sa_addr->sa.sa_family); if (asoc)
return asoc->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
: -EALREADY;
/* If the address family is not supported or if this address if (sctp_endpoint_is_peeled_off(ep, daddr))
* causes the address buffer to overflow return EINVAL. return -EADDRNOTAVAIL;
*/
if (!af || (walk_size + af->sockaddr_len) > addrs_size) {
err = -EINVAL;
goto out_free;
}
port = ntohs(sa_addr->v4.sin_port); 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;
}
/* Save current address so we can work with it */ scope = sctp_scope(daddr);
memcpy(&to, sa_addr, af->sockaddr_len); asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!asoc)
return -ENOMEM;
err = sctp_verify_addr(sk, &to, af->sockaddr_len); err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, GFP_KERNEL);
if (err) if (err < 0)
goto out_free; goto out_free;
/* Make sure the destination port is correctly set transport = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
* in all addresses. if (!transport) {
*/ err = -ENOMEM;
if (asoc && asoc->peer.port && asoc->peer.port != port) {
err = -EINVAL;
goto out_free; goto out_free;
} }
/* Check if there already is a matching association on the addr_buf += af->sockaddr_len;
* endpoint (other than the one created here). walk_size = af->sockaddr_len;
*/ while (walk_size < addrs_size) {
asoc2 = sctp_endpoint_lookup_assoc(ep, &to, &transport); err = -EINVAL;
if (asoc2 && asoc2 != asoc) { if (walk_size + sizeof(sa_family_t) > addrs_size)
if (asoc2->state >= SCTP_STATE_ESTABLISHED)
err = -EISCONN;
else
err = -EALREADY;
goto out_free; goto out_free;
}
/* If we could not find a matching association on the endpoint, daddr = addr_buf;
* make sure that there is no peeled-off association matching af = sctp_get_af_specific(daddr->sa.sa_family);
* the peer address even on another socket. if (!af || af->sockaddr_len + walk_size > addrs_size)
*/
if (sctp_endpoint_is_peeled_off(ep, &to)) {
err = -EADDRNOTAVAIL;
goto out_free; goto out_free;
}
if (!asoc) { if (asoc->peer.port != ntohs(daddr->v4.sin_port))
/* 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; goto out_free;
}
}
scope = sctp_scope(&to); err = sctp_verify_addr(sk, daddr, af->sockaddr_len);
asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL); if (err)
if (!asoc) {
err = -ENOMEM;
goto out_free; goto out_free;
}
err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, old = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
GFP_KERNEL); if (old && old != asoc) {
if (err < 0) { err = old->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
: -EALREADY;
goto out_free; goto out_free;
} }
if (sctp_endpoint_is_peeled_off(ep, daddr)) {
err = -EADDRNOTAVAIL;
goto out_free;
} }
/* Prime the peer's transport structures. */ transport = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL,
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL,
SCTP_UNKNOWN); SCTP_UNKNOWN);
if (!transport) { if (!transport) {
err = -ENOMEM; err = -ENOMEM;
goto out_free; goto out_free;
} }
addrcnt++;
addr_buf += af->sockaddr_len; addr_buf += af->sockaddr_len;
walk_size += af->sockaddr_len; walk_size += af->sockaddr_len;
} }
...@@ -1209,39 +1161,24 @@ static int __sctp_connect(struct sock *sk, ...@@ -1209,39 +1161,24 @@ static int __sctp_connect(struct sock *sk,
} }
err = sctp_primitive_ASSOCIATE(net, asoc, NULL); err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
if (err < 0) { if (err < 0)
goto out_free; goto out_free;
}
/* Initialize sk's dport and daddr for getpeername() */ /* Initialize sk's dport and daddr for getpeername() */
inet_sk(sk)->inet_dport = htons(asoc->peer.port); 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; sk->sk_err = 0;
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
if (assoc_id) if (assoc_id)
*assoc_id = asoc->assoc_id; *assoc_id = asoc->assoc_id;
err = sctp_wait_for_connect(asoc, &timeo); timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
/* Note: the asoc may be freed after the return of return sctp_wait_for_connect(asoc, &timeo);
* sctp_wait_for_connect.
*/
/* Don't free association on exit. */
asoc = NULL;
out_free: out_free:
pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n", pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
__func__, asoc, kaddrs, err); __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; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment