diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index 959865ef1db0..295204a85449 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 e3bb19f1fd85..a1df7e6f01c2 100644 --- a/sys/netinet/sctp_asconf.c +++ b/sys/netinet/sctp_asconf.c @@ -930,6 +930,56 @@ sctp_asconf_addr_match(struct sctp_asconf_addr *aa, struct sockaddr *sa) return (0); } +/* + * 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 */ @@ -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 ca57c00324bb..12f1281998db 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 3e22a54879c1..a4ada89c5e88 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 268ff68b747d..9f628b858949 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 bb74b7b5f6f2..424d54c798d7 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 4c515f663419..694d8caec979 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, ¬ification, - 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 01a67c60d6f8..2a2897054bca 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 eeaca5d2efa0..95f3ed45b6bc 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 01c0fcb0d469..2d01952ad815 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 8fa1846ce61d..3272ab88553b 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 d37c163dd6a4..f0961b85a1ec 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 7ad3f7f95aac..6f540d1c414d 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; + struct sockaddr_in6 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)); + 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 44ac300a4fc4..9b4df183bd7a 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 69b6f921f91b..8beb3bfb6fac 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 b42802938153..abf3f9e5e082 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,10 +497,34 @@ 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) { @@ -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 3b93b68ee857..388ebbc42503 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 6f6d188b92ae..b6dd3182409e 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 1be19f9b7c52..c1a1acd1493b 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 cae00b0ad077..21af7d1c4575 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 aab1510d7c17..05082acd05f1 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 ec4631c7a67f..3fd384e55d50 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 65372da6c021..879809290a60 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)