Merge pull request #6531 from brummer-simon/gnrc_tcp-improvement-option_handling
gnrc_tcp: Improved Option Handling
This commit is contained in:
commit
794be95395
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue