support c->is_full flow control in builtin TCP/IP

This commit is contained in:
Sergio R. Caprile committed 2026-09-09 15:55:55 -03:00
1 parent c13d5d7a56
commit 7e9ddc2d09
3 files changed
+90 -30

No files matched your search

+38 -15
View File
@@ -53,6 +53,7 @@ struct connstate {
bool fin_rcvd; // We have received FIN from the peer
bool twclosure; // 3-way closure done
bool retransmit; // Retain sent data until acknowledged
bool is_full; // Advertise a zero receive window
struct mg_iobuf txq; // Length-prefixed sent TCP segments (RFC-9293, 3.8)
};
@@ -1228,7 +1229,7 @@ static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
struct mg_addr *ip_src, struct mg_addr *ip_dst,
uint8_t dscp, uint8_t flags, uint32_t seq, uint32_t ack,
const void *buf, size_t len) {
uint16_t win, const void *buf, size_t len) {
uint8_t *l3p;
struct ip *ip = NULL;
struct tcp *tcp;
@@ -1273,7 +1274,7 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
tcp->seq = seq;
tcp->ack = ack;
tcp->flags = flags;
tcp->win = mg_htons(MG_TCPIP_WIN);
tcp->win = mg_htons(win);
tcp->off = (uint8_t) (hlen / 4 << 4);
#if MG_ENABLE_IPV6
if (ip_dst->is_ip6) {
@@ -1311,7 +1312,8 @@ static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
ipd.port = pkt->tcp->sport;
if ((l2addr = get_return_l2addr(ifp, &ipd, false, pkt)) == NULL)
return 0; // safety net for lousy networks
return tx_tcp(ifp, l2addr, &ips, &ipd, 0, flags, seqno, ackno, NULL, 0);
return tx_tcp(ifp, l2addr, &ips, &ipd, 0, flags, seqno, ackno, MG_TCPIP_WIN,
NULL, 0);
}
static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
@@ -1466,7 +1468,8 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
if (!txq_add(s, buf, len)) return MG_IO_WAIT;
}
sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
mg_htonl(s->seq), mg_htonl(s->ack),
s->is_full ? 0 : MG_TCPIP_WIN, buf, len);
if (sent == 0 || sent == (size_t) -1) {
if (s->retransmit)
mg_iobuf_del(&s->txq, s->txq.len - len - sizeof(uint32_t),
@@ -1539,7 +1542,8 @@ static void retransmit(struct mg_connection *c) {
// Send the front retransmission-queue segment only (RFC-9293, 3.10.8)
if (len == 0 ||
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
mg_htonl(s->txq_seq), mg_htonl(s->ack), buf, len) == (size_t) -1) {
mg_htonl(s->txq_seq), mg_htonl(s->ack),
s->is_full ? 0 : MG_TCPIP_WIN, buf, len) == (size_t) -1) {
mg_error(c, "retransmit");
return;
}
@@ -1563,7 +1567,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
if (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, &c->loc, &c->rem, c->dscp, TH_ACK,
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
"", 0);
return;
}
// If we initiated the closure, we reply with ACK upon receiving FIN
@@ -1585,12 +1590,14 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
c->is_draining = 1;
}
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, flags,
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
"", 0);
} else if (pkt->pay.len <= 1 && mg_ntohl(pkt->tcp->seq) == s->ack - 1) {
// Keep-Alive (RFC-9293 3.8.4, allow erroneous implementations)
MG_VERBOSE(("%lu keepalive ACK", c->id));
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
NULL, 0);
return; // RFC-9293 3.10.7.4 discard (incorrect) payload, ACK, window
} else if (seq != s->ack) {
uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
@@ -1600,7 +1607,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
}
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
"", 0);
return; // drop it, RFC-9293 3.10.7.4: ignore ACKno
}
// Now process the segment for ACK and payload
@@ -1634,7 +1642,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
// Send ACK immediately
MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked));
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
NULL, 0);
s->unacked = 0;
s->acked = s->ack;
if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
@@ -1762,6 +1771,12 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
settmout(c,
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
read_conn(c, pkt); // Override timer with ACK timeout if needed
if (c->is_full && !s->is_full) {
// The receive handler paused the connection: advertise zero window now
s->is_full = true;
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq),
mg_htonl(s->ack), 0, NULL, 0);
}
} 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
@@ -2163,6 +2178,12 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
struct connstate *s = (struct connstate *) (c + 1);
if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving)
continue;
if (!c->is_full && s->is_full) {
// Application resumed the connection: advertise the restored window
s->is_full = false;
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq),
mg_htonl(s->ack), MG_TCPIP_WIN, NULL, 0);
}
if (s->retransmit && s->txq.len > 0 && ifp->now > s->txq_timer) {
if (s->txq_retries < 255) s->txq_retries++;
// R1 reports a delivery problem; it does not close (RFC-9293, 3.8.3).
@@ -2178,7 +2199,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
} else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) {
MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack));
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq),
mg_htonl(s->ack), NULL, 0);
mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
s->acked = s->ack;
} else if (s->ttype == MIP_TTYPE_SYN) {
mg_error(c, "Connection timeout");
@@ -2191,7 +2212,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
} else {
MG_VERBOSE(("%lu keepalive", c->id));
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0);
mg_htonl(s->seq - 1), mg_htonl(s->ack),
s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
}
}
@@ -2255,8 +2277,8 @@ void mg_tcpip_free(struct mg_tcpip_if *ifp) {
static void send_syn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_SYN, isn, 0, NULL,
0);
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_SYN, isn, 0,
MG_TCPIP_WIN, NULL, 0);
}
static void l2addr_resolved(struct mg_connection *c) {
@@ -2359,7 +2381,8 @@ static void init_closure(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
if (c->is_listening == false && c->is_connecting == false) {
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_FIN | TH_ACK,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
NULL, 0);
settmout(c, MIP_TTYPE_FIN);
}
}