Merge pull request #6531 from brummer-simon/gnrc_tcp-improvement-option_handling

gnrc_tcp: Improved Option Handling
This commit is contained in:
Martine Lenders 2017-02-01 12:17:10 +01:00 committed by GitHub
commit 794be95395
6 changed files with 97 additions and 103 deletions

View File

@ -29,25 +29,52 @@ extern "C" {
#endif
/**
* @brief TCP Options could contain up to 10 32-Bit values of Information.
* @brief TCP offset value boundries
* @{
*/
#define TCP_MAX_HDR_OPTIONS 10
#define TCP_HDR_OFFSET_MIN (0x05)
#define TCP_HDR_OFFSET_MAX (0x0F)
/** @} */
/**
* @brief TCP Option "Kind" field defines
* @{
*/
#define TCP_OPTION_KIND_EOL (0x00) /**< "End of List"-Option */
#define TCP_OPTION_KIND_NOP (0x01) /**< "No Operatrion"-Option */
#define TCP_OPTION_KIND_MSS (0x02) /**< "Maximum Segment Size"-Option */
/** @} */
/**
* @brief TCP Option Length Field Values
* @{
*/
#define TCP_OPTION_LENGTH_MSS (0x04) /**< MSS Option Size always 4 */
/** @} */
/**
* @brief TCP header definition
*/
typedef struct __attribute__((packed)) {
network_uint16_t src_port; /**< source port, in network byte order */
network_uint16_t dst_port; /**< destination port, in network byte order */
network_uint32_t seq_num; /**< sequence number, in network byte order */
network_uint32_t ack_num; /**< Acknowledgement number, in network byte order */
network_uint16_t off_ctl; /**< Data Offset and control Bits in network byte order */
network_uint16_t window; /**< window, in network byte order */
network_uint16_t checksum; /**< checksum, in network byte order */
network_uint16_t urgent_ptr; /**< urgent pointer, in network byte order */
network_uint32_t options[TCP_MAX_HDR_OPTIONS]; /**< Option Fields (Optional) */
network_uint16_t src_port; /**< source port, in network byte order */
network_uint16_t dst_port; /**< destination port, in network byte order */
network_uint32_t seq_num; /**< sequence number, in network byte order */
network_uint32_t ack_num; /**< Acknowledgement number, in network byte order */
network_uint16_t off_ctl; /**< Data Offset and control Bits in network byte order */
network_uint16_t window; /**< window, in network byte order */
network_uint16_t checksum; /**< checksum, in network byte order */
network_uint16_t urgent_ptr; /**< urgent pointer, in network byte order */
} tcp_hdr_t;
/**
* @brief TCP Option Field Helper Structure
*/
typedef struct __attribute__((packed)) {
uint8_t kind; /**< TCP options Kind field */
uint8_t length; /**< TCP options Length field */
uint8_t value[]; /**< Pointer to TCP options Value field */
} tcp_hdr_opt_t;
#ifdef __cplusplus
}
#endif

View File

@ -611,6 +611,6 @@ gnrc_pktsnip_t *gnrc_tcp_hdr_build(gnrc_pktsnip_t *payload, uint16_t src, uint16
hdr->src_port = byteorder_htons(src);
hdr->dst_port = byteorder_htons(dst);
hdr->checksum = byteorder_htons(0);
hdr->off_ctl = byteorder_htons(OPTION_OFFSET_MAX);
hdr->off_ctl = byteorder_htons(TCP_HDR_OFFSET_MIN);
return res;
}

View File

