|  | @@ -904,50 +904,86 @@ static struct tcp_connection * tcp_demux ( unsigned int local_port ) {
 | 
		
	
		
			
			| 904 | 904 |  /**
 | 
		
	
		
			
			| 905 | 905 |   * Parse TCP received options
 | 
		
	
		
			
			| 906 | 906 |   *
 | 
		
	
		
			
			| 907 |  | - * @v tcp		TCP connection
 | 
		
	
		
			
			| 908 |  | - * @v data		Raw options data
 | 
		
	
		
			
			| 909 |  | - * @v len		Raw options length
 | 
		
	
		
			
			|  | 907 | + * @v tcp		TCP connection (may be NULL)
 | 
		
	
		
			
			|  | 908 | + * @v tcphdr		TCP header
 | 
		
	
		
			
			|  | 909 | + * @v hlen		TCP header length
 | 
		
	
		
			
			| 910 | 910 |   * @v options		Options structure to fill in
 | 
		
	
		
			
			|  | 911 | + * @ret rc		Return status code
 | 
		
	
		
			
			| 911 | 912 |   */
 | 
		
	
		
			
			| 912 |  | -static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
 | 
		
	
		
			
			| 913 |  | -			  size_t len, struct tcp_options *options ) {
 | 
		
	
		
			
			| 914 |  | -	const void *end = ( data + len );
 | 
		
	
		
			
			|  | 913 | +static int tcp_rx_opts ( struct tcp_connection *tcp,
 | 
		
	
		
			
			|  | 914 | +			 const struct tcp_header *tcphdr, size_t hlen,
 | 
		
	
		
			
			|  | 915 | +			 struct tcp_options *options ) {
 | 
		
	
		
			
			|  | 916 | +	const void *data = ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) );
 | 
		
	
		
			
			|  | 917 | +	const void *end = ( ( ( void * ) tcphdr ) + hlen );
 | 
		
	
		
			
			| 915 | 918 |  	const struct tcp_option *option;
 | 
		
	
		
			
			| 916 | 919 |  	unsigned int kind;
 | 
		
	
		
			
			|  | 920 | +	size_t remaining;
 | 
		
	
		
			
			|  | 921 | +	size_t min;
 | 
		
	
		
			
			| 917 | 922 |  
 | 
		
	
		
			
			|  | 923 | +	/* Sanity check */
 | 
		
	
		
			
			|  | 924 | +	assert ( hlen >= sizeof ( *tcphdr ) );
 | 
		
	
		
			
			|  | 925 | +
 | 
		
	
		
			
			|  | 926 | +	/* Parse options */
 | 
		
	
		
			
			| 918 | 927 |  	memset ( options, 0, sizeof ( *options ) );
 | 
		
	
		
			
			| 919 |  | -	while ( data < end ) {
 | 
		
	
		
			
			|  | 928 | +	while ( ( remaining = ( end - data ) ) ) {
 | 
		
	
		
			
			|  | 929 | +
 | 
		
	
		
			
			|  | 930 | +		/* Extract option code */
 | 
		
	
		
			
			| 920 | 931 |  		option = data;
 | 
		
	
		
			
			| 921 | 932 |  		kind = option->kind;
 | 
		
	
		
			
			|  | 933 | +
 | 
		
	
		
			
			|  | 934 | +		/* Handle single-byte options */
 | 
		
	
		
			
			| 922 | 935 |  		if ( kind == TCP_OPTION_END )
 | 
		
	
		
			
			| 923 |  | -			return;
 | 
		
	
		
			
			|  | 936 | +			break;
 | 
		
	
		
			
			| 924 | 937 |  		if ( kind == TCP_OPTION_NOP ) {
 | 
		
	
		
			
			| 925 | 938 |  			data++;
 | 
		
	
		
			
			| 926 | 939 |  			continue;
 | 
		
	
		
			
			| 927 | 940 |  		}
 | 
		
	
		
			
			|  | 941 | +
 | 
		
	
		
			
			|  | 942 | +		/* Handle multi-byte options */
 | 
		
	
		
			
			|  | 943 | +		min = sizeof ( *option );
 | 
		
	
		
			
			| 928 | 944 |  		switch ( kind ) {
 | 
		
	
		
			
			| 929 | 945 |  		case TCP_OPTION_MSS:
 | 
		
	
		
			
			| 930 |  | -			options->mssopt = data;
 | 
		
	
		
			
			|  | 946 | +			/* Ignore received MSS */
 | 
		
	
		
			
			| 931 | 947 |  			break;
 | 
		
	
		
			
			| 932 | 948 |  		case TCP_OPTION_WS:
 | 
		
	
		
			
			| 933 | 949 |  			options->wsopt = data;
 | 
		
	
		
			
			|  | 950 | +			min = sizeof ( *options->wsopt );
 | 
		
	
		
			
			| 934 | 951 |  			break;
 | 
		
	
		
			
			| 935 | 952 |  		case TCP_OPTION_SACK_PERMITTED:
 | 
		
	
		
			
			| 936 | 953 |  			options->spopt = data;
 | 
		
	
		
			
			|  | 954 | +			min = sizeof ( *options->spopt );
 | 
		
	
		
			
			| 937 | 955 |  			break;
 | 
		
	
		
			
			| 938 | 956 |  		case TCP_OPTION_SACK:
 | 
		
	
		
			
			| 939 | 957 |  			/* Ignore received SACKs */
 | 
		
	
		
			
			| 940 | 958 |  			break;
 | 
		
	
		
			
			| 941 | 959 |  		case TCP_OPTION_TS:
 | 
		
	
		
			
			| 942 | 960 |  			options->tsopt = data;
 | 
		
	
		
			
			|  | 961 | +			min = sizeof ( *options->tsopt );
 | 
		
	
		
			
			| 943 | 962 |  			break;
 | 
		
	
		
			
			| 944 | 963 |  		default:
 | 
		
	
		
			
			| 945 | 964 |  			DBGC ( tcp, "TCP %p received unknown option %d\n",
 | 
		
	
		
			
			| 946 | 965 |  			       tcp, kind );
 | 
		
	
		
			
			| 947 | 966 |  			break;
 | 
		
	
		
			
			| 948 | 967 |  		}
 | 
		
	
		
			
			|  | 968 | +		if ( remaining < min ) {
 | 
		
	
		
			
			|  | 969 | +			DBGC ( tcp, "TCP %p received truncated option %d\n",
 | 
		
	
		
			
			|  | 970 | +			       tcp, kind );
 | 
		
	
		
			
			|  | 971 | +			return -EINVAL;
 | 
		
	
		
			
			|  | 972 | +		}
 | 
		
	
		
			
			|  | 973 | +		if ( option->length < min ) {
 | 
		
	
		
			
			|  | 974 | +			DBGC ( tcp, "TCP %p received underlength option %d\n",
 | 
		
	
		
			
			|  | 975 | +			       tcp, kind );
 | 
		
	
		
			
			|  | 976 | +			return -EINVAL;
 | 
		
	
		
			
			|  | 977 | +		}
 | 
		
	
		
			
			|  | 978 | +		if ( option->length > remaining ) {
 | 
		
	
		
			
			|  | 979 | +			DBGC ( tcp, "TCP %p received overlength option %d\n",
 | 
		
	
		
			
			|  | 980 | +			       tcp, kind );
 | 
		
	
		
			
			|  | 981 | +			return -EINVAL;
 | 
		
	
		
			
			|  | 982 | +		}
 | 
		
	
		
			
			| 949 | 983 |  		data += option->length;
 | 
		
	
		
			
			| 950 | 984 |  	}
 | 
		
	
		
			
			|  | 985 | +
 | 
		
	
		
			
			|  | 986 | +	return 0;
 | 
		
	
		
			
			| 951 | 987 |  }
 | 
		
	
		
			
			| 952 | 988 |  
 | 
		
	
		
			
			| 953 | 989 |  /**
 | 
		
	
	
		
			
			|  | @@ -1011,6 +1047,12 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq,
 | 
		
	
		
			
			| 1011 | 1047 |  			tcp->snd_win_scale = options->wsopt->scale;
 | 
		
	
		
			
			| 1012 | 1048 |  			tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE;
 | 
		
	
		
			
			| 1013 | 1049 |  		}
 | 
		
	
		
			
			|  | 1050 | +		DBGC ( tcp, "TCP %p using %stimestamps, %sSACK, TX window "
 | 
		
	
		
			
			|  | 1051 | +		       "x%d, RX window x%d\n", tcp,
 | 
		
	
		
			
			|  | 1052 | +		       ( ( tcp->flags & TCP_TS_ENABLED ) ? "" : "no " ),
 | 
		
	
		
			
			|  | 1053 | +		       ( ( tcp->flags & TCP_SACK_ENABLED ) ? "" : "no " ),
 | 
		
	
		
			
			|  | 1054 | +		       ( 1 << tcp->snd_win_scale ),
 | 
		
	
		
			
			|  | 1055 | +		       ( 1 << tcp->rcv_win_scale ) );
 | 
		
	
		
			
			| 1014 | 1056 |  	}
 | 
		
	
		
			
			| 1015 | 1057 |  
 | 
		
	
		
			
			| 1016 | 1058 |  	/* Ignore duplicate SYN */
 | 
		
	
	
		
			
			|  | @@ -1369,8 +1411,8 @@ static int tcp_rx ( struct io_buffer *iobuf,
 | 
		
	
		
			
			| 1369 | 1411 |  	ack = ntohl ( tcphdr->ack );
 | 
		
	
		
			
			| 1370 | 1412 |  	raw_win = ntohs ( tcphdr->win );
 | 
		
	
		
			
			| 1371 | 1413 |  	flags = tcphdr->flags;
 | 
		
	
		
			
			| 1372 |  | -	tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
 | 
		
	
		
			
			| 1373 |  | -		      ( hlen - sizeof ( *tcphdr ) ), &options );
 | 
		
	
		
			
			|  | 1414 | +	if ( ( rc = tcp_rx_opts ( tcp, tcphdr, hlen, &options ) ) != 0 )
 | 
		
	
		
			
			|  | 1415 | +		goto discard;
 | 
		
	
		
			
			| 1374 | 1416 |  	if ( tcp && options.tsopt )
 | 
		
	
		
			
			| 1375 | 1417 |  		tcp->ts_val = ntohl ( options.tsopt->tsval );
 | 
		
	
		
			
			| 1376 | 1418 |  	iob_pull ( iobuf, hlen );
 |