mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-11 17:04:33 +07:00
add TCP backlog to handle client MSS
This commit is contained in:
+78
-11
@@ -4739,7 +4739,7 @@ static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
}
|
||||
|
||||
static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
struct pkt *pkt) {
|
||||
struct pkt *pkt, uint16_t mss) {
|
||||
struct mg_connection *c = mg_alloc_conn(lsn->mgr);
|
||||
struct connstate *s;
|
||||
if (c == NULL) {
|
||||
@@ -4747,7 +4747,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
return NULL;
|
||||
}
|
||||
s = (struct connstate *) (c + 1);
|
||||
s->dmss = 536; // assume default, RFC-9293 3.7.1
|
||||
s->dmss = mss; // from options in client SYN
|
||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
|
||||
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
|
||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
@@ -4851,7 +4851,7 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
||||
if (pkt->tcp->flags & TH_FIN) {
|
||||
uint8_t flags = TH_ACK;
|
||||
if (mg_ntohl(pkt->tcp->seq) != s->ack) {
|
||||
MG_VERBOSE(("ignoring FIN, SEQ != ACK: %x %x", mg_ntohl(pkt->tcp->seq), s->ack));
|
||||
MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack));
|
||||
tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
|
||||
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
|
||||
return;
|
||||
@@ -4934,6 +4934,57 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TCP backlog
|
||||
struct mg_backlog {
|
||||
uint16_t port, mss; // use port=0 for available entries
|
||||
uint8_t age;
|
||||
};
|
||||
|
||||
static int backlog_insert(struct mg_connection *c, uint16_t port,
|
||||
uint16_t mss) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
|
||||
if (p[i].port != 0) continue;
|
||||
p[i].age = 2; // remove after two calls, average 1.5 call rate
|
||||
p[i].port = port, p[i].mss = mss;
|
||||
return (int) i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct mg_backlog *backlog_retrieve(struct mg_connection *c,
|
||||
uint16_t key, uint16_t port) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
if (key >= sizeof(c->data) / sizeof(*p)) return NULL;
|
||||
if (p[key].port != port) return NULL;
|
||||
p += key;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void backlog_remove(struct mg_connection *c, uint16_t key) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
p[key].port = 0;
|
||||
}
|
||||
|
||||
static void backlog_maintain(struct mg_connection *c) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
size_t i; // dec age and remove those where it reaches 0
|
||||
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
|
||||
if (p[i].port == 0) continue;
|
||||
if (p[i].age != 0) --p[i].age;
|
||||
if (p[i].age == 0) p[i].port = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void backlog_poll(struct mg_mgr *mgr) {
|
||||
struct mg_connection *c = NULL;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
if (!c->is_udp && c->is_listening) backlog_maintain(c);
|
||||
}
|
||||
}
|
||||
|
||||
// process options (MSS)
|
||||
static void handle_opt(struct connstate *s, struct tcp *tcp) {
|
||||
uint8_t *opts = (uint8_t *) (tcp + 1);
|
||||
@@ -4991,15 +5042,30 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
|
||||
// ignore RST if not connected
|
||||
} else if (pkt->tcp->flags & TH_SYN) {
|
||||
// TODO(): handle_opt(s, pkt->tcp); // process options (MSS)
|
||||
// At this point, s = NULL, there is no connection.
|
||||
// Use peer's source port as ISN, in order to recognise the handshake
|
||||
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
|
||||
tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
|
||||
struct connstate cs; // At this point, s = NULL, there is no connection
|
||||
int key;
|
||||
uint32_t isn;
|
||||
if (pkt->tcp->sport != 0) {
|
||||
handle_opt(&cs, pkt->tcp); // process options (MSS)
|
||||
key = backlog_insert(c, pkt->tcp->sport, cs.dmss); // backlog options (MSS)
|
||||
if (key < 0) return; // no room in backlog, discard SYN, client retries
|
||||
// Use peer's src port and bl key as ISN, to later identify the handshake
|
||||
isn = (mg_htonl(((uint32_t)key << 16) | mg_ntohs(pkt->tcp->sport)));
|
||||
tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
|
||||
} // what should we do when port=0 ? Linux takes port 0 as any other port
|
||||
} else if (pkt->tcp->flags & TH_FIN) {
|
||||
tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
|
||||
accept_conn(c, pkt);
|
||||
} else if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) ==
|
||||
mg_htons(pkt->tcp->sport)) {
|
||||
uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16);
|
||||
struct mg_backlog *b = backlog_retrieve(c, key, pkt->tcp->sport);
|
||||
if (b != NULL) {
|
||||
accept_conn(c, pkt, b->mss); // pass options
|
||||
backlog_remove(c, key);
|
||||
} else if (!c->is_accepted) { // not an actual match, reset
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
// TODO(scaprile): revisit this and below, weird scenarios
|
||||
}
|
||||
} else if (!c->is_accepted) { // no peer
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else {
|
||||
@@ -5127,6 +5193,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
||||
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
|
||||
ifp->ndrop, ifp->nerr));
|
||||
#endif
|
||||
backlog_poll(ifp->mgr);
|
||||
}
|
||||
// Handle gw ARP request timeout, order is important
|
||||
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
|
||||
@@ -5460,7 +5527,7 @@ static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) {
|
||||
MG_DEBUG(("Set DNS URL to %s", dnsc->url));
|
||||
if (ifp->mgr->use_dns6) return;
|
||||
if (dnsc->c != NULL) mg_close_conn(dnsc->c);
|
||||
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
|
||||
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
|
||||
MG_ERROR(("DNS connection creation failed"));
|
||||
}
|
||||
|
||||
|
||||
+35
-35
@@ -3693,6 +3693,41 @@ struct mg_tcpip_driver_tms570_data {
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
|
||||
|
||||
struct mg_tcpip_driver_xmc7_data {
|
||||
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
|
||||
uint8_t phy_addr;
|
||||
};
|
||||
|
||||
#ifndef MG_TCPIP_PHY_ADDR
|
||||
#define MG_TCPIP_PHY_ADDR 0
|
||||
#endif
|
||||
|
||||
#ifndef MG_DRIVER_MDC_CR
|
||||
#define MG_DRIVER_MDC_CR 3
|
||||
#endif
|
||||
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_driver_xmc7_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
||||
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
||||
mif_.ip = MG_TCPIP_IP; \
|
||||
mif_.mask = MG_TCPIP_MASK; \
|
||||
mif_.gw = MG_TCPIP_GW; \
|
||||
mif_.driver = &mg_tcpip_driver_xmc7; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
|
||||
|
||||
struct mg_tcpip_driver_xmc_data {
|
||||
@@ -3739,41 +3774,6 @@ struct mg_tcpip_driver_xmc_data {
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
|
||||
|
||||
struct mg_tcpip_driver_xmc7_data {
|
||||
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
|
||||
uint8_t phy_addr;
|
||||
};
|
||||
|
||||
#ifndef MG_TCPIP_PHY_ADDR
|
||||
#define MG_TCPIP_PHY_ADDR 0
|
||||
#endif
|
||||
|
||||
#ifndef MG_DRIVER_MDC_CR
|
||||
#define MG_DRIVER_MDC_CR 3
|
||||
#endif
|
||||
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_driver_xmc7_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
||||
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
||||
mif_.ip = MG_TCPIP_IP; \
|
||||
mif_.mask = MG_TCPIP_MASK; \
|
||||
mif_.gw = MG_TCPIP_GW; \
|
||||
mif_.driver = &mg_tcpip_driver_xmc7; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+78
-11
@@ -582,7 +582,7 @@ static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
}
|
||||
|
||||
static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
struct pkt *pkt) {
|
||||
struct pkt *pkt, uint16_t mss) {
|
||||
struct mg_connection *c = mg_alloc_conn(lsn->mgr);
|
||||
struct connstate *s;
|
||||
if (c == NULL) {
|
||||
@@ -590,7 +590,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
return NULL;
|
||||
}
|
||||
s = (struct connstate *) (c + 1);
|
||||
s->dmss = 536; // assume default, RFC-9293 3.7.1
|
||||
s->dmss = mss; // from options in client SYN
|
||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
|
||||
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
|
||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
@@ -694,7 +694,7 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
||||
if (pkt->tcp->flags & TH_FIN) {
|
||||
uint8_t flags = TH_ACK;
|
||||
if (mg_ntohl(pkt->tcp->seq) != s->ack) {
|
||||
MG_VERBOSE(("ignoring FIN, SEQ != ACK: %x %x", mg_ntohl(pkt->tcp->seq), s->ack));
|
||||
MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack));
|
||||
tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
|
||||
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
|
||||
return;
|
||||
@@ -777,6 +777,57 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TCP backlog
|
||||
struct mg_backlog {
|
||||
uint16_t port, mss; // use port=0 for available entries
|
||||
uint8_t age;
|
||||
};
|
||||
|
||||
static int backlog_insert(struct mg_connection *c, uint16_t port,
|
||||
uint16_t mss) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
|
||||
if (p[i].port != 0) continue;
|
||||
p[i].age = 2; // remove after two calls, average 1.5 call rate
|
||||
p[i].port = port, p[i].mss = mss;
|
||||
return (int) i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct mg_backlog *backlog_retrieve(struct mg_connection *c,
|
||||
uint16_t key, uint16_t port) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
if (key >= sizeof(c->data) / sizeof(*p)) return NULL;
|
||||
if (p[key].port != port) return NULL;
|
||||
p += key;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void backlog_remove(struct mg_connection *c, uint16_t key) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
p[key].port = 0;
|
||||
}
|
||||
|
||||
static void backlog_maintain(struct mg_connection *c) {
|
||||
struct mg_backlog *p = (struct mg_backlog *) c->data;
|
||||
size_t i; // dec age and remove those where it reaches 0
|
||||
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
|
||||
if (p[i].port == 0) continue;
|
||||
if (p[i].age != 0) --p[i].age;
|
||||
if (p[i].age == 0) p[i].port = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void backlog_poll(struct mg_mgr *mgr) {
|
||||
struct mg_connection *c = NULL;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
if (!c->is_udp && c->is_listening) backlog_maintain(c);
|
||||
}
|
||||
}
|
||||
|
||||
// process options (MSS)
|
||||
static void handle_opt(struct connstate *s, struct tcp *tcp) {
|
||||
uint8_t *opts = (uint8_t *) (tcp + 1);
|
||||
@@ -834,15 +885,30 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
|
||||
// ignore RST if not connected
|
||||
} else if (pkt->tcp->flags & TH_SYN) {
|
||||
// TODO(): handle_opt(s, pkt->tcp); // process options (MSS)
|
||||
// At this point, s = NULL, there is no connection.
|
||||
// Use peer's source port as ISN, in order to recognise the handshake
|
||||
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
|
||||
tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
|
||||
struct connstate cs; // At this point, s = NULL, there is no connection
|
||||
int key;
|
||||
uint32_t isn;
|
||||
if (pkt->tcp->sport != 0) {
|
||||
handle_opt(&cs, pkt->tcp); // process options (MSS)
|
||||
key = backlog_insert(c, pkt->tcp->sport, cs.dmss); // backlog options (MSS)
|
||||
if (key < 0) return; // no room in backlog, discard SYN, client retries
|
||||
// Use peer's src port and bl key as ISN, to later identify the handshake
|
||||
isn = (mg_htonl(((uint32_t)key << 16) | mg_ntohs(pkt->tcp->sport)));
|
||||
tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
|
||||
} // what should we do when port=0 ? Linux takes port 0 as any other port
|
||||
} else if (pkt->tcp->flags & TH_FIN) {
|
||||
tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
|
||||
accept_conn(c, pkt);
|
||||
} else if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) ==
|
||||
mg_htons(pkt->tcp->sport)) {
|
||||
uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16);
|
||||
struct mg_backlog *b = backlog_retrieve(c, key, pkt->tcp->sport);
|
||||
if (b != NULL) {
|
||||
accept_conn(c, pkt, b->mss); // pass options
|
||||
backlog_remove(c, key);
|
||||
} else if (!c->is_accepted) { // not an actual match, reset
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
// TODO(scaprile): revisit this and below, weird scenarios
|
||||
}
|
||||
} else if (!c->is_accepted) { // no peer
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else {
|
||||
@@ -970,6 +1036,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
||||
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
|
||||
ifp->ndrop, ifp->nerr));
|
||||
#endif
|
||||
backlog_poll(ifp->mgr);
|
||||
}
|
||||
// Handle gw ARP request timeout, order is important
|
||||
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
|
||||
@@ -1303,7 +1370,7 @@ static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) {
|
||||
MG_DEBUG(("Set DNS URL to %s", dnsc->url));
|
||||
if (ifp->mgr->use_dns6) return;
|
||||
if (dnsc->c != NULL) mg_close_conn(dnsc->c);
|
||||
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
|
||||
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
|
||||
MG_ERROR(("DNS connection creation failed"));
|
||||
}
|
||||
|
||||
|
||||
+222
-86
@@ -2,6 +2,7 @@
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
|
||||
#include "mongoose.c" // order is important, this one first
|
||||
|
||||
#include "driver_mock.c"
|
||||
|
||||
static int s_num_tests = 0;
|
||||
@@ -134,86 +135,157 @@ static bool received_response(struct driver_data *driver) {
|
||||
return was_ready;
|
||||
}
|
||||
|
||||
static void create_tcp_pkt(struct eth *e, struct ip *ip, uint32_t seq,
|
||||
uint32_t ack, uint8_t flags, size_t payload_len) {
|
||||
static void create_tcp_seg(struct eth *e, struct ip *ip, uint32_t seq,
|
||||
uint32_t ack, uint8_t flags, uint16_t sport,
|
||||
uint16_t dport, size_t payload_len, void *opts, unsigned int opts_len) {
|
||||
struct tcp t;
|
||||
memset(&t, 0, sizeof(struct tcp));
|
||||
t.flags = flags;
|
||||
t.seq = mg_htonl(seq);
|
||||
t.ack = mg_htonl(ack);
|
||||
t.off = 5 << 4;
|
||||
t.sport = mg_htons(sport);
|
||||
t.dport = mg_htons(dport);
|
||||
t.off = (uint8_t) (sizeof(t) / 4 << 4) + (uint8_t) (opts_len / 4 << 4);
|
||||
memcpy(s_driver_data.buf, e, sizeof(*e));
|
||||
ip->len = mg_htons((uint16_t)(sizeof(*ip) + sizeof(struct tcp) + payload_len));
|
||||
ip->len =
|
||||
mg_htons((uint16_t) (sizeof(*ip) + sizeof(struct tcp) + payload_len));
|
||||
memcpy(s_driver_data.buf + sizeof(*e), ip, sizeof(*ip));
|
||||
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip), &t, sizeof(t));
|
||||
s_driver_data.len = sizeof(*e) + sizeof(*ip) + sizeof(t) + payload_len;
|
||||
if (opts != NULL && opts_len)
|
||||
memcpy(s_driver_data.buf + sizeof(*e) + sizeof(*ip) + sizeof(t), opts, opts_len);
|
||||
s_driver_data.len = sizeof(*e) + sizeof(*ip) + sizeof(t) + payload_len + opts_len;
|
||||
}
|
||||
|
||||
static void init_tcp_handshake(struct eth *e, struct ip *ip, struct tcp *tcp,
|
||||
struct mg_mgr *mgr) {
|
||||
// SYN
|
||||
create_tcp_pkt(e, ip, 1000, 0, TH_SYN | TH_ACK, 0);
|
||||
static void create_tcp_simpleseg(struct eth *e, struct ip *ip, uint32_t seq,
|
||||
uint32_t ack, uint8_t flags,
|
||||
size_t payload_len) {
|
||||
// use sport=1 to ease seqno stuff, dport=80 due to init_tcp_tests() below
|
||||
create_tcp_seg(e, ip, seq, ack, flags, 1, 80, payload_len, NULL, 0);
|
||||
}
|
||||
|
||||
static void init_tcp_tests(struct mg_mgr *mgr, struct eth *e, struct ip *ip,
|
||||
struct mg_tcpip_driver *driver,
|
||||
struct mg_tcpip_if *mif, mg_event_handler_t f) {
|
||||
mg_mgr_init(mgr);
|
||||
memset(mif, 0, sizeof(*mif));
|
||||
memset(&s_driver_data, 0, sizeof(struct driver_data));
|
||||
driver->init = NULL, driver->tx = if_tx, driver->poll = if_poll,
|
||||
driver->rx = if_rx;
|
||||
mif->driver = driver;
|
||||
mif->driver_data = &s_driver_data;
|
||||
mg_tcpip_init(mgr, mif);
|
||||
mg_http_listen(mgr, "http://0.0.0.0:80", f, NULL);
|
||||
mgr->conns->pfn = NULL; // HTTP handler not needed
|
||||
mg_mgr_poll(mgr, 0);
|
||||
|
||||
// setting the Ethernet header
|
||||
memset(e, 0, sizeof(*e));
|
||||
memcpy(e->dst, mif->mac, 6 * sizeof(uint8_t));
|
||||
e->type = mg_htons(0x800);
|
||||
|
||||
// setting the IP header
|
||||
memset(ip, 0, sizeof(*ip));
|
||||
ip->ver = 4 << 4 | 5;
|
||||
ip->proto = 6;
|
||||
}
|
||||
|
||||
static void init_tcp_handshake(struct eth *e, struct ip *ip,
|
||||
struct mg_mgr *mgr) {
|
||||
struct tcp *t = (struct tcp *)(s_driver_data.buf + sizeof(*e) + sizeof(*ip));
|
||||
|
||||
// SYN
|
||||
create_tcp_simpleseg(e, ip, 1000, 0, TH_SYN | TH_ACK, 0);
|
||||
mg_mgr_poll(mgr, 0); // make sure we clean former stuff in buffer
|
||||
|
||||
// SYN-ACK
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(mgr, 0);
|
||||
tcp = (struct tcp *) (s_driver_data.buf + sizeof(struct eth) +
|
||||
sizeof(struct ip));
|
||||
ASSERT((tcp->flags == (TH_SYN | TH_ACK)));
|
||||
ASSERT((tcp->ack == mg_htonl(1001)));
|
||||
ASSERT((t->flags == (TH_SYN | TH_ACK)));
|
||||
ASSERT((t->ack == mg_htonl(1001)));
|
||||
|
||||
// ACK
|
||||
create_tcp_pkt(e, ip, 1001, 1, TH_ACK, 0);
|
||||
create_tcp_simpleseg(e, ip, 1001, 2, TH_ACK, 0);
|
||||
mg_mgr_poll(mgr, 0);
|
||||
}
|
||||
|
||||
|
||||
static void test_tcp_basics(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct eth e;
|
||||
struct ip ip;
|
||||
struct tcp *t = (struct tcp *) (s_driver_data.buf + sizeof(e) + sizeof(ip));
|
||||
// uint64_t start, now;
|
||||
// bool response_recv = true;
|
||||
struct mg_tcpip_driver driver;
|
||||
struct mg_tcpip_if mif;
|
||||
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, fn);
|
||||
|
||||
// send SYN to a non-used port, expect RST + ACK
|
||||
create_tcp_seg(&e, &ip, 1234, 1, TH_SYN, 0, 69, 0, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT((t->flags == (TH_RST | TH_ACK)));
|
||||
ASSERT((t->ack == mg_htonl(1235)));
|
||||
|
||||
// send data to a non-used port, expect a RST + ACK
|
||||
create_tcp_seg(&e, &ip, 1234, 1, TH_PUSH, 0, 69, 0, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT((t->flags == (TH_RST | TH_ACK)));
|
||||
ASSERT((t->ack == mg_htonl(1234)));
|
||||
|
||||
|
||||
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
|
||||
// no MSS sent, so it must default to 536 (RFC-9293 3.7.1)
|
||||
ASSERT((((struct connstate *)(mgr.conns + 1))->dmss == 536));
|
||||
|
||||
// segment with seq_no within window
|
||||
create_tcp_simpleseg(&e, &ip, 1010, 2, TH_PUSH, 2);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT((t->flags == TH_ACK));
|
||||
ASSERT((t->ack == mg_htonl(1001))); // expecting 1001, dude
|
||||
|
||||
// segment with seq_no way out of window
|
||||
create_tcp_simpleseg(&e, &ip, 1000000, 2, TH_PUSH, 2);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT((t->flags == TH_ACK));
|
||||
ASSERT((t->ack == mg_htonl(1001))); // expecting 1001, dude
|
||||
|
||||
// we currently don't validate checksum, no silently discarded segment test
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
}
|
||||
|
||||
// NOTE: a 1-byte payload could be an erroneous Keep-Alive, keep length > 1 in
|
||||
// this operation, we're testing retransmissions and having len=1 won't work
|
||||
|
||||
static void test_retransmit(void) {
|
||||
static void test_tcp_retransmit(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct eth e;
|
||||
struct ip ip;
|
||||
struct tcp *t = NULL;
|
||||
struct tcp *t = (struct tcp *) (s_driver_data.buf + sizeof(e) + sizeof(ip));
|
||||
uint64_t start, now;
|
||||
bool response_recv = true;
|
||||
struct mg_tcpip_driver driver;
|
||||
struct mg_tcpip_if mif;
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
memset(&mif, 0, sizeof(mif));
|
||||
memset(&s_driver_data, 0, sizeof(struct driver_data));
|
||||
driver.init = NULL, driver.tx = if_tx, driver.poll = if_poll,
|
||||
driver.rx = if_rx;
|
||||
mif.driver = &driver;
|
||||
mif.driver_data = &s_driver_data;
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0:0", fn, NULL);
|
||||
mgr.conns->pfn = NULL; // HTTP handler not needed
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, fn);
|
||||
|
||||
// setting the Ethernet header
|
||||
memset(&e, 0, sizeof(e));
|
||||
memcpy(e.dst, mif.mac, 6 * sizeof(uint8_t));
|
||||
e.type = mg_htons(0x800);
|
||||
|
||||
// setting the IP header
|
||||
memset(&ip, 0, sizeof(ip));
|
||||
ip.ver = 4 << 4, ip.proto = 6;
|
||||
|
||||
init_tcp_handshake(&e, &ip, t, &mgr);
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
|
||||
// packet with seq_no = 1001
|
||||
create_tcp_pkt(&e, &ip, 1001, 1, TH_PUSH | TH_ACK, 2);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 2, TH_PUSH | TH_ACK, 2);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
t = (struct tcp *) (s_driver_data.buf + sizeof(struct eth) +
|
||||
sizeof(struct ip));
|
||||
ASSERT((t->flags == TH_ACK));
|
||||
ASSERT((t->ack == mg_htonl(1003))); // OK
|
||||
|
||||
// resend packet with seq_no = 1001 (e.g.: MIP ACK lost)
|
||||
create_tcp_pkt(&e, &ip, 1001, 1, TH_PUSH | TH_ACK, 2);
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 2, TH_PUSH | TH_ACK, 2);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
start = mg_millis();
|
||||
while (!received_response(&s_driver_data)) {
|
||||
@@ -228,7 +300,7 @@ static void test_retransmit(void) {
|
||||
ASSERT((!response_recv)); // replies should not be sent for duplicate packets
|
||||
|
||||
// packet with seq_no = 1003 got lost/delayed, send seq_no = 1005
|
||||
create_tcp_pkt(&e, &ip, 1005, 1, TH_PUSH | TH_ACK, 2);
|
||||
create_tcp_simpleseg(&e, &ip, 1005, 2, TH_PUSH | TH_ACK, 2);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
start = mg_millis();
|
||||
while (!received_response(&s_driver_data)) {
|
||||
@@ -237,22 +309,17 @@ static void test_retransmit(void) {
|
||||
if (now > 2 * MIP_TCP_ACK_MS)
|
||||
ASSERT(0); // response should have been received by now
|
||||
}
|
||||
t = (struct tcp *) (s_driver_data.buf + sizeof(struct eth) +
|
||||
sizeof(struct ip));
|
||||
ASSERT((t->flags == TH_ACK));
|
||||
ASSERT((t->ack == mg_htonl(1003))); // dup ACK
|
||||
|
||||
// retransmitting packet with seq_no = 1003
|
||||
create_tcp_pkt(&e, &ip, 1003, 1, TH_PUSH | TH_ACK, 2);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
create_tcp_simpleseg(&e, &ip, 1003, 2, TH_PUSH | TH_ACK, 2);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
t = (struct tcp *) (s_driver_data.buf + sizeof(struct eth) +
|
||||
sizeof(struct ip));
|
||||
ASSERT((t->flags == TH_ACK));
|
||||
ASSERT((t->ack == mg_htonl(1005))); // OK
|
||||
|
||||
// packet with seq_no = 1005 got delayed, send FIN with seq_no = 1007
|
||||
create_tcp_pkt(&e, &ip, 1007, 1, TH_FIN, 0);
|
||||
create_tcp_simpleseg(&e, &ip, 1007, 2, TH_FIN, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
start = mg_millis();
|
||||
while (!received_response(&s_driver_data)) {
|
||||
@@ -261,28 +328,20 @@ static void test_retransmit(void) {
|
||||
if (now > 2 * MIP_TCP_ACK_MS)
|
||||
ASSERT(0); // response should have been received by now
|
||||
}
|
||||
t = (struct tcp *) (s_driver_data.buf + sizeof(struct eth) +
|
||||
sizeof(struct ip));
|
||||
ASSERT((t->flags == TH_ACK));
|
||||
ASSERT((t->ack == mg_htonl(1005))); // dup ACK
|
||||
|
||||
// retransmitting packet with seq_no = 1005
|
||||
create_tcp_pkt(&e, &ip, 1005, 1, TH_PUSH | TH_ACK, 2);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
create_tcp_simpleseg(&e, &ip, 1005, 2, TH_PUSH | TH_ACK, 2);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
t = (struct tcp *) (s_driver_data.buf + sizeof(struct eth) +
|
||||
sizeof(struct ip));
|
||||
ASSERT((t->flags == TH_ACK));
|
||||
ASSERT((t->ack == mg_htonl(1007))); // OK
|
||||
|
||||
// retransmitting FIN packet with seq_no = 1007
|
||||
create_tcp_pkt(&e, &ip, 1007, 1, TH_FIN | TH_ACK, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
create_tcp_simpleseg(&e, &ip, 1007, 2, TH_FIN | TH_ACK, 0);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
t = (struct tcp *) (s_driver_data.buf + sizeof(struct eth) +
|
||||
sizeof(struct ip));
|
||||
ASSERT((t->flags == (TH_FIN | TH_ACK))); // check we respond with FIN ACK
|
||||
ASSERT((t->ack == mg_htonl(1008))); // OK
|
||||
ASSERT((t->ack == mg_htonl(1008))); // OK
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
@@ -292,36 +351,16 @@ static void test_frag_recv_path(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct eth e;
|
||||
struct ip ip;
|
||||
struct tcp *t = NULL;
|
||||
struct mg_tcpip_driver driver;
|
||||
struct mg_tcpip_if mif;
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
memset(&mif, 0, sizeof(mif));
|
||||
memset(&s_driver_data, 0, sizeof(struct driver_data));
|
||||
driver.init = NULL, driver.tx = if_tx, driver.poll = if_poll,
|
||||
driver.rx = if_rx;
|
||||
mif.driver = &driver;
|
||||
mif.driver_data = &s_driver_data;
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0:0", frag_recv_fn, NULL);
|
||||
mgr.conns->pfn = NULL;
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, frag_recv_fn);
|
||||
|
||||
// setting the Ethernet header
|
||||
memset(&e, 0, sizeof(e));
|
||||
memcpy(e.dst, mif.mac, 6 * sizeof(uint8_t));
|
||||
e.type = mg_htons(0x800);
|
||||
|
||||
// setting the IP header
|
||||
memset(&ip, 0, sizeof(ip));
|
||||
ip.ver = 0x45, ip.proto = 6;
|
||||
|
||||
init_tcp_handshake(&e, &ip, t, &mgr);
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
|
||||
// send fragmented TCP packet
|
||||
ip.frag |= IP_MORE_FRAGS_MSK; // setting More Fragments bit to 1
|
||||
create_tcp_pkt(&e, &ip, 1001, 1, TH_PUSH | TH_ACK, 1000);
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 2, TH_PUSH | TH_ACK, 1000);
|
||||
s_sent_fragment = 1; // "enable" fn
|
||||
mg_mgr_poll(&mgr, 0); // call it (process fake frag IP)
|
||||
ASSERT(s_sent_fragment == 2); // check it followed the right path
|
||||
@@ -345,7 +384,7 @@ static void test_frag_send_path(void) {
|
||||
mif.driver_data = &s_driver_data;
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
mif.mtu = 500; // force ad hoc small MTU to fragment IP
|
||||
mg_http_listen(&mgr, "http://0.0.0.0:0", frag_send_fn, NULL);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0:80", frag_send_fn, NULL);
|
||||
mgr.conns->pfn = NULL;
|
||||
for (i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(s_seg_sent == 3);
|
||||
@@ -358,11 +397,108 @@ static void test_fragmentation(void) {
|
||||
test_frag_send_path();
|
||||
}
|
||||
|
||||
|
||||
static void test_tcp_backlog(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct eth e;
|
||||
struct ip ip;
|
||||
struct tcp *t = (struct tcp *) (s_driver_data.buf + sizeof(e) + sizeof(ip));
|
||||
struct ip *i = (struct ip *) (s_driver_data.buf + sizeof(e));
|
||||
uint64_t start, now;
|
||||
//bool response_recv = true;
|
||||
struct mg_tcpip_driver driver;
|
||||
struct mg_tcpip_if mif;
|
||||
uint16_t opts[4 / 2]; // Send MSS, RFC-9293 3.7.1
|
||||
struct mg_connection *c;
|
||||
unsigned int j;
|
||||
#define LOGSZ (sizeof(c->data) / sizeof(struct mg_backlog))
|
||||
uint32_t seqnos[LOGSZ];
|
||||
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, fn);
|
||||
|
||||
// test expired connection attempts cleanup
|
||||
create_tcp_seg(&e, &ip, 1234, 0, TH_SYN, 1, 80, 0, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(t->flags == (TH_SYN | TH_ACK));
|
||||
// delay ACK so it is removed from the backlog
|
||||
start = mg_millis();
|
||||
do {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
now = mg_millis() - start;
|
||||
} while (now < 2100);
|
||||
// Mongoose may have retransmitted SYN + ACK, so
|
||||
create_tcp_simpleseg(&e, &ip, 1235, 2, TH_ACK, 0);
|
||||
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
|
||||
while (!received_response(&s_driver_data) || i->proto != 6) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(t->flags == (TH_RST | TH_ACK));
|
||||
|
||||
|
||||
opts[0] = mg_htons(0x0204); // RFC-9293 3.2
|
||||
// fill the backlog
|
||||
for (j = 0; j < LOGSZ; j++) {
|
||||
// assign one MSS for each connection
|
||||
opts[1] = mg_htons((uint16_t)(1010 + j));
|
||||
create_tcp_seg(&e, &ip, 100 + j, 0, TH_SYN, (uint16_t)(j + 1), 80, 0, opts, sizeof(opts));
|
||||
while (!received_response(&s_driver_data) || i->proto != 6)
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(t->flags == (TH_SYN | TH_ACK));
|
||||
seqnos[j] = ntohl(t->seq);
|
||||
MG_VERBOSE(("SEQ: %p", seqnos[j]));
|
||||
}
|
||||
// check backlog is full and MSS are there
|
||||
c = mgr.conns;
|
||||
for (j = 0; j < LOGSZ; j++) {
|
||||
struct mg_backlog *b = (struct mg_backlog *)(c->data) + j;
|
||||
ASSERT(b->port != 0);
|
||||
MG_DEBUG(("SEQ: %p, MSS: %u", seqnos[j], (unsigned int)b->mss));
|
||||
ASSERT(b->mss == (1010 + j));
|
||||
}
|
||||
// one more attempt, it must fail
|
||||
opts[1] = mg_htons((uint16_t)(1010 + j));
|
||||
create_tcp_seg(&e, &ip, 100 + j, 0, TH_SYN, (uint16_t)(j + 1), 80, 0, opts, sizeof(opts));
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(!received_response(&s_driver_data) || i->proto != 6);
|
||||
// a late response for this attempt would break what follows
|
||||
// establish all connections
|
||||
for (j = 0; j < LOGSZ; j++) {
|
||||
create_tcp_seg(&e, &ip, 100 + j + 1, seqnos[j] + 1, TH_ACK, (uint16_t)(j + 1), 80, 0, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(!received_response(&s_driver_data) || i->proto != 6);
|
||||
}
|
||||
// check backlog is now empty
|
||||
c = mgr.conns; // last one is the listener
|
||||
for (; c->next != NULL; c = c->next);
|
||||
for (j = 0; j < LOGSZ; j++) {
|
||||
struct mg_backlog *b = (struct mg_backlog *)(c->data) + j;
|
||||
ASSERT(b->port == 0);
|
||||
}
|
||||
c = mgr.conns; // first one is more recent
|
||||
// check MSS is what we sent, everything's fine
|
||||
for (j = LOGSZ; j > 0 ; j--, c = c->next) {
|
||||
struct connstate *s = (struct connstate *)(c + 1);
|
||||
ASSERT(c != NULL);
|
||||
MG_DEBUG(("MSS: %u", (unsigned int) s->dmss));
|
||||
ASSERT(s->dmss == (1010 + j - 1));
|
||||
}
|
||||
ASSERT(c != NULL); // last one is the listener
|
||||
ASSERT(c->next == NULL);
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
}
|
||||
|
||||
static void test_tcp(void) {
|
||||
test_tcp_basics();
|
||||
test_tcp_backlog();
|
||||
test_tcp_retransmit();
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_csum();
|
||||
test_statechange();
|
||||
test_poll();
|
||||
test_retransmit();
|
||||
test_tcp();
|
||||
test_fragmentation();
|
||||
printf("SUCCESS. Total tests: %d\n", s_num_tests);
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user