Skip to content

Commit

Permalink
net: tcp: Add support for TCP options parsing
Browse files Browse the repository at this point in the history
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: Paul Sokolovsky <paul.sokolovsky@linaro.org>
  • Loading branch information
pfalcon authored and jukkar committed Nov 8, 2017
1 parent 3c65299 commit cdea2bf
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 9 deletions.
16 changes: 14 additions & 2 deletions subsys/net/ip/net_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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");
Expand Down
79 changes: 77 additions & 2 deletions subsys/net/ip/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
38 changes: 33 additions & 5 deletions subsys/net/ip/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit cdea2bf

Please sign in to comment.