summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/sctp_input.c')
-rw-r--r--sys/netinet/sctp_input.c519
1 files changed, 280 insertions, 239 deletions
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index 88d67a3..1329c40 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -1,5 +1,7 @@
/*-
* Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2008-2011, by Randall Stewart. All rights reserved.
+ * Copyright (c) 2008-2011, by Michael Tuexen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -48,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_timer.h>
#include <netinet/sctp_crc32.h>
#include <netinet/udp.h>
+#include <sys/smp.h>
@@ -193,8 +196,8 @@ int
sctp_is_there_unsent_data(struct sctp_tcb *stcb)
{
int unsent_data = 0;
+ unsigned int i;
struct sctp_stream_queue_pending *sp;
- struct sctp_stream_out *strq;
struct sctp_association *asoc;
/*
@@ -205,12 +208,11 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb)
*/
asoc = &stcb->asoc;
SCTP_TCB_SEND_LOCK(stcb);
- if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+ if (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
/* Check to see if some data queued */
- TAILQ_FOREACH(strq, &asoc->out_wheel, next_spoke) {
- is_there_another:
+ for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
/* sa_ignore FREED_MEMORY */
- sp = TAILQ_FIRST(&strq->outqueue);
+ sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
if (sp == NULL) {
continue;
}
@@ -231,7 +233,7 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb)
sp->put_last_out);
}
atomic_subtract_int(&stcb->asoc.stream_queue_cnt, 1);
- TAILQ_REMOVE(&strq->outqueue, sp, next);
+ TAILQ_REMOVE(&stcb->asoc.strmout[i].outqueue, sp, next);
if (sp->net) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
@@ -241,10 +243,9 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb)
sp->data = NULL;
}
sctp_free_a_strmoq(stcb, sp);
- goto is_there_another;
} else {
unsent_data++;
- continue;
+ break;
}
}
}
@@ -266,11 +267,13 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
/* save off parameters */
asoc->peer_vtag = ntohl(init->initiate_tag);
asoc->peers_rwnd = ntohl(init->a_rwnd);
+ /* init tsn's */
+ asoc->highest_tsn_inside_map = asoc->asconf_seq_in = ntohl(init->initial_tsn) - 1;
+
if (!TAILQ_EMPTY(&asoc->nets)) {
/* update any ssthresh's that may have a default */
TAILQ_FOREACH(lnet, &asoc->nets, sctp_next) {
lnet->ssthresh = asoc->peers_rwnd;
-
if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_CWND_MONITOR_ENABLE | SCTP_CWND_LOGGING_ENABLE)) {
sctp_log_cwnd(stcb, lnet, 0, SCTP_CWND_INITIALIZATION);
}
@@ -280,38 +283,32 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
if (asoc->pre_open_streams > ntohs(init->num_inbound_streams)) {
unsigned int newcnt;
struct sctp_stream_out *outs;
- struct sctp_stream_queue_pending *sp;
- struct sctp_tmit_chunk *chk, *chk_next;
+ struct sctp_stream_queue_pending *sp, *nsp;
+ struct sctp_tmit_chunk *chk, *nchk;
/* abandon the upper streams */
newcnt = ntohs(init->num_inbound_streams);
- if (!TAILQ_EMPTY(&asoc->send_queue)) {
- chk = TAILQ_FIRST(&asoc->send_queue);
- while (chk) {
- chk_next = TAILQ_NEXT(chk, sctp_next);
- if (chk->rec.data.stream_number >= newcnt) {
- TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next);
- asoc->send_queue_cnt--;
- if (chk->data != NULL) {
- sctp_free_bufspace(stcb, asoc, chk, 1);
- sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
- SCTP_NOTIFY_DATAGRAM_UNSENT, chk, SCTP_SO_NOT_LOCKED);
- if (chk->data) {
- sctp_m_freem(chk->data);
- chk->data = NULL;
- }
+ TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) {
+ if (chk->rec.data.stream_number >= newcnt) {
+ TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next);
+ asoc->send_queue_cnt--;
+ if (chk->data != NULL) {
+ sctp_free_bufspace(stcb, asoc, chk, 1);
+ sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
+ SCTP_NOTIFY_DATAGRAM_UNSENT, chk, SCTP_SO_NOT_LOCKED);
+ if (chk->data) {
+ sctp_m_freem(chk->data);
+ chk->data = NULL;
}
- sctp_free_a_chunk(stcb, chk);
- /* sa_ignore FREED_MEMORY */
}
- chk = chk_next;
+ sctp_free_a_chunk(stcb, chk);
+ /* sa_ignore FREED_MEMORY */
}
}
if (asoc->strmout) {
for (i = newcnt; i < asoc->pre_open_streams; i++) {
outs = &asoc->strmout[i];
- sp = TAILQ_FIRST(&outs->outqueue);
- while (sp) {
+ TAILQ_FOREACH_SAFE(sp, &outs->outqueue, next, nsp) {
TAILQ_REMOVE(&outs->outqueue, sp, next);
asoc->stream_queue_cnt--;
sctp_ulp_notify(SCTP_NOTIFY_SPECIAL_SP_FAIL,
@@ -328,7 +325,6 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
/* Free the chunk */
sctp_free_a_strmoq(stcb, sp);
/* sa_ignore FREED_MEMORY */
- sp = TAILQ_FIRST(&outs->outqueue);
}
}
}
@@ -337,8 +333,7 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
}
SCTP_TCB_SEND_UNLOCK(stcb);
asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams;
- /* init tsn's */
- asoc->highest_tsn_inside_map = asoc->asconf_seq_in = ntohl(init->initial_tsn) - 1;
+
/* EY - nr_sack: initialize highest tsn in nr_mapping_array */
asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map;
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) {
@@ -349,24 +344,22 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
asoc->mapping_array_base_tsn = ntohl(init->initial_tsn);
asoc->tsn_last_delivered = asoc->cumulative_tsn = asoc->asconf_seq_in;
- asoc->last_echo_tsn = asoc->asconf_seq_in;
+
asoc->advanced_peer_ack_point = asoc->last_acked_seq;
/* open the requested streams */
if (asoc->strmin != NULL) {
/* Free the old ones */
- struct sctp_queued_to_read *ctl;
+ struct sctp_queued_to_read *ctl, *nctl;
for (i = 0; i < asoc->streamincnt; i++) {
- ctl = TAILQ_FIRST(&asoc->strmin[i].inqueue);
- while (ctl) {
+ TAILQ_FOREACH_SAFE(ctl, &asoc->strmin[i].inqueue, next, nctl) {
TAILQ_REMOVE(&asoc->strmin[i].inqueue, ctl, next);
sctp_free_remote_addr(ctl->whoFrom);
ctl->whoFrom = NULL;
sctp_m_freem(ctl->data);
ctl->data = NULL;
sctp_free_a_readq(stcb, ctl);
- ctl = TAILQ_FIRST(&asoc->strmin[i].inqueue);
}
}
SCTP_FREE(asoc->strmin, SCTP_M_STRMI);
@@ -456,13 +449,11 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset,
}
/* if the peer doesn't support asconf, flush the asconf queue */
if (asoc->peer_supports_asconf == 0) {
- struct sctp_asconf_addr *aparam;
+ struct sctp_asconf_addr *param, *nparam;
- while (!TAILQ_EMPTY(&asoc->asconf_queue)) {
- /* sa_ignore FREED_MEMORY */
- aparam = TAILQ_FIRST(&asoc->asconf_queue);
- TAILQ_REMOVE(&asoc->asconf_queue, aparam, next);
- SCTP_FREE(aparam, SCTP_M_ASC_ADDR);
+ TAILQ_FOREACH_SAFE(param, &asoc->asconf_queue, next, nparam) {
+ TAILQ_REMOVE(&asoc->asconf_queue, param, next);
+ SCTP_FREE(param, SCTP_M_ASC_ADDR);
}
}
stcb->asoc.peer_hmac_id = sctp_negotiate_hmacid(stcb->asoc.peer_hmacs,
@@ -493,7 +484,8 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset,
asoc->primary_destination, SCTP_FROM_SCTP_INPUT + SCTP_LOC_4);
/* calculate the RTO */
- net->RTO = sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered, sctp_align_safe_nocopy);
+ net->RTO = sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered, sctp_align_safe_nocopy,
+ SCTP_DETERMINE_LL_NOTOK);
retval = sctp_send_cookie_echo(m, offset, stcb, net);
if (retval < 0) {
@@ -623,7 +615,7 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp,
* timer is running, for the destination, stop the timer because a
* PF-heartbeat was received.
*/
- if ((stcb->asoc.sctp_cmt_on_off == 1) &&
+ if ((stcb->asoc.sctp_cmt_on_off > 0) &&
(stcb->asoc.sctp_cmt_pf > 0) &&
((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) {
if (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
@@ -637,7 +629,8 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp,
net, net->cwnd);
}
/* Now lets do a RTO with this */
- r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv, sctp_align_safe_nocopy);
+ r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv, sctp_align_safe_nocopy,
+ SCTP_DETERMINE_LL_OK);
/* Mobility adaptation */
if (req_prim) {
if ((sctp_is_mobility_feature_on(stcb->sctp_ep,
@@ -975,7 +968,7 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp,
/* are the queues empty? */
if (!TAILQ_EMPTY(&asoc->send_queue) ||
!TAILQ_EMPTY(&asoc->sent_queue) ||
- !TAILQ_EMPTY(&asoc->out_wheel)) {
+ !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
sctp_report_all_outbound(stcb, 0, SCTP_SO_NOT_LOCKED);
}
/* stop the timer */
@@ -1057,11 +1050,6 @@ sctp_process_unrecog_param(struct sctp_tcb *stcb, struct sctp_paramhdr *phdr)
case SCTP_HAS_NAT_SUPPORT:
stcb->asoc.peer_supports_nat = 0;
break;
- case SCTP_ECN_NONCE_SUPPORTED:
- stcb->asoc.peer_supports_ecn_nonce = 0;
- stcb->asoc.ecn_nonce_allowed = 0;
- stcb->asoc.ecn_allowed = 0;
- break;
case SCTP_ADD_IP_ADDRESS:
case SCTP_DEL_IP_ADDRESS:
case SCTP_SET_PRIM_ADDR:
@@ -1557,7 +1545,9 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
*/
net->hb_responded = 1;
net->RTO = sctp_calculate_rto(stcb, asoc, net,
- &cookie->time_entered, sctp_align_unsafe_makecopy);
+ &cookie->time_entered,
+ sctp_align_unsafe_makecopy,
+ SCTP_DETERMINE_LL_NOTOK);
if (stcb->asoc.sctp_autoclose_ticks &&
(sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE))) {
@@ -1691,8 +1681,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams);
- /* Note last_cwr_tsn? where is this used? */
- asoc->last_cwr_tsn = asoc->init_seq_number - 1;
if (ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) {
/*
* Ok the peer probably discarded our data (if we
@@ -1848,7 +1836,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number;
asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
- asoc->last_cwr_tsn = asoc->init_seq_number - 1;
asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1;
asoc->str_reset_seq_in = asoc->init_seq_number;
@@ -2086,7 +2073,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn);
asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number;
asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
- asoc->last_cwr_tsn = asoc->init_seq_number - 1;
asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1;
asoc->str_reset_seq_in = asoc->init_seq_number;
@@ -2264,7 +2250,8 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
(void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
if ((netp) && (*netp)) {
(*netp)->RTO = sctp_calculate_rto(stcb, asoc, *netp,
- &cookie->time_entered, sctp_align_unsafe_makecopy);
+ &cookie->time_entered, sctp_align_unsafe_makecopy,
+ SCTP_DETERMINE_LL_NOTOK);
}
/* respond with a COOKIE-ACK */
sctp_send_cookie_ack(stcb);
@@ -2329,6 +2316,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
int notification = 0;
struct sctp_nets *netl;
int had_a_existing_tcb = 0;
+ int send_int_conf = 0;
SCTPDBG(SCTP_DEBUG_INPUT2,
"sctp_handle_cookie: handling COOKIE-ECHO\n");
@@ -2627,6 +2615,12 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
/* still no TCB... must be bad cookie-echo */
return (NULL);
}
+ if ((*netp != NULL) && (m->m_flags & M_FLOWID)) {
+ (*netp)->flowid = m->m_pkthdr.flowid;
+#ifdef INVARIANTS
+ (*netp)->flowidset = 1;
+#endif
+ }
/*
* Ok, we built an association so confirm the address we sent the
* INIT-ACK to.
@@ -2648,8 +2642,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
netl->dest_state &= ~SCTP_ADDR_UNCONFIRMED;
(void)sctp_set_primary_addr((*stcb), (struct sockaddr *)NULL,
netl);
- sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED,
- (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED);
+ send_int_conf = 1;
}
}
if (*stcb) {
@@ -2673,13 +2666,19 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
* socket, no need to do anything. I THINK!!
*/
sctp_ulp_notify(notification, *stcb, 0, (void *)&sac_restart_id, SCTP_SO_NOT_LOCKED);
+ if (send_int_conf) {
+ sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED,
+ (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED);
+ }
return (m);
}
oso = (*inp_p)->sctp_socket;
atomic_add_int(&(*stcb)->asoc.refcnt, 1);
SCTP_TCB_UNLOCK((*stcb));
+ CURVNET_SET(oso->so_vnet);
so = sonewconn(oso, 0
);
+ CURVNET_RESTORE();
SCTP_TCB_LOCK((*stcb));
atomic_subtract_int(&(*stcb)->asoc.refcnt, 1);
@@ -2727,6 +2726,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
inp->sctp_socket = so;
inp->sctp_frag_point = (*inp_p)->sctp_frag_point;
inp->sctp_cmt_on_off = (*inp_p)->sctp_cmt_on_off;
+ inp->sctp_ecn_enable = (*inp_p)->sctp_ecn_enable;
inp->partial_delivery_point = (*inp_p)->partial_delivery_point;
inp->sctp_context = (*inp_p)->sctp_context;
inp->inp_starting_point_for_iterator = NULL;
@@ -2784,7 +2784,10 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
/* Switch over to the new guy */
*inp_p = inp;
sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED);
-
+ if (send_int_conf) {
+ sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED,
+ (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED);
+ }
/*
* Pull it from the incomplete queue and wake the
* guy
@@ -2803,8 +2806,14 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
return (m);
}
}
- if ((notification) && ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) {
- sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED);
+ if ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) {
+ if (notification) {
+ sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED);
+ }
+ if (send_int_conf) {
+ sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED,
+ (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED);
+ }
}
return (m);
}
@@ -2839,7 +2848,8 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp,
SCTP_STAT_INCR_GAUGE32(sctps_currestab);
if (asoc->overall_error_count == 0) {
net->RTO = sctp_calculate_rto(stcb, asoc, net,
- &asoc->time_entered, sctp_align_safe_nocopy);
+ &asoc->time_entered, sctp_align_safe_nocopy,
+ SCTP_DETERMINE_LL_NOTOK);
}
(void)SCTP_GETTIME_TIMEVAL(&asoc->time_entered);
sctp_ulp_notify(SCTP_NOTIFY_ASSOC_UP, stcb, 0, NULL, SCTP_SO_NOT_LOCKED);
@@ -2857,24 +2867,31 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp,
SCTP_SOCKET_LOCK(so, 1);
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
- if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) {
- SCTP_SOCKET_UNLOCK(so, 1);
- return;
- }
#endif
- soisconnected(stcb->sctp_socket);
+ if ((stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) == 0) {
+ soisconnected(stcb->sctp_socket);
+ }
#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
SCTP_SOCKET_UNLOCK(so, 1);
#endif
}
- sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep,
- stcb, net);
/*
* since we did not send a HB make sure we don't double
* things
*/
net->hb_responded = 1;
+ if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) {
+ /*
+ * We don't need to do the asconf thing, nor hb or
+ * autoclose if the socket is closed.
+ */
+ goto closed_socket;
+ }
+ sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep,
+ stcb, net);
+
+
if (stcb->asoc.sctp_autoclose_ticks &&
sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_AUTOCLOSE)) {
sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE,
@@ -2898,6 +2915,7 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp,
#endif
}
}
+closed_socket:
/* Toss the cookie if I can */
sctp_toss_old_cookies(stcb, asoc);
if (!TAILQ_EMPTY(&asoc->sent_queue)) {
@@ -2905,10 +2923,7 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp,
struct sctp_tmit_chunk *chk;
chk = TAILQ_FIRST(&asoc->sent_queue);
- if (chk) {
- sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
- stcb, chk->whoTo);
- }
+ sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, chk->whoTo);
}
}
@@ -2918,62 +2933,119 @@ sctp_handle_ecn_echo(struct sctp_ecne_chunk *cp,
{
struct sctp_nets *net;
struct sctp_tmit_chunk *lchk;
- uint32_t tsn;
+ struct sctp_ecne_chunk bkup;
+ uint8_t override_bit = 0;
+ uint32_t tsn, window_data_tsn;
+ int len;
+ unsigned int pkt_cnt;
- if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_ecne_chunk)) {
+ len = ntohs(cp->ch.chunk_length);
+ if ((len != sizeof(struct sctp_ecne_chunk)) &&
+ (len != sizeof(struct old_sctp_ecne_chunk))) {
return;
}
+ if (len == sizeof(struct old_sctp_ecne_chunk)) {
+ /* Its the old format */
+ memcpy(&bkup, cp, sizeof(struct old_sctp_ecne_chunk));
+ bkup.num_pkts_since_cwr = htonl(1);
+ cp = &bkup;
+ }
SCTP_STAT_INCR(sctps_recvecne);
tsn = ntohl(cp->tsn);
- /* ECN Nonce stuff: need a resync and disable the nonce sum check */
- /* Also we make sure we disable the nonce_wait */
- lchk = TAILQ_FIRST(&stcb->asoc.send_queue);
+ pkt_cnt = ntohl(cp->num_pkts_since_cwr);
+ lchk = TAILQ_LAST(&stcb->asoc.send_queue, sctpchunk_listhead);
if (lchk == NULL) {
- stcb->asoc.nonce_resync_tsn = stcb->asoc.sending_seq;
+ window_data_tsn = stcb->asoc.sending_seq - 1;
} else {
- stcb->asoc.nonce_resync_tsn = lchk->rec.data.TSN_seq;
+ window_data_tsn = lchk->rec.data.TSN_seq;
}
- stcb->asoc.nonce_wait_for_ecne = 0;
- stcb->asoc.nonce_sum_check = 0;
- /* Find where it was sent, if possible */
+ /* Find where it was sent to if possible. */
net = NULL;
- lchk = TAILQ_FIRST(&stcb->asoc.sent_queue);
- while (lchk) {
+ TAILQ_FOREACH(lchk, &stcb->asoc.sent_queue, sctp_next) {
if (lchk->rec.data.TSN_seq == tsn) {
net = lchk->whoTo;
+ net->ecn_prev_cwnd = lchk->rec.data.cwnd_at_send;
break;
}
- if (compare_with_wrap(lchk->rec.data.TSN_seq, tsn, MAX_SEQ))
+ if (SCTP_TSN_GT(lchk->rec.data.TSN_seq, tsn)) {
break;
- lchk = TAILQ_NEXT(lchk, sctp_next);
+ }
}
- if (net == NULL)
- /* default is we use the primary */
- net = stcb->asoc.primary_destination;
-
- if (compare_with_wrap(tsn, stcb->asoc.last_cwr_tsn, MAX_TSN)) {
+ if (net == NULL) {
+ /*
+ * What to do. A previous send of a CWR was possibly lost.
+ * See how old it is, we may have it marked on the actual
+ * net.
+ */
+ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+ if (tsn == net->last_cwr_tsn) {
+ /* Found him, send it off */
+ goto out;
+ }
+ }
+ /*
+ * If we reach here, we need to send a special CWR that says
+ * hey, we did this a long time ago and you lost the
+ * response.
+ */
+ net = TAILQ_FIRST(&stcb->asoc.nets);
+ override_bit = SCTP_CWR_REDUCE_OVERRIDE;
+ }
+out:
+ if (SCTP_TSN_GT(tsn, net->cwr_window_tsn) &&
+ ((override_bit & SCTP_CWR_REDUCE_OVERRIDE) == 0)) {
/*
* JRS - Use the congestion control given in the pluggable
* CC module
*/
- stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net);
+ int ocwnd;
+
+ ocwnd = net->cwnd;
+ stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net, 0, pkt_cnt);
/*
- * we reduce once every RTT. So we will only lower cwnd at
- * the next sending seq i.e. the resync_tsn.
+ * We reduce once every RTT. So we will only lower cwnd at
+ * the next sending seq i.e. the window_data_tsn
*/
- stcb->asoc.last_cwr_tsn = stcb->asoc.nonce_resync_tsn;
+ net->cwr_window_tsn = window_data_tsn;
+ net->ecn_ce_pkt_cnt += pkt_cnt;
+ net->lost_cnt = pkt_cnt;
+ net->last_cwr_tsn = tsn;
+ } else {
+ override_bit |= SCTP_CWR_IN_SAME_WINDOW;
+ if (SCTP_TSN_GT(tsn, net->last_cwr_tsn) &&
+ ((override_bit & SCTP_CWR_REDUCE_OVERRIDE) == 0)) {
+ /*
+ * Another loss in the same window update how many
+ * marks/packets lost we have had.
+ */
+ int cnt = 1;
+
+ if (pkt_cnt > net->lost_cnt) {
+ /* Should be the case */
+ cnt = (pkt_cnt - net->lost_cnt);
+ net->ecn_ce_pkt_cnt += cnt;
+ }
+ net->lost_cnt = pkt_cnt;
+ net->last_cwr_tsn = tsn;
+ /*
+ * Most CC functions will ignore this call, since we
+ * are in-window yet of the initial CE the peer saw.
+ */
+ stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net, 1, cnt);
+ }
}
/*
* We always send a CWR this way if our previous one was lost our
* peer will get an update, or if it is not time again to reduce we
- * still get the cwr to the peer.
+ * still get the cwr to the peer. Note we set the override when we
+ * could not find the TSN on the chunk or the destination network.
*/
- sctp_send_cwr(stcb, net, tsn);
+ sctp_send_cwr(stcb, net, net->last_cwr_tsn, override_bit);
}
static void
-sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb)
+sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb, struct sctp_nets *net)
{
/*
* Here we get a CWR from the peer. We must look in the outqueue and
@@ -2982,19 +3054,22 @@ sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb)
*/
struct sctp_tmit_chunk *chk;
struct sctp_ecne_chunk *ecne;
+ int override;
+ uint32_t cwr_tsn;
+ cwr_tsn = ntohl(cp->tsn);
+
+ override = cp->ch.chunk_flags & SCTP_CWR_REDUCE_OVERRIDE;
TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
if (chk->rec.chunk_id.id != SCTP_ECN_ECHO) {
continue;
}
- /*
- * Look for and remove if it is the right TSN. Since there
- * is only ONE ECNE on the control queue at any one time we
- * don't need to worry about more than one!
- */
+ if ((override == 0) && (chk->whoTo != net)) {
+ /* Must be from the right src unless override is set */
+ continue;
+ }
ecne = mtod(chk->data, struct sctp_ecne_chunk *);
- if (compare_with_wrap(ntohl(cp->tsn), ntohl(ecne->tsn),
- MAX_TSN) || (cp->tsn == ecne->tsn)) {
+ if (SCTP_TSN_GE(cwr_tsn, ntohl(ecne->tsn))) {
/* this covers this ECNE, we can remove it */
stcb->asoc.ecn_echo_cnt_onq--;
TAILQ_REMOVE(&stcb->asoc.control_send_queue, chk,
@@ -3005,7 +3080,9 @@ sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb)
}
stcb->asoc.ctrl_queue_cnt--;
sctp_free_a_chunk(stcb, chk);
- break;
+ if (override == 0) {
+ break;
+ }
}
}
}
@@ -3041,7 +3118,7 @@ sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp,
/* are the queues empty? they should be */
if (!TAILQ_EMPTY(&asoc->send_queue) ||
!TAILQ_EMPTY(&asoc->sent_queue) ||
- !TAILQ_EMPTY(&asoc->out_wheel)) {
+ !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
sctp_report_all_outbound(stcb, 0, SCTP_SO_NOT_LOCKED);
}
}
@@ -3078,19 +3155,16 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
struct sctp_tmit_chunk *tp1;
tsn = ntohl(desc->tsn_ifany);
- tp1 = TAILQ_FIRST(&stcb->asoc.sent_queue);
- while (tp1) {
+ TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) {
if (tp1->rec.data.TSN_seq == tsn) {
/* found it */
break;
}
- if (compare_with_wrap(tp1->rec.data.TSN_seq, tsn,
- MAX_TSN)) {
+ if (SCTP_TSN_GT(tp1->rec.data.TSN_seq, tsn)) {
/* not found */
tp1 = NULL;
break;
}
- tp1 = TAILQ_NEXT(tp1, sctp_next);
}
if (tp1 == NULL) {
/*
@@ -3098,13 +3172,11 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
* attention to queue seq order.
*/
SCTP_STAT_INCR(sctps_pdrpdnfnd);
- tp1 = TAILQ_FIRST(&stcb->asoc.sent_queue);
- while (tp1) {
+ TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) {
if (tp1->rec.data.TSN_seq == tsn) {
/* found it */
break;
}
- tp1 = TAILQ_NEXT(tp1, sctp_next);
}
}
if (tp1 == NULL) {
@@ -3140,11 +3212,6 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
}
}
}
- /*
- * We zero out the nonce so resync not
- * needed
- */
- tp1->rec.data.ect_nonce = 0;
if (tp1->do_rtt) {
/*
@@ -3156,7 +3223,6 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
SCTP_STAT_INCR(sctps_pdrpmark);
if (tp1->sent != SCTP_DATAGRAM_RESEND)
sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt);
- tp1->sent = SCTP_DATAGRAM_RESEND;
/*
* mark it as if we were doing a FR, since
* we will be getting gap ack reports behind
@@ -3191,6 +3257,7 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
sctp_flight_size_decrease(tp1);
sctp_total_flight_decrease(stcb, tp1);
}
+ tp1->sent = SCTP_DATAGRAM_RESEND;
} {
/* audit code */
unsigned int audit;
@@ -3695,8 +3762,7 @@ sctp_handle_str_reset_request_out(struct sctp_tcb *stcb,
if (trunc) {
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED);
asoc->last_reset_action[0] = SCTP_STREAM_RESET_DENIED;
- } else if ((tsn == asoc->cumulative_tsn) ||
- (compare_with_wrap(asoc->cumulative_tsn, tsn, MAX_TSN))) {
+ } else if (SCTP_TSN_GE(asoc->cumulative_tsn, tsn)) {
/* we can do it now */
sctp_reset_in_stream(stcb, number_entries, req->list_of_streams);
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED);
@@ -3755,7 +3821,7 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch
uint16_t num_stream, i;
uint32_t seq;
struct sctp_association *asoc = &stcb->asoc;
- struct sctp_queued_to_read *ctl;
+ struct sctp_queued_to_read *ctl, *nctl;
/* Get the number. */
seq = ntohl(str_add->request_seq);
@@ -3789,8 +3855,7 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch
stcb->asoc.strmin[i].last_sequence_delivered = oldstrm[i].last_sequence_delivered;
stcb->asoc.strmin[i].delivery_started = oldstrm[i].delivery_started;
/* now anything on those queues? */
- while (TAILQ_EMPTY(&oldstrm[i].inqueue) == 0) {
- ctl = TAILQ_FIRST(&oldstrm[i].inqueue);
+ TAILQ_FOREACH_SAFE(ctl, &oldstrm[i].inqueue, next, nctl) {
TAILQ_REMOVE(&oldstrm[i].inqueue, ctl, next);
TAILQ_INSERT_TAIL(&stcb->asoc.strmin[i].inqueue, ctl, next);
}
@@ -4158,6 +4223,7 @@ __attribute__((noinline))
uint32_t chk_length;
int ret;
int abort_no_unlock = 0;
+ int ecne_seen = 0;
/*
* How big should this be, and should it be alloc'd? Lets try the
@@ -4601,7 +4667,6 @@ process_control_chunks:
uint16_t num_seg, num_dup;
uint8_t flags;
int offset_seg, offset_dup;
- int nonce_sum_flag;
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_SACK\n");
SCTP_STAT_INCR(sctps_recvsacks);
@@ -4623,7 +4688,6 @@ process_control_chunks:
}
sack = (struct sctp_sack_chunk *)ch;
flags = ch->chunk_flags;
- nonce_sum_flag = flags & SCTP_SACK_NONCE_SUM;
cum_ack = ntohl(sack->sack.cum_tsn_ack);
num_seg = ntohs(sack->sack.num_gap_ack_blks);
num_dup = ntohs(sack->sack.num_dup_tsns);
@@ -4641,8 +4705,7 @@ process_control_chunks:
stcb->asoc.seen_a_sack_this_pkt = 1;
if ((stcb->asoc.pr_sctp_cnt == 0) &&
(num_seg == 0) &&
- ((compare_with_wrap(cum_ack, stcb->asoc.last_acked_seq, MAX_TSN)) ||
- (cum_ack == stcb->asoc.last_acked_seq)) &&
+ SCTP_TSN_GE(cum_ack, stcb->asoc.last_acked_seq) &&
(stcb->asoc.saw_sack_with_frags == 0) &&
(stcb->asoc.saw_sack_with_nr_frags == 0) &&
(!TAILQ_EMPTY(&stcb->asoc.sent_queue))
@@ -4656,14 +4719,13 @@ process_control_chunks:
* with no missing segments to go
* this way too.
*/
- sctp_express_handle_sack(stcb, cum_ack, a_rwnd, nonce_sum_flag,
- &abort_now);
+ sctp_express_handle_sack(stcb, cum_ack, a_rwnd, &abort_now, ecne_seen);
} else {
if (netp && *netp)
sctp_handle_sack(m, offset_seg, offset_dup,
stcb, *netp,
num_seg, 0, num_dup, &abort_now, flags,
- cum_ack, a_rwnd);
+ cum_ack, a_rwnd, ecne_seen);
}
if (abort_now) {
/* ABORT signal from sack processing */
@@ -4689,7 +4751,6 @@ process_control_chunks:
uint16_t num_seg, num_nr_seg, num_dup;
uint8_t flags;
int offset_seg, offset_dup;
- int nonce_sum_flag;
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_NR_SACK\n");
SCTP_STAT_INCR(sctps_recvsacks);
@@ -4715,8 +4776,6 @@ process_control_chunks:
}
nr_sack = (struct sctp_nr_sack_chunk *)ch;
flags = ch->chunk_flags;
- nonce_sum_flag = flags & SCTP_SACK_NONCE_SUM;
-
cum_ack = ntohl(nr_sack->nr_sack.cum_tsn_ack);
num_seg = ntohs(nr_sack->nr_sack.num_gap_ack_blks);
num_nr_seg = ntohs(nr_sack->nr_sack.num_nr_gap_ack_blks);
@@ -4735,8 +4794,7 @@ process_control_chunks:
stcb->asoc.seen_a_sack_this_pkt = 1;
if ((stcb->asoc.pr_sctp_cnt == 0) &&
(num_seg == 0) && (num_nr_seg == 0) &&
- ((compare_with_wrap(cum_ack, stcb->asoc.last_acked_seq, MAX_TSN)) ||
- (cum_ack == stcb->asoc.last_acked_seq)) &&
+ SCTP_TSN_GE(cum_ack, stcb->asoc.last_acked_seq) &&
(stcb->asoc.saw_sack_with_frags == 0) &&
(stcb->asoc.saw_sack_with_nr_frags == 0) &&
(!TAILQ_EMPTY(&stcb->asoc.sent_queue))) {
@@ -4749,14 +4807,14 @@ process_control_chunks:
* missing segments to go this way
* too.
*/
- sctp_express_handle_sack(stcb, cum_ack, a_rwnd, nonce_sum_flag,
- &abort_now);
+ sctp_express_handle_sack(stcb, cum_ack, a_rwnd,
+ &abort_now, ecne_seen);
} else {
if (netp && *netp)
sctp_handle_sack(m, offset_seg, offset_dup,
stcb, *netp,
num_seg, num_nr_seg, num_dup, &abort_now, flags,
- cum_ack, a_rwnd);
+ cum_ack, a_rwnd, ecne_seen);
}
if (abort_now) {
/* ABORT signal from sack processing */
@@ -4965,11 +5023,7 @@ process_control_chunks:
struct sctp_tmit_chunk *chk;
chk = TAILQ_FIRST(&stcb->asoc.sent_queue);
- if (chk) {
- sctp_timer_start(SCTP_TIMER_TYPE_SEND,
- stcb->sctp_ep, stcb,
- chk->whoTo);
- }
+ sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, chk->whoTo);
}
}
break;
@@ -5037,6 +5091,7 @@ process_control_chunks:
stcb->asoc.overall_error_count = 0;
sctp_handle_ecn_echo((struct sctp_ecne_chunk *)ch,
stcb);
+ ecne_seen = 1;
}
break;
case SCTP_ECN_CWR:
@@ -5059,7 +5114,7 @@ process_control_chunks:
__LINE__);
}
stcb->asoc.overall_error_count = 0;
- sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb);
+ sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb, *netp);
}
break;
case SCTP_SHUTDOWN_COMPLETE:
@@ -5359,69 +5414,6 @@ next_chunk:
}
-/*
- * Process the ECN bits we have something set so we must look to see if it is
- * ECN(0) or ECN(1) or CE
- */
-static void
-sctp_process_ecn_marked_a(struct sctp_tcb *stcb, struct sctp_nets *net,
- uint8_t ecn_bits)
-{
- if ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS) {
- ;
- } else if ((ecn_bits & SCTP_ECT1_BIT) == SCTP_ECT1_BIT) {
- /*
- * we only add to the nonce sum for ECT1, ECT0 does not
- * change the NS bit (that we have yet to find a way to send
- * it yet).
- */
-
- /* ECN Nonce stuff */
- stcb->asoc.receiver_nonce_sum++;
- stcb->asoc.receiver_nonce_sum &= SCTP_SACK_NONCE_SUM;
-
- /*
- * Drag up the last_echo point if cumack is larger since we
- * don't want the point falling way behind by more than
- * 2^^31 and then having it be incorrect.
- */
- if (compare_with_wrap(stcb->asoc.cumulative_tsn,
- stcb->asoc.last_echo_tsn, MAX_TSN)) {
- stcb->asoc.last_echo_tsn = stcb->asoc.cumulative_tsn;
- }
- } else if ((ecn_bits & SCTP_ECT0_BIT) == SCTP_ECT0_BIT) {
- /*
- * Drag up the last_echo point if cumack is larger since we
- * don't want the point falling way behind by more than
- * 2^^31 and then having it be incorrect.
- */
- if (compare_with_wrap(stcb->asoc.cumulative_tsn,
- stcb->asoc.last_echo_tsn, MAX_TSN)) {
- stcb->asoc.last_echo_tsn = stcb->asoc.cumulative_tsn;
- }
- }
-}
-
-static void
-sctp_process_ecn_marked_b(struct sctp_tcb *stcb, struct sctp_nets *net,
- uint32_t high_tsn, uint8_t ecn_bits)
-{
- if ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS) {
- /*
- * we possibly must notify the sender that a congestion
- * window reduction is in order. We do this by adding a ECNE
- * chunk to the output chunk queue. The incoming CWR will
- * remove this chunk.
- */
- if (compare_with_wrap(high_tsn, stcb->asoc.last_echo_tsn,
- MAX_TSN)) {
- /* Yep, we need to add a ECNE */
- sctp_send_ecn_echo(stcb, net, high_tsn);
- stcb->asoc.last_echo_tsn = high_tsn;
- }
- }
-}
-
#ifdef INVARIANTS
#ifdef __GNUC__
__attribute__((noinline))
@@ -5463,6 +5455,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
struct mbuf *m = *mm;
int abort_flag = 0;
int un_sent;
+ int cnt_ctrl_ready = 0;
SCTP_STAT_INCR(sctps_recvdatagrams);
#ifdef SCTP_AUDITING_ENABLED
@@ -5620,11 +5613,6 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
case SCTP_STATE_SHUTDOWN_SENT:
break;
}
- /* take care of ECN, part 1. */
- if (stcb->asoc.ecn_allowed &&
- (ecn_bits & (SCTP_ECT0_BIT | SCTP_ECT1_BIT))) {
- sctp_process_ecn_marked_a(stcb, net, ecn_bits);
- }
/* plow through the data chunks while length > offset */
retval = sctp_process_data(mm, iphlen, &offset, length, sh,
inp, stcb, net, &high_tsn);
@@ -5636,27 +5624,27 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
goto out_now;
}
data_processed = 1;
- if (retval == 0) {
- /* take care of ecn part 2. */
- if (stcb->asoc.ecn_allowed &&
- (ecn_bits & (SCTP_ECT0_BIT | SCTP_ECT1_BIT))) {
- sctp_process_ecn_marked_b(stcb, net, high_tsn,
- ecn_bits);
- }
- }
/*
* Anything important needs to have been m_copy'ed in
* process_data
*/
}
+ /* take care of ecn */
+ if ((stcb->asoc.ecn_allowed == 1) &&
+ ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS)) {
+ /* Yep, we need to add a ECNE */
+ sctp_send_ecn_echo(stcb, net, high_tsn);
+ }
if ((data_processed == 0) && (fwd_tsn_seen)) {
- int was_a_gap = 0;
+ int was_a_gap;
+ uint32_t highest_tsn;
- if (compare_with_wrap(stcb->asoc.highest_tsn_inside_map,
- stcb->asoc.cumulative_tsn, MAX_TSN)) {
- /* there was a gap before this data was processed */
- was_a_gap = 1;
+ if (SCTP_TSN_GT(stcb->asoc.highest_tsn_inside_nr_map, stcb->asoc.highest_tsn_inside_map)) {
+ highest_tsn = stcb->asoc.highest_tsn_inside_nr_map;
+ } else {
+ highest_tsn = stcb->asoc.highest_tsn_inside_map;
}
+ was_a_gap = SCTP_TSN_GT(highest_tsn, stcb->asoc.cumulative_tsn);
stcb->asoc.send_sack = 1;
sctp_sack_check(stcb, was_a_gap, &abort_flag);
if (abort_flag) {
@@ -5678,8 +5666,10 @@ trigger_send:
TAILQ_EMPTY(&stcb->asoc.control_send_queue),
stcb->asoc.total_flight);
un_sent = (stcb->asoc.total_output_queue_size - stcb->asoc.total_flight);
-
- if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue) ||
+ if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue)) {
+ cnt_ctrl_ready = stcb->asoc.ctrl_queue_cnt - stcb->asoc.ecn_echo_cnt_onq;
+ }
+ if (cnt_ctrl_ready ||
((un_sent) &&
(stcb->asoc.peers_rwnd > 0 ||
(stcb->asoc.peers_rwnd <= 0 && stcb->asoc.total_flight == 0)))) {
@@ -5837,6 +5827,12 @@ sctp_input_with_port(struct mbuf *i_pak, int off, uint16_t port)
}
net->port = port;
}
+ if ((net != NULL) && (m->m_flags & M_FLOWID)) {
+ net->flowid = m->m_pkthdr.flowid;
+#ifdef INVARIANTS
+ net->flowidset = 1;
+#endif
+ }
if ((inp) && (stcb)) {
sctp_send_packet_dropped(stcb, net, m, iphlen, 1);
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED);
@@ -5866,6 +5862,12 @@ sctp_skip_csum_4:
}
net->port = port;
}
+ if ((net != NULL) && (m->m_flags & M_FLOWID)) {
+ net->flowid = m->m_pkthdr.flowid;
+#ifdef INVARIANTS
+ net->flowidset = 1;
+#endif
+ }
/* inp's ref-count increased && stcb locked */
if (inp == NULL) {
struct sctp_init_chunk *init_chk, chunk_buf;
@@ -5952,10 +5954,49 @@ bad:
}
return;
}
+
+#if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP)
+extern int *sctp_cpuarry;
+
+#endif
+
void
-sctp_input(i_pak, off)
- struct mbuf *i_pak;
- int off;
+sctp_input(struct mbuf *m, int off)
{
- sctp_input_with_port(i_pak, off, 0);
+#if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP)
+ struct ip *ip;
+ struct sctphdr *sh;
+ int offset;
+ int cpu_to_use;
+ uint32_t flowid, tag;
+
+ if (mp_ncpus > 1) {
+ if (m->m_flags & M_FLOWID) {
+ flowid = m->m_pkthdr.flowid;
+ } else {
+ /*
+ * No flow id built by lower layers fix it so we
+ * create one.
+ */
+ ip = mtod(m, struct ip *);
+ offset = off + sizeof(*sh);
+ if (SCTP_BUF_LEN(m) < offset) {
+ if ((m = m_pullup(m, offset)) == 0) {
+ SCTP_STAT_INCR(sctps_hdrops);
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ }
+ sh = (struct sctphdr *)((caddr_t)ip + off);
+ tag = htonl(sh->v_tag);
+ flowid = tag ^ ntohs(sh->dest_port) ^ ntohs(sh->src_port);
+ m->m_pkthdr.flowid = flowid;
+ m->m_flags |= M_FLOWID;
+ }
+ cpu_to_use = sctp_cpuarry[flowid % mp_ncpus];
+ sctp_queue_to_mcore(m, off, cpu_to_use);
+ return;
+ }
+#endif
+ sctp_input_with_port(m, off, 0);
}
OpenPOWER on IntegriCloud