Compare commits

..
2 Commits
Author SHA1 Message Date
Sergey Lyubka 08305b9000 Merge pull request #3751 from cesanta/dhcp
Fix: DHCP client stuck in REQUESTING state forever if the REQUEST or ACK is lost
2026-10-30 12:43:26 +00:00
cpq 69e8c0a92f Fix: DHCP client stuck in REQUESTING state forever if the REQUEST or ACK is lost 2026-10-30 09:42:12 +00:00
19 changed files with 132 additions and 1403 deletions

No files matched your search

+58 -216
View File
@@ -167,7 +167,6 @@ fail:
#endif #endif
#include <stdarg.h> #include <stdarg.h>
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS #if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
@@ -278,57 +277,6 @@ static struct mg_connection *bsd_new_conn(struct mg_bsd_sock *s) {
return c; return c;
} }
// Mirror net_builtin.c::trim_len() logic
static bool bsd_udp_too_big(struct mg_connection *c, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
size_t hlen = c->rem.is_ip6 ? 48 : 32; // IP plus UDP headers
size_t cap = ifp->mtu - hlen;
size_t room = ifp->tx.len - (ifp->framesize - ifp->l2mtu) - hlen;
return len > cap || len > room;
}
static bool bsd_udp_send(struct mg_connection *c, uint32_t ip, uint16_t port,
const void *buf, size_t len) {
struct mg_bsd_udp_frame *h;
size_t off = c->send.len, total = sizeof(*h) + len;
if (mg_iobuf_add(&c->send, off, NULL, total) != total) return false;
h = (struct mg_bsd_udp_frame *) (c->send.buf + off);
h->ip = ip;
h->port = port;
h->len = (uint16_t) len;
memcpy(h + 1, buf, len);
return true;
}
static size_t bsd_recv(struct mg_bsd_sock *s, void *buf, size_t len,
int flags, struct sockaddr *src, socklen_t *addrlen) {
struct mg_connection *c = s->c;
size_t n;
if (!s->is_udp) {
n = c->recv.len < len ? c->recv.len : len;
memcpy(buf, c->recv.buf, n);
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
} else {
struct mg_bsd_udp_frame *h = (struct mg_bsd_udp_frame *) c->recv.buf;
assert(c->recv.len >= sizeof(*h));
assert(h->len <= c->recv.len - sizeof(*h));
n = h->len < len ? h->len : len;
memcpy(buf, h + 1, n);
if (src != NULL && addrlen != NULL) {
struct sockaddr_in *sa = (struct sockaddr_in *) src;
sa->sin_family = AF_INET;
sa->sin_port = h->port;
sa->sin_addr.s_addr = h->ip;
*addrlen = sizeof(*sa);
}
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, sizeof(*h) + h->len);
}
return n;
}
#if MG_ENABLE_FREERTOS #if MG_ENABLE_FREERTOS
// Complete operations blocked on c before Mongoose destroys it. // Complete operations blocked on c before Mongoose destroys it.
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) { static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
@@ -509,9 +457,9 @@ static int bsd_ready(struct mg_bsd_sock *s) {
return 0; return 0;
} }
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ; if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
if ((s->is_udp || !s->c->is_connecting) && !s->c->is_closing && if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
!s->c->is_draining) !s->c->is_draining)
ready |= BSD_WRITE; // UDP socket or established TCP socket ready |= BSD_WRITE; // Established TCP socket
return ready; return ready;
} }
@@ -525,7 +473,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
if (t->orphan && t->listener == s) { if (t->orphan && t->listener == s) {
if (t->c != NULL) { if (t->c != NULL) {
t->c->fn_data = NULL; t->c->fn_data = NULL;
t->c->is_draining = 1; mg_close_conn(t->c);
t->c = NULL; t->c = NULL;
} }
memset(t, 0, sizeof(*t)); // Release orphan memset(t, 0, sizeof(*t)); // Release orphan
@@ -533,7 +481,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
} }
if (s->c != NULL) { if (s->c != NULL) {
s->c->fn_data = NULL; s->c->fn_data = NULL;
s->c->is_draining = 1; mg_close_conn(s->c);
s->c = NULL; s->c = NULL;
} }
memset(s, 0, sizeof(*s)); // Release socket memset(s, 0, sizeof(*s)); // Release socket
@@ -712,8 +660,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
cmd->result = -1, cmd->err = ECONNREFUSED; cmd->result = -1, cmd->err = ECONNREFUSED;
return true; return true;
} }
if (s->is_udp || !c->is_connecting) { if (!c->is_connecting) {
cmd->result = 0; // UDP may still resolve ARP; TCP waits for SYN/SYN-ACK cmd->result = 0;
return true; return true;
} }
if (s->nonblock) { if (s->nonblock) {
@@ -737,8 +685,11 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
return true; return true;
} }
if (c->recv.len > 0) { if (c->recv.len > 0) {
cmd->result = (int) bsd_recv(s, cmd->op_recv.buf, cmd->op_recv.len, size_t n = cmd->op_recv.len;
cmd->op_recv.flags, NULL, NULL); if (c->recv.len < n) n = c->recv.len;
memcpy(cmd->op_recv.buf, c->recv.buf, n);
if (!(cmd->op_recv.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
cmd->result = (int) n;
return true; return true;
} }
if (s->err != 0) { if (s->err != 0) {
@@ -759,17 +710,10 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
case BSD_OP_SEND: case BSD_OP_SEND:
if ((s = bsd_get(cmd->op_send.fd)) == NULL) { if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
cmd->result = -1, cmd->err = EBADF; cmd->result = -1, cmd->err = EBADF;
} else if (s->err != 0 || s->closed || } else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
((c = s->c) != NULL && (c->is_closing || c->is_draining))) { c->is_draining) {
cmd->result = -1, cmd->err = EPIPE; cmd->result = -1, cmd->err = EPIPE;
} else if (c == NULL || (s->is_udp && !c->is_client)) { } else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
cmd->result = -1, cmd->err = s->is_udp ? EDESTADDRREQ : EPIPE;
} else if (s->is_udp && bsd_udp_too_big(c, cmd->op_send.len)) {
cmd->result = -1, cmd->err = EMSGSIZE;
} else if ((s->is_udp ? bsd_udp_send(c, c->rem.addr.ip4, c->rem.port,
cmd->op_send.buf, cmd->op_send.len)
: mg_send(c, cmd->op_send.buf,
cmd->op_send.len))) {
cmd->result = (int) cmd->op_send.len; cmd->result = (int) cmd->op_send.len;
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) { } else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
cmd->result = -1, cmd->err = EAGAIN; cmd->result = -1, cmd->err = EAGAIN;
@@ -784,20 +728,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
case BSD_OP_SENDTO: case BSD_OP_SENDTO:
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) { if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
cmd->result = -1, cmd->err = EBADF; cmd->result = -1, cmd->err = EBADF;
} else if (!s->is_udp) {
cmd->result = -1, cmd->err = EOPNOTSUPP;
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing || } else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
c->is_draining) { c->is_draining) {
cmd->result = -1, cmd->err = EPIPE; cmd->result = -1, cmd->err = EPIPE;
} else if (c->is_client) {
cmd->result = -1, cmd->err = EISCONN;
} else { } else {
const struct sockaddr_in *sa = const struct sockaddr_in *sa =
(const struct sockaddr_in *) cmd->op_sendto.addr; (const struct sockaddr_in *) cmd->op_sendto.addr;
if (bsd_udp_too_big(c, cmd->op_sendto.len)) { c->rem.port = sa->sin_port;
cmd->result = -1, cmd->err = EMSGSIZE; c->rem.addr.ip4 = sa->sin_addr.s_addr;
} else if (bsd_udp_send(c, sa->sin_addr.s_addr, sa->sin_port, c->rem.is_ip6 = false;
cmd->op_sendto.buf, cmd->op_sendto.len)) { if (mg_send(c, cmd->op_sendto.buf, cmd->op_sendto.len)) {
cmd->result = (int) cmd->op_sendto.len; cmd->result = (int) cmd->op_sendto.len;
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) { } else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
cmd->result = -1, cmd->err = EAGAIN; cmd->result = -1, cmd->err = EAGAIN;
@@ -814,9 +754,6 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) { if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
cmd->result = -1, cmd->err = EBADF; cmd->result = -1, cmd->err = EBADF;
return true; return true;
} else if (!s->is_udp) {
cmd->result = -1, cmd->err = EOPNOTSUPP;
return true;
} }
if ((c = s->c) == NULL) { if ((c = s->c) == NULL) {
cmd->result = s->closed && s->err == 0 ? 0 : -1; cmd->result = s->closed && s->err == 0 ? 0 : -1;
@@ -824,11 +761,18 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
return true; return true;
} }
if (c->recv.len > 0) { if (c->recv.len > 0) {
cmd->result = (int) bsd_recv(s, cmd->op_recvfrom.buf, size_t n = cmd->op_recvfrom.len;
cmd->op_recvfrom.len, if (c->recv.len < n) n = c->recv.len;
cmd->op_recvfrom.flags, memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
cmd->op_recvfrom.src, if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
cmd->op_recvfrom.addrlen); if (cmd->op_recvfrom.src != NULL && cmd->op_recvfrom.addrlen != NULL) {
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_recvfrom.src;
sa->sin_family = AF_INET;
sa->sin_port = c->rem.port;
sa->sin_addr.s_addr = c->rem.addr.ip4;
*cmd->op_recvfrom.addrlen = sizeof(*sa);
}
cmd->result = (int) n;
return true; return true;
} }
if (s->err != 0) { if (s->err != 0) {
@@ -9330,36 +9274,6 @@ static void l2addr_resolved(struct mg_connection *c);
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem, static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
bool is_udp, struct pkt *pkt); bool is_udp, struct pkt *pkt);
#if MG_ENABLE_BSD_SOCKETS
static uint8_t *sendto_cache_find(struct mg_tcpip_if *ifp, uint32_t ip) {
struct mg_tcpip_sendto_entry *a;
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
if (a->ip == ip) return (uint8_t *) &a->addr;
}
return NULL;
}
static void sendto_cache_add(struct mg_tcpip_if *ifp, uint32_t ip,
const uint8_t *addr) {
struct mg_tcpip_sendto_entry *a;
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
if (a->ip == ip) return; // already cached, skip
if (a->ip == 0) break; // not present, first empty slot
}
if (a == ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE) { // no room
memmove(ifp->sendto, ifp->sendto + 1,
sizeof(ifp->sendto[0]) * (MG_TCPIP_SENDTO_CACHE_SIZE - 1));
a--; // make room and point to last entry (just made)
}
a->ip = ip; // populate this entry
memcpy(&a->addr, addr, sizeof(a->addr));
}
static void sendto_cache_clear(struct mg_tcpip_if *ifp) {
memset(ifp->sendto, 0, sizeof(ifp->sendto));
}
#endif
// RFC826, ARP assumes Ethernet MAC addresses // RFC826, ARP assumes Ethernet MAC addresses
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) { if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
@@ -9485,6 +9399,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
: pkt->dhcp->siaddr ? pkt->dhcp->siaddr : pkt->dhcp->siaddr ? pkt->dhcp->siaddr
: pkt->ip->src); : pkt->ip->src);
ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state
ifp->lease_expire = ifp->now + 5000; // No ACK by then: start over
} else if (msgtype == 5) { // DHCPACK } else if (msgtype == 5) { // DHCPACK
if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP
uint64_t rand; uint64_t rand;
@@ -9965,57 +9880,30 @@ static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct mg_connection *c = getpeer(ifp->mgr, pkt, true); struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
struct connstate *s; struct connstate *s;
struct mg_addr rem;
uint8_t *l2addr; uint8_t *l2addr;
size_t len = pkt->pay.len;
if (c == NULL) return false; // No UDP listener on this port if (c == NULL) return false; // No UDP listener on this port
s = (struct connstate *) (c + 1); s = (struct connstate *) (c + 1);
c->rem.port = pkt->udp->sport;
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer() if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false; if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
if (c->is_bsd && c->is_client && c->rem.addr.ip6[0] = pkt->ip6->src[0],
(c->rem.port != pkt->udp->sport || c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
!MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src)))
return true;
rem.addr.ip6[0] = pkt->ip6->src[0],
rem.addr.ip6[1] = pkt->ip6->src[1], rem.is_ip6 = true;
} else } else
#endif #endif
{ {
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false; if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
if (c->is_bsd && c->is_client && c->rem.addr.ip4 = pkt->ip->src;
(c->rem.port != pkt->udp->sport || c->rem.addr.ip4 != pkt->ip->src))
return true;
rem.addr.ip4 = pkt->ip->src, rem.is_ip6 = false;
} }
rem.port = pkt->udp->sport; if ((l2addr = get_return_l2addr(ifp, &c->rem, true, pkt)) == NULL)
if (!c->is_bsd || c->is_client) c->rem = rem;
if ((l2addr = get_return_l2addr(ifp, &rem, true, pkt)) == NULL)
return false; // safety net for lousy networks return false; // safety net for lousy networks
if (!c->is_bsd || c->is_client) memcpy(s->mac, l2addr, sizeof(s->mac)); memcpy(s->mac, l2addr, sizeof(s->mac));
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd && !rem.is_ip6 && MG_IPISLOCAL(ifp, rem.addr.ip4))
sendto_cache_add(ifp, rem.addr.ip4, l2addr);
#endif
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd) len += sizeof(struct mg_bsd_udp_frame);
#endif
if (c->recv.len >= MG_MAX_RECV_SIZE) { if (c->recv.len >= MG_MAX_RECV_SIZE) {
proto_error(c, false, "max_recv_buf_size reached"); proto_error(c, false, "max_recv_buf_size reached");
} else if (c->recv.size - c->recv.len < len && } else if (c->recv.size - c->recv.len < pkt->pay.len &&
!mg_iobuf_resize(&c->recv, c->recv.len + len)) { !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
proto_error(c, false, "oom"); proto_error(c, false, "oom");
} else { } else {
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd) {
struct mg_bsd_udp_frame *h =
(struct mg_bsd_udp_frame *) &c->recv.buf[c->recv.len];
h->ip = rem.addr.ip4;
h->port = rem.port;
h->len = (uint16_t) pkt->pay.len;
c->recv.len += sizeof(*h);
}
#endif
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
c->recv.len += pkt->pay.len; c->recv.len += pkt->pay.len;
mg_call(c, MG_EV_READ, &pkt->pay.len); mg_call(c, MG_EV_READ, &pkt->pay.len);
@@ -10819,6 +10707,9 @@ static void mg_ip_poll(struct mg_tcpip_if *ifp, bool s1) {
if (ifp->enable_dhcp_client && s1) { if (ifp->enable_dhcp_client && s1) {
if (ifp->state == MG_TCPIP_STATE_UP) { if (ifp->state == MG_TCPIP_STATE_UP) {
tx_dhcp_discover(ifp); // INIT (4.4.1) tx_dhcp_discover(ifp); // INIT (4.4.1)
} else if (ifp->state == MG_TCPIP_STATE_REQ &&
ifp->now >= ifp->lease_expire) {
ifp->state = MG_TCPIP_STATE_UP; // REQUEST or ACK lost: DISCOVER again
} else if (ifp->state == MG_TCPIP_STATE_READY && } else if (ifp->state == MG_TCPIP_STATE_READY &&
ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
if (ifp->now >= ifp->lease_expire) { if (ifp->now >= ifp->lease_expire) {
@@ -11068,9 +10959,6 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
static void close_conn(struct mg_connection *c) { static void close_conn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd && c->is_udp) sendto_cache_clear(c->mgr->ifp);
#endif
mg_iobuf_free(&s->txq); mg_iobuf_free(&s->txq);
mg_close_conn(c); mg_close_conn(c);
} }
@@ -11092,11 +10980,6 @@ static void send_syn(struct mg_connection *c) {
static void l2addr_resolved(struct mg_connection *c) { static void l2addr_resolved(struct mg_connection *c) {
if (c->is_udp) { if (c->is_udp) {
c->is_connecting = 0; c->is_connecting = 0;
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd)
sendto_cache_add(c->mgr->ifp, c->rem.addr.ip4,
((struct connstate *) (c + 1))->mac);
#endif
mg_call(c, MG_EV_CONNECT, NULL); mg_call(c, MG_EV_CONNECT, NULL);
} else { } else {
send_syn(c); send_syn(c);
@@ -11111,25 +10994,20 @@ void mg_connect_resolved(struct mg_connection *c) {
s->retransmit = ifp->enable_tcp_retransmit; s->retransmit = ifp->enable_tcp_retransmit;
s->txq.align = MG_TCPIP_TXQ_ALIGN; s->txq.align = MG_TCPIP_TXQ_ALIGN;
c->is_resolving = 0; c->is_resolving = 0;
if (c->loc.port == 0) { if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) c->loc.port = mg_htons(ifp->eport++);
ifp->eport = MG_EPHEMERAL_PORT_BASE;
c->loc.port = mg_htons(ifp->eport++);
} // otherwise retain an existing bound port
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (c->rem.is_ip6) { if (c->rem.is_ip6) {
if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0 && if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1]; c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
} else if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0) { } else { // use global address
// Use global address
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1]; c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
} // otherwise retain an existing bound address }
c->loc.is_ip6 = true; c->loc.is_ip6 = true;
} else } else
#endif #endif
{ {
if (c->loc.addr.ip4 == 0) c->loc.addr.ip4 = ifp->ip; c->loc.addr.ip4 = ifp->ip;
} }
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
&c->rem)); &c->rem));
@@ -11160,20 +11038,11 @@ void mg_connect_resolved(struct mg_connection *c) {
uint32_t rem_ip = c->rem.addr.ip4; uint32_t rem_ip = c->rem.addr.ip4;
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) && if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP) rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
// We're in the same LAN // If we're in the same LAN, fire an ARP lookup.
#if MG_ENABLE_BSD_SOCKETS MG_DEBUG(("%lu ARP lookup...", c->id));
if (c->is_bsd && c->is_udp && // see if we already have L2 data mg_tcpip_arp_request(ifp, rem_ip, NULL);
(l2addr = sendto_cache_find(ifp, rem_ip)) != NULL) { settmout(c, MIP_TTYPE_ARP);
memcpy(s->mac, l2addr, sizeof(s->mac)); c->is_arplooking = 1;
l2addr_resolved(c);
} else
#endif
{ // fire an ARP lookup
MG_DEBUG(("%lu ARP lookup...", c->id));
mg_tcpip_arp_request(ifp, rem_ip, NULL);
settmout(c, MIP_TTYPE_ARP);
c->is_arplooking = 1;
}
} else if (ifp->gw_ready) { } else if (ifp->gw_ready) {
memcpy(s->mac, ifp->gwmac, sizeof(s->mac)); memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
l2addr_resolved(c); l2addr_resolved(c);
@@ -11193,36 +11062,13 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
} }
static void write_conn(struct mg_connection *c) { static void write_conn(struct mg_connection *c) {
long len; long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
size_t off = 0; : mg_io_send(c, c->send.buf, c->send.len);
bool empty = false;
#if MG_ENABLE_BSD_SOCKETS
struct mg_bsd_udp_frame *h;
if (c->is_bsd && c->is_udp) {
if (c->is_connecting) return;
h = (struct mg_bsd_udp_frame *) c->send.buf;
assert(c->send.len >= sizeof(*h));
assert(h->len <= c->send.len - sizeof(*h));
c->rem.port = h->port;
c->rem.is_ip6 = false;
if (!c->is_client && c->rem.addr.ip4 != h->ip) {
c->rem.addr.ip4 = h->ip;
mg_connect_resolved(c);
if (c->is_connecting) return;
}
len = mg_io_send(c, h + 1, h->len);
off = sizeof(*h), empty = h->len == 0;
} else
#endif
{
len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
: mg_io_send(c, c->send.buf, c->send.len);
}
// TODO(): mg_tls_send() may return 0 forever on steady OOM // TODO(): mg_tls_send() may return 0 forever on steady OOM
if (len == MG_IO_ERR) { if (len == MG_IO_ERR) {
proto_error(c, false, "tx err"); proto_error(c, false, "tx err");
} else if (len > 0 || empty) { } else if (len > 0) {
mg_iobuf_del(&c->send, 0, off + (size_t) len); mg_iobuf_del(&c->send, 0, (size_t) len);
mg_call(c, MG_EV_WRITE, &len); mg_call(c, MG_EV_WRITE, &len);
} }
} }
@@ -15531,11 +15377,7 @@ static long recv_raw(struct mg_connection *c, void *buf, size_t len) {
union usa usa; union usa usa;
socklen_t slen = tousa(&c->rem, &usa); socklen_t slen = tousa(&c->rem, &usa);
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen); n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
if (n > 0) { if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
struct mg_addr rem;
tomgaddr(&usa, &rem, slen != sizeof(usa.sin));
c->rem = rem;
}
} else { } else {
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING); n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
} }
-41
View File
@@ -4620,17 +4620,6 @@ struct mg_tcpip_driver {
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev, typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
void *ev_data); void *ev_data);
#if MG_ENABLE_BSD_SOCKETS
#ifndef MG_TCPIP_SENDTO_CACHE_SIZE
#define MG_TCPIP_SENDTO_CACHE_SIZE 4
#endif
struct mg_tcpip_sendto_entry {
uint32_t ip;
struct mg_l2addr addr;
};
#endif
// clang-format off // clang-format off
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver). // Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
// Each entry shows the ev_data type to cast inside the handler. // Each entry shows the ev_data type to cast inside the handler.
@@ -4693,9 +4682,6 @@ struct mg_tcpip_if {
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h) enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
union mg_l2data l2data; // Layer-2 config and state (see l2.h) union mg_l2data l2data; // Layer-2 config and state (see l2.h)
char *dns4_url; // Active DNS server URL (set from DHCP or config) char *dns4_url; // Active DNS server URL (set from DHCP or config)
#if MG_ENABLE_BSD_SOCKETS
struct mg_tcpip_sendto_entry sendto[MG_TCPIP_SENDTO_CACHE_SIZE]; // Local IPv4 neighbors
#endif
uint64_t now; // Current time in ms (updated each mg_mgr_poll) uint64_t now; // Current time in ms (updated each mg_mgr_poll)
uint64_t timer_1000ms; // Timestamp of last 1-second tick uint64_t timer_1000ms; // Timestamp of last 1-second tick
uint64_t lease_expire; // DHCP lease expiry time in ms uint64_t lease_expire; // DHCP lease expiry time in ms
@@ -4907,37 +4893,10 @@ void mg_bsd_poll(struct mg_mgr *);
#ifndef EPIPE #ifndef EPIPE
#define EPIPE 32 #define EPIPE 32
#endif #endif
#ifndef EOPNOTSUPP
#define EOPNOTSUPP 95
#endif
#ifndef EISCONN
#define EISCONN 106
#endif
#ifndef EDESTADDRREQ
#define EDESTADDRREQ 121
#endif
#ifndef EMSGSIZE
#define EMSGSIZE 90
#endif
#ifndef EPROTONOSUPPORT #ifndef EPROTONOSUPPORT
#define EPROTONOSUPPORT 93 #define EPROTONOSUPPORT 93
#endif #endif
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct mg_bsd_udp_frame {
uint32_t ip;
uint16_t port, len;
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
#if MG_ENABLE_BSD_PROTOTYPES #if MG_ENABLE_BSD_PROTOTYPES
typedef unsigned int socklen_t; typedef unsigned int socklen_t;
typedef unsigned long nfds_t; typedef unsigned long nfds_t;
+30 -86
View File
@@ -1,6 +1,5 @@
#include "bsd.h" #include "bsd.h"
#include "dns.h" #include "dns.h"
#include "util.h"
#include <stdarg.h> #include <stdarg.h>
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS #if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
@@ -111,57 +110,6 @@ static struct mg_connection *bsd_new_conn(struct mg_bsd_sock *s) {
return c; return c;
} }
// Mirror net_builtin.c::trim_len() logic
static bool bsd_udp_too_big(struct mg_connection *c, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
size_t hlen = c->rem.is_ip6 ? 48 : 32; // IP plus UDP headers
size_t cap = ifp->mtu - hlen;
size_t room = ifp->tx.len - (ifp->framesize - ifp->l2mtu) - hlen;
return len > cap || len > room;
}
static bool bsd_udp_send(struct mg_connection *c, uint32_t ip, uint16_t port,
const void *buf, size_t len) {
struct mg_bsd_udp_frame *h;
size_t off = c->send.len, total = sizeof(*h) + len;
if (mg_iobuf_add(&c->send, off, NULL, total) != total) return false;
h = (struct mg_bsd_udp_frame *) (c->send.buf + off);
h->ip = ip;
h->port = port;
h->len = (uint16_t) len;
memcpy(h + 1, buf, len);
return true;
}
static size_t bsd_recv(struct mg_bsd_sock *s, void *buf, size_t len,
int flags, struct sockaddr *src, socklen_t *addrlen) {
struct mg_connection *c = s->c;
size_t n;
if (!s->is_udp) {
n = c->recv.len < len ? c->recv.len : len;
memcpy(buf, c->recv.buf, n);
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
} else {
struct mg_bsd_udp_frame *h = (struct mg_bsd_udp_frame *) c->recv.buf;
assert(c->recv.len >= sizeof(*h));
assert(h->len <= c->recv.len - sizeof(*h));
n = h->len < len ? h->len : len;
memcpy(buf, h + 1, n);
if (src != NULL && addrlen != NULL) {
struct sockaddr_in *sa = (struct sockaddr_in *) src;
sa->sin_family = AF_INET;
sa->sin_port = h->port;
sa->sin_addr.s_addr = h->ip;
*addrlen = sizeof(*sa);
}
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, sizeof(*h) + h->len);
}
return n;
}
#if MG_ENABLE_FREERTOS #if MG_ENABLE_FREERTOS
// Complete operations blocked on c before Mongoose destroys it. // Complete operations blocked on c before Mongoose destroys it.
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) { static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
@@ -342,9 +290,9 @@ static int bsd_ready(struct mg_bsd_sock *s) {
return 0; return 0;
} }
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ; if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
if ((s->is_udp || !s->c->is_connecting) && !s->c->is_closing && if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
!s->c->is_draining) !s->c->is_draining)
ready |= BSD_WRITE; // UDP socket or established TCP socket ready |= BSD_WRITE; // Established TCP socket
return ready; return ready;
} }
@@ -358,7 +306,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
if (t->orphan && t->listener == s) { if (t->orphan && t->listener == s) {
if (t->c != NULL) { if (t->c != NULL) {
t->c->fn_data = NULL; t->c->fn_data = NULL;
t->c->is_draining = 1; mg_close_conn(t->c);
t->c = NULL; t->c = NULL;
} }
memset(t, 0, sizeof(*t)); // Release orphan memset(t, 0, sizeof(*t)); // Release orphan
@@ -366,7 +314,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
} }
if (s->c != NULL) { if (s->c != NULL) {
s->c->fn_data = NULL; s->c->fn_data = NULL;
s->c->is_draining = 1; mg_close_conn(s->c);
s->c = NULL; s->c = NULL;
} }
memset(s, 0, sizeof(*s)); // Release socket memset(s, 0, sizeof(*s)); // Release socket
@@ -545,8 +493,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
cmd->result = -1, cmd->err = ECONNREFUSED; cmd->result = -1, cmd->err = ECONNREFUSED;
return true; return true;
} }
if (s->is_udp || !c->is_connecting) { if (!c->is_connecting) {
cmd->result = 0; // UDP may still resolve ARP; TCP waits for SYN/SYN-ACK cmd->result = 0;
return true; return true;
} }
if (s->nonblock) { if (s->nonblock) {
@@ -570,8 +518,11 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
return true; return true;
} }
if (c->recv.len > 0) { if (c->recv.len > 0) {
cmd->result = (int) bsd_recv(s, cmd->op_recv.buf, cmd->op_recv.len, size_t n = cmd->op_recv.len;
cmd->op_recv.flags, NULL, NULL); if (c->recv.len < n) n = c->recv.len;
memcpy(cmd->op_recv.buf, c->recv.buf, n);
if (!(cmd->op_recv.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
cmd->result = (int) n;
return true; return true;
} }
if (s->err != 0) { if (s->err != 0) {
@@ -592,17 +543,10 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
case BSD_OP_SEND: case BSD_OP_SEND:
if ((s = bsd_get(cmd->op_send.fd)) == NULL) { if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
cmd->result = -1, cmd->err = EBADF; cmd->result = -1, cmd->err = EBADF;
} else if (s->err != 0 || s->closed || } else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
((c = s->c) != NULL && (c->is_closing || c->is_draining))) { c->is_draining) {
cmd->result = -1, cmd->err = EPIPE; cmd->result = -1, cmd->err = EPIPE;
} else if (c == NULL || (s->is_udp && !c->is_client)) { } else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
cmd->result = -1, cmd->err = s->is_udp ? EDESTADDRREQ : EPIPE;
} else if (s->is_udp && bsd_udp_too_big(c, cmd->op_send.len)) {
cmd->result = -1, cmd->err = EMSGSIZE;
} else if ((s->is_udp ? bsd_udp_send(c, c->rem.addr.ip4, c->rem.port,
cmd->op_send.buf, cmd->op_send.len)
: mg_send(c, cmd->op_send.buf,
cmd->op_send.len))) {
cmd->result = (int) cmd->op_send.len; cmd->result = (int) cmd->op_send.len;
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) { } else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
cmd->result = -1, cmd->err = EAGAIN; cmd->result = -1, cmd->err = EAGAIN;
@@ -617,20 +561,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
case BSD_OP_SENDTO: case BSD_OP_SENDTO:
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) { if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
cmd->result = -1, cmd->err = EBADF; cmd->result = -1, cmd->err = EBADF;
} else if (!s->is_udp) {
cmd->result = -1, cmd->err = EOPNOTSUPP;
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing || } else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
c->is_draining) { c->is_draining) {
cmd->result = -1, cmd->err = EPIPE; cmd->result = -1, cmd->err = EPIPE;
} else if (c->is_client) {
cmd->result = -1, cmd->err = EISCONN;
} else { } else {
const struct sockaddr_in *sa = const struct sockaddr_in *sa =
(const struct sockaddr_in *) cmd->op_sendto.addr; (const struct sockaddr_in *) cmd->op_sendto.addr;
if (bsd_udp_too_big(c, cmd->op_sendto.len)) { c->rem.port = sa->sin_port;
cmd->result = -1, cmd->err = EMSGSIZE; c->rem.addr.ip4 = sa->sin_addr.s_addr;
} else if (bsd_udp_send(c, sa->sin_addr.s_addr, sa->sin_port, c->rem.is_ip6 = false;
cmd->op_sendto.buf, cmd->op_sendto.len)) { if (mg_send(c, cmd->op_sendto.buf, cmd->op_sendto.len)) {
cmd->result = (int) cmd->op_sendto.len; cmd->result = (int) cmd->op_sendto.len;
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) { } else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
cmd->result = -1, cmd->err = EAGAIN; cmd->result = -1, cmd->err = EAGAIN;
@@ -647,9 +587,6 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) { if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
cmd->result = -1, cmd->err = EBADF; cmd->result = -1, cmd->err = EBADF;
return true; return true;
} else if (!s->is_udp) {
cmd->result = -1, cmd->err = EOPNOTSUPP;
return true;
} }
if ((c = s->c) == NULL) { if ((c = s->c) == NULL) {
cmd->result = s->closed && s->err == 0 ? 0 : -1; cmd->result = s->closed && s->err == 0 ? 0 : -1;
@@ -657,11 +594,18 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
return true; return true;
} }
if (c->recv.len > 0) { if (c->recv.len > 0) {
cmd->result = (int) bsd_recv(s, cmd->op_recvfrom.buf, size_t n = cmd->op_recvfrom.len;
cmd->op_recvfrom.len, if (c->recv.len < n) n = c->recv.len;
cmd->op_recvfrom.flags, memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
cmd->op_recvfrom.src, if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
cmd->op_recvfrom.addrlen); if (cmd->op_recvfrom.src != NULL && cmd->op_recvfrom.addrlen != NULL) {
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_recvfrom.src;
sa->sin_family = AF_INET;
sa->sin_port = c->rem.port;
sa->sin_addr.s_addr = c->rem.addr.ip4;
*cmd->op_recvfrom.addrlen = sizeof(*sa);
}
cmd->result = (int) n;
return true; return true;
} }
if (s->err != 0) { if (s->err != 0) {
-27
View File
@@ -55,37 +55,10 @@ void mg_bsd_poll(struct mg_mgr *);
#ifndef EPIPE #ifndef EPIPE
#define EPIPE 32 #define EPIPE 32
#endif #endif
#ifndef EOPNOTSUPP
#define EOPNOTSUPP 95
#endif
#ifndef EISCONN
#define EISCONN 106
#endif
#ifndef EDESTADDRREQ
#define EDESTADDRREQ 121
#endif
#ifndef EMSGSIZE
#define EMSGSIZE 90
#endif
#ifndef EPROTONOSUPPORT #ifndef EPROTONOSUPPORT
#define EPROTONOSUPPORT 93 #define EPROTONOSUPPORT 93
#endif #endif
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct mg_bsd_udp_frame {
uint32_t ip;
uint16_t port, len;
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
#if MG_ENABLE_BSD_PROTOTYPES #if MG_ENABLE_BSD_PROTOTYPES
typedef unsigned int socklen_t; typedef unsigned int socklen_t;
typedef unsigned long nfds_t; typedef unsigned long nfds_t;
+27 -125
View File
@@ -609,36 +609,6 @@ static void l2addr_resolved(struct mg_connection *c);
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem, static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
bool is_udp, struct pkt *pkt); bool is_udp, struct pkt *pkt);
#if MG_ENABLE_BSD_SOCKETS
static uint8_t *sendto_cache_find(struct mg_tcpip_if *ifp, uint32_t ip) {
struct mg_tcpip_sendto_entry *a;
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
if (a->ip == ip) return (uint8_t *) &a->addr;
}
return NULL;
}
static void sendto_cache_add(struct mg_tcpip_if *ifp, uint32_t ip,
const uint8_t *addr) {
struct mg_tcpip_sendto_entry *a;
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
if (a->ip == ip) return; // already cached, skip
if (a->ip == 0) break; // not present, first empty slot
}
if (a == ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE) { // no room
memmove(ifp->sendto, ifp->sendto + 1,
sizeof(ifp->sendto[0]) * (MG_TCPIP_SENDTO_CACHE_SIZE - 1));
a--; // make room and point to last entry (just made)
}
a->ip = ip; // populate this entry
memcpy(&a->addr, addr, sizeof(a->addr));
}
static void sendto_cache_clear(struct mg_tcpip_if *ifp) {
memset(ifp->sendto, 0, sizeof(ifp->sendto));
}
#endif
// RFC826, ARP assumes Ethernet MAC addresses // RFC826, ARP assumes Ethernet MAC addresses
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) { if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
@@ -764,6 +734,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
: pkt->dhcp->siaddr ? pkt->dhcp->siaddr : pkt->dhcp->siaddr ? pkt->dhcp->siaddr
: pkt->ip->src); : pkt->ip->src);
ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state
ifp->lease_expire = ifp->now + 5000; // No ACK by then: start over
} else if (msgtype == 5) { // DHCPACK } else if (msgtype == 5) { // DHCPACK
if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP
uint64_t rand; uint64_t rand;
@@ -1244,57 +1215,30 @@ static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct mg_connection *c = getpeer(ifp->mgr, pkt, true); struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
struct connstate *s; struct connstate *s;
struct mg_addr rem;
uint8_t *l2addr; uint8_t *l2addr;
size_t len = pkt->pay.len;
if (c == NULL) return false; // No UDP listener on this port if (c == NULL) return false; // No UDP listener on this port
s = (struct connstate *) (c + 1); s = (struct connstate *) (c + 1);
c->rem.port = pkt->udp->sport;
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer() if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false; if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
if (c->is_bsd && c->is_client && c->rem.addr.ip6[0] = pkt->ip6->src[0],
(c->rem.port != pkt->udp->sport || c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
!MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src)))
return true;
rem.addr.ip6[0] = pkt->ip6->src[0],
rem.addr.ip6[1] = pkt->ip6->src[1], rem.is_ip6 = true;
} else } else
#endif #endif
{ {
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false; if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
if (c->is_bsd && c->is_client && c->rem.addr.ip4 = pkt->ip->src;
(c->rem.port != pkt->udp->sport || c->rem.addr.ip4 != pkt->ip->src))
return true;
rem.addr.ip4 = pkt->ip->src, rem.is_ip6 = false;
} }
rem.port = pkt->udp->sport; if ((l2addr = get_return_l2addr(ifp, &c->rem, true, pkt)) == NULL)
if (!c->is_bsd || c->is_client) c->rem = rem;
if ((l2addr = get_return_l2addr(ifp, &rem, true, pkt)) == NULL)
return false; // safety net for lousy networks return false; // safety net for lousy networks
if (!c->is_bsd || c->is_client) memcpy(s->mac, l2addr, sizeof(s->mac)); memcpy(s->mac, l2addr, sizeof(s->mac));
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd && !rem.is_ip6 && MG_IPISLOCAL(ifp, rem.addr.ip4))
sendto_cache_add(ifp, rem.addr.ip4, l2addr);
#endif
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd) len += sizeof(struct mg_bsd_udp_frame);
#endif
if (c->recv.len >= MG_MAX_RECV_SIZE) { if (c->recv.len >= MG_MAX_RECV_SIZE) {
proto_error(c, false, "max_recv_buf_size reached"); proto_error(c, false, "max_recv_buf_size reached");
} else if (c->recv.size - c->recv.len < len && } else if (c->recv.size - c->recv.len < pkt->pay.len &&
!mg_iobuf_resize(&c->recv, c->recv.len + len)) { !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
proto_error(c, false, "oom"); proto_error(c, false, "oom");
} else { } else {
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd) {
struct mg_bsd_udp_frame *h =
(struct mg_bsd_udp_frame *) &c->recv.buf[c->recv.len];
h->ip = rem.addr.ip4;
h->port = rem.port;
h->len = (uint16_t) pkt->pay.len;
c->recv.len += sizeof(*h);
}
#endif
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
c->recv.len += pkt->pay.len; c->recv.len += pkt->pay.len;
mg_call(c, MG_EV_READ, &pkt->pay.len); mg_call(c, MG_EV_READ, &pkt->pay.len);
@@ -2098,6 +2042,9 @@ static void mg_ip_poll(struct mg_tcpip_if *ifp, bool s1) {
if (ifp->enable_dhcp_client && s1) { if (ifp->enable_dhcp_client && s1) {
if (ifp->state == MG_TCPIP_STATE_UP) { if (ifp->state == MG_TCPIP_STATE_UP) {
tx_dhcp_discover(ifp); // INIT (4.4.1) tx_dhcp_discover(ifp); // INIT (4.4.1)
} else if (ifp->state == MG_TCPIP_STATE_REQ &&
ifp->now >= ifp->lease_expire) {
ifp->state = MG_TCPIP_STATE_UP; // REQUEST or ACK lost: DISCOVER again
} else if (ifp->state == MG_TCPIP_STATE_READY && } else if (ifp->state == MG_TCPIP_STATE_READY &&
ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
if (ifp->now >= ifp->lease_expire) { if (ifp->now >= ifp->lease_expire) {
@@ -2347,9 +2294,6 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
static void close_conn(struct mg_connection *c) { static void close_conn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd && c->is_udp) sendto_cache_clear(c->mgr->ifp);
#endif
mg_iobuf_free(&s->txq); mg_iobuf_free(&s->txq);
mg_close_conn(c); mg_close_conn(c);
} }
@@ -2371,11 +2315,6 @@ static void send_syn(struct mg_connection *c) {
static void l2addr_resolved(struct mg_connection *c) { static void l2addr_resolved(struct mg_connection *c) {
if (c->is_udp) { if (c->is_udp) {
c->is_connecting = 0; c->is_connecting = 0;
#if MG_ENABLE_BSD_SOCKETS
if (c->is_bsd)
sendto_cache_add(c->mgr->ifp, c->rem.addr.ip4,
((struct connstate *) (c + 1))->mac);
#endif
mg_call(c, MG_EV_CONNECT, NULL); mg_call(c, MG_EV_CONNECT, NULL);
} else { } else {
send_syn(c); send_syn(c);
@@ -2390,25 +2329,20 @@ void mg_connect_resolved(struct mg_connection *c) {
s->retransmit = ifp->enable_tcp_retransmit; s->retransmit = ifp->enable_tcp_retransmit;
s->txq.align = MG_TCPIP_TXQ_ALIGN; s->txq.align = MG_TCPIP_TXQ_ALIGN;
c->is_resolving = 0; c->is_resolving = 0;
if (c->loc.port == 0) { if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) c->loc.port = mg_htons(ifp->eport++);
ifp->eport = MG_EPHEMERAL_PORT_BASE;
c->loc.port = mg_htons(ifp->eport++);
} // otherwise retain an existing bound port
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (c->rem.is_ip6) { if (c->rem.is_ip6) {
if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0 && if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1]; c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
} else if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0) { } else { // use global address
// Use global address
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1]; c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
} // otherwise retain an existing bound address }
c->loc.is_ip6 = true; c->loc.is_ip6 = true;
} else } else
#endif #endif
{ {
if (c->loc.addr.ip4 == 0) c->loc.addr.ip4 = ifp->ip; c->loc.addr.ip4 = ifp->ip;
} }
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
&c->rem)); &c->rem));
@@ -2439,20 +2373,11 @@ void mg_connect_resolved(struct mg_connection *c) {
uint32_t rem_ip = c->rem.addr.ip4; uint32_t rem_ip = c->rem.addr.ip4;
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) && if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP) rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
// We're in the same LAN // If we're in the same LAN, fire an ARP lookup.
#if MG_ENABLE_BSD_SOCKETS MG_DEBUG(("%lu ARP lookup...", c->id));
if (c->is_bsd && c->is_udp && // see if we already have L2 data mg_tcpip_arp_request(ifp, rem_ip, NULL);
(l2addr = sendto_cache_find(ifp, rem_ip)) != NULL) { settmout(c, MIP_TTYPE_ARP);
memcpy(s->mac, l2addr, sizeof(s->mac)); c->is_arplooking = 1;
l2addr_resolved(c);
} else
#endif
{ // fire an ARP lookup
MG_DEBUG(("%lu ARP lookup...", c->id));
mg_tcpip_arp_request(ifp, rem_ip, NULL);
settmout(c, MIP_TTYPE_ARP);
c->is_arplooking = 1;
}
} else if (ifp->gw_ready) { } else if (ifp->gw_ready) {
memcpy(s->mac, ifp->gwmac, sizeof(s->mac)); memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
l2addr_resolved(c); l2addr_resolved(c);
@@ -2472,36 +2397,13 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
} }
static void write_conn(struct mg_connection *c) { static void write_conn(struct mg_connection *c) {
long len; long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
size_t off = 0; : mg_io_send(c, c->send.buf, c->send.len);
bool empty = false;
#if MG_ENABLE_BSD_SOCKETS
struct mg_bsd_udp_frame *h;
if (c->is_bsd && c->is_udp) {
if (c->is_connecting) return;
h = (struct mg_bsd_udp_frame *) c->send.buf;
assert(c->send.len >= sizeof(*h));
assert(h->len <= c->send.len - sizeof(*h));
c->rem.port = h->port;
c->rem.is_ip6 = false;
if (!c->is_client && c->rem.addr.ip4 != h->ip) {
c->rem.addr.ip4 = h->ip;
mg_connect_resolved(c);
if (c->is_connecting) return;
}
len = mg_io_send(c, h + 1, h->len);
off = sizeof(*h), empty = h->len == 0;
} else
#endif
{
len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
: mg_io_send(c, c->send.buf, c->send.len);
}
// TODO(): mg_tls_send() may return 0 forever on steady OOM // TODO(): mg_tls_send() may return 0 forever on steady OOM
if (len == MG_IO_ERR) { if (len == MG_IO_ERR) {
proto_error(c, false, "tx err"); proto_error(c, false, "tx err");
} else if (len > 0 || empty) { } else if (len > 0) {
mg_iobuf_del(&c->send, 0, off + (size_t) len); mg_iobuf_del(&c->send, 0, (size_t) len);
mg_call(c, MG_EV_WRITE, &len); mg_call(c, MG_EV_WRITE, &len);
} }
} }
-14
View File
@@ -26,17 +26,6 @@ struct mg_tcpip_driver {
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev, typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
void *ev_data); void *ev_data);
#if MG_ENABLE_BSD_SOCKETS
#ifndef MG_TCPIP_SENDTO_CACHE_SIZE
#define MG_TCPIP_SENDTO_CACHE_SIZE 4
#endif
struct mg_tcpip_sendto_entry {
uint32_t ip;
struct mg_l2addr addr;
};
#endif
// clang-format off // clang-format off
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver). // Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
// Each entry shows the ev_data type to cast inside the handler. // Each entry shows the ev_data type to cast inside the handler.
@@ -99,9 +88,6 @@ struct mg_tcpip_if {
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h) enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
union mg_l2data l2data; // Layer-2 config and state (see l2.h) union mg_l2data l2data; // Layer-2 config and state (see l2.h)
char *dns4_url; // Active DNS server URL (set from DHCP or config) char *dns4_url; // Active DNS server URL (set from DHCP or config)
#if MG_ENABLE_BSD_SOCKETS
struct mg_tcpip_sendto_entry sendto[MG_TCPIP_SENDTO_CACHE_SIZE]; // Local IPv4 neighbors
#endif
uint64_t now; // Current time in ms (updated each mg_mgr_poll) uint64_t now; // Current time in ms (updated each mg_mgr_poll)
uint64_t timer_1000ms; // Timestamp of last 1-second tick uint64_t timer_1000ms; // Timestamp of last 1-second tick
uint64_t lease_expire; // DHCP lease expiry time in ms uint64_t lease_expire; // DHCP lease expiry time in ms
+1 -5
View File
@@ -299,11 +299,7 @@ static long recv_raw(struct mg_connection *c, void *buf, size_t len) {
union usa usa; union usa usa;
socklen_t slen = tousa(&c->rem, &usa); socklen_t slen = tousa(&c->rem, &usa);
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen); n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
if (n > 0) { if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
struct mg_addr rem;
tomgaddr(&usa, &rem, slen != sizeof(usa.sin));
c->rem = rem;
}
} else { } else {
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING); n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
} }
+6 -9
View File
@@ -13,7 +13,7 @@ else
C_WARN ?= -Wno-deprecated C_WARN ?= -Wno-deprecated
endif endif
.PHONY: all bsd bsd_test bsd_client bsd_udp bsd_nortos bsd_nortos_test shm_tap_bridge \ .PHONY: all bsd bsd_test bsd_client bsd_nortos bsd_nortos_test shm_tap_bridge \
shm_port_bridge bsd_s390 bsd_nortos_s390 clean shm_port_bridge bsd_s390 bsd_nortos_s390 clean
TEST_SOURCES = bsd_shm_test.c TEST_SOURCES = bsd_shm_test.c
@@ -47,11 +47,11 @@ DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(R
CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
all bsd: shm_tap_bridge bsd_client bsd_udp FreeRTOS-Kernel bsd_test all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS) ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
# also # also
bsd_nortos: shm_tap_bridge bsd_client bsd_udp bsd_nortos_test bsd_nortos: shm_tap_bridge bsd_client bsd_nortos_test
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS) ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
# requires shm_tap_bridge to be running # requires shm_tap_bridge to be running
@@ -70,9 +70,6 @@ bsd_nortos_test:
bsd_client: bsd_client.c bsd_client: bsd_client.c
$(CC) bsd_client.c -pthread -o $@ $(CC) bsd_client.c -pthread -o $@
bsd_udp: bsd_udp.c
$(CC) bsd_udp.c -o $@
# bridge shm queue <--> TAP interface # bridge shm queue <--> TAP interface
shm_tap_bridge: shm_tap_bridge.c shm_queue.h shm_tap_bridge: shm_tap_bridge.c shm_queue.h
$(CC) shm_tap_bridge.c -pthread -o $@ $(CC) shm_tap_bridge.c -pthread -o $@
@@ -82,7 +79,7 @@ bsd_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
bsd_s390: ASAN= bsd_s390: ASAN=
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390 bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
bsd_s390: shm_port_bridge bsd_client bsd_udp FreeRTOS-Kernel bsd_test bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc' $(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
#also #also
@@ -90,14 +87,14 @@ bsd_nortos_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
bsd_nortos_s390: ASAN= bsd_nortos_s390: ASAN=
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390 bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
bsd_nortos_s390: shm_port_bridge bsd_client bsd_udp bsd_nortos_test bsd_nortos_s390: shm_port_bridge bsd_client bsd_nortos_test
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc' $(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
shm_port_bridge: shm_port_bridge.c shm_queue.h shm_port_bridge: shm_port_bridge.c shm_queue.h
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@ $(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
clean: # Cleanup. Delete built program and all build artifacts clean: # Cleanup. Delete built program and all build artifacts
rm -rf bsd_test bsd_client bsd_udp shm_port_bridge *.o *.obj FreeRTOS-Kernel rm -rf bsd_test bsd_client shm_port_bridge *.o *.obj FreeRTOS-Kernel
FreeRTOS-Kernel: # FreeRTOS sources FreeRTOS-Kernel: # FreeRTOS sources
git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@ git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@
-55
View File
@@ -1,55 +0,0 @@
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#define PORT 1235
int main(int argc, char *argv[]) {
struct sockaddr_in sa;
struct timeval timeout;
char buf[8];
ssize_t n;
int fd, rogue;
if (argc != 2) return 1;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(PORT);
if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1) return 1;
timeout.tv_sec = 5, timeout.tv_usec = 0;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0 ||
connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0 ||
send(fd, "hello", 5, 0) != 5 || recv(fd, buf, sizeof(buf), 0) != 5 ||
memcmp(buf, "ready", 5) != 0)
goto fail;
if ((rogue = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
setsockopt(rogue, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) !=
0 ||
send(fd, "one", 3, 0) != 3 ||
sendto(rogue, "two2", 4, 0, (struct sockaddr *) &sa, sizeof(sa)) != 4 ||
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "peer", 4) != 0 ||
recv(rogue, buf, sizeof(buf), 0) != 5 || memcmp(buf, "rogue", 5) != 0 ||
send(fd, "truncate", 8, 0) != 8 || send(fd, "peek", 4, 0) != 4 ||
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "more", 4) != 0 ||
recv(fd, buf, sizeof(buf), 0) != 3 || memcmp(buf, "one", 3) != 0 ||
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "two2", 4) != 0 ||
recv(fd, buf, sizeof(buf), 0) != 0 ||
sendto(rogue, "rogue", 5, 0, (struct sockaddr *) &sa, sizeof(sa)) != 5 ||
recv(fd, buf, sizeof(buf), 0) != 7 || memcmp(buf, "checked", 7) != 0 ||
send(fd, "echo", 4, 0) != 4 || recv(fd, buf, sizeof(buf), 0) != 4 ||
memcmp(buf, "echo", 4) != 0)
goto fail_close_rogue;
close(rogue);
close(fd);
return 0;
fail_close_rogue:
close(rogue);
fail:
close(fd);
return 1;
}
+2 -230
View File
@@ -33,175 +33,6 @@ static int s_abort = 0;
#define DASHBOARD(x) \ #define DASHBOARD(x) \
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true") printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
#define UDP_PORT 1235
#if MG_ENABLE_FREERTOS
static volatile bool s_tx_held, s_tx_waits;
#endif
static int open_udp(void) {
struct sockaddr_in sa;
char buf;
int fd;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(UDP_PORT);
sa.sin_addr.s_addr = INADDR_ANY;
ASSERT((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0);
ASSERT(bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
ASSERT(send(fd, &buf, sizeof(buf), 0) < 0 && errno == EDESTADDRREQ);
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
return fd;
}
static void check_udp_oversize(struct mg_mgr *mgr, int fd,
const struct sockaddr *dst, socklen_t dstlen) {
static char buf[1600];
size_t len = mgr->ifp->mtu + 1;
ASSERT(len <= sizeof(buf));
if (dst == NULL) {
ASSERT(send(fd, buf, len, 0) < 0 && errno == EMSGSIZE);
} else {
ASSERT(sendto(fd, buf, len, 0, dst, dstlen) < 0 && errno == EMSGSIZE);
}
}
static void udp_exchange(struct mg_mgr *mgr, int fd,
void (*progress)(struct mg_mgr *)) {
struct sockaddr_in peer, other, rogue = {0};
socklen_t peerlen, otherlen;
uint64_t deadline;
struct timeval zero = {0, 0};
struct pollfd p;
fd_set r, w, e;
bool one;
char buf[8];
ssize_t n;
for (;;) {
peerlen = sizeof(peer);
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &peer, &peerlen);
if (n >= 0) break;
ASSERT(errno == EAGAIN);
progress(mgr);
}
ASSERT(n == 5 && memcmp(buf, "hello", 5) == 0);
ASSERT(sendto(fd, "ready", 5, 0, (struct sockaddr *) &peer, peerlen) ==
5);
deadline = mg_millis() + 100;
while (mg_millis() < deadline) progress(mgr); // Queue both UDP datagrams
do {
otherlen = sizeof(other);
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &other,
&otherlen);
if (n < 0) {
ASSERT(errno == EAGAIN);
progress(mgr);
}
} while (n < 0);
one = n == 3;
if (one) {
ASSERT(memcmp(buf, "one", 3) == 0 && other.sin_port == peer.sin_port);
} else {
ASSERT(n == 4 && memcmp(buf, "two2", 4) == 0 &&
other.sin_port != peer.sin_port);
}
if (other.sin_port != peer.sin_port) rogue = other;
do {
otherlen = sizeof(other);
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &other,
&otherlen);
if (n < 0) {
ASSERT(errno == EAGAIN);
progress(mgr);
}
} while (n < 0);
if (one) {
ASSERT(n == 4 && memcmp(buf, "two2", 4) == 0 &&
other.sin_port != peer.sin_port);
} else {
ASSERT(n == 3 && memcmp(buf, "one", 3) == 0 &&
other.sin_port == peer.sin_port);
}
if (other.sin_port != peer.sin_port) rogue = other;
check_udp_oversize(mgr, fd, (struct sockaddr *) &peer, peerlen);
ASSERT(sendto(fd, "peer", 4, 0, (struct sockaddr *) &peer, peerlen) == 4);
ASSERT(sendto(fd, "rogue", 5, 0, (struct sockaddr *) &rogue,
sizeof(rogue)) == 5);
deadline = mg_millis() + 100;
while (mg_millis() < deadline) progress(mgr);
ASSERT(connect(fd, (struct sockaddr *) &peer, peerlen) == 0);
ASSERT(sendto(fd, "no", 2, 0, (struct sockaddr *) &peer, peerlen) < 0 &&
errno == EISCONN);
check_udp_oversize(mgr, fd, NULL, 0);
ASSERT(send(fd, "more", 4, 0) == 4);
deadline = mg_millis() + 100;
while (mg_millis() < deadline) progress(mgr); // Queue both UDP datagrams
do {
p.fd = fd, p.events = POLLIN | POLLOUT, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 1);
if (!(p.revents & POLLIN)) progress(mgr);
} while (!(p.revents & POLLIN));
ASSERT(p.revents == (POLLIN | POLLOUT));
FD_ZERO(&r);
FD_ZERO(&w);
FD_ZERO(&e);
FD_SET(fd, &r);
FD_SET(fd, &w);
FD_SET(fd, &e);
ASSERT(select(fd + 1, &r, &w, &e, &zero) == 1);
ASSERT(FD_ISSET(fd, &r) && FD_ISSET(fd, &w) && !FD_ISSET(fd, &e));
do {
n = recv(fd, buf, 3, 0);
if (n < 0) {
ASSERT(errno == EAGAIN);
progress(mgr);
}
} while (n < 0);
ASSERT(n == 3 && memcmp(buf, "tru", 3) == 0);
do {
n = recv(fd, buf, sizeof(buf), MSG_PEEK);
if (n < 0) {
ASSERT(errno == EAGAIN);
progress(mgr);
}
} while (n < 0);
ASSERT(n == 4 && memcmp(buf, "peek", 4) == 0);
ASSERT(recv(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "peek", 4) == 0);
p.events = POLLIN | POLLOUT, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 1 && p.revents == POLLOUT);
#if MG_ENABLE_FREERTOS
s_tx_waits = false, s_tx_held = true;
#endif
ASSERT(send(fd, "one", 3, 0) == 3);
ASSERT(send(fd, "two2", 4, 0) == 4);
ASSERT(send(fd, "", 0, 0) == 0);
#if MG_ENABLE_FREERTOS
while (!s_tx_waits) progress(mgr); // MG_IO_WAIT keeps all records queued
s_tx_held = false;
#endif
deadline = mg_millis() + 100;
while (mg_millis() < deadline) progress(mgr); // Let rogue arrive
ASSERT(recv(fd, buf, sizeof(buf), MSG_DONTWAIT) < 0 && errno == EAGAIN);
while ((n = send(fd, "checked", 7, 0)) < 0) {
ASSERT(errno == EAGAIN);
progress(mgr);
}
ASSERT(n == 7);
for (;;) {
n = recv(fd, buf, sizeof(buf), 0);
if (n >= 0) break;
ASSERT(errno == EAGAIN);
progress(mgr);
}
ASSERT(n == 4 && memcmp(buf, "echo", 4) == 0);
ASSERT(send(fd, "echo", 4, 0) == 4);
progress(mgr); // Drain the last queued datagram before close
ASSERT(close(fd) == 0);
}
static pid_t start_client(const char *ip, const char *connections) { static pid_t start_client(const char *ip, const char *connections) {
char *cmd[4]; char *cmd[4];
pid_t pid; pid_t pid;
@@ -215,18 +46,6 @@ static pid_t start_client(const char *ip, const char *connections) {
return pid; return pid;
} }
static pid_t start_udp(const char *ip) {
char *cmd[3];
pid_t pid;
extern char **environ;
cmd[0] = (char *) "./bsd_udp";
cmd[1] = (char *) ip;
cmd[2] = NULL;
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
return pid;
}
static bool client_done(pid_t pid, int *status) { static bool client_done(pid_t pid, int *status) {
pid_t r = waitpid(pid, status, WNOHANG); pid_t r = waitpid(pid, status, WNOHANG);
ASSERT(r >= 0); ASSERT(r >= 0);
@@ -241,17 +60,12 @@ static void check_client(int status) {
static int open_listener(int backlog, bool nonblock) { static int open_listener(int backlog, bool nonblock) {
int lfd = socket(AF_INET, SOCK_STREAM, 0); int lfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sa; struct sockaddr_in sa;
char buf;
memset(&sa, 0, sizeof(sa)); memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET; sa.sin_family = AF_INET;
sa.sin_port = htons(1234); sa.sin_port = htons(1234);
sa.sin_addr.s_addr = INADDR_ANY; sa.sin_addr.s_addr = INADDR_ANY;
ASSERT(lfd >= 0); ASSERT(lfd >= 0);
ASSERT(sendto(lfd, &buf, sizeof(buf), 0, (struct sockaddr *) &sa,
sizeof(sa)) < 0 && errno == EOPNOTSUPP);
ASSERT(recvfrom(lfd, &buf, sizeof(buf), 0, NULL, NULL) < 0 &&
errno == EOPNOTSUPP);
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0); ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
ASSERT(listen(lfd, backlog) == 0); ASSERT(listen(lfd, backlog) == 0);
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0); if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
@@ -316,12 +130,11 @@ static volatile bool s_select_waiting, s_select_done;
static volatile bool s_poll_waiting, s_poll_done; static volatile bool s_poll_waiting, s_poll_done;
static volatile bool s_ready_waiting, s_ready_done; static volatile bool s_ready_waiting, s_ready_done;
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept; static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
static volatile bool s_tx_fail, s_tx_error; static volatile bool s_tx_held, s_tx_waits, s_tx_fail, s_tx_error;
static volatile bool s_send_waiting; static volatile bool s_send_waiting;
static volatile bool s_send_done, s_send_expect_error; static volatile bool s_send_done, s_send_expect_error;
static volatile bool s_recv_start, s_recv_waiting, s_recv_done; static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
static volatile bool s_recv_expect_error; static volatile bool s_recv_expect_error;
static volatile bool s_udp_ready, s_udp_done;
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *); static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
struct client_data { struct client_data {
@@ -339,7 +152,6 @@ static void select_task(void *args);
static void readiness_task(void *args); static void readiness_task(void *args);
static void backlog_task(void *args); static void backlog_task(void *args);
static void tx_gate_task(void *args); static void tx_gate_task(void *args);
static void udp_task(void *args);
struct atask_args { struct atask_args {
bool listener_nonblock; bool listener_nonblock;
@@ -494,21 +306,6 @@ static void tx_gate_task(void *args) {
(void) args; (void) args;
} }
static void udp_delay(struct mg_mgr *mgr) {
(void) mgr;
vTaskDelay(1);
}
static void udp_task(void *args) {
struct mg_mgr *mgr = (struct mg_mgr *) args;
int fd = open_udp();
s_udp_ready = true;
udp_exchange(mgr, fd, udp_delay);
s_udp_done = true;
vTaskDelete(NULL);
}
static void recv_wait_task(void *args) { static void recv_wait_task(void *args) {
char buf; char buf;
int fd = (int) (uintptr_t) args; int fd = (int) (uintptr_t) args;
@@ -722,22 +519,12 @@ static void task2(void *args) {
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
char *ip; char *ip;
int status; int status;
pid_t first, second, udp; pid_t first, second;
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
s_error = false; s_error = false;
s_wait = false; s_wait = false;
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip); ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
s_udp_ready = s_udp_done = false;
ASSERT(xTaskCreate(udp_task, "udp", 512, mgr, configMAX_PRIORITIES - 1,
NULL) == pdPASS);
while (!s_udp_ready) vTaskDelay(1);
udp = start_udp(ip);
while (!client_done(udp, &status)) vTaskDelay(1);
check_client(status);
while (!s_udp_done) vTaskDelay(1);
DASHBOARD("udp");
s_poll_waiting = s_poll_done = false; s_poll_waiting = s_poll_done = false;
ASSERT(xTaskCreate(poll_task, "poll", 512, NULL, ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
configMAX_PRIORITIES - 1, NULL) == pdPASS); configMAX_PRIORITIES - 1, NULL) == pdPASS);
@@ -1065,20 +852,6 @@ static void nortos_echo(struct mg_mgr *mgr, struct nortos_echo_client *clients,
nortos_wait_client(mgr, pid); nortos_wait_client(mgr, pid);
} }
static void udp_poll(struct mg_mgr *mgr) {
mg_mgr_poll(mgr, 0);
}
static void nortos_udp(struct mg_mgr *mgr, const char *ip) {
pid_t pid;
int fd = open_udp();
pid = start_udp(ip);
udp_exchange(mgr, fd, udp_poll);
nortos_wait_client(mgr, pid);
DASHBOARD("nortos_udp");
}
static void nortos_test(struct mg_mgr *mgr) { static void nortos_test(struct mg_mgr *mgr) {
static const char bootstrap = '\0'; static const char bootstrap = '\0';
static const char grow[512] = {0}; static const char grow[512] = {0};
@@ -1180,7 +953,6 @@ static void nortos_test(struct mg_mgr *mgr) {
ASSERT(close(fd) == 0); ASSERT(close(fd) == 0);
mg_mgr_poll(mgr, 0); mg_mgr_poll(mgr, 0);
DASHBOARD("nortos_nonblocking_connect"); DASHBOARD("nortos_nonblocking_connect");
nortos_udp(mgr, ip);
mg_free(ip); mg_free(ip);
} }
+4
View File
@@ -80,6 +80,10 @@ static void test_statechange(void) {
onstatechange(&iface); onstatechange(&iface);
ASSERT(executed == true); ASSERT(executed == true);
executed = false; executed = false;
iface.enable_dhcp_client = true; // DHCP REQUEST unanswered: start over
iface.state = MG_TCPIP_STATE_REQ, iface.lease_expire = 0;
mg_ip_poll(&iface, true);
ASSERT(iface.state == MG_TCPIP_STATE_UP);
} }
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
static void mif6_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) { static void mif6_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
@@ -6,6 +6,7 @@
#include "mongoose.h" #include "mongoose.h"
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
struct echo_server { struct echo_server {
@@ -42,12 +43,12 @@ int echo_init(uint16_t port) {
if (fd < 0 || !echo_nonblock(fd) || if (fd < 0 || !echo_nonblock(fd) ||
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 || listen(fd, 3) < 0) { bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 || listen(fd, 3) < 0) {
MG_ERROR(("BSD echo init: %d", errno)); printf("BSD echo init: %d\n", errno);
if (fd >= 0) close(fd); if (fd >= 0) close(fd);
return 0; return 0;
} }
s_server.sock = fd; s_server.sock = fd;
MG_INFO(("BSD echo server on port %hu", port)); printf("BSD echo server on port %hu\n", port);
return 1; return 1;
} }
@@ -79,7 +80,7 @@ void echo_poll(void) {
if (fd >= 0) { if (fd >= 0) {
c = (struct echo_client *) calloc(1, sizeof(*c)); c = (struct echo_client *) calloc(1, sizeof(*c));
if (c == NULL || !echo_nonblock(fd)) { if (c == NULL || !echo_nonblock(fd)) {
MG_ERROR(("BSD echo accept: %d", c == NULL ? ENOMEM : errno)); printf("BSD echo accept: %d\n", c == NULL ? ENOMEM : errno);
close(fd); close(fd);
free(c); free(c);
} else { } else {
@@ -1,36 +0,0 @@
# Copyright (c) 2026 Cesanta Software Limited
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
MGDIR = ../../../..
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include
CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c hal.c udp.c
SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h723xx.s
SOURCES += $(MGDIR)/mongoose.c
all build example: firmware.bin
firmware.elf: cmsis_core cmsis_h7 hal.h link.ld Makefile $(SOURCES) $(MGDIR)/mongoose.h mongoose_config.h
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
@echo
@echo "To flash, run 'make flash', or use STM32CubeProgrammer"
flash: firmware.bin
STM32_Programmer_CLI -c port=swd -w firmware.elf -hardRst
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
cmsis_h7:
git clone -q -c advice.detachedHead=false --depth 1 -b v1.10.6 https://github.com/STMicroelectronics/cmsis_device_h7 $@
clean:
rm -rf firmware.* cmsis_*
@@ -1,125 +0,0 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
if (period == 0) return false; // Avoid division by zero
if (diff < period) return false; // Period has not elapsed yet
*t += (diff / period) * period; // Preserve cadence, skip missed periods
return true;
}
static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
uint64_t hal_get_tick(void) {
return s_ticks;
};
uint32_t SystemCoreClock = 160000000;
void SystemInit(void) { // Called automatically by startup code
hal_system_init(); // Enable FPU
hal_clock_init();
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
void ExitRun0Mode(void) {
}
struct stat;
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
(void) fd, (void) st;
return -1;
}
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
static unsigned char *s_current_heap_end = _end;
size_t hal_ram_used(void) {
return (size_t) (s_current_heap_end - _end);
}
size_t hal_ram_free(void) {
unsigned char endofstack;
return (size_t) (&endofstack - s_current_heap_end);
}
void *_sbrk(int incr) {
unsigned char *prev_heap;
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
prev_heap = s_current_heap_end;
// Check how much space we got from the heap end to the stack end
if (s_current_heap_end + incr > heap_end) return (void *) -1;
s_current_heap_end += incr;
return prev_heap;
}
__attribute__((weak)) int _open(const char *path) {
(void) path;
return -1;
}
__attribute__((weak)) int _close(int fd) {
(void) fd;
return -1;
}
__attribute__((weak)) int _isatty(int fd) {
(void) fd;
return 1;
}
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
__attribute__((weak)) void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
__attribute__((weak)) void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
__attribute__((weak)) int _getpid(void) {
return -1;
}
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
__attribute__((weak)) int _unlink(const char *a) {
(void) a;
return -1;
}
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
__attribute__((weak)) int mkdir(const char *path, int mode) {
(void) path, (void) mode;
return -1;
}
__attribute__((weak)) void _init(void) {
}
@@ -1,225 +0,0 @@
// Copyright (c) 2022-2026 Cesanta Software Limited
// Datasheet: RM0468, devboard manual: UM2407
// https://www.st.com/resource/en/reference_manual/rm0468-stm32h723733-stm32h725735-and-stm32h730-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
// Alternate functions: https://www.st.com/resource/en/datasheet/stm32h723.pdf
#pragma once
#include <stm32h723xx.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now);
extern uint64_t hal_get_tick(void);
extern size_t hal_ram_free(void);
extern size_t hal_ram_used(void);
extern uint32_t SystemCoreClock;
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
#define HAL_ETH_PINS PIN('A', 1), PIN('A', 2), PIN('A', 7), \
PIN('B', 13), PIN('C', 1), PIN('C', 4), \
PIN('C', 5), PIN('G', 11), PIN('G', 13)
// System clock (2.1, Figure 1; 8.5, Figure 45; 8.5.5, Figure 47; 8.5.6, Figure
// 49; 8.5.8 Table 56; datasheet) CPU_FREQUENCY <= 550 MHz; hclk = CPU_FREQUENCY
// / HAL_HPRE ; hclk <= 275 MHz; APB clocks <= 137.5 MHz. D1 domain bus matrix (and
// so flash) runs at hclk frequency. Configure flash latency (WS) in accordance
// to hclk freq (4.3.8, Table 16) The Ethernet controller is in D2 domain and
// runs at hclk frequency
enum {
HAL_D1CPRE = 1, // actual divisor value
HAL_HPRE = 2, // actual divisor value
HAL_D1PPRE = 4, // register values, divisor value = BIT(value - 3) = / 2
HAL_D2PPRE1 = 4,
HAL_D2PPRE2 = 4,
HAL_D3PPRE = 4
};
// PLL1_P: odd division factors are not allowed (8.7.12) (except for '1')
enum { PLL1_HSI = 64, PLL1_M = 32, PLL1_N = 225, PLL1_P = 1 };
#define HAL_FLASH_LATENCY 0x33 // WRHIGHFREQ LATENCY
#define SYS_FREQUENCY ((PLL1_HSI * PLL1_N / PLL1_M / PLL1_P / HAL_D1CPRE) * 1000000)
#define AHB_FREQUENCY (SYS_FREQUENCY / HAL_HPRE)
#define APB2_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE2 - 3)))
#define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE1 - 3)))
enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_MODE_AF, HAL_GPIO_MODE_ANALOG };
enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN };
enum { HAL_GPIO_SPEED_LOW, HAL_GPIO_SPEED_MEDIUM, HAL_GPIO_SPEED_HIGH, HAL_GPIO_SPEED_INSANE };
enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN };
#define HAL_GPIO(N) ((GPIO_TypeDef *) (0x40000000 + 0x18020000UL + 0x400 * (N)))
static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) {
return HAL_GPIO(PINBANK(pin));
}
static inline void hal_gpio_toggle(uint16_t pin) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
}
static inline int hal_gpio_read(uint16_t pin) {
return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
}
static inline void hal_gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
}
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint8_t n = (uint8_t) (PINNO(pin));
RCC->AHB4ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
((uint32_t) af) << ((n & 7) * 4));
CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
}
static inline void hal_gpio_input(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
HAL_GPIO_PULL_NONE, 0);
}
static inline void hal_gpio_output(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
HAL_GPIO_PULL_NONE, 0);
}
// D2 Kernel clock (8.7.21) USART1 defaults to pclk2 (APB2), while USART2,3
// default to pclk1 (APB1). Even if using other kernel clocks, the APBx clocks
// must be enabled for CPU access, as the kernel clock drives the BRR, not the
// APB bus interface
static inline void hal_uart_init(USART_TypeDef *uart, uint16_t tx,
uint16_t rx, unsigned long baud) {
uint8_t af = 7; // Alternate function
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1
if (uart == USART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(4);
if (uart == USART2) freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(17);
if (uart == USART3) freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(18);
#if 0 // CONSTANT BAUD RATE FOR REMOTE DEBUGGING WHILE SETTING THE PLL
CLRSET(RCC->D2CCIP2R, 7 << 3, 3 << 3); // use HSI for UART1
freq = 64000000;
#endif
hal_gpio_init(tx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
hal_gpio_init(rx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
uart->CR1 = 0; // Disable this UART
uart->BRR = freq / baud; // Set baud rate
uart->CR1 = BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE
}
static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->TDR = byte;
while ((uart->ISR & USART_ISR_TXE_TXFNF) == 0) (void) 0;
}
static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
while (len-- > 0) hal_uart_write_byte(uart, *(uint8_t *) buf++);
}
static inline int hal_uart_read_ready(USART_TypeDef *uart) {
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->RDR & 255);
}
// RNG clock (rng_clk) defaults to HSI48 and is characterized at this frequency.
// Enable at SystemInit. (STM32CubeMX) It won't work with a 275MHz (AHB)
static inline void hal_rng_init(void) {
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG AHB clock (hal_rng_hclk)
// 39.6.2 Table 320 init
RNG->CR = RNG_CR_CONDRST | (0x0F << RNG_CR_RNG_CONFIG1_Pos) |
(0 << RNG_CR_RNG_CONFIG2_Pos) | (0xD << RNG_CR_RNG_CONFIG3_Pos);
RNG->HTCR = 0x17590abc;
RNG->HTCR = 0xaa74;
RNG->CR &= ~RNG_CR_CONDRST ;
while (RNG->CR & RNG_CR_CONDRST) (void) 0; // 39.7.1
RNG->CR |= RNG_CR_RNGEN; // Enable RNG
}
static inline uint32_t hal_rng_read(void) {
while ((RNG->SR & RNG_SR_DRDY) == 0) (void) 0;
return RNG->DR;
}
// Hw pull-ups on PHY RXD0,1,DV to enable autonegotiation
static inline void hal_ethernet_init(void) {
// Initialise Ethernet. Enable MAC GPIO pins, see
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
uint16_t pins[] = {HAL_ETH_PINS};
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
hal_gpio_init(pins[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_INSANE,
HAL_GPIO_PULL_NONE, 11); // 11 is the Ethernet function
}
NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler
CLRSET(SYSCFG->PMCR, 7 << 21, 4 << 21); // Use RMII (12.3.1)
RCC->AHB1ENR |= BIT(15) | BIT(16) | BIT(17); // Enable Ethernet clocks
}
static inline unsigned int hal_div2prescval(unsigned int div) {
// 0 --> /1; 8 --> /2 ... 11 --> /16; 12 --> /64 ... 15 --> /512
if (div == 1) return 0;
if (div > 16) div /= 2;
unsigned int val = 7;
while (div >>= 1) ++val;
return val;
}
static inline unsigned int hal_pllrge(unsigned int f) {
unsigned int val = 0;
while (f >>= 1) ++val;
return val - 1;
}
// Enable RTC/BKP APB clock and backup domain write access.
// Must be called before accessing RTC backup registers or IWDG.
static inline void hal_backup_domain_init(void) {
RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN; // enable RTC/BKP APB clock
PWR->CR1 |= PWR_CR1_DBP; // enable backup domain write access
}
static inline void hal_clock_init(void) {
hal_backup_domain_init();
PWR->CR3 |= PWR_CR3_LDOEN; // select LDO (reset value)
while ((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0) (void) 0; // ACTVOSRDY
PWR->D3CR &= ~PWR_D3CR_VOS_Msk; // Select VOS0
while ((PWR->D3CR & PWR_D3CR_VOSRDY) == 0) (void) 0; // VOSRDY
CLRSET(
RCC->D1CFGR, (0x0F << 8) | (7 << 4) | (0x0F << 0),
(hal_div2prescval(HAL_D1CPRE) << 8) | (HAL_D1PPRE << 4) | (hal_div2prescval(HAL_HPRE) << 0));
RCC->D2CFGR = (HAL_D2PPRE2 << 8) | (HAL_D2PPRE1 << 4);
RCC->D3CFGR = (HAL_D3PPRE << 4);
CLRSET(RCC->PLLCFGR, 3 << 2,
hal_pllrge(PLL1_HSI / PLL1_M)
<< 2); // keep reset config (DIVP1EN, !PLL1VCOSEL), PLL1RGE
CLRSET(RCC->PLL1DIVR, (0x7F << 9) | (0x1FF << 0),
((PLL1_P - 1) << 9) | ((PLL1_N - 1) << 0)); // Set PLL1_P PLL1_N
CLRSET(RCC->PLLCKSELR, 0x3F << 4,
PLL1_M << 4); // Set PLL1_M (source defaults to HSI)
RCC->CR |= RCC_CR_PLL1ON | RCC_CR_HSI48ON; // Enable PLL1 and HSI48 (for RNG)
while ((RCC->CR & RCC_CR_PLL1RDY) == 0) (void) 0; // Wait until done
RCC->CFGR |= (3 << 0); // Set clock source to PLL1
while ((RCC->CFGR & (7 << 3)) != (3 << 3)) (void) 0; // Wait until done
FLASH->ACR = HAL_FLASH_LATENCY; // default is larger
RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; // Enable SYSCFG
while ((RCC->CR & RCC_CR_HSI48RDY) == 0) (void) 0; // Make sure HSI48 is ready
SystemCoreClock = SYS_FREQUENCY; // Update SystemCoreClock global var
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
static inline void hal_system_init(void) {
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
__DSB();
__ISB();
}
@@ -1,32 +0,0 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 1024k
dtcmram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
ram_d1 (rwx) : ORIGIN = 0x24000000, LENGTH = 320K
ram_d2 (rwx) : ORIGIN = 0x30000000, LENGTH = 32K
ram_d3 (rwx) : ORIGIN = 0x38000000, LENGTH = 16K
itcmram (rwx) : ORIGIN = 0x00000000, LENGTH = 64K
}
_estack = ORIGIN(ram_d1) + LENGTH(ram_d1);
SECTIONS {
.vectors : { KEEP(*(.isr_vector)) } > flash
.text : { *(.text* .text.*) } > flash
.rodata : { *(.rodata*) } > flash
/* Note the .iram section, for functions copied to RAM. Required for MG_IRAM OTA support */
.data : { _sdata = .; *(.first_data) *(.iram .iram* .iram.*) *(.data SORT(.data.*)) _edata = .; } > ram_d1 AT > flash
_sidata = LOADADDR(.data);
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > ram_d1
.eth_ram : { *(.eth_ram .eth_ram*) } > ram_d1 AT > flash
/* Health record. NOLOAD and in unused DTCM, so startup code neither copies
nor zeroes it and the contents survive a warm reset */
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > dtcmram
. = ALIGN(8);
_end = .;
}
@@ -1,69 +0,0 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
#include "mongoose.h"
int udp_init(uint16_t port);
void udp_poll(void);
#ifndef UART_DEBUG
#define UART_DEBUG USART3
#define UART_DEBUG_TX_PIN PIN('D', 8)
#define UART_DEBUG_RX_PIN PIN('D', 9)
#else
#define UART_DEBUG_TX_PIN PIN('A', 9)
#define UART_DEBUG_RX_PIN PIN('A', 10)
#endif
#define LED1 PIN('B', 0)
#define LED2 PIN('E', 1)
#define LED3 PIN('B', 14)
static void log_fn(char ch, void *param) {
hal_uart_write_buf(param, &ch, 1);
}
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
hal_gpio_toggle(LED2);
}
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
bool mg_random(void *buf, size_t len) {
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = hal_rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
return true;
}
int main(void) {
struct mg_mgr mgr;
hal_clock_init();
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
hal_rng_init();
hal_ethernet_init();
hal_gpio_output(LED1);
hal_gpio_output(LED2);
hal_gpio_output(LED3);
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
mg_mgr_init(&mgr);
udp_init(1234);
for (;;) {
mg_mgr_poll(&mgr, 0);
udp_poll();
blink_task();
}
return 0;
}
@@ -1,37 +0,0 @@
#pragma once
#include "hal.h"
// See https://mongoose.ws/docs/getting-started/build-options/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_TLS MG_TLS_BUILTIN
#define MG_OTA MG_OTA_STM32H7
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_BSD_SOCKETS 1
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_ENABLE_DRIVER_STM32H 1
#define MG_ENABLE_PACKED_FS 1
#define MG_ETH_RAM __attribute__((section(".eth_ram")))
// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 5
// #define MG_TCPIP_PHY_ADDR 0 // PHY address
// For static IP configuration, define MG_TCPIP_{IP,MASK,GW}
// By default, those are set to zero, meaning that DHCP is used
//
// #define MG_TCPIP_IP MG_IPV4(192, 168, 0, 10) // IP
// #define MG_TCPIP_GW MG_IPV4(192, 168, 0, 1) // Gateway
// #define MG_TCPIP_MASK MG_IPV4(255, 255, 255, 0) // Netmask
// Construct MAC address from the MCU unique ID
#define MGUID ((uint32_t *) UID_BASE)
#define MG_SET_MAC_ADDRESS(mac) \
do { \
mac[0] = 2; \
mac[1] = MGUID[0] & 255; \
mac[2] = (MGUID[0] >> 10) & 255; \
mac[3] = (MGUID[0] >> 19) & 255; \
mac[4] = MGUID[1] & 255; \
mac[5] = MGUID[2] & 255; \
} while (0)
@@ -1,68 +0,0 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
struct udp_server {
int fd;
bool pending;
struct sockaddr_in peer;
socklen_t peerlen;
size_t len;
char buf[128];
};
static struct udp_server s_server = {.fd = -1};
static int udp_nonblock(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0;
}
int udp_init(uint16_t port) {
struct sockaddr_in sa = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr = {.s_addr = INADDR_ANY},
};
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0 || !udp_nonblock(fd) ||
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
MG_ERROR(("BSD UDP init: %d", errno));
if (fd >= 0) close(fd);
return 0;
}
s_server.fd = fd;
MG_INFO(("BSD UDP echo server on port %hu", port));
return 1;
}
void udp_poll(void) {
struct udp_server *s = &s_server;
struct pollfd p;
ssize_t n;
if (s->fd < 0) return;
p.fd = s->fd, p.events = s->pending ? POLLOUT : POLLIN, p.revents = 0;
if (poll(&p, 1, 0) <= 0) return;
if (s->pending) {
n = sendto(s->fd, s->buf, s->len, 0, (struct sockaddr *) &s->peer,
s->peerlen);
if (n == (ssize_t) s->len) {
s->pending = false;
} else if (n < 0 && errno != EAGAIN) {
MG_ERROR(("BSD UDP send: %d", errno));
s->pending = false;
}
} else {
s->peerlen = sizeof(s->peer);
n = recvfrom(s->fd, s->buf, sizeof(s->buf), 0,
(struct sockaddr *) &s->peer, &s->peerlen);
if (n >= 0) {
s->len = (size_t) n, s->pending = true;
} else if (errno != EAGAIN) {
MG_ERROR(("BSD UDP recv: %d", errno));
}
}
}