diff --git a/mongoose.c b/mongoose.c index 2801a185e24..554b58f6296 100644 --- a/mongoose.c +++ b/mongoose.c @@ -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 @@ -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 }; @@ -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; @@ -8694,9 +8697,22 @@ 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) { @@ -8704,8 +8720,6 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { 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), "", @@ -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 = @@ -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"); @@ -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); } } @@ -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); } @@ -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; diff --git a/mongoose.h b/mongoose.h index 2ad6f32ca0a..c2916dae4a4 100644 --- a/mongoose.h +++ b/mongoose.h @@ -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 diff --git a/src/net.h b/src/net.h index fac5fe8f06d..eca44e04104 100644 --- a/src/net.h +++ b/src/net.h @@ -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 diff --git a/src/tcpip/tcpip.c b/src/tcpip/tcpip.c index a0078f87b76..527ea21746c 100644 --- a/src/tcpip/tcpip.c +++ b/src/tcpip/tcpip.c @@ -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 @@ -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 }; @@ -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; @@ -584,9 +587,22 @@ 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) { @@ -594,8 +610,6 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { 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), "", @@ -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 = @@ -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"); @@ -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); } } @@ -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); } @@ -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; diff --git a/test/mip_tap_test.c b/test/mip_tap_test.c index 97333c4dc19..7bd95ffdf6e 100644 --- a/test/mip_tap_test.c +++ b/test/mip_tap_test.c @@ -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); @@ -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],