diff --git a/include/nuttx/net/tcp.h b/include/nuttx/net/tcp.h index 83e4fa1b05b1e..9a76af558a5ed 100644 --- a/include/nuttx/net/tcp.h +++ b/include/nuttx/net/tcp.h @@ -137,6 +137,12 @@ #define TCP_DEFAULT_IPv4_MSS 536 #define TCP_DEFAULT_IPv6_MSS 1220 +/* Minimal accepted MSS. It is (60+60+8) - (20+20). + * (MAX_IP_HDR + MAX_TCP_HDR + MIN_IP_FRAG) - (MIN_IP_HDR + MIN_TCP_HDR) + */ + +#define TCP_MIN_MSS 88 + /* However, we do need to make allowance for certain links such as SLIP that * have unusually small MTUs. */ diff --git a/net/socket/Kconfig b/net/socket/Kconfig index 6877dc3d5fbfd..ada47c6de88a8 100644 --- a/net/socket/Kconfig +++ b/net/socket/Kconfig @@ -43,7 +43,7 @@ config NET_SOCKOPTS Enable or disable support for socket options config NET_TCPPROTO_OPTIONS - bool + bool "TCP proto socket options" default n ---help--- Enable or disable support for TCP protocol level socket options. diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index aae70be268611..c133882542559 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -229,6 +229,10 @@ struct tcp_conn_s uint16_t rport; /* The remoteTCP port, in network byte order */ uint16_t mss; /* Current maximum segment size for the * connection */ +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + uint16_t user_mss; /* Configured maximum segment size for the + * connection */ +#endif uint32_t rcv_adv; /* The right edge of the recv window advertized */ #ifdef CONFIG_NET_TCP_WINDOW_SCALE uint32_t snd_wnd; /* Sequence and acknowledgement numbers of last diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c index 329196e5ad0a3..b5658e019dbf6 100644 --- a/net/tcp/tcp_conn.c +++ b/net/tcp/tcp_conn.c @@ -754,6 +754,28 @@ FAR struct tcp_conn_s *tcp_alloc(uint8_t domain) nxsem_init(&conn->snd_sem, 0, 0); #endif + + /* Set the default value of mss to max, this field will changed when + * receive SYN. + */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (domain == PF_INET) +#endif + { + conn->mss = MIN_IPv4_TCP_INITIAL_MSS; + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + conn->mss = MIN_IPv6_TCP_INITIAL_MSS; + } +#endif /* CONFIG_NET_IPv6 */ } return conn; @@ -1293,9 +1315,6 @@ int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr) FAR const struct sockaddr_in *inaddr = (FAR const struct sockaddr_in *)addr; - /* Save MSS and the port from the sockaddr (already in network order) */ - - conn->mss = MIN_IPv4_TCP_INITIAL_MSS; conn->rport = inaddr->sin_port; /* The sockaddr address is 32-bits in network order. @@ -1327,9 +1346,6 @@ int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr) FAR const struct sockaddr_in6 *inaddr = (FAR const struct sockaddr_in6 *)addr; - /* Save MSS and the port from the sockaddr (already in network order) */ - - conn->mss = MIN_IPv6_TCP_INITIAL_MSS; conn->rport = inaddr->sin6_port; /* The sockaddr address is 128-bits in network order. diff --git a/net/tcp/tcp_getsockopt.c b/net/tcp/tcp_getsockopt.c index 7d815f6a5fa77..c8d3d7142f15b 100644 --- a/net/tcp/tcp_getsockopt.c +++ b/net/tcp/tcp_getsockopt.c @@ -82,11 +82,6 @@ int tcp_getsockopt(FAR struct socket *psock, int option, FAR void *value, FAR socklen_t *value_len) { -#ifdef CONFIG_NET_TCP_KEEPALIVE - /* Keep alive options are the only TCP protocol socket option currently - * supported. - */ - FAR struct tcp_conn_s *conn; int ret; @@ -118,6 +113,7 @@ int tcp_getsockopt(FAR struct socket *psock, int option, * all of the clones that may use the underlying connection. */ +#ifdef CONFIG_NET_TCP_KEEPALIVE case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the * periodic transmission of probes */ if (*value_len < sizeof(int)) @@ -221,6 +217,26 @@ int tcp_getsockopt(FAR struct socket *psock, int option, ret = OK; } break; +#endif /* CONFIG_NET_TCP_KEEPALIVE */ + + case TCP_MAXSEG: /* The maximum segment size */ + if (*value_len < sizeof(int)) + { + /* REVISIT: POSIX says that we should truncate the value if it + * is larger than value_len. That just doesn't make sense + * to me in this case. + */ + + ret = -EINVAL; + } + else + { + FAR int *mss = (FAR int *)value; + *mss = conn->mss; + *value_len = sizeof(int); + ret = OK; + } + break; default: nerr("ERROR: Unrecognized TCP option: %d\n", option); @@ -229,9 +245,6 @@ int tcp_getsockopt(FAR struct socket *psock, int option, } return ret; -#else - return -ENOPROTOOPT; -#endif /* CONFIG_NET_TCP_KEEPALIVE */ } #endif /* CONFIG_NET_TCPPROTO_OPTIONS */ diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c index aca3d8457824f..86c6224a27181 100644 --- a/net/tcp/tcp_input.c +++ b/net/tcp/tcp_input.c @@ -586,6 +586,13 @@ static void tcp_parse_option(FAR struct net_driver_s *dev, tmp16 = ((uint16_t)IPDATA(tcpiplen + 2 + i) << 8) | (uint16_t)IPDATA(tcpiplen + 3 + i); +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + if (conn->user_mss > 0 && conn->user_mss < tcp_mss) + { + tcp_mss = conn->user_mss; + } +#endif + conn->mss = tmp16 > tcp_mss ? tcp_mss : tmp16; } #ifdef CONFIG_NET_TCP_WINDOW_SCALE diff --git a/net/tcp/tcp_send.c b/net/tcp/tcp_send.c index 416e0e5f2151b..edd6422c13192 100644 --- a/net/tcp/tcp_send.c +++ b/net/tcp/tcp_send.c @@ -585,7 +585,16 @@ void tcp_synack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, /* Set the packet length for the TCP Maximum Segment Size */ - tcp_mss = tcp_rx_mss(dev); +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + if (conn->user_mss != 0 && conn->user_mss < tcp_rx_mss(dev)) + { + tcp_mss = conn->user_mss; + } + else +#endif + { + tcp_mss = tcp_rx_mss(dev); + } /* Save the ACK bits */ diff --git a/net/tcp/tcp_setsockopt.c b/net/tcp/tcp_setsockopt.c index d447ea84b1df7..1123d71e9259e 100644 --- a/net/tcp/tcp_setsockopt.c +++ b/net/tcp/tcp_setsockopt.c @@ -72,11 +72,6 @@ int tcp_setsockopt(FAR struct socket *psock, int option, FAR const void *value, socklen_t value_len) { -#ifdef CONFIG_NET_TCP_KEEPALIVE - /* Keep alive options are the only TCP protocol socket option currently - * supported. - */ - FAR struct tcp_conn_s *conn; int ret = OK; @@ -107,6 +102,7 @@ int tcp_setsockopt(FAR struct socket *psock, int option, * all of the clones that may use the underlying connection. */ +#ifdef CONFIG_NET_TCP_KEEPALIVE case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the * periodic transmission of probes */ if (value_len != sizeof(int)) @@ -233,6 +229,35 @@ int tcp_setsockopt(FAR struct socket *psock, int option, } } break; +#endif /* CONFIG_NET_TCP_KEEPALIVE */ + + case TCP_MAXSEG: /* The maximum segment size */ + if (value_len != sizeof(int)) + { + ret = -EFAULT; + } + else + { + int mss = *(FAR int *)value; + + if (conn->tcpstateflags != TCP_ALLOCATED) + { + /* Set TCP_MAXSEG in the wrong state, direct return success */ + + return OK; + } + + if (mss < TCP_MIN_MSS || mss > UINT16_MAX) + { + nerr("ERROR: TCP_MAXSEG value out of range: %d\n", mss); + return -EINVAL; + } + else + { + conn->user_mss = mss; + } + } + break; default: nerr("ERROR: Unrecognized TCP option: %d\n", option); @@ -241,9 +266,6 @@ int tcp_setsockopt(FAR struct socket *psock, int option, } return ret; -#else - return -ENOPROTOOPT; -#endif /* CONFIG_NET_TCP_KEEPALIVE */ } #endif /* CONFIG_NET_TCPPROTO_OPTIONS */