|
@@ -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 );
|