From 4a2b92d99fee987e87a5c3789e0892ab6e680018 Mon Sep 17 00:00:00 2001 From: Michael Tuexen Date: Fri, 10 Mar 2023 01:45:46 +0100 Subject: [PATCH] sctp: initial implementation of draft-tuexen-tsvwg-sctp-zero-checksum --- sys/netinet/sctp.h | 1 + sys/netinet/sctp_constants.h | 2 +- sys/netinet/sctp_input.c | 161 ++++++++++++++++++++++++----------- sys/netinet/sctp_output.c | 71 +++++++++++++-- sys/netinet/sctp_pcb.c | 9 ++ sys/netinet/sctp_pcb.h | 1 + sys/netinet/sctp_structs.h | 6 ++ sys/netinet/sctp_sysctl.c | 5 ++ sys/netinet/sctp_sysctl.h | 8 +- sys/netinet/sctp_uio.h | 6 +- sys/netinet/sctp_usrreq.c | 23 +++++ sys/netinet/sctputil.c | 1 + 12 files changed, 234 insertions(+), 60 deletions(-) diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index d67b33acd8ad..95dd85c5d8c0 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -129,6 +129,7 @@ struct sctp_paramhdr { #define SCTP_NRSACK_SUPPORTED 0x00000030 #define SCTP_PKTDROP_SUPPORTED 0x00000031 #define SCTP_MAX_CWND 0x00000032 +#define SCTP_ACCEPT_ZERO_CHECKSUM 0x00000033 /* * read-only options diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index 3df6ad6db2aa..3772a933bf85 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -410,7 +410,7 @@ __FBSDID("$FreeBSD$"); /*************0x8000 series*************/ #define SCTP_ECN_CAPABLE 0x8000 - +#define SCTP_ZERO_CHECKSUM_ACCEPTABLE 0x8001 /* RFC 4895 */ #define SCTP_RANDOM 0x8002 #define SCTP_CHUNK_LIST 0x8003 diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 3f5046811258..dfc74e1e84f3 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -5263,73 +5263,138 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt uint8_t mflowtype, uint32_t mflowid, uint16_t fibnum, uint32_t vrf_id, uint16_t port) { - uint32_t high_tsn; - int fwd_tsn_seen = 0, data_processed = 0; - struct mbuf *m = *mm, *op_err; char msg[SCTP_DIAG_INFO_LEN]; - int un_sent; - int cnt_ctrl_ready = 0; + struct mbuf *m = *mm, *op_err; struct sctp_inpcb *inp = NULL, *inp_decr = NULL; struct sctp_tcb *stcb = NULL; struct sctp_nets *net = NULL; + uint32_t high_tsn; + uint32_t cksum_in_hdr; + int un_sent; + int cnt_ctrl_ready = 0; + int fwd_tsn_seen = 0, data_processed = 0; + bool cksum_validated, stcb_looked_up; SCTP_STAT_INCR(sctps_recvdatagrams); #ifdef SCTP_AUDITING_ENABLED sctp_audit_log(0xE0, 1); sctp_auditing(0, inp, stcb, net); #endif - if (compute_crc != 0) { - uint32_t check, calc_check; - check = sh->checksum; - sh->checksum = 0; - calc_check = sctp_calculate_cksum(m, iphlen); - sh->checksum = check; - if (calc_check != check) { - SCTPDBG(SCTP_DEBUG_INPUT1, "Bad CSUM on SCTP packet calc_check:%x check:%x m:%p mlen:%d iphlen:%d\n", - calc_check, check, (void *)m, length, iphlen); + stcb_looked_up = false; + if (compute_crc != 0) { + cksum_validated = false; + cksum_in_hdr = sh->checksum; + if (cksum_in_hdr != htonl(0)) { + uint32_t cksum_calculated; + + validate_cksum: + sh->checksum = 0; + cksum_calculated = sctp_calculate_cksum(m, iphlen); + sh->checksum = cksum_in_hdr; + if (cksum_calculated != cksum_in_hdr) { + if (stcb_looked_up) { + /* + * The packet has a zero checksum, + * which is not the correct CRC, no + * stcb has been found or an stcb + * has been found but an incorrect + * zero checksum is not acceptable. + */ + KASSERT(cksum_in_hdr == htonl(0), + ("cksum in header not zero: %x", + ntohl(cksum_in_hdr))); + if ((inp == NULL) && + (SCTP_BASE_SYSCTL(sctp_ootb_with_zero_cksum) == 1)) { + /* + * This is an OOTB packet, + * depending on the sysctl + * variable, pretend that + * the checksum is + * acceptable, to allow an + * appropriate response + * (ABORT, for examlpe) to + * be sent. + */ + KASSERT(stcb == NULL, + ("stcb is %p", stcb)); + SCTP_STAT_INCR(sctps_recvzerocrc); + goto cksum_validated; + } + } else { + stcb = sctp_findassociation_addr(m, offset, src, dst, + sh, ch, &inp, &net, vrf_id); + } + SCTPDBG(SCTP_DEBUG_INPUT1, "Bad cksum in SCTP packet:%x calculated:%x m:%p mlen:%d iphlen:%d\n", + ntohl(cksum_in_hdr), ntohl(cksum_calculated), (void *)m, length, iphlen); +#if defined(INET) || defined(INET6) + if ((ch->chunk_type != SCTP_INITIATION) && + (net != NULL) && (net->port != port)) { + if (net->port == 0) { + /* + * UDP encapsulation turned + * on. + */ + net->mtu -= sizeof(struct udphdr); + if (stcb->asoc.smallest_mtu > net->mtu) { + sctp_pathmtu_adjustment(stcb, net->mtu, true); + } + } else if (port == 0) { + /* + * UDP encapsulation turned + * off. + */ + net->mtu += sizeof(struct udphdr); + /* XXX Update smallest_mtu */ + } + net->port = port; + } +#endif + if (net != NULL) { + net->flowtype = mflowtype; + net->flowid = mflowid; + } + SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh); + if ((inp != NULL) && (stcb != NULL)) { + if (stcb->asoc.pktdrop_supported) { + sctp_send_packet_dropped(stcb, net, m, length, iphlen, 1); + sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED); + } + } else if ((inp != NULL) && (stcb == NULL)) { + inp_decr = inp; + } + SCTP_STAT_INCR(sctps_badsum); + SCTP_STAT_INCR_COUNTER32(sctps_checksumerrors); + goto out; + } else { + cksum_validated = true; + } + } + KASSERT(cksum_validated || cksum_in_hdr == htonl(0), + ("cksum 0x%08x not zero and not validated", ntohl(cksum_in_hdr))); + if (!cksum_validated) { stcb = sctp_findassociation_addr(m, offset, src, dst, sh, ch, &inp, &net, vrf_id); -#if defined(INET) || defined(INET6) - if ((ch->chunk_type != SCTP_INITIATION) && - (net != NULL) && (net->port != port)) { - if (net->port == 0) { - /* UDP encapsulation turned on. */ - net->mtu -= sizeof(struct udphdr); - if (stcb->asoc.smallest_mtu > net->mtu) { - sctp_pathmtu_adjustment(stcb, net->mtu, true); - } - } else if (port == 0) { - /* UDP encapsulation turned off. */ - net->mtu += sizeof(struct udphdr); - /* XXX Update smallest_mtu */ - } - net->port = port; + stcb_looked_up = true; + if ((stcb == NULL) || (stcb->asoc.zero_checksum == 0)) { + goto validate_cksum; } -#endif - if (net != NULL) { - net->flowtype = mflowtype; - net->flowid = mflowid; - } - SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh); - if ((inp != NULL) && (stcb != NULL)) { - sctp_send_packet_dropped(stcb, net, m, length, iphlen, 1); - sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED); - } else if ((inp != NULL) && (stcb == NULL)) { - inp_decr = inp; - } - SCTP_STAT_INCR(sctps_badsum); - SCTP_STAT_INCR_COUNTER32(sctps_checksumerrors); - goto out; + SCTP_STAT_INCR(sctps_recvzerocrc); } } +cksum_validated: /* Destination port of 0 is illegal, based on RFC4960. */ - if (sh->dest_port == 0) { + if (sh->dest_port == htons(0)) { SCTP_STAT_INCR(sctps_hdrops); + if ((stcb == NULL) && (inp != NULL)) { + inp_decr = inp; + } goto out; } - stcb = sctp_findassociation_addr(m, offset, src, dst, - sh, ch, &inp, &net, vrf_id); + if (!stcb_looked_up) { + stcb = sctp_findassociation_addr(m, offset, src, dst, + sh, ch, &inp, &net, vrf_id); + } #if defined(INET) || defined(INET6) if ((ch->chunk_type != SCTP_INITIATION) && (net != NULL) && (net->port != port)) { @@ -5352,8 +5417,8 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt net->flowid = mflowid; } if (inp == NULL) { - SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh); SCTP_STAT_INCR(sctps_noport); + SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh); if (badport_bandlim(BANDLIM_SCTP_OOTB) < 0) { goto out; } diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index feb3fc31561d..97e89cb1396c 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -3969,6 +3969,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, uint16_t port, union sctp_sockstore *over_addr, uint8_t mflowtype, uint32_t mflowid, + bool use_zero_crc, int so_locked) { /* nofragment_flag to tell if IP_DF should be set (IPv4 only) */ @@ -4203,15 +4204,23 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } SCTP_ATTACH_CHAIN(o_pak, m, packet_length); if (port) { - sctphdr->checksum = sctp_calculate_cksum(m, sizeof(struct ip) + sizeof(struct udphdr)); - SCTP_STAT_INCR(sctps_sendswcrc); + if (use_zero_crc) { + SCTP_STAT_INCR(sctps_sendzerocrc); + } else { + sctphdr->checksum = sctp_calculate_cksum(m, sizeof(struct ip) + sizeof(struct udphdr)); + SCTP_STAT_INCR(sctps_sendswcrc); + } if (V_udp_cksum) { SCTP_ENABLE_UDP_CSUM(o_pak); } } else { - m->m_pkthdr.csum_flags = CSUM_SCTP; - m->m_pkthdr.csum_data = offsetof(struct sctphdr, checksum); - SCTP_STAT_INCR(sctps_sendhwcrc); + if (use_zero_crc) { + SCTP_STAT_INCR(sctps_sendzerocrc); + } else { + m->m_pkthdr.csum_flags = CSUM_SCTP; + m->m_pkthdr.csum_data = offsetof(struct sctphdr, checksum); + SCTP_STAT_INCR(sctps_sendhwcrc); + } } #ifdef SCTP_PACKET_LOGGING if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING) @@ -4710,6 +4719,15 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked) chunk_len += parameter_len; } + /* Zero checksum acceptable parameter */ + if (stcb->asoc.zero_checksum > 0) { + parameter_len = (uint16_t)sizeof(struct sctp_paramhdr); + ph = (struct sctp_paramhdr *)(mtod(m, caddr_t)+chunk_len); + ph->param_type = htons(SCTP_ZERO_CHECKSUM_ACCEPTABLE); + ph->param_length = htons(parameter_len); + chunk_len += parameter_len; + } + /* Add NAT friendly parameter. */ if (SCTP_BASE_SYSCTL(sctp_inits_include_nat_friendly)) { parameter_len = (uint16_t)sizeof(struct sctp_paramhdr); @@ -4883,7 +4901,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked) inp->sctp_lport, stcb->rport, htonl(0), net->port, NULL, 0, 0, - so_locked))) { + false, so_locked))) { SCTPDBG(SCTP_DEBUG_OUTPUT4, "Gak send error %d\n", error); if (error == ENOBUFS) { stcb->asoc.ifp_had_enobuf = 1; @@ -5909,6 +5927,16 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, chunk_len += parameter_len; } + /* Zero checksum acceptable parameter */ + if (((asoc != NULL) && (asoc->zero_checksum > 0)) || + ((asoc == NULL) && (inp->zero_checksum == 1))) { + parameter_len = (uint16_t)sizeof(struct sctp_paramhdr); + ph = (struct sctp_paramhdr *)(mtod(m, caddr_t)+chunk_len); + ph->param_type = htons(SCTP_ZERO_CHECKSUM_ACCEPTABLE); + ph->param_length = htons(parameter_len); + chunk_len += parameter_len; + } + /* Add NAT friendly parameter */ if (nat_friendly) { parameter_len = (uint16_t)sizeof(struct sctp_paramhdr); @@ -6117,6 +6145,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, inp->sctp_lport, sh->src_port, init_chk->init.initiate_tag, port, over_addr, mflowtype, mflowid, + false, /* XXXMT: Improve this! */ SCTP_SO_NOT_LOCKED))) { SCTPDBG(SCTP_DEBUG_OUTPUT4, "Gak send error %d\n", error); if (error == ENOBUFS) { @@ -7784,6 +7813,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, * destination. */ int quit_now = 0; + bool use_zero_crc; *num_out = 0; *reason_code = 0; @@ -8145,7 +8175,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, htonl(stcb->asoc.peer_vtag), net->port, NULL, 0, 0, - so_locked))) { + false, so_locked))) { /* * error, we could not * output @@ -8167,6 +8197,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, */ sctp_move_chunks_from_net(stcb, net); } + asconf = 0; *reason_code = 7; break; } else { @@ -8180,6 +8211,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, outchain = endoutchain = NULL; auth = NULL; auth_offset = 0; + asconf = 0; if (!no_out_cnt) *num_out += ctl_cnt; /* recalc a clean slate and setup */ @@ -8391,8 +8423,10 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, * flight size since this little guy * is a control only packet. */ + use_zero_crc = asoc->zero_checksum = 2; if (asconf) { sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net); + use_zero_crc = false; /* * do NOT clear the asconf * flag as it is used to do @@ -8402,6 +8436,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, } if (cookie) { sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net); + use_zero_crc = false; cookie = 0; } /* Only HB or ASCONF advances time */ @@ -8423,7 +8458,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, htonl(stcb->asoc.peer_vtag), net->port, NULL, 0, 0, - so_locked))) { + use_zero_crc, so_locked))) { /* * error, we could not * output @@ -8444,6 +8479,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, */ sctp_move_chunks_from_net(stcb, net); } + asconf = 0; *reason_code = 7; break; } else { @@ -8457,6 +8493,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, outchain = endoutchain = NULL; auth = NULL; auth_offset = 0; + asconf = 0; if (!no_out_cnt) *num_out += ctl_cnt; /* recalc a clean slate and setup */ @@ -8719,9 +8756,11 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, /* Is there something to send for this destination? */ if (outchain) { /* We may need to start a control timer or two */ + use_zero_crc = asoc->zero_checksum == 2; if (asconf) { sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net); + use_zero_crc = false; /* * do NOT clear the asconf flag as it is * used to do appropriate source address @@ -8730,6 +8769,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, } if (cookie) { sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net); + use_zero_crc = false; cookie = 0; } /* must start a send timer if data is being sent */ @@ -8764,6 +8804,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, htonl(stcb->asoc.peer_vtag), net->port, NULL, 0, 0, + use_zero_crc, so_locked))) { /* error, we could not output */ SCTPDBG(SCTP_DEBUG_OUTPUT3, "Gak send error %d\n", error); @@ -8781,6 +8822,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, */ sctp_move_chunks_from_net(stcb, net); } + asconf = 0; *reason_code = 6; /*- * I add this line to be paranoid. As far as @@ -8797,6 +8839,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, endoutchain = NULL; auth = NULL; auth_offset = 0; + asconf = 0; if (!no_out_cnt) { *num_out += (ctl_cnt + bundle_at); } @@ -9385,6 +9428,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp, int override_ok = 1; int data_auth_reqd = 0; uint32_t dmtu = 0; + bool use_zero_crc; SCTP_TCB_LOCK_ASSERT(stcb); tmr_started = ctl_cnt = 0; @@ -9448,10 +9492,15 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp, /* do we have control chunks to retransmit? */ if (m != NULL) { /* Start a timer no matter if we succeed or fail */ + use_zero_crc = asoc->zero_checksum == 2; if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) { sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, chk->whoTo); - } else if (chk->rec.chunk_id.id == SCTP_ASCONF) + use_zero_crc = false; + } else if (chk->rec.chunk_id.id == SCTP_ASCONF) { + /* XXXMT: Can this happen? */ sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, chk->whoTo); + use_zero_crc = false; + } chk->snd_count++; /* update our count */ if ((error = sctp_lowlevel_chunk_output(inp, stcb, chk->whoTo, (struct sockaddr *)&chk->whoTo->ro._l_addr, m, @@ -9460,6 +9509,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp, inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), chk->whoTo->port, NULL, 0, 0, + use_zero_crc, so_locked))) { SCTPDBG(SCTP_DEBUG_OUTPUT3, "Gak send error %d\n", error); if (error == ENOBUFS) { @@ -9737,6 +9787,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp, inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), net->port, NULL, 0, 0, + asoc->zero_checksum == 2, so_locked))) { /* error, we could not output */ SCTPDBG(SCTP_DEBUG_OUTPUT3, "Gak send error %d\n", error); @@ -10897,6 +10948,7 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr, int so_locked) stcb->sctp_ep->sctp_lport, stcb->rport, htonl(vtag), stcb->asoc.primary_destination->port, NULL, 0, 0, + stcb->asoc.zero_checksum == 2, so_locked))) { SCTPDBG(SCTP_DEBUG_OUTPUT3, "Gak send error %d\n", error); if (error == ENOBUFS) { @@ -10945,6 +10997,7 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb, htonl(vtag), net->port, NULL, 0, 0, + stcb->asoc.zero_checksum == 2, SCTP_SO_NOT_LOCKED))) { SCTPDBG(SCTP_DEBUG_OUTPUT3, "Gak send error %d\n", error); if (error == ENOBUFS) { diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index fa6ad102f864..7567764bfd72 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -2433,6 +2433,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) inp->nrsack_supported = (uint8_t)SCTP_BASE_SYSCTL(sctp_nrsack_enable); inp->pktdrop_supported = (uint8_t)SCTP_BASE_SYSCTL(sctp_pktdrop_enable); inp->idata_supported = 0; + inp->zero_checksum = 0; inp->fibnum = so->so_fibnum; /* init the small hash table we use to track asocid <-> tcb */ @@ -6402,6 +6403,14 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, } else if (ptype == SCTP_PRSCTP_SUPPORTED) { /* Peer supports pr-sctp */ peer_supports_prsctp = 1; + } else if (ptype == SCTP_ZERO_CHECKSUM_ACCEPTABLE) { + /* + * Only send zero checksums if the upper layer has + * also enabled the support for this. + */ + if (stcb->asoc.zero_checksum == 1) { + stcb->asoc.zero_checksum = 2; + } } else if (ptype == SCTP_SUPPORTED_CHUNK_EXT) { /* A supported extension chunk */ struct sctp_supported_chunk_types_param *pr_supported; diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index fd8115a8101a..39d8b4e7013e 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -409,6 +409,7 @@ struct sctp_inpcb { uint8_t reconfig_supported; uint8_t nrsack_supported; uint8_t pktdrop_supported; + uint8_t zero_checksum; struct sctp_nonpad_sndrcvinfo def_send; /*- * These three are here for the sosend_dgram diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index e28c2265589d..504e9ad79fe3 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -1185,6 +1185,12 @@ struct sctp_association { uint8_t pktdrop_supported; uint8_t idata_supported; + /* + * Zero checksum supported information: 0: disabled 1: enabled for + * rcv 2: enabled for snd/rcv + */ + uint8_t zero_checksum; + /* Did the peer make the stream config (add out) request */ uint8_t peer_req_out; diff --git a/sys/netinet/sctp_sysctl.c b/sys/netinet/sctp_sysctl.c index fcc02f315a1b..b48aedf0b950 100644 --- a/sys/netinet/sctp_sysctl.c +++ b/sys/netinet/sctp_sysctl.c @@ -121,6 +121,7 @@ sctp_init_sysctls(void) SCTP_BASE_SYSCTL(sctp_blackhole) = SCTPCTL_BLACKHOLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_sendall_limit) = SCTPCTL_SENDALL_LIMIT_DEFAULT; SCTP_BASE_SYSCTL(sctp_diag_info_code) = SCTPCTL_DIAG_INFO_CODE_DEFAULT; + SCTP_BASE_SYSCTL(sctp_ootb_with_zero_cksum) = SCTPCTL_OOTB_WITH_ZERO_CKSUM_DEFAULT; #if defined(SCTP_LOCAL_TRACE_BUF) memset(&SCTP_BASE_SYSCTL(sctp_log), 0, sizeof(struct sctp_log)); #endif @@ -828,6 +829,9 @@ sctp_sysctl_handle_stats(SYSCTL_HANDLER_ARGS) sb.sctps_send_burst_avoid += sarry->sctps_send_burst_avoid; sb.sctps_send_cwnd_avoid += sarry->sctps_send_cwnd_avoid; sb.sctps_fwdtsn_map_over += sarry->sctps_fwdtsn_map_over; + sb.sctps_queue_upd_ecne += sarry->sctps_queue_upd_ecne; + sb.sctps_recvzerocrc += sarry->sctps_recvzerocrc; + sb.sctps_sendzerocrc += sarry->sctps_sendzerocrc; if (req->newptr != NULL) { memcpy(sarry, &sb_temp, sizeof(struct sctpstat)); } @@ -970,6 +974,7 @@ SCTP_UINT_SYSCTL(use_dcccecn, sctp_use_dccc_ecn, SCTPCTL_RTTVAR_DCCCECN) SCTP_UINT_SYSCTL(blackhole, sctp_blackhole, SCTPCTL_BLACKHOLE) SCTP_UINT_SYSCTL(sendall_limit, sctp_sendall_limit, SCTPCTL_SENDALL_LIMIT) SCTP_UINT_SYSCTL(diag_info_code, sctp_diag_info_code, SCTPCTL_DIAG_INFO_CODE) +SCTP_UINT_SYSCTL(ootb_with_zero_cksum, sctp_ootb_with_zero_cksum, SCTPCTL_OOTB_WITH_ZERO_CKSUM) #ifdef SCTP_DEBUG SCTP_UINT_SYSCTL(debug, sctp_debug_on, SCTPCTL_DEBUG) #endif diff --git a/sys/netinet/sctp_sysctl.h b/sys/netinet/sctp_sysctl.h index 695cd743403b..12a7c5200b40 100644 --- a/sys/netinet/sctp_sysctl.h +++ b/sys/netinet/sctp_sysctl.h @@ -106,7 +106,6 @@ struct sctp_sysctl { uint32_t sctp_rttvar_eqret; uint32_t sctp_steady_step; uint32_t sctp_use_dccc_ecn; - uint32_t sctp_diag_info_code; #if defined(SCTP_LOCAL_TRACE_BUF) struct sctp_log sctp_log; #endif @@ -117,6 +116,8 @@ struct sctp_sysctl { uint32_t sctp_initial_cwnd; uint32_t sctp_blackhole; uint32_t sctp_sendall_limit; + uint32_t sctp_diag_info_code; + uint32_t sctp_ootb_with_zero_cksum; #if defined(SCTP_DEBUG) uint32_t sctp_debug_on; #endif @@ -546,6 +547,11 @@ struct sctp_sysctl { #define SCTPCTL_DIAG_INFO_CODE_MAX 65535 #define SCTPCTL_DIAG_INFO_CODE_DEFAULT 0 +#define SCTPCTL_OOTB_WITH_ZERO_CKSUM_DESC "Accept OOTB packets with zero checksum" +#define SCTPCTL_OOTB_WITH_ZERO_CKSUM_MIN 0 +#define SCTPCTL_OOTB_WITH_ZERO_CKSUM_MAX 1 +#define SCTPCTL_OOTB_WITH_ZERO_CKSUM_DEFAULT 0 + #if defined(SCTP_DEBUG) /* debug: Configure debug output */ #define SCTPCTL_DEBUG_DESC "Configure debug output" diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index 330a4529b039..e833b0ab33ae 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -1120,7 +1120,11 @@ struct sctpstat { * fwd-tsn's */ uint32_t sctps_queue_upd_ecne; /* Number of times we queued or * updated an ECN chunk on send queue */ - uint32_t sctps_reserved[31]; /* Future ABI compat - remove int's + uint32_t sctps_recvzerocrc; /* Number of accepted packets with + * zero CRC */ + uint32_t sctps_sendzerocrc; /* Number of packets sent with zero + * CRC */ + uint32_t sctps_reserved[29]; /* Future ABI compat - remove int's * from here when adding new */ }; diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index d308b75aa90d..57d5abdebd30 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -6834,6 +6834,29 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } break; } + case SCTP_ACCEPT_ZERO_CHECKSUM: + { + uint32_t *value; + + SCTP_CHECK_AND_CAST(value, optval, uint32_t, optsize); + SCTP_INP_WLOCK(inp); + if (*value == 0) { + /* + * Do not allow turning zero checksum + * acceptance off again, since this could + * result in inconsistent behaviour for + * listeners. + */ + if (inp->zero_checksum > 0) { + error = EINVAL; + } + } else { + inp->zero_checksum = 1; + } + SCTP_INP_WUNLOCK(inp); + break; + } + default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOPROTOOPT); error = ENOPROTOOPT; diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index f4b6ca8c0836..8bb0f7446b68 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -1149,6 +1149,7 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, asoc->nrsack_supported = inp->nrsack_supported; asoc->pktdrop_supported = inp->pktdrop_supported; asoc->idata_supported = inp->idata_supported; + asoc->zero_checksum = inp->zero_checksum; asoc->sctp_cmt_pf = (uint8_t)0; asoc->sctp_frag_point = inp->sctp_frag_point; asoc->sctp_features = inp->sctp_features;