Unify the sending of ABORT, SHUTDOWN-COMPLETE and ERROR chunks.

While there: Fix also some minor bugs and prepare for SCTP/DTLS.

MFC after: 3 days
This commit is contained in:
Michael Tuexen 2012-06-12 13:15:27 +00:00
parent cef68c63ec
commit c9e089587c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=236956
3 changed files with 133 additions and 604 deletions

View File

@ -1442,7 +1442,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
ph = mtod(op_err, struct sctp_paramhdr *);
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,
sctp_send_operr_to(m, sh, cookie->peers_vtag, op_err,
vrf_id, net->port);
if (how_indx < sizeof(asoc->cookie_how))
asoc->cookie_how[how_indx] = 2;
@ -2570,7 +2570,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
if (tim == 0)
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,
sctp_send_operr_to(m, sh, cookie->peers_vtag, op_err,
vrf_id, port);
return (NULL);
}

View File

@ -4478,7 +4478,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
m->m_pkthdr.csum_flags = CSUM_SCTP_IPV6;
m->m_pkthdr.csum_flags = CSUM_SCTP;
m->m_pkthdr.csum_data = 0;
SCTP_STAT_INCR(sctps_sendhwcrc);
#endif
@ -10854,19 +10854,20 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb,
return;
}
void
sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
uint32_t vrf_id, uint16_t port)
static void
sctp_send_resp_msg(struct mbuf *m, struct sctphdr *sh, uint32_t vtag,
uint8_t type, struct mbuf *cause, uint32_t vrf_id, uint16_t port)
{
/* formulate and SEND a SHUTDOWN-COMPLETE */
struct mbuf *o_pak;
struct mbuf *mout;
struct sctphdr *shout;
struct sctp_chunkhdr *ch;
struct ip *iph;
struct udphdr *udp = NULL;
int offset_out, len, mlen;
struct sctp_shutdown_complete_msg *comp_cp;
struct udphdr *udp;
int len, cause_len, padding_len, ret;
#ifdef INET
sctp_route_t ro;
struct ip *iph_out;
#endif
@ -10875,31 +10876,59 @@ sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
#endif
/* Compute the length of the cause and add final padding. */
cause_len = 0;
if (cause != NULL) {
struct mbuf *m_at, *m_last = NULL;
for (m_at = cause; m_at; m_at = SCTP_BUF_NEXT(m_at)) {
if (SCTP_BUF_NEXT(m_at) == NULL)
m_last = m_at;
cause_len += SCTP_BUF_LEN(m_at);
}
padding_len = cause_len % 4;
if (padding_len != 0) {
padding_len = 4 - padding_len;
}
if (padding_len != 0) {
if (sctp_add_pad_tombuf(m_last, padding_len)) {
sctp_m_freem(cause);
return;
}
}
} else {
padding_len = 0;
}
/* Get an mbuf for the header. */
len = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr);
iph = mtod(m, struct ip *);
switch (iph->ip_v) {
#ifdef INET
case IPVERSION:
len = (sizeof(struct ip) + sizeof(struct sctp_shutdown_complete_msg));
len += sizeof(struct ip);
break;
#endif
#ifdef INET6
case IPV6_VERSION >> 4:
len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_shutdown_complete_msg));
len += sizeof(struct ip6_hdr);
break;
#endif
default:
return;
break;
}
if (port) {
len += sizeof(struct udphdr);
}
mout = sctp_get_mbuf_for_msg(len + max_linkhdr, 1, M_DONTWAIT, 1, MT_DATA);
if (mout == NULL) {
if (cause) {
sctp_m_freem(cause);
}
return;
}
SCTP_BUF_RESV_UF(mout, max_linkhdr);
SCTP_BUF_LEN(mout) = len;
SCTP_BUF_NEXT(mout) = NULL;
SCTP_BUF_NEXT(mout) = cause;
if (m->m_flags & M_FLOWID) {
mout->m_pkthdr.flowid = m->m_pkthdr.flowid;
mout->m_flags |= M_FLOWID;
@ -10910,18 +10939,14 @@ sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
#ifdef INET6
ip6_out = NULL;
#endif
offset_out = 0;
switch (iph->ip_v) {
#ifdef INET
case IPVERSION:
iph_out = mtod(mout, struct ip *);
/* Fill in the IP header for the ABORT */
iph_out->ip_v = IPVERSION;
iph_out->ip_hl = (sizeof(struct ip) / 4);
iph_out->ip_tos = (u_char)0;
iph_out->ip_id = 0;
iph_out->ip_hl = (sizeof(struct ip) >> 2);
iph_out->ip_tos = 0;
iph_out->ip_id = ip_newid();
iph_out->ip_off = 0;
iph_out->ip_ttl = MODULE_GLOBAL(ip_defttl);
if (port) {
@ -10931,21 +10956,19 @@ sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
}
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 */
iph_out->ip_sum = 0;
offset_out += sizeof(*iph_out);
comp_cp = (struct sctp_shutdown_complete_msg *)(
(caddr_t)iph_out + offset_out);
len = sizeof(struct ip);
shout = (struct sctphdr *)((caddr_t)iph_out + len);
break;
#endif
#ifdef INET6
case IPV6_VERSION >> 4:
ip6 = (struct ip6_hdr *)iph;
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_flow = htonl(0x60000000);
if (V_ip6_auto_flowlabel) {
ip6_out->ip6_flow |= (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK);
}
ip6_out->ip6_hlim = MODULE_GLOBAL(ip6_defhlim);
if (port) {
ip6_out->ip6_nxt = IPPROTO_UDP;
@ -10954,78 +10977,84 @@ sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
}
ip6_out->ip6_src = ip6->ip6_dst;
ip6_out->ip6_dst = ip6->ip6_src;
/*
* ?? The old code had both the iph len + payload, I think
* this is wrong and would never have worked
*/
ip6_out->ip6_plen = sizeof(struct sctp_shutdown_complete_msg);
offset_out += sizeof(*ip6_out);
comp_cp = (struct sctp_shutdown_complete_msg *)(
(caddr_t)ip6_out + offset_out);
len = sizeof(struct ip6_hdr);
shout = (struct sctphdr *)((caddr_t)ip6_out + len);
break;
#endif /* INET6 */
#endif
default:
/* Currently not supported. */
sctp_m_freem(mout);
return;
len = 0;
shout = mtod(mout, struct sctphdr *);
break;
}
if (port) {
if (htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)) == 0) {
sctp_m_freem(mout);
return;
}
udp = (struct udphdr *)comp_cp;
udp = (struct udphdr *)shout;
udp->uh_sport = htons(SCTP_BASE_SYSCTL(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;
udp->uh_ulen = htons(sizeof(struct udphdr) +
sizeof(struct sctphdr) +
sizeof(struct sctp_chunkhdr) +
cause_len + padding_len);
len += sizeof(struct udphdr);
shout = (struct sctphdr *)((caddr_t)shout + sizeof(struct udphdr));
} else {
udp = NULL;
}
shout->src_port = sh->dest_port;
shout->dest_port = sh->src_port;
shout->checksum = 0;
if (vtag) {
shout->v_tag = htonl(vtag);
} else {
shout->v_tag = sh->v_tag;
}
len += sizeof(struct sctphdr);
ch = (struct sctp_chunkhdr *)((caddr_t)shout + sizeof(struct sctphdr));
ch->chunk_type = type;
if (vtag) {
ch->chunk_flags = 0;
} else {
ch->chunk_flags = SCTP_HAD_NO_TCB;
}
ch->chunk_length = htons(sizeof(struct sctp_chunkhdr) + cause_len);
len += sizeof(struct sctp_chunkhdr);
len += cause_len + padding_len;
if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) {
sctp_m_freem(mout);
return;
}
SCTP_ATTACH_CHAIN(o_pak, mout, len);
#ifdef INET
if (iph_out) {
if (iph_out != NULL) {
/* zap the stack pointer to the route */
bzero(&ro, sizeof(sctp_route_t));
if (port) {
if (V_udp_cksum) {
udp->uh_sum = in_pseudo(iph_out->ip_src.s_addr, iph_out->ip_dst.s_addr, udp->uh_ulen + htons(IPPROTO_UDP));
} else {
udp->uh_sum = 0;
}
}
#endif
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);
return;
}
/* Now copy in and fill in the ABORT tags etc. */
comp_cp->sh.src_port = sh->dest_port;
comp_cp->sh.dest_port = sh->src_port;
comp_cp->sh.checksum = 0;
comp_cp->sh.v_tag = sh->v_tag;
comp_cp->shut_cmp.ch.chunk_flags = SCTP_HAD_NO_TCB;
comp_cp->shut_cmp.ch.chunk_type = SCTP_SHUTDOWN_COMPLETE;
comp_cp->shut_cmp.ch.chunk_length = htons(sizeof(struct sctp_shutdown_complete_chunk));
#ifdef INET
if (iph_out != NULL) {
sctp_route_t ro;
int ret;
mlen = SCTP_BUF_LEN(mout);
bzero(&ro, sizeof ro);
/* set IPv4 length */
iph_out->ip_len = mlen;
#ifdef SCTP_PACKET_LOGGING
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, mlen);
iph_out->ip_len = len;
#ifdef SCTP_PACKET_LOGGING
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING) {
sctp_packet_log(mout, len);
}
#endif
if (port) {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
comp_cp->sh.checksum = sctp_calculate_cksum(mout, offset_out);
shout->checksum = sctp_calculate_cksum(mout, sizeof(struct ip) + sizeof(struct udphdr));
SCTP_STAT_INCR(sctps_sendswcrc);
#endif
if (V_udp_cksum) {
SCTP_ENABLE_UDP_CSUM(mout);
SCTP_ENABLE_UDP_CSUM(o_pak);
}
} else {
#if defined(SCTP_WITH_NO_CSUM)
@ -11036,40 +11065,36 @@ sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
SCTP_STAT_INCR(sctps_sendhwcrc);
#endif
}
SCTP_ATTACH_CHAIN(o_pak, mout, mlen);
/* out it goes */
SCTP_IP_OUTPUT(ret, o_pak, &ro, NULL, vrf_id);
/* Free the route if we got one back */
if (ro.ro_rt)
if (ro.ro_rt) {
RTFREE(ro.ro_rt);
}
}
#endif
#ifdef INET6
if (ip6_out != NULL) {
int ret;
mlen = SCTP_BUF_LEN(mout);
ip6_out->ip6_plen = len - sizeof(struct ip6_hdr);
#ifdef SCTP_PACKET_LOGGING
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, mlen);
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING) {
sctp_packet_log(mout, len);
}
#endif
SCTP_ATTACH_CHAIN(o_pak, mout, mlen);
if (port) {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
comp_cp->sh.checksum = sctp_calculate_cksum(mout, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
shout->checksum = sctp_calculate_cksum(mout, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
SCTP_STAT_INCR(sctps_sendswcrc);
#endif
if ((udp->uh_sum = in6_cksum(o_pak, IPPROTO_UDP, sizeof(struct ip6_hdr), mlen - sizeof(struct ip6_hdr))) == 0) {
if ((udp->uh_sum = in6_cksum(o_pak, IPPROTO_UDP, sizeof(struct ip6_hdr), len - sizeof(struct ip6_hdr))) == 0) {
udp->uh_sum = 0xffff;
}
} else {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
mout->m_pkthdr.csum_flags = CSUM_SCTP_IPV6;
mout->m_pkthdr.csum_flags = CSUM_SCTP;
mout->m_pkthdr.csum_data = 0;
SCTP_STAT_INCR(sctps_sendhwcrc);
#endif
@ -11081,7 +11106,13 @@ sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
SCTP_STAT_INCR_COUNTER64(sctps_outpackets);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
return;
}
void
sctp_send_shutdown_complete2(struct mbuf *m, struct sctphdr *sh,
uint32_t vrf_id, uint16_t port)
{
sctp_send_resp_msg(m, sh, 0, SCTP_SHUTDOWN_COMPLETE, NULL, vrf_id, port);
}
void
@ -11913,528 +11944,24 @@ 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, uint16_t port)
struct mbuf *cause, uint32_t vrf_id, uint16_t port)
{
/*-
* Formulate the abort message, and send it back down.
*/
struct mbuf *o_pak;
struct mbuf *mout;
struct sctp_abort_msg *abm;
struct ip *iph;
struct udphdr *udp;
int iphlen_out, len;
#ifdef INET
struct ip *iph_out;
#endif
#ifdef INET6
struct ip6_hdr *ip6, *ip6_out;
#endif
/* don't respond to ABORT with ABORT */
/* Don't respond to an ABORT with an ABORT. */
if (sctp_is_there_an_abort_here(m, iphlen, &vtag)) {
if (err_cause)
sctp_m_freem(err_cause);
if (cause)
sctp_m_freem(cause);
return;
}
iph = mtod(m, struct ip *);
switch (iph->ip_v) {
#ifdef INET
case IPVERSION:
len = (sizeof(struct ip) + sizeof(struct sctp_abort_msg));
break;
#endif
#ifdef INET6
case IPV6_VERSION >> 4:
len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg));
break;
#endif
default:
if (err_cause) {
sctp_m_freem(err_cause);
}
return;
}
if (port) {
len += sizeof(struct udphdr);
}
mout = sctp_get_mbuf_for_msg(len + max_linkhdr, 1, M_DONTWAIT, 1, MT_DATA);
if (mout == NULL) {
if (err_cause) {
sctp_m_freem(err_cause);
}
return;
}
SCTP_BUF_RESV_UF(mout, max_linkhdr);
SCTP_BUF_LEN(mout) = len;
SCTP_BUF_NEXT(mout) = err_cause;
if (m->m_flags & M_FLOWID) {
mout->m_pkthdr.flowid = m->m_pkthdr.flowid;
mout->m_flags |= M_FLOWID;
}
#ifdef INET
iph_out = NULL;
#endif
#ifdef INET6
ip6_out = NULL;
#endif
switch (iph->ip_v) {
#ifdef INET
case IPVERSION:
iph_out = mtod(mout, struct ip *);
/* Fill in the IP header for the ABORT */
iph_out->ip_v = IPVERSION;
iph_out->ip_hl = (sizeof(struct ip) / 4);
iph_out->ip_tos = (u_char)0;
iph_out->ip_id = 0;
iph_out->ip_off = 0;
iph_out->ip_ttl = MODULE_GLOBAL(ip_defttl);
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 */
iph_out->ip_sum = 0;
iphlen_out = sizeof(*iph_out);
abm = (struct sctp_abort_msg *)((caddr_t)iph_out + iphlen_out);
break;
#endif
#ifdef INET6
case IPV6_VERSION >> 4:
ip6 = (struct ip6_hdr *)iph;
ip6_out = mtod(mout, struct ip6_hdr *);
/* Fill in the IP6 header for the ABORT */
ip6_out->ip6_flow = ip6->ip6_flow;
ip6_out->ip6_hlim = MODULE_GLOBAL(ip6_defhlim);
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;
iphlen_out = sizeof(*ip6_out);
abm = (struct sctp_abort_msg *)((caddr_t)ip6_out + iphlen_out);
break;
#endif /* INET6 */
default:
/* Currently not supported */
sctp_m_freem(mout);
return;
}
udp = (struct udphdr *)abm;
if (port) {
if (htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)) == 0) {
sctp_m_freem(mout);
return;
}
udp->uh_sport = htons(SCTP_BASE_SYSCTL(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;
if (vtag == 0) {
abm->sh.v_tag = sh->v_tag;
abm->msg.ch.chunk_flags = SCTP_HAD_NO_TCB;
} else {
abm->sh.v_tag = htonl(vtag);
abm->msg.ch.chunk_flags = 0;
}
abm->msg.ch.chunk_type = SCTP_ABORT_ASSOCIATION;
if (err_cause) {
struct mbuf *m_tmp = err_cause;
int err_len = 0;
/* get length of the err_cause chain */
while (m_tmp != NULL) {
err_len += SCTP_BUF_LEN(m_tmp);
m_tmp = SCTP_BUF_NEXT(m_tmp);
}
len = SCTP_BUF_LEN(mout) + err_len;
if (err_len % 4) {
/* need pad at end of chunk */
uint32_t cpthis = 0;
int padlen;
padlen = 4 - (len % 4);
m_copyback(mout, len, padlen, (caddr_t)&cpthis);
len += padlen;
}
abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch) + err_len);
} else {
len = SCTP_BUF_LEN(mout);
abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch));
}
if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) {
/* no mbuf's */
sctp_m_freem(mout);
return;
}
#ifdef INET
if (iph_out != NULL) {
sctp_route_t ro;
int ret;
/* zap the stack pointer to the route */
bzero(&ro, sizeof ro);
if (port) {
udp->uh_ulen = htons(len - sizeof(struct ip));
if (V_udp_cksum) {
udp->uh_sum = in_pseudo(iph_out->ip_src.s_addr, iph_out->ip_dst.s_addr, udp->uh_ulen + htons(IPPROTO_UDP));
} else {
udp->uh_sum = 0;
}
}
SCTPDBG(SCTP_DEBUG_OUTPUT2, "sctp_send_abort calling ip_output:\n");
SCTPDBG_PKT(SCTP_DEBUG_OUTPUT2, iph_out, &abm->sh);
/* set IPv4 length */
iph_out->ip_len = len;
/* out it goes */
#ifdef SCTP_PACKET_LOGGING
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, len);
#endif
SCTP_ATTACH_CHAIN(o_pak, mout, len);
if (port) {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
abm->sh.checksum = sctp_calculate_cksum(mout, iphlen_out);
SCTP_STAT_INCR(sctps_sendswcrc);
#endif
if (V_udp_cksum) {
SCTP_ENABLE_UDP_CSUM(o_pak);
}
} else {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
mout->m_pkthdr.csum_flags = CSUM_SCTP;
mout->m_pkthdr.csum_data = 0;
SCTP_STAT_INCR(sctps_sendhwcrc);
#endif
}
SCTP_IP_OUTPUT(ret, o_pak, &ro, NULL, vrf_id);
/* Free the route if we got one back */
if (ro.ro_rt)
RTFREE(ro.ro_rt);
}
#endif
#ifdef INET6
if (ip6_out != NULL) {
int ret;
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);
#ifdef SCTP_PACKET_LOGGING
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, len);
#endif
SCTP_ATTACH_CHAIN(o_pak, mout, len);
if (port) {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
abm->sh.checksum = sctp_calculate_cksum(mout, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
SCTP_STAT_INCR(sctps_sendswcrc);
#endif
if ((udp->uh_sum = in6_cksum(o_pak, IPPROTO_UDP, sizeof(struct ip6_hdr), len - sizeof(struct ip6_hdr))) == 0) {
udp->uh_sum = 0xffff;
}
} else {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
mout->m_pkthdr.csum_flags = CSUM_SCTP_IPV6;
mout->m_pkthdr.csum_data = 0;
SCTP_STAT_INCR(sctps_sendhwcrc);
#endif
}
SCTP_IP6_OUTPUT(ret, o_pak, NULL, NULL, NULL, vrf_id);
}
#endif
SCTP_STAT_INCR(sctps_sendpackets);
SCTP_STAT_INCR_COUNTER64(sctps_outpackets);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
sctp_send_resp_msg(m, sh, vtag, SCTP_ABORT_ASSOCIATION, cause, vrf_id, port);
return;
}
void
sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
uint32_t vrf_id, uint16_t port)
sctp_send_operr_to(struct mbuf *m, struct sctphdr *sh, uint32_t vtag,
struct mbuf *cause, uint32_t vrf_id, uint16_t port)
{
struct mbuf *o_pak;
struct sctphdr *sh, *sh_out;
struct sctp_chunkhdr *ch;
struct ip *iph;
struct udphdr *udp = NULL;
struct mbuf *mout;
int iphlen_out, len;
#ifdef INET
struct ip *iph_out;
#endif
#ifdef INET6
struct ip6_hdr *ip6, *ip6_out;
#endif
iph = mtod(m, struct ip *);
sh = (struct sctphdr *)((caddr_t)iph + iphlen);
switch (iph->ip_v) {
#ifdef INET
case IPVERSION:
len = (sizeof(struct ip) + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr));
break;
#endif
#ifdef INET6
case IPV6_VERSION >> 4:
len = (sizeof(struct ip6_hdr) + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr));
break;
#endif
default:
if (scm) {
sctp_m_freem(scm);
}
return;
}
if (port) {
len += sizeof(struct udphdr);
}
mout = sctp_get_mbuf_for_msg(len + max_linkhdr, 1, M_DONTWAIT, 1, MT_DATA);
if (mout == NULL) {
if (scm) {
sctp_m_freem(scm);
}
return;
}
SCTP_BUF_RESV_UF(mout, max_linkhdr);
SCTP_BUF_LEN(mout) = len;
SCTP_BUF_NEXT(mout) = scm;
if (m->m_flags & M_FLOWID) {
mout->m_pkthdr.flowid = m->m_pkthdr.flowid;
mout->m_flags |= M_FLOWID;
}
#ifdef INET
iph_out = NULL;
#endif
#ifdef INET6
ip6_out = NULL;
#endif
switch (iph->ip_v) {
#ifdef INET
case IPVERSION:
iph_out = mtod(mout, struct ip *);
/* Fill in the IP header for the ABORT */
iph_out->ip_v = IPVERSION;
iph_out->ip_hl = (sizeof(struct ip) / 4);
iph_out->ip_tos = (u_char)0;
iph_out->ip_id = 0;
iph_out->ip_off = 0;
iph_out->ip_ttl = MODULE_GLOBAL(ip_defttl);
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 */
iph_out->ip_sum = 0;
iphlen_out = sizeof(struct ip);
sh_out = (struct sctphdr *)((caddr_t)iph_out + iphlen_out);
break;
#endif
#ifdef INET6
case IPV6_VERSION >> 4:
ip6 = (struct ip6_hdr *)iph;
ip6_out = mtod(mout, struct ip6_hdr *);
/* Fill in the IP6 header for the ABORT */
ip6_out->ip6_flow = ip6->ip6_flow;
ip6_out->ip6_hlim = MODULE_GLOBAL(ip6_defhlim);
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;
iphlen_out = sizeof(struct ip6_hdr);
sh_out = (struct sctphdr *)((caddr_t)ip6_out + iphlen_out);
break;
#endif /* INET6 */
default:
/* Currently not supported */
sctp_m_freem(mout);
return;
}
udp = (struct udphdr *)sh_out;
if (port) {
if (htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)) == 0) {
sctp_m_freem(mout);
return;
}
udp->uh_sport = htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port));
udp->uh_dport = port;
/* set udp->uh_ulen later */
udp->uh_sum = 0;
iphlen_out += sizeof(struct udphdr);
sh_out = (struct sctphdr *)((caddr_t)udp + sizeof(struct udphdr));
}
sh_out->src_port = sh->dest_port;
sh_out->dest_port = sh->src_port;
sh_out->v_tag = vtag;
sh_out->checksum = 0;
ch = (struct sctp_chunkhdr *)((caddr_t)sh_out + sizeof(struct sctphdr));
ch->chunk_type = SCTP_OPERATION_ERROR;
ch->chunk_flags = 0;
if (scm) {
struct mbuf *m_tmp = scm;
int cause_len = 0;
/* get length of the err_cause chain */
while (m_tmp != NULL) {
cause_len += SCTP_BUF_LEN(m_tmp);
m_tmp = SCTP_BUF_NEXT(m_tmp);
}
len = SCTP_BUF_LEN(mout) + cause_len;
if (cause_len % 4) {
/* need pad at end of chunk */
uint32_t cpthis = 0;
int padlen;
padlen = 4 - (len % 4);
m_copyback(mout, len, padlen, (caddr_t)&cpthis);
len += padlen;
}
ch->chunk_length = htons(sizeof(struct sctp_chunkhdr) + cause_len);
} else {
len = SCTP_BUF_LEN(mout);
ch->chunk_length = htons(sizeof(struct sctp_chunkhdr));
}
if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) {
/* no mbuf's */
sctp_m_freem(mout);
return;
}
#ifdef INET
if (iph_out != NULL) {
sctp_route_t ro;
int ret;
/* zap the stack pointer to the route */
bzero(&ro, sizeof ro);
if (port) {
udp->uh_ulen = htons(len - sizeof(struct ip));
if (V_udp_cksum) {
udp->uh_sum = in_pseudo(iph_out->ip_src.s_addr, iph_out->ip_dst.s_addr, udp->uh_ulen + htons(IPPROTO_UDP));
} else {
udp->uh_sum = 0;
}
}
/* set IPv4 length */
iph_out->ip_len = len;
/* out it goes */
#ifdef SCTP_PACKET_LOGGING
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, len);
#endif
SCTP_ATTACH_CHAIN(o_pak, mout, len);
if (port) {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
sh_out->checksum = sctp_calculate_cksum(mout, iphlen_out);
SCTP_STAT_INCR(sctps_sendswcrc);
#endif
if (V_udp_cksum) {
SCTP_ENABLE_UDP_CSUM(o_pak);
}
} else {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
mout->m_pkthdr.csum_flags = CSUM_SCTP;
mout->m_pkthdr.csum_data = 0;
SCTP_STAT_INCR(sctps_sendhwcrc);
#endif
}
SCTP_IP_OUTPUT(ret, o_pak, &ro, NULL, vrf_id);
/* Free the route if we got one back */
if (ro.ro_rt)
RTFREE(ro.ro_rt);
}
#endif
#ifdef INET6
if (ip6_out != NULL) {
int ret;
if (port) {
udp->uh_ulen = htons(len - sizeof(struct ip6_hdr));
}
ip6_out->ip6_plen = len - sizeof(*ip6_out);
#ifdef SCTP_PACKET_LOGGING
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(mout, len);
#endif
SCTP_ATTACH_CHAIN(o_pak, mout, len);
if (port) {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
sh_out->checksum = sctp_calculate_cksum(mout, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
SCTP_STAT_INCR(sctps_sendswcrc);
#endif
if ((udp->uh_sum = in6_cksum(o_pak, IPPROTO_UDP, sizeof(struct ip6_hdr), len - sizeof(struct ip6_hdr))) == 0) {
udp->uh_sum = 0xffff;
}
} else {
#if defined(SCTP_WITH_NO_CSUM)
SCTP_STAT_INCR(sctps_sendnocrc);
#else
mout->m_pkthdr.csum_flags = CSUM_SCTP_IPV6;
mout->m_pkthdr.csum_data = 0;
SCTP_STAT_INCR(sctps_sendhwcrc);
#endif
}
SCTP_IP6_OUTPUT(ret, o_pak, NULL, NULL, NULL, vrf_id);
}
#endif
SCTP_STAT_INCR(sctps_sendpackets);
SCTP_STAT_INCR_COUNTER64(sctps_outpackets);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
sctp_send_resp_msg(m, sh, vtag, SCTP_OPERATION_ERROR, cause, vrf_id, port);
return;
}
static struct mbuf *

View File

@ -204,7 +204,9 @@ void
sctp_send_abort(struct mbuf *, int, struct sctphdr *, uint32_t,
struct mbuf *, uint32_t, uint16_t);
void sctp_send_operr_to(struct mbuf *, int, struct mbuf *, uint32_t, uint32_t, uint16_t);
void
sctp_send_operr_to(struct mbuf *, struct sctphdr *, uint32_t,
struct mbuf *, uint32_t, uint16_t);
#endif /* _KERNEL || __Userspace__ */