+ * First we need to pad options so that the
+ * SACK blocks can start at a 4-byte boundary
+ * (sack option and length are at a 2 byte offset).
+ */
+ padlen = (MAX_TCPOPTLEN - optlen - sackoptlen) % 4;
+ optlen += padlen;
+ while (padlen-- > 0)
+ *bp++ = TCPOPT_NOP;
+
+ tcpstat.tcps_sack_send_blocks++;
+ *bp++ = TCPOPT_SACK;
+ *bp++ = sackoptlen;
+ lp = (u_int32_t *)(void *)bp;
+
+ /*
+ * First block of SACK option should represent
+ * DSACK. Prefer to send SACK information if there
+ * is space for only one SACK block. This will
+ * allow for faster recovery.
+ */
+ if (TCP_SEND_DSACK_OPT(tp) && nsack > 0 &&
+ (tp->rcv_numsacks == 0 || nsack > 1)) {
+ *lp++ = htonl(tp->t_dsack_lseq);
+ *lp++ = htonl(tp->t_dsack_rseq);
+ tcpstat.tcps_dsack_sent++;
+ tp->t_dsack_sent++;
+ nsack--;
+ }
+ VERIFY(nsack == 0 || tp->rcv_numsacks >= nsack);
+ for (i = 0; i < nsack; i++) {
+ struct sackblk sack = tp->sackblks[i];
+ *lp++ = htonl(sack.start);
+ *lp++ = htonl(sack.end);
+ }
+ optlen += sackoptlen;
+ }
+ }
+
+ /* Pad TCP options to a 4 byte boundary */
+ if (optlen < MAX_TCPOPTLEN && (optlen % sizeof(u_int32_t))) {
+ int pad = sizeof(u_int32_t) - (optlen % sizeof(u_int32_t));
+ u_char *bp = (u_char *)opt + optlen;
+
+ optlen += pad;
+ while (pad) {
+ *bp++ = TCPOPT_EOL;
+ pad--;
+ }
+ }
+
+ /*
+ * RFC 3168 states that:
+ * - If you ever sent an ECN-setup SYN/SYN-ACK you must be prepared
+ * to handle the TCP ECE flag, even if you also later send a
+ * non-ECN-setup SYN/SYN-ACK.
+ * - If you ever send a non-ECN-setup SYN/SYN-ACK, you must not set
+ * the ip ECT flag.
+ *
+ * It is not clear how the ECE flag would ever be set if you never
+ * set the IP ECT flag on outbound packets. All the same, we use
+ * the TE_SETUPSENT to indicate that we have committed to handling
+ * the TCP ECE flag correctly. We use the TE_SENDIPECT to indicate
+ * whether or not we should set the IP ECT flag on outbound packet
+ *
+ * For a SYN-ACK, send an ECN setup SYN-ACK
+ */
+ if ((flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK) &&
+ (tp->ecn_flags & TE_ENABLE_ECN)) {
+ if (tp->ecn_flags & TE_SETUPRECEIVED) {
+ if (tcp_send_ecn_flags_on_syn(tp, so)) {
+ /*
+ * Setting TH_ECE makes this an ECN-setup
+ * SYN-ACK
+ */
+ flags |= TH_ECE;
+
+ /*
+ * Record that we sent the ECN-setup and
+ * default to setting IP ECT.
+ */
+ tp->ecn_flags |= (TE_SETUPSENT|TE_SENDIPECT);
+ tcpstat.tcps_ecn_server_setup++;
+ tcpstat.tcps_ecn_server_success++;
+ } else {
+ /*
+ * We sent an ECN-setup SYN-ACK but it was
+ * dropped. Fallback to non-ECN-setup
+ * SYN-ACK and clear flag to indicate that
+ * we should not send data with IP ECT set
+ *
+ * Pretend we didn't receive an
+ * ECN-setup SYN.
+ *
+ * We already incremented the counter
+ * assuming that the ECN setup will
+ * succeed. Decrementing here
+ * tcps_ecn_server_success to correct it.
+ */
+ if (tp->ecn_flags & TE_SETUPSENT) {
+ tcpstat.tcps_ecn_lost_synack++;
+ tcpstat.tcps_ecn_server_success--;
+ tp->ecn_flags |= TE_LOST_SYNACK;
+ }
+
+ tp->ecn_flags &=
+ ~(TE_SETUPRECEIVED | TE_SENDIPECT |
+ TE_SENDCWR);
+ }
+ }
+ } else if ((flags & (TH_SYN | TH_ACK)) == TH_SYN &&
+ (tp->ecn_flags & TE_ENABLE_ECN)) {
+ if (tcp_send_ecn_flags_on_syn(tp, so)) {
+ /*
+ * Setting TH_ECE and TH_CWR makes this an
+ * ECN-setup SYN