summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r--sys/netinet/sctp_output.c658
1 files changed, 527 insertions, 131 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 8fa1846..3272ab8 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_indata.h>
#include <netinet/sctp_bsd_addr.h>
#include <netinet/sctp_input.h>
+#include <netinet/udp.h>
@@ -2211,12 +2212,21 @@ sctp_is_ifa_addr_preferred(struct sctp_ifa *ifa,
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &ifa->address.sa);
/* Ok the address may be ok */
if (fam == AF_INET6) {
- /* ok to use deprecated addresses? */
+ /* ok to use deprecated addresses? no lets not! */
if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:1\n");
return (NULL);
}
- if (ifa->src_is_priv) {
+ if (ifa->src_is_priv && ifa->src_is_loop) {
+ /*
+ * don't allow fe80::1 to be a src on loop ::1, we
+ * don't list it to the peer so we will get an
+ * abort.
+ */
+ SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:2a\n");
+ return (NULL);
+ }
+ if (ifa->src_is_priv && !ifa->src_is_loop) {
if (dest_is_loop) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:2\n");
return (NULL);
@@ -2532,8 +2542,11 @@ sctp_choose_boundspecific_stcb(struct sctp_inpcb *inp,
sifa = sctp_is_ifa_addr_preferred(sctp_ifa, dest_is_loop, dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2549,8 +2562,11 @@ sctp_choose_boundspecific_stcb(struct sctp_inpcb *inp,
sifa = sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop, dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2584,8 +2600,11 @@ sctp_from_the_top:
sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop, dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2620,8 +2639,11 @@ sctp_from_the_top2:
dest_is_priv, fam);
if (sifa == NULL)
continue;
- if ((non_asoc_addr_ok == 0) &&
- (sctp_is_addr_restricted(stcb, sifa))) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/* on the no-no list */
continue;
}
@@ -2650,6 +2672,14 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn,
struct sctp_ifa *ifa, *sifa;
int num_eligible_addr = 0;
+#ifdef INET6
+ struct sockaddr_in6 sin6, lsa6;
+
+ if (fam == AF_INET6) {
+ memcpy(&sin6, &ro->ro_dst, sizeof(struct sockaddr_in6));
+ (void)sa6_recoverscope(&sin6);
+ }
+#endif /* INET6 */
LIST_FOREACH(ifa, &ifn->ifalist, next_ifa) {
if ((ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
(non_asoc_addr_ok == 0))
@@ -2658,6 +2688,29 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn,
dest_is_priv, fam);
if (sifa == NULL)
continue;
+#ifdef INET6
+ if (fam == AF_INET6 &&
+ IN6_IS_ADDR_LINKLOCAL(&sifa->address.sin6.sin6_addr) &&
+ IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
+ /*
+ * link-local <-> link-local must belong to the same
+ * scope.
+ */
+ memcpy(&lsa6, &sifa->address.sin6, sizeof(struct sockaddr_in6));
+ (void)sa6_recoverscope(&lsa6);
+ if (sin6.sin6_scope_id != lsa6.sin6_scope_id) {
+ continue;
+ }
+ if (dest_is_loop) {
+ /*
+ * we don't give out fe80::1, we must use
+ * ::1
+ */
+ continue;
+ }
+ }
+#endif /* INET6 */
+
/*
* Check if the IPv6 address matches to next-hop. In the
* mobile case, old IPv6 address may be not deleted from the
@@ -2682,8 +2735,11 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn,
}
}
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some reason..
* probably not yet added.
@@ -2722,8 +2778,11 @@ sctp_count_num_preferred_boundall(struct sctp_ifn *ifn,
continue;
}
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some reason..
* probably not yet added.
@@ -2899,8 +2958,11 @@ bound_all_plan_b:
if (sifa == NULL)
continue;
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some reason..
* probably not yet added.
@@ -2938,8 +3000,11 @@ plan_d:
if (sifa == NULL)
continue;
if (stcb) {
- if ((non_asoc_addr_ok == 0) &&
- sctp_is_addr_restricted(stcb, sifa)) {
+ if (((non_asoc_addr_ok == 0) &&
+ (sctp_is_addr_restricted(stcb, sifa))) ||
+ (non_asoc_addr_ok &&
+ (sctp_is_addr_restricted(stcb, sifa)) &&
+ (!sctp_is_addr_pending(stcb, sifa)))) {
/*
* It is restricted for some
* reason.. probably not yet added.
@@ -3056,25 +3121,26 @@ sctp_source_address_selection(struct sctp_inpcb *inp,
switch (fam) {
case AF_INET:
/* Scope based on outbound address */
- if ((IN4_ISPRIVATE_ADDRESS(&to->sin_addr))) {
- dest_is_priv = 1;
- } else if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) {
+ if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) {
dest_is_loop = 1;
if (net != NULL) {
/* mark it as local */
net->addr_is_local = 1;
}
+ } else if ((IN4_ISPRIVATE_ADDRESS(&to->sin_addr))) {
+ dest_is_priv = 1;
}
break;
#ifdef INET6
case AF_INET6:
/* Scope based on outbound address */
- if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr)) {
+ if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr) ||
+ SCTP_ROUTE_IS_REAL_LOOP(ro)) {
/*
- * If the route goes to the loopback address OR the
- * address is a loopback address, we are loopback
- * scope. But we don't use dest_is_priv (link local
- * addresses).
+ * If the address is a loopback address, which
+ * consists of "::1" OR "fe80::1%lo0", we are
+ * loopback scope. But we don't use dest_is_priv
+ * (link local addresses).
*/
dest_is_loop = 1;
if (net != NULL) {
@@ -3345,6 +3411,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
int ecn_ok,
struct sctp_tmit_chunk *chk,
int out_of_asoc_ok,
+ uint16_t port,
int so_locked
#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
SCTP_UNUSED
@@ -3371,6 +3438,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
int ret;
uint32_t vrf_id;
sctp_route_t *ro = NULL;
+ struct udphdr *udp;
if ((net) && (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)) {
SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EFAULT);
@@ -3411,15 +3479,25 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
sctp_route_t iproute;
uint8_t tos_value;
- newm = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ }
if (newm == NULL) {
sctp_m_freem(m);
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
- SCTP_ALIGN_TO_END(newm, sizeof(struct ip));
- SCTP_BUF_LEN(newm) = sizeof(struct ip);
- packet_length += sizeof(struct ip);
+ if (port) {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip) + sizeof(struct udphdr));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip) + sizeof(struct udphdr);
+ packet_length += sizeof(struct ip) + sizeof(struct udphdr);
+ } else {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip);
+ packet_length += sizeof(struct ip);
+ }
SCTP_BUF_NEXT(newm) = m;
m = newm;
ip = mtod(m, struct ip *);
@@ -3430,7 +3508,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
tos_value = inp->ip_inp.inp.inp_ip_tos;
}
- if (nofragment_flag) {
+ if ((nofragment_flag) && (port == 0)) {
#if defined(WITH_CONVERT_IP_OFF) || defined(__FreeBSD__) || defined(__APPLE__)
ip->ip_off = IP_DF;
#else
@@ -3456,7 +3534,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
/* no association at all */
ip->ip_tos = (tos_value & 0xfc);
}
- ip->ip_p = IPPROTO_SCTP;
+ if (port) {
+ ip->ip_p = IPPROTO_UDP;
+ } else {
+ ip->ip_p = IPPROTO_SCTP;
+ }
ip->ip_sum = 0;
if (net == NULL) {
ro = &iproute;
@@ -3469,7 +3551,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
ip->ip_dst.s_addr = ((struct sockaddr_in *)to)->sin_addr.s_addr;
/* call the routine to select the src address */
- if (net) {
+ if (net && out_of_asoc_ok == 0) {
if (net->ro._s_addr && (net->ro._s_addr->localifa_flags & (SCTP_BEING_DELETED | SCTP_ADDR_IFA_UNUSEABLE))) {
sctp_free_ifa(net->ro._s_addr);
net->ro._s_addr = NULL;
@@ -3480,13 +3562,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
}
}
if (net->src_addr_selected == 0) {
- if (out_of_asoc_ok) {
- /* do not cache */
- goto temp_v4_src;
- }
/* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp, stcb,
- ro, net, out_of_asoc_ok,
+ ro, net, 0,
vrf_id);
net->src_addr_selected = 1;
}
@@ -3499,7 +3577,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
struct sctp_ifa *_lsrc;
- temp_v4_src:
_lsrc = sctp_source_address_selection(inp, stcb, ro,
net,
out_of_asoc_ok,
@@ -3510,7 +3587,13 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
ip->ip_src = _lsrc->address.sin.sin_addr;
sctp_free_ifa(_lsrc);
}
-
+ if (port) {
+ udp = (struct udphdr *)(ip + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(packet_length - sizeof(struct ip));
+ udp->uh_sum = 0;
+ }
/*
* If source address selection fails and we find no route
* then the ip_output should fail as well with a
@@ -3685,15 +3768,25 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
flowlabel = ((struct in6pcb *)inp)->in6p_flowinfo;
}
- newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ }
if (newm == NULL) {
sctp_m_freem(m);
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
- SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr));
- SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr);
- packet_length += sizeof(struct ip6_hdr);
+ if (port) {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
+ packet_length += sizeof(struct ip6_hdr) + sizeof(struct udphdr);
+ } else {
+ SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr));
+ SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr);
+ packet_length += sizeof(struct ip6_hdr);
+ }
SCTP_BUF_NEXT(newm) = m;
m = newm;
@@ -3735,7 +3828,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4);
}
ip6h->ip6_flow = htonl(((tosTop << 24) | ((tosBottom | flowTop) << 16) | flowBottom));
- ip6h->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ ip6h->ip6_nxt = IPPROTO_UDP;
+ } else {
+ ip6h->ip6_nxt = IPPROTO_SCTP;
+ }
ip6h->ip6_plen = (packet_length - sizeof(struct ip6_hdr));
ip6h->ip6_dst = sin6->sin6_addr;
@@ -3748,7 +3845,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
lsa6_tmp.sin6_family = AF_INET6;
lsa6_tmp.sin6_len = sizeof(lsa6_tmp);
lsa6 = &lsa6_tmp;
- if (net) {
+ if (net && out_of_asoc_ok == 0) {
if (net->ro._s_addr && (net->ro._s_addr->localifa_flags & (SCTP_BEING_DELETED | SCTP_ADDR_IFA_UNUSEABLE))) {
sctp_free_ifa(net->ro._s_addr);
net->ro._s_addr = NULL;
@@ -3759,17 +3856,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
}
}
if (net->src_addr_selected == 0) {
- if (out_of_asoc_ok) {
- /* do not cache */
- goto temp_v6_src;
+ sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+ /* KAME hack: embed scopeid */
+ if (sa6_embedscope(sin6, ip6_use_defzone) != 0) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
+ return (EINVAL);
}
/* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp,
stcb,
ro,
net,
- out_of_asoc_ok,
+ 0,
vrf_id);
+ (void)sa6_recoverscope(sin6);
net->src_addr_selected = 1;
}
if (net->ro._s_addr == NULL) {
@@ -3781,11 +3881,17 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
struct sctp_ifa *_lsrc;
- temp_v6_src:
+ sin6 = (struct sockaddr_in6 *)&ro->ro_dst;
+ /* KAME hack: embed scopeid */
+ if (sa6_embedscope(sin6, ip6_use_defzone) != 0) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
+ return (EINVAL);
+ }
_lsrc = sctp_source_address_selection(inp, stcb, ro,
net,
out_of_asoc_ok,
vrf_id);
+ (void)sa6_recoverscope(sin6);
if (_lsrc == NULL) {
goto no_route;
}
@@ -3809,6 +3915,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
bzero(&lsa6_storage, sizeof(lsa6_storage));
lsa6_storage.sin6_family = AF_INET6;
lsa6_storage.sin6_len = sizeof(lsa6_storage);
+ lsa6_storage.sin6_addr = lsa6->sin6_addr;
if ((error = sa6_recoverscope(&lsa6_storage)) != 0) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "recover scope fails error %d\n", error);
sctp_m_freem(m);
@@ -3820,6 +3927,13 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
lsa6 = &lsa6_storage;
ip6h->ip6_src = lsa6->sin6_addr;
+ if (port) {
+ udp = (struct udphdr *)(ip6h + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(packet_length - sizeof(struct ip6_hdr));
+ udp->uh_sum = 0;
+ }
/*
* We set the hop limit now since there is a good chance
* that our ro pointer is now filled
@@ -4208,7 +4322,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT - calls lowlevel_output\n");
ret = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr,
- m, 0, NULL, 0, 0, NULL, 0, so_locked);
+ m, 0, NULL, 0, 0, NULL, 0, net->port, so_locked);
SCTPDBG(SCTP_DEBUG_OUTPUT4, "lowlevel_output - %d\n", ret);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
@@ -4629,8 +4743,8 @@ sctp_are_there_new_addresses(struct sctp_association *asoc,
#ifdef INET6
if (sa->sa_family == AF_INET6) {
sa6 = (struct sockaddr_in6 *)sa;
- if (SCTP6_ARE_ADDR_EQUAL(&sa6->sin6_addr,
- &sin6.sin6_addr)) {
+ if (SCTP6_ARE_ADDR_EQUAL(sa6,
+ &sin6)) {
fnd = 1;
break;
}
@@ -4699,7 +4813,7 @@ sctp_are_there_new_addresses(struct sctp_association *asoc,
if (sa->sa_family == AF_INET6) {
sa6 = (struct sockaddr_in6 *)sa;
if (SCTP6_ARE_ADDR_EQUAL(
- &sa6->sin6_addr, &sin6.sin6_addr)) {
+ sa6, &sin6)) {
fnd = 1;
break;
}
@@ -4726,7 +4840,7 @@ sctp_are_there_new_addresses(struct sctp_association *asoc,
void
sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct mbuf *init_pkt, int iphlen, int offset, struct sctphdr *sh,
- struct sctp_init_chunk *init_chk, uint32_t vrf_id, int hold_inp_lock)
+ struct sctp_init_chunk *init_chk, uint32_t vrf_id, uint16_t port, int hold_inp_lock)
{
struct sctp_association *asoc;
struct mbuf *m, *m_at, *m_tmp, *m_cookie, *op_err, *mp_last;
@@ -4774,7 +4888,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
* though we even set the T bit and copy in the 0 tag.. this
* looks no different than if no listener was present.
*/
- sctp_send_abort(init_pkt, iphlen, sh, 0, NULL, vrf_id);
+ sctp_send_abort(init_pkt, iphlen, sh, 0, NULL, vrf_id, port);
return;
}
abort_flag = 0;
@@ -4783,7 +4897,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
&abort_flag, (struct sctp_chunkhdr *)init_chk);
if (abort_flag) {
sctp_send_abort(init_pkt, iphlen, sh,
- init_chk->init.initiate_tag, op_err, vrf_id);
+ init_chk->init.initiate_tag, op_err, vrf_id, port);
return;
}
m = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA);
@@ -4926,6 +5040,13 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
stc.addr_type = SCTP_IPV6_ADDRESS;
stc.scope_id = 0;
if (sctp_is_address_on_local_host((struct sockaddr *)sin6, vrf_id)) {
+ /*
+ * FIX ME: does this have scope from
+ * rcvif?
+ */
+ (void)sa6_recoverscope(sin6);
+ stc.scope_id = sin6->sin6_scope_id;
+ sa6_embedscope(sin6, ip6_use_defzone);
stc.loopback_scope = 1;
stc.local_scope = 0;
stc.site_scope = 1;
@@ -4960,9 +5081,8 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
* rcvif?
*/
(void)sa6_recoverscope(sin6);
-
- sa6_embedscope(sin6, ip6_use_defzone);
stc.scope_id = sin6->sin6_scope_id;
+ sa6_embedscope(sin6, ip6_use_defzone);
} else if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
/*
* If the new destination is
@@ -5374,7 +5494,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
p_len += padval;
}
(void)sctp_lowlevel_chunk_output(inp, NULL, NULL, to, m, 0, NULL, 0, 0,
- NULL, 0, SCTP_SO_NOT_LOCKED);
+ NULL, 0, port, SCTP_SO_NOT_LOCKED);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
}
@@ -6243,15 +6363,23 @@ sctp_toss_old_asconf(struct sctp_tcb *stcb)
{
struct sctp_association *asoc;
struct sctp_tmit_chunk *chk, *chk_tmp;
+ struct sctp_asconf_chunk *acp;
asoc = &stcb->asoc;
- for (chk = TAILQ_FIRST(&asoc->control_send_queue); chk != NULL;
+ for (chk = TAILQ_FIRST(&asoc->asconf_send_queue); chk != NULL;
chk = chk_tmp) {
/* get next chk */
chk_tmp = TAILQ_NEXT(chk, sctp_next);
- /* find SCTP_ASCONF chunk in queue (only one ever in queue) */
+ /* find SCTP_ASCONF chunk in queue */
if (chk->rec.chunk_id.id == SCTP_ASCONF) {
- TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+ if (chk->data) {
+ acp = mtod(chk->data, struct sctp_asconf_chunk *);
+ if (compare_with_wrap(ntohl(acp->serial_number), stcb->asoc.asconf_seq_out_acked, MAX_SEQ)) {
+ /* Not Acked yet */
+ break;
+ }
+ }
+ TAILQ_REMOVE(&asoc->asconf_send_queue, chk, sctp_next);
if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
@@ -7191,6 +7319,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
/* Nothing to possible to send? */
if (TAILQ_EMPTY(&asoc->control_send_queue) &&
+ TAILQ_EMPTY(&asoc->asconf_send_queue) &&
TAILQ_EMPTY(&asoc->send_queue) &&
TAILQ_EMPTY(&asoc->out_wheel)) {
*reason_code = 9;
@@ -7305,12 +7434,15 @@ skip_the_fill_from_streams:
/* now service each destination and send out what we can for it */
/* Nothing to send? */
if ((TAILQ_FIRST(&asoc->control_send_queue) == NULL) &&
+ (TAILQ_FIRST(&asoc->asconf_send_queue) == NULL) &&
(TAILQ_FIRST(&asoc->send_queue) == NULL)) {
*reason_code = 8;
return (0);
}
if (no_data_chunks) {
- chk = TAILQ_FIRST(&asoc->control_send_queue);
+ chk = TAILQ_FIRST(&asoc->asconf_send_queue);
+ if (chk == NULL)
+ chk = TAILQ_FIRST(&asoc->control_send_queue);
} else {
chk = TAILQ_FIRST(&asoc->send_queue);
}
@@ -7390,6 +7522,194 @@ again_one_more_time:
r_mtu = mtu;
}
/************************/
+ /* ASCONF transmission */
+ /************************/
+ /* Now first lets go through the asconf queue */
+ for (chk = TAILQ_FIRST(&asoc->asconf_send_queue);
+ chk; chk = nchk) {
+ nchk = TAILQ_NEXT(chk, sctp_next);
+ if (chk->rec.chunk_id.id != SCTP_ASCONF) {
+ continue;
+ }
+ if (chk->whoTo != net) {
+ /*
+ * No, not sent to the network we are
+ * looking at
+ */
+ break;
+ }
+ if (chk->data == NULL) {
+ break;
+ }
+ if (chk->sent != SCTP_DATAGRAM_UNSENT &&
+ chk->sent != SCTP_DATAGRAM_RESEND) {
+ break;
+ }
+ /*
+ * if no AUTH is yet included and this chunk
+ * requires it, make sure to account for it. We
+ * don't apply the size until the AUTH chunk is
+ * actually added below in case there is no room for
+ * this chunk. NOTE: we overload the use of "omtu"
+ * here
+ */
+ if ((auth == NULL) &&
+ sctp_auth_is_required_chunk(chk->rec.chunk_id.id,
+ stcb->asoc.peer_auth_chunks)) {
+ omtu = sctp_get_auth_chunk_len(stcb->asoc.peer_hmac_id);
+ } else
+ omtu = 0;
+ /* Here we do NOT factor the r_mtu */
+ if ((chk->send_size < (int)(mtu - omtu)) ||
+ (chk->flags & CHUNK_FLAGS_FRAGMENT_OK)) {
+ /*
+ * We probably should glom the mbuf chain
+ * from the chk->data for control but the
+ * problem is it becomes yet one more level
+ * of tracking to do if for some reason
+ * output fails. Then I have got to
+ * reconstruct the merged control chain.. el
+ * yucko.. for now we take the easy way and
+ * do the copy
+ */
+ /*
+ * Add an AUTH chunk, if chunk requires it
+ * save the offset into the chain for AUTH
+ */
+ if ((auth == NULL) &&
+ (sctp_auth_is_required_chunk(chk->rec.chunk_id.id,
+ stcb->asoc.peer_auth_chunks))) {
+ outchain = sctp_add_auth_chunk(outchain,
+ &endoutchain,
+ &auth,
+ &auth_offset,
+ stcb,
+ chk->rec.chunk_id.id);
+ SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
+ }
+ outchain = sctp_copy_mbufchain(chk->data, outchain, &endoutchain,
+ (int)chk->rec.chunk_id.can_take_data,
+ chk->send_size, chk->copy_by_ref);
+ if (outchain == NULL) {
+ *reason_code = 8;
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
+ return (ENOMEM);
+ }
+ SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
+ /* update our MTU size */
+ if (mtu > (chk->send_size + omtu))
+ mtu -= (chk->send_size + omtu);
+ else
+ mtu = 0;
+ to_out += (chk->send_size + omtu);
+ /* Do clear IP_DF ? */
+ if (chk->flags & CHUNK_FLAGS_FRAGMENT_OK) {
+ no_fragmentflg = 0;
+ }
+ if (chk->rec.chunk_id.can_take_data)
+ chk->data = NULL;
+ /*
+ * set hb flag since we can use these for
+ * RTO
+ */
+ hbflag = 1;
+ asconf = 1;
+ /*
+ * should sysctl this: don't bundle data
+ * with ASCONF since it requires AUTH
+ */
+ no_data_chunks = 1;
+ chk->sent = SCTP_DATAGRAM_SENT;
+ chk->snd_count++;
+ if (mtu == 0) {
+ /*
+ * Ok we are out of room but we can
+ * output without effecting the
+ * flight size since this little guy
+ * is a control only packet.
+ */
+ sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net);
+ /*
+ * do NOT clear the asconf flag as
+ * it is used to do appropriate
+ * source address selection.
+ */
+ SCTP_BUF_PREPEND(outchain, sizeof(struct sctphdr), M_DONTWAIT);
+ if (outchain == NULL) {
+ /* no memory */
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
+ error = ENOBUFS;
+ *reason_code = 7;
+ continue;
+ }
+ shdr = mtod(outchain, struct sctphdr *);
+ shdr->src_port = inp->sctp_lport;
+ shdr->dest_port = stcb->rport;
+ shdr->v_tag = htonl(stcb->asoc.peer_vtag);
+ shdr->checksum = 0;
+ auth_offset += sizeof(struct sctphdr);
+ if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
+ (struct sockaddr *)&net->ro._l_addr,
+ outchain, auth_offset, auth,
+ no_fragmentflg, 0, NULL, asconf, net->port, so_locked))) {
+ if (error == ENOBUFS) {
+ asoc->ifp_had_enobuf = 1;
+ SCTP_STAT_INCR(sctps_lowlevelerr);
+ }
+ if (from_where == 0) {
+ SCTP_STAT_INCR(sctps_lowlevelerrusr);
+ }
+ if (*now_filled == 0) {
+ (void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+ *now_filled = 1;
+ *now = net->last_sent_time;
+ } else {
+ net->last_sent_time = *now;
+ }
+ hbflag = 0;
+ /* error, could not output */
+ if (error == EHOSTUNREACH) {
+ /*
+ * Destination went
+ * unreachable
+ * during this send
+ */
+ sctp_move_to_an_alt(stcb, asoc, net);
+ }
+ *reason_code = 7;
+ continue;
+ } else
+ asoc->ifp_had_enobuf = 0;
+ if (*now_filled == 0) {
+ (void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+ *now_filled = 1;
+ *now = net->last_sent_time;
+ } else {
+ net->last_sent_time = *now;
+ }
+ hbflag = 0;
+ /*
+ * increase the number we sent, if a
+ * cookie is sent we don't tell them
+ * any was sent out.
+ */
+ outchain = endoutchain = NULL;
+ auth = NULL;
+ auth_offset = 0;
+ if (!no_out_cnt)
+ *num_out += ctl_cnt;
+ /* recalc a clean slate and setup */
+ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+ mtu = (net->mtu - SCTP_MIN_OVERHEAD);
+ } else {
+ mtu = (net->mtu - SCTP_MIN_V4_OVERHEAD);
+ }
+ to_out = 0;
+ no_fragmentflg = 1;
+ }
+ }
+ }
+ /************************/
/* Control transmission */
/************************/
/* Now first lets go through the control queue */
@@ -7510,27 +7830,14 @@ again_one_more_time:
} else {
/*
* Other chunks, since they have
- * timers running (i.e. COOKIE or
- * ASCONF) we just "trust" that it
- * gets sent or retransmitted.
+ * timers running (i.e. COOKIE) we
+ * just "trust" that it gets sent or
+ * retransmitted.
*/
ctl_cnt++;
if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) {
cookie = 1;
no_out_cnt = 1;
- } else if (chk->rec.chunk_id.id == SCTP_ASCONF) {
- /*
- * set hb flag since we can
- * use these for RTO
- */
- hbflag = 1;
- asconf = 1;
- /*
- * should sysctl this: don't
- * bundle data with ASCONF
- * since it requires AUTH
- */
- no_data_chunks = 1;
}
chk->sent = SCTP_DATAGRAM_SENT;
chk->snd_count++;
@@ -7571,7 +7878,7 @@ again_one_more_time:
if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr,
outchain, auth_offset, auth,
- no_fragmentflg, 0, NULL, asconf, so_locked))) {
+ no_fragmentflg, 0, NULL, asconf, net->port, so_locked))) {
if (error == ENOBUFS) {
asoc->ifp_had_enobuf = 1;
SCTP_STAT_INCR(sctps_lowlevelerr);
@@ -7860,7 +8167,7 @@ again_one_more_time:
no_fragmentflg,
bundle_at,
data_list[0],
- asconf, so_locked))) {
+ asconf, net->port, so_locked))) {
/* error, we could not output */
if (error == ENOBUFS) {
SCTP_STAT_INCR(sctps_lowlevelerr);
@@ -8332,16 +8639,20 @@ sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net, int addr_locked)
*/
struct sctp_tmit_chunk *chk;
struct mbuf *m_asconf;
- struct sctp_asconf_chunk *acp;
int len;
SCTP_TCB_LOCK_ASSERT(stcb);
+
+ if ((!TAILQ_EMPTY(&stcb->asoc.asconf_send_queue)) &&
+ (!sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS))) {
+ /* can't send a new one if there is one in flight already */
+ return;
+ }
/* compose an ASCONF chunk, maximum length is PMTU */
m_asconf = sctp_compose_asconf(stcb, &len, addr_locked);
if (m_asconf == NULL) {
return;
}
- acp = mtod(m_asconf, struct sctp_asconf_chunk *);
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
/* no memory */
@@ -8355,11 +8666,11 @@ sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net, int addr_locked)
chk->rec.chunk_id.can_take_data = 0;
chk->sent = SCTP_DATAGRAM_UNSENT;
chk->snd_count = 0;
- chk->flags = 0;
+ chk->flags = CHUNK_FLAGS_FRAGMENT_OK;
chk->asoc = &stcb->asoc;
- chk->whoTo = chk->asoc->primary_destination;
+ chk->whoTo = net;
atomic_add_int(&chk->whoTo->ref_count, 1);
- TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+ TAILQ_INSERT_TAIL(&chk->asoc->asconf_send_queue, chk, sctp_next);
chk->asoc->ctrl_queue_cnt++;
return;
}
@@ -8482,7 +8793,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
struct sctp_tmit_chunk *chk, *fwd;
struct mbuf *m, *endofchain;
struct sctphdr *shdr;
- int asconf;
struct sctp_nets *net = NULL;
uint32_t tsns_sent = 0;
int no_fragmentflg, bundle_at, cnt_thru;
@@ -8495,7 +8805,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
SCTP_TCB_LOCK_ASSERT(stcb);
tmr_started = ctl_cnt = bundle_at = error = 0;
no_fragmentflg = 1;
- asconf = 0;
fwd_tsn = 0;
*cnt_out = 0;
fwd = NULL;
@@ -8515,7 +8824,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
}
TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
if ((chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) ||
- (chk->rec.chunk_id.id == SCTP_ASCONF) ||
(chk->rec.chunk_id.id == SCTP_STREAM_RESET) ||
(chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN)) {
if (chk->rec.chunk_id.id == SCTP_STREAM_RESET) {
@@ -8528,10 +8836,6 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
}
}
ctl_cnt++;
- if (chk->rec.chunk_id.id == SCTP_ASCONF) {
- no_fragmentflg = 1;
- asconf = 1;
- }
if (chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) {
fwd_tsn = 1;
fwd = chk;
@@ -8577,7 +8881,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
if ((error = sctp_lowlevel_chunk_output(inp, stcb, chk->whoTo,
(struct sockaddr *)&chk->whoTo->ro._l_addr, m, auth_offset,
- auth, no_fragmentflg, 0, NULL, asconf, so_locked))) {
+ auth, no_fragmentflg, 0, NULL, 0, chk->whoTo->port, so_locked))) {
SCTP_STAT_INCR(sctps_lowlevelerr);
return (error);
}
@@ -8815,7 +9119,7 @@ one_chunk_around:
/* Now lets send it, if there is anything to send :> */
if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr, m, auth_offset,
- auth, no_fragmentflg, 0, NULL, asconf, so_locked))) {
+ auth, no_fragmentflg, 0, NULL, 0, net->port, so_locked))) {
/* error, we could not output */
SCTP_STAT_INCR(sctps_lowlevelerr);
return (error);
@@ -9795,7 +10099,7 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr, int so_locked
(void)sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb,
stcb->asoc.primary_destination,
(struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr,
- m_out, auth_offset, auth, 1, 0, NULL, 0, so_locked);
+ m_out, auth_offset, auth, 1, 0, NULL, 0, stcb->asoc.primary_destination->port, so_locked);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
}
@@ -9824,19 +10128,20 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb,
SCTP_BUF_LEN(m_shutdown_comp) = sizeof(struct sctp_shutdown_complete_msg);
(void)sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb, net,
(struct sockaddr *)&net->ro._l_addr,
- m_shutdown_comp, 0, NULL, 1, 0, NULL, 0, SCTP_SO_NOT_LOCKED);
+ m_shutdown_comp, 0, NULL, 1, 0, NULL, 0, net->port, SCTP_SO_NOT_LOCKED);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
return;
}
void
sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
- uint32_t vrf_id)
+ uint32_t vrf_id, uint16_t port)
{
/* formulate and SEND a SHUTDOWN-COMPLETE */
struct mbuf *o_pak;
struct mbuf *mout;
struct ip *iph, *iph_out;
+ struct udphdr *udp;
#ifdef INET6
struct ip6_hdr *ip6, *ip6_out;
@@ -9845,28 +10150,36 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
int offset_out, len, mlen;
struct sctp_shutdown_complete_msg *comp_cp;
- /* Get room for the largest message */
+ iph = mtod(m, struct ip *);
+ switch (iph->ip_v) {
+ case IPVERSION:
+ len = (sizeof(struct ip) + sizeof(struct sctp_shutdown_complete_msg));
+ break;
#ifdef INET6
- len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_shutdown_complete_msg));
-#else
- len = (sizeof(struct ip) + sizeof(struct sctp_shutdown_complete_msg));
+ case IPV6_VERSION >> 4:
+ len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_shutdown_complete_msg));
+ break;
#endif
+ default:
+ return;
+ }
+ if (port) {
+ len += sizeof(struct udphdr);
+ }
mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA);
if (mout == NULL) {
return;
}
SCTP_BUF_LEN(mout) = len;
- iph = mtod(m, struct ip *);
+ SCTP_BUF_NEXT(mout) = NULL;
iph_out = NULL;
#ifdef INET6
ip6_out = NULL;
#endif
offset_out = 0;
+
switch (iph->ip_v) {
case IPVERSION:
- SCTP_BUF_LEN(mout) = sizeof(struct ip) +
- sizeof(struct sctp_shutdown_complete_msg);
- SCTP_BUF_NEXT(mout) = NULL;
iph_out = mtod(mout, struct ip *);
/* Fill in the IP header for the ABORT */
@@ -9876,7 +10189,11 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
iph_out->ip_id = 0;
iph_out->ip_off = 0;
iph_out->ip_ttl = MAXTTL;
- iph_out->ip_p = IPPROTO_SCTP;
+ if (port) {
+ iph_out->ip_p = IPPROTO_UDP;
+ } else {
+ iph_out->ip_p = IPPROTO_SCTP;
+ }
iph_out->ip_src.s_addr = iph->ip_dst.s_addr;
iph_out->ip_dst.s_addr = iph->ip_src.s_addr;
@@ -9889,15 +10206,16 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
#ifdef INET6
case IPV6_VERSION >> 4:
ip6 = (struct ip6_hdr *)iph;
- SCTP_BUF_LEN(mout) = sizeof(struct ip6_hdr) +
- sizeof(struct sctp_shutdown_complete_msg);
- SCTP_BUF_NEXT(mout) = NULL;
ip6_out = mtod(mout, struct ip6_hdr *);
/* Fill in the IPv6 header for the ABORT */
ip6_out->ip6_flow = ip6->ip6_flow;
ip6_out->ip6_hlim = ip6_defhlim;
- ip6_out->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ ip6_out->ip6_nxt = IPPROTO_UDP;
+ } else {
+ ip6_out->ip6_nxt = IPPROTO_SCTP;
+ }
ip6_out->ip6_src = ip6->ip6_dst;
ip6_out->ip6_dst = ip6->ip6_src;
/*
@@ -9914,6 +10232,15 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
/* Currently not supported. */
return;
}
+ if (port) {
+ udp = (struct udphdr *)comp_cp;
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(sizeof(struct sctp_shutdown_complete_msg) + sizeof(struct udphdr));
+ udp->uh_sum = 0;
+ offset_out += sizeof(struct udphdr);
+ comp_cp = (struct sctp_shutdown_complete_msg *)((caddr_t)comp_cp + sizeof(struct udphdr));
+ }
if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) {
/* no mbuf's */
sctp_m_freem(mout);
@@ -10758,7 +11085,7 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
void
sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
- struct mbuf *err_cause, uint32_t vrf_id)
+ struct mbuf *err_cause, uint32_t vrf_id, uint16_t port)
{
/*-
* Formulate the abort message, and send it back down.
@@ -10767,6 +11094,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
struct mbuf *mout;
struct sctp_abort_msg *abm;
struct ip *iph, *iph_out;
+ struct udphdr *udp;
#ifdef INET6
struct ip6_hdr *ip6, *ip6_out;
@@ -10780,18 +11108,30 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
sctp_m_freem(err_cause);
return;
}
+ iph = mtod(m, struct ip *);
+ switch (iph->ip_v) {
+ case IPVERSION:
+ len = (sizeof(struct ip) + sizeof(struct sctp_abort_msg));
+ break;
#ifdef INET6
- len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg));
-#else
- len = (sizeof(struct ip) + sizeof(struct sctp_abort_msg));
+ case IPV6_VERSION >> 4:
+ len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg));
+ break;
#endif
+ default:
+ return;
+ }
+ if (port) {
+ len += sizeof(struct udphdr);
+ }
mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA);
if (mout == NULL) {
if (err_cause)
sctp_m_freem(err_cause);
return;
}
- iph = mtod(m, struct ip *);
+ SCTP_BUF_LEN(mout) = len;
+ SCTP_BUF_NEXT(mout) = err_cause;
iph_out = NULL;
#ifdef INET6
ip6_out = NULL;
@@ -10799,8 +11139,6 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
switch (iph->ip_v) {
case IPVERSION:
iph_out = mtod(mout, struct ip *);
- SCTP_BUF_LEN(mout) = sizeof(*iph_out) + sizeof(*abm);
- SCTP_BUF_NEXT(mout) = err_cause;
/* Fill in the IP header for the ABORT */
iph_out->ip_v = IPVERSION;
@@ -10809,7 +11147,11 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
iph_out->ip_id = 0;
iph_out->ip_off = 0;
iph_out->ip_ttl = MAXTTL;
- iph_out->ip_p = IPPROTO_SCTP;
+ if (port) {
+ iph_out->ip_p = IPPROTO_UDP;
+ } else {
+ iph_out->ip_p = IPPROTO_SCTP;
+ }
iph_out->ip_src.s_addr = iph->ip_dst.s_addr;
iph_out->ip_dst.s_addr = iph->ip_src.s_addr;
/* let IP layer calculate this */
@@ -10822,13 +11164,15 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
case IPV6_VERSION >> 4:
ip6 = (struct ip6_hdr *)iph;
ip6_out = mtod(mout, struct ip6_hdr *);
- SCTP_BUF_LEN(mout) = sizeof(*ip6_out) + sizeof(*abm);
- SCTP_BUF_NEXT(mout) = err_cause;
/* Fill in the IP6 header for the ABORT */
ip6_out->ip6_flow = ip6->ip6_flow;
ip6_out->ip6_hlim = ip6_defhlim;
- ip6_out->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ ip6_out->ip6_nxt = IPPROTO_UDP;
+ } else {
+ ip6_out->ip6_nxt = IPPROTO_SCTP;
+ }
ip6_out->ip6_src = ip6->ip6_dst;
ip6_out->ip6_dst = ip6->ip6_src;
@@ -10844,6 +11188,15 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
return;
}
+ udp = (struct udphdr *)abm;
+ if (port) {
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ /* set udp->uh_ulen later */
+ udp->uh_sum = 0;
+ iphlen_out += sizeof(struct udphdr);
+ abm = (struct sctp_abort_msg *)((caddr_t)abm + sizeof(struct udphdr));
+ }
abm->sh.src_port = sh->dest_port;
abm->sh.dest_port = sh->src_port;
abm->sh.checksum = 0;
@@ -10895,6 +11248,9 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
/* zap the stack pointer to the route */
bzero(&ro, sizeof ro);
+ if (port) {
+ udp->uh_ulen = htons(len - sizeof(struct ip));
+ }
SCTPDBG(SCTP_DEBUG_OUTPUT2, "sctp_send_abort calling ip_output:\n");
SCTPDBG_PKT(SCTP_DEBUG_OUTPUT2, iph_out, &abm->sh);
/* set IPv4 length */
@@ -10920,6 +11276,9 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
/* zap the stack pointer to the route */
bzero(&ro, sizeof(ro));
+ if (port) {
+ udp->uh_ulen = htons(len - sizeof(struct ip6_hdr));
+ }
SCTPDBG(SCTP_DEBUG_OUTPUT2, "sctp_send_abort calling ip6_output:\n");
SCTPDBG_PKT(SCTP_DEBUG_OUTPUT2, (struct ip *)ip6_out, &abm->sh);
ip6_out->ip6_plen = len - sizeof(*ip6_out);
@@ -10941,7 +11300,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
void
sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
- uint32_t vrf_id)
+ uint32_t vrf_id, uint16_t port)
{
struct mbuf *o_pak;
struct sctphdr *ihdr;
@@ -10949,6 +11308,7 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
struct sctphdr *ohdr;
struct sctp_chunkhdr *ophdr;
struct ip *iph;
+ struct udphdr *udp;
struct mbuf *mout;
#ifdef SCTP_DEBUG
@@ -10993,9 +11353,17 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
}
val = sctp_calculate_sum(scm, NULL, 0);
#ifdef INET6
- mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
+ }
#else
- mout = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ if (port) {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip) + sizeof(struct udphdr), 1, M_DONTWAIT, 1, MT_DATA);
+ } else {
+ mout = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
+ }
#endif
if (mout == NULL) {
sctp_m_freem(scm);
@@ -11017,7 +11385,10 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
SCTP_BUF_LEN(mout) = sizeof(struct ip);
len += sizeof(struct ip);
-
+ if (port) {
+ SCTP_BUF_LEN(mout) += sizeof(struct udphdr);
+ len += sizeof(struct udphdr);
+ }
bzero(&ro, sizeof ro);
out = mtod(mout, struct ip *);
out->ip_v = iph->ip_v;
@@ -11026,11 +11397,22 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
out->ip_id = iph->ip_id;
out->ip_off = 0;
out->ip_ttl = MAXTTL;
- out->ip_p = IPPROTO_SCTP;
+ if (port) {
+ out->ip_p = IPPROTO_UDP;
+ } else {
+ out->ip_p = IPPROTO_SCTP;
+ }
out->ip_sum = 0;
out->ip_src = iph->ip_dst;
out->ip_dst = iph->ip_src;
out->ip_len = len;
+ if (port) {
+ udp = (struct udphdr *)(out + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(len - sizeof(struct ip));
+ udp->uh_sum = 0;
+ }
#ifdef SCTP_PACKET_LOGGING
if (sctp_logging_level & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, len);
@@ -11059,15 +11441,29 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
SCTP_BUF_LEN(mout) = sizeof(struct ip6_hdr);
len += sizeof(struct ip6_hdr);
bzero(&ro, sizeof ro);
+ if (port) {
+ SCTP_BUF_LEN(mout) += sizeof(struct udphdr);
+ len += sizeof(struct udphdr);
+ }
in6 = mtod(m, struct ip6_hdr *);
out6 = mtod(mout, struct ip6_hdr *);
out6->ip6_flow = in6->ip6_flow;
out6->ip6_hlim = ip6_defhlim;
- out6->ip6_nxt = IPPROTO_SCTP;
+ if (port) {
+ out6->ip6_nxt = IPPROTO_UDP;
+ } else {
+ out6->ip6_nxt = IPPROTO_SCTP;
+ }
out6->ip6_src = in6->ip6_dst;
out6->ip6_dst = in6->ip6_src;
out6->ip6_plen = len - sizeof(struct ip6_hdr);
-
+ if (port) {
+ udp = (struct udphdr *)(out6 + 1);
+ udp->uh_sport = htons(sctp_udp_tunneling_port);
+ udp->uh_dport = port;
+ udp->uh_ulen = htons(len - sizeof(struct ip6_hdr));
+ udp->uh_sum = 0;
+ }
#ifdef SCTP_DEBUG
bzero(&lsa6, sizeof(lsa6));
lsa6.sin6_len = sizeof(lsa6);
OpenPOWER on IntegriCloud