Commit d9a91e70 authored by Sridhar Samudrala's avatar Sridhar Samudrala Committed by Jon Grimm

[SCTP] udp-style connect support(non-blocking).

parent 16f3fd6b
...@@ -1060,6 +1060,7 @@ void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc); ...@@ -1060,6 +1060,7 @@ void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc);
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
const union sctp_addr *paddr, const union sctp_addr *paddr,
sctp_transport_t **); sctp_transport_t **);
int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *);
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const union sctp_addr *); const union sctp_addr *);
......
...@@ -301,6 +301,30 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, ...@@ -301,6 +301,30 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
return asoc; return asoc;
} }
/* Look for any peeled off association from the endpoint that matches the
* given peer address.
*/
int sctp_endpoint_is_peeled_off(sctp_endpoint_t *ep,
const union sctp_addr *paddr)
{
struct list_head *pos;
struct sockaddr_storage_list *addr;
sctp_bind_addr_t *bp;
sctp_read_lock(&ep->base.addr_lock);
bp = &ep->base.bind_addr;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
if (sctp_has_association(&addr->a, paddr)) {
sctp_read_unlock(&ep->base.addr_lock);
return 1;
}
}
sctp_read_unlock(&ep->base.addr_lock);
return 0;
}
/* Do delayed input processing. This is scheduled by sctp_rcv(). /* Do delayed input processing. This is scheduled by sctp_rcv().
* This may be called on BH or task time. * This may be called on BH or task time.
*/ */
......
...@@ -91,7 +91,7 @@ static inline void sctp_sk_addr_set(struct sock *, ...@@ -91,7 +91,7 @@ static inline void sctp_sk_addr_set(struct sock *,
union sctp_addr *saveaddr); union sctp_addr *saveaddr);
static inline void sctp_sk_addr_restore(struct sock *, static inline void sctp_sk_addr_restore(struct sock *,
const union sctp_addr *); const union sctp_addr *);
static inline int sctp_sendmsg_verify_name(struct sock *, struct msghdr *); static inline int sctp_verify_addr(struct sock *, struct sockaddr *, int);
static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_do_bind(struct sock *, union sctp_addr *, int);
...@@ -777,7 +777,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -777,7 +777,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
* For a peeled-off socket, msg_name is ignored. * For a peeled-off socket, msg_name is ignored.
*/ */
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) { if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) {
err = sctp_sendmsg_verify_name(sk, msg); err = sctp_verify_addr(sk, (struct sockaddr *)msg->msg_name,
msg->msg_namelen);
if (err) if (err)
return err; return err;
...@@ -826,28 +827,14 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -826,28 +827,14 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* Look for a matching association on the endpoint. */ /* Look for a matching association on the endpoint. */
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (!asoc) { if (!asoc) {
struct list_head *pos; /* If we could not find a matching association on the
struct sockaddr_storage_list *addr; * endpoint, make sure that there is no peeled-off
sctp_bind_addr_t *bp = &ep->base.bind_addr; * association on another socket.
*/
sctp_read_lock(&ep->base.addr_lock); if (sctp_endpoint_is_peeled_off(ep, &to)) {
err = -EADDRNOTAVAIL;
/* If we could not find a matching association on goto out_unlock;
* the endpoint, make sure that there is no peeled-
* off association.
*/
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos,
struct sockaddr_storage_list,
list);
if (sctp_has_association(&addr->a, &to)) {
err = -EINVAL;
sctp_read_unlock(&ep->base.addr_lock);
goto out_unlock;
}
} }
sctp_read_unlock(&ep->base.addr_lock);
} }
} else { } else {
/* For a peeled-off socket, ignore any associd specified by /* For a peeled-off socket, ignore any associd specified by
...@@ -1420,11 +1407,107 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1420,11 +1407,107 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
return retval; return retval;
} }
/* FIXME: Write comments. */ /* API 3.1.6 connect() - UDP Style Syntax
*
* An application may use the connect() call in the UDP model to initiate an
* association without sending data.
*
* The syntax is:
*
* ret = connect(int sd, const struct sockaddr *nam, socklen_t len);
*
* sd: the socket descriptor to have a new association added to.
*
* nam: the address structure (either struct sockaddr_in or struct
* sockaddr_in6 defined in RFC2553 [7]).
*
* len: the size of the address.
*/
SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len) int addr_len)
{ {
return -EOPNOTSUPP; /* STUB */ sctp_opt_t *sp;
sctp_endpoint_t *ep;
sctp_association_t *asoc;
sctp_transport_t *transport;
union sctp_addr to;
sctp_scope_t scope;
int err = 0;
sctp_lock_sock(sk);
SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d)\n",
__FUNCTION__, sk, uaddr, addr_len);
sp = sctp_sk(sk);
ep = sp->ep;
/* connect() cannot be done on a peeled-off socket. */
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) {
err = -EISCONN;
goto out_unlock;
}
err = sctp_verify_addr(sk, uaddr, addr_len);
if (err)
goto out_unlock;
memcpy(&to, uaddr, addr_len);
to.v4.sin_port = ntohs(to.v4.sin_port);
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (asoc) {
if (asoc->state >= SCTP_STATE_ESTABLISHED)
err = -EISCONN;
else
err = -EALREADY;
goto out_unlock;
}
/* 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)) {
err = -EADDRNOTAVAIL;
goto out_unlock;
}
/* If a bind() or sctp_bindx() is not called prior to a connect()
* 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_unlock;
}
}
scope = sctp_scope(&to);
asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!asoc) {
err = -ENOMEM;
goto out_unlock;
}
/* Prime the peer's transport structures. */
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0)
sctp_association_free(asoc);
/* FIXME: Currently we support only non-blocking connect().
* To support blocking connect(), we need to wait for the association
* to be ESTABLISHED before returning.
*/
err = -EINPROGRESS;
out_unlock:
sctp_release_sock(sk);
return err;
} }
/* FIXME: Write comments. */ /* FIXME: Write comments. */
...@@ -2608,17 +2691,17 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int no ...@@ -2608,17 +2691,17 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int no
return NULL; return NULL;
} }
static inline int sctp_sendmsg_verify_name(struct sock *sk, struct msghdr *msg) static inline int sctp_verify_addr(struct sock *sk, struct sockaddr *addr, int addrlen)
{ {
union sctp_addr *sa; union sctp_addr *sa;
if (msg->msg_namelen < sizeof (struct sockaddr)) if (addrlen < sizeof (struct sockaddr))
return -EINVAL; return -EINVAL;
sa = (union sctp_addr *) msg->msg_name; sa = (union sctp_addr *)addr;
switch (sa->sa.sa_family) { switch (sa->sa.sa_family) {
case AF_INET: case AF_INET:
if (msg->msg_namelen < sizeof(struct sockaddr_in)) if (addrlen < sizeof(struct sockaddr_in))
return -EINVAL; return -EINVAL;
break; break;
...@@ -2626,7 +2709,7 @@ static inline int sctp_sendmsg_verify_name(struct sock *sk, struct msghdr *msg) ...@@ -2626,7 +2709,7 @@ static inline int sctp_sendmsg_verify_name(struct sock *sk, struct msghdr *msg)
if (PF_INET == sk->family) if (PF_INET == sk->family)
return -EINVAL; return -EINVAL;
SCTP_V6( SCTP_V6(
if (msg->msg_namelen < sizeof(struct sockaddr_in6)) if (addrlen < sizeof(struct sockaddr_in6))
return -EINVAL; return -EINVAL;
break; break;
); );
......
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