Skip to content

Commit

Permalink
improved tcp closure
Browse files Browse the repository at this point in the history
  • Loading branch information
robert committed Aug 15, 2023
1 parent 8c03aa2 commit 49aab3a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 25 deletions.
48 changes: 37 additions & 11 deletions mongoose.c
Original file line number Diff line number Diff line change
Expand Up @@ -8122,6 +8122,7 @@ struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx,
#define MIP_TCP_ACK_MS 150 // Timeout for ACKing
#define MIP_TCP_ARP_MS 100 // Timeout for ARP response
#define MIP_TCP_SYN_MS 15000 // Timeout for connection establishment
#define MIP_TCP_FIN_MS 100 // Timeout for closing connection

struct connstate {
uint32_t seq, ack; // TCP seq/ack counters
Expand All @@ -8132,6 +8133,7 @@ struct connstate {
#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon
#define MIP_TTYPE_ARP 2 // ARP resolve sent, waiting for response
#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response
#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection
uint8_t tmiss; // Number of keep-alive misses
struct mg_iobuf raw; // For TLS only. Incoming raw data
};
Expand Down Expand Up @@ -8271,6 +8273,7 @@ static void settmout(struct mg_connection *c, uint8_t type) {
unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS
: type == MIP_TTYPE_ARP ? MIP_TCP_ARP_MS
: type == MIP_TTYPE_SYN ? MIP_TCP_SYN_MS
: type == MIP_TTYPE_FIN ? MIP_TCP_FIN_MS
: MIP_TCP_KEEPALIVE_MS;
s->timer = ifp->now + n;
s->ttype = type;
Expand Down Expand Up @@ -8694,18 +8697,29 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv;
uint32_t seq = mg_ntohl(pkt->tcp->seq);
s->raw.align = c->recv.align;
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
if (pkt->tcp->flags & TH_FIN) {
s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
c->is_closing = 1;
if (c->is_fin) {
// We initiated FIN so when we receive FIN, we will have to ACK it
tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_ACK,
c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "",
0);
}
else {
tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_FIN | TH_ACK,
c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
settmout(c, MIP_TTYPE_FIN);
}
//c->is_closing = 1;
} else if (pkt->pay.len == 0) {
// TODO(cpq): handle this peer's ACK
} else if (seq != s->ack) {
uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
if (s->ack == ack) {
MG_VERBOSE(("ignoring duplicate pkt"));
} else {
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_ACK,
c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "",
Expand Down Expand Up @@ -8944,6 +8958,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
} // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5)
}



// Read data from the network
if (ifp->driver->rx != NULL) { // Polling driver. We must call it
size_t len =
Expand Down Expand Up @@ -8973,6 +8989,9 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
mg_error(c, "ARP timeout");
} else if (s->ttype == MIP_TTYPE_SYN) {
mg_error(c, "Connection timeout");
} else if (s->ttype == MIP_TTYPE_FIN) {
c->is_closing = 1;
continue;
} else {
if (s->tmiss++ > 2) {
mg_error(c, "keepalive");
Expand All @@ -8982,6 +9001,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
}
}

settmout(c, MIP_TTYPE_KEEPALIVE);
}
}
Expand Down Expand Up @@ -9106,18 +9126,23 @@ static void write_conn(struct mg_connection *c) {
}
}