@ -127,7 +127,7 @@ static int _receive(gnrc_pktsnip_t *pkt)
syn = ((ctl & MSK_SYN_ACK) == MSK_SYN);
/* Validate Offset */
if (GET_OFFSET(ctl) < OPTION_OFFSET_BASE) {
if (GET_OFFSET(ctl) < TCP_HDR_OFFSET_MIN) {
DEBUG("gnrc_tcp_eventloop.c : _receive() : unexpected Offset Value\n");
gnrc_pktbuf_release(pkt);
return -ERANGE;

View File

@ -24,67 +24,59 @@
uint32_t _option_build_mss(uint16_t mss)
{
return (((uint32_t )OPT_KIND_MSS) << 24) | (((uint32_t) OPT_LENGTH_MSS) << 16) | mss;
return (((uint32_t )TCP_OPTION_KIND_MSS) << 24) | (((uint32_t) TCP_OPTION_LENGTH_MSS) << 16) | mss;
}
uint16_t _option_build_offset_control(uint16_t nopts, uint16_t ctl)
{
assert(OPTION_OFFSET_BASE <= nopts && nopts <= OPTION_OFFSET_MAX);
assert(TCP_HDR_OFFSET_MIN <= nopts && nopts <= TCP_HDR_OFFSET_MAX);
return (nopts << 12) | ctl;
}
int _option_parse(gnrc_tcp_tcb_t* tcb, tcp_hdr_t *hdr)
{
uint8_t word_idx = 0;
uint8_t byte_idx = 0;
uint8_t word_end = 0;
uint16_t off_ctl = byteorder_ntohs(hdr->off_ctl);
/* Extract Offset value. Return if no options are set */
uint8_t offset = GET_OFFSET(byteorder_ntohs(hdr->off_ctl));
if (offset <= TCP_HDR_OFFSET_MIN) {
return 0;
}
word_end = GET_OFFSET(off_ctl) - OPTION_OFFSET_BASE;
/* Get Pointer to option field and field-size */
uint8_t *opt_ptr = (uint8_t *) hdr + sizeof(tcp_hdr_t);
uint8_t opt_left = (offset - TCP_HDR_OFFSET_MIN) * 4;
while (word_idx < word_end) {
uint32_t word = byteorder_ntohl(hdr->options[word_idx]);
/* Parse Options via tcp_hdr_opt_t */
while (opt_left > 0) {
tcp_hdr_opt_t *option = (tcp_hdr_opt_t *) opt_ptr;
/* If byte index is not aligned to word index. Fill word with bytes from next word. */
if (byte_idx) {
word >>= (byte_idx * 8);
word |= (byteorder_ntohl(hdr->options[word_idx + 1]) << ((sizeof(word) - byte_idx) * 8));
}
/* Option handling */
switch (OPT_GET_KIND(word)) {
case OPT_KIND_EOL:
DEBUG("gnrc_tcp_option.c : _option_parse() : Option eol\n");
/* Examine current option */
switch (option->kind) {
case TCP_OPTION_KIND_EOL:
DEBUG("gnrc_tcp_option.c : _option_parse() : EOL option found\n");
return 0;
case OPT_KIND_NOP:
byte_idx += 1;
DEBUG("gnrc_tcp_option.c : _option_parse() : Option nop\n");
break;
case TCP_OPTION_KIND_NOP:
DEBUG("gnrc_tcp_option.c : _option_parse() : NOP option found\n");
opt_ptr += 1;
opt_left -= 1;
continue;
case OPT_KIND_MSS:
DEBUG("gnrc_tcp_option.c : _option_parse() : Option mss\n");
if (OPT_GET_LENGTH(word) == OPT_LENGTH_MSS) {
tcb->mss = OPT_GET_VAL_2B(word);
byte_idx += 4;
}
else {
case TCP_OPTION_KIND_MSS:
if (option->length != TCP_OPTION_LENGTH_MSS) {
DEBUG("gnrc_tcp_option.c : _option_parse() : invalid MSS Option length.\n");
return -1;
}
tcb->mss = (option->value[0] << 8) | option->value[1];
DEBUG("gnrc_tcp_option.c : _option_parse() : MSS option found. MSS=%"PRIu16"\n",
tcb->mss);
break;
/* Add options support HERE */
default:
DEBUG("gnrc_tcp_option.c : _option_parse() : Unsupported option received\n");
byte_idx += 1;
}
/* Update index */
if (byte_idx >= 4) {
word_idx += 1;
byte_idx -= 4;
DEBUG("gnrc_tcp_option.c : _option_parse() : Unknown option found.\
KIND=%"PRIu8", LENGTH=%"PRIu8"\n", option->kind, option->length);
}
opt_ptr += option->length;
opt_left -= option->length;
}
return 0;
}

View File

@ -71,13 +71,13 @@ int _pkt_build_reset_from_pkt(gnrc_pktsnip_t **out_pkt, gnrc_pktsnip_t *in_pkt)
/* Seq/Ackno and control flags depend on inputs ACK-Flag */
uint16_t ctl = byteorder_ntohs(tcp_hdr_in->off_ctl);
if (ctl & MSK_ACK) {
tcp_hdr_out.off_ctl = byteorder_htons((OPTION_OFFSET_BASE << 12) | MSK_RST);
tcp_hdr_out.off_ctl = byteorder_htons((TCP_HDR_OFFSET_MIN << 12) | MSK_RST);
tcp_hdr_out.seq_num = tcp_hdr_in->ack_num;
tcp_hdr_out.ack_num = byteorder_htonl(0);
}
else {
uint8_t seq_no = 0;
tcp_hdr_out.off_ctl = byteorder_htons((OPTION_OFFSET_BASE << 12) | MSK_RST_ACK);
tcp_hdr_out.off_ctl = byteorder_htons((TCP_HDR_OFFSET_MIN << 12) | MSK_RST_ACK);
tcp_hdr_out.seq_num = byteorder_htonl(0);
if (ctl & MSK_SYN) {
seq_no += 1;
@ -90,7 +90,7 @@ int _pkt_build_reset_from_pkt(gnrc_pktsnip_t **out_pkt, gnrc_pktsnip_t *in_pkt)
}
/* Allocate new tcp header */
tcp_snp = gnrc_pktbuf_add(NULL, &tcp_hdr_out, OPTION_OFFSET_BASE * 4, GNRC_NETTYPE_TCP);
tcp_snp = gnrc_pktbuf_add(NULL, &tcp_hdr_out, TCP_HDR_OFFSET_MIN * 4, GNRC_NETTYPE_TCP);
if (tcp_snp == NULL) {
DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() : Can't alloc buffer for TCP Header\n.");
*(out_pkt) = NULL;
@ -121,7 +121,7 @@ int _pkt_build(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t **out_pkt, uint16_t *seq_con,
gnrc_pktsnip_t *pay_snp = NULL;
gnrc_pktsnip_t *tcp_snp = NULL;
tcp_hdr_t tcp_hdr;
uint8_t nopts = 0;
uint8_t offset = TCP_HDR_OFFSET_MIN;
/* Add payload, if supplied */
if (payload != NULL && payload_len > 0) {
@ -142,18 +142,16 @@ int _pkt_build(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t **out_pkt, uint16_t *seq_con,
tcp_hdr.window = byteorder_htons(tcb->rcv_wnd);
tcp_hdr.urgent_ptr = byteorder_htons(0);
/* tcp option handling */
/* If this is a syn-message, send mss option */
/* Calculate option field size. */
/* Add MSS option if SYN is sent */
if (ctl & MSK_SYN) {
/* NOTE: MSS usually based on lower layers MTU */
tcp_hdr.options[nopts] = byteorder_htonl(_option_build_mss(GNRC_TCP_MSS));
nopts += 1;
offset += 1;
}
/* Set offset and control bit accordingly */
tcp_hdr.off_ctl = byteorder_htons(_option_build_offset_control(OPTION_OFFSET_BASE + nopts, ctl));
tcp_hdr.off_ctl = byteorder_htons(_option_build_offset_control(offset, ctl));
/* allocate tcp header */
tcp_snp = gnrc_pktbuf_add(pay_snp, &tcp_hdr, (OPTION_OFFSET_BASE + nopts) * 4, GNRC_NETTYPE_TCP);
/* allocate tcp header: size = offset * 4 bytes */
tcp_snp = gnrc_pktbuf_add(pay_snp, &tcp_hdr, offset * 4, GNRC_NETTYPE_TCP);
if (tcp_snp == NULL) {
DEBUG("gnrc_tcp_pkt.c : _pkt_build() : Can't allocate buffer for TCP Header\n.");
gnrc_pktbuf_release(pay_snp);
@ -161,6 +159,22 @@ int _pkt_build(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t **out_pkt, uint16_t *seq_con,
return -ENOMEM;
}
else {
/* Add options if existing */
if (TCP_HDR_OFFSET_MIN < offset) {
uint8_t* opt_ptr = (uint8_t *) tcp_snp->data + sizeof(tcp_hdr);
uint8_t opt_left = (offset - TCP_HDR_OFFSET_MIN) * sizeof(network_uint32_t);
/* Init options field with 'End Of List' - option (0) */
memset(opt_ptr, TCP_OPTION_KIND_EOL, opt_left);
/* If SYN flag is set: Add MSS option */
if (ctl & MSK_SYN) {
network_uint32_t mss_option = byteorder_htonl(_option_build_mss(GNRC_TCP_MSS));
memcpy(opt_ptr, &mss_option, sizeof(mss_option));
}
/* Increase opt_ptr and decrease opt_ptr, if other options are added */
/* NOTE: Add Additional Options here */
}
*(out_pkt) = tcp_snp;
}

View File

@ -30,50 +30,11 @@
extern "C" {
#endif
/**
* @brief TCP Option field boundries
* @{
*/
#define OPTION_OFFSET_BASE (0x5)
#define OPTION_OFFSET_MAX (0xF)
/** @} */
/**
* @brief Extract offset value from offet and ctl bit field.
*/
#define GET_OFFSET( x ) (((x) & MSK_OFFSET) >> 12)
/**
* @brief TCP Option Kind Field Values
* @{
*/
#define OPT_KIND_EOL (00) /**< End of List */
#define OPT_KIND_NOP (01) /**< No Operatrion */
#define OPT_KIND_MSS (02) /**< Maximum Segment Size */
/** @} */
/**
* @brief TCP Option Length Field Values
* @{
*/
#define OPT_LENGTH_MSS (04) /**< MSS Option Size is 4 byte */
/** @} */
/**
* @brief Extract kind field value from 4-byte option field
*/
#define OPT_GET_KIND( x ) (((x) & 0xFF000000) >> 24)
/**
* @brief Extract length field value from 4-byte option field
*/
#define OPT_GET_LENGTH( x ) (((x) & 0x00FF0000) >> 16)
/**
* @brief Extract two byte option value from 4-byte option field
*/
#define OPT_GET_VAL_2B( x ) (((x) & 0x0000FFFF))
/**
* @brief Helper Function to build the MSS Option
*