summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/sctp.h1
-rw-r--r--sys/netinet/sctp_asconf.c251
-rw-r--r--sys/netinet/sctp_asconf.h3
-rw-r--r--sys/netinet/sctp_cc_functions.c432
-rw-r--r--sys/netinet/sctp_constants.h6
-rw-r--r--sys/netinet/sctp_indata.c4
-rw-r--r--sys/netinet/sctp_input.c104
-rw-r--r--sys/netinet/sctp_input.h2
-rw-r--r--sys/netinet/sctp_lock_bsd.h8
-rw-r--r--sys/netinet/sctp_os_bsd.h6
-rw-r--r--sys/netinet/sctp_output.c658
-rw-r--r--sys/netinet/sctp_output.h8
-rw-r--r--sys/netinet/sctp_pcb.c174
-rw-r--r--sys/netinet/sctp_pcb.h3
-rw-r--r--sys/netinet/sctp_structs.h18
-rw-r--r--sys/netinet/sctp_sysctl.c37
-rw-r--r--sys/netinet/sctp_sysctl.h22
-rw-r--r--sys/netinet/sctp_timer.c65
-rw-r--r--sys/netinet/sctp_usrreq.c2
-rw-r--r--sys/netinet/sctp_var.h2
-rw-r--r--sys/netinet/sctputil.c40
-rw-r--r--sys/netinet/sctputil.h7
-rw-r--r--sys/netinet6/sctp6_usrreq.c6
23 files changed, 1304 insertions, 555 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h
index 959865e..295204a 100644
--- a/sys/netinet/sctp.h
+++ b/sys/netinet/sctp.h
@@ -472,6 +472,7 @@ __attribute__((packed));
#define SCTP_PCB_FLAGS_NO_FRAGMENT 0x00100000
#define SCTP_PCB_FLAGS_EXPLICIT_EOR 0x00400000
#define SCTP_PCB_FLAGS_NEEDS_MAPPED_V4 0x00800000
+#define SCTP_PCB_FLAGS_MULTIPLE_ASCONFS 0x01000000
/*-
* mobility_features parameters (by micchie).Note
diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c
index e3bb19f..a1df7e6 100644
--- a/sys/netinet/sctp_asconf.c
+++ b/sys/netinet/sctp_asconf.c
@@ -931,6 +931,56 @@ sctp_asconf_addr_match(struct sctp_asconf_addr *aa, struct sockaddr *sa)
}
/*
+ * does the address match? returns 0 if not, 1 if so
+ */
+static uint32_t
+sctp_addr_match(
+#ifdef INET6
+ struct sctp_ipv6addr_param *v6addr,
+#else
+ struct sctp_ipv4addr_param *v4addr,
+#endif
+ struct sockaddr *sa)
+{
+ uint16_t param_type, param_length;
+
+#ifdef INET6
+ struct sctp_ipv4addr_param *v4addr = (struct sctp_ipv4addr_param *)v6addr;
+
+ if (sa->sa_family == AF_INET6) {
+ /* IPv6 sa address */
+ /* XXX scopeid */
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ param_type = ntohs(v6addr->ph.param_type);
+ param_length = ntohs(v6addr->ph.param_length);
+
+ if ((param_type == SCTP_IPV6_ADDRESS) &&
+ param_length == sizeof(struct sctp_ipv6addr_param) &&
+ (memcmp(&v6addr->addr, &sin6->sin6_addr,
+ sizeof(struct in6_addr)) == 0)) {
+ return (1);
+ }
+ } else
+#endif /* INET6 */
+ if (sa->sa_family == AF_INET) {
+ /* IPv4 sa address */
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+ param_type = ntohs(v4addr->ph.param_type);
+ param_length = ntohs(v4addr->ph.param_length);
+
+ if ((param_type == SCTP_IPV4_ADDRESS) &&
+ param_length == sizeof(struct sctp_ipv4addr_param) &&
+ (memcmp(&v4addr->addr, &sin->sin_addr,
+ sizeof(struct in_addr)) == 0)) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
* Cleanup for non-responded/OP ERR'd ASCONF
*/
void
@@ -943,7 +993,7 @@ sctp_asconf_cleanup(struct sctp_tcb *stcb, struct sctp_nets *net)
*/
sctp_timer_stop(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep, stcb, net,
SCTP_FROM_SCTP_ASCONF + SCTP_LOC_2);
- stcb->asoc.asconf_seq_out++;
+ stcb->asoc.asconf_seq_out_acked = stcb->asoc.asconf_seq_out;
/* remove the old ASCONF on our outbound queue */
sctp_toss_old_asconf(stcb);
}
@@ -1259,8 +1309,14 @@ sctp_asconf_queue_mgmt(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
/* address match? */
if (sctp_asconf_addr_match(aa, &ifa->address.sa) == 0)
continue;
- /* is the request already in queue (sent or not) */
- if (aa->ap.aph.ph.param_type == type) {
+ /*
+ * is the request already in queue but not sent? pass the
+ * request already sent in order to resolve the following
+ * case: 1. arrival of ADD, then sent 2. arrival of DEL. we
+ * can't remove the ADD request already sent 3. arrival of
+ * ADD
+ */
+ if (aa->ap.aph.ph.param_type == type && aa->sent == 0) {
return (-1);
}
/* is the negative request already in queue, and not sent */
@@ -1334,31 +1390,21 @@ sctp_asconf_queue_mgmt(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
}
aa->sent = 0; /* clear sent flag */
- /*
- * if we are deleting an address it should go out last otherwise,
- * add it to front of the pending queue
- */
- if (type == SCTP_ADD_IP_ADDRESS) {
- /* add goes to the front of the queue */
- TAILQ_INSERT_HEAD(&stcb->asoc.asconf_queue, aa, next);
- SCTPDBG(SCTP_DEBUG_ASCONF2,
- "asconf_queue_mgmt: inserted asconf ADD_IP_ADDRESS: ");
- SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
- } else {
- /* delete and set primary goes to the back of the queue */
- TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next);
+ TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next);
#ifdef SCTP_DEBUG
- if (sctp_debug_on && SCTP_DEBUG_ASCONF2) {
- if (type == SCTP_DEL_IP_ADDRESS) {
- SCTP_PRINTF("asconf_queue_mgmt: appended asconf DEL_IP_ADDRESS: ");
- SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
- } else {
- SCTP_PRINTF("asconf_queue_mgmt: appended asconf SET_PRIM_ADDR: ");
- SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
- }
+ if (sctp_debug_on && SCTP_DEBUG_ASCONF2) {
+ if (type == SCTP_ADD_IP_ADDRESS) {
+ SCTP_PRINTF("asconf_queue_mgmt: inserted asconf ADD_IP_ADDRESS: ");
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
+ } else if (type == SCTP_DEL_IP_ADDRESS) {
+ SCTP_PRINTF("asconf_queue_mgmt: appended asconf DEL_IP_ADDRESS: ");
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
+ } else {
+ SCTP_PRINTF("asconf_queue_mgmt: appended asconf SET_PRIM_ADDR: ");
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
}
-#endif
}
+#endif
return (0);
}
@@ -1395,12 +1441,15 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
"asconf_queue_add: mark delete last address pending\n");
return (-1);
}
+ /* queue an asconf parameter */
+ status = sctp_asconf_queue_mgmt(stcb, ifa, type);
+
/*
* if this is an add, and there is a delete also pending (i.e. the
* last local address is being changed), queue the pending delete
* too.
*/
- if ((type == SCTP_ADD_IP_ADDRESS) && stcb->asoc.asconf_del_pending) {
+ if ((type == SCTP_ADD_IP_ADDRESS) && stcb->asoc.asconf_del_pending && (status == 0)) {
/* queue in the pending delete */
if (sctp_asconf_queue_mgmt(stcb,
stcb->asoc.asconf_addr_del_pending,
@@ -1413,10 +1462,7 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
stcb->asoc.asconf_addr_del_pending = NULL;
}
}
- /* queue an asconf parameter */
- status = sctp_asconf_queue_mgmt(stcb, ifa, type);
-
- if (pending_delete_queued && (status == 0)) {
+ if (pending_delete_queued) {
struct sctp_nets *net;
/*
@@ -1652,6 +1698,8 @@ sctp_asconf_process_param_ack(struct sctp_tcb *stcb,
/* nothing really to do... lists already updated */
break;
case SCTP_SET_PRIM_ADDR:
+ SCTPDBG(SCTP_DEBUG_ASCONF1,
+ "process_param_ack: set primary IP address\n");
/* nothing to do... peer may start using this addr */
if (flag == 0)
stcb->asoc.peer_supports_asconf = 0;
@@ -1725,23 +1773,17 @@ sctp_handle_asconf_ack(struct mbuf *m, int offset,
*abort_no_unlock = 1;
return;
}
- if (serial_num != asoc->asconf_seq_out) {
+ if (serial_num != asoc->asconf_seq_out_acked + 1) {
/* got a duplicate/unexpected ASCONF-ACK */
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf_ack: got duplicate/unexpected serial number = %xh (expected = %xh)\n",
- serial_num, asoc->asconf_seq_out);
+ serial_num, asoc->asconf_seq_out_acked + 1);
return;
}
- if (stcb->asoc.asconf_sent == 0) {
- /* got a unexpected ASCONF-ACK for serial not in flight */
- SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf_ack: got serial number = %xh but not in flight\n",
- serial_num);
- /* nothing to do... duplicate ACK received */
- return;
+ if (serial_num == asoc->asconf_seq_out - 1) {
+ /* stop our timer */
+ sctp_timer_stop(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep, stcb, net,
+ SCTP_FROM_SCTP_ASCONF + SCTP_LOC_3);
}
- /* stop our timer */
- sctp_timer_stop(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep, stcb, net,
- SCTP_FROM_SCTP_ASCONF + SCTP_LOC_3);
-
/* process the ASCONF-ACK contents */
ack_length = ntohs(cp->ch.chunk_length) -
sizeof(struct sctp_asconf_ack_chunk);
@@ -1855,11 +1897,9 @@ sctp_handle_asconf_ack(struct mbuf *m, int offset,
}
/* update the next sequence number to use */
- asoc->asconf_seq_out++;
+ asoc->asconf_seq_out_acked++;
/* remove the old ASCONF on our outbound queue */
sctp_toss_old_asconf(stcb);
- /* clear the sent flag to allow new ASCONFs */
- asoc->asconf_sent = 0;
if (!TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
#ifdef SCTP_TIMER_BASED_ASCONF
/* we have more params, so restart our timer */
@@ -2379,6 +2419,102 @@ sctp_set_primary_ip_address(struct sctp_ifa *ifa)
} /* for each inp */
}
+int
+sctp_is_addr_pending(struct sctp_tcb *stcb, struct sctp_ifa *sctp_ifa)
+{
+ struct sctp_tmit_chunk *chk, *nchk;
+ unsigned int offset, asconf_limit;
+ struct sctp_asconf_chunk *acp;
+ struct sctp_asconf_paramhdr *aph;
+ uint8_t aparam_buf[SCTP_PARAM_BUFFER_SIZE];
+ struct sctp_ipv6addr_param *p_addr;
+ int add_cnt, del_cnt;
+ uint16_t last_param_type;
+
+ add_cnt = del_cnt = 0;
+ last_param_type = 0;
+ for (chk = TAILQ_FIRST(&stcb->asoc.asconf_send_queue); chk != NULL;
+ chk = nchk) {
+ /* get next chk */
+ nchk = TAILQ_NEXT(chk, sctp_next);
+
+ if (chk->data == NULL) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "is_addr_pending: No mbuf data?\n");
+ continue;
+ }
+ offset = 0;
+ acp = mtod(chk->data, struct sctp_asconf_chunk *);
+ offset += sizeof(struct sctp_asconf_chunk);
+ asconf_limit = ntohs(acp->ch.chunk_length);
+ p_addr = (struct sctp_ipv6addr_param *)sctp_m_getptr(chk->data, offset, sizeof(struct sctp_paramhdr), aparam_buf);
+ if (p_addr == NULL) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "is_addr_pending: couldn't get lookup addr!\n");
+ continue;
+ }
+ offset += ntohs(p_addr->ph.param_length);
+
+ aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(chk->data, offset, sizeof(struct sctp_asconf_paramhdr), aparam_buf);
+ if (aph == NULL) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "is_addr_pending: Empty ASCONF will be sent?\n");
+ continue;
+ }
+ while (aph != NULL) {
+ unsigned int param_length, param_type;
+
+ param_type = ntohs(aph->ph.param_type);
+ param_length = ntohs(aph->ph.param_length);
+ if (offset + param_length > asconf_limit) {
+ /* parameter goes beyond end of chunk! */
+ break;
+ }
+ if (param_length > sizeof(aparam_buf)) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "is_addr_pending: param length (%u) larger than buffer size!\n", param_length);
+ break;
+ }
+ if (param_length <= sizeof(struct sctp_paramhdr)) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "is_addr_pending: param length(%u) too short\n", param_length);
+ break;
+ }
+ aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(chk->data, offset, param_length, aparam_buf);
+ if (aph == NULL) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "is_addr_pending: couldn't get entire param\n");
+ break;
+ }
+ p_addr = (struct sctp_ipv6addr_param *)(aph + 1);
+ if (sctp_addr_match(p_addr, &sctp_ifa->address.sa) != 0) {
+ switch (param_type) {
+ case SCTP_ADD_IP_ADDRESS:
+ add_cnt++;
+ break;
+ case SCTP_DEL_IP_ADDRESS:
+ del_cnt++;
+ break;
+ default:
+ break;
+ }
+ last_param_type = param_type;
+ }
+ offset += SCTP_SIZE32(param_length);
+ if (offset >= asconf_limit) {
+ /* no more data in the mbuf chain */
+ break;
+ }
+ /* get pointer to next asconf param */
+ aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(chk->data, offset, sizeof(struct sctp_asconf_paramhdr), aparam_buf);
+ }
+ }
+
+ /*
+ * we want to find the sequences which consist of ADD -> DEL -> ADD
+ * or DEL -> ADD
+ */
+ if (add_cnt > del_cnt ||
+ (add_cnt == del_cnt && last_param_type == SCTP_ADD_IP_ADDRESS)) {
+ return 1;
+ }
+ return 0;
+}
+
static struct sockaddr *
sctp_find_valid_localaddr(struct sctp_tcb *stcb, int addr_locked)
{
@@ -2414,7 +2550,8 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb, int addr_locked)
IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))
continue;
- if (sctp_is_addr_restricted(stcb, sctp_ifa))
+ if (sctp_is_addr_restricted(stcb, sctp_ifa) &&
+ (!sctp_is_addr_pending(stcb, sctp_ifa)))
continue;
/* found a valid local v4 address to use */
if (addr_locked == SCTP_ADDR_NOT_LOCKED)
@@ -2439,6 +2576,9 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb, int addr_locked)
IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
continue;
+ if (sctp_is_addr_restricted(stcb, sctp_ifa) &&
+ (!sctp_is_addr_pending(stcb, sctp_ifa)))
+ continue;
/* found a valid local v6 address to use */
if (addr_locked == SCTP_ADDR_NOT_LOCKED)
SCTP_IPI_ADDR_RUNLOCK();
@@ -2462,7 +2602,8 @@ sctp_find_valid_localaddr_ep(struct sctp_tcb *stcb)
continue;
}
/* is the address restricted ? */
- if (sctp_is_addr_restricted(stcb, laddr->ifa))
+ if (sctp_is_addr_restricted(stcb, laddr->ifa) &&
+ (!sctp_is_addr_pending(stcb, laddr->ifa)))
continue;
/* found a valid local address to use */
@@ -2490,13 +2631,13 @@ sctp_compose_asconf(struct sctp_tcb *stcb, int *retlen, int addr_locked)
uint8_t lookup_used = 0;
/* are there any asconf params to send? */
- if (TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
- return (NULL);
+ TAILQ_FOREACH(aa, &stcb->asoc.asconf_queue, next) {
+ if (aa->sent == 0)
+ break;
}
- /* can't send a new one if there is one in flight already */
- if (stcb->asoc.asconf_sent > 0) {
+ if (aa == NULL)
return (NULL);
- }
+
/*
* get a chunk header mbuf and a cluster for the asconf params since
* it's simpler to fill in the asconf chunk header lookup address on
@@ -2529,9 +2670,12 @@ sctp_compose_asconf(struct sctp_tcb *stcb, int *retlen, int addr_locked)
acp->ch.chunk_type = SCTP_ASCONF;
acp->ch.chunk_flags = 0;
acp->serial_number = htonl(stcb->asoc.asconf_seq_out);
+ stcb->asoc.asconf_seq_out++;
/* add parameters... up to smallest MTU allowed */
TAILQ_FOREACH(aa, &stcb->asoc.asconf_queue, next) {
+ if (aa->sent)
+ continue;
/* get the parameter length */
p_length = SCTP_SIZE32(aa->ap.aph.ph.param_length);
/* will it fit in current chunk? */
@@ -2647,9 +2791,6 @@ sctp_compose_asconf(struct sctp_tcb *stcb, int *retlen, int addr_locked)
*retlen = SCTP_BUF_LEN(m_asconf_chk) + SCTP_BUF_LEN(m_asconf);
acp->ch.chunk_length = ntohs(*retlen);
- /* update "sent" flag */
- stcb->asoc.asconf_sent++;
-
return (m_asconf_chk);
}
diff --git a/sys/netinet/sctp_asconf.h b/sys/netinet/sctp_asconf.h
index ca57c00..12f1281 100644
--- a/sys/netinet/sctp_asconf.h
+++ b/sys/netinet/sctp_asconf.h
@@ -86,6 +86,9 @@ extern void
extern void
sctp_net_immediate_retrans(struct sctp_tcb *, struct sctp_nets *);
+extern int
+ sctp_is_addr_pending(struct sctp_tcb *, struct sctp_ifa *);
+
#endif /* _KERNEL */
#endif /* !_NETINET_SCTP_ASCONF_H_ */
diff --git a/sys/netinet/sctp_cc_functions.c b/sys/netinet/sctp_cc_functions.c
index 3e22a54..a4ada89 100644
--- a/sys/netinet/sctp_cc_functions.c
+++ b/sys/netinet/sctp_cc_functions.c
@@ -49,13 +49,10 @@ sctp_set_initial_cc_param(struct sctp_tcb *stcb, struct sctp_nets *net)
{
/*
* We take the max of the burst limit times a MTU or the
- * INITIAL_CWND. We then limit this to 4 MTU's of sending.
+ * INITIAL_CWND. We then limit this to 4 MTU's of sending. cwnd must
+ * be at least 2 MTU.
*/
net->cwnd = min((net->mtu * 4), max((2 * net->mtu), SCTP_INITIAL_CWND));
- /* we always get at LEAST 2 MTU's */
- if (net->cwnd < (2 * net->mtu)) {
- net->cwnd = 2 * net->mtu;
- }
net->ssthresh = stcb->asoc.peers_rwnd;
if (sctp_logging_level & (SCTP_CWND_MONITOR_ENABLE | SCTP_CWND_LOGGING_ENABLE)) {
@@ -277,8 +274,7 @@ sctp_cwnd_update_after_sack(struct sctp_tcb *stcb,
/* If the cumulative ack moved we can proceed */
if (net->cwnd <= net->ssthresh) {
/* We are in slow start */
- if (net->flight_size + net->net_ack >=
- net->cwnd) {
+ if (net->flight_size + net->net_ack >= net->cwnd) {
if (net->net_ack > (net->mtu * sctp_L2_abc_variable)) {
net->cwnd += (net->mtu * sctp_L2_abc_variable);
if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
@@ -293,10 +289,6 @@ sctp_cwnd_update_after_sack(struct sctp_tcb *stcb,
}
}
} else {
- unsigned int dif;
-
- dif = net->cwnd - (net->flight_size +
- net->net_ack);
if (sctp_logging_level & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, net, net->net_ack,
SCTP_CWND_LOG_NOADV_SS);
@@ -307,8 +299,7 @@ sctp_cwnd_update_after_sack(struct sctp_tcb *stcb,
/*
* Add to pba
*/
- net->partial_bytes_acked +=
- net->net_ack;
+ net->partial_bytes_acked += net->net_ack;
if ((net->flight_size + net->net_ack >= net->cwnd) &&
(net->partial_bytes_acked >= net->cwnd)) {
@@ -352,23 +343,182 @@ skip_cwnd_update:
}
void
-sctp_cwnd_update_after_timeout(struct sctp_tcb *stcb,
- struct sctp_nets *net)
+sctp_cwnd_update_after_timeout(struct sctp_tcb *stcb, struct sctp_nets *net)
{
int old_cwnd = net->cwnd;
- net->ssthresh = net->cwnd >> 1;
- if (net->ssthresh < (net->mtu << 1)) {
- net->ssthresh = (net->mtu << 1);
- }
+ net->ssthresh = max(net->cwnd / 2, 2 * net->mtu);
net->cwnd = net->mtu;
- /* floor of 1 mtu */
- if (net->cwnd < net->mtu)
- net->cwnd = net->mtu;
+ net->partial_bytes_acked = 0;
+
if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
sctp_log_cwnd(stcb, net, net->cwnd - old_cwnd, SCTP_CWND_LOG_FROM_RTX);
}
- net->partial_bytes_acked = 0;
+}
+
+void
+sctp_cwnd_update_after_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+ int old_cwnd = net->cwnd;
+
+ SCTP_STAT_INCR(sctps_ecnereducedcwnd);
+ net->ssthresh = net->cwnd / 2;
+ if (net->ssthresh < net->mtu) {
+ net->ssthresh = net->mtu;
+ /* here back off the timer as well, to slow us down */
+ net->RTO <<= 1;
+ }
+ net->cwnd = net->ssthresh;
+ if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
+ sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_SAT);
+ }
+}
+
+void
+sctp_cwnd_update_after_packet_dropped(struct sctp_tcb *stcb,
+ struct sctp_nets *net, struct sctp_pktdrop_chunk *cp,
+ uint32_t * bottle_bw, uint32_t * on_queue)
+{
+ uint32_t bw_avail;
+ int rtt, incr;
+ int old_cwnd = net->cwnd;
+
+ /* need real RTT for this calc */
+ rtt = ((net->lastsa >> 2) + net->lastsv) >> 1;
+ /* get bottle neck bw */
+ *bottle_bw = ntohl(cp->bottle_bw);
+ /* and whats on queue */
+ *on_queue = ntohl(cp->current_onq);
+ /*
+ * adjust the on-queue if our flight is more it could be that the
+ * router has not yet gotten data "in-flight" to it
+ */
+ if (*on_queue < net->flight_size)
+ *on_queue = net->flight_size;
+ /* calculate the available space */
+ bw_avail = (*bottle_bw * rtt) / 1000;
+ if (bw_avail > *bottle_bw) {
+ /*
+ * Cap the growth to no more than the bottle neck. This can
+ * happen as RTT slides up due to queues. It also means if
+ * you have more than a 1 second RTT with a empty queue you
+ * will be limited to the bottle_bw per second no matter if
+ * other points have 1/2 the RTT and you could get more
+ * out...
+ */
+ bw_avail = *bottle_bw;
+ }
+ if (*on_queue > bw_avail) {
+ /*
+ * No room for anything else don't allow anything else to be
+ * "added to the fire".
+ */
+ int seg_inflight, seg_onqueue, my_portion;
+
+ net->partial_bytes_acked = 0;
+
+ /* how much are we over queue size? */
+ incr = *on_queue - bw_avail;
+ if (stcb->asoc.seen_a_sack_this_pkt) {
+ /*
+ * undo any cwnd adjustment that the sack might have
+ * made
+ */
+ net->cwnd = net->prev_cwnd;
+ }
+ /* Now how much of that is mine? */
+ seg_inflight = net->flight_size / net->mtu;
+ seg_onqueue = *on_queue / net->mtu;
+ my_portion = (incr * seg_inflight) / seg_onqueue;
+
+ /* Have I made an adjustment already */
+ if (net->cwnd > net->flight_size) {
+ /*
+ * for this flight I made an adjustment we need to
+ * decrease the portion by a share our previous
+ * adjustment.
+ */
+ int diff_adj;
+
+ diff_adj = net->cwnd - net->flight_size;
+ if (diff_adj > my_portion)
+ my_portion = 0;
+ else
+ my_portion -= diff_adj;
+ }
+ /*
+ * back down to the previous cwnd (assume we have had a sack
+ * before this packet). minus what ever portion of the
+ * overage is my fault.
+ */
+ net->cwnd -= my_portion;
+
+ /* we will NOT back down more than 1 MTU */
+ if (net->cwnd <= net->mtu) {
+ net->cwnd = net->mtu;
+ }
+ /* force into CA */
+ net->ssthresh = net->cwnd - 1;
+ } else {
+ /*
+ * Take 1/4 of the space left or max burst up .. whichever
+ * is less.
+ */
+ incr = min((bw_avail - *on_queue) >> 2,
+ stcb->asoc.max_burst * net->mtu);
+ net->cwnd += incr;
+ }
+ if (net->cwnd > bw_avail) {
+ /* We can't exceed the pipe size */
+ net->cwnd = bw_avail;
+ }
+ if (net->cwnd < net->mtu) {
+ /* We always have 1 MTU */
+ net->cwnd = net->mtu;
+ }
+ if (net->cwnd - old_cwnd != 0) {
+ /* log only changes */
+ if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
+ sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd),
+ SCTP_CWND_LOG_FROM_SAT);
+ }
+ }
+}
+
+void
+sctp_cwnd_update_after_output(struct sctp_tcb *stcb,
+ struct sctp_nets *net, int burst_limit)
+{
+ int old_cwnd = net->cwnd;
+
+ if (net->ssthresh < net->cwnd)
+ net->ssthresh = net->cwnd;
+ net->cwnd = (net->flight_size + (burst_limit * net->mtu));
+
+ if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
+ sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_BRST);
+ }
+}
+
+void
+sctp_cwnd_update_after_fr_timer(struct sctp_inpcb *inp,
+ struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+ int old_cwnd = net->cwnd;
+
+ sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_EARLY_FR_TMR, SCTP_SO_NOT_LOCKED);
+ /*
+ * make a small adjustment to cwnd and force to CA.
+ */
+ if (net->cwnd > net->mtu)
+ /* drop down one MTU after sending */
+ net->cwnd -= net->mtu;
+ if (net->cwnd < net->ssthresh)
+ /* still in SS move to CA */
+ net->ssthresh = net->cwnd - 1;
+ if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
+ sctp_log_cwnd(stcb, net, (old_cwnd - net->cwnd), SCTP_CWND_LOG_FROM_FR);
+ }
}
struct sctp_hs_raise_drop {
@@ -741,16 +891,11 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb,
/* If the cumulative ack moved we can proceed */
if (net->cwnd <= net->ssthresh) {
/* We are in slow start */
- if (net->flight_size + net->net_ack >=
- net->cwnd) {
+ if (net->flight_size + net->net_ack >= net->cwnd) {
sctp_hs_cwnd_increase(stcb, net);
} else {
- unsigned int dif;
-
- dif = net->cwnd - (net->flight_size +
- net->net_ack);
if (sctp_logging_level & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, net, net->net_ack,
SCTP_CWND_LOG_NOADV_SS);
@@ -758,50 +903,20 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb,
}
} else {
/* We are in congestion avoidance */
- if (net->flight_size + net->net_ack >=
- net->cwnd) {
- /*
- * add to pba only if we had a
- * cwnd's worth (or so) in flight OR
- * the burst limit was applied.
- */
- net->partial_bytes_acked +=
- net->net_ack;
-
- /*
- * Do we need to increase (if pba is
- * > cwnd)?
- */
- if (net->partial_bytes_acked >=
- net->cwnd) {
- if (net->cwnd <
- net->partial_bytes_acked) {
- net->partial_bytes_acked -=
- net->cwnd;
- } else {
- net->partial_bytes_acked =
- 0;
- }
- net->cwnd += net->mtu;
- if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
- sctp_log_cwnd(stcb, net, net->mtu,
- SCTP_CWND_LOG_FROM_CA);
- }
- } else {
- if (sctp_logging_level & SCTP_CWND_LOGGING_ENABLE) {
- sctp_log_cwnd(stcb, net, net->net_ack,
- SCTP_CWND_LOG_NOADV_CA);
- }
+ net->partial_bytes_acked += net->net_ack;
+ if ((net->flight_size + net->net_ack >= net->cwnd) &&
+ (net->partial_bytes_acked >= net->cwnd)) {
+ net->partial_bytes_acked -= net->cwnd;
+ net->cwnd += net->mtu;
+ if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
+ sctp_log_cwnd(stcb, net, net->mtu,
+ SCTP_CWND_LOG_FROM_CA);
}
} else {
- unsigned int dif;
-
if (sctp_logging_level & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, net, net->net_ack,
SCTP_CWND_LOG_NOADV_CA);
}
- dif = net->cwnd - (net->flight_size +
- net->net_ack);
}
}
} else {
@@ -830,176 +945,6 @@ skip_cwnd_update:
}
}
-void
-sctp_cwnd_update_after_ecn_echo(struct sctp_tcb *stcb,
- struct sctp_nets *net)
-{
- int old_cwnd;
-
- old_cwnd = net->cwnd;
-
- SCTP_STAT_INCR(sctps_ecnereducedcwnd);
- net->ssthresh = net->cwnd / 2;
- if (net->ssthresh < net->mtu) {
- net->ssthresh = net->mtu;
- /* here back off the timer as well, to slow us down */
- net->RTO <<= 1;
- }
- net->cwnd = net->ssthresh;
- if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
- sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_SAT);
- }
-}
-
-void
-sctp_cwnd_update_after_packet_dropped(struct sctp_tcb *stcb,
- struct sctp_nets *net, struct sctp_pktdrop_chunk *cp,
- uint32_t * bottle_bw, uint32_t * on_queue)
-{
- uint32_t bw_avail;
- int rtt, incr;
- int old_cwnd = net->cwnd;
-
- /* need real RTT for this calc */
- rtt = ((net->lastsa >> 2) + net->lastsv) >> 1;
- /* get bottle neck bw */
- *bottle_bw = ntohl(cp->bottle_bw);
- /* and whats on queue */
- *on_queue = ntohl(cp->current_onq);
- /*
- * adjust the on-queue if our flight is more it could be that the
- * router has not yet gotten data "in-flight" to it
- */
- if (*on_queue < net->flight_size)
- *on_queue = net->flight_size;
- /* calculate the available space */
- bw_avail = (*bottle_bw * rtt) / 1000;
- if (bw_avail > *bottle_bw) {
- /*
- * Cap the growth to no more than the bottle neck. This can
- * happen as RTT slides up due to queues. It also means if
- * you have more than a 1 second RTT with a empty queue you
- * will be limited to the bottle_bw per second no matter if
- * other points have 1/2 the RTT and you could get more
- * out...
- */
- bw_avail = *bottle_bw;
- }
- if (*on_queue > bw_avail) {
- /*
- * No room for anything else don't allow anything else to be
- * "added to the fire".
- */
- int seg_inflight, seg_onqueue, my_portion;
-
- net->partial_bytes_acked = 0;
-
- /* how much are we over queue size? */
- incr = *on_queue - bw_avail;
- if (stcb->asoc.seen_a_sack_this_pkt) {
- /*
- * undo any cwnd adjustment that the sack might have
- * made
- */
- net->cwnd = net->prev_cwnd;
- }
- /* Now how much of that is mine? */
- seg_inflight = net->flight_size / net->mtu;
- seg_onqueue = *on_queue / net->mtu;
- my_portion = (incr * seg_inflight) / seg_onqueue;
-
- /* Have I made an adjustment already */
- if (net->cwnd > net->flight_size) {
- /*
- * for this flight I made an adjustment we need to
- * decrease the portion by a share our previous
- * adjustment.
- */
- int diff_adj;
-
- diff_adj = net->cwnd - net->flight_size;
- if (diff_adj > my_portion)
- my_portion = 0;
- else
- my_portion -= diff_adj;
- }
- /*
- * back down to the previous cwnd (assume we have had a sack
- * before this packet). minus what ever portion of the
- * overage is my fault.
- */
- net->cwnd -= my_portion;
-
- /* we will NOT back down more than 1 MTU */
- if (net->cwnd <= net->mtu) {
- net->cwnd = net->mtu;
- }
- /* force into CA */
- net->ssthresh = net->cwnd - 1;
- } else {
- /*
- * Take 1/4 of the space left or max burst up .. whichever
- * is less.
- */
- incr = min((bw_avail - *on_queue) >> 2,
- stcb->asoc.max_burst * net->mtu);
- net->cwnd += incr;
- }
- if (net->cwnd > bw_avail) {
- /* We can't exceed the pipe size */
- net->cwnd = bw_avail;
- }
- if (net->cwnd < net->mtu) {
- /* We always have 1 MTU */
- net->cwnd = net->mtu;
- }
- if (net->cwnd - old_cwnd != 0) {
- /* log only changes */
- if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
- sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd),
- SCTP_CWND_LOG_FROM_SAT);
- }
- }
-}
-
-void
-sctp_cwnd_update_after_output(struct sctp_tcb *stcb,
- struct sctp_nets *net, int burst_limit)
-{
- int old_cwnd;
-
- if (net->ssthresh < net->cwnd)
- net->ssthresh = net->cwnd;
- old_cwnd = net->cwnd;
- net->cwnd = (net->flight_size + (burst_limit * net->mtu));
-
- if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
- sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_BRST);
- }
-}
-
-void
-sctp_cwnd_update_after_fr_timer(struct sctp_inpcb *inp,
- struct sctp_tcb *stcb, struct sctp_nets *net)
-{
- int old_cwnd;
-
- old_cwnd = net->cwnd;
-
- sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_EARLY_FR_TMR, SCTP_SO_NOT_LOCKED);
- /*
- * make a small adjustment to cwnd and force to CA.
- */
- if (net->cwnd > net->mtu)
- /* drop down one MTU after sending */
- net->cwnd -= net->mtu;
- if (net->cwnd < net->ssthresh)
- /* still in SS move to CA */
- net->ssthresh = net->cwnd - 1;
- if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
- sctp_log_cwnd(stcb, net, (old_cwnd - net->cwnd), SCTP_CWND_LOG_FROM_FR);
- }
-}
/*
* H-TCP congestion control. The algorithm is detailed in:
@@ -1220,10 +1165,6 @@ htcp_cong_avoid(struct sctp_tcb *stcb, struct sctp_nets *net)
}
}
} else {
- unsigned int dif;
-
- dif = net->cwnd - (net->flight_size +
- net->net_ack);
if (sctp_logging_level & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, net, net->net_ack,
SCTP_CWND_LOG_NOADV_SS);
@@ -1289,10 +1230,6 @@ sctp_htcp_set_initial_cc_param(struct sctp_tcb *stcb, struct sctp_nets *net)
* INITIAL_CWND. We then limit this to 4 MTU's of sending.
*/
net->cwnd = min((net->mtu * 4), max((2 * net->mtu), SCTP_INITIAL_CWND));
- /* we always get at LEAST 2 MTU's */
- if (net->cwnd < (2 * net->mtu)) {
- net->cwnd = 2 * net->mtu;
- }
net->ssthresh = stcb->asoc.peers_rwnd;
htcp_init(stcb, net);
@@ -1549,13 +1486,10 @@ sctp_htcp_cwnd_update_after_timeout(struct sctp_tcb *stcb,
htcp_reset(&net->htcp_ca);
net->ssthresh = htcp_recalc_ssthresh(stcb, net);
net->cwnd = net->mtu;
- /* floor of 1 mtu */
- if (net->cwnd < net->mtu)
- net->cwnd = net->mtu;
+ net->partial_bytes_acked = 0;
if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
sctp_log_cwnd(stcb, net, net->cwnd - old_cwnd, SCTP_CWND_LOG_FROM_RTX);
}
- net->partial_bytes_acked = 0;
}
void
diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h
index 268ff68..9f628b8 100644
--- a/sys/netinet/sctp_constants.h
+++ b/sys/netinet/sctp_constants.h
@@ -36,6 +36,9 @@ __FBSDID("$FreeBSD$");
#ifndef __sctp_constants_h__
#define __sctp_constants_h__
+/* IANA assigned port number for SCTP over UDP encapsulation */
+#define SCTP_OVER_UDP_TUNNELING_PORT 9899
+
/* Number of packets to get before sack sent by default */
#define SCTP_DEFAULT_SACK_FREQ 2
@@ -268,6 +271,9 @@ __FBSDID("$FreeBSD$");
#define SCTP_DEFAULT_AUTO_ASCONF 1
#endif
+/* default MULTIPLE_ASCONF mode enable(1)/disable(0) value (sysctl) */
+#define SCTP_DEFAULT_MULTIPLE_ASCONFS 0
+
/* default MOBILITY_BASE mode enable(1)/disable(0) value (sysctl) */
#if defined (__APPLE__) && !defined(SCTP_APPLE_MOBILITY_BASE)
#define SCTP_DEFAULT_MOBILITY_BASE 0
diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c
index bb74b7b..424d54c 100644
--- a/sys/netinet/sctp_indata.c
+++ b/sys/netinet/sctp_indata.c
@@ -2620,7 +2620,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
}
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_19;
sctp_abort_association(inp, stcb, m, iphlen, sh,
- op_err, 0);
+ op_err, 0, net->port);
return (2);
}
#ifdef SCTP_AUDITING_ENABLED
@@ -2683,7 +2683,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
struct mbuf *op_err;
op_err = sctp_generate_invmanparam(SCTP_CAUSE_PROTOCOL_VIOLATION);
- sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, 0);
+ sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, 0, net->port);
return (2);
}
break;
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index 4c515f6..694d8ca 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_asconf.h>
#include <netinet/sctp_bsd_addr.h>
#include <netinet/sctp_timer.h>
+#include <netinet/udp.h>
@@ -79,7 +80,7 @@ sctp_stop_all_cookie_timers(struct sctp_tcb *stcb)
static void
sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
struct sctp_init_chunk *cp, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
- struct sctp_nets *net, int *abort_no_unlock, uint32_t vrf_id)
+ struct sctp_nets *net, int *abort_no_unlock, uint32_t vrf_id, uint16_t port)
{
struct sctp_init *init;
struct mbuf *op_err;
@@ -113,7 +114,7 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
* state :-)
*/
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
- vrf_id);
+ vrf_id, port);
if (stcb)
*abort_no_unlock = 1;
goto outnow;
@@ -122,7 +123,7 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
/* Invalid length */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
- vrf_id);
+ vrf_id, port);
if (stcb)
*abort_no_unlock = 1;
goto outnow;
@@ -132,7 +133,7 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
/* protocol error... send abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
- vrf_id);
+ vrf_id, port);
if (stcb)
*abort_no_unlock = 1;
goto outnow;
@@ -141,7 +142,7 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
/* invalid parameter... send abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
- vrf_id);
+ vrf_id, port);
if (stcb)
*abort_no_unlock = 1;
goto outnow;
@@ -150,7 +151,7 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
/* protocol error... send abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
- vrf_id);
+ vrf_id, port);
if (stcb)
*abort_no_unlock = 1;
goto outnow;
@@ -159,7 +160,7 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
/* protocol error... send abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
- vrf_id);
+ vrf_id, port);
if (stcb)
*abort_no_unlock = 1;
goto outnow;
@@ -168,14 +169,14 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
if (sctp_validate_init_auth_params(m, offset + sizeof(*cp),
init_limit)) {
/* auth parameter(s) error... send abort */
- sctp_abort_association(inp, stcb, m, iphlen, sh, NULL, vrf_id);
+ sctp_abort_association(inp, stcb, m, iphlen, sh, NULL, vrf_id, port);
if (stcb)
*abort_no_unlock = 1;
goto outnow;
}
/* send an INIT-ACK w/cookie */
SCTPDBG(SCTP_DEBUG_INPUT3, "sctp_handle_init: sending INIT-ACK\n");
- sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, sh, cp, vrf_id,
+ sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, sh, cp, vrf_id, port,
((stcb == NULL) ? SCTP_HOLDS_LOCK : SCTP_NOT_LOCKED));
outnow:
if (stcb == NULL) {
@@ -422,7 +423,7 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset,
"Load addresses from INIT causes an abort %d\n",
retval);
sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
- NULL, 0);
+ NULL, 0, net->port);
*abort_no_unlock = 1;
return (-1);
}
@@ -497,7 +498,7 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset,
mp->resv = 0;
}
sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen,
- sh, op_err, 0);
+ sh, op_err, 0, net->port);
*abort_no_unlock = 1;
}
return (retval);
@@ -1105,7 +1106,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset,
/* Invalid length */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
- op_err, 0);
+ op_err, 0, net->port);
*abort_no_unlock = 1;
return (-1);
}
@@ -1115,7 +1116,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset,
/* protocol error... send an abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
- op_err, 0);
+ op_err, 0, net->port);
*abort_no_unlock = 1;
return (-1);
}
@@ -1123,7 +1124,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset,
/* protocol error... send an abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
- op_err, 0);
+ op_err, 0, net->port);
*abort_no_unlock = 1;
return (-1);
}
@@ -1131,7 +1132,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset,
/* protocol error... send an abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
- op_err, 0);
+ op_err, 0, net->port);
*abort_no_unlock = 1;
return (-1);
}
@@ -1139,7 +1140,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset,
/* protocol error... send an abort */
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
- op_err, 0);
+ op_err, 0, net->port);
*abort_no_unlock = 1;
return (-1);
}
@@ -1269,7 +1270,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
ph->param_type = htons(SCTP_CAUSE_COOKIE_IN_SHUTDOWN);
ph->param_length = htons(sizeof(struct sctp_paramhdr));
sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag,
- vrf_id);
+ vrf_id, net->port);
if (how_indx < sizeof(asoc->cookie_how))
asoc->cookie_how[how_indx] = 2;
return (NULL);
@@ -1646,6 +1647,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
ntohs(initack_cp->init.num_outbound_streams);
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;
@@ -1749,7 +1751,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
struct sctp_inpcb *inp, struct sctp_nets **netp,
struct sockaddr *init_src, int *notification,
int auth_skipped, uint32_t auth_offset, uint32_t auth_len,
- uint32_t vrf_id)
+ uint32_t vrf_id, uint16_t port)
{
struct sctp_tcb *stcb;
struct sctp_init_chunk *init_cp, init_buf;
@@ -1841,7 +1843,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen,
- sh, op_err, vrf_id);
+ sh, op_err, vrf_id, port);
return (NULL);
}
/* get the correct sctp_nets */
@@ -1867,7 +1869,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
atomic_add_int(&stcb->asoc.refcnt, 1);
op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen,
- sh, op_err, vrf_id);
+ sh, op_err, vrf_id, port);
#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
SCTP_TCB_UNLOCK(stcb);
SCTP_SOCKET_LOCK(so, 1);
@@ -1888,6 +1890,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams);
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;
@@ -2095,7 +2098,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
struct sctphdr *sh, struct sctp_cookie_echo_chunk *cp,
struct sctp_inpcb **inp_p, struct sctp_tcb **stcb, struct sctp_nets **netp,
int auth_skipped, uint32_t auth_offset, uint32_t auth_len,
- struct sctp_tcb **locked_tcb, uint32_t vrf_id)
+ struct sctp_tcb **locked_tcb, uint32_t vrf_id, uint16_t port)
{
struct sctp_state_cookie *cookie;
struct sockaddr_in6 sin6;
@@ -2329,7 +2332,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
tim = now.tv_usec - cookie->time_entered.tv_usec;
scm->time_usec = htonl(tim);
sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag,
- vrf_id);
+ vrf_id, port);
return (NULL);
}
/*
@@ -2409,7 +2412,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
/* this is the "normal" case... get a new TCB */
*stcb = sctp_process_cookie_new(m, iphlen, offset, sh, cookie,
cookie_len, *inp_p, netp, to, &notification,
- auth_skipped, auth_offset, auth_len, vrf_id);
+ auth_skipped, auth_offset, auth_len, vrf_id, port);
} else {
/* this is abnormal... cookie-echo on existing TCB */
had_a_existing_tcb = 1;
@@ -2489,7 +2492,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
SCTPDBG(SCTP_DEBUG_INPUT1, "process_cookie_new: no room for another socket!\n");
op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
sctp_abort_association(*inp_p, NULL, m, iphlen,
- sh, op_err, vrf_id);
+ sh, op_err, vrf_id, port);
#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
pcb_so = SCTP_INP_SO(*inp_p);
atomic_add_int(&(*stcb)->asoc.refcnt, 1);
@@ -3814,7 +3817,7 @@ __attribute__((noinline))
sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
struct sctphdr *sh, struct sctp_chunkhdr *ch, struct sctp_inpcb *inp,
struct sctp_tcb *stcb, struct sctp_nets **netp, int *fwd_tsn_seen,
- uint32_t vrf_id)
+ uint32_t vrf_id, uint16_t port)
{
struct sctp_association *asoc;
uint32_t vtag_in;
@@ -3968,7 +3971,7 @@ __attribute__((noinline))
if (stcb == NULL) {
/* no association, so it's out of the blue... */
sctp_handle_ootb(m, iphlen, *offset, sh, inp, NULL,
- vrf_id);
+ vrf_id, port);
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
@@ -4005,7 +4008,7 @@ __attribute__((noinline))
SCTP_TCB_UNLOCK(locked_tcb);
}
sctp_handle_ootb(m, iphlen, *offset, sh, inp,
- NULL, vrf_id);
+ NULL, vrf_id, port);
return (NULL);
}
} else {
@@ -4182,7 +4185,7 @@ process_control_chunks:
if (netp) {
sctp_handle_init(m, iphlen, *offset, sh,
(struct sctp_init_chunk *)ch, inp,
- stcb, *netp, &abort_no_unlock, vrf_id);
+ stcb, *netp, &abort_no_unlock, vrf_id, port);
}
if (abort_no_unlock)
return (NULL);
@@ -4448,7 +4451,7 @@ process_control_chunks:
htons(sizeof(struct sctp_paramhdr));
}
sctp_abort_association(inp, stcb, m,
- iphlen, sh, oper, vrf_id);
+ iphlen, sh, oper, vrf_id, port);
}
*offset = length;
return (NULL);
@@ -4475,7 +4478,8 @@ process_control_chunks:
auth_offset,
auth_len,
&locked_tcb,
- vrf_id);
+ vrf_id,
+ port);
} else {
ret_buf = NULL;
}
@@ -4984,7 +4988,7 @@ void
sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
int length, struct sctphdr *sh, struct sctp_chunkhdr *ch,
struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net,
- uint8_t ecn_bits, uint32_t vrf_id)
+ uint8_t ecn_bits, uint32_t vrf_id, uint16_t port)
{
/*
* Control chunk processing
@@ -5020,7 +5024,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
*/
SCTP_TCB_UNLOCK(stcb);
sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL,
- vrf_id);
+ vrf_id, port);
goto out_now;
}
}
@@ -5028,7 +5032,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
/* process the control portion of the SCTP packet */
/* sa_ignore NO_NULL_CHK */
stcb = sctp_process_control(m, iphlen, &offset, length, sh, ch,
- inp, stcb, &net, &fwd_tsn_seen, vrf_id);
+ inp, stcb, &net, &fwd_tsn_seen, vrf_id, port);
if (stcb) {
/*
* This covers us if the cookie-echo was there and
@@ -5058,7 +5062,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
if (stcb == NULL) {
/* out of the blue DATA chunk */
sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL,
- vrf_id);
+ vrf_id, port);
goto out_now;
}
if (stcb->asoc.my_vtag != ntohl(sh->v_tag)) {
@@ -5126,7 +5130,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
* We consider OOTB any data sent during asoc setup.
*/
sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL,
- vrf_id);
+ vrf_id, port);
SCTP_TCB_UNLOCK(stcb);
goto out_now;
/* sa_ignore NOTREACHED */
@@ -5221,12 +5225,11 @@ out_now:
}
-
void
-sctp_input(i_pak, off)
+sctp_input_with_port(i_pak, off, port)
struct mbuf *i_pak;
int off;
-
+ uint16_t port;
{
#ifdef SCTP_MBUF_LOGGING
struct mbuf *mat;
@@ -5329,6 +5332,12 @@ sctp_input(i_pak, off)
offset - sizeof(*ch),
sh, ch, &inp, &net,
vrf_id);
+ if ((net) && (port)) {
+ if (net->port == 0) {
+ sctp_pathmtu_adjustment(inp, stcb, net, net->mtu - sizeof(struct udphdr));
+ }
+ net->port = port;
+ }
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);
@@ -5357,6 +5366,12 @@ sctp_skip_csum_4:
*/
stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch),
sh, ch, &inp, &net, vrf_id);
+ if ((net) && (port)) {
+ if (net->port == 0) {
+ sctp_pathmtu_adjustment(inp, stcb, net, net->mtu - sizeof(struct udphdr));
+ }
+ net->port = port;
+ }
/* inp's ref-count increased && stcb locked */
if (inp == NULL) {
struct sctp_init_chunk *init_chk, chunk_buf;
@@ -5386,14 +5401,14 @@ sctp_skip_csum_4:
sh->v_tag = init_chk->init.initiate_tag;
}
if (ch->chunk_type == SCTP_SHUTDOWN_ACK) {
- sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id);
+ sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, port);
goto bad;
}
if (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) {
goto bad;
}
if (ch->chunk_type != SCTP_ABORT_ASSOCIATION)
- sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id);
+ sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id, port);
goto bad;
} else if (stcb == NULL) {
refcount_up = 1;
@@ -5420,7 +5435,7 @@ sctp_skip_csum_4:
/* sa_ignore NO_NULL_CHK */
sctp_common_input_processing(&m, iphlen, offset, length, sh, ch,
- inp, stcb, net, ecn_bits, vrf_id);
+ inp, stcb, net, ecn_bits, vrf_id, port);
/* inp's ref-count reduced && stcb unlocked */
if (m) {
sctp_m_freem(m);
@@ -5443,3 +5458,10 @@ bad:
}
return;
}
+void
+sctp_input(i_pak, off)
+ struct mbuf *i_pak;
+ int off;
+{
+ sctp_input_with_port(i_pak, off, 0);
+}
diff --git a/sys/netinet/sctp_input.h b/sys/netinet/sctp_input.h
index 01a67c6..2a28970 100644
--- a/sys/netinet/sctp_input.h
+++ b/sys/netinet/sctp_input.h
@@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$");
void
sctp_common_input_processing(struct mbuf **, int, int, int,
struct sctphdr *, struct sctp_chunkhdr *, struct sctp_inpcb *,
- struct sctp_tcb *, struct sctp_nets *, uint8_t, uint32_t);
+ struct sctp_tcb *, struct sctp_nets *, uint8_t, uint32_t, uint16_t);
struct sctp_stream_reset_out_request *
sctp_find_stream_reset(struct sctp_tcb *stcb, uint32_t seq,
diff --git a/sys/netinet/sctp_lock_bsd.h b/sys/netinet/sctp_lock_bsd.h
index eeaca5d..95f3ed4 100644
--- a/sys/netinet/sctp_lock_bsd.h
+++ b/sys/netinet/sctp_lock_bsd.h
@@ -83,10 +83,10 @@ extern int sctp_logoff_stuff;
#define SCTP_STATLOG_DESTROY()
#define SCTP_INP_INFO_LOCK_DESTROY() do { \
- if(rw_wowned(sctppcbinfo.ipi_ep_mtx)) { \
+ if(rw_wowned(&sctppcbinfo.ipi_ep_mtx)) { \
rw_wunlock(&sctppcbinfo.ipi_ep_mtx); \
} \
- rw_destroy(sctppcbinfo.ipi_ep_mtx); \
+ rw_destroy(&sctppcbinfo.ipi_ep_mtx); \
} while (0)
#define SCTP_INP_INFO_LOCK_INIT() \
@@ -111,10 +111,10 @@ extern int sctp_logoff_stuff;
rw_init(&sctppcbinfo.ipi_addr_mtx, "sctp-addr")
#define SCTP_IPI_ADDR_DESTROY() do { \
- if(rw_wowned(sctppcbinfo.ipi_addr_mtx)) { \
+ if(rw_wowned(&sctppcbinfo.ipi_addr_mtx)) { \
rw_wunlock(&sctppcbinfo.ipi_addr_mtx); \
} \
- rw_destroy(&sctppcbinfo.ipi_addr_mtx) \
+ rw_destroy(&sctppcbinfo.ipi_addr_mtx); \
} while (0)
diff --git a/sys/netinet/sctp_os_bsd.h b/sys/netinet/sctp_os_bsd.h
index 01c0fcb..2d01952 100644
--- a/sys/netinet/sctp_os_bsd.h
+++ b/sys/netinet/sctp_os_bsd.h
@@ -202,6 +202,7 @@ MALLOC_DECLARE(SCTP_M_SOCKOPT);
#define SCTP_INIT_VRF_TABLEID(vrf)
#define SCTP_IFN_IS_IFT_LOOP(ifn) ((ifn)->ifn_type == IFT_LOOP)
+#define SCTP_ROUTE_IS_REAL_LOOP(ro) ((ro)->ro_rt && (ro)->ro_rt->rt_ifa && (ro)->ro_rt->rt_ifa->ifa_ifp && (ro)->ro_rt->rt_ifa->ifa_ifp->if_type == IFT_LOOP)
/*
* Access to IFN's to help with src-addr-selection
@@ -234,6 +235,7 @@ MALLOC_DECLARE(SCTP_M_SOCKOPT);
* zone allocation functions
*/
#include <vm/uma.h>
+
/* SCTP_ZONE_INIT: initialize the zone */
typedef struct uma_zone *sctp_zone_t;
@@ -244,6 +246,8 @@ typedef struct uma_zone *sctp_zone_t;
uma_zone_set_max(zone, number); \
}
+#define SCTP_ZONE_DESTROY(zone) uma_zdestroy(zone)
+
/* SCTP_ZONE_GET: allocate element from the zone */
#define SCTP_ZONE_GET(zone, type) \
(type *)uma_zalloc(zone, M_NOWAIT);
@@ -251,6 +255,7 @@ typedef struct uma_zone *sctp_zone_t;
/* SCTP_ZONE_FREE: free element from the zone */
#define SCTP_ZONE_FREE(zone, element) \
uma_zfree(zone, element);
+
#define SCTP_HASH_INIT(size, hashmark) hashinit_flags(size, M_PCB, hashmark, HASH_NOWAIT)
#define SCTP_HASH_FREE(table, hashmark) hashdestroy(table, M_PCB, hashmark)
@@ -262,6 +267,7 @@ typedef struct uma_zone *sctp_zone_t;
#include <sys/callout.h>
typedef struct callout sctp_os_timer_t;
+
#define SCTP_OS_TIMER_INIT(tmr) callout_init(tmr, 1)
#define SCTP_OS_TIMER_START callout_reset
#define SCTP_OS_TIMER_STOP callout_stop
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 8fa1846..3272ab8 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_indata.h>
#include <netinet/sctp_bsd_addr.h>
#include <netinet/sctp_input.h>
+#include <netinet/udp.h>
@@ -2211,12 +2212,21 @@ sctp_is_ifa_addr_preferred(struct sctp_ifa *ifa,
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &ifa->address.sa);
/* Ok the address may be ok */
if (fam == AF_INET6) {
- /* ok to use deprecated addresses? */
+ /* ok to use deprecated addresses? no lets not! */
if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:1\n");
return (NULL);
}
- if (ifa->src_is_priv) {
+ if (ifa->src_is_priv && ifa->src_is_loop) {
+ /*
+ * don't allow fe80::1 to be a src on loop ::1, we
+ * don't list it to the peer so we will get an
+ * abort.
+ */
+ SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:2a\n");
+ return (NULL);
+ }
+ if (ifa->src_is_priv && !ifa->src_is_loop) {
if (dest_is_loop) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:2\n");
return (NULL);
@@ -2532,8 +2542,11 @@ sctp_choose_boundspecific_stcb(struct sctp_inpcb *inp,
sifa = sctp_is_ifa_addr_preferred(sctp_ifa, dest_is_loop, dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2549,8 +2562,11 @@ sctp_choose_boundspecific_stcb(struct sctp_inpcb *inp,
sifa = sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop, dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2584,8 +2600,11 @@ sctp_from_the_top:
sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop, dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2620,8 +2639,11 @@ sctp_from_the_top2:
dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2650,6 +2672,14 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn,
struct sctp_ifa *ifa, *sifa;
int num_eligible_addr = 0;
+#ifdef INET6
+ struct sockaddr_in6 sin6, lsa6;
+
+ if (fam == AF_INET6) {
+ memcpy(&sin6, &ro->ro_dst, sizeof(struct sockaddr_in6));
+ (void)sa6_recoverscope(&sin6);
+ }
+#endif /* INET6 */
LIST_FOREACH(ifa, &ifn->ifalist, next_ifa) {
if ((ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
(non_asoc_addr_ok == 0))
@@ -2658,6 +2688,29 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn,
dest_is_priv, fam);
if (sifa == NULL)
continue;
+#ifdef INET6
+ if (fam == AF_INET6 &&
+ IN6_IS_ADDR_LINKLOCAL(&sifa->address.sin6.sin6_addr) &&
+ IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
+ /*
+ * link-local <-> link-local must belong to the same
+ * scope.
+ */
+ memcpy(&lsa6, &sifa->address.sin6, sizeof(struct sockaddr_in6));
+ (void)sa6_recoverscope(&lsa6);
+ if (sin6.sin6_scope_id != lsa6.sin6_scope_id) {
+ continue;
+ }
+ if (dest_is_loop) {
+ /*
+ * we don't give out fe80::1, we must use
+ * ::1
+ */
+ continue;
+ }
+ }
+#endif /* INET6 */
+
/*
* Check if the IPv6 address matches to next-hop. In the
* mobile case, old IPv6 address may be not deleted from the
@@ -2682,8 +2735,11 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn,
}
}
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some reason..
* probably not yet added.
@@ -2722,8 +2778,11 @@ sctp_count_num_preferred_boundall(struct sctp_ifn *ifn,
continue;
}
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some reason..
* probably not yet added.
@@ -2899,8 +2958,11 @@ bound_all_plan_b:
if (sifa == NULL)
continue;
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some reason..
* probably not yet added.
@@ -2938,8 +3000,11 @@ plan_d:
if (sifa == NULL)
continue;
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some
* reason.. probably not yet added.
@@ -3056,25 +3121,26 @@ sctp_source_address_selection(struct sctp_inpcb *inp,
switch (fam) {
case AF_INET:
/* Scope based on outbound address */
- if ((IN4_ISPRIVATE_ADDRESS(&to->sin_addr))) {
- dest_is_priv = 1;
- } else if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) {
+ if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) {
dest_is_loop = 1;
if (net != NULL) {
/* mark it as local */
net->addr_is_local = 1;
}
+ } else if ((IN4_ISPRIVATE_ADDRESS(&to->sin_addr))) {
+ dest_is_priv = 1;
}
break;
#ifdef INET6
case AF_INET6:
/* Scope based on outbound address */
- if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr)) {
+ if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr) ||
+ SCTP_ROUTE_IS_REAL_LOOP(ro)) {
/*
- * If the route goes to the loopback address OR the
- * address is a loopback address, we are loopback
- * scope. But we don't use dest_is_priv (link local
- * addresses).
+ * If the address is a loopback address, which
+ * consists of "::1" OR "fe80::1%lo0", we are
+ * loopback scope. But we don't use dest_is_priv
+ * (link local addresses).
*/
dest_is_loop = 1;
if (net != NULL) {
@@ -3345,6 +3411,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
int ecn_ok,
struct sctp_tmit_chunk *chk,
int out_of_asoc_ok,
+ uint16_t port,
int so_locked
#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
SCTP_UNUSED
@@ -3371,6 +3438,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
int ret;
uint32_t vrf_id;
sctp_route_t *ro = NULL;
+ struct udphdr *udp;
if ((net) && (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)) {
SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EFAULT);
@@ -3411,15 +3479,25 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
sctp_route_t iproute;
uint8_t tos_value;
- newm = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ }
if (newm == NULL) {
sctp_m_freem(m);
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
- SCTP_ALIGN_TO_END(newm, sizeof(struct ip));
- SCTP_BUF_LEN(newm) = sizeof(struct ip);
- packet_length += sizeof(struct ip);
+ if (port) {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip) + sizeof(struct udphdr));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip) + sizeof(struct udphdr);
+ packet_length += sizeof(struct ip) + sizeof(struct udphdr);
+ } else {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip);
+ packet_length += sizeof(struct ip);
+ }
SCTP_BUF_NEXT(newm) = m;
m = newm;
ip = mtod(m, struct ip *);
@@ -3430,7 +3508,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
tos_value = inp->ip_inp.inp.inp_ip_tos;
}
- if (nofragment_flag) {
+ if ((nofragment_flag) && (port == 0)) {
#if defined(WITH_CONVERT_IP_OFF) || defined(__FreeBSD__) || defined(__APPLE__)
ip->ip_off = IP_DF;
#else
@@ -3456,7 +3534,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
/* no association at all */
ip->ip_tos = (tos_value & 0xfc);
}
- ip->ip_p = IPPROTO_SCTP;
+ if (port) {
+ ip->ip_p = IPPROTO_UDP;
+ } else {
+ ip->ip_p = IPPROTO_SCTP;
+ }
ip->ip_sum = 0;
if (net == NULL) {
ro = &iproute;
@@ -3469,7 +3551,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
ip->ip_dst.s_addr = ((struct sockaddr_in *)to)->sin_addr.s_addr;
/* call the routine to select the src address */
- if (net) {
+ if (net && out_of_asoc_ok == 0) {
if (net->ro._s_addr && (net->ro._s_addr->localifa_flags & (SCTP_BEING_DELETED | SCTP_ADDR_IFA_UNUSEABLE))) {
sctp_free_ifa(net->ro._s_addr);
net->ro._s_addr = NULL;
@@ -3480,13 +3562,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
}
}
if (net->src_addr_selected == 0) {
- if (out_of_asoc_ok) {
- /* do not cache */
- goto temp_v4_src;
- }
/* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp, stcb,
- ro, net, out_of_asoc_ok,
+ ro, net, 0,
vrf_id);
net->src_addr_selected = 1;
}
@@ -3499,7 +3577,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
struct sctp_ifa *_lsrc;
- temp_v4_src:
_lsrc = sctp_source_address_selection(inp, stcb, ro,
net,
out_of_asoc_ok,
@@ -3510,7 +3587,13 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
ip->ip_src = _lsrc->address.sin.sin_addr;
sctp_free_ifa(_lsrc);
}
-
+ if (port) {
+ udp = (struct udphdr *)(ip + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(packet_length - sizeof(struct ip));
+ udp->uh_sum = 0;
+ }
/*
* If source address selection fails and we find no route
* then the ip_output should fail as well with a
@@ -3685,15 +3768,25 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
flowlabel = ((struct in6pcb *)inp)->in6p_flowinfo;
}
- newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ }
if (newm == NULL) {
sctp_m_freem(m);
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
- SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr));
- SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr);
- packet_length += sizeof(struct ip6_hdr);
+ if (port) {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
+ packet_length += sizeof(struct ip6_hdr) + sizeof(struct udphdr);
+ } else {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr);
+ packet_length += sizeof(struct ip6_hdr);
+ }
SCTP_BUF_NEXT(newm) = m;
m = newm;
@@ -3735,7 +3828,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4);
}
ip6h->ip6_flow = htonl(((tosTop << 24) | ((tosBottom | flowTop) << 16) | flowBottom));
- ip6h->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ ip6h->ip6_nxt = IPPROTO_UDP;
+ } else {
+ ip6h->ip6_nxt = IPPROTO_SCTP;
+ }
ip6h->ip6_plen = (packet_length - sizeof(struct ip6_hdr));
ip6h->ip6_dst = sin6->sin6_addr;
@@ -3748,7 +3845,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
lsa6_tmp.sin6_family = AF_INET6;
lsa6_tmp.sin6_len = sizeof(lsa6_tmp);
lsa6 = &lsa6_tmp;
- if (net) {
+ if (net && out_of_asoc_ok == 0) {
if (net->ro._s_addr && (net->ro._s_addr->localifa_flags & (SCTP_BEING_DELETED | SCTP_ADDR_IFA_UNUSEABLE))) {
sctp_free_ifa(net->ro._s_addr);
net->ro._s_addr = NULL;
@@ -3759,17 +3856,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
}
}
if (net->src_addr_selected == 0) {
- if (out_of_asoc_ok) {
- /* do not cache */
- goto temp_v6_src;
+ sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+ /* KAME hack: embed scopeid */
+ if (sa6_embedscope(sin6, ip6_use_defzone) != 0) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
+ return (EINVAL);
}
/* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp,
stcb,
ro,
net,
- out_of_asoc_ok,
+ 0,
vrf_id);
+ (void)sa6_recoverscope(sin6);
net->src_addr_selected = 1;
}
if (net->ro._s_addr == NULL) {
@@ -3781,11 +3881,17 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
struct sctp_ifa *_lsrc;
- temp_v6_src:
+ sin6 = (struct sockaddr_in6 *)&ro->ro_dst;
+ /* KAME hack: embed scopeid */
+ if (sa6_embedscope(sin6, ip6_use_defzone) != 0) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
+ return (EINVAL);
+ }
_lsrc = sctp_source_address_selection(inp, stcb, ro,
net,
out_of_asoc_ok,
vrf_id);
+ (void)sa6_recoverscope(sin6);
if (_lsrc == NULL) {
goto no_route;
}
@@ -3809,6 +3915,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
bzero(&lsa6_storage, sizeof(lsa6_storage));
lsa6_storage.sin6_family = AF_INET6;
lsa6_storage.sin6_len = sizeof(lsa6_storage);
+ lsa6_storage.sin6_addr = lsa6->sin6_addr;
if ((error = sa6_recoverscope(&lsa6_storage)) != 0) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "recover scope fails error %d\n", error);
sctp_m_freem(m);
@@ -3820,6 +3927,13 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
lsa6 = &lsa6_storage;
ip6h->ip6_src = lsa6->sin6_addr;
+ if (port) {
+ udp = (struct udphdr *)(ip6h + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(packet_length - sizeof(struct ip6_hdr));
+ udp->uh_sum = 0;
+ }
/*
* We set the hop limit now since there is a good chance
* that our ro pointer is now filled
@@ -4208,7 +4322,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT - calls lowlevel_output\n");
ret = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr,
- m, 0, NULL, 0, 0, NULL, 0, so_locked);
+ m, 0, NULL, 0, 0, NULL, 0, net->port, so_locked);
SCTPDBG(SCTP_DEBUG_OUTPUT4, "lowlevel_output - %d\n", ret);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
@@ -4629,8 +4743,8 @@ sctp_are_there_new_addresses(struct sctp_association *asoc,
#ifdef INET6
if (sa->sa_family == AF_INET6) {
sa6 = (struct sockaddr_in6 *)sa;
- if (SCTP6_ARE_ADDR_EQUAL(&sa6->sin6_addr,
- &sin6.sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL(sa6,
+ &sin6)) {
fnd = 1;
break;
}
@@ -4699,7 +4813,7 @@ sctp_are_there_new_addresses(struct sctp_association *asoc,
if (sa->sa_family == AF_INET6) {
sa6 = (struct sockaddr_in6 *)sa;
if (SCTP6_ARE_ADDR_EQUAL(
- &sa6->sin6_addr, &sin6.sin6_addr)) {
+ sa6, &sin6)) {
fnd = 1;
break;
}
@@ -4726,7 +4840,7 @@ sctp_are_there_new_addresses(struct sctp_association *asoc,
void
sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct mbuf *init_pkt, int iphlen, int offset, struct sctphdr *sh,
- struct sctp_init_chunk *init_chk, uint32_t vrf_id, int hold_inp_lock)
+ struct sctp_init_chunk *init_chk, uint32_t vrf_id, uint16_t port, int hold_inp_lock)
{
struct sctp_association *asoc;
struct mbuf *m, *m_at, *m_tmp, *m_cookie, *op_err, *mp_last;
@@ -4774,7 +4888,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
* though we even set the T bit and copy in the 0 tag.. this
* looks no different than if no listener was present.
*/
- sctp_send_abort(init_pkt, iphlen, sh, 0, NULL, vrf_id);
+ sctp_send_abort(init_pkt, iphlen, sh, 0, NULL, vrf_id, port);
return;
}
abort_flag = 0;
@@ -4783,7 +4897,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
&abort_flag, (struct sctp_chunkhdr *)init_chk);
if (abort_flag) {
sctp_send_abort(init_pkt, iphlen, sh,
- init_chk->init.initiate_tag, op_err, vrf_id);
+ init_chk->init.initiate_tag, op_err, vrf_id, port);
return;
}
m = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA);
@@ -4926,6 +5040,13 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
stc.addr_type = SCTP_IPV6_ADDRESS;
stc.scope_id = 0;
if (sctp_is_address_on_local_host((struct sockaddr *)sin6, vrf_id)) {
+ /*
+ * FIX ME: does this have scope from
+ * rcvif?
+ */
+ (void)sa6_recoverscope(sin6);
+ stc.scope_id = sin6->sin6_scope_id;
+ sa6_embedscope(sin6, ip6_use_defzone);
stc.loopback_scope = 1;
stc.local_scope = 0;
stc.site_scope = 1;
@@ -4960,9 +5081,8 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
* rcvif?
*/
(void)sa6_recoverscope(sin6);
-
- sa6_embedscope(sin6, ip6_use_defzone);
stc.scope_id = sin6->sin6_scope_id;
+ sa6_embedscope(sin6, ip6_use_defzone);
} else if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
/*
* If the new destination is
@@ -5374,7 +5494,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
p_len += padval;
}
(void)sctp_lowlevel_chunk_output(inp, NULL, NULL, to, m, 0, NULL, 0, 0,
- NULL, 0, SCTP_SO_NOT_LOCKED);
+ NULL, 0, port, SCTP_SO_NOT_LOCKED);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
}
@@ -6243,15 +6363,23 @@ sctp_toss_old_asconf(struct sctp_tcb *stcb)
{
struct sctp_association *asoc;
struct sctp_tmit_chunk *chk, *chk_tmp;
+ struct sctp_asconf_chunk *acp;
asoc = &stcb->asoc;
- for (chk = TAILQ_FIRST(&asoc->control_send_queue); chk != NULL;
+ for (chk = TAILQ_FIRST(&asoc->asconf_send_queue); chk != NULL;
chk = chk_tmp) {
/* get next chk */
chk_tmp = TAILQ_NEXT(chk, sctp_next);
- /* find SCTP_ASCONF chunk in queue (only one ever in queue) */
+ /* find SCTP_ASCONF chunk in queue */
if (chk->rec.chunk_id.id == SCTP_ASCONF) {
- TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+ if (chk->data) {
+ acp = mtod(chk->data, struct sctp_asconf_chunk *);
+ if (compare_with_wrap(ntohl(acp->serial_number), stcb->asoc.asconf_seq_out_acked, MAX_SEQ)) {
+ /* Not Acked yet */
+ break;
+ }
+ }
+ TAILQ_REMOVE(&asoc->asconf_send_queue, chk, sctp_next);
if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
@@ -7191,6 +7319,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
/* Nothing to possible to send? */
if (TAILQ_EMPTY(&asoc->control_send_queue) &&
+ TAILQ_EMPTY(&asoc->asconf_send_queue) &&
TAILQ_EMPTY(&asoc->send_queue) &&
TAILQ_EMPTY(&asoc->out_wheel)) {
*reason_code = 9;
@@ -7305,12 +7434,15 @@ skip_the_fill_from_streams:
/* now service each destination and send out what we can for it */
/* Nothing to send? */
if ((TAILQ_FIRST(&asoc->control_send_queue) == NULL) &&
+ (TAILQ_FIRST(&asoc->asconf_send_queue) == NULL) &&
(TAILQ_FIRST(&asoc->send_queue) == NULL)) {
*reason_code = 8;
return (0);
}
if (no_data_chunks) {
- chk = TAILQ_FIRST(&asoc->control_send_queue);
+ chk = TAILQ_FIRST(&asoc->asconf_send_queue);
+ if (chk == NULL)
+ chk = TAILQ_FIRST(&asoc->control_send_queue);
} else {
chk = TAILQ_FIRST(&asoc->send_queue);
}
@@ -7390,6 +7522,194 @@ again_one_more_time:
r_mtu = mtu;
}
/************************/
+ /* ASCONF transmission */
+ /************************/
+ /* Now first lets go through the asconf queue */
+ for (chk = TAILQ_FIRST(&asoc->asconf_send_queue);
+ chk; chk = nchk) {
+ nchk = TAILQ_NEXT(chk, sctp_next);
+ if (chk->rec.chunk_id.id != SCTP_ASCONF) {
+ continue;
+ }
+ if (chk->whoTo != net) {
+ /*
+ * No, not sent to the network we are
+ * looking at
+ */
+ break;
+ }
+ if (chk->data == NULL) {
+ break;
+ }
+ if (chk->sent != SCTP_DATAGRAM_UNSENT &&
+ chk->sent != SCTP_DATAGRAM_RESEND) {
+ break;
+ }
+ /*
+ * if no AUTH is yet included and this chunk
+ * requires it, make sure to account for it. We
+ * don't apply the size until the AUTH chunk is
+ * actually added below in case there is no room for
+ * this chunk. NOTE: we overload the use of "omtu"
+ * here
+ */
+ if ((auth == NULL) &&
+ sctp_auth_is_required_chunk(chk->rec.chunk_id.id,
+ stcb->asoc.peer_auth_chunks)) {
+ omtu = sctp_get_auth_chunk_len(stcb->asoc.peer_hmac_id);
+ } else
+ omtu = 0;
+ /* Here we do NOT factor the r_mtu */
+ if ((chk->send_size < (int)(mtu - omtu)) ||
+ (chk->flags & CHUNK_FLAGS_FRAGMENT_OK)) {
+ /*
+ * We probably should glom the mbuf chain
+ * from the chk->data for control but the
+ * problem is it becomes yet one more level
+ * of tracking to do if for some reason
+ * output fails. Then I have got to
+ * reconstruct the merged control chain.. el
+ * yucko.. for now we take the easy way and
+ * do the copy
+ */
+ /*
+ * Add an AUTH chunk, if chunk requires it
+ * save the offset into the chain for AUTH
+ */
+ if ((auth == NULL) &&
+ (sctp_auth_is_required_chunk(chk->rec.chunk_id.id,
+ stcb->asoc.peer_auth_chunks))) {
+ outchain = sctp_add_auth_chunk(outchain,
+ &endoutchain,
+ &auth,
+ &auth_offset,
+ stcb,
+ chk->rec.chunk_id.id);
+ SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
+ }
+ outchain = sctp_copy_mbufchain(chk->data, outchain, &endoutchain,
+ (int)chk->rec.chunk_id.can_take_data,
+ chk->send_size, chk->copy_by_ref);
+ if (outchain == NULL) {
+ *reason_code = 8;
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
+ return (ENOMEM);
+ }
+ SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
+ /* update our MTU size */
+ if (mtu > (chk->send_size + omtu))
+ mtu -= (chk->send_size + omtu);
+ else
+ mtu = 0;
+ to_out += (chk->send_size + omtu);
+ /* Do clear IP_DF ? */
+ if (chk->flags & CHUNK_FLAGS_FRAGMENT_OK) {
+ no_fragmentflg = 0;
+ }
+ if (chk->rec.chunk_id.can_take_data)
+ chk->data = NULL;
+ /*
+ * set hb flag since we can use these for
+ * RTO
+ */
+ hbflag = 1;
+ asconf = 1;
+ /*
+ * should sysctl this: don't bundle data
+ * with ASCONF since it requires AUTH
+ */
+ no_data_chunks = 1;
+ chk->sent = SCTP_DATAGRAM_SENT;
+ chk->snd_count++;
+ if (mtu == 0) {
+ /*
+ * Ok we are out of room but we can
+ * output without effecting the
+ * flight size since this little guy
+ * is a control only packet.
+ */
+ sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net);
+ /*
+ * do NOT clear the asconf flag as
+ * it is used to do appropriate
+ * source address selection.
+ */
+ SCTP_BUF_PREPEND(outchain, sizeof(struct sctphdr), M_DONTWAIT);
+ if (outchain == NULL) {
+ /* no memory */
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
+ error = ENOBUFS;
+ *reason_code = 7;
+ continue;
+ }
+ shdr = mtod(outchain, struct sctphdr *);
+ shdr->src_port = inp->sctp_lport;
+ shdr->dest_port = stcb->rport;
+ shdr->v_tag = htonl(stcb->asoc.peer_vtag);
+ shdr->checksum = 0;
+ auth_offset += sizeof(struct sctphdr);
+ if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
+ (struct sockaddr *)&net->ro._l_addr,
+ outchain, auth_offset, auth,
+ no_fragmentflg, 0, NULL, asconf, net->port, so_locked))) {
+ if (error == ENOBUFS) {
+ asoc->ifp_had_enobuf = 1;
+ SCTP_STAT_INCR(sctps_lowlevelerr);
+ }
+ if (from_where == 0) {
+ SCTP_STAT_INCR(sctps_lowlevelerrusr);
+ }
+ if (*now_filled == 0) {
+ (void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+ *now_filled = 1;
+ *now = net->last_sent_time;
+ } else {
+ net->last_sent_time = *now;
+ }
+ hbflag = 0;
+ /* error, could not output */
+ if (error == EHOSTUNREACH) {
+ /*
+ * Destination went
+ * unreachable
+ * during this send
+ */
+ sctp_move_to_an_alt(stcb, asoc, net);
+ }
+ *reason_code = 7;
+ continue;
+ } else
+ asoc->ifp_had_enobuf = 0;
+ if (*now_filled == 0) {
+ (void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+ *now_filled = 1;
+ *now = net->last_sent_time;
+ } else {
+ net->last_sent_time = *now;
+ }
+ hbflag = 0;
+ /*
+ * increase the number we sent, if a
+ * cookie is sent we don't tell them
+ * any was sent out.
+ */
+ outchain = endoutchain = NULL;
+ auth = NULL;
+ auth_offset = 0;
+ if (!no_out_cnt)
+ *num_out += ctl_cnt;
+ /* recalc a clean slate and setup */
+ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+ mtu = (net->mtu - SCTP_MIN_OVERHEAD);
+ } else {
+ mtu = (net->mtu - SCTP_MIN_V4_OVERHEAD);
+ }
+ to_out = 0;
+ no_fragmentflg = 1;
+ }
+ }
+ }
+ /************************/
/* Control transmission */
/************************/
/* Now first lets go through the control queue */
@@ -7510,27 +7830,14 @@ again_one_more_time:
} else {
/*
* Other chunks, since they have
- * timers running (i.e. COOKIE or
- * ASCONF) we just "trust" that it
- * gets sent or retransmitted.
+ * timers running (i.e. COOKIE) we
+ * just "trust" that it gets sent or
+ * retransmitted.
*/
ctl_cnt++;
if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) {
cookie = 1;
no_out_cnt = 1;
- } else if (chk->rec.chunk_id.id == SCTP_ASCONF) {
- /*
- * set hb flag since we can
- * use these for RTO
- */
- hbflag = 1;
- asconf = 1;
- /*
- * should sysctl this: don't
- * bundle data with ASCONF
- * since it requires AUTH
- */
- no_data_chunks = 1;
}
chk->sent = SCTP_DATAGRAM_SENT;
chk->snd_count++;
@@ -7571,7 +7878,7 @@ again_one_more_time:
if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr,
outchain, auth_offset, auth,
- no_fragmentflg, 0, NULL, asconf, so_locked))) {
+ no_fragmentflg, 0, NULL, asconf, net->port, so_locked))) {
if (error == ENOBUFS) {
asoc->ifp_had_enobuf = 1;
SCTP_STAT_INCR(sctps_lowlevelerr);
@@ -7860,7 +8167,7 @@ again_one_more_time:
no_fragmentflg,
bundle_at,
data_list[0],
- asconf, so_locked))) {
+ asconf, net->port, so_locked))) {
/* error, we could not output */
if (error == ENOBUFS) {
SCTP_STAT_INCR(sctps_lowlevelerr);
@@ -8332,16 +8639,20 @@ sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net, int addr_locked)
*/
struct sctp_tmit_chunk *chk;
struct mbuf *m_asconf;
- struct sctp_asconf_chunk *acp;
int len;
SCTP_TCB_LOCK_ASSERT(stcb);
+
+ if ((!TAILQ_EMPTY(&stcb->asoc.asconf_send_queue)) &&
+ (!sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS))) {
+ /* can't send a new one if there is one in flight already */
+ return;
+ }
/* compose an ASCONF chunk, maximum length is PMTU */
m_asconf = sctp_compose_asconf(stcb, &len, addr_locked);
if (m_asconf == NULL) {
return;
}
- acp = mtod(m_asconf, struct sctp_asconf_chunk *);
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
/* no memory */
@@ -8355,11 +8666,11 @@ sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net, int addr_locked)
chk->rec.chunk_id.can_take_data = 0;
chk->sent = SCTP_DATAGRAM_UNSENT;
chk->snd_count = 0;
- chk->flags = 0;
+ chk->flags = CHUNK_FLAGS_FRAGMENT_OK;
chk->asoc = &stcb->asoc;
- chk->whoTo = chk->asoc->primary_destination;
+ chk->whoTo = net;
atomic_add_int(&chk->whoTo->ref_count, 1);
- TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+ TAILQ_INSERT_TAIL(&chk->asoc->asconf_send_queue, chk, sctp_next);
chk->asoc->ctrl_queue_cnt++;
return;
}
@@ -8482,7 +8793,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
struct sctp_tmit_chunk *chk, *fwd;
struct mbuf *m, *endofchain;
struct sctphdr *shdr;
- int asconf;
struct sctp_nets *net = NULL;
uint32_t tsns_sent = 0;
int no_fragmentflg, bundle_at, cnt_thru;
@@ -8495,7 +8805,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
SCTP_TCB_LOCK_ASSERT(stcb);
tmr_started = ctl_cnt = bundle_at = error = 0;
no_fragmentflg = 1;
- asconf = 0;
fwd_tsn = 0;
*cnt_out = 0;
fwd = NULL;
@@ -8515,7 +8824,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
}
TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
if ((chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) ||
- (chk->rec.chunk_id.id == SCTP_ASCONF) ||
(chk->rec.chunk_id.id == SCTP_STREAM_RESET) ||
(chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN)) {
if (chk->rec.chunk_id.id == SCTP_STREAM_RESET) {
@@ -8528,10 +8836,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
}
}
ctl_cnt++;
- if (chk->rec.chunk_id.id == SCTP_ASCONF) {
- no_fragmentflg = 1;
- asconf = 1;
- }
if (chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) {
fwd_tsn = 1;
fwd = chk;
@@ -8577,7 +8881,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
if ((error = sctp_lowlevel_chunk_output(inp, stcb, chk->whoTo,
(struct sockaddr *)&chk->whoTo->ro._l_addr, m, auth_offset,
- auth, no_fragmentflg, 0, NULL, asconf, so_locked))) {
+ auth, no_fragmentflg, 0, NULL, 0, chk->whoTo->port, so_locked))) {
SCTP_STAT_INCR(sctps_lowlevelerr);
return (error);
}
@@ -8815,7 +9119,7 @@ one_chunk_around:
/* Now lets send it, if there is anything to send :> */
if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr, m, auth_offset,
- auth, no_fragmentflg, 0, NULL, asconf, so_locked))) {
+ auth, no_fragmentflg, 0, NULL, 0, net->port, so_locked))) {
/* error, we could not output */
SCTP_STAT_INCR(sctps_lowlevelerr);
return (error);
@@ -9795,7 +10099,7 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr, int so_locked
(void)sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb,
stcb->asoc.primary_destination,
(struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr,
- m_out, auth_offset, auth, 1, 0, NULL, 0, so_locked);
+ m_out, auth_offset, auth, 1, 0, NULL, 0, stcb->asoc.primary_destination->port, so_locked);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
}
@@ -9824,19 +10128,20 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb,
SCTP_BUF_LEN(m_shutdown_comp) = sizeof(struct sctp_shutdown_complete_msg);
(void)sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb, net,
(struct sockaddr *)&net->ro._l_addr,
- m_shutdown_comp, 0, NULL, 1, 0, NULL, 0, SCTP_SO_NOT_LOCKED);
+ m_shutdown_comp, 0, NULL, 1, 0, NULL, 0, net->port, SCTP_SO_NOT_LOCKED);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
return;
}
void
sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
- uint32_t vrf_id)
+ uint32_t vrf_id, uint16_t port)
{
/* formulate and SEND a SHUTDOWN-COMPLETE */
struct mbuf *o_pak;
struct mbuf *mout;
struct ip *iph, *iph_out;
+ struct udphdr *udp;
#ifdef INET6
struct ip6_hdr *ip6, *ip6_out;
@@ -9845,28 +10150,36 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
int offset_out, len, mlen;
struct sctp_shutdown_complete_msg *comp_cp;
- /* Get room for the largest message */
+ iph = mtod(m, struct ip *);
+ switch (iph->ip_v) {
+ case IPVERSION:
+ len = (sizeof(struct ip) + sizeof(struct sctp_shutdown_complete_msg));
+ break;
#ifdef INET6
- len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_shutdown_complete_msg));
-#else
- len = (sizeof(struct ip) + sizeof(struct sctp_shutdown_complete_msg));
+ case IPV6_VERSION >> 4:
+ len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_shutdown_complete_msg));
+ break;
#endif
+ default:
+ return;
+ }
+ if (port) {
+ len += sizeof(struct udphdr);
+ }
mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA);
if (mout == NULL) {
return;
}
SCTP_BUF_LEN(mout) = len;
- iph = mtod(m, struct ip *);
+ SCTP_BUF_NEXT(mout) = NULL;
iph_out = NULL;
#ifdef INET6
ip6_out = NULL;
#endif
offset_out = 0;
+
switch (iph->ip_v) {
case IPVERSION:
- SCTP_BUF_LEN(mout) = sizeof(struct ip) +
- sizeof(struct sctp_shutdown_complete_msg);
- SCTP_BUF_NEXT(mout) = NULL;
iph_out = mtod(mout, struct ip *);
/* Fill in the IP header for the ABORT */
@@ -9876,7 +10189,11 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
iph_out->ip_id = 0;
iph_out->ip_off = 0;
iph_out->ip_ttl = MAXTTL;
- iph_out->ip_p = IPPROTO_SCTP;
+ if (port) {
+ iph_out->ip_p = IPPROTO_UDP;
+ } else {
+ iph_out->ip_p = IPPROTO_SCTP;
+ }
iph_out->ip_src.s_addr = iph->ip_dst.s_addr;
iph_out->ip_dst.s_addr = iph->ip_src.s_addr;
@@ -9889,15 +10206,16 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
#ifdef INET6
case IPV6_VERSION >> 4:
ip6 = (struct ip6_hdr *)iph;
- SCTP_BUF_LEN(mout) = sizeof(struct ip6_hdr) +
- sizeof(struct sctp_shutdown_complete_msg);
- SCTP_BUF_NEXT(mout) = NULL;
ip6_out = mtod(mout, struct ip6_hdr *);
/* Fill in the IPv6 header for the ABORT */
ip6_out->ip6_flow = ip6->ip6_flow;
ip6_out->ip6_hlim = ip6_defhlim;
- ip6_out->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ ip6_out->ip6_nxt = IPPROTO_UDP;
+ } else {
+ ip6_out->ip6_nxt = IPPROTO_SCTP;
+ }
ip6_out->ip6_src = ip6->ip6_dst;
ip6_out->ip6_dst = ip6->ip6_src;
/*
@@ -9914,6 +10232,15 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
/* Currently not supported. */
return;
}
+ if (port) {
+ udp = (struct udphdr *)comp_cp;
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(sizeof(struct sctp_shutdown_complete_msg) + sizeof(struct udphdr));
+ udp->uh_sum = 0;
+ offset_out += sizeof(struct udphdr);
+ comp_cp = (struct sctp_shutdown_complete_msg *)((caddr_t)comp_cp + sizeof(struct udphdr));
+ }
if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) {
/* no mbuf's */
sctp_m_freem(mout);
@@ -10758,7 +11085,7 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
void
sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
- struct mbuf *err_cause, uint32_t vrf_id)
+ struct mbuf *err_cause, uint32_t vrf_id, uint16_t port)
{
/*-
* Formulate the abort message, and send it back down.
@@ -10767,6 +11094,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
struct mbuf *mout;
struct sctp_abort_msg *abm;
struct ip *iph, *iph_out;
+ struct udphdr *udp;
#ifdef INET6
struct ip6_hdr *ip6, *ip6_out;
@@ -10780,18 +11108,30 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
sctp_m_freem(err_cause);
return;
}
+ iph = mtod(m, struct ip *);
+ switch (iph->ip_v) {
+ case IPVERSION:
+ len = (sizeof(struct ip) + sizeof(struct sctp_abort_msg));
+ break;
#ifdef INET6
- len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg));
-#else
- len = (sizeof(struct ip) + sizeof(struct sctp_abort_msg));
+ case IPV6_VERSION >> 4:
+ len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg));
+ break;
#endif
+ default:
+ return;
+ }
+ if (port) {
+ len += sizeof(struct udphdr);
+ }
mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA);
if (mout == NULL) {
if (err_cause)
sctp_m_freem(err_cause);
return;
}
- iph = mtod(m, struct ip *);
+ SCTP_BUF_LEN(mout) = len;
+ SCTP_BUF_NEXT(mout) = err_cause;
iph_out = NULL;
#ifdef INET6
ip6_out = NULL;
@@ -10799,8 +11139,6 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
switch (iph->ip_v) {
case IPVERSION:
iph_out = mtod(mout, struct ip *);
- SCTP_BUF_LEN(mout) = sizeof(*iph_out) + sizeof(*abm);
- SCTP_BUF_NEXT(mout) = err_cause;
/* Fill in the IP header for the ABORT */
iph_out->ip_v = IPVERSION;
@@ -10809,7 +11147,11 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
iph_out->ip_id = 0;
iph_out->ip_off = 0;
iph_out->ip_ttl = MAXTTL;
- iph_out->ip_p = IPPROTO_SCTP;
+ if (port) {
+ iph_out->ip_p = IPPROTO_UDP;
+ } else {
+ iph_out->ip_p = IPPROTO_SCTP;
+ }
iph_out->ip_src.s_addr = iph->ip_dst.s_addr;
iph_out->ip_dst.s_addr = iph->ip_src.s_addr;
/* let IP layer calculate this */
@@ -10822,13 +11164,15 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
case IPV6_VERSION >> 4:
ip6 = (struct ip6_hdr *)iph;
ip6_out = mtod(mout, struct ip6_hdr *);
- SCTP_BUF_LEN(mout) = sizeof(*ip6_out) + sizeof(*abm);
- SCTP_BUF_NEXT(mout) = err_cause;
/* Fill in the IP6 header for the ABORT */
ip6_out->ip6_flow = ip6->ip6_flow;
ip6_out->ip6_hlim = ip6_defhlim;
- ip6_out->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ ip6_out->ip6_nxt = IPPROTO_UDP;
+ } else {
+ ip6_out->ip6_nxt = IPPROTO_SCTP;
+ }
ip6_out->ip6_src = ip6->ip6_dst;
ip6_out->ip6_dst = ip6->ip6_src;
@@ -10844,6 +11188,15 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
return;
}
+ udp = (struct udphdr *)abm;
+ if (port) {
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ /* set udp->uh_ulen later */
+ udp->uh_sum = 0;
+ iphlen_out += sizeof(struct udphdr);
+ abm = (struct sctp_abort_msg *)((caddr_t)abm + sizeof(struct udphdr));
+ }
abm->sh.src_port = sh->dest_port;
abm->sh.dest_port = sh->src_port;
abm->sh.checksum = 0;
@@ -10895,6 +11248,9 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
/* zap the stack pointer to the route */
bzero(&ro, sizeof ro);
+ if (port) {
+ udp->uh_ulen = htons(len - sizeof(struct ip));
+ }
SCTPDBG(SCTP_DEBUG_OUTPUT2, "sctp_send_abort calling ip_output:\n");
SCTPDBG_PKT(SCTP_DEBUG_OUTPUT2, iph_out, &abm->sh);
/* set IPv4 length */
@@ -10920,6 +11276,9 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
/* zap the stack pointer to the route */
bzero(&ro, sizeof(ro));
+ if (port) {
+ udp->uh_ulen = htons(len - sizeof(struct ip6_hdr));
+ }
SCTPDBG(SCTP_DEBUG_OUTPUT2, "sctp_send_abort calling ip6_output:\n");
SCTPDBG_PKT(SCTP_DEBUG_OUTPUT2, (struct ip *)ip6_out, &abm->sh);
ip6_out->ip6_plen = len - sizeof(*ip6_out);
@@ -10941,7 +11300,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
void
sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
- uint32_t vrf_id)
+ uint32_t vrf_id, uint16_t port)
{
struct mbuf *o_pak;
struct sctphdr *ihdr;
@@ -10949,6 +11308,7 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
struct sctphdr *ohdr;
struct sctp_chunkhdr *ophdr;
struct ip *iph;
+ struct udphdr *udp;
struct mbuf *mout;
#ifdef SCTP_DEBUG
@@ -10993,9 +11353,17 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
}
val = sctp_calculate_sum(scm, NULL, 0);
#ifdef INET6
- mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ }
#else
- mout = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ }
#endif
if (mout == NULL) {
sctp_m_freem(scm);
@@ -11017,7 +11385,10 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
SCTP_BUF_LEN(mout) = sizeof(struct ip);
len += sizeof(struct ip);
-
+ if (port) {
+ SCTP_BUF_LEN(mout) += sizeof(struct udphdr);
+ len += sizeof(struct udphdr);
+ }
bzero(&ro, sizeof ro);
out = mtod(mout, struct ip *);
out->ip_v = iph->ip_v;
@@ -11026,11 +11397,22 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
out->ip_id = iph->ip_id;
out->ip_off = 0;
out->ip_ttl = MAXTTL;
- out->ip_p = IPPROTO_SCTP;
+ if (port) {
+ out->ip_p = IPPROTO_UDP;
+ } else {
+ out->ip_p = IPPROTO_SCTP;
+ }
out->ip_sum = 0;
out->ip_src = iph->ip_dst;
out->ip_dst = iph->ip_src;
out->ip_len = len;
+ if (port) {
+ udp = (struct udphdr *)(out + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(len - sizeof(struct ip));
+ udp->uh_sum = 0;
+ }
#ifdef SCTP_PACKET_LOGGING
if (sctp_logging_level & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, len);
@@ -11059,15 +11441,29 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
SCTP_BUF_LEN(mout) = sizeof(struct ip6_hdr);
len += sizeof(struct ip6_hdr);
bzero(&ro, sizeof ro);
+ if (port) {
+ SCTP_BUF_LEN(mout) += sizeof(struct udphdr);
+ len += sizeof(struct udphdr);
+ }
in6 = mtod(m, struct ip6_hdr *);
out6 = mtod(mout, struct ip6_hdr *);
out6->ip6_flow = in6->ip6_flow;
out6->ip6_hlim = ip6_defhlim;
- out6->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ out6->ip6_nxt = IPPROTO_UDP;
+ } else {
+ out6->ip6_nxt = IPPROTO_SCTP;
+ }
out6->ip6_src = in6->ip6_dst;
out6->ip6_dst = in6->ip6_src;
out6->ip6_plen = len - sizeof(struct ip6_hdr);
-
+ if (port) {
+ udp = (struct udphdr *)(out6 + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(len - sizeof(struct ip6_hdr));
+ udp->uh_sum = 0;
+ }
#ifdef SCTP_DEBUG
bzero(&lsa6, sizeof(lsa6));
lsa6.sin6_len = sizeof(lsa6);
diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h
index d37c163..f0961b8 100644
--- a/sys/netinet/sctp_output.h
+++ b/sys/netinet/sctp_output.h
@@ -84,7 +84,7 @@ sctp_send_initiate(struct sctp_inpcb *, struct sctp_tcb *, int
void
sctp_send_initiate_ack(struct sctp_inpcb *, struct sctp_tcb *,
struct mbuf *, int, int, struct sctphdr *, struct sctp_init_chunk *,
- uint32_t, int);
+ uint32_t, uint16_t, int);
struct mbuf *
sctp_arethere_unrecognized_parameters(struct mbuf *, int, int *,
@@ -110,7 +110,7 @@ void sctp_send_shutdown_complete(struct sctp_tcb *, struct sctp_nets *);
void
sctp_send_shutdown_complete2(struct mbuf *, int, struct sctphdr *,
- uint32_t);
+ uint32_t, uint16_t);
void sctp_send_asconf(struct sctp_tcb *, struct sctp_nets *, int addr_locked);
@@ -197,9 +197,9 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
void
sctp_send_abort(struct mbuf *, int, struct sctphdr *, uint32_t,
- struct mbuf *, uint32_t);
+ struct mbuf *, uint32_t, uint16_t);
-void sctp_send_operr_to(struct mbuf *, int, struct mbuf *, uint32_t, uint32_t);
+void sctp_send_operr_to(struct mbuf *, int, struct mbuf *, uint32_t, uint32_t, uint16_t);
int
sctp_sosend(struct socket *so,
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index 7ad3f7f..6f540d1 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -45,24 +45,30 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_output.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_bsd_addr.h>
+#include <netinet/udp.h>
+void sctp_pcb_finish(void);
+
struct sctp_epinfo sctppcbinfo;
/* FIX: we don't handle multiple link local scopes */
/* "scopeless" replacement IN6_ARE_ADDR_EQUAL */
#ifdef INET6
int
-SCTP6_ARE_ADDR_EQUAL(struct in6_addr *a, struct in6_addr *b)
+SCTP6_ARE_ADDR_EQUAL(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
{
- struct in6_addr tmp_a, tmp_b;
-
- /* use a copy of a and b */
- tmp_a = *a;
- tmp_b = *b;
- in6_clearscope(&tmp_a);
- in6_clearscope(&tmp_b);
- return (IN6_ARE_ADDR_EQUAL(&tmp_a, &tmp_b));
+ struct sockaddr_in6 tmp_a, tmp_b;
+
+ memcpy(&tmp_a, a, sizeof(struct sockaddr_in6));
+ if (sa6_embedscope(&tmp_a, ip6_use_defzone) != 0) {
+ return 0;
+ }
+ memcpy(&tmp_b, b, sizeof(struct sockaddr_in6));
+ if (sa6_embedscope(&tmp_b, ip6_use_defzone) != 0) {
+ return 0;
+ }
+ return (IN6_ARE_ADDR_EQUAL(&tmp_a.sin6_addr, &tmp_b.sin6_addr));
}
#endif
@@ -916,8 +922,8 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
to;
intf_addr6 = &laddr->ifa->address.sin6;
- if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
- &intf_addr6->sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL(sin6,
+ intf_addr6)) {
match = 1;
break;
}
@@ -990,8 +996,8 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
rsin6 = (struct sockaddr_in6 *)from;
- if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
- &rsin6->sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL(sin6,
+ rsin6)) {
/* found it */
if (netp != NULL) {
*netp = net;
@@ -1147,8 +1153,8 @@ sctp_findassociation_ep_addr(struct sctp_inpcb **inp_p, struct sockaddr *remote,
sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
rsin6 = (struct sockaddr_in6 *)remote;
- if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
- &rsin6->sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL(sin6,
+ rsin6)) {
/* found it */
if (netp != NULL) {
*netp = net;
@@ -1245,8 +1251,8 @@ sctp_findassociation_ep_addr(struct sctp_inpcb **inp_p, struct sockaddr *remote,
sin6 = (struct sockaddr_in6 *)
&net->ro._l_addr;
rsin6 = (struct sockaddr_in6 *)remote;
- if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
- &rsin6->sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL(sin6,
+ rsin6)) {
/* found it */
if (netp != NULL) {
*netp = net;
@@ -1536,8 +1542,8 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
#ifdef INET6
case AF_INET6:
intf_addr6 = &laddr->ifa->address.sin6;
- if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
- &intf_addr6->sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL(sin6,
+ intf_addr6)) {
SCTP_INP_RUNLOCK(inp);
return (inp);
}
@@ -2775,6 +2781,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
sctp_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF);
sctp_feature_on(inp, SCTP_PCB_FLAGS_AUTO_ASCONF);
}
+ if (sctp_multiple_asconfs == 0) {
+ sctp_feature_off(inp, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS);
+ } else {
+ sctp_feature_on(inp, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS);
+ }
/*
* set the automatic mobility_base from kernel flag (by
* micchie)
@@ -3506,6 +3517,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
addr_inscope = 0;
}
}
+#ifdef INET6
} else if (newaddr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6;
@@ -3553,6 +3565,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
addr_inscope = 0;
}
}
+#endif
} else {
/* not supported family type */
return (-1);
@@ -3599,6 +3612,11 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
stcb->asoc.numnets++;
*(&net->ref_count) = 1;
net->tos_flowlabel = 0;
+ if (sctp_udp_tunneling_for_client_enable) {
+ net->port = htons(sctp_udp_tunneling_port);
+ } else {
+ net->port = 0;
+ }
#ifdef INET
if (newaddr->sa_family == AF_INET)
net->tos_flowlabel = stcb->asoc.default_tos;
@@ -3625,14 +3643,6 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
#endif
SCTP_RTALLOC((sctp_route_t *) & net->ro, stcb->asoc.vrf_id);
-#ifdef INET6
- if (newaddr->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6;
-
- sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
- (void)sa6_recoverscope(sin6);
- }
-#endif
if (SCTP_ROUTE_HAS_VALID_IFN(&net->ro)) {
/* Get source address */
net->ro._s_addr = sctp_source_address_selection(stcb->sctp_ep,
@@ -3685,6 +3695,17 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
} else {
net->mtu = stcb->asoc.smallest_mtu;
}
+#ifdef INET6
+ if (newaddr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+ (void)sa6_recoverscope(sin6);
+ }
+#endif
+ if (net->port) {
+ net->mtu -= sizeof(struct udphdr);
+ }
if (stcb->asoc.smallest_mtu > net->mtu) {
#ifdef SCTP_PRINT_FOR_B_AND_M
SCTP_PRINTF("new address mtu:%d smaller than smallest:%d\n",
@@ -4004,7 +4025,7 @@ sctp_remove_net(struct sctp_tcb *stcb, struct sctp_nets *net)
SCTPDBG(SCTP_DEBUG_ASCONF1, "remove_net: primary dst is deleting\n");
if (asoc->deleted_primary != NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "remove_net: deleted primary may be already stored\n");
- goto leave;
+ goto out;
}
asoc->deleted_primary = net;
atomic_add_int(&net->ref_count, 1);
@@ -4015,7 +4036,7 @@ sctp_remove_net(struct sctp_tcb *stcb, struct sctp_nets *net)
sctp_timer_start(SCTP_TIMER_TYPE_PRIM_DELETED,
stcb->sctp_ep, stcb, NULL);
}
-leave:
+out:
/* Try to find a confirmed primary */
asoc->primary_destination = sctp_find_alternate_net(stcb, lnet, 0);
}
@@ -4625,6 +4646,30 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
ccnt = 0;
}
*/
+
+ /* ASCONF queue MAY not be empty */
+ if (!TAILQ_EMPTY(&asoc->asconf_send_queue)) {
+ chk = TAILQ_FIRST(&asoc->asconf_send_queue);
+ while (chk) {
+ TAILQ_REMOVE(&asoc->asconf_send_queue, chk, sctp_next);
+ if (chk->data) {
+ sctp_m_freem(chk->data);
+ chk->data = NULL;
+ }
+ ccnt++;
+ sctp_free_remote_addr(chk->whoTo);
+ SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+ SCTP_DECR_CHK_COUNT();
+ /* sa_ignore FREED_MEMORY */
+ chk = TAILQ_FIRST(&asoc->asconf_send_queue);
+ }
+ }
+/*
+ if(ccnt) {
+ printf("Freed %d from asconf_queue\n", ccnt);
+ ccnt = 0;
+ }
+*/
if (!TAILQ_EMPTY(&asoc->reasmqueue)) {
chk = TAILQ_FIRST(&asoc->reasmqueue);
while (chk) {
@@ -5239,6 +5284,10 @@ sctp_pcb_init()
sizeof(struct sctp_stream_queue_pending),
(sctp_max_number_of_assoc * sctp_chunkscale));
+ SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_asconf, "sctp_asconf",
+ sizeof(struct sctp_asconf),
+ (sctp_max_number_of_assoc * sctp_chunkscale));
+
SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_asconf_ack, "sctp_asconf_ack",
sizeof(struct sctp_asconf_ack),
(sctp_max_number_of_assoc * sctp_chunkscale));
@@ -5298,6 +5347,73 @@ sctp_pcb_init()
}
+/*
+ * Assumes that the sctppcbinfo lock is NOT held.
+ */
+void
+sctp_pcb_finish(void)
+{
+ struct sctp_vrflist *vrf_bucket;
+ struct sctp_vrf *vrf;
+ struct sctp_ifn *ifn;
+ struct sctp_ifa *ifa;
+
+ /* FIXME MT */
+ /*
+ * free the vrf/ifn/ifa lists and hashes (be sure address monitor is
+ * destroyed first).
+ */
+ vrf_bucket = &sctppcbinfo.sctp_vrfhash[(SCTP_DEFAULT_VRFID & sctppcbinfo.hashvrfmark)];
+ vrf = LIST_FIRST(vrf_bucket);
+ while (vrf) {
+ ifn = LIST_FIRST(&vrf->ifnlist);
+ while (ifn) {
+ ifa = LIST_FIRST(&ifn->ifalist);
+ while (ifa) {
+ /* free the ifa */
+ LIST_REMOVE(ifa, next_bucket);
+ LIST_REMOVE(ifa, next_ifa);
+ SCTP_FREE(ifa, SCTP_M_IFA);
+ ifa = LIST_FIRST(&ifn->ifalist);
+ }
+ /* free the ifn */
+ LIST_REMOVE(ifn, next_bucket);
+ LIST_REMOVE(ifn, next_ifn);
+ SCTP_FREE(ifn, SCTP_M_IFN);
+ ifn = LIST_FIRST(&vrf->ifnlist);
+ }
+ SCTP_HASH_FREE(vrf->vrf_addr_hash, vrf->vrf_addr_hashmark);
+ /* free the vrf */
+ LIST_REMOVE(vrf, next_vrf);
+ SCTP_FREE(vrf, SCTP_M_VRF);
+ vrf = LIST_FIRST(vrf_bucket);
+ }
+ /* free the vrf hashes */
+ SCTP_HASH_FREE(sctppcbinfo.sctp_vrfhash, sctppcbinfo.hashvrfmark);
+ SCTP_HASH_FREE(sctppcbinfo.vrf_ifn_hash, sctppcbinfo.vrf_ifn_hashmark);
+
+ /* free the locks and mutexes */
+#ifdef SCTP_PACKET_LOGGING
+ SCTP_IP_PKTLOG_DESTROY();
+
+#endif
+ SCTP_IPI_ITERATOR_WQ_DESTROY();
+ SCTP_IPI_ADDR_DESTROY();
+ SCTP_ITERATOR_LOCK_DESTROY();
+ SCTP_STATLOG_DESTROY();
+ SCTP_INP_INFO_LOCK_DESTROY();
+
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_ep);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_asoc);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_laddr);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_net);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_chunk);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_readq);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_strmoq);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_asconf);
+ SCTP_ZONE_DESTROY(sctppcbinfo.ipi_zone_asconf_ack);
+}
+
int
sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h
index 44ac300..9b4df18 100644
--- a/sys/netinet/sctp_pcb.h
+++ b/sys/netinet/sctp_pcb.h
@@ -184,6 +184,7 @@ struct sctp_epinfo {
sctp_zone_t ipi_zone_chunk;
sctp_zone_t ipi_zone_readq;
sctp_zone_t ipi_zone_strmoq;
+ sctp_zone_t ipi_zone_asconf;
sctp_zone_t ipi_zone_asconf_ack;
struct rwlock ipi_ep_mtx;
@@ -435,7 +436,7 @@ struct sctp_tcb {
extern struct sctp_epinfo sctppcbinfo;
#ifdef INET6
-int SCTP6_ARE_ADDR_EQUAL(struct in6_addr *a, struct in6_addr *b);
+int SCTP6_ARE_ADDR_EQUAL(struct sockaddr_in6 *a, struct sockaddr_in6 *b);
#endif
diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h
index 69b6f92..8beb3bf 100644
--- a/sys/netinet/sctp_structs.h
+++ b/sys/netinet/sctp_structs.h
@@ -257,6 +257,8 @@ struct sctp_nets {
uint16_t failure_threshold;
/* error stats on destination */
uint16_t error_count;
+ /* UDP port number in case of UDP tunneling */
+ uint16_t port;
uint8_t fast_retran_loss_recovery;
uint8_t will_exit_fast_recovery;
@@ -547,6 +549,16 @@ struct sctp_cc_functions {
struct sctp_tcb *stcb, struct sctp_nets *net);
};
+/* used to save ASCONF chunks for retransmission */
+TAILQ_HEAD(sctp_asconf_head, sctp_asconf);
+struct sctp_asconf {
+ TAILQ_ENTRY(sctp_asconf) next;
+ uint32_t serial_number;
+ uint16_t snd_count;
+ struct mbuf *data;
+ uint16_t len;
+};
+
/* used to save ASCONF-ACK chunks for retransmission */
TAILQ_HEAD(sctp_asconf_ackhead, sctp_asconf_ack);
struct sctp_asconf_ack {
@@ -602,6 +614,9 @@ struct sctp_association {
/* Control chunk queue */
struct sctpchunk_listhead control_send_queue;
+ /* ASCONF chunk queue */
+ struct sctpchunk_listhead asconf_send_queue;
+
/*
* Once a TSN hits the wire it is moved to the sent_queue. We
* maintain two counts here (don't know if any but retran_cnt is
@@ -686,6 +701,7 @@ struct sctp_association {
uint32_t cookie_preserve_req;
/* ASCONF next seq I am sending out, inits at init-tsn */
uint32_t asconf_seq_out;
+ uint32_t asconf_seq_out_acked;
/* ASCONF last received ASCONF from peer, starts at peer's TSN-1 */
uint32_t asconf_seq_in;
@@ -919,8 +935,6 @@ struct sctp_association {
* lock flag: 0 is ok to send, 1+ (duals as a retran count) is
* awaiting ACK
*/
- uint16_t asconf_sent;
-
uint16_t mapping_array_size;
uint16_t last_strm_seq_delivered;
diff --git a/sys/netinet/sctp_sysctl.c b/sys/netinet/sctp_sysctl.c
index b428029..abf3f9e 100644
--- a/sys/netinet/sctp_sysctl.c
+++ b/sys/netinet/sctp_sysctl.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
uint32_t sctp_sendspace = SCTPCTL_MAXDGRAM_DEFAULT;
uint32_t sctp_recvspace = SCTPCTL_RECVSPACE_DEFAULT;
uint32_t sctp_auto_asconf = SCTPCTL_AUTOASCONF_DEFAULT;
+uint32_t sctp_multiple_asconfs = SCTPCTL_MULTIPLEASCONFS_DEFAULT;
uint32_t sctp_ecn_enable = SCTPCTL_ECN_ENABLE_DEFAULT;
uint32_t sctp_ecn_nonce = SCTPCTL_ECN_NONCE_DEFAULT;
uint32_t sctp_strict_sacks = SCTPCTL_STRICT_SACKS_DEFAULT;
@@ -102,6 +103,9 @@ uint32_t sctp_mobility_fasthandoff = SCTPCTL_MOBILITY_FASTHANDOFF_DEFAULT;
struct sctp_log sctp_log;
#endif
+uint32_t sctp_udp_tunneling_for_client_enable = SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_DEFAULT;
+uint32_t sctp_udp_tunneling_port = SCTPCTL_UDP_TUNNELING_PORT_DEFAULT;
+
#ifdef SCTP_DEBUG
uint32_t sctp_debug_on = SCTPCTL_DEBUG_DEFAULT;
@@ -493,11 +497,35 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS)
return error;
}
+
+
#define RANGECHK(var, min, max) \
if ((var) < (min)) { (var) = (min); } \
else if ((var) > (max)) { (var) = (max); }
static int
+sysctl_sctp_udp_tunneling_check(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ uint32_t old_sctp_udp_tunneling_port;
+
+ old_sctp_udp_tunneling_port = sctp_udp_tunneling_port;
+ error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req);
+ if (error == 0) {
+ RANGECHK(sctp_udp_tunneling_port, SCTPCTL_UDP_TUNNELING_PORT_MIN, SCTPCTL_UDP_TUNNELING_PORT_MAX);
+ if (old_sctp_udp_tunneling_port) {
+ sctp_over_udp_stop();
+ }
+ if (sctp_udp_tunneling_port) {
+ if (sctp_over_udp_start()) {
+ sctp_udp_tunneling_port = 0;
+ }
+ }
+ }
+ return (error);
+}
+
+static int
sysctl_sctp_check(SYSCTL_HANDLER_ARGS)
{
int error;
@@ -565,6 +593,7 @@ sysctl_sctp_check(SYSCTL_HANDLER_ARGS)
#if defined(__FreeBSD__) || defined(SCTP_APPLE_MOBILITY_FASTHANDOFF)
RANGECHK(sctp_mobility_fasthandoff, SCTPCTL_MOBILITY_FASTHANDOFF_MIN, SCTPCTL_MOBILITY_FASTHANDOFF_MAX);
#endif
+ RANGECHK(sctp_udp_tunneling_for_client_enable, SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_MIN, SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_MAX);
#ifdef SCTP_DEBUG
RANGECHK(sctp_debug_on, SCTPCTL_DEBUG_MIN, SCTPCTL_DEBUG_MAX);
#endif
@@ -808,6 +837,14 @@ SYSCTL_STRUCT(_net_inet_sctp, OID_AUTO, log, CTLFLAG_RD,
"SCTP logging (struct sctp_log)");
#endif
+SYSCTL_PROC(_net_inet_sctp, OID_AUTO, udp_tunneling_for_client_enable, CTLTYPE_INT | CTLFLAG_RW,
+ &sctp_udp_tunneling_for_client_enable, 0, sysctl_sctp_check, "IU",
+ SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_DESC);
+
+SYSCTL_PROC(_net_inet_sctp, OID_AUTO, udp_tunneling_port, CTLTYPE_INT | CTLFLAG_RW,
+ &sctp_udp_tunneling_port, 0, sysctl_sctp_udp_tunneling_check, "IU",
+ SCTPCTL_UDP_TUNNELING_PORT_DESC);
+
#ifdef SCTP_DEBUG
SYSCTL_PROC(_net_inet_sctp, OID_AUTO, debug, CTLTYPE_INT | CTLFLAG_RW,
&sctp_debug_on, 0, sysctl_sctp_check, "IU",
diff --git a/sys/netinet/sctp_sysctl.h b/sys/netinet/sctp_sysctl.h
index 3b93b68..388ebbc 100644
--- a/sys/netinet/sctp_sysctl.h
+++ b/sys/netinet/sctp_sysctl.h
@@ -58,6 +58,12 @@ __FBSDID("$FreeBSD$");
#define SCTPCTL_AUTOASCONF_MAX 1
#define SCTPCTL_AUTOASCONF_DEFAULT SCTP_DEFAULT_AUTO_ASCONF
+/* autoasconf: Enable SCTP Auto-ASCONF */
+#define SCTPCTL_MULTIPLEASCONFS_DESC "Enable SCTP Muliple-ASCONFs"
+#define SCTPCTL_MULTIPLEASCONFS_MIN 0
+#define SCTPCTL_MULTIPLEASCONFS_MAX 1
+#define SCTPCTL_MULTIPLEASCONFS_DEFAULT SCTP_DEFAULT_MULTIPLE_ASCONFS
+
/* ecn_enable: Enable SCTP ECN */
#define SCTPCTL_ECN_ENABLE_DESC "Enable SCTP ECN"
#define SCTPCTL_ECN_ENABLE_MIN 0
@@ -370,6 +376,18 @@ __FBSDID("$FreeBSD$");
#define SCTPCTL_MOBILITY_FASTHANDOFF_MAX 1
#define SCTPCTL_MOBILITY_FASTHANDOFF_DEFAULT SCTP_DEFAULT_MOBILITY_FASTHANDOFF
+/* Enable SCTP/UDP tunneling for clients*/
+#define SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_DESC "Enable SCTP/UDP tunneling for client"
+#define SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_MIN 0
+#define SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_MAX 1
+#define SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_DEFAULT SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_MIN
+
+/* Enable SCTP/UDP tunneling port */
+#define SCTPCTL_UDP_TUNNELING_PORT_DESC "Set the SCTP/UDP tunneling port"
+#define SCTPCTL_UDP_TUNNELING_PORT_MIN 0
+#define SCTPCTL_UDP_TUNNELING_PORT_MAX 65535
+#define SCTPCTL_UDP_TUNNELING_PORT_DEFAULT SCTP_OVER_UDP_TUNNELING_PORT
+
#if defined(SCTP_DEBUG)
/* debug: Configure debug output */
#define SCTPCTL_DEBUG_DESC "Configure debug output"
@@ -388,6 +406,7 @@ __FBSDID("$FreeBSD$");
extern uint32_t sctp_sendspace;
extern uint32_t sctp_recvspace;
extern uint32_t sctp_auto_asconf;
+extern uint32_t sctp_multiple_asconfs;
extern uint32_t sctp_ecn_enable;
extern uint32_t sctp_ecn_nonce;
extern uint32_t sctp_strict_sacks;
@@ -449,6 +468,9 @@ extern uint32_t sctp_mobility_fasthandoff;
extern struct sctp_log sctp_log;
#endif
+extern uint32_t sctp_udp_tunneling_for_client_enable;
+extern uint32_t sctp_udp_tunneling_port;
+
#if defined(SCTP_DEBUG)
extern uint32_t sctp_debug_on;
diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c
index 6f6d188..b6dd318 100644
--- a/sys/netinet/sctp_timer.c
+++ b/sys/netinet/sctp_timer.c
@@ -173,6 +173,11 @@ sctp_audit_retranmission_queue(struct sctp_association *asoc)
sctp_ucount_incr(asoc->sent_queue_retran_cnt);
}
}
+ TAILQ_FOREACH(chk, &asoc->asconf_send_queue, sctp_next) {
+ if (chk->sent == SCTP_DATAGRAM_RESEND) {
+ sctp_ucount_incr(asoc->sent_queue_retran_cnt);
+ }
+ }
SCTPDBG(SCTP_DEBUG_TIMER4, "Audit completes retran:%d onqueue:%d\n",
asoc->sent_queue_retran_cnt,
asoc->sent_queue_cnt);
@@ -1314,10 +1319,10 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_nets *net)
{
struct sctp_nets *alt;
- struct sctp_tmit_chunk *asconf, *chk;
+ struct sctp_tmit_chunk *asconf, *chk, *nchk;
/* is this a first send, or a retransmission? */
- if (stcb->asoc.asconf_sent == 0) {
+ if (TAILQ_EMPTY(&stcb->asoc.asconf_send_queue)) {
/* compose a new ASCONF chunk and send it */
sctp_send_asconf(stcb, net, SCTP_ADDR_NOT_LOCKED);
} else {
@@ -1326,12 +1331,7 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
*/
/* find the existing ASCONF */
- TAILQ_FOREACH(asconf, &stcb->asoc.control_send_queue,
- sctp_next) {
- if (asconf->rec.chunk_id.id == SCTP_ASCONF) {
- break;
- }
- }
+ asconf = TAILQ_FIRST(&stcb->asoc.asconf_send_queue);
if (asconf == NULL) {
return (0);
}
@@ -1359,10 +1359,11 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
*/
sctp_backoff_on_timeout(stcb, asconf->whoTo, 1, 0);
alt = sctp_find_alternate_net(stcb, asconf->whoTo, 0);
- sctp_free_remote_addr(asconf->whoTo);
- asconf->whoTo = alt;
- atomic_add_int(&alt->ref_count, 1);
-
+ if (asconf->whoTo != alt) {
+ sctp_free_remote_addr(asconf->whoTo);
+ asconf->whoTo = alt;
+ atomic_add_int(&alt->ref_count, 1);
+ }
/* See if an ECN Echo is also stranded */
TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
if ((chk->whoTo == net) &&
@@ -1376,17 +1377,32 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
atomic_add_int(&alt->ref_count, 1);
}
}
+ for (chk = asconf; chk; chk = nchk) {
+ nchk = TAILQ_NEXT(chk, sctp_next);
+ if (chk->whoTo != alt) {
+ sctp_free_remote_addr(chk->whoTo);
+ chk->whoTo = alt;
+ atomic_add_int(&alt->ref_count, 1);
+ }
+ if (asconf->sent != SCTP_DATAGRAM_RESEND && chk->sent != SCTP_DATAGRAM_UNSENT)
+ sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt);
+ chk->sent = SCTP_DATAGRAM_RESEND;
+ }
if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) {
/*
* If the address went un-reachable, we need to move
* to the alternate for ALL chunks in queue
*/
sctp_move_all_chunks_to_alt(stcb, net, alt);
+ net = alt;
}
/* mark the retran info */
if (asconf->sent != SCTP_DATAGRAM_RESEND)
sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt);
asconf->sent = SCTP_DATAGRAM_RESEND;
+
+ /* send another ASCONF if any and we can do */
+ sctp_send_asconf(stcb, alt, SCTP_ADDR_NOT_LOCKED);
}
return (0);
}
@@ -1677,16 +1693,11 @@ sctp_pathmtu_timer(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
struct sctp_nets *net)
{
- uint32_t next_mtu;
+ uint32_t next_mtu, mtu;
- /* restart the timer in any case */
next_mtu = sctp_getnext_mtu(inp, net->mtu);
- if (next_mtu <= net->mtu) {
- /* nothing to do */
- return;
- } {
- uint32_t mtu;
+ if ((next_mtu > net->mtu) && (net->port == 0)) {
if ((net->src_addr_selected == 0) ||
(net->ro._s_addr == NULL) ||
(net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED)) {
@@ -1695,10 +1706,26 @@ sctp_pathmtu_timer(struct sctp_inpcb *inp,
net->ro._s_addr = NULL;
net->src_addr_selected = 0;
} else if (net->ro._s_addr == NULL) {
+#if defined(INET6) && defined(SCTP_EMBEDDED_V6_SCOPE)
+ if (net->ro._l_addr.sa.sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+
+ /* KAME hack: embed scopeid */
+ (void)sa6_embedscope(sin6, ip6_use_defzone);
+ }
+#endif
+
net->ro._s_addr = sctp_source_address_selection(inp,
stcb,
(sctp_route_t *) & net->ro,
net, 0, stcb->asoc.vrf_id);
+#if defined(INET6) && defined(SCTP_EMBEDDED_V6_SCOPE)
+ if (net->ro._l_addr.sa.sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+
+ (void)sa6_recoverscope(sin6);
+ }
+#endif /* INET6 */
}
if (net->ro._s_addr)
net->src_addr_selected = 1;
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 1be19f9..c1a1acd 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -102,7 +102,7 @@ sctp_pcbinfo_cleanup(void)
}
-static void
+void
sctp_pathmtu_adjustment(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
struct sctp_nets *net,
diff --git a/sys/netinet/sctp_var.h b/sys/netinet/sctp_var.h
index cae00b0..21af7d1 100644
--- a/sys/netinet/sctp_var.h
+++ b/sys/netinet/sctp_var.h
@@ -300,7 +300,9 @@ int sctp_disconnect(struct socket *so);
void sctp_ctlinput __P((int, struct sockaddr *, void *));
int sctp_ctloutput __P((struct socket *, struct sockopt *));
+void sctp_input_with_port __P((struct mbuf *, int, uint16_t));
void sctp_input __P((struct mbuf *, int));
+void sctp_pathmtu_adjustment __P((struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *, uint16_t));
void sctp_drain __P((void));
void sctp_init __P((void));
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index aab1510..05082ac 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -956,6 +956,7 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb,
asoc->assoc_id = asoc->my_vtag;
asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number = asoc->sending_seq =
sctp_select_initial_TSN(&m->sctp_ep);
+ asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
/* we are optimisitic here */
asoc->peer_supports_pktdrop = 1;
@@ -1151,6 +1152,7 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb,
TAILQ_INIT(&asoc->free_chunks);
TAILQ_INIT(&asoc->out_wheel);
TAILQ_INIT(&asoc->control_send_queue);
+ TAILQ_INIT(&asoc->asconf_send_queue);
TAILQ_INIT(&asoc->send_queue);
TAILQ_INIT(&asoc->sent_queue);
TAILQ_INIT(&asoc->reasmqueue);
@@ -3792,7 +3794,7 @@ sctp_abort_notification(struct sctp_tcb *stcb, int error, int so_locked
void
sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct mbuf *m, int iphlen, struct sctphdr *sh, struct mbuf *op_err,
- uint32_t vrf_id)
+ uint32_t vrf_id, uint16_t port)
{
uint32_t vtag;
@@ -3810,7 +3812,7 @@ sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
vrf_id = stcb->asoc.vrf_id;
stcb->asoc.state |= SCTP_STATE_WAS_ABORTED;
}
- sctp_send_abort(m, iphlen, sh, vtag, op_err, vrf_id);
+ sctp_send_abort(m, iphlen, sh, vtag, op_err, vrf_id, port);
if (stcb != NULL) {
/* Ok, now lets free it */
#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
@@ -3967,7 +3969,7 @@ sctp_abort_an_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
void
sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
- struct sctp_inpcb *inp, struct mbuf *op_err, uint32_t vrf_id)
+ struct sctp_inpcb *inp, struct mbuf *op_err, uint32_t vrf_id, uint16_t port)
{
struct sctp_chunkhdr *ch, chunk_buf;
unsigned int chk_length;
@@ -4005,7 +4007,7 @@ sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
*/
return;
case SCTP_SHUTDOWN_ACK:
- sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id);
+ sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, port);
return;
default:
break;
@@ -4014,7 +4016,7 @@ sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
sizeof(*ch), (uint8_t *) & chunk_buf);
}
- sctp_send_abort(m, iphlen, sh, 0, op_err, vrf_id);
+ sctp_send_abort(m, iphlen, sh, 0, op_err, vrf_id, port);
}
/*
@@ -4140,8 +4142,8 @@ sctp_cmpaddr(struct sockaddr *sa1, struct sockaddr *sa2)
sin6_1 = (struct sockaddr_in6 *)sa1;
sin6_2 = (struct sockaddr_in6 *)sa2;
- return (SCTP6_ARE_ADDR_EQUAL(&sin6_1->sin6_addr,
- &sin6_2->sin6_addr));
+ return (SCTP6_ARE_ADDR_EQUAL(sin6_1,
+ sin6_2));
}
#endif
case AF_INET:
@@ -4776,8 +4778,8 @@ sctp_find_ifa_in_ep(struct sctp_inpcb *inp, struct sockaddr *addr,
}
#ifdef INET6
if (addr->sa_family == AF_INET6) {
- if (SCTP6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)addr)->sin6_addr,
- &laddr->ifa->address.sin6.sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL((struct sockaddr_in6 *)addr,
+ &laddr->ifa->address.sin6)) {
/* found him. */
if (holds_lock == 0) {
SCTP_INP_RUNLOCK(inp);
@@ -4866,8 +4868,8 @@ sctp_find_ifa_by_addr(struct sockaddr *addr, uint32_t vrf_id, int holds_lock)
}
#ifdef INET6
if (addr->sa_family == AF_INET6) {
- if (SCTP6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)addr)->sin6_addr,
- &sctp_ifap->address.sin6.sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL((struct sockaddr_in6 *)addr,
+ &sctp_ifap->address.sin6)) {
/* found him. */
if (holds_lock == 0)
SCTP_IPI_ADDR_RUNLOCK();
@@ -6605,3 +6607,19 @@ sctp_log_trace(uint32_t subsys, const char *str SCTP_UNUSED, uint32_t a, uint32_
}
#endif
+/* We will need to add support
+ * to bind the ports and such here
+ * so we can do UDP tunneling. In
+ * the mean-time, we return error
+ */
+
+void
+sctp_over_udp_stop(void)
+{
+ return;
+}
+int
+sctp_over_udp_start(void)
+{
+ return (-1);
+}
diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h
index ec4631c..3fd384e 100644
--- a/sys/netinet/sctputil.h
+++ b/sys/netinet/sctputil.h
@@ -178,7 +178,7 @@ sctp_abort_notification(struct sctp_tcb *, int, int
/* We abort responding to an IP packet for some reason */
void
sctp_abort_association(struct sctp_inpcb *, struct sctp_tcb *,
- struct mbuf *, int, struct sctphdr *, struct mbuf *, uint32_t);
+ struct mbuf *, int, struct sctphdr *, struct mbuf *, uint32_t, uint16_t);
/* We choose to abort via user input */
@@ -192,7 +192,7 @@ sctp_abort_an_association(struct sctp_inpcb *, struct sctp_tcb *, int,
void
sctp_handle_ootb(struct mbuf *, int, int, struct sctphdr *,
- struct sctp_inpcb *, struct mbuf *, uint32_t);
+ struct sctp_inpcb *, struct mbuf *, uint32_t, uint16_t);
int
sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr,
@@ -314,6 +314,9 @@ do { \
} \
} while (0)
+/* new functions to start/stop udp tunneling */
+void sctp_over_udp_stop(void);
+int sctp_over_udp_start(void);
int
sctp_soreceive(struct socket *so, struct sockaddr **psa,
diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c
index 65372da..8798092 100644
--- a/sys/netinet6/sctp6_usrreq.c
+++ b/sys/netinet6/sctp6_usrreq.c
@@ -177,14 +177,14 @@ sctp_skip_csum:
sh->v_tag = 0;
}
if (ch->chunk_type == SCTP_SHUTDOWN_ACK) {
- sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id);
+ sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, 0);
goto bad;
}
if (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) {
goto bad;
}
if (ch->chunk_type != SCTP_ABORT_ASSOCIATION)
- sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id);
+ sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id, 0);
goto bad;
} else if (stcb == NULL) {
refcount_up = 1;
@@ -212,7 +212,7 @@ sctp_skip_csum:
/* sa_ignore NO_NULL_CHK */
sctp_common_input_processing(&m, iphlen, offset, length, sh, ch,
- in6p, stcb, net, ecn_bits, vrf_id);
+ in6p, stcb, net, ecn_bits, vrf_id, 0);
/* inp's ref-count reduced && stcb unlocked */
/* XXX this stuff below gets moved to appropriate parts later... */
if (m)
OpenPOWER on IntegriCloud