static void close_conn(struct mg_connection *c) {
static void init_tcp_closure(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
mg_iobuf_free(&s->raw); // For TLS connections, release raw data
if (c->is_udp == false && c->is_listening == false &&
c->is_connecting == false) { // For TCP conns,
struct mg_tcpip_if *ifp =
c->is_connecting == false && s->ttype != MIP_TTYPE_FIN) { // For TCP conns,
struct mg_tcpip_if *ifp =
(struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN
tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
settmout(c, MIP_TTYPE_FIN);
}
}

static void close_conn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
mg_iobuf_free(&s->raw); // For TLS connections, release raw data
mg_close_conn(c);
}

Expand All @@ -9140,6 +9165,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
if (c->is_tls_hs) mg_tls_handshake(c);
if (can_write(c)) write_conn(c);
if (c->is_draining && c->send.len == 0) c->is_closing = 1;
if (c->is_fin) init_tcp_closure(c);
if (c->is_closing) close_conn(c);
}
(void) ms;
Expand Down
1 change: 1 addition & 0 deletions mongoose.h
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,7 @@ struct mg_connection {
unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator
unsigned is_hexdumping : 1; // Hexdump in/out traffic
unsigned is_draining : 1; // Send remaining data, then close and free
unsigned is_fin : 1; // Set when initializing TCP closure
unsigned is_closing : 1; // Close and free the connection immediately
unsigned is_full : 1; // Stop reads, until cleared
unsigned is_resp : 1; // Response is still being generated
Expand Down
1 change: 1 addition & 0 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct mg_connection {
unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator
unsigned is_hexdumping : 1; // Hexdump in/out traffic
unsigned is_draining : 1; // Send remaining data, then close and free
unsigned is_fin : 1; // Set when initializing TCP closure
unsigned is_closing : 1; // Close and free the connection immediately
unsigned is_full : 1; // Stop reads, until cleared
unsigned is_resp : 1; // Response is still being generated
Expand Down
48 changes: 37 additions & 11 deletions src/tcpip/tcpip.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define MIP_TCP_ACK_MS 150 // Timeout for ACKing
#define MIP_TCP_ARP_MS 100 // Timeout for ARP response
#define MIP_TCP_SYN_MS 15000 // Timeout for connection establishment
#define MIP_TCP_FIN_MS 100 // Timeout for closing connection

struct connstate {
uint32_t seq, ack; // TCP seq/ack counters
Expand All @@ -22,6 +23,7 @@ struct connstate {
#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon
#define MIP_TTYPE_ARP 2 // ARP resolve sent, waiting for response
#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response
#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection
uint8_t tmiss; // Number of keep-alive misses
struct mg_iobuf raw; // For TLS only. Incoming raw data
};
Expand Down Expand Up @@ -161,6 +163,7 @@ static void settmout(struct mg_connection *c, uint8_t type) {
unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS
: type == MIP_TTYPE_ARP ? MIP_TCP_ARP_MS
: type == MIP_TTYPE_SYN ? MIP_TCP_SYN_MS
: type == MIP_TTYPE_FIN ? MIP_TCP_FIN_MS
: MIP_TCP_KEEPALIVE_MS;
s->timer = ifp->now + n;
s->ttype = type;
Expand Down Expand Up @@ -584,18 +587,29 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv;
uint32_t seq = mg_ntohl(pkt->tcp->seq);
s->raw.align = c->recv.align;
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
if (pkt->tcp->flags & TH_FIN) {
s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
c->is_closing = 1;
if (c->is_fin) {
// We initiated FIN so when we receive FIN, we will have to ACK it
tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_ACK,
c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "",
0);
}
else {
tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_FIN | TH_ACK,
c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
settmout(c, MIP_TTYPE_FIN);
}
//c->is_closing = 1;
} else if (pkt->pay.len == 0) {
// TODO(cpq): handle this peer's ACK
} else if (seq != s->ack) {
uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
if (s->ack == ack) {
MG_VERBOSE(("ignoring duplicate pkt"));
} else {
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_ACK,
c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "",
Expand Down Expand Up @@ -834,6 +848,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
} // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5)
}



// Read data from the network
if (ifp->driver->rx != NULL) { // Polling driver. We must call it
size_t len =
Expand Down Expand Up @@ -863,6 +879,9 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
mg_error(c, "ARP timeout");
} else if (s->ttype == MIP_TTYPE_SYN) {
mg_error(c, "Connection timeout");
} else if (s->ttype == MIP_TTYPE_FIN) {
c->is_closing = 1;
continue;
} else {
if (s->tmiss++ > 2) {
mg_error(c, "keepalive");
Expand All @@ -872,6 +891,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
}
}

settmout(c, MIP_TTYPE_KEEPALIVE);
}
}
Expand Down Expand Up @@ -996,18 +1016,23 @@ static void write_conn(struct mg_connection *c) {
}
}

static void close_conn(struct mg_connection *c) {
static void init_tcp_closure(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
mg_iobuf_free(&s->raw); // For TLS connections, release raw data
if (c->is_udp == false && c->is_listening == false &&
c->is_connecting == false) { // For TCP conns,
struct mg_tcpip_if *ifp =
c->is_connecting == false && s->ttype != MIP_TTYPE_FIN) { // For TCP conns,
struct mg_tcpip_if *ifp =
(struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN
tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
uint32_t rem_ip;
memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
settmout(c, MIP_TTYPE_FIN);
}
}

static void close_conn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
mg_iobuf_free(&s->raw); // For TLS connections, release raw data
mg_close_conn(c);
}

Expand All @@ -1030,6 +1055,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
if (c->is_tls_hs) mg_tls_handshake(c);
if (can_write(c)) write_conn(c);
if (c->is_draining && c->send.len == 0) c->is_closing = 1;
if (c->is_fin) init_tcp_closure(c);
if (c->is_closing) close_conn(c);
}
(void) ms;
Expand Down
7 changes: 4 additions & 3 deletions test/mip_tap_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ char *fetch(struct mg_mgr *mgr, const char *url, const char *fn_data) {
mg_mgr_poll(mgr, 100);
usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit
}

if (mgr->conns != 0) {
conn->is_closing = 1;
mg_mgr_poll(mgr, 0);
Expand Down Expand Up @@ -175,9 +176,9 @@ static void test_http_fetch(void) {

#if MG_USING_DHCP == 1
#else
mif.ip = 0x0220a8c0; // 192.168.32.2 // Triggering a network failure
mif.mask = 0x00ffffff; // 255.255.255.0
mif.gw = 0x0120a8c0; // 192.168.32.1
mif.ip = mg_htonl(MG_U32(192, 168, 32, 2)); // Triggering a network failure
mif.mask = mg_htonl(MG_U32(255, 255, 255, 0));
mif.gw = mg_htonl(MG_U32(192, 168, 32, 1));
#endif

sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1],
Expand Down

0 comments on commit 49aab3a

Please sign in to comment.