Commit cdea2bfa authored by Paul Sokolovsky's avatar Paul Sokolovsky Committed by Jukka Rissanen
Browse files

net: tcp: Add support for TCP options parsing



Add a generic function for TCP option parsing. So far we're
interested only in MSS option value, so that's what it handles.
Use it to parse MSS value in net_context incoming SYN packet
handler.

Signed-off-by: default avatarPaul Sokolovsky <paul.sokolovsky@linaro.org>
parent 3c652996
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -1613,10 +1613,22 @@ NET_CONN_CB(tcp_syn_rcvd)
	 */
	if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_SYN) {
		int r;
		u16_t send_mss = NET_TCP_DEFAULT_MSS;
		int opt_totlen;
		struct net_tcp_options tcp_opts = {
			.mss = NET_TCP_DEFAULT_MSS,
		};

		net_tcp_print_recv_info("SYN", pkt, tcp_hdr->src_port);

		opt_totlen = NET_TCP_HDR_LEN(tcp_hdr)
			     - sizeof(struct net_tcp_hdr);
		/* We expect MSS option to be present (opt_totlen > 0),
		 * so call unconditionally.
		 */
		if (net_tcp_parse_opts(pkt, opt_totlen, &tcp_opts) < 0) {
			return NET_DROP;
		}

		net_tcp_change_state(tcp, NET_TCP_SYN_RCVD);

		/* Set TCP seq and ack which are then stored in the backlog */
@@ -1627,7 +1639,7 @@ NET_CONN_CB(tcp_syn_rcvd)

		/* Get MSS from TCP options here*/

		r = tcp_backlog_syn(pkt, context, send_mss);
		r = tcp_backlog_syn(pkt, context, tcp_opts.mss);
		if (r < 0) {
			if (r == -EADDRINUSE) {
				NET_DBG("TCP connection already exists");
+77 −2
Original line number Diff line number Diff line
@@ -600,7 +600,7 @@ u16_t net_tcp_get_recv_mss(const struct net_tcp *tcp)
static void net_tcp_set_syn_opt(struct net_tcp *tcp, u8_t *options,
				u8_t *optionlen)
{
	u16_t recv_mss;
	u32_t recv_mss;

	*optionlen = 0;

@@ -611,7 +611,8 @@ static void net_tcp_set_syn_opt(struct net_tcp *tcp, u8_t *options,
		recv_mss = 0;
	}

	UNALIGNED_PUT(htonl((u32_t)recv_mss | NET_TCP_MSS_HEADER),
	recv_mss |= (NET_TCP_MSS_OPT << 24) | (NET_TCP_MSS_SIZE << 16);
	UNALIGNED_PUT(htonl(recv_mss),
		      (u32_t *)(options + *optionlen));

	*optionlen += NET_TCP_MSS_SIZE;
@@ -1235,3 +1236,77 @@ struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag)

	return frag;
}

int net_tcp_parse_opts(struct net_pkt *pkt, int opt_totlen,
		       struct net_tcp_options *opts)
{
	struct net_buf *frag = pkt->frags;
	u16_t pos = net_pkt_ip_hdr_len(pkt)
		  + net_pkt_ipv6_ext_len(pkt)
		  + sizeof(struct net_tcp_hdr);
	u8_t opt, optlen;

	/* TODO: this should be done for each TCP pkt, on reception */
	if (pos + opt_totlen > net_pkt_get_len(pkt)) {
		NET_ERR("Truncated pkt len: %d, expected: %d",
			(int)net_pkt_get_len(pkt), pos + opt_totlen);
		return -EINVAL;
	}

	while (opt_totlen) {
		frag = net_frag_read(frag, pos, &pos, sizeof(opt), &opt);
		opt_totlen--;

		/* https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-parameters-1 */
		/* "Options 0 and 1 are exactly one octet which is their
		 * kind field.  All other options have their one octet
		 * kind field, followed by a one octet length field,
		 * followed by length-2 octets of option data."
		 */
		if (opt == NET_TCP_END_OPT) {
			break;
		} else if (opt == NET_TCP_NOP_OPT) {
			continue;
		}

		if (!opt_totlen) {
			optlen = 0;
			goto error;
		}

		frag = net_frag_read(frag, pos, &pos, sizeof(optlen), &optlen);
		opt_totlen--;
		if (optlen < 2) {
			goto error;
		}

		/* Subtract opt/optlen size now to avoid doing this
		 * repeatedly.
		 */
		optlen -= 2;
		if (opt_totlen < optlen) {
			goto error;
		}

		switch (opt) {
		case NET_TCP_MSS_OPT:
			if (optlen != 2) {
				goto error;
			}
			frag = net_frag_read(frag, pos, &pos,
					     optlen, (u8_t *)&opts->mss);
			break;
		default:
			frag = net_frag_skip(frag, pos, &pos, optlen);
			break;
		}

		opt_totlen -= optlen;
	}

	return 0;

error:
	NET_ERR("Invalid TCP opt: %d len: %d", opt, optlen);
	return -EINVAL;
}
+33 −5
Original line number Diff line number Diff line
@@ -89,11 +89,22 @@ enum net_tcp_state {

#define NET_TCP_MAX_OPT_SIZE  8

#define NET_TCP_MSS_HEADER    0x02040000 /* MSS option */
#define NET_TCP_WINDOW_HEADER 0x30300    /* Window scale option */

#define NET_TCP_MSS_SIZE      4          /* MSS option size */
#define NET_TCP_WINDOW_SIZE   3          /* Window scale option size */
/* TCP Option codes */
#define NET_TCP_END_OPT          0
#define NET_TCP_NOP_OPT          1
#define NET_TCP_MSS_OPT          2
#define NET_TCP_WINDOW_SCALE_OPT 3

/* TCP Option sizes */
#define NET_TCP_END_SIZE          1
#define NET_TCP_NOP_SIZE          1
#define NET_TCP_MSS_SIZE          4
#define NET_TCP_WINDOW_SCALE_SIZE 3

/** Parsed TCP option values for net_tcp_parse_opts()  */
struct net_tcp_options {
	u16_t mss;
};

/* Max received bytes to buffer internally */
#define NET_TCP_BUF_MAX_LEN 1280
@@ -443,6 +454,23 @@ struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag);
 */
u16_t net_tcp_get_chksum(struct net_pkt *pkt, struct net_buf *frag);

/**
 * @brief Parse TCP options from network packet.
 *
 * Parse TCP options, returning MSS value (as that the only one we
 * handle so far).
 *
 * @param pkt Network packet
 * @param opt_totlen Total length of options to parse
 * @param opts Pointer to TCP options structure. (Each option is updated
 * only if present, so the structure must be initialized with the default
 * values.)
 *
 * @return 0 if no error, <0 in case of error
 */
int net_tcp_parse_opts(struct net_pkt *pkt, int opt_totlen,
		       struct net_tcp_options *opts);

#else

static inline u16_t net_tcp_get_chksum(struct net_pkt *pkt,