The branch main has been updated by tuexen:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=4a2b92d99fee987e87a5c3789e0892ab6e680018

commit 4a2b92d99fee987e87a5c3789e0892ab6e680018
Author:     Michael Tuexen <[email protected]>
AuthorDate: 2023-03-10 00:45:46 +0000
Commit:     Michael Tuexen <[email protected]>
CommitDate: 2023-03-10 00:45:46 +0000

    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     | 157 ++++++++++++++++++++++++++++++-------------
 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, 232 insertions(+), 58 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
+
+       stcb_looked_up = false;
        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 = sctp_findassociation_addr(m, offset, src, dst,
-                           sh, ch, &inp, &net, vrf_id);
+               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);
+                               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 */
                                        }
-                               } else if (port == 0) {
-                                       /* UDP encapsulation turned off. */
-                                       net->mtu += sizeof(struct udphdr);
-                                       /* XXX Update smallest_mtu */
+                                       net->port = port;
                                }
-                               net->port = port;
-                       }
 #endif
-                       if (net != NULL) {
-                               net->flowtype = mflowtype;
-                               net->flowid = mflowid;
+                               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;
                        }
-                       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;
+               }
+               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);
+                       stcb_looked_up = true;
+                       if ((stcb == NULL) || (stcb->asoc.zero_checksum == 0)) {
+                               goto validate_cksum;
+                       }
+                       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 @@ do_a_abort:
                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 @@ do_a_abort:
            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 @@ again_one_more_time:
                                            htonl(stcb->asoc.peer_vtag),
                                            net->port, NULL,
                                            0, 0,
-                                           so_locked))) {
+                                           false, so_locked))) {
                                                /*
                                                 * error, we could not
                                                 * output
@@ -8167,6 +8197,7 @@ again_one_more_time:
                                                         */
                                                        
sctp_move_chunks_from_net(stcb, net);
                                                }
+                                               asconf = 0;
                                                *reason_code = 7;
                                                break;
                                        } else {
@@ -8180,6 +8211,7 @@ again_one_more_time:
                                        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 @@ again_one_more_time:
                                         * 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 @@ again_one_more_time:
                                        }
                                        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 @@ again_one_more_time:
                                            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 @@ again_one_more_time:
                                                         */
                                                        
sctp_move_chunks_from_net(stcb, net);
                                                }
+                                               asconf = 0;
                                                *reason_code = 7;
                                                break;
                                        } else {
@@ -8457,6 +8493,7 @@ again_one_more_time:
                                        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 @@ no_data_fill:
                /* 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 @@ no_data_fill:
                        }
                        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 @@ no_data_fill:
                            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 @@ no_data_fill:
                                         */
                                        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 @@ no_data_fill:
                        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 @@ one_chunk_around:
                            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;

Reply via email to