+
+ /*
+ 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 packets.
+ */
+ /*
+ * For a SYN-ACK, send an ECN setup SYN-ACK
+ */
+ if (tcp_ecn_inbound && (flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) {
+ if ((tp->ecn_flags & TE_SETUPRECEIVED) != 0) {
+ if ((tp->ecn_flags & TE_SETUPSENT) == 0) {
+ /* 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);
+ }
+ else {
+ /*
+ * We sent an ECN-setup SYN-ACK but it was dropped.
+ * Fallback to non-ECN-setup SYN-ACK and clear flag
+ * that to indicate we should not send data with IP ECT set.
+ *
+ * Pretend we didn't receive an ECN-setup SYN.
+ */
+ tp->ecn_flags &= ~TE_SETUPRECEIVED;
+ }
+ }
+ }
+ else if (tcp_ecn_outbound && (flags & (TH_SYN | TH_ACK)) == TH_SYN) {
+ if ((tp->ecn_flags & TE_SETUPSENT) == 0) {
+ /* Setting TH_ECE and TH_CWR makes this an ECN-setup SYN */
+ flags |= (TH_ECE | TH_CWR);
+
+ /*
+ * Record that we sent the ECN-setup and default to
+ * setting IP ECT.
+ */
+ tp->ecn_flags |= (TE_SETUPSENT | TE_SENDIPECT);
+ }
+ else {
+ /*
+ * We sent an ECN-setup SYN but it was dropped.
+ * Fall back to no ECN and clear flag indicating
+ * we should send data with IP ECT set.
+ */
+ tp->ecn_flags &= ~TE_SENDIPECT;
+ }
+ }
+
+ /*
+ * Check if we should set the TCP CWR flag.
+ * CWR flag is sent when we reduced the congestion window because
+ * we received a TCP ECE or we performed a fast retransmit. We
+ * never set the CWR flag on retransmitted packets. We only set
+ * the CWR flag on data packets. Pure acks don't have this set.
+ */
+ if ((tp->ecn_flags & TE_SENDCWR) != 0 && len != 0 &&
+ !SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+ flags |= TH_CWR;
+ tp->ecn_flags &= ~TE_SENDCWR;
+ }
+
+ /*
+ * Check if we should set the TCP ECE flag.
+ */
+ if ((tp->ecn_flags & TE_SENDECE) != 0 && len == 0) {
+ flags |= TH_ECE;
+ }