FreeBSD/sys/netinet/tcp_output.c
/*
 * Insert TCP options according to the supplied parameters to the place
 * optp in a consistent way.  Can handle unaligned destinations.
 *
 * The order of the option processing is crucial for optimal packing and
 * alignment for the scarce option space.
 *
 * The optimal order for a SYN/SYN-ACK segment is:
 *   MSS (4) + NOP (1) + Window scale (3) + SACK permitted (2) +
 *   Timestamp (10) + Signature (18) = 38 bytes out of a maximum of 40.
 *
 * The SACK options should be last.  SACK blocks consume 8*n+2 bytes.
 * So a full size SACK blocks option is 34 bytes (with 4 SACK blocks).
 * At minimum we need 10 bytes (to generate 1 SACK block).  If both
 * TCP Timestamps (12 bytes) and TCP Signatures (18 bytes) are present,
 * we only have 10 bytes for SACK options (40 - (12 + 18)).
 */
Insert TCP options according to the supplied parameters to the place
optp in a consistent way.  Can handle unaligned destinations.

The order of the option processing is crucial for optimal packing and
alignment for the scarce option space.



The SACK options should be last.  SACK blocks consume 8*n+2 bytes.
So a full size SACK blocks option is 34 bytes (with 4 SACK blocks).
At minimum we need 10 bytes (to generate 1 SACK block).  If both
TCP Timestamps (12 bytes) and TCP Signatures (18 bytes) are present,
we only have 10 bytes for SACK options (40 - (12 + 18)).
Insert TCP options according to the supplied parameters to the place optp in a consistent way . Can handle unaligned destinations . The order of the option processing is crucial for optimal packing and alignment for the scarce option space . The SACK options should be last . SACK blocks consume 8 * n + 2 bytes . So a full size SACK blocks option is 34 bytes ( with 4 SACK blocks ) . At minimum we need 10 bytes ( to generate 1 SACK block ) . If both TCP Timestamps ( 12 bytes ) and TCP Signatures ( 18 bytes ) are present we only have 10 bytes for SACK options ( 40 - ( 12 + 18 )) .
int
tcp_addoptions(struct tcpopt *to, u_char *optp)
{
	u_int32_t mask, optlen = 0;
	for (mask = 1; mask < TOF_MAXOPT; mask <<= 1) {
		if ((to->to_flags & mask) != mask)
			continue;
		if (optlen == TCP_MAXOLEN)
			break;
		switch (to->to_flags & mask) {
		case TOF_MSS:
			while (optlen % 4) {
				optlen += TCPOLEN_NOP;
				*optp++ = TCPOPT_NOP;
			}
			if (TCP_MAXOLEN - optlen < TCPOLEN_MAXSEG)
				continue;
			optlen += TCPOLEN_MAXSEG;
			*optp++ = TCPOPT_MAXSEG;
			*optp++ = TCPOLEN_MAXSEG;
			to->to_mss = htons(to->to_mss);
			bcopy((u_char *)&to->to_mss, optp, sizeof(to->to_mss));
			optp += sizeof(to->to_mss);
			break;
		case TOF_SCALE:
			while (!optlen || optlen % 2 != 1) {
				optlen += TCPOLEN_NOP;
				*optp++ = TCPOPT_NOP;
			}
			if (TCP_MAXOLEN - optlen < TCPOLEN_WINDOW)
				continue;
			optlen += TCPOLEN_WINDOW;
			*optp++ = TCPOPT_WINDOW;
			*optp++ = TCPOLEN_WINDOW;
			*optp++ = to->to_wscale;
			break;
		case TOF_SACKPERM:
			while (optlen % 2) {
				optlen += TCPOLEN_NOP;
				*optp++ = TCPOPT_NOP;
			}
			if (TCP_MAXOLEN - optlen < TCPOLEN_SACK_PERMITTED)
				continue;
			optlen += TCPOLEN_SACK_PERMITTED;
			*optp++ = TCPOPT_SACK_PERMITTED;
			*optp++ = TCPOLEN_SACK_PERMITTED;
			break;
		case TOF_TS:
			while (!optlen || optlen % 4 != 2) {
				optlen += TCPOLEN_NOP;
				*optp++ = TCPOPT_NOP;
			}
			if (TCP_MAXOLEN - optlen < TCPOLEN_TIMESTAMP)
				continue;
			optlen += TCPOLEN_TIMESTAMP;
			*optp++ = TCPOPT_TIMESTAMP;
			*optp++ = TCPOLEN_TIMESTAMP;
			to->to_tsval = htonl(to->to_tsval);
			to->to_tsecr = htonl(to->to_tsecr);
			bcopy((u_char *)&to->to_tsval, optp, sizeof(to->to_tsval));
			optp += sizeof(to->to_tsval);
			bcopy((u_char *)&to->to_tsecr, optp, sizeof(to->to_tsecr));
			optp += sizeof(to->to_tsecr);
			break;
#ifdef TCP_SIGNATURE
		case TOF_SIGNATURE:
			{
			int siglen = TCPOLEN_SIGNATURE - 2;
			while (!optlen || optlen % 4 != 2) {
				optlen += TCPOLEN_NOP;
				*optp++ = TCPOPT_NOP;
			}
			if (TCP_MAXOLEN - optlen < TCPOLEN_SIGNATURE)
				continue;
			optlen += TCPOLEN_SIGNATURE;
			*optp++ = TCPOPT_SIGNATURE;
			*optp++ = TCPOLEN_SIGNATURE;
			to->to_signature = optp;
			while (siglen--)
				 *optp++ = 0;
			break;
			}
#endif
		case TOF_SACK:
			{
			int sackblks = 0;
			struct sackblk *sack = (struct sackblk *)to->to_sacks;
			tcp_seq sack_seq;
			while (!optlen || optlen % 4 != 2) {
				optlen += TCPOLEN_NOP;
				*optp++ = TCPOPT_NOP;
			}
			if (TCP_MAXOLEN - optlen < TCPOLEN_SACKHDR + TCPOLEN_SACK)
				continue;
			optlen += TCPOLEN_SACKHDR;
			*optp++ = TCPOPT_SACK;
			sackblks = min(to->to_nsacks,
					(TCP_MAXOLEN - optlen) / TCPOLEN_SACK);
			*optp++ = TCPOLEN_SACKHDR + sackblks * TCPOLEN_SACK;
			while (sackblks--) {
				sack_seq = htonl(sack->start);
				bcopy((u_char *)&sack_seq, optp, sizeof(sack_seq));
				optp += sizeof(sack_seq);
				sack_seq = htonl(sack->end);
				bcopy((u_char *)&sack_seq, optp, sizeof(sack_seq));
				optp += sizeof(sack_seq);
				optlen += TCPOLEN_SACK;
				sack++;
			}
			TCPSTAT_INC(tcps_sack_send_blocks);
			break;
			}
#ifdef TCP_RFC7413
		case TOF_FASTOPEN:
			{
			int total_len;
			total_len = TCPOLEN_FAST_OPEN_EMPTY + to->to_tfo_len;
			if (TCP_MAXOLEN - optlen < total_len)
				continue;
			*optp++ = TCPOPT_FAST_OPEN;
			*optp++ = total_len;
			if (to->to_tfo_len > 0) {
				bcopy(to->to_tfo_cookie, optp, to->to_tfo_len);
				optp += to->to_tfo_len;
			}
			optlen += total_len;
			break;
			}
#endif
		default:
			panic("%s: unknown TCP option type", __func__);
			break;
		}
	}
	if (optlen % 4) {
		optlen += TCPOLEN_EOL;
		*optp++ = TCPOPT_EOL;
	}
	while (optlen % 4) {
		optlen += TCPOLEN_PAD;
		*optp++ = TCPOPT_PAD;
	}
	KASSERT(optlen <= TCP_MAXOLEN, ("%s: TCP options too long", __func__));
	return (optlen);
}
int tcp_addoptions ( struct tcpopt * to , u_char * optp ) { u_int32_t mask , optlen = 0 ; for ( mask = 1 ; mask < TOF_MAXOPT ; mask <<= 1 ) { if ( ( to -> to_flags & mask ) != mask ) continue ; if ( optlen == TCP_MAXOLEN ) break ; switch ( to -> to_flags & mask ) { case TOF_MSS : while ( optlen % 4 ) { optlen += TCPOLEN_NOP ; * optp ++ = TCPOPT_NOP ; } if ( TCP_MAXOLEN - optlen < TCPOLEN_MAXSEG ) continue ; optlen += TCPOLEN_MAXSEG ; * optp ++ = TCPOPT_MAXSEG ; * optp ++ = TCPOLEN_MAXSEG ; to -> to_mss = htons ( to -> to_mss ) ; bcopy ( ( u_char * ) & to -> to_mss , optp , sizeof ( to -> to_mss ) ) ; optp += sizeof ( to -> to_mss ) ; break ; case TOF_SCALE : while ( ! optlen || optlen % 2 != 1 ) { optlen += TCPOLEN_NOP ; * optp ++ = TCPOPT_NOP ; } if ( TCP_MAXOLEN - optlen < TCPOLEN_WINDOW ) continue ; optlen += TCPOLEN_WINDOW ; * optp ++ = TCPOPT_WINDOW ; * optp ++ = TCPOLEN_WINDOW ; * optp ++ = to -> to_wscale ; break ; case TOF_SACKPERM : while ( optlen % 2 ) { optlen += TCPOLEN_NOP ; * optp ++ = TCPOPT_NOP ; } if ( TCP_MAXOLEN - optlen < TCPOLEN_SACK_PERMITTED ) continue ; optlen += TCPOLEN_SACK_PERMITTED ; * optp ++ = TCPOPT_SACK_PERMITTED ; * optp ++ = TCPOLEN_SACK_PERMITTED ; break ; case TOF_TS : while ( ! optlen || optlen % 4 != 2 ) { optlen += TCPOLEN_NOP ; * optp ++ = TCPOPT_NOP ; } if ( TCP_MAXOLEN - optlen < TCPOLEN_TIMESTAMP ) continue ; optlen += TCPOLEN_TIMESTAMP ; * optp ++ = TCPOPT_TIMESTAMP ; * optp ++ = TCPOLEN_TIMESTAMP ; to -> to_tsval = htonl ( to -> to_tsval ) ; to -> to_tsecr = htonl ( to -> to_tsecr ) ; bcopy ( ( u_char * ) & to -> to_tsval , optp , sizeof ( to -> to_tsval ) ) ; optp += sizeof ( to -> to_tsval ) ; bcopy ( ( u_char * ) & to -> to_tsecr , optp , sizeof ( to -> to_tsecr ) ) ; optp += sizeof ( to -> to_tsecr ) ; break ; #ifdef TCP_SIGNATURE case TOF_SIGNATURE : { int siglen = TCPOLEN_SIGNATURE - 2 ; while ( ! optlen || optlen % 4 != 2 ) { optlen += TCPOLEN_NOP ; * optp ++ = TCPOPT_NOP ; } if ( TCP_MAXOLEN - optlen < TCPOLEN_SIGNATURE ) continue ; optlen += TCPOLEN_SIGNATURE ; * optp ++ = TCPOPT_SIGNATURE ; * optp ++ = TCPOLEN_SIGNATURE ; to -> to_signature = optp ; while ( siglen -- ) * optp ++ = 0 ; break ; } #endif case TOF_SACK : { int sackblks = 0 ; struct sackblk * sack = ( struct sackblk * ) to -> to_sacks ; tcp_seq sack_seq ; while ( ! optlen || optlen % 4 != 2 ) { optlen += TCPOLEN_NOP ; * optp ++ = TCPOPT_NOP ; } if ( TCP_MAXOLEN - optlen < TCPOLEN_SACKHDR + TCPOLEN_SACK ) continue ; optlen += TCPOLEN_SACKHDR ; * optp ++ = TCPOPT_SACK ; sackblks = min ( to -> to_nsacks , ( TCP_MAXOLEN - optlen ) / TCPOLEN_SACK ) ; * optp ++ = TCPOLEN_SACKHDR + sackblks * TCPOLEN_SACK ; while ( sackblks -- ) { sack_seq = htonl ( sack -> start ) ; bcopy ( ( u_char * ) & sack_seq , optp , sizeof ( sack_seq ) ) ; optp += sizeof ( sack_seq ) ; sack_seq = htonl ( sack -> end ) ; bcopy ( ( u_char * ) & sack_seq , optp , sizeof ( sack_seq ) ) ; optp += sizeof ( sack_seq ) ; optlen += TCPOLEN_SACK ; sack ++ ; } TCPSTAT_INC ( tcps_sack_send_blocks ) ; break ; } #ifdef TCP_RFC7413 case TOF_FASTOPEN : { int total_len ; total_len = TCPOLEN_FAST_OPEN_EMPTY + to -> to_tfo_len ; if ( TCP_MAXOLEN - optlen < total_len ) continue ; * optp ++ = TCPOPT_FAST_OPEN ; * optp ++ = total_len ; if ( to -> to_tfo_len > 0 ) { bcopy ( to -> to_tfo_cookie , optp , to -> to_tfo_len ) ; optp += to -> to_tfo_len ; } optlen += total_len ; break ; } #endif default : panic ( " " , __func__ ) ; break ; } } if ( optlen % 4 ) { optlen += TCPOLEN_EOL ; * optp ++ = TCPOPT_EOL ; } while ( optlen % 4 ) { optlen += TCPOLEN_PAD ; * optp ++ = TCPOPT_PAD ; } KASSERT ( optlen <= TCP_MAXOLEN , ( " " , __func__ ) ) ; return ( optlen ) ; }
Insert TCP options according to the supplied parameters to the place
 optp in a consistent way.
Insert TCP options according to the supplied parameters to the place optp in a consistent way .
/* XXX is there any point to aligning this option? */ /* Terminate and pad TCP options to a 4 byte boundary. */ /*
	 * According to RFC 793 (STD0007):
	 *   "The content of the header beyond the End-of-Option option
	 *    must be header padding (i.e., zero)."
	 *   and later: "The padding is composed of zeros."
	 */