Commit bf953999 authored by Sridhar Samudrala's avatar Sridhar Samudrala

[SCTP] accept() support for TCP-style SCTP sockets.

parent 088e723f
...@@ -262,6 +262,8 @@ struct sctp_pf { ...@@ -262,6 +262,8 @@ struct sctp_pf {
struct sctp_opt *); struct sctp_opt *);
int (*bind_verify) (struct sctp_opt *, union sctp_addr *); int (*bind_verify) (struct sctp_opt *, union sctp_addr *);
int (*supported_addrs)(const struct sctp_opt *, __u16 *); int (*supported_addrs)(const struct sctp_opt *, __u16 *);
struct sock *(*create_accept_sk) (struct sock *sk,
struct sctp_association *asoc);
struct sctp_af *af; struct sctp_af *af;
}; };
......
...@@ -195,6 +195,8 @@ void sctp_endpoint_destroy(sctp_endpoint_t *ep) ...@@ -195,6 +195,8 @@ void sctp_endpoint_destroy(sctp_endpoint_t *ep)
{ {
SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
ep->base.sk->state = SCTP_SS_CLOSED;
/* Unlink this endpoint, so we can't find it again! */ /* Unlink this endpoint, so we can't find it again! */
sctp_unhash_endpoint(ep); sctp_unhash_endpoint(ep);
......
...@@ -432,6 +432,62 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) ...@@ -432,6 +432,62 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr)
return retval; return retval;
} }
/* Create and initialize a new sk for the socket to be returned by accept(). */
struct sock *sctp_v6_create_accept_sk(struct sock *sk,
struct sctp_association *asoc)
{
struct inet_opt *inet = inet_sk(sk);
struct sock *newsk;
struct inet_opt *newinet;
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
struct sctp6_sock *newsctp6sk;
newsk = sk_alloc(PF_INET6, GFP_KERNEL, sizeof(struct sctp6_sock),
sk->slab);
if (!newsk)
goto out;
sock_init_data(NULL, newsk);
newsk->type = SOCK_STREAM;
newsk->prot = sk->prot;
newsk->no_check = sk->no_check;
newsk->reuse = sk->reuse;
newsk->destruct = inet_sock_destruct;
newsk->zapped = 0;
newsk->family = PF_INET6;
newsk->protocol = IPPROTO_SCTP;
newsk->backlog_rcv = sk->prot->backlog_rcv;
newsctp6sk = (struct sctp6_sock *)newsk;
newsctp6sk->pinet6 = &newsctp6sk->inet6;
newinet = inet_sk(newsk);
newnp = inet6_sk(newsk);
memcpy(newnp, np, sizeof(struct ipv6_pinfo));
ipv6_addr_copy(&newnp->daddr, &asoc->peer.primary_addr.v6.sin6_addr);
newinet->sport = inet->sport;
newinet->dport = asoc->peer.port;
#ifdef INET_REFCNT_DEBUG
atomic_inc(&inet6_sock_nr);
atomic_inc(&inet_sock_nr);
#endif
if (0 != newsk->prot->init(newsk)) {
inet_sock_release(newsk);
newsk = NULL;
}
out:
return newsk;
}
/* Initialize a PF_INET6 socket msg_name. */ /* Initialize a PF_INET6 socket msg_name. */
static void sctp_inet6_msgname(char *msgname, int *addr_len) static void sctp_inet6_msgname(char *msgname, int *addr_len)
{ {
...@@ -597,7 +653,7 @@ static struct proto_ops inet6_seqpacket_ops = { ...@@ -597,7 +653,7 @@ static struct proto_ops inet6_seqpacket_ops = {
.mmap = sock_no_mmap, .mmap = sock_no_mmap,
}; };
static struct inet_protosw sctpv6_protosw = { static struct inet_protosw sctpv6_seqpacket_protosw = {
.type = SOCK_SEQPACKET, .type = SOCK_SEQPACKET,
.protocol = IPPROTO_SCTP, .protocol = IPPROTO_SCTP,
.prot = &sctp_prot, .prot = &sctp_prot,
...@@ -606,6 +662,15 @@ static struct inet_protosw sctpv6_protosw = { ...@@ -606,6 +662,15 @@ static struct inet_protosw sctpv6_protosw = {
.no_check = 0, .no_check = 0,
.flags = SCTP_PROTOSW_FLAG .flags = SCTP_PROTOSW_FLAG
}; };
static struct inet_protosw sctpv6_stream_protosw = {
.type = SOCK_STREAM,
.protocol = IPPROTO_SCTP,
.prot = &sctp_prot,
.ops = &inet6_seqpacket_ops,
.capability = -1,
.no_check = 0,
.flags = SCTP_PROTOSW_FLAG
};
static struct inet6_protocol sctpv6_protocol = { static struct inet6_protocol sctpv6_protocol = {
.handler = sctp_rcv, .handler = sctp_rcv,
...@@ -641,6 +706,7 @@ static struct sctp_pf sctp_pf_inet6_specific = { ...@@ -641,6 +706,7 @@ static struct sctp_pf sctp_pf_inet6_specific = {
.cmp_addr = sctp_inet6_cmp_addr, .cmp_addr = sctp_inet6_cmp_addr,
.bind_verify = sctp_inet6_bind_verify, .bind_verify = sctp_inet6_bind_verify,
.supported_addrs = sctp_inet6_supported_addrs, .supported_addrs = sctp_inet6_supported_addrs,
.create_accept_sk = sctp_v6_create_accept_sk,
.af = &sctp_ipv6_specific, .af = &sctp_ipv6_specific,
}; };
...@@ -651,8 +717,9 @@ int sctp_v6_init(void) ...@@ -651,8 +717,9 @@ int sctp_v6_init(void)
if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0)
return -EAGAIN; return -EAGAIN;
/* Add SCTPv6 to inetsw6 linked list. */ /* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */
inet6_register_protosw(&sctpv6_protosw); inet6_register_protosw(&sctpv6_seqpacket_protosw);
inet6_register_protosw(&sctpv6_stream_protosw);
/* Register the SCTP specfic PF_INET6 functions. */ /* Register the SCTP specfic PF_INET6 functions. */
sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6);
...@@ -671,6 +738,7 @@ void sctp_v6_exit(void) ...@@ -671,6 +738,7 @@ void sctp_v6_exit(void)
{ {
list_del(&sctp_ipv6_specific.list); list_del(&sctp_ipv6_specific.list);
inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP); inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP);
inet6_unregister_protosw(&sctpv6_protosw); inet6_unregister_protosw(&sctpv6_seqpacket_protosw);
inet6_unregister_protosw(&sctpv6_stream_protosw);
unregister_inet6addr_notifier(&sctp_inetaddr_notifier); unregister_inet6addr_notifier(&sctp_inetaddr_notifier);
} }
...@@ -480,6 +480,61 @@ void sctp_v4_get_saddr(sctp_association_t *asoc, ...@@ -480,6 +480,61 @@ void sctp_v4_get_saddr(sctp_association_t *asoc,
} }
/* Create and initialize a new sk for the socket returned by accept(). */
struct sock *sctp_v4_create_accept_sk(struct sock *sk,
struct sctp_association *asoc)
{
struct sock *newsk;
struct inet_opt *inet = inet_sk(sk);
struct inet_opt *newinet;
newsk = sk_alloc(PF_INET, GFP_KERNEL, sizeof(struct sctp_sock),
sk->slab);
if (!newsk)
goto out;
sock_init_data(NULL, newsk);
newsk->type = SOCK_STREAM;
newsk->prot = sk->prot;
newsk->no_check = sk->no_check;
newsk->reuse = sk->reuse;
newsk->destruct = inet_sock_destruct;
newsk->zapped = 0;
newsk->family = PF_INET;
newsk->protocol = IPPROTO_SCTP;
newsk->backlog_rcv = sk->prot->backlog_rcv;
newinet = inet_sk(newsk);
newinet->sport = inet->sport;
newinet->saddr = inet->saddr;
newinet->rcv_saddr = inet->saddr;
newinet->dport = asoc->peer.port;
newinet->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
newinet->pmtudisc = inet->pmtudisc;
newinet->id = 0;
newinet->ttl = sysctl_ip_default_ttl;
newinet->mc_loop = 1;
newinet->mc_ttl = 1;
newinet->mc_index = 0;
newinet->mc_list = NULL;
#ifdef INET_REFCNT_DEBUG
atomic_inc(&inet_sock_nr);
#endif
if (0 != newsk->prot->init(newsk)) {
inet_sock_release(newsk);
newsk = NULL;
}
out:
return newsk;
}
/* Event handler for inet address addition/deletion events. /* Event handler for inet address addition/deletion events.
* Basically, whenever there is an event, we re-build our local address list. * Basically, whenever there is an event, we re-build our local address list.
*/ */
...@@ -664,6 +719,7 @@ static struct sctp_pf sctp_pf_inet = { ...@@ -664,6 +719,7 @@ static struct sctp_pf sctp_pf_inet = {
.cmp_addr = sctp_inet_cmp_addr, .cmp_addr = sctp_inet_cmp_addr,
.bind_verify = sctp_inet_bind_verify, .bind_verify = sctp_inet_bind_verify,
.supported_addrs = sctp_inet_supported_addrs, .supported_addrs = sctp_inet_supported_addrs,
.create_accept_sk = sctp_v4_create_accept_sk,
.af = &sctp_ipv4_specific, .af = &sctp_ipv4_specific,
}; };
...@@ -694,7 +750,7 @@ struct proto_ops inet_seqpacket_ops = { ...@@ -694,7 +750,7 @@ struct proto_ops inet_seqpacket_ops = {
}; };
/* Registration with AF_INET family. */ /* Registration with AF_INET family. */
struct inet_protosw sctp_protosw = { static struct inet_protosw sctp_seqpacket_protosw = {
.type = SOCK_SEQPACKET, .type = SOCK_SEQPACKET,
.protocol = IPPROTO_SCTP, .protocol = IPPROTO_SCTP,
.prot = &sctp_prot, .prot = &sctp_prot,
...@@ -703,6 +759,15 @@ struct inet_protosw sctp_protosw = { ...@@ -703,6 +759,15 @@ struct inet_protosw sctp_protosw = {
.no_check = 0, .no_check = 0,
.flags = SCTP_PROTOSW_FLAG .flags = SCTP_PROTOSW_FLAG
}; };
static struct inet_protosw sctp_stream_protosw = {
.type = SOCK_STREAM,
.protocol = IPPROTO_SCTP,
.prot = &sctp_prot,
.ops = &inet_seqpacket_ops,
.capability = -1,
.no_check = 0,
.flags = SCTP_PROTOSW_FLAG
};
/* Register with IP layer. */ /* Register with IP layer. */
static struct inet_protocol sctp_protocol = { static struct inet_protocol sctp_protocol = {
...@@ -809,8 +874,9 @@ __init int sctp_init(void) ...@@ -809,8 +874,9 @@ __init int sctp_init(void)
if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0)
return -EAGAIN; return -EAGAIN;
/* Add SCTP to inetsw linked list. */ /* Add SCTP(TCP and UDP style) to inetsw linked list. */
inet_register_protosw(&sctp_protosw); inet_register_protosw(&sctp_seqpacket_protosw);
inet_register_protosw(&sctp_stream_protosw);
/* Allocate and initialise sctp mibs. */ /* Allocate and initialise sctp mibs. */
status = init_sctp_mibs(); status = init_sctp_mibs();
...@@ -956,7 +1022,8 @@ __init int sctp_init(void) ...@@ -956,7 +1022,8 @@ __init int sctp_init(void)
cleanup_sctp_mibs(); cleanup_sctp_mibs();
err_init_mibs: err_init_mibs:
inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); inet_del_protocol(&sctp_protocol, IPPROTO_SCTP);
inet_unregister_protosw(&sctp_protosw); inet_unregister_protosw(&sctp_seqpacket_protosw);
inet_unregister_protosw(&sctp_stream_protosw);
return status; return status;
} }
...@@ -989,7 +1056,8 @@ __exit void sctp_exit(void) ...@@ -989,7 +1056,8 @@ __exit void sctp_exit(void)
cleanup_sctp_mibs(); cleanup_sctp_mibs();
inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); inet_del_protocol(&sctp_protocol, IPPROTO_SCTP);
inet_unregister_protosw(&sctp_protosw); inet_unregister_protosw(&sctp_seqpacket_protosw);
inet_unregister_protosw(&sctp_stream_protosw);
} }
module_init(sctp_init); module_init(sctp_init);
......
...@@ -1223,13 +1223,28 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, ...@@ -1223,13 +1223,28 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_state_t state) sctp_state_t state)
{ {
struct sock *sk = asoc->base.sk;
struct sctp_opt *sp = sctp_sk(sk);
asoc->state = state; asoc->state = state;
asoc->state_timestamp = jiffies; asoc->state_timestamp = jiffies;
/* Wake up any process waiting for the association to if ((SCTP_STATE_ESTABLISHED == asoc->state) ||
* get established. (SCTP_STATE_CLOSED == asoc->state)) {
*/ /* Wake up any processes waiting in the asoc's wait queue in
if ((SCTP_STATE_ESTABLISHED == asoc->state) && * sctp_wait_for_connect() or sctp_wait_for_sndbuf().
(waitqueue_active(&asoc->wait))) */
wake_up_interruptible(&asoc->wait); if (waitqueue_active(&asoc->wait))
wake_up_interruptible(&asoc->wait);
/* Wake up any processes waiting in the sk's sleep queue of
* a tcp-style or udp-style peeled-off socket in
* sctp_wait_for_accept() or sctp_wait_for_packet().
* For a udp-style socket, the waiters are woken up by the
* notifications.
*/
if (sp->type != SCTP_SOCKET_UDP)
sk->state_change(sk);
}
} }
...@@ -88,11 +88,14 @@ static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p, ...@@ -88,11 +88,14 @@ static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p,
int msg_len); int msg_len);
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
static int sctp_wait_for_accept(struct sock *sk, long timeo);
static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int); static inline int sctp_verify_addr(struct sock *, union sctp_addr *, 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);
static int sctp_autobind(struct sock *sk); static int sctp_autobind(struct sock *sk);
static void sctp_sock_migrate(struct sock *, struct sock *,
struct sctp_association *, sctp_socket_type_t);
/* Look up the association by its id. If this is not a UDP-style /* Look up the association by its id. If this is not a UDP-style
* socket, the ID field is always ignored. * socket, the ID field is always ignored.
...@@ -1151,6 +1154,13 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1151,6 +1154,13 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
"flags", flags, "addr_len", addr_len); "flags", flags, "addr_len", addr_len);
sctp_lock_sock(sk); sctp_lock_sock(sk);
if ((SCTP_SOCKET_TCP == sp->type) &&
(SCTP_SS_ESTABLISHED != sk->state)) {
err = -ENOTCONN;
goto out;
}
skb = sctp_skb_recv_datagram(sk, flags, noblock, &err); skb = sctp_skb_recv_datagram(sk, flags, noblock, &err);
if (!skb) if (!skb)
goto out; goto out;
...@@ -1563,6 +1573,8 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1563,6 +1573,8 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
if (err) if (err)
goto out_unlock; goto out_unlock;
if (addr_len > sizeof(to))
addr_len = sizeof(to);
memcpy(&to, uaddr, addr_len); memcpy(&to, uaddr, addr_len);
to.v4.sin_port = ntohs(to.v4.sin_port); to.v4.sin_port = ntohs(to.v4.sin_port);
...@@ -1635,13 +1647,63 @@ SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags) ...@@ -1635,13 +1647,63 @@ SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
return -EOPNOTSUPP; /* STUB */ return -EOPNOTSUPP; /* STUB */
} }
/* FIXME: Write comments. */ /* 4.1.4 accept() - TCP Style Syntax
*
* Applications use accept() call to remove an established SCTP
* association from the accept queue of the endpoint. A new socket
* descriptor will be returned from accept() to represent the newly
* formed association.
*/
SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err) SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
{ {
int error = -EOPNOTSUPP; struct sctp_opt *sp;
struct sctp_endpoint *ep;
struct sock *newsk = NULL;
struct sctp_association *assoc;
long timeo;
int error = 0;
sctp_lock_sock(sk);
*err = error; sp = sctp_sk(sk);
return NULL; ep = sp->ep;
if (SCTP_SOCKET_TCP != sp->type) {
error = -EOPNOTSUPP;
goto out;
}
if (SCTP_SS_LISTENING != sk->state) {
error = -EINVAL;
goto out;
}
timeo = sock_rcvtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
error = sctp_wait_for_accept(sk, timeo);
if (error)
goto out;
/* We treat the list of associations on the endpoint as the accept
* queue and pick the first association on the list.
*/
assoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
newsk = sp->pf->create_accept_sk(sk, assoc);
if (!newsk) {
error = -ENOMEM;
goto out;
}
/* Populate the fields of the newsk from the oldsk and migrate the
* assoc to the newsk.
*/
sctp_sock_migrate(sk, newsk, assoc, SCTP_SOCKET_TCP);
out:
sctp_release_sock(sk);
*err = error;
return newsk;
} }
/* FIXME: Write Comments. */ /* FIXME: Write Comments. */
...@@ -1667,7 +1729,16 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) ...@@ -1667,7 +1729,16 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp = sctp_sk(sk); sp = sctp_sk(sk);
/* Initialize the SCTP per socket area. */ /* Initialize the SCTP per socket area. */
sp->type = SCTP_SOCKET_UDP; switch (sk->type) {
case SOCK_SEQPACKET:
sp->type = SCTP_SOCKET_UDP;
break;
case SOCK_STREAM:
sp->type = SCTP_SOCKET_TCP;
break;
default:
return -ESOCKTNOSUPPORT;
}
/* FIXME: The next draft (04) of the SCTP Sockets Extensions /* FIXME: The next draft (04) of the SCTP Sockets Extensions
* should include a socket option for manipulating these * should include a socket option for manipulating these
...@@ -1775,7 +1846,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1775,7 +1846,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
int *optlen) int *optlen)
{ {
struct sctp_status status; struct sctp_status status;
sctp_endpoint_t *ep;
sctp_association_t *assoc = NULL; sctp_association_t *assoc = NULL;
struct sctp_transport *transport; struct sctp_transport *transport;
sctp_assoc_t associd; sctp_assoc_t associd;
...@@ -1879,11 +1949,6 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1879,11 +1949,6 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
struct sock *oldsk = assoc->base.sk; struct sock *oldsk = assoc->base.sk;
struct sock *newsk; struct sock *newsk;
struct socket *tmpsock; struct socket *tmpsock;
sctp_endpoint_t *newep;
struct sctp_opt *oldsp = sctp_sk(oldsk);
struct sctp_opt *newsp;
struct sk_buff *skb, *tmp;
struct sctp_ulpevent *event;
int err = 0; int err = 0;
/* An association cannot be branched off from an already peeled-off /* An association cannot be branched off from an already peeled-off
...@@ -1893,81 +1958,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1893,81 +1958,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* Create a new socket. */ /* Create a new socket. */
err = sock_create(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, &tmpsock); err = sock_create(oldsk->family, SOCK_SEQPACKET, IPPROTO_SCTP,
&tmpsock);
if (err < 0) if (err < 0)
return err; return err;
newsk = tmpsock->sk; newsk = tmpsock->sk;
newsp = sctp_sk(newsk);
newep = newsp->ep;
/* Migrate socket buffer sizes and all the socket level options to the
* new socket.
*/
newsk->sndbuf = oldsk->sndbuf;
newsk->rcvbuf = oldsk->rcvbuf;
*newsp = *oldsp;
/* Restore the ep value that was overwritten with the above structure
* copy.
*/
newsp->ep = newep;
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
*/
sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(&newsk->receive_queue, skb);
}
}
/* Clean up an messages pending delivery due to partial
* delivery. Three cases:
* 1) No partial deliver; no work.
* 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
* 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue.
*/
skb_queue_head_init(&newsp->pd_lobby);
sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;;
if (sctp_sk(oldsk)->pd_mode) {
struct sk_buff_head *queue;
/* Decide which queue to move pd_lobby skbs to. */ /* Populate the fields of the newsk from the oldsk and migrate the
if (assoc->ulpq.pd_mode) { * assoc to the newsk.
queue = &newsp->pd_lobby; */
} else sctp_sock_migrate(oldsk, newsk, assoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
queue = &newsk->receive_queue;
/* Walk through the pd_lobby, looking for skbs that
* need moved to the new socket.
*/
sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(queue, skb);
}
}
/* Clear up any skbs waiting for the partial
* delivery to finish.
*/
if (assoc->ulpq.pd_mode)
sctp_clear_pd(oldsk);
}
/* Set the type of socket to indicate that it is peeled off from the
* original socket.
*/
newsp->type = SCTP_SOCKET_UDP_HIGH_BANDWIDTH;
/* Migrate the association to the new socket. */
sctp_assoc_migrate(assoc, newsk);
*newsock = tmpsock; *newsock = tmpsock;
...@@ -2615,6 +2616,42 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) ...@@ -2615,6 +2616,42 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
if (SCTP_SOCKET_UDP != sp->type) if (SCTP_SOCKET_UDP != sp->type)
return -EINVAL; return -EINVAL;
if (sk->state == SCTP_SS_LISTENING)
return 0;
/*
* If a bind() or sctp_bindx() is not called prior to a listen()
* call that allows new associations to be accepted, the system
* picks an ephemeral port and will choose an address set equivalent
* to binding with a wildcard address.
*
* This is not currently spelled out in the SCTP sockets
* extensions draft, but follows the practice as seen in TCP
* sockets.
*/
if (!ep->base.bind_addr.port) {
if (sctp_autobind(sk))
return -EAGAIN;
}
sk->state = SCTP_SS_LISTENING;
sctp_hash_endpoint(ep);
return 0;
}
/*
* 4.1.3 listen() - TCP Style Syntax
*
* Applications uses listen() to ready the SCTP endpoint for accepting
* inbound associations.
*/
SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
{
struct sctp_opt *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep;
if (sk->state == SCTP_SS_LISTENING)
return 0;
/* /*
* If a bind() or sctp_bindx() is not called prior to a listen() * If a bind() or sctp_bindx() is not called prior to a listen()
* call that allows new associations to be accepted, the system * call that allows new associations to be accepted, the system
...@@ -2630,6 +2667,7 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) ...@@ -2630,6 +2667,7 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
return -EAGAIN; return -EAGAIN;
} }
sk->state = SCTP_SS_LISTENING; sk->state = SCTP_SS_LISTENING;
sk->max_ack_backlog = backlog;
sctp_hash_endpoint(ep); sctp_hash_endpoint(ep);
return 0; return 0;
} }
...@@ -2653,8 +2691,8 @@ int sctp_inet_listen(struct socket *sock, int backlog) ...@@ -2653,8 +2691,8 @@ int sctp_inet_listen(struct socket *sock, int backlog)
break; break;
case SOCK_STREAM: case SOCK_STREAM:
/* FIXME for TCP-style sockets. */ err = sctp_stream_listen(sk, backlog);
err = -EOPNOTSUPP; break;
default: default:
goto out; goto out;
...@@ -3285,7 +3323,7 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p) ...@@ -3285,7 +3323,7 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p)
return err; return err;
do_error: do_error:
err = -ECONNABORTED; err = -ECONNREFUSED;
goto out; goto out;
do_interrupted: do_interrupted:
...@@ -3297,6 +3335,131 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p) ...@@ -3297,6 +3335,131 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p)
goto out; goto out;
} }
static int sctp_wait_for_accept(struct sock *sk, long timeo)
{
struct sctp_endpoint *ep;
int err = 0;
DECLARE_WAITQUEUE(wait, current);
ep = sctp_sk(sk)->ep;
add_wait_queue_exclusive(sk->sleep, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&ep->asocs)) {
sctp_release_sock(sk);
timeo = schedule_timeout(timeo);
sctp_lock_sock(sk);
}
err = -EINVAL;
if (sk->state != SCTP_SS_LISTENING)
break;
err = 0;
if (!list_empty(&ep->asocs))
break;
err = sock_intr_errno(timeo);
if (signal_pending(current))
break;
err = -EAGAIN;
if (!timeo)
break;
}
remove_wait_queue(sk->sleep, &wait);
__set_current_state(TASK_RUNNING);
return err;
}
/* Populate the fields of the newsk from the oldsk and migrate the assoc
* and its messages to the newsk.
*/
void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
struct sctp_association *assoc, sctp_socket_type_t type)
{
struct sctp_opt *oldsp = sctp_sk(oldsk);
struct sctp_opt *newsp = sctp_sk(newsk);
sctp_endpoint_t *newep = newsp->ep;
struct sk_buff *skb, *tmp;
struct sctp_ulpevent *event;
/* Migrate socket buffer sizes and all the socket level options to the
* new socket.
*/
newsk->sndbuf = oldsk->sndbuf;
newsk->rcvbuf = oldsk->rcvbuf;
*newsp = *oldsp;
/* Restore the ep value that was overwritten with the above structure
* copy.
*/
newsp->ep = newep;
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
*/
sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(&newsk->receive_queue, skb);
}
}
/* Clean up any messages pending delivery due to partial
* delivery. Three cases:
* 1) No partial deliver; no work.
* 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
* 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue.
*/
skb_queue_head_init(&newsp->pd_lobby);
sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;;
if (sctp_sk(oldsk)->pd_mode) {
struct sk_buff_head *queue;
/* Decide which queue to move pd_lobby skbs to. */
if (assoc->ulpq.pd_mode) {
queue = &newsp->pd_lobby;
} else
queue = &newsk->receive_queue;
/* Walk through the pd_lobby, looking for skbs that
* need moved to the new socket.
*/
sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(queue, skb);
}
}
/* Clear up any skbs waiting for the partial
* delivery to finish.
*/
if (assoc->ulpq.pd_mode)
sctp_clear_pd(oldsk);
}
/* Set the type of socket to indicate that it is peeled off from the
* original UDP-style socket or created with the accept() call on a
* TCP-style socket..
*/
newsp->type = type;
/* Migrate the association to the new socket. */
sctp_assoc_migrate(assoc, newsk);
newsk->state = SCTP_SS_ESTABLISHED;
}
/* This proto struct describes the ULP interface for SCTP. */ /* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = { struct proto sctp_prot = {
.name = "SCTP", .name = "SCTP",
......
...@@ -230,7 +230,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) ...@@ -230,7 +230,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
sctp_ulpq_clear_pd(ulpq); sctp_ulpq_clear_pd(ulpq);
if (queue == &sk->receive_queue) if (queue == &sk->receive_queue)
wake_up_interruptible(sk->sleep); sk->data_ready(sk, 0);
return 1; return 1;
out_free: out_free:
...@@ -790,5 +790,5 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int priority) ...@@ -790,5 +790,5 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int priority)
/* If there is data waiting, send it up the socket now. */ /* If there is data waiting, send it up the socket now. */
if (sctp_ulpq_clear_pd(ulpq) || ev) if (sctp_ulpq_clear_pd(ulpq) || ev)
wake_up_interruptible(sk->sleep); sk->data_ready(sk, 0);
} }
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