mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-01 05:52:32 +07:00
+66
-58
@@ -4729,13 +4729,17 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
|
||||
return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len);
|
||||
}
|
||||
|
||||
static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
uint8_t flags, uint32_t seq, const void *buf,
|
||||
size_t len) {
|
||||
uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
|
||||
static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
uint8_t flags, uint32_t seqno) {
|
||||
uint32_t ackno = mg_htonl(mg_ntohl(pkt->tcp->seq) + (uint32_t) pkt->pay.len +
|
||||
((pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0));
|
||||
return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
|
||||
pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
|
||||
buf, len);
|
||||
pkt->tcp->sport, seqno, ackno, NULL, 0);
|
||||
}
|
||||
|
||||
static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
|
||||
return tx_tcp_ctrlresp(ifp, pkt, toack ? TH_RST : (TH_RST | TH_ACK),
|
||||
toack ? pkt->tcp->ack : 0);
|
||||
}
|
||||
|
||||
static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
@@ -4934,10 +4938,9 @@ 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
|
||||
uint16_t port, mss; // use port=0 for available entries
|
||||
uint8_t age;
|
||||
};
|
||||
|
||||
@@ -4947,7 +4950,7 @@ static int backlog_insert(struct mg_connection *c, uint16_t port,
|
||||
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].age = 2; // remove after two calls, average 1.5 call rate
|
||||
p[i].port = port, p[i].mss = mss;
|
||||
return (int) i;
|
||||
}
|
||||
@@ -4970,7 +4973,7 @@ static void backlog_remove(struct mg_connection *c, uint16_t key) {
|
||||
|
||||
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
|
||||
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;
|
||||
@@ -5007,70 +5010,74 @@ static void handle_opt(struct connstate *s, struct tcp *tcp) {
|
||||
static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
|
||||
struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
|
||||
#if 0
|
||||
MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
|
||||
#endif
|
||||
// Order is VERY important; RFC-9293 3.5.2
|
||||
// - check clients (Group 1) and established connections (Group 3)
|
||||
if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) {
|
||||
// client got a server connection accept
|
||||
handle_opt(s, pkt->tcp); // process options (MSS)
|
||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
|
||||
tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
|
||||
c->is_connecting = 0; // Client connected
|
||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
mg_call(c, MG_EV_CONNECT, NULL); // Let user know
|
||||
if (c->is_tls_hs) mg_tls_handshake(c);
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
|
||||
// mg_hexdump(pkt->raw.buf, pkt->raw.len);
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
mg_error(c, "connection refused");
|
||||
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
|
||||
// TODO(): validate RST is within window (and optional with proper ACK)
|
||||
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
|
||||
} else if (c != NULL) {
|
||||
#if 0
|
||||
MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
|
||||
mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
|
||||
mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
|
||||
mg_hexdump(pkt->pay.buf, pkt->pay.len);
|
||||
#endif
|
||||
// process segment
|
||||
s->tmiss = 0; // Reset missed keep-alive counter
|
||||
if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
|
||||
settmout(c,
|
||||
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
|
||||
read_conn(c, pkt); // Override timer with ACK timeout if needed
|
||||
} else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else if (pkt->tcp->flags & TH_RST) {
|
||||
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) {
|
||||
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 ((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 {
|
||||
// MG_VERBOSE(("dropped silently.."));
|
||||
}
|
||||
} else
|
||||
// - we don't listen on that port; RFC-9293 3.5.2 Group 1
|
||||
// - check listening connections; RFC-9293 3.5.2 Group 2
|
||||
if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
|
||||
// not listening on that port
|
||||
if (!(pkt->tcp->flags & TH_RST)) {
|
||||
tx_tcp_rst(ifp, pkt, pkt->tcp->flags & TH_ACK);
|
||||
} // else silently discard
|
||||
} else if (pkt->tcp->flags == TH_SYN) {
|
||||
// listener receives a connection request
|
||||
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_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn);
|
||||
} // what should we do when port=0 ? Linux takes port 0 as any other
|
||||
// port
|
||||
} else if (pkt->tcp->flags == TH_ACK) {
|
||||
// listener receives an ACK
|
||||
struct mg_backlog *b = NULL;
|
||||
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);
|
||||
b = backlog_retrieve(c, key, pkt->tcp->sport);
|
||||
if (b != NULL) { // ACK is a response to a SYN+ACK
|
||||
accept_conn(c, pkt, b->mss); // pass options
|
||||
backlog_remove(c, key);
|
||||
} // else not an actual match, reset
|
||||
}
|
||||
if (b == NULL) tx_tcp_rst(ifp, pkt, true);
|
||||
} else if (pkt->tcp->flags & TH_RST) {
|
||||
// silently discard
|
||||
} else if (pkt->tcp->flags & TH_ACK) { // ACK + something else != RST
|
||||
tx_tcp_rst(ifp, pkt, true);
|
||||
} else if (pkt->tcp->flags & TH_SYN) { // SYN + something else != ACK
|
||||
tx_tcp_rst(ifp, pkt, false);
|
||||
} // else silently discard
|
||||
}
|
||||
|
||||
static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
@@ -5090,6 +5097,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
pkt->udp = (struct udp *) (pkt->ip + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->udp)) return;
|
||||
mkpay(pkt, pkt->udp + 1);
|
||||
if (pkt->udp->len < pkt->pay.len) pkt->pay.len = pkt->udp->len;
|
||||
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
|
||||
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
|
||||
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
||||
@@ -5108,7 +5116,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
uint16_t iplen, off;
|
||||
pkt->tcp = (struct tcp *) (pkt->ip + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
|
||||
mkpay(pkt, pkt->tcp + 1);
|
||||
mkpay(pkt, (uint32_t *) pkt->tcp + (pkt->tcp->off >> 4)); // may have opts
|
||||
iplen = mg_ntohs(pkt->ip->len);
|
||||
off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
|
||||
if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
|
||||
|
||||
+66
-58
@@ -572,13 +572,17 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
|
||||
return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len);
|
||||
}
|
||||
|
||||
static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
uint8_t flags, uint32_t seq, const void *buf,
|
||||
size_t len) {
|
||||
uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
|
||||
static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
uint8_t flags, uint32_t seqno) {
|
||||
uint32_t ackno = mg_htonl(mg_ntohl(pkt->tcp->seq) + (uint32_t) pkt->pay.len +
|
||||
((pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0));
|
||||
return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
|
||||
pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
|
||||
buf, len);
|
||||
pkt->tcp->sport, seqno, ackno, NULL, 0);
|
||||
}
|
||||
|
||||
static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
|
||||
return tx_tcp_ctrlresp(ifp, pkt, toack ? TH_RST : (TH_RST | TH_ACK),
|
||||
toack ? pkt->tcp->ack : 0);
|
||||
}
|
||||
|
||||
static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
@@ -777,10 +781,9 @@ 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
|
||||
uint16_t port, mss; // use port=0 for available entries
|
||||
uint8_t age;
|
||||
};
|
||||
|
||||
@@ -790,7 +793,7 @@ static int backlog_insert(struct mg_connection *c, uint16_t port,
|
||||
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].age = 2; // remove after two calls, average 1.5 call rate
|
||||
p[i].port = port, p[i].mss = mss;
|
||||
return (int) i;
|
||||
}
|
||||
@@ -813,7 +816,7 @@ static void backlog_remove(struct mg_connection *c, uint16_t key) {
|
||||
|
||||
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
|
||||
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;
|
||||
@@ -850,70 +853,74 @@ static void handle_opt(struct connstate *s, struct tcp *tcp) {
|
||||
static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
|
||||
struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
|
||||
#if 0
|
||||
MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
|
||||
#endif
|
||||
// Order is VERY important; RFC-9293 3.5.2
|
||||
// - check clients (Group 1) and established connections (Group 3)
|
||||
if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) {
|
||||
// client got a server connection accept
|
||||
handle_opt(s, pkt->tcp); // process options (MSS)
|
||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
|
||||
tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
|
||||
c->is_connecting = 0; // Client connected
|
||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
mg_call(c, MG_EV_CONNECT, NULL); // Let user know
|
||||
if (c->is_tls_hs) mg_tls_handshake(c);
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
|
||||
// mg_hexdump(pkt->raw.buf, pkt->raw.len);
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
mg_error(c, "connection refused");
|
||||
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
|
||||
// TODO(): validate RST is within window (and optional with proper ACK)
|
||||
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
|
||||
} else if (c != NULL) {
|
||||
#if 0
|
||||
MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
|
||||
mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
|
||||
mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
|
||||
mg_hexdump(pkt->pay.buf, pkt->pay.len);
|
||||
#endif
|
||||
// process segment
|
||||
s->tmiss = 0; // Reset missed keep-alive counter
|
||||
if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
|
||||
settmout(c,
|
||||
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
|
||||
read_conn(c, pkt); // Override timer with ACK timeout if needed
|
||||
} else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else if (pkt->tcp->flags & TH_RST) {
|
||||
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) {
|
||||
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 ((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 {
|
||||
// MG_VERBOSE(("dropped silently.."));
|
||||
}
|
||||
} else
|
||||
// - we don't listen on that port; RFC-9293 3.5.2 Group 1
|
||||
// - check listening connections; RFC-9293 3.5.2 Group 2
|
||||
if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
|
||||
// not listening on that port
|
||||
if (!(pkt->tcp->flags & TH_RST)) {
|
||||
tx_tcp_rst(ifp, pkt, pkt->tcp->flags & TH_ACK);
|
||||
} // else silently discard
|
||||
} else if (pkt->tcp->flags == TH_SYN) {
|
||||
// listener receives a connection request
|
||||
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_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn);
|
||||
} // what should we do when port=0 ? Linux takes port 0 as any other
|
||||
// port
|
||||
} else if (pkt->tcp->flags == TH_ACK) {
|
||||
// listener receives an ACK
|
||||
struct mg_backlog *b = NULL;
|
||||
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);
|
||||
b = backlog_retrieve(c, key, pkt->tcp->sport);
|
||||
if (b != NULL) { // ACK is a response to a SYN+ACK
|
||||
accept_conn(c, pkt, b->mss); // pass options
|
||||
backlog_remove(c, key);
|
||||
} // else not an actual match, reset
|
||||
}
|
||||
if (b == NULL) tx_tcp_rst(ifp, pkt, true);
|
||||
} else if (pkt->tcp->flags & TH_RST) {
|
||||
// silently discard
|
||||
} else if (pkt->tcp->flags & TH_ACK) { // ACK + something else != RST
|
||||
tx_tcp_rst(ifp, pkt, true);
|
||||
} else if (pkt->tcp->flags & TH_SYN) { // SYN + something else != ACK
|
||||
tx_tcp_rst(ifp, pkt, false);
|
||||
} // else silently discard
|
||||
}
|
||||
|
||||
static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
@@ -933,6 +940,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
pkt->udp = (struct udp *) (pkt->ip + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->udp)) return;
|
||||
mkpay(pkt, pkt->udp + 1);
|
||||
if (pkt->udp->len < pkt->pay.len) pkt->pay.len = pkt->udp->len;
|
||||
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
|
||||
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
|
||||
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
||||
@@ -951,7 +959,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
uint16_t iplen, off;
|
||||
pkt->tcp = (struct tcp *) (pkt->ip + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
|
||||
mkpay(pkt, pkt->tcp + 1);
|
||||
mkpay(pkt, (uint32_t *) pkt->tcp + (pkt->tcp->off >> 4)); // may have opts
|
||||
iplen = mg_ntohs(pkt->ip->len);
|
||||
off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
|
||||
if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
|
||||
|
||||
+340
-23
@@ -54,6 +54,17 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
(void) c, (void) ev, (void) ev_data;
|
||||
}
|
||||
|
||||
static void tcpclosure_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_ACCEPT) c->is_draining = 1;
|
||||
(void) c, (void) ev_data;
|
||||
}
|
||||
|
||||
static void client_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_ERROR || ev == MG_EV_CONNECT)
|
||||
(*(int *) c->fn_data) = ev;
|
||||
(void) c, (void) ev_data;
|
||||
}
|
||||
|
||||
static void frag_recv_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_ERROR) {
|
||||
if (s_sent_fragment > 0) {
|
||||
@@ -83,6 +94,7 @@ static void frag_send_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
(void) c, (void) ev_data;
|
||||
}
|
||||
|
||||
|
||||
static void test_poll(void) {
|
||||
int count = 0, i;
|
||||
struct mg_tcpip_if mif;
|
||||
@@ -194,47 +206,118 @@ static void init_tcp_handshake(struct eth *e, struct ip *ip,
|
||||
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);
|
||||
create_tcp_simpleseg(e, ip, 1000, 0, TH_SYN, 0);
|
||||
MG_VERBOSE(("SYN -->"));
|
||||
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);
|
||||
ASSERT((t->flags == (TH_SYN | TH_ACK)));
|
||||
ASSERT((t->ack == mg_htonl(1001)));
|
||||
MG_VERBOSE(("SYN+ACK <--"));
|
||||
|
||||
// ACK
|
||||
create_tcp_simpleseg(e, ip, 1001, 2, TH_ACK, 0);
|
||||
mg_mgr_poll(mgr, 0);
|
||||
MG_VERBOSE(("ACK -->"));
|
||||
mg_mgr_poll(mgr, 0); // this may have data on return !
|
||||
}
|
||||
|
||||
|
||||
// DHCP discovery works as a 1 second timeout, we take advantage of it
|
||||
// (something is received within 1s) and we mask it when doing longer waits
|
||||
// (verify received data is TCP by checking IP's protocol field)
|
||||
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 ip *i = (struct ip *) (s_driver_data.buf + sizeof(e));
|
||||
uint64_t start, now;
|
||||
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);
|
||||
// https://datatracker.ietf.org/doc/html/rfc9293#section-3.5.2 Reset Generation
|
||||
// non-used port. Group 1 in RFC
|
||||
// send SYN, expect RST + ACK
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_SYN, 1, 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)));
|
||||
ASSERT(t->flags == (TH_RST | TH_ACK));
|
||||
ASSERT(t->seq == mg_htonl(0));
|
||||
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);
|
||||
// send SYN+ACK, expect RST
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_SYN | TH_ACK, 1, 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);
|
||||
ASSERT(t->seq == mg_htonl(4321));
|
||||
// send data, expect RST + ACK
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_PUSH, 1, 69, 2, 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)));
|
||||
ASSERT(t->flags == (TH_RST | TH_ACK));
|
||||
ASSERT(t->seq == mg_htonl(0));
|
||||
ASSERT(t->ack == mg_htonl(1236));
|
||||
|
||||
// send ACK, expect RST
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_ACK, 1, 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);
|
||||
ASSERT(t->seq == mg_htonl(4321));
|
||||
|
||||
// send FIN, expect RST + ACK
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_FIN, 1, 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)); // Linux answers RST only
|
||||
ASSERT(t->seq == mg_htonl(0));
|
||||
ASSERT(t->ack == mg_htonl(1235));
|
||||
|
||||
// send FIN+ACK, expect RST
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_FIN | TH_ACK, 1, 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);
|
||||
ASSERT(t->seq == mg_htonl(4321));
|
||||
|
||||
// listening, non-connected port. Group 2 in RFC
|
||||
// send data, expect no response
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_PUSH, 1, 80, 2, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(!received_response(&s_driver_data));
|
||||
|
||||
// send ACK, expect RST
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_ACK, 1, 80, 0, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(t->flags == TH_RST);
|
||||
ASSERT(t->seq == mg_htonl(4321));
|
||||
|
||||
// send SYN+ACK, expect RST
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_SYN | TH_ACK, 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_RST);
|
||||
ASSERT(t->seq == mg_htonl(4321));
|
||||
|
||||
// send FIN, expect no response
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_FIN, 1, 80, 0, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(!received_response(&s_driver_data));
|
||||
|
||||
// send FIN+ACK, expect RST
|
||||
create_tcp_seg(&e, &ip, 1234, 4321, TH_FIN | TH_ACK, 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_RST);
|
||||
ASSERT(t->seq == mg_htonl(4321));
|
||||
|
||||
|
||||
// we currently don't validate checksum, no silently discarded segment test
|
||||
|
||||
|
||||
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
|
||||
@@ -255,7 +338,238 @@ static void test_tcp_basics(void) {
|
||||
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
|
||||
// Initiate closure, send FIN (test client-initiated closure)
|
||||
// https://datatracker.ietf.org/doc/html/rfc9293#section-3.6
|
||||
// We are case 1, Mongoose is case 2
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 2, TH_FIN, 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);
|
||||
// Mongoose does a fast reduced ("3-way instead of 4-way" closure)
|
||||
ASSERT((t->flags == (TH_FIN | TH_ACK))); // Mongoose ACKs our FIN, sends FIN
|
||||
ASSERT((t->seq == mg_htonl(2)));
|
||||
ASSERT((t->ack == mg_htonl(1002)));
|
||||
// make sure it is still open
|
||||
ASSERT(mgr.conns->next != NULL); // more than one connection: the listener + us
|
||||
create_tcp_simpleseg(&e, &ip, 1002, 3, TH_ACK, 0); // ACK Mongoose FIN
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
ASSERT(!received_response(&s_driver_data));
|
||||
// make sure it is closed
|
||||
ASSERT(mgr.conns->next == NULL); // only one connection: the listener
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
// Test client-initiated closure timeout, do not ACK
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, fn);
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 2, TH_FIN, 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);
|
||||
// Mongoose does a fast reduced ("3-way instead of 4-way" closure)
|
||||
ASSERT((t->flags == (TH_FIN | TH_ACK))); // Mongoose ACKs our FIN, sends FIN
|
||||
ASSERT((t->seq == mg_htonl(2)));
|
||||
ASSERT((t->ack == mg_htonl(1002)));
|
||||
// make sure it is still open
|
||||
ASSERT(mgr.conns->next != NULL); // more than one connection: the listener + us
|
||||
s_driver_data.len = 0; // avoid Mongoose "receiving itself"
|
||||
start = mg_millis();
|
||||
now = 0;
|
||||
do {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
if (received_response(&s_driver_data) && i->proto == 6) break; // check first
|
||||
now = mg_millis() - start;
|
||||
} while (now < (12 * MIP_TCP_FIN_MS)/10);
|
||||
ASSERT(now > MIP_TCP_FIN_MS);
|
||||
// make sure it is closed
|
||||
ASSERT(mgr.conns->next == NULL); // only one connection: the listener
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
// Test server-initiated closure, abbreviated 3-way: respond FIN+ACK
|
||||
// https://datatracker.ietf.org/doc/html/rfc9293#section-3.6
|
||||
// We are case 2, Mongoose is case 1
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, tcpclosure_fn);
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
// we should have already received the FIN due to the call above
|
||||
start = mg_millis();
|
||||
while (!received_response(&s_driver_data)) {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
now = mg_millis() - start;
|
||||
if (now > 2 * MIP_TCP_ACK_MS)
|
||||
ASSERT(0); // response should have been received by now
|
||||
}
|
||||
ASSERT((t->seq == mg_htonl(2)));
|
||||
ASSERT((t->ack == mg_htonl(1001)));
|
||||
ASSERT(t->flags == (TH_FIN | TH_ACK)); // Mongoose ACKs last data, sends FIN
|
||||
// send FIN + ACK
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 3, TH_FIN | TH_ACK, 0); // ACK FIN, send FIN
|
||||
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_ACK)); // Mongoose ACKs our FIN
|
||||
ASSERT((t->seq == mg_htonl(3)));
|
||||
ASSERT((t->ack == mg_htonl(1002)));
|
||||
// make sure it is closed
|
||||
ASSERT(mgr.conns->next == NULL); // only one connection: the listener
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
// Test server-initiated closure, long 4-way closure: respond ACK
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, tcpclosure_fn);
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
// we should have already received the FIN, tested in above tst
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT((t->seq == mg_htonl(2)));
|
||||
ASSERT((t->ack == mg_htonl(1001)));
|
||||
ASSERT(t->flags == (TH_FIN | TH_ACK)); // Mongoose ACKs last data, sends FIN
|
||||
// ACK Mongoose FIN, do *not* send FIN yet
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 3, TH_ACK, 0); // ACK FIN
|
||||
mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer
|
||||
start = mg_millis();
|
||||
now = 0;
|
||||
do {
|
||||
if (received_response(&s_driver_data)) break; // check first
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
now = mg_millis() - start;
|
||||
} while (now < 2 * MIP_TCP_ACK_MS); // keep timeout below 1s (DHCP discover)
|
||||
ASSERT(now >= 2 * MIP_TCP_ACK_MS);
|
||||
// make sure it is still open
|
||||
ASSERT(mgr.conns->next != NULL); // more than one connection: the listener + us
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 3, TH_FIN, 0); // send FIN
|
||||
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_ACK)); // Mongoose ACKs our FIN
|
||||
ASSERT((t->seq == mg_htonl(3)));
|
||||
ASSERT((t->ack == mg_htonl(1002)));
|
||||
// make sure it is closed
|
||||
ASSERT(mgr.conns->next == NULL); // only one connection: the listener
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
// Test server-initiated closure, FIN retransmission: do not ACK FIN
|
||||
// Actual data retransmission is tested on another unit test
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, tcpclosure_fn);
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
// we should have already received the FIN, tested in some tst above
|
||||
while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0);
|
||||
ASSERT((t->seq == mg_htonl(2)));
|
||||
ASSERT((t->ack == mg_htonl(1001)));
|
||||
ASSERT(t->flags == (TH_FIN | TH_ACK)); // Mongoose ACKs last data, sends FIN
|
||||
s_driver_data.len = 0; // avoid Mongoose "receiving itself"
|
||||
start = mg_millis();
|
||||
now = 0;
|
||||
do {
|
||||
if (received_response(&s_driver_data)) break; // check first
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
now = mg_millis() - start;
|
||||
} while (now < 2 * MIP_TCP_ACK_MS); // keep timeout below 1s (DHCP discover)
|
||||
// ASSERT(now < 2 * MIP_TCP_ACK_MS); ******** WE FAIL THIS, Mongoose does not retransmit, FIN is not an additional element in the stream
|
||||
// ASSERT((t->seq == mg_htonl(2)));
|
||||
// ASSERT((t->ack == mg_htonl(1001)));
|
||||
// ASSERT(t->flags == (TH_FIN | TH_ACK)); // Mongoose retransmits FIN
|
||||
// send FIN + ACK
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 3, TH_FIN | TH_ACK, 0); // ACK FIN, send FIN
|
||||
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_ACK)); // Mongoose ACKs our FIN
|
||||
ASSERT((t->seq == mg_htonl(3)));
|
||||
ASSERT((t->ack == mg_htonl(1002)));
|
||||
// make sure it is closed
|
||||
ASSERT(mgr.conns->next == NULL); // only one connection: the listener
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
// Test simultaneous closure
|
||||
// https://datatracker.ietf.org/doc/html/rfc9293#section-3.6 case 3
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, tcpclosure_fn);
|
||||
init_tcp_handshake(&e, &ip, &mgr); // starts with seq_no=1000, ackno=2
|
||||
// we should have already received the FIN due to the call above
|
||||
start = mg_millis();
|
||||
while (!received_response(&s_driver_data)) {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
now = mg_millis() - start;
|
||||
if (now > 2 * MIP_TCP_ACK_MS)
|
||||
ASSERT(0); // response should have been received by now
|
||||
}
|
||||
ASSERT((t->seq == mg_htonl(2)));
|
||||
ASSERT((t->ack == mg_htonl(1001)));
|
||||
ASSERT(t->flags == (TH_FIN | TH_ACK)); // Mongoose ACKs last data, sends FIN
|
||||
// Also initiate closure, send FIN, do *not* ACK Mongoose FIN
|
||||
create_tcp_simpleseg(&e, &ip, 1001, 2, TH_FIN, 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_ACK)); // Mongoose ACKs our FIN
|
||||
ASSERT((t->seq == mg_htonl(3)));
|
||||
ASSERT((t->ack == mg_htonl(1002)));
|
||||
// make sure it is still open ******** WE FAIL THIS, Mongoose closes immediately, does not wait to retransmit its ACK nor to get the other end ACK
|
||||
// ASSERT(mgr.conns->next != NULL); // more than one connection: the listener + us
|
||||
// create_tcp_simpleseg(&e, &ip, 1002, 3, TH_ACK, 0); // ACK FIN
|
||||
// mg_mgr_poll(&mgr, 0);
|
||||
// make sure it is closed
|
||||
ASSERT(mgr.conns->next == NULL); // only one connection: the listener
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
// Test responses to a connecting client
|
||||
// https://datatracker.ietf.org/doc/html/rfc9293#section-3.5
|
||||
// NOTE: Mongoose ignores any data until connection is actually established
|
||||
// NOTE: Mongoose does not support the concept of "simultaneous open", Mongoose is either client or server
|
||||
{
|
||||
struct mg_connection *c;
|
||||
int event = 255;
|
||||
uint32_t ackno;
|
||||
// this creates a listener we won't use
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, tcpclosure_fn);
|
||||
|
||||
c = mg_connect(&mgr, "tcp://1.2.3.4:1234/", client_fn, &event);
|
||||
ASSERT(c!=NULL);
|
||||
ASSERT(received_response(&s_driver_data));
|
||||
ASSERT((t->flags == TH_SYN));
|
||||
ASSERT(event == 255);
|
||||
// invalid SYN + ACK to connecting client (after SYN...), send ACK out of seq
|
||||
ackno = mg_ntohl(t->seq) + 1000;
|
||||
// create_tcp_seg(&e, &ip, 4321, ackno, TH_SYN | TH_ACK, 1234, mg_ntohs(c->loc.port), 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))); // ***************** WHAT DOES LINUX DO HERE ????
|
||||
// ******** WE FAIL THIS, Mongoose does not validate the ACK number
|
||||
// ASSERT((t->seq == mg_htonl(ackno)));
|
||||
// ASSERT((t->ack == mg_htonl(4322)));
|
||||
|
||||
// connect
|
||||
ackno = mg_ntohl(t->seq) + 1;
|
||||
create_tcp_seg(&e, &ip, 4321, ackno, TH_SYN | TH_ACK, 1234, mg_ntohs(c->loc.port), 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_ACK);
|
||||
ASSERT(t->seq == mg_htonl(ackno));
|
||||
ASSERT((t->ack == mg_htonl(4322)));
|
||||
ASSERT(event == MG_EV_CONNECT);
|
||||
|
||||
event = 255;
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
// test connection failure, send RST+ACK
|
||||
// this creates a listener we won't use
|
||||
init_tcp_tests(&mgr, &e, &ip, &driver, &mif, tcpclosure_fn);
|
||||
c = mg_connect(&mgr, "tcp://1.2.3.4:1234/", client_fn, &event);
|
||||
received_response(&s_driver_data); // get the SYN
|
||||
ackno = mg_ntohl(t->seq) + 1;
|
||||
create_tcp_seg(&e, &ip, 4321, ackno, TH_RST + TH_ACK, 1234, mg_ntohs(c->loc.port), 0, NULL, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
MG_DEBUG(("event: %d", event));
|
||||
ASSERT(event == MG_EV_ERROR);
|
||||
ASSERT(!received_response(&s_driver_data));
|
||||
}
|
||||
|
||||
// TODO(): RST handling
|
||||
// https://datatracker.ietf.org/doc/html/rfc9293#section-3.5.3
|
||||
// all reset (RST) segments are validated by checking their SEQ fields. A reset is valid if its sequence number is in the window; otherwise, it is silently discarded
|
||||
|
||||
s_driver_data.len = 0;
|
||||
mg_mgr_free(&mgr);
|
||||
@@ -405,7 +719,6 @@ static void test_tcp_backlog(void) {
|
||||
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
|
||||
@@ -421,18 +734,22 @@ static void test_tcp_backlog(void) {
|
||||
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
|
||||
// delay ACK so conn attempt is removed from the backlog
|
||||
s_driver_data.len = 0; // avoid Mongoose "receiving itself"
|
||||
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));
|
||||
|
||||
// check backlog is empty
|
||||
c = mgr.conns;
|
||||
ASSERT(c->next == NULL);
|
||||
for (j = 0; j < LOGSZ; j++) {
|
||||
struct mg_backlog *b = (struct mg_backlog *)(c->data) + j;
|
||||
ASSERT(b->port == 0);
|
||||
}
|
||||
// Mongoose may have retransmitted SYN + ACK, and DHCP sent discover
|
||||
received_response(&s_driver_data); // make sure we clean buffer
|
||||
|
||||
opts[0] = mg_htons(0x0204); // RFC-9293 3.2
|
||||
// fill the backlog
|
||||
|
||||
Reference in New Issue
Block a user