Commit fee91672 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6:
  [SCTP]: Allow linger to abort 1-N style sockets.
  [SCTP]: Validate the parameter length in HB-ACK chunk.
  [SCTP]: Respect the real chunk length when walking parameters.
  [SCTP]: A better solution to fix the race between sctp_peeloff() and
  [SCTP]: Set sk_err so that poll wakes up after a non-blocking connect failure.
parents ae83e255 b89498a1
...@@ -99,6 +99,7 @@ typedef enum { ...@@ -99,6 +99,7 @@ typedef enum {
SCTP_CMD_DEL_NON_PRIMARY, /* Removes non-primary peer transports. */ SCTP_CMD_DEL_NON_PRIMARY, /* Removes non-primary peer transports. */
SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */ SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */
SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */ SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */
SCTP_CMD_SET_SK_ERR, /* Set sk_err */
SCTP_CMD_LAST SCTP_CMD_LAST
} sctp_verb_t; } sctp_verb_t;
......
...@@ -461,12 +461,12 @@ static inline int sctp_frag_point(const struct sctp_sock *sp, int pmtu) ...@@ -461,12 +461,12 @@ static inline int sctp_frag_point(const struct sctp_sock *sp, int pmtu)
* there is room for a param header too. * there is room for a param header too.
*/ */
#define sctp_walk_params(pos, chunk, member)\ #define sctp_walk_params(pos, chunk, member)\
_sctp_walk_params((pos), (chunk), WORD_ROUND(ntohs((chunk)->chunk_hdr.length)), member) _sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member)
#define _sctp_walk_params(pos, chunk, end, member)\ #define _sctp_walk_params(pos, chunk, end, member)\
for (pos.v = chunk->member;\ for (pos.v = chunk->member;\
pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\ pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\
pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)) &&\ pos.v <= (void *)chunk + end - ntohs(pos.p->length) &&\
ntohs(pos.p->length) >= sizeof(sctp_paramhdr_t);\ ntohs(pos.p->length) >= sizeof(sctp_paramhdr_t);\
pos.v += WORD_ROUND(ntohs(pos.p->length))) pos.v += WORD_ROUND(ntohs(pos.p->length)))
...@@ -477,7 +477,7 @@ _sctp_walk_errors((err), (chunk_hdr), ntohs((chunk_hdr)->length)) ...@@ -477,7 +477,7 @@ _sctp_walk_errors((err), (chunk_hdr), ntohs((chunk_hdr)->length))
for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \ for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
sizeof(sctp_chunkhdr_t));\ sizeof(sctp_chunkhdr_t));\
(void *)err <= (void *)chunk_hdr + end - sizeof(sctp_errhdr_t) &&\ (void *)err <= (void *)chunk_hdr + end - sizeof(sctp_errhdr_t) &&\
(void *)err <= (void *)chunk_hdr + end - WORD_ROUND(ntohs(err->length)) &&\ (void *)err <= (void *)chunk_hdr + end - ntohs(err->length) &&\
ntohs(err->length) >= sizeof(sctp_errhdr_t); \ ntohs(err->length) >= sizeof(sctp_errhdr_t); \
err = (sctp_errhdr_t *)((void *)err + WORD_ROUND(ntohs(err->length)))) err = (sctp_errhdr_t *)((void *)err + WORD_ROUND(ntohs(err->length))))
......
...@@ -73,6 +73,8 @@ static struct sctp_association *__sctp_lookup_association( ...@@ -73,6 +73,8 @@ static struct sctp_association *__sctp_lookup_association(
const union sctp_addr *peer, const union sctp_addr *peer,
struct sctp_transport **pt); struct sctp_transport **pt);
static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb);
/* Calculate the SCTP checksum of an SCTP packet. */ /* Calculate the SCTP checksum of an SCTP packet. */
static inline int sctp_rcv_checksum(struct sk_buff *skb) static inline int sctp_rcv_checksum(struct sk_buff *skb)
...@@ -186,7 +188,6 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -186,7 +188,6 @@ int sctp_rcv(struct sk_buff *skb)
*/ */
if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb))) if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb)))
{ {
sock_put(sk);
if (asoc) { if (asoc) {
sctp_association_put(asoc); sctp_association_put(asoc);
asoc = NULL; asoc = NULL;
...@@ -197,7 +198,6 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -197,7 +198,6 @@ int sctp_rcv(struct sk_buff *skb)
sk = sctp_get_ctl_sock(); sk = sctp_get_ctl_sock();
ep = sctp_sk(sk)->ep; ep = sctp_sk(sk)->ep;
sctp_endpoint_hold(ep); sctp_endpoint_hold(ep);
sock_hold(sk);
rcvr = &ep->base; rcvr = &ep->base;
} }
...@@ -253,25 +253,18 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -253,25 +253,18 @@ int sctp_rcv(struct sk_buff *skb)
*/ */
sctp_bh_lock_sock(sk); sctp_bh_lock_sock(sk);
/* It is possible that the association could have moved to a different
* socket if it is peeled off. If so, update the sk.
*/
if (sk != rcvr->sk) {
sctp_bh_lock_sock(rcvr->sk);
sctp_bh_unlock_sock(sk);
sk = rcvr->sk;
}
if (sock_owned_by_user(sk)) if (sock_owned_by_user(sk))
sk_add_backlog(sk, skb); sctp_add_backlog(sk, skb);
else else
sctp_backlog_rcv(sk, skb); sctp_inq_push(&chunk->rcvr->inqueue, chunk);
/* Release the sock and the sock ref we took in the lookup calls.
* The asoc/ep ref will be released in sctp_backlog_rcv.
*/
sctp_bh_unlock_sock(sk); sctp_bh_unlock_sock(sk);
sock_put(sk);
/* Release the asoc/ep ref we took in the lookup calls. */
if (asoc)
sctp_association_put(asoc);
else
sctp_endpoint_put(ep);
return 0; return 0;
...@@ -280,8 +273,7 @@ discard_it: ...@@ -280,8 +273,7 @@ discard_it:
return 0; return 0;
discard_release: discard_release:
/* Release any structures we may be holding. */ /* Release the asoc/ep ref we took in the lookup calls. */
sock_put(sk);
if (asoc) if (asoc)
sctp_association_put(asoc); sctp_association_put(asoc);
else else
...@@ -290,56 +282,87 @@ discard_release: ...@@ -290,56 +282,87 @@ discard_release:
goto discard_it; goto discard_it;
} }
/* Handle second half of inbound skb processing. If the sock was busy, /* Process the backlog queue of the socket. Every skb on
* we may have need to delay processing until later when the sock is * the backlog holds a ref on an association or endpoint.
* released (on the backlog). If not busy, we call this routine * We hold this ref throughout the state machine to make
* directly from the bottom half. * sure that the structure we need is still around.
*/ */
int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{ {
struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
struct sctp_inq *inqueue = NULL; struct sctp_inq *inqueue = &chunk->rcvr->inqueue;
struct sctp_ep_common *rcvr = NULL; struct sctp_ep_common *rcvr = NULL;
int backloged = 0;
rcvr = chunk->rcvr; rcvr = chunk->rcvr;
BUG_TRAP(rcvr->sk == sk); /* If the rcvr is dead then the association or endpoint
* has been deleted and we can safely drop the chunk
* and refs that we are holding.
*/
if (rcvr->dead) { if (rcvr->dead) {
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
goto done;
}
if (unlikely(rcvr->sk != sk)) {
/* In this case, the association moved from one socket to
* another. We are currently sitting on the backlog of the
* old socket, so we need to move.
* However, since we are here in the process context we
* need to take make sure that the user doesn't own
* the new socket when we process the packet.
* If the new socket is user-owned, queue the chunk to the
* backlog of the new socket without dropping any refs.
* Otherwise, we can safely push the chunk on the inqueue.
*/
sk = rcvr->sk;
sctp_bh_lock_sock(sk);
if (sock_owned_by_user(sk)) {
sk_add_backlog(sk, skb);
backloged = 1;
} else
sctp_inq_push(inqueue, chunk);
sctp_bh_unlock_sock(sk);
/* If the chunk was backloged again, don't drop refs */
if (backloged)
return 0;
} else { } else {
inqueue = &chunk->rcvr->inqueue;
sctp_inq_push(inqueue, chunk); sctp_inq_push(inqueue, chunk);
} }
/* Release the asoc/ep ref we took in the lookup calls in sctp_rcv. */ done:
/* Release the refs we took in sctp_add_backlog */
if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
sctp_association_put(sctp_assoc(rcvr)); sctp_association_put(sctp_assoc(rcvr));
else else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
sctp_endpoint_put(sctp_ep(rcvr)); sctp_endpoint_put(sctp_ep(rcvr));
else
BUG();
return 0; return 0;
} }
void sctp_backlog_migrate(struct sctp_association *assoc, static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
struct sock *oldsk, struct sock *newsk)
{ {
struct sk_buff *skb; struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
struct sctp_chunk *chunk; struct sctp_ep_common *rcvr = chunk->rcvr;
skb = oldsk->sk_backlog.head;
oldsk->sk_backlog.head = oldsk->sk_backlog.tail = NULL;
while (skb != NULL) {
struct sk_buff *next = skb->next;
chunk = SCTP_INPUT_CB(skb)->chunk; /* Hold the assoc/ep while hanging on the backlog queue.
skb->next = NULL; * This way, we know structures we need will not disappear from us
if (&assoc->base == chunk->rcvr) */
sk_add_backlog(newsk, skb); if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
sctp_association_hold(sctp_assoc(rcvr));
else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
sctp_endpoint_hold(sctp_ep(rcvr));
else else
sk_add_backlog(oldsk, skb); BUG();
skb = next;
} sk_add_backlog(sk, skb);
} }
/* Handle icmp frag needed error. */ /* Handle icmp frag needed error. */
...@@ -412,7 +435,7 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, ...@@ -412,7 +435,7 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
union sctp_addr daddr; union sctp_addr daddr;
struct sctp_af *af; struct sctp_af *af;
struct sock *sk = NULL; struct sock *sk = NULL;
struct sctp_association *asoc = NULL; struct sctp_association *asoc;
struct sctp_transport *transport = NULL; struct sctp_transport *transport = NULL;
*app = NULL; *tpp = NULL; *app = NULL; *tpp = NULL;
...@@ -453,7 +476,6 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, ...@@ -453,7 +476,6 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
return sk; return sk;
out: out:
sock_put(sk);
if (asoc) if (asoc)
sctp_association_put(asoc); sctp_association_put(asoc);
return NULL; return NULL;
...@@ -463,7 +485,6 @@ out: ...@@ -463,7 +485,6 @@ out:
void sctp_err_finish(struct sock *sk, struct sctp_association *asoc) void sctp_err_finish(struct sock *sk, struct sctp_association *asoc)
{ {
sctp_bh_unlock_sock(sk); sctp_bh_unlock_sock(sk);
sock_put(sk);
if (asoc) if (asoc)
sctp_association_put(asoc); sctp_association_put(asoc);
} }
...@@ -490,7 +511,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) ...@@ -490,7 +511,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
int type = skb->h.icmph->type; int type = skb->h.icmph->type;
int code = skb->h.icmph->code; int code = skb->h.icmph->code;
struct sock *sk; struct sock *sk;
struct sctp_association *asoc; struct sctp_association *asoc = NULL;
struct sctp_transport *transport; struct sctp_transport *transport;
struct inet_sock *inet; struct inet_sock *inet;
char *saveip, *savesctp; char *saveip, *savesctp;
...@@ -716,7 +737,6 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *l ...@@ -716,7 +737,6 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *l
hit: hit:
sctp_endpoint_hold(ep); sctp_endpoint_hold(ep);
sock_hold(epb->sk);
read_unlock(&head->lock); read_unlock(&head->lock);
return ep; return ep;
} }
...@@ -818,7 +838,6 @@ static struct sctp_association *__sctp_lookup_association( ...@@ -818,7 +838,6 @@ static struct sctp_association *__sctp_lookup_association(
hit: hit:
*pt = transport; *pt = transport;
sctp_association_hold(asoc); sctp_association_hold(asoc);
sock_hold(epb->sk);
read_unlock(&head->lock); read_unlock(&head->lock);
return asoc; return asoc;
} }
...@@ -846,7 +865,6 @@ int sctp_has_association(const union sctp_addr *laddr, ...@@ -846,7 +865,6 @@ int sctp_has_association(const union sctp_addr *laddr,
struct sctp_transport *transport; struct sctp_transport *transport;
if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) { if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
sock_put(asoc->base.sk);
sctp_association_put(asoc); sctp_association_put(asoc);
return 1; return 1;
} }
......
...@@ -498,10 +498,6 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, ...@@ -498,10 +498,6 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED)); SCTP_STATE(SCTP_STATE_CLOSED));
/* Set sk_err to ECONNRESET on a 1-1 style socket. */
if (!sctp_style(asoc->base.sk, UDP))
asoc->base.sk->sk_err = ECONNRESET;
/* SEND_FAILED sent later when cleaning up the association. */ /* SEND_FAILED sent later when cleaning up the association. */
asoc->outqueue.error = error; asoc->outqueue.error = error;
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
...@@ -838,6 +834,15 @@ static void sctp_cmd_del_non_primary(struct sctp_association *asoc) ...@@ -838,6 +834,15 @@ static void sctp_cmd_del_non_primary(struct sctp_association *asoc)
return; return;
} }
/* Helper function to set sk_err on a 1-1 style socket. */
static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error)
{
struct sock *sk = asoc->base.sk;
if (!sctp_style(sk, UDP))
sk->sk_err = error;
}
/* These three macros allow us to pull the debugging code out of the /* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real * main flow of sctp_do_sm() to keep attention focused on the real
* functionality there. * functionality there.
...@@ -1458,6 +1463,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1458,6 +1463,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
local_cork = 0; local_cork = 0;
asoc->peer.retran_path = t; asoc->peer.retran_path = t;
break; break;
case SCTP_CMD_SET_SK_ERR:
sctp_cmd_set_sk_err(asoc, cmd->obj.error);
break;
default: default:
printk(KERN_WARNING "Impossible command: %u, %p\n", printk(KERN_WARNING "Impossible command: %u, %p\n",
cmd->verb, cmd->obj.ptr); cmd->verb, cmd->obj.ptr);
......
...@@ -93,7 +93,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, ...@@ -93,7 +93,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk);
static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
__u16 error, __u16 error, int sk_err,
const struct sctp_association *asoc, const struct sctp_association *asoc,
struct sctp_transport *transport); struct sctp_transport *transport);
...@@ -448,7 +448,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, ...@@ -448,7 +448,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
__u32 init_tag; __u32 init_tag;
struct sctp_chunk *err_chunk; struct sctp_chunk *err_chunk;
struct sctp_packet *packet; struct sctp_packet *packet;
sctp_disposition_t ret; __u16 error;
if (!sctp_vtag_verify(chunk, asoc)) if (!sctp_vtag_verify(chunk, asoc))
return sctp_sf_pdiscard(ep, asoc, type, arg, commands); return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
...@@ -480,11 +480,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, ...@@ -480,11 +480,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
goto nomem; goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, return sctp_stop_t1_and_abort(commands, SCTP_ERROR_INV_PARAM,
SCTP_STATE(SCTP_STATE_CLOSED)); ECONNREFUSED, asoc,
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); chunk->transport);
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
return SCTP_DISPOSITION_DELETE_TCB;
} }
/* Verify the INIT chunk before processing it. */ /* Verify the INIT chunk before processing it. */
...@@ -511,27 +509,16 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, ...@@ -511,27 +509,16 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
SCTP_PACKET(packet)); SCTP_PACKET(packet));
SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, error = SCTP_ERROR_INV_PARAM;
SCTP_STATE(SCTP_STATE_CLOSED));
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
SCTP_NULL());
return SCTP_DISPOSITION_CONSUME;
} else { } else {
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, error = SCTP_ERROR_NO_RESOURCE;
SCTP_STATE(SCTP_STATE_CLOSED));
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
SCTP_NULL());
return SCTP_DISPOSITION_NOMEM;
} }
} else { } else {
ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg, sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
commands); error = SCTP_ERROR_INV_PARAM;
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED));
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
SCTP_NULL());
return ret;
} }
return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED,
asoc, chunk->transport);
} }
/* Tag the variable length parameters. Note that we never /* Tag the variable length parameters. Note that we never
...@@ -886,6 +873,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, ...@@ -886,6 +873,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
struct sctp_transport *transport = (struct sctp_transport *) arg; struct sctp_transport *transport = (struct sctp_transport *) arg;
if (asoc->overall_error_count >= asoc->max_retrans) { if (asoc->overall_error_count >= asoc->max_retrans) {
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_U32(SCTP_ERROR_NO_ERROR));
...@@ -1030,6 +1019,12 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, ...@@ -1030,6 +1019,12 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
commands); commands);
hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
/* Make sure that the length of the parameter is what we expect */
if (ntohs(hbinfo->param_hdr.length) !=
sizeof(sctp_sender_hb_info_t)) {
return SCTP_DISPOSITION_DISCARD;
}
from_addr = hbinfo->daddr; from_addr = hbinfo->daddr;
link = sctp_assoc_lookup_paddr(asoc, &from_addr); link = sctp_assoc_lookup_paddr(asoc, &from_addr);
...@@ -2126,6 +2121,8 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, ...@@ -2126,6 +2121,8 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep,
int attempts = asoc->init_err_counter + 1; int attempts = asoc->init_err_counter + 1;
if (attempts > asoc->max_init_attempts) { if (attempts > asoc->max_init_attempts) {
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_STALE_COOKIE)); SCTP_U32(SCTP_ERROR_STALE_COOKIE));
return SCTP_DISPOSITION_DELETE_TCB; return SCTP_DISPOSITION_DELETE_TCB;
...@@ -2262,6 +2259,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, ...@@ -2262,6 +2259,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr))
error = ((sctp_errhdr_t *)chunk->skb->data)->cause; error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET));
/* ASSOC_FAILED will DELETE_TCB. */ /* ASSOC_FAILED will DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error));
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
...@@ -2306,7 +2304,8 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, ...@@ -2306,7 +2304,8 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep,
if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr))
error = ((sctp_errhdr_t *)chunk->skb->data)->cause; error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
return sctp_stop_t1_and_abort(commands, error, asoc, chunk->transport); return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, asoc,
chunk->transport);
} }
/* /*
...@@ -2318,7 +2317,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep ...@@ -2318,7 +2317,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR, asoc, return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR,
ENOPROTOOPT, asoc,
(struct sctp_transport *)arg); (struct sctp_transport *)arg);
} }
...@@ -2343,7 +2343,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, ...@@ -2343,7 +2343,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep,
* This is common code called by several sctp_sf_*_abort() functions above. * This is common code called by several sctp_sf_*_abort() functions above.
*/ */
static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
__u16 error, __u16 error, int sk_err,
const struct sctp_association *asoc, const struct sctp_association *asoc,
struct sctp_transport *transport) struct sctp_transport *transport)
{ {
...@@ -2353,6 +2353,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, ...@@ -2353,6 +2353,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(sk_err));
/* CMD_INIT_FAILED will DELETE_TCB. */ /* CMD_INIT_FAILED will DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(error)); SCTP_U32(error));
...@@ -3336,6 +3337,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, ...@@ -3336,6 +3337,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ECONNABORTED));
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_ASCONF_ACK)); SCTP_U32(SCTP_ERROR_ASCONF_ACK));
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
...@@ -3362,6 +3365,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, ...@@ -3362,6 +3365,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
* processing the rest of the chunks in the packet. * processing the rest of the chunks in the packet.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ECONNABORTED));
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_ASCONF_ACK)); SCTP_U32(SCTP_ERROR_ASCONF_ACK));
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
...@@ -3714,9 +3719,13 @@ static sctp_disposition_t sctp_sf_violation_chunklen( ...@@ -3714,9 +3719,13 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) { if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) {
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ECONNREFUSED));
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_PROTO_VIOLATION)); SCTP_U32(SCTP_ERROR_PROTO_VIOLATION));
} else { } else {
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ECONNABORTED));
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_PROTO_VIOLATION)); SCTP_U32(SCTP_ERROR_PROTO_VIOLATION));
SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
...@@ -4034,6 +4043,8 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( ...@@ -4034,6 +4043,8 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
* TCB. This is a departure from our typical NOMEM handling. * TCB. This is a departure from our typical NOMEM handling.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ECONNABORTED));
/* Delete the established association. */ /* Delete the established association. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_USER_ABORT)); SCTP_U32(SCTP_ERROR_USER_ABORT));
...@@ -4175,6 +4186,8 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( ...@@ -4175,6 +4186,8 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
* TCB. This is a departure from our typical NOMEM handling. * TCB. This is a departure from our typical NOMEM handling.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ECONNREFUSED));
/* Delete the established association. */ /* Delete the established association. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_USER_ABORT)); SCTP_U32(SCTP_ERROR_USER_ABORT));
...@@ -4543,6 +4556,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep, ...@@ -4543,6 +4556,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
struct sctp_transport *transport = arg; struct sctp_transport *transport = arg;
if (asoc->overall_error_count >= asoc->max_retrans) { if (asoc->overall_error_count >= asoc->max_retrans) {
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_U32(SCTP_ERROR_NO_ERROR));
...@@ -4662,6 +4677,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep, ...@@ -4662,6 +4677,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep,
SCTP_DEBUG_PRINTK("Giving up on INIT, attempts: %d" SCTP_DEBUG_PRINTK("Giving up on INIT, attempts: %d"
" max_init_attempts: %d\n", " max_init_attempts: %d\n",
attempts, asoc->max_init_attempts); attempts, asoc->max_init_attempts);
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_U32(SCTP_ERROR_NO_ERROR));
return SCTP_DISPOSITION_DELETE_TCB; return SCTP_DISPOSITION_DELETE_TCB;
...@@ -4711,6 +4728,8 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep ...@@ -4711,6 +4728,8 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
} else { } else {
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_U32(SCTP_ERROR_NO_ERROR));
return SCTP_DISPOSITION_DELETE_TCB; return SCTP_DISPOSITION_DELETE_TCB;
...@@ -4742,6 +4761,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep, ...@@ -4742,6 +4761,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); SCTP_DEBUG_PRINTK("Timer T2 expired.\n");
if (asoc->overall_error_count >= asoc->max_retrans) { if (asoc->overall_error_count >= asoc->max_retrans) {
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
/* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ /* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_U32(SCTP_ERROR_NO_ERROR));
...@@ -4817,6 +4838,8 @@ sctp_disposition_t sctp_sf_t4_timer_expire( ...@@ -4817,6 +4838,8 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
if (asoc->overall_error_count >= asoc->max_retrans) { if (asoc->overall_error_count >= asoc->max_retrans) {
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_U32(SCTP_ERROR_NO_ERROR));
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
...@@ -4870,6 +4893,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep, ...@@ -4870,6 +4893,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep,
goto nomem; goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_U32(SCTP_ERROR_NO_ERROR));
...@@ -5309,6 +5334,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, ...@@ -5309,6 +5334,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
* processing the rest of the chunks in the packet. * processing the rest of the chunks in the packet.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ECONNABORTED));
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_DATA)); SCTP_U32(SCTP_ERROR_NO_DATA));
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
......
...@@ -1057,6 +1057,7 @@ static int __sctp_connect(struct sock* sk, ...@@ -1057,6 +1057,7 @@ static int __sctp_connect(struct sock* sk,
inet_sk(sk)->dport = htons(asoc->peer.port); inet_sk(sk)->dport = htons(asoc->peer.port);
af = sctp_get_af_specific(to.sa.sa_family); af = sctp_get_af_specific(to.sa.sa_family);
af->to_sk_daddr(&to, sk); af->to_sk_daddr(&to, sk);
sk->sk_err = 0;
timeo = sock_sndtimeo(sk, sk->sk_socket->file->f_flags & O_NONBLOCK); timeo = sock_sndtimeo(sk, sk->sk_socket->file->f_flags & O_NONBLOCK);
err = sctp_wait_for_connect(asoc, &timeo); err = sctp_wait_for_connect(asoc, &timeo);
...@@ -1228,7 +1229,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) ...@@ -1228,7 +1229,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
ep = sctp_sk(sk)->ep; ep = sctp_sk(sk)->ep;
/* Walk all associations on a socket, not on an endpoint. */ /* Walk all associations on an endpoint. */
list_for_each_safe(pos, temp, &ep->asocs) { list_for_each_safe(pos, temp, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs); asoc = list_entry(pos, struct sctp_association, asocs);
...@@ -1241,14 +1242,14 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) ...@@ -1241,14 +1242,14 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
if (sctp_state(asoc, CLOSED)) { if (sctp_state(asoc, CLOSED)) {
sctp_unhash_established(asoc); sctp_unhash_established(asoc);
sctp_association_free(asoc); sctp_association_free(asoc);
continue;
}
}
} else if (sock_flag(sk, SOCK_LINGER) && if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)
!sk->sk_lingertime)
sctp_primitive_ABORT(asoc, NULL); sctp_primitive_ABORT(asoc, NULL);
else else
sctp_primitive_SHUTDOWN(asoc, NULL); sctp_primitive_SHUTDOWN(asoc, NULL);
} else
sctp_primitive_SHUTDOWN(asoc, NULL);
} }
/* Clean up any skbs sitting on the receive queue. */ /* Clean up any skbs sitting on the receive queue. */
...@@ -5317,6 +5318,7 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, ...@@ -5317,6 +5318,7 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
*/ */
sctp_release_sock(sk); sctp_release_sock(sk);
current_timeo = schedule_timeout(current_timeo); current_timeo = schedule_timeout(current_timeo);
BUG_ON(sk != asoc->base.sk);
sctp_lock_sock(sk); sctp_lock_sock(sk);
*timeo_p = current_timeo; *timeo_p = current_timeo;
...@@ -5604,12 +5606,14 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, ...@@ -5604,12 +5606,14 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
*/ */
newsp->type = type; newsp->type = type;
spin_lock_bh(&oldsk->sk_lock.slock); /* Mark the new socket "in-use" by the user so that any packets
/* Migrate the backlog from oldsk to newsk. */ * that may arrive on the association after we've moved it are
sctp_backlog_migrate(assoc, oldsk, newsk); * queued to the backlog. This prevents a potential race between
/* Migrate the association to the new socket. */ * backlog processing on the old socket and new-packet processing
* on the new socket.
*/
sctp_lock_sock(newsk);
sctp_assoc_migrate(assoc, newsk); sctp_assoc_migrate(assoc, newsk);
spin_unlock_bh(&oldsk->sk_lock.slock);
/* If the association on the newsk is already closed before accept() /* If the association on the newsk is already closed before accept()
* is called, set RCV_SHUTDOWN flag. * is called, set RCV_SHUTDOWN flag.
...@@ -5618,6 +5622,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, ...@@ -5618,6 +5622,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
newsk->sk_shutdown |= RCV_SHUTDOWN; newsk->sk_shutdown |= RCV_SHUTDOWN;
newsk->sk_state = SCTP_SS_ESTABLISHED; newsk->sk_state = SCTP_SS_ESTABLISHED;
sctp_release_sock(newsk);
} }
/* This proto struct describes the ULP interface for SCTP. */ /* This proto struct describes the ULP interface for SCTP. */
......
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