[PATCH 1/2] mongoose: Update to version 7.20

29 views
Skip to first unread message

Stefano Babic

unread,
Nov 25, 2025, 11:05:02 AM (7 days ago) Nov 25
to swup...@googlegroups.com, Stefano Babic
mongoose rev-id: 236724ea2948396b4f83b68c366a169e795aa7ef

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
mongoose/mongoose.c | 2265 +++++++++++++++++++++++++++++++------------
mongoose/mongoose.h | 313 ++++--
2 files changed, 1870 insertions(+), 708 deletions(-)

diff --git a/mongoose/mongoose.c b/mongoose/mongoose.c
index 82ab5a1f..2e7696c1 100644
--- a/mongoose/mongoose.c
+++ b/mongoose/mongoose.c
@@ -15,7 +15,7 @@
// Alternatively, you can license this software under a commercial
// license, as set out in https://www.mongoose.ws/licensing/
//
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0-only or commercial

#include "mongoose.h"

@@ -1793,14 +1793,14 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt,
va_list *ap) {
size_t len = c->send.len;
- mg_send(c, " \r\n", 10);
+ if (!mg_send(c, " \r\n", 10)) mg_error(c, "OOM");
mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
if (c->send.len >= len + 10) {
mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10);
c->send.buf[len + 8] = '\r';
if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker
}
- mg_send(c, "\r\n", 2);
+ if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
}

void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
@@ -1812,8 +1812,7 @@ void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {

void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
mg_printf(c, "%lx\r\n", (unsigned long) len);
- mg_send(c, buf, len);
- mg_send(c, "\r\n", 2);
+ if (!mg_send(c, buf, len) || !mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
if (len == 0) c->is_resp = 0;
}

@@ -2590,8 +2589,8 @@ static size_t roundup(size_t size, size_t align) {
return align == 0 ? size : (size + align - 1) / align * align;
}

-int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
- int ok = 1;
+bool mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
+ bool ok = true;
new_size = roundup(new_size, io->align);
if (new_size == 0) {
mg_bzero(io->buf, io->size);
@@ -2608,15 +2607,16 @@ int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
mg_free(io->buf);
io->buf = (unsigned char *) p;
io->size = new_size;
+ io->len = len;
} else {
- ok = 0;
+ ok = false;
MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size));
}
}
return ok;
}

-int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
+bool mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
io->buf = NULL;
io->align = align;
io->size = io->len = 0;
@@ -3352,8 +3352,8 @@ static const struct mg_mqtt_pmap s_prop_map[] = {
{MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE},
{MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}};

-void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags,
- uint32_t len) {
+static bool mqtt_send_header(struct mg_connection *c, uint8_t cmd,
+ uint8_t flags, uint32_t len) {
uint8_t buf[1 + sizeof(len)], *vlen = &buf[1];
buf[0] = (uint8_t) ((cmd << 4) | flags);
do {
@@ -3362,15 +3362,20 @@ void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags,
if (len > 0) *vlen |= 0x80;
vlen++;
} while (len > 0 && vlen < &buf[sizeof(buf)]);
- mg_send(c, buf, (size_t) (vlen - buf));
+ return mg_send(c, buf, (size_t) (vlen - buf));
+}
+
+void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags,
+ uint32_t len) {
+ if (!mqtt_send_header(c, cmd, flags, len)) mg_error(c, "OOM");
}

-static void mg_send_u16(struct mg_connection *c, uint16_t value) {
- mg_send(c, &value, sizeof(value));
+static bool mg_send_u16(struct mg_connection *c, uint16_t value) {
+ return mg_send(c, &value, sizeof(value));
}

-static void mg_send_u32(struct mg_connection *c, uint32_t value) {
- mg_send(c, &value, sizeof(value));
+static bool mg_send_u32(struct mg_connection *c, uint32_t value) {
+ return mg_send(c, &value, sizeof(value));
}

static uint8_t varint_size(size_t length) {
@@ -3463,46 +3468,50 @@ static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) {
return size;
}

-static void mg_send_mqtt_properties(struct mg_connection *c,
+static bool mg_send_mqtt_properties(struct mg_connection *c,
struct mg_mqtt_prop *props, size_t nprops) {
size_t total_size = get_properties_length(props, nprops);
uint8_t buf_v[4] = {0, 0, 0, 0};
uint8_t buf[4] = {0, 0, 0, 0};
size_t i, len = encode_varint(buf, total_size);

- mg_send(c, buf, (size_t) len);
+ if (!mg_send(c, buf, (size_t) len)) return false;
for (i = 0; i < nprops; i++) {
- mg_send(c, &props[i].id, sizeof(props[i].id));
+ if (!mg_send(c, &props[i].id, sizeof(props[i].id))) return false;
switch (mqtt_prop_type_by_id(props[i].id)) {
case MQTT_PROP_TYPE_STRING_PAIR:
- mg_send_u16(c, mg_htons((uint16_t) props[i].key.len));
- mg_send(c, props[i].key.buf, props[i].key.len);
- mg_send_u16(c, mg_htons((uint16_t) props[i].val.len));
- mg_send(c, props[i].val.buf, props[i].val.len);
+ if (!mg_send_u16(c, mg_htons((uint16_t) props[i].key.len)) ||
+ !mg_send(c, props[i].key.buf, props[i].key.len) ||
+ !mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) ||
+ !mg_send(c, props[i].val.buf, props[i].val.len))
+ return false;
break;
case MQTT_PROP_TYPE_BYTE:
- mg_send(c, &props[i].iv, sizeof(uint8_t));
+ if (!mg_send(c, &props[i].iv, sizeof(uint8_t))) return false;
break;
case MQTT_PROP_TYPE_SHORT:
- mg_send_u16(c, mg_htons((uint16_t) props[i].iv));
+ if (!mg_send_u16(c, mg_htons((uint16_t) props[i].iv))) return false;
break;
case MQTT_PROP_TYPE_INT:
- mg_send_u32(c, mg_htonl((uint32_t) props[i].iv));
+ if (!mg_send_u32(c, mg_htonl((uint32_t) props[i].iv))) return false;
break;
case MQTT_PROP_TYPE_STRING:
- mg_send_u16(c, mg_htons((uint16_t) props[i].val.len));
- mg_send(c, props[i].val.buf, props[i].val.len);
+ if (!mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) ||
+ !mg_send(c, props[i].val.buf, props[i].val.len))
+ return false;
break;
case MQTT_PROP_TYPE_BINARY_DATA:
- mg_send_u16(c, mg_htons((uint16_t) props[i].val.len));
- mg_send(c, props[i].val.buf, props[i].val.len);
+ if (!mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) ||
+ !mg_send(c, props[i].val.buf, props[i].val.len))
+ return false;
break;
case MQTT_PROP_TYPE_VARIABLE_INT:
len = encode_varint(buf_v, props[i].iv);
- mg_send(c, buf_v, (size_t) len);
+ if (!mg_send(c, buf_v, (size_t) len)) return false;
break;
}
}
+ return true;
}

size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
@@ -3595,33 +3604,41 @@ void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
total_len += get_props_size(opts->will_props, opts->num_will_props);
}

- mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len);
- mg_send(c, hdr, sizeof(hdr));
// keepalive == 0 means "do not disconnect us!"
- mg_send_u16(c, mg_htons((uint16_t) opts->keepalive));
+ if (!mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len) ||
+ !mg_send(c, hdr, sizeof(hdr)) ||
+ !mg_send_u16(c, mg_htons((uint16_t) opts->keepalive)))
+ goto fail;

- if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+ if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props))
+ goto fail;

- mg_send_u16(c, mg_htons((uint16_t) cid.len));
- mg_send(c, cid.buf, cid.len);
+ if (!mg_send_u16(c, mg_htons((uint16_t) cid.len)) ||
+ !mg_send(c, cid.buf, cid.len))
+ goto fail;

if (hdr[7] & MQTT_HAS_WILL) {
- if (c->is_mqtt5)
- mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props);
+ if (c->is_mqtt5 &&
+ !mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props))
+ goto fail;

- mg_send_u16(c, mg_htons((uint16_t) opts->topic.len));
- mg_send(c, opts->topic.buf, opts->topic.len);
- mg_send_u16(c, mg_htons((uint16_t) opts->message.len));
- mg_send(c, opts->message.buf, opts->message.len);
- }
- if (opts->user.len > 0) {
- mg_send_u16(c, mg_htons((uint16_t) opts->user.len));
- mg_send(c, opts->user.buf, opts->user.len);
- }
- if (opts->pass.len > 0) {
- mg_send_u16(c, mg_htons((uint16_t) opts->pass.len));
- mg_send(c, opts->pass.buf, opts->pass.len);
+ if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) ||
+ !mg_send(c, opts->topic.buf, opts->topic.len) ||
+ !mg_send_u16(c, mg_htons((uint16_t) opts->message.len)) ||
+ !mg_send(c, opts->message.buf, opts->message.len))
+ goto fail;
}
+ if (opts->user.len > 0 &&
+ (!mg_send_u16(c, mg_htons((uint16_t) opts->user.len)) ||
+ !mg_send(c, opts->user.buf, opts->user.len)))
+ goto fail;
+ if (opts->pass.len > 0 &&
+ (!mg_send_u16(c, mg_htons((uint16_t) opts->pass.len)) ||
+ !mg_send(c, opts->pass.buf, opts->pass.len)))
+ goto fail;
+ return;
+fail:
+ mg_error(c, "OOM");
}

uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
@@ -3636,20 +3653,28 @@ uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
if (c->is_mqtt5) len += get_props_size(opts->props, opts->num_props);

if (opts->qos > 0 && id != 0) flags |= 1 << 3;
- mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len);
- mg_send_u16(c, mg_htons((uint16_t) opts->topic.len));
- mg_send(c, opts->topic.buf, opts->topic.len);
+ if (!mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len) ||
+ !mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) ||
+ !mg_send(c, opts->topic.buf, opts->topic.len))
+ goto fail;
if (opts->qos > 0) { // need to send 'id' field
if (id == 0) { // generate new one if not resending
if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
id = c->mgr->mqtt_id;
}
- mg_send_u16(c, mg_htons(id));
+ if (!mg_send_u16(c, mg_htons(id))) goto fail;
}

- if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+ if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props))
+ goto fail;

- if (opts->message.len > 0) mg_send(c, opts->message.buf, opts->message.len);
+ if (opts->message.len > 0 &&
+ !mg_send(c, opts->message.buf, opts->message.len))
+ goto fail;
+ return id;
+
+fail:
+ mg_error(c, "OOM");
return id;
}

@@ -3658,14 +3683,20 @@ void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0;
size_t len = 2 + opts->topic.len + 2 + 1 + plen;

- mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len);
+ if (!mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len)) goto fail;
if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
- mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
- if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+ if (!mg_send_u16(c, mg_htons(c->mgr->mqtt_id))) goto fail;

- mg_send_u16(c, mg_htons((uint16_t) opts->topic.len));
- mg_send(c, opts->topic.buf, opts->topic.len);
- mg_send(c, &qos_, sizeof(qos_));
+ if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props))
+ goto fail;
+
+ if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) ||
+ !mg_send(c, opts->topic.buf, opts->topic.len) ||
+ !mg_send(c, &qos_, sizeof(qos_)))
+ goto fail;
+ return;
+fail:
+ mg_error(c, "OOM");
}

int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
@@ -3772,15 +3803,16 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
uint32_t remaining_len = sizeof(id);
if (c->is_mqtt5) remaining_len += 2; // 3.4.2

- mg_mqtt_send_header(
- c,
- (uint8_t) (mm.qos == 2 ? MQTT_CMD_PUBREC : MQTT_CMD_PUBACK),
- 0, remaining_len);
- mg_send(c, &id, sizeof(id));
+ if (!mqtt_send_header(c,
+ (uint8_t) (mm.qos == 2 ? MQTT_CMD_PUBREC
+ : MQTT_CMD_PUBACK),
+ 0, remaining_len) ||
+ !mg_send(c, &id, sizeof(id)))
+ goto fail;

if (c->is_mqtt5) {
uint16_t zero = 0;
- mg_send(c, &zero, sizeof(zero));
+ if (!mg_send(c, &zero, sizeof(zero))) goto fail;
}
}
mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff
@@ -3789,15 +3821,18 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
case MQTT_CMD_PUBREC: { // MQTT5: 3.5.2-1 TODO(): variable header rc
uint16_t id = mg_ntohs(mm.id);
uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1
- mg_mqtt_send_header(c, MQTT_CMD_PUBREL, 2, remaining_len);
- mg_send(c, &id, sizeof(id)); // MQTT5 3.6.1-1, flags = 2
+ if (!mqtt_send_header(c, MQTT_CMD_PUBREL, 2,
+ remaining_len) // MQTT5 3.6.1-1, flags = 2
+ || !mg_send(c, &id, sizeof(id)))
+ goto fail;
break;
}
case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc
uint16_t id = mg_ntohs(mm.id);
uint32_t remaining_len = sizeof(id); // MQTT5 3.7.2-1
- mg_mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len);
- mg_send(c, &id, sizeof(id));
+ if (!mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len) ||
+ !mg_send(c, &id, sizeof(id)))
+ goto fail;
break;
}
}
@@ -3809,6 +3844,9 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
}
}
(void) ev_data;
+ return;
+fail:
+ mg_error(c, "OOM");
}

void mg_mqtt_ping(struct mg_connection *nc) {
@@ -3823,19 +3861,24 @@ void mg_mqtt_disconnect(struct mg_connection *c,
const struct mg_mqtt_opts *opts) {
size_t len = 0;
if (c->is_mqtt5) len = 1 + get_props_size(opts->props, opts->num_props);
- mg_mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len);
+ if (!mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len)) goto fail;

if (c->is_mqtt5) {
uint8_t zero = 0;
- mg_send(c, &zero, sizeof(zero)); // reason code
- mg_send_mqtt_properties(c, opts->props, opts->num_props);
+ if (!mg_send(c, &zero, sizeof(zero)) // reason code
+ || !mg_send_mqtt_properties(c, opts->props, opts->num_props))
+ goto fail;
}
+ return;
+fail:
+ mg_error(c, "OOM");
}

struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
const struct mg_mqtt_opts *opts,
mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_connect_svc(mgr, url, fn, fn_data, mqtt_cb, NULL);
+ struct mg_connection *c =
+ mg_connect_svc(mgr, url, fn, fn_data, mqtt_cb, NULL);
if (c != NULL) {
struct mg_mqtt_opts empty;
memset(&empty, 0, sizeof(empty));
@@ -3865,8 +3908,14 @@ struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,

size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) {
size_t old = c->send.len;
- mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
- return c->send.len - old;
+ size_t expected = mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
+ size_t actual = c->send.len - old;
+ if (actual != expected) {
+ mg_error(c, "OOM");
+ c->send.len = old;
+ actual = 0;
+ }
+ return actual;
}

size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
@@ -4157,6 +4206,7 @@ void mg_mgr_init(struct mg_mgr *mgr) {
#endif


+
#if MG_ENABLE_TCPIP
#define MG_EPHEMERAL_PORT_BASE 32768
#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
@@ -4205,7 +4255,7 @@ struct eth {
struct ip {
uint8_t ver; // Version
uint8_t tos; // Unused
- uint16_t len; // Length
+ uint16_t len; // Datagram length
uint16_t id; // Unused
uint16_t frag; // Fragmentation
#define IP_FRAG_OFFSET_MSK 0x1fff
@@ -4218,13 +4268,13 @@ struct ip {
};

struct ip6 {
- uint8_t ver; // Version
- uint8_t opts[3]; // Options
- uint16_t len; // Length
- uint8_t proto; // Upper level protocol
- uint8_t ttl; // Time to live
- uint8_t src[16]; // Source IP
- uint8_t dst[16]; // Destination IP
+ uint8_t ver; // Version
+ uint8_t label[3]; // Flow label
+ uint16_t plen; // Payload length
+ uint8_t next; // Upper level protocol
+ uint8_t hops; // Hop limit
+ uint64_t src[2]; // Source IP
+ uint64_t dst[2]; // Destination IP
};

struct icmp {
@@ -4233,6 +4283,25 @@ struct icmp {
uint16_t csum;
};

+struct icmp6 {
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+};
+
+struct ndp_na {
+ uint8_t res[4]; // R S O, reserved
+ uint64_t addr[2]; // Target address
+};
+
+struct ndp_ra {
+ uint8_t cur_hop_limit;
+ uint8_t flags; // M,O,Prf,Resvd
+ uint16_t router_lifetime;
+ uint32_t reachable_time;
+ uint32_t retrans_timer;
+};
+
struct arp {
uint16_t fmt; // Format of hardware address
uint16_t pro; // Format of protocol address
@@ -4282,8 +4351,18 @@ struct dhcp {
uint8_t options[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)];
};

+struct dhcp6 {
+ union {
+ uint8_t type;
+ uint32_t xid;
+ };
+ uint8_t options[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)];
+};
+
#pragma pack(pop)

+// pkt is 8-bit aligned, pointers to headers hint compilers to generate
+// byte-copy code for micros with alignment constraints
struct pkt {
struct mg_str raw; // Raw packet data
struct mg_str pay; // Payload data
@@ -4293,12 +4372,26 @@ struct pkt {
struct ip *ip;
struct ip6 *ip6;
struct icmp *icmp;
+ struct icmp6 *icmp6;
struct tcp *tcp;
struct udp *udp;
struct dhcp *dhcp;
+ struct dhcp6 *dhcp6;
};

static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
+#if MG_ENABLE_PROFILE
+ const char *names[] = {"TCPIP_EV_ST_CHG", "TCPIP_EV_DHCP_DNS",
+ "TCPIP_EV_DHCP_SNTP", "TCPIP_EV_ARP",
+ "TCPIP_EV_TIMER_1S", "TCPIP_EV_WIFI_SCAN_RESULT",
+ "TCPIP_EV_WIFI_SCAN_END", "TCPIP_EV_WIFI_CONNECT_ERR",
+ "TCPIP_EV_DRIVER", "TCPIP_EV_USER"};
+ if (ev != MG_TCPIP_EV_POLL && ev < (int) (sizeof(names) / sizeof(names[0]))) {
+ MG_PROF_ADD(c, names[ev]);
+ }
+#endif
+ // Fire protocol handler first, user handler second. See #2559
+ if (ifp->pfn != NULL) ifp->pfn(ifp, ev, ev_data);
if (ifp->fn != NULL) ifp->fn(ifp, ev, ev_data);
}

@@ -4306,7 +4399,7 @@ static void send_syn(struct mg_connection *c);

static void mkpay(struct pkt *pkt, void *p) {
pkt->pay =
- mg_str_n((char *) p, (size_t) (&pkt->raw.buf[pkt->raw.len] - (char *) p));
+ mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p));
}

static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
@@ -4326,6 +4419,61 @@ static uint16_t ipcsum(const void *buf, size_t len) {
return csumfin(sum);
}

+#if MG_ENABLE_IPV6
+static void meui64(uint8_t *addr, uint8_t *mac) {
+ *addr++ = *mac++ ^ (uint8_t) 0x02, *addr++ = *mac++, *addr++ = *mac++;
+ *addr++ = 0xff, *addr++ = 0xfe;
+ *addr++ = *mac++, *addr++ = *mac++, *addr = *mac;
+}
+static void ip6gen(uint8_t *addr, uint8_t *prefix, uint8_t *mac) {
+ memcpy(addr, prefix, 8); // RFC-4291 2.5.4
+ meui64(addr + 8, mac); // 2.5.1
+}
+static void ip6genll(uint8_t *addr, uint8_t *mac) {
+ uint8_t prefix[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6
+ ip6gen(addr, prefix, mac); // 2.5.1
+}
+static void ip6sn(uint64_t *addr, uint64_t *sn_addr) {
+ // Build solicited-node multicast address from a given unicast IP
+ // RFC-4291 2.7
+ uint8_t *sn = (uint8_t *) sn_addr;
+ memset(sn_addr, 0, 16);
+ sn[0] = 0xff;
+ sn[1] = 0x02;
+ sn[11] = 0x01;
+ sn[12] = 0xff;
+ sn[13] = ((uint8_t *) addr)[13];
+ sn[14] = ((uint8_t *) addr)[14];
+ sn[15] = ((uint8_t *) addr)[15];
+}
+static void ip6_mcastmac(uint8_t *mac, uint64_t *ip6) {
+ // Build multicast MAC address from a solicited-node
+ // multicast IPv6 address, RFC-4291 2.7
+ uint8_t *ip = (uint8_t *) ip6;
+ mac[0] = 0x33;
+ mac[1] = 0x33;
+ mac[2] = ip[12];
+ mac[3] = ip[13];
+ mac[4] = ip[14];
+ mac[5] = ip[15];
+}
+
+union ip6addr {
+ uint64_t u[2];
+ uint8_t b[16];
+};
+
+static const union ip6addr ip6_allrouters = {
+ .b = {0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}};
+static const union ip6addr ip6_allnodes = {
+ .b = {0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}};
+static const uint8_t ip6mac_allnodes[6] = {0x33, 0x33, 0x00, 0x00, 0x00, 0x01};
+static const uint8_t ip6mac_allrouters[6] = {0x33, 0x33, 0x00,
+ 0x00, 0x00, 0x02};
+
+#define MG_IP6MATCH(a, b) (a[0] == b[0] && a[1] == b[1])
+#endif
+
static void settmout(struct mg_connection *c, uint8_t type) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
struct connstate *s = (struct connstate *) (c + 1);
@@ -4379,6 +4527,7 @@ static void onstatechange(struct mg_tcpip_if *ifp) {
static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
uint8_t proto, uint32_t ip_src, uint32_t ip_dst,
size_t plen) {
+ // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
struct eth *eth = (struct eth *) ifp->tx.buf;
struct ip *ip = (struct ip *) (eth + 1);
memcpy(eth->dst, mac_dst, sizeof(eth->dst));
@@ -4396,31 +4545,69 @@ static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
return ip;
}

-static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
- uint16_t sport, uint32_t ip_dst, uint16_t dport,
+#if MG_ENABLE_IPV6
+static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint8_t next, uint64_t *ip_src, uint64_t *ip_dst,
+ size_t plen);
+#endif
+
+static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ struct mg_addr *ip_src, struct mg_addr *ip_dst,
const void *buf, size_t len) {
- struct ip *ip =
- tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp));
- struct udp *udp = (struct udp *) (ip + 1);
+ struct ip *ip = NULL;
+ struct udp *udp;
size_t eth_len;
uint32_t cs;
- // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len));
- udp->sport = sport;
- udp->dport = dport;
+#if MG_ENABLE_IPV6
+ struct ip6 *ip6 = NULL;
+ if (ip_dst->is_ip6) {
+ ip6 = tx_ip6(ifp, mac_dst, 17, ip_src->ip6, ip_dst->ip6,
+ len + sizeof(struct udp));
+ udp = (struct udp *) (ip6 + 1);
+ eth_len = sizeof(struct eth) + sizeof(*ip6) + sizeof(*udp) + len;
+ } else
+#endif
+ {
+ ip = tx_ip(ifp, mac_dst, 17, ip_src->ip4, ip_dst->ip4,
+ len + sizeof(struct udp));
+ udp = (struct udp *) (ip + 1);
+ eth_len = sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len;
+ }
+ udp->sport = ip_src->port;
+ udp->dport = ip_dst->port;
udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
udp->csum = 0;
cs = csumup(0, udp, sizeof(*udp));
cs = csumup(cs, buf, len);
- cs = csumup(cs, &ip->src, sizeof(ip->src));
- cs = csumup(cs, &ip->dst, sizeof(ip->dst));
- cs += (uint32_t) (ip->proto + sizeof(*udp) + len);
+#if MG_ENABLE_IPV6
+ if (ip_dst->is_ip6) {
+ cs = csumup(cs, &ip6->src, sizeof(ip6->src));
+ cs = csumup(cs, &ip6->dst, sizeof(ip6->dst));
+ } else
+#endif
+ {
+ cs = csumup(cs, &ip->src, sizeof(ip->src));
+ cs = csumup(cs, &ip->dst, sizeof(ip->dst));
+ }
+ cs += (uint32_t) (17 + sizeof(*udp) + len);
udp->csum = csumfin(cs);
memmove(udp + 1, buf, len);
- // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len));
- eth_len = sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len;
return (ether_output(ifp, eth_len) == eth_len);
}

+static bool tx_udp4(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
+ uint16_t sport, uint32_t ip_dst, uint16_t dport,
+ const void *buf, size_t len) {
+ struct mg_addr ips, ipd;
+ memset(&ips, 0, sizeof(ips));
+ ips.ip4 = ip_src;
+ ips.port = sport;
+ memset(&ipd, 0, sizeof(ipd));
+ ipd.ip4 = ip_dst;
+ ipd.port = dport;
+ return tx_udp(ifp, mac_dst, &ips, &ipd, buf, len);
+}
+
static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
uint32_t ip_dst, uint8_t *opts, size_t optslen,
bool ciaddr) {
@@ -4431,8 +4618,8 @@ static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid));
memcpy(&dhcp.options, opts, optslen);
if (ciaddr) dhcp.ciaddr = ip_src;
- tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp,
- sizeof(dhcp));
+ tx_udp4(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp,
+ sizeof(dhcp));
}

static const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
@@ -4488,12 +4675,18 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
bool lsn) {
struct mg_connection *c = NULL;
for (c = mgr->conns; c != NULL; c = c->next) {
- if (c->is_arplooking && pkt->arp &&
- memcmp(&pkt->arp->spa, c->rem.ip, sizeof(pkt->arp->spa)) == 0)
+ if (c->is_arplooking && pkt->arp && pkt->arp->spa == c->rem.ip4) break;
+#if MG_ENABLE_IPV6
+ if (c->is_arplooking && pkt->icmp6 && pkt->icmp6->type == 136) {
+ struct ndp_na *na = (struct ndp_na *) (pkt->icmp6 + 1);
+ if (MG_IP6MATCH(na->addr, c->rem.ip6)) break;
+ }
+#endif
+ if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport &&
+ (!c->loc.is_ip6 || pkt->ip6))
break;
- if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break;
if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
- lsn == (bool) c->is_listening &&
+ (!c->loc.is_ip6 || pkt->ip6) && lsn == (bool) c->is_listening &&
(lsn || c->rem.port == pkt->tcp->sport))
break;
}
@@ -4505,8 +4698,7 @@ static void mac_resolved(struct mg_connection *c);
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
// ARP request. Make a response, then send
- // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4,
- // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa));
+ // MG_VERBOSE(("ARP req from %M", mg_print_ip4, &pkt->arp->spa));
struct eth *eth = (struct eth *) ifp->tx.buf;
struct arp *arp = (struct arp *) (eth + 1);
memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst));
@@ -4523,6 +4715,7 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
ether_output(ifp, PDIFF(eth, arp + 1));
} else if (pkt->arp->op == mg_htons(2)) {
if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
+ // MG_VERBOSE(("ARP resp from %M", mg_print_ip4, &pkt->arp->spa));
if (pkt->arp->spa == ifp->gw) {
// Got response for the GW ARP request. Set ifp->gwmac and IP -> READY
memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac));
@@ -4545,7 +4738,6 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
}

static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- // MG_DEBUG(("ICMP %d", (int) len));
if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp);
size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
@@ -4555,7 +4747,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src,
sizeof(*icmp) + plen);
icmp = (struct icmp *) (ip + 1);
- memset(icmp, 0, sizeof(*icmp)); // Set csum to 0
+ memset(icmp, 0, sizeof(*icmp)); // Set csum, type, code to 0
memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX
icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
ether_output(ifp, hlen + plen);
@@ -4569,7 +4761,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint8_t msgtype = 0, state = ifp->state;
// perform size check first, then access fields
uint8_t *p = pkt->dhcp->options,
- *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len];
+ *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9
@@ -4629,7 +4821,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
// Simple DHCP server that assigns a next IP address: ifp->ip + 1
static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint8_t op = 0, *p = pkt->dhcp->options,
- *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len];
+ *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
// struct dhcp *req = pkt->dhcp;
struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
@@ -4661,51 +4853,344 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's
memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
}
- tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67),
- op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res));
+ tx_udp4(ifp, pkt->eth->src, ifp->ip, mg_htons(67),
+ op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res));
}
}

-static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
- if (c == NULL) {
- // No UDP listener on this port. Should send ICMP, but keep silent.
+#if MG_ENABLE_IPV6
+static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint8_t next, uint64_t *ip_src, uint64_t *ip_dst,
+ size_t plen) {
+ // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
+ struct eth *eth = (struct eth *) ifp->tx.buf;
+ struct ip6 *ip6 = (struct ip6 *) (eth + 1);
+ memcpy(eth->dst, mac_dst, sizeof(eth->dst));
+ memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC
+ eth->type = mg_htons(0x86dd);
+ memset(ip6, 0, sizeof(*ip6));
+ ip6->ver = 0x60; // Version 6, traffic class 0
+ ip6->plen = mg_htons((uint16_t) plen);
+ ip6->next = next;
+ ip6->hops = 255; // NDP requires max
+ ip6->src[0] = *ip_src++;
+ ip6->src[1] = *ip_src;
+ ip6->dst[0] = *ip_dst++;
+ ip6->dst[1] = *ip_dst;
+ return ip6;
+}
+
+static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint64_t *ip_src, uint64_t *ip_dst, uint8_t type,
+ uint8_t code, const void *buf, size_t len) {
+ struct ip6 *ip6;
+ struct icmp6 *icmp6;
+ uint32_t cs;
+ ip6 = tx_ip6(ifp, mac_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len);
+ icmp6 = (struct icmp6 *) (ip6 + 1);
+ memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0
+ icmp6->type = type;
+ icmp6->code = code;
+ memcpy(icmp6 + 1, buf, len); // Copy payload
+ icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1
+ cs = csumup(0, icmp6, sizeof(*icmp6));
+ cs = csumup(cs, buf, len);
+ cs = csumup(cs, ip_src, 16);
+ cs = csumup(cs, ip_dst, 16);
+ cs += (uint32_t) (58 + sizeof(*icmp6) + len);
+ icmp6->csum = csumfin(cs);
+ ether_output(ifp, sizeof(struct eth) + sizeof(*ip6) + sizeof(*icmp6) + len);
+}
+
+// Neighbor Discovery Protocol, RFC-4861
+// Neighbor Advertisement, 4.4
+static void tx_ndp_na(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint64_t *ip_src, uint64_t *ip_dst, bool solicited,
+ uint8_t *mac) {
+ uint8_t data[28];
+ memset(data, 0, sizeof(data));
+ data[0] = solicited ? 0x60 : 0x20; // O + S
+ memcpy(data + 4, ip_src, 16); // Target address
+ data[20] = 2, data[21] = 1; // 4.6.1, target MAC
+ memcpy(data + 22, mac, 6);
+ tx_icmp6(ifp, mac_dst, ip_src, ip_dst, 136, 0, data, sizeof(data));
+}
+
+static void onstate6change(struct mg_tcpip_if *ifp);
+
+static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ struct ndp_na *na = (struct ndp_na *) (pkt->icmp6 + 1);
+ uint8_t *opts = (uint8_t *) (na + 1);
+ if ((na->res[0] & 0x40) == 0) return; // not "solicited"
+ if (*opts++ != 2) return; // no target hwaddr
+ if (*opts++ != 1) return; // don't know what to do with this layer 2
+ MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr));
+ if (MG_IP6MATCH(na->addr, ifp->gw6)) {
+ // Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY
+ memcpy(ifp->gw6mac, opts, sizeof(ifp->gw6mac));
+ if (ifp->state6 == MG_TCPIP_STATE_IP) {
+ ifp->state6 = MG_TCPIP_STATE_READY;
+ onstate6change(ifp);
+ }
} else {
- struct connstate *s = (struct connstate *) (c + 1);
- c->rem.port = pkt->udp->sport;
- memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t));
- memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
- if (c->recv.len >= MG_MAX_RECV_SIZE) {
- mg_error(c, "max_recv_buf_size reached");
- } else if (c->recv.size - c->recv.len < pkt->pay.len &&
- !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
- mg_error(c, "oom");
- } else {
- memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
- c->recv.len += pkt->pay.len;
- mg_call(c, MG_EV_READ, &pkt->pay.len);
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
+ if (c != NULL && c->is_arplooking) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, opts, sizeof(s->mac));
+ MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6, c->rem.ip,
+ mg_print_mac, s->mac));
+ c->is_arplooking = 0;
+ mac_resolved(c);
+ }
+ }
+}
+
+// Neighbor Solicitation, 4.3
+static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ uint64_t target[2];
+ if (pkt->pay.len < sizeof(target)) return;
+ memcpy(target, pkt->pay.buf + 4, sizeof(target));
+ if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6))
+ tx_ndp_na(ifp, (uint8_t *) pkt->pay.buf + 22, target, pkt->ip6->src, true,
+ ifp->mac);
+}
+
+static void tx_ndp_ns(struct mg_tcpip_if *ifp, uint64_t *ip_dst, uint8_t *mac) {
+ uint8_t payload[4 + 16 + 8];
+ uint64_t unspec_ip[2] = {0, 0};
+ size_t payload_len;
+ bool mcast_ns = true;
+ uint64_t mcast_ip[2] = {0, 0};
+ uint8_t mcast_mac[6];
+
+ memset(payload, 0, sizeof(payload));
+ memcpy(payload + 4, ip_dst, 16);
+ for (int i = 0; i < 6; i++) {
+ if (mac[i] != 0xff) {
+ mcast_ns = false;
+ break;
+ }
+ }
+ if (mcast_ns) {
+ ip6sn(ip_dst, mcast_ip);
+ ip6_mcastmac(mcast_mac, mcast_ip);
+ }
+ // TODO(robertc2000): using only link-local IP addr for now
+ // We might consider to add an option to use either link-local or global IP
+ if (!MG_IP6MATCH(ifp->ip6ll, unspec_ip)) {
+ payload[20] = payload[21] = 1; // Type = 1; Length = 1
+ memcpy(payload + 22, ifp->mac, 6);
+ payload_len = sizeof(payload);
+ } else {
+ payload_len = sizeof(payload) - 8;
+ }
+ tx_icmp6(ifp, mcast_ns ? mcast_mac : mac, ifp->ip6ll,
+ mcast_ns ? mcast_ip : ip_dst, 135, 0, payload, payload_len);
+}
+
+// Router Solicitation, 4.1
+static void tx_ndp_rs(struct mg_tcpip_if *ifp, uint64_t *ip_dst, uint8_t *mac) {
+ // Note: currently, this function only sends multicast RS packets
+ (void) ip_dst;
+ (void) mac;
+ uint8_t payload[4 + 8]; // reserved + optional source MAC addr
+ size_t payload_len = 4;
+ uint64_t unspec_ip[2] = {0, 0};
+
+ memset(payload, 0, sizeof(payload));
+ if (!MG_IP6MATCH(ifp->ip6ll, unspec_ip)) {
+ payload[4] = payload[5] = 1; // Type = 1; Length = 1
+ memcpy(payload + 6, ifp->mac, 6);
+ payload_len += 8;
+ }
+ tx_icmp6(ifp, (uint8_t *) ip6mac_allrouters, ifp->ip6ll,
+ (uint64_t *) ip6_allrouters.u, 133, 0, payload, payload_len);
+ MG_DEBUG(("NDP Router Solicitation sent"));
+}
+
+static bool fill_global(uint64_t *ip6, uint8_t *prefix, uint8_t prefix_len,
+ uint8_t *mac) {
+ uint8_t full = prefix_len / 8;
+ uint8_t rem = prefix_len % 8;
+ if (full >= 8 && rem != 0) {
+ MG_ERROR(("Prefix length > 64, UNSUPPORTED"));
+ return false;
+ } else if (full == 8 && rem == 0) {
+ ip6gen((uint8_t *) ip6, prefix, mac);
+ } else {
+ ip6[0] = ip6[1] = 0; // already zeroed before firing RS...
+ if (full) memcpy(ip6, prefix, full);
+ if (rem) {
+ uint8_t mask = (uint8_t) (0xFF << (8 - rem));
+ ((uint8_t *) ip6)[full] = prefix[full] & mask;
}
+ meui64(((uint8_t *) &ip6[1]), mac); // RFC-4291 2.5.4, 2.5.1
}
+ return true;
}

-static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
- uint8_t flags, uint16_t sport, uint16_t dport,
- uint32_t seq, uint32_t ack, const void *buf, size_t len) {
- struct ip *ip;
+// Router Advertisement, 4.2
+static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ if (pkt->pay.len < 12) return;
+ struct ndp_ra *ra = (struct ndp_ra *) (pkt->icmp6 + 1);
+ uint8_t *opts = (uint8_t *) (ra + 1);
+ size_t opt_left = pkt->pay.len - 12;
+
+ if (ifp->state6 == MG_TCPIP_STATE_UP) {
+ MG_DEBUG(("Received NDP RA"));
+ memcpy(ifp->gw6, (uint8_t *) pkt->ip6->src, 16); // fill gw6 address
+ // parse options
+ while (opt_left >= 2) {
+ uint8_t type = opts[0], len = opts[1];
+ size_t length = (size_t) len * 8;
+ if (length == 0 || length > opt_left) break; // malformed
+ if (type == 1 && length >= 8) {
+ // Received router's MAC address
+ ifp->state6 = MG_TCPIP_STATE_READY;
+ memcpy(ifp->gw6mac, opts + 2, 6);
+ } else if (type == 5 && length >= 8) {
+ // process MTU if available
+ uint32_t mtu = mg_ntohl(*(uint32_t *) (opts + 4));
+ MG_INFO(("got a nice MTU: %u, do you care ?", mtu));
+ // TODO(): **** This is an IPv6-given MTU, ifp->MTU is a LINK MTU, are
+ // we talkin'bout the same ? ***
+ } else if (type == 3 && length >= 32) {
+ // process prefix, 4.6.2
+ uint8_t prefix_len = opts[2];
+ uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40
+ uint32_t valid = mg_ntohl(*(uint32_t *) (opts + 4));
+ uint32_t pref_lifetime = mg_ntohl(*(uint32_t *) (opts + 8));
+ uint8_t *prefix = opts + 16;
+
+ // TODO (robertc2000): handle prefix options if necessary
+ (void) prefix_len;
+ (void) pfx_flags;
+ (void) valid;
+ (void) pref_lifetime;
+ (void) prefix;
+
+ // fill prefix length and global
+ ifp->prefix_len = prefix_len;
+ if (!fill_global(ifp->ip6, prefix, prefix_len, ifp->mac)) return;
+ }
+ opts += length;
+ opt_left -= length;
+ }
+
+ if (ifp->state6 != MG_TCPIP_STATE_READY) {
+ tx_ndp_ns(ifp, ifp->gw6, ifp->gw6mac); // unsolicited GW MAC resolution
+ ifp->state6 = MG_TCPIP_STATE_IP;
+ }
+ onstate6change(ifp);
+ }
+}
+
+static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ switch (pkt->icmp6->type) {
+ case 128: { // Echo Request, RFC-4443 4.1
+ uint64_t target[2];
+ memcpy(target, pkt->ip6->dst, sizeof(target));
+ if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) {
+ size_t hlen =
+ sizeof(struct eth) + sizeof(struct ip6) + sizeof(struct icmp6);
+ size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
+ if (plen > space) plen = space; // Copy (truncated) RX payload to TX
+ // Echo Reply, 4.2
+ tx_icmp6(ifp, pkt->eth->src, pkt->ip6->dst, pkt->ip6->src, 129, 0,
+ pkt->pay.buf, plen);
+ }
+ } break;
+ case 134: // Router Advertisement
+ rx_ndp_ra(ifp, pkt);
+ break;
+ case 135: // Neighbor Solicitation
+ rx_ndp_ns(ifp, pkt);
+ break;
+ case 136: // Neighbor Advertisement
+ rx_ndp_na(ifp, pkt);
+ break;
+ }
+}
+
+static void onstate6change(struct mg_tcpip_if *ifp) {
+ if (ifp->state6 == MG_TCPIP_STATE_READY) {
+ MG_INFO(("READY, IP: %M", mg_print_ip6, &ifp->ip6));
+ MG_INFO((" GW: %M", mg_print_ip6, &ifp->gw6));
+ MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac));
+ } else if (ifp->state6 == MG_TCPIP_STATE_IP) {
+ tx_ndp_ns(ifp, ifp->gw6, ifp->gw6mac); // unsolicited GW MAC resolution
+ } else if (ifp->state6 == MG_TCPIP_STATE_UP) {
+ MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll));
+ }
+ if (ifp->state6 != MG_TCPIP_STATE_UP && ifp->state6 != MG_TCPIP_STATE_DOWN)
+ mg_tcpip_call(ifp, MG_TCPIP_EV_ST6_CHG, &ifp->state6);
+}
+#endif
+
+static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
+ struct connstate *s;
+ if (c == NULL) return false; // No UDP listener on this port
+ s = (struct connstate *) (c + 1);
+ c->rem.port = pkt->udp->sport;
+#if MG_ENABLE_IPV6
+ if (c->loc.is_ip6) {
+ c->rem.ip6[0] = pkt->ip6->src[0], c->rem.ip6[1] = pkt->ip6->src[1],
+ c->rem.is_ip6 = true;
+ } else
+#endif
+ {
+ c->rem.ip4 = pkt->ip->src;
+ }
+ memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
+ if (c->recv.len >= MG_MAX_RECV_SIZE) {
+ mg_error(c, "max_recv_buf_size reached");
+ } else if (c->recv.size - c->recv.len < pkt->pay.len &&
+ !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
+ mg_error(c, "oom");
+ } else {
+ memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
+ c->recv.len += pkt->pay.len;
+ mg_call(c, MG_EV_READ, &pkt->pay.len);
+ }
+ return true;
+}
+
+static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ struct mg_addr *ip_src, struct mg_addr *ip_dst,
+ uint8_t flags, uint32_t seq, uint32_t ack, const void *buf,
+ size_t len) {
+ struct ip *ip = NULL;
struct tcp *tcp;
- uint16_t opts[4 / 2];
- if (flags & TH_SYN) { // Send MSS, RFC-9293 3.7.1
- opts[0] = mg_htons(0x0204); // RFC-9293 3.2
- opts[1] = mg_htons((uint16_t) (ifp->mtu - 40)); // RFC-6691
+ uint16_t opts[4 / 2], mss;
+#if MG_ENABLE_IPV6
+ struct ip6 *ip6 = NULL;
+ mss = (uint16_t) (ifp->mtu - 60); // RFC-9293 3.7.1; RFC-6691 2
+#else
+ mss = (uint16_t) (ifp->mtu - 40); // RFC-9293 3.7.1; RFC-6691 2
+#endif
+ if (flags & TH_SYN) { // Send MSS
+ opts[0] = mg_htons(0x0204); // RFC-9293 3.2
+ opts[1] = mg_htons(mss);
buf = opts;
len = sizeof(opts);
}
- ip = tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len);
- tcp = (struct tcp *) (ip + 1);
+#if MG_ENABLE_IPV6
+ if (ip_dst->is_ip6) {
+ ip6 = tx_ip6(ifp, mac_dst, 6, ip_src->ip6, ip_dst->ip6,
+ sizeof(struct tcp) + len);
+ tcp = (struct tcp *) (ip6 + 1);
+ } else
+#endif
+ {
+ ip = tx_ip(ifp, mac_dst, 6, ip_src->ip4, ip_dst->ip4,
+ sizeof(struct tcp) + len);
+ tcp = (struct tcp *) (ip + 1);
+ }
memset(tcp, 0, sizeof(*tcp));
if (buf != NULL && len) memmove(tcp + 1, buf, len);
- tcp->sport = sport;
- tcp->dport = dport;
+ tcp->sport = ip_src->port;
+ tcp->dport = ip_dst->port;
tcp->seq = seq;
tcp->ack = ack;
tcp->flags = flags;
@@ -4715,31 +5200,60 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
{
uint32_t cs = 0;
uint16_t n = (uint16_t) (sizeof(*tcp) + len);
- uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)};
cs = csumup(cs, tcp, n);
- cs = csumup(cs, &ip->src, sizeof(ip->src));
- cs = csumup(cs, &ip->dst, sizeof(ip->dst));
- cs = csumup(cs, pseudo, sizeof(pseudo));
+#if MG_ENABLE_IPV6
+ if (ip_dst->is_ip6) {
+ cs = csumup(cs, &ip6->src, sizeof(ip6->src));
+ cs = csumup(cs, &ip6->dst, sizeof(ip6->dst));
+ cs += (uint32_t) (6 + n);
+ MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip6, &ip6->src,
+ mg_ntohs(tcp->sport), mg_print_ip6, &ip6->dst,
+ mg_ntohs(tcp->dport), tcp->flags, len));
+ } else
+#endif
+ {
+ uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8),
+ (uint8_t) (n & 255)};
+ cs = csumup(cs, &ip->src, sizeof(ip->src));
+ cs = csumup(cs, &ip->dst, sizeof(ip->dst));
+ cs = csumup(cs, pseudo, sizeof(pseudo));
+ MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src,
+ mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst,
+ mg_ntohs(tcp->dport), tcp->flags, len));
+ }
tcp->csum = csumfin(cs);
}
- MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src,
- mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst,
- mg_ntohs(tcp->dport), tcp->flags, len));
- // mg_hexdump(ifp->tx.buf, PDIFF(ifp->tx.buf, tcp + 1) + len);
return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len);
}

-static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
- uint8_t flags, uint32_t seq, const void *buf,
- size_t len) {
- uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
- return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
- pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
- buf, len);
+static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
+ uint8_t flags, uint32_t seqno) {
+ uint32_t ackno = mg_htonl(mg_ntohl(pkt->tcp->seq) + (uint32_t) pkt->pay.len +
+ ((pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0));
+ struct mg_addr ips, ipd;
+ memset(&ips, 0, sizeof(ips));
+ memset(&ipd, 0, sizeof(ipd));
+ if (pkt->ip != NULL) {
+ ips.ip4 = pkt->ip->dst;
+ ipd.ip4 = pkt->ip->src;
+ } else {
+ ips.ip6[0] = pkt->ip6->dst[0], ips.ip6[1] = pkt->ip6->dst[1];
+ ipd.ip6[0] = pkt->ip6->src[0], ipd.ip6[1] = pkt->ip6->src[1];
+ ips.is_ip6 = true;
+ ipd.is_ip6 = true;
+ }
+ ips.port = pkt->tcp->dport;
+ ipd.port = pkt->tcp->sport;
+ return tx_tcp(ifp, pkt->eth->src, &ips, &ipd, flags, seqno, ackno, NULL, 0);
+}
+
+static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
+ return tx_tcp_ctrlresp(ifp, pkt, toack ? TH_RST : (TH_RST | TH_ACK),
+ toack ? pkt->tcp->ack : 0);
}

static struct mg_connection *accept_conn(struct mg_connection *lsn,
- struct pkt *pkt) {
+ struct pkt *pkt, uint16_t mss) {
struct mg_connection *c = mg_alloc_conn(lsn->mgr);
struct connstate *s;
if (c == NULL) {
@@ -4747,18 +5261,30 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
return NULL;
}
s = (struct connstate *) (c + 1);
- s->dmss = 536; // assume default, RFC-9293 3.7.1
+ s->dmss = mss; // from options in client SYN
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
settmout(c, MIP_TTYPE_KEEPALIVE);
- memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t));
+#if MG_ENABLE_IPV6
+ if (lsn->loc.is_ip6) {
+ c->rem.ip6[0] = pkt->ip6->src[0], c->rem.ip6[1] = pkt->ip6->src[1],
+ c->rem.is_ip6 = true;
+ c->loc.ip6[0] = c->mgr->ifp->ip6[0], c->loc.ip6[1] = c->mgr->ifp->ip6[1],
+ c->loc.is_ip6 = true;
+ // TODO(): compare lsn to link-local, or rem as link-local: use ll instead
+ } else
+#endif
+ {
+ c->rem.ip4 = pkt->ip->src;
+ c->loc.ip4 = c->mgr->ifp->ip;
+ }
c->rem.port = pkt->tcp->sport;
+ c->loc.port = lsn->loc.port;
MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem));
LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c);
c->is_accepted = 1;
c->is_hexdumping = lsn->is_hexdumping;
c->pfn = lsn->pfn;
- c->loc = lsn->loc;
c->pfn_data = lsn->pfn_data;
c->fn = lsn->fn;
c->fn_data = lsn->fn_data;
@@ -4771,10 +5297,13 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,

static size_t trim_len(struct mg_connection *c, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
- size_t eth_h_len = 14, ip_max_h_len = 24, tcp_max_h_len = 60, udp_h_len = 8;
+ size_t eth_h_len = 14, tcp_max_h_len = 60, udp_h_len = 8;
+ size_t ip_max_h_len = c->rem.is_ip6 ? 40 : 24; // we don't send options
size_t max_headers_len =
eth_h_len + ip_max_h_len + (c->is_udp ? udp_h_len : tcp_max_h_len);
- size_t min_mtu = c->is_udp ? 68 /* RFC-791 */ : max_headers_len - eth_h_len;
+ size_t min_mtu = c->rem.is_ip6 ? 1280
+ : c->is_udp ? 68 /* RFC-791 */
+ : max_headers_len - eth_h_len;

// If the frame exceeds the available buffer, trim the length
if (len + max_headers_len > ifp->tx.len) {
@@ -4796,20 +5325,35 @@ static size_t trim_len(struct mg_connection *c, size_t len) {
return len;
}

-long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
+static bool udp_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
struct connstate *s = (struct connstate *) (c + 1);
- uint32_t dst_ip = *(uint32_t *) c->rem.ip;
+ struct mg_addr ips;
+ memset(&ips, 0, sizeof(ips));
+#if MG_ENABLE_IPV6
+ if (c->loc.is_ip6) {
+ ips.ip6[0] = ifp->ip6[0], ips.ip6[1] = ifp->ip6[1], ips.is_ip6 = true;
+ // TODO(): detect link-local (c->rem) and use it
+ } else
+#endif
+ {
+ ips.ip4 = ifp->ip;
+ }
+ ips.port = c->loc.port;
+ return tx_udp(ifp, s->mac, &ips, &c->rem, buf, len);
+}
+
+long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct connstate *s = (struct connstate *) (c + 1);
len = trim_len(c, len);
if (c->is_udp) {
- if (!tx_udp(ifp, s->mac, ifp->ip, c->loc.port, dst_ip, c->rem.port, buf,
- len))
- return MG_IO_WAIT;
+ if (!udp_send(c, buf, len)) return MG_IO_WAIT;
} else { // TCP, cap to peer's MSS
+ struct mg_tcpip_if *ifp = c->mgr->ifp;
size_t sent;
if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts
- sent = tx_tcp(ifp, s->mac, dst_ip, TH_PUSH | TH_ACK, c->loc.port,
- c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
+ sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_PUSH | TH_ACK,
+ mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
if (sent == 0) {
return MG_IO_WAIT;
} else if (sent == (size_t) -1) {
@@ -4846,14 +5390,12 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
struct connstate *s = (struct connstate *) (c + 1);
struct mg_iobuf *io = c->is_tls ? &c->rtls : &c->recv;
uint32_t seq = mg_ntohl(pkt->tcp->seq);
- uint32_t rem_ip;
- memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
if (pkt->tcp->flags & TH_FIN) {
uint8_t flags = TH_ACK;
if (mg_ntohl(pkt->tcp->seq) != s->ack) {
- MG_VERBOSE(("ignoring FIN, SEQ != ACK: %x %x", mg_ntohl(pkt->tcp->seq), s->ack));
- tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
+ MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack));
+ tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
+ mg_htonl(s->ack), "", 0);
return;
}
// If we initiated the closure, we reply with ACK upon receiving FIN
@@ -4873,14 +5415,14 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
c->is_draining = 1;
settmout(c, MIP_TTYPE_FIN);
}
- tx_tcp(c->mgr->ifp, s->mac, rem_ip, flags, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
+ tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, flags, mg_htonl(s->seq),
+ mg_htonl(s->ack), "", 0);
if (pkt->pay.len == 0) return; // if no data, we're done
} 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, rem_ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
+ tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
+ mg_htonl(s->ack), NULL, 0);
return; // no data to process
} else if (pkt->pay.len == 0) { // this is an ACK
if (s->fin_rcvd && s->ttype == MIP_TTYPE_FIN) s->twclosure = true;
@@ -4891,8 +5433,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
MG_VERBOSE(("ignoring duplicate pkt"));
} else {
MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
- tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
+ tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
+ mg_htonl(s->ack), "", 0);
}
return; // drop it
} else if (io->size - io->len < pkt->pay.len &&
@@ -4915,8 +5457,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
if (s->unacked > MIP_TCP_WIN / 2 && s->acked != s->ack) {
// Send ACK immediately
MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked));
- tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
+ tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
+ mg_htonl(s->ack), NULL, 0);
s->unacked = 0;
s->acked = s->ack;
if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
@@ -4924,22 +5466,70 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
// if not already running, setup a timer to send an ACK later
if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
}
- if (c->is_tls && c->is_tls_hs) {
- mg_tls_handshake(c);
- } else if (c->is_tls) {
- handle_tls_recv(c);
+ if (c->is_tls) {
+ c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
} else {
// Plain text connection, data is already in c->recv, trigger MG_EV_READ
mg_call(c, MG_EV_READ, &pkt->pay.len);
}
}

+// TCP backlog
+struct mg_backlog {
+ uint16_t port, mss; // use port=0 for available entries
+ uint8_t age;
+};
+
+static int backlog_insert(struct mg_connection *c, uint16_t port,
+ uint16_t mss) {
+ struct mg_backlog *p = (struct mg_backlog *) c->data;
+ size_t i;
+ for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
+ if (p[i].port != 0) continue;
+ p[i].age = 2; // remove after two calls, average 1.5 call rate
+ p[i].port = port, p[i].mss = mss;
+ return (int) i;
+ }
+ return -1;
+}
+
+static struct mg_backlog *backlog_retrieve(struct mg_connection *c,
+ uint16_t key, uint16_t port) {
+ struct mg_backlog *p = (struct mg_backlog *) c->data;
+ if (key >= sizeof(c->data) / sizeof(*p)) return NULL;
+ if (p[key].port != port) return NULL;
+ p += key;
+ return p;
+}
+
+static void backlog_remove(struct mg_connection *c, uint16_t key) {
+ struct mg_backlog *p = (struct mg_backlog *) c->data;
+ p[key].port = 0;
+}
+
+static void backlog_maintain(struct mg_connection *c) {
+ struct mg_backlog *p = (struct mg_backlog *) c->data;
+ size_t i; // dec age and remove those where it reaches 0
+ for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
+ if (p[i].port == 0) continue;
+ if (p[i].age != 0) --p[i].age;
+ if (p[i].age == 0) p[i].port = 0;
+ }
+}
+
+static void backlog_poll(struct mg_mgr *mgr) {
+ struct mg_connection *c = NULL;
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ if (!c->is_udp && c->is_listening) backlog_maintain(c);
+ }
+}
+
// process options (MSS)
-static void handle_opt(struct connstate *s, struct tcp *tcp) {
+static void handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
uint8_t *opts = (uint8_t *) (tcp + 1);
int len = 4 * ((int) (tcp->off >> 4) - ((int) sizeof(*tcp) / 4));
- s->dmss = 536; // assume default, RFC-9293 3.7.1
- while (len > 0) { // RFC-9293 3.1 3.2
+ s->dmss = ip6 ? 1220 : 536; // assume default, RFC-9293 3.7.1
+ while (len > 0) { // RFC-9293 3.1 3.2
uint8_t kind = opts[0], optlen = 1;
if (kind != 1) { // No-Operation
if (kind == 0) break; // End of Option List
@@ -4956,73 +5546,108 @@ static void handle_opt(struct connstate *s, struct tcp *tcp) {
static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
-#if 0
- MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
-#endif
+ // Order is VERY important; RFC-9293 3.5.2
+ // - check clients (Group 1) and established connections (Group 3)
if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) {
- handle_opt(s, pkt->tcp); // process options (MSS)
+ // client got a server connection accept
+ handle_opt(s, pkt->tcp, pkt->ip6 != NULL); // process options (MSS)
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
- tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
+ tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
c->is_connecting = 0; // Client connected
settmout(c, MIP_TTYPE_KEEPALIVE);
mg_call(c, MG_EV_CONNECT, NULL); // Let user know
if (c->is_tls_hs) mg_tls_handshake(c);
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
- // mg_hexdump(pkt->raw.buf, pkt->raw.len);
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
+ mg_error(c, "connection refused");
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
+ // TODO(): validate RST is within window (and optional with proper ACK)
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
} else if (c != NULL) {
-#if 0
- MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
- mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
- mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
- mg_hexdump(pkt->pay.buf, pkt->pay.len);
-#endif
+ // process segment
s->tmiss = 0; // Reset missed keep-alive counter
if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
settmout(c,
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
read_conn(c, pkt); // Override timer with ACK timeout if needed
- } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (pkt->tcp->flags & TH_RST) {
- if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
- // ignore RST if not connected
- } else if (pkt->tcp->flags & TH_SYN) {
- // TODO(): handle_opt(s, pkt->tcp); // process options (MSS)
- // At this point, s = NULL, there is no connection.
- // Use peer's source port as ISN, in order to recognise the handshake
- uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
- tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
- } else if (pkt->tcp->flags & TH_FIN) {
- tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
- accept_conn(c, pkt);
- } else if (!c->is_accepted) { // no peer
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else {
- // MG_VERBOSE(("dropped silently.."));
- }
+ } else
+ // - we don't listen on that port; RFC-9293 3.5.2 Group 1
+ // - check listening connections; RFC-9293 3.5.2 Group 2
+ if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
+ // not listening on that port
+ if (!(pkt->tcp->flags & TH_RST)) {
+ tx_tcp_rst(ifp, pkt, pkt->tcp->flags & TH_ACK);
+ } // else silently discard
+ } else if (pkt->tcp->flags == TH_SYN) {
+ // listener receives a connection request
+ struct connstate cs; // At this point, s = NULL, there is no connection
+ int key;
+ uint32_t isn;
+ if (pkt->tcp->sport != 0) {
+ handle_opt(&cs, pkt->tcp, pkt->ip6 != NULL); // process options (MSS)
+ key = backlog_insert(c, pkt->tcp->sport,
+ cs.dmss); // backlog options (MSS)
+ if (key < 0) return; // no room in backlog, discard SYN, client retries
+ // Use peer's src port and bl key as ISN, to later identify the
+ // handshake
+ isn = (mg_htonl(((uint32_t) key << 16) | mg_ntohs(pkt->tcp->sport)));
+ tx_tcp_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn);
+ } // what should we do when port=0 ? Linux takes port 0 as any other
+ // port
+ } else if (pkt->tcp->flags == TH_ACK) {
+ // listener receives an ACK
+ struct mg_backlog *b = NULL;
+ if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) ==
+ mg_htons(pkt->tcp->sport)) {
+ uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16);
+ b = backlog_retrieve(c, key, pkt->tcp->sport);
+ if (b != NULL) { // ACK is a response to a SYN+ACK
+ accept_conn(c, pkt, b->mss); // pass options
+ backlog_remove(c, key);
+ } // else not an actual match, reset
+ }
+ if (b == NULL) tx_tcp_rst(ifp, pkt, true);
+ } else if (pkt->tcp->flags & TH_RST) {
+ // silently discard
+ } else if (pkt->tcp->flags & TH_ACK) { // ACK + something else != RST
+ tx_tcp_rst(ifp, pkt, true);
+ } else if (pkt->tcp->flags & TH_SYN) { // SYN + something else != ACK
+ tx_tcp_rst(ifp, pkt, false);
+ } // else silently discard
}

static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- uint16_t frag = mg_ntohs(pkt->ip->frag);
+ uint8_t ihl;
+ uint16_t frag, len;
+ if (pkt->pay.len < sizeof(*pkt->ip)) return; // Truncated
+ if ((pkt->ip->ver >> 4) != 4) return; // Not IP
+ ihl = pkt->ip->ver & 0x0F;
+ if (ihl < 5) return; // bad IHL
+ if (pkt->pay.len < (uint16_t) (ihl * 4)) return; // Truncated / malformed
+ // There can be link padding, take length from IP header
+ len = mg_ntohs(pkt->ip->len); // IP datagram length
+ if (len < (uint16_t) (ihl * 4) || len > pkt->pay.len) return; // malformed
+ pkt->pay.len = len; // strip padding
+ mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts
+ frag = mg_ntohs(pkt->ip->frag);
if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) {
struct mg_connection *c;
- if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->ip + 1);
- if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->ip + 1);
+ if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
+ if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
c = getpeer(ifp->mgr, pkt, false);
if (c) mg_error(c, "Received fragmented packet");
} else if (pkt->ip->proto == 1) {
- pkt->icmp = (struct icmp *) (pkt->ip + 1);
+ pkt->icmp = (struct icmp *) (pkt->pay.buf);
if (pkt->pay.len < sizeof(*pkt->icmp)) return;
mkpay(pkt, pkt->icmp + 1);
rx_icmp(ifp, pkt);
} else if (pkt->ip->proto == 17) {
- pkt->udp = (struct udp *) (pkt->ip + 1);
- if (pkt->pay.len < sizeof(*pkt->udp)) return;
+ pkt->udp = (struct udp *) (pkt->pay.buf);
+ if (pkt->pay.len < sizeof(*pkt->udp)) return; // truncated
+ // Take length from UDP header
+ len = mg_ntohs(pkt->udp->len); // UDP datagram length
+ if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
+ pkt->pay.len = len; // strip excess data
mkpay(pkt, pkt->udp + 1);
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
@@ -5035,47 +5660,129 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
mkpay(pkt, pkt->dhcp + 1);
rx_dhcp_server(ifp, pkt);
- } else {
- rx_udp(ifp, pkt);
+ } else if (!rx_udp(ifp, pkt)) {
+ // Should send ICMP Destination Unreachable for unicasts, but keep
+ // silent
}
} else if (pkt->ip->proto == 6) {
- uint16_t iplen, off;
- pkt->tcp = (struct tcp *) (pkt->ip + 1);
+ uint8_t off;
+ pkt->tcp = (struct tcp *) (pkt->pay.buf);
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
- mkpay(pkt, pkt->tcp + 1);
- iplen = mg_ntohs(pkt->ip->len);
- off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
- if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
+ off = pkt->tcp->off >> 4; // account for opts
+ if (pkt->pay.len < (uint16_t) (4 * off)) return;
+ mkpay(pkt, (uint32_t *) pkt->tcp + off);
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
rx_tcp(ifp, pkt);
+ } else {
+ MG_DEBUG(("Unknown IP proto %x", (int) pkt->ip->proto));
+ if (mg_log_level >= MG_LL_VERBOSE)
+ mg_hexdump(pkt->ip, pkt->pay.len >= 32 ? 32 : pkt->pay.len);
}
}

+#if MG_ENABLE_IPV6
static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- // MG_DEBUG(("IP %d", (int) len));
- if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) {
- pkt->icmp = (struct icmp *) (pkt->ip6 + 1);
- if (pkt->pay.len < sizeof(*pkt->icmp)) return;
- mkpay(pkt, pkt->icmp + 1);
- rx_icmp(ifp, pkt);
- } else if (pkt->ip6->proto == 17) {
- pkt->udp = (struct udp *) (pkt->ip6 + 1);
+ uint16_t len = 0, plen;
+ uint8_t next, *nhdr;
+ bool loop = true;
+ if (pkt->pay.len < sizeof(*pkt->ip6)) return; // Truncated
+ if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6
+ plen = mg_ntohs(pkt->ip6->plen);
+ if (plen > (pkt->pay.len - sizeof(*pkt->ip6))) return; // malformed
+ next = pkt->ip6->next;
+ nhdr = (uint8_t *) (pkt->ip6 + 1);
+ while (loop) {
+ switch (next) {
+ case 0: // Hop-by-Hop 4.3
+ case 43: // Routing 4.4
+ case 60: // Destination Options 4.6
+ case 51: // Authentication RFC-4302
+ MG_INFO(("IPv6 extension header %d", (int) next));
+ next = nhdr[0];
+ len += (uint16_t) (8 * (nhdr[1] + 1));
+ nhdr += 8 * (nhdr[1] + 1);
+ break;
+ case 44: // Fragment 4.5
+ {
+ struct mg_connection *c;
+ if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
+ if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
+ c = getpeer(ifp->mgr, pkt, false);
+ if (c) mg_error(c, "Received fragmented packet");
+ }
+ return;
+ case 59: // No Next Header 4.7
+ return;
+ case 50: // IPsec ESP RFC-4303, unsupported
+ default:
+ loop = false;
+ break;
+ }
+ }
+ if (len >= plen) return;
+ // There can be link padding, take payload length from IPv6 header - options
+ pkt->pay.buf = (char *) nhdr;
+ pkt->pay.len = plen - len;
+ if (next == 58) {
+ pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf);
+ if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
+ mkpay(pkt, pkt->icmp6 + 1);
+ MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
+ mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
+ rx_icmp6(ifp, pkt);
+ } else if (next == 17) {
+ pkt->udp = (struct udp *) (pkt->pay.buf);
if (pkt->pay.len < sizeof(*pkt->udp)) return;
- // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
- // mg_htons(udp->dport)));
+ // Take length from UDP header
+ len = mg_ntohs(pkt->udp->len); // UDP datagram length
+ if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
+ pkt->pay.len = len; // strip excess data
mkpay(pkt, pkt->udp + 1);
+ MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
+ mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
+ mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
+ if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
+ pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
+ mkpay(pkt, pkt->dhcp6 + 1);
+ // rx_dhcp6_client(ifp, pkt);
+#if 0
+ } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(547)) {
+ pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
+ mkpay(pkt, pkt->dhcp6 + 1);
+ rx_dhcp6_server(ifp, pkt);
+#endif
+ } else if (!rx_udp(ifp, pkt)) {
+ // Should send ICMPv6 Destination Unreachable for unicasts, keep silent
+ }
+ } else if (next == 6) {
+ uint8_t off;
+ pkt->tcp = (struct tcp *) (pkt->pay.buf);
+ if (pkt->pay.len < sizeof(*pkt->tcp)) return;
+ off = pkt->tcp->off >> 4; // account for opts
+ if (pkt->pay.len < (uint16_t) (4 * off)) return;
+ mkpay(pkt, (uint32_t *) pkt->tcp + off);
+ MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
+ mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
+ mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
+ rx_tcp(ifp, pkt);
+ } else {
+ MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
+ if (mg_log_level >= MG_LL_VERBOSE)
+ mg_hexdump(pkt->ip6, pkt->pay.len >= 32 ? 32 : pkt->pay.len);
}
}
+#else
+#define rx_ip6(x, y)
+#endif

static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
struct pkt pkt;
memset(&pkt, 0, sizeof(pkt));
- pkt.raw.buf = (char *) buf;
- pkt.raw.len = len;
- pkt.eth = (struct eth *) buf;
- // mg_hexdump(buf, len > 16 ? 16: len);
+ pkt.pay.buf = pkt.raw.buf = (char *) buf;
+ pkt.pay.len = pkt.raw.len = len; // payload = raw
+ pkt.eth = (struct eth *) buf; // Ethernet = raw
if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt?
if (ifp->enable_mac_check &&
memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 &&
@@ -5086,28 +5793,19 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
len -= 4; // TODO(scaprile): check on bigendian
crc = mg_crc32(0, (const char *) buf, len);
if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return;
+ pkt.pay.len = len;
}
+ mkpay(&pkt, pkt.eth + 1);
if (pkt.eth->type == mg_htons(0x806)) {
- pkt.arp = (struct arp *) (pkt.eth + 1);
- if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated
+ pkt.arp = (struct arp *) (pkt.pay.buf);
+ if (pkt.pay.len < sizeof(*pkt.arp)) return; // Truncated
mg_tcpip_call(ifp, MG_TCPIP_EV_ARP, &pkt.raw);
rx_arp(ifp, &pkt);
} else if (pkt.eth->type == mg_htons(0x86dd)) {
- pkt.ip6 = (struct ip6 *) (pkt.eth + 1);
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated
- if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP
- mkpay(&pkt, pkt.ip6 + 1);
+ pkt.ip6 = (struct ip6 *) (pkt.pay.buf);
rx_ip6(ifp, &pkt);
} else if (pkt.eth->type == mg_htons(0x800)) {
- pkt.ip = (struct ip *) (pkt.eth + 1);
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
- // Truncate frame to what IP header tells us
- if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) {
- pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth);
- }
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
- if ((pkt.ip->ver >> 4) != 4) return; // Not IP
- mkpay(&pkt, pkt.ip + 1);
+ pkt.ip = (struct ip *) (pkt.pay.buf);
rx_ip(ifp, &pkt);
} else {
MG_DEBUG(("Unknown eth type %x", mg_htons(pkt.eth->type)));
@@ -5115,6 +5813,65 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
}
}

+static void mg_ip_poll(struct mg_tcpip_if *ifp, bool s1) {
+ if (ifp->state == MG_TCPIP_STATE_DOWN) return;
+ // DHCP RFC-2131 (4.4)
+ if (ifp->enable_dhcp_client && s1) {
+ if (ifp->state == MG_TCPIP_STATE_UP) {
+ tx_dhcp_discover(ifp); // INIT (4.4.1)
+ } else if (ifp->state == MG_TCPIP_STATE_READY &&
+ ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
+ if (ifp->now >= ifp->lease_expire) {
+ ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP
+ onstatechange(ifp);
+ } else if (ifp->now + 30UL * 60UL * 1000UL > ifp->lease_expire &&
+ ((ifp->now / 1000) % 60) == 0) {
+ // hack: 30 min before deadline, try to rebind (4.3.6) every min
+ tx_dhcp_request_re(ifp, (uint8_t *) broadcast, ifp->ip, 0xffffffff);
+ } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5)
+ }
+ }
+}
+static void mg_ip_link(struct mg_tcpip_if *ifp, bool up) {
+ bool current = ifp->state != MG_TCPIP_STATE_DOWN;
+ if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
+ if (up != current) { // link state has changed
+ ifp->state = up == false ? MG_TCPIP_STATE_DOWN
+ : ifp->enable_dhcp_client || ifp->ip == 0 ? MG_TCPIP_STATE_UP
+ : MG_TCPIP_STATE_IP;
+ onstatechange(ifp);
+ } else if (!ifp->enable_dhcp_client && ifp->state == MG_TCPIP_STATE_UP &&
+ ifp->ip) {
+ ifp->state = MG_TCPIP_STATE_IP; // ifp->fn has set an IP
+ onstatechange(ifp);
+ }
+}
+
+#if MG_ENABLE_IPV6
+static void mg_ip6_poll(struct mg_tcpip_if *ifp, bool s1) {
+ if (ifp->state6 == MG_TCPIP_STATE_DOWN) return;
+ if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
+ tx_ndp_rs(ifp, ifp->gw6, ifp->gw6mac);
+}
+static void mg_ip6_link(struct mg_tcpip_if *ifp, bool up) {
+ bool current = ifp->state6 != MG_TCPIP_STATE_DOWN;
+ if (!up && ifp->enable_slaac) ifp->ip6[0] = ifp->ip6[1] = 0;
+ if (up != current) { // link state has changed
+ ifp->state6 = !up ? MG_TCPIP_STATE_DOWN
+ : ifp->enable_slaac || ifp->ip6[0] == 0 ? MG_TCPIP_STATE_UP
+ : MG_TCPIP_STATE_IP;
+ onstate6change(ifp);
+ } else if (!ifp->enable_slaac && ifp->state6 == MG_TCPIP_STATE_UP &&
+ ifp->ip6[0]) {
+ ifp->state6 = MG_TCPIP_STATE_IP; // ifp->fn has set an IP
+ onstate6change(ifp);
+ }
+}
+#else
+#define mg_ip6_poll(x, y)
+#define mg_ip6_link(x, y)
+#endif
+
static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
struct mg_connection *c;
bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, now);
@@ -5123,57 +5880,48 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
if (expired_1000ms) {
#if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS
const char *names[] = {"down", "up", "req", "ip", "ready"};
- MG_INFO(("Status: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u",
- names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
- ifp->ndrop, ifp->nerr));
+ size_t max = sizeof(names) / sizeof(char *);
+ unsigned int state = ifp->state >= max ? max - 1 : ifp->state;
+ MG_INFO(("Status: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", names[state],
+ mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, ifp->ndrop,
+ ifp->nerr));
+#if MG_ENABLE_IPV6
+ state = ifp->state6 >= max ? max - 1 : ifp->state6;
+ if (state > MG_TCPIP_STATE_UP)
+ MG_INFO(("Status: %s, IPv6: %M", names[state], mg_print_ip6, &ifp->ip6));
+#endif
#endif
+ backlog_poll(ifp->mgr);
}
// Handle gw ARP request timeout, order is important
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC
onstatechange(ifp);
}
+#if MG_ENABLE_IPV6
+ // Handle gw NS/NA req/resp timeout, order is important
+ if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_IP) {
+ ifp->state6 = MG_TCPIP_STATE_READY; // keep best-effort MAC
+ onstate6change(ifp);
+ }
+#endif
+
// poll driver
if (ifp->driver->poll) {
bool up = ifp->driver->poll(ifp, expired_1000ms);
- // Handle physical interface up/down status
+ // Handle physical interface up/down status, ifp->state rules over state6
if (expired_1000ms) {
- bool current = ifp->state != MG_TCPIP_STATE_DOWN;
- if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
- if (up != current) { // link state has changed
- ifp->state = up == false ? MG_TCPIP_STATE_DOWN
- : ifp->enable_dhcp_client || ifp->ip == 0
- ? MG_TCPIP_STATE_UP
- : MG_TCPIP_STATE_IP;
- onstatechange(ifp);
- } else if (!ifp->enable_dhcp_client && ifp->state == MG_TCPIP_STATE_UP &&
- ifp->ip) {
- ifp->state = MG_TCPIP_STATE_IP; // ifp->fn has set an IP
- onstatechange(ifp);
- }
+ mg_ip_link(ifp, up); // Handle IPv4
+ mg_ip6_link(ifp, up); // Handle IPv6
if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down"));
mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL);
}
}
- if (ifp->state == MG_TCPIP_STATE_DOWN) return;

- // DHCP RFC-2131 (4.4)
- if (ifp->enable_dhcp_client && expired_1000ms) {
- if (ifp->state == MG_TCPIP_STATE_UP) {
- tx_dhcp_discover(ifp); // INIT (4.4.1)
- } else if (ifp->state == MG_TCPIP_STATE_READY &&
- ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
- if (ifp->now >= ifp->lease_expire) {
- ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP
- onstatechange(ifp);
- } else if (ifp->now + 30UL * 60UL * 1000UL > ifp->lease_expire &&
- ((ifp->now / 1000) % 60) == 0) {
- // hack: 30 min before deadline, try to rebind (4.3.6) every min
- tx_dhcp_request_re(ifp, (uint8_t *) broadcast, ifp->ip, 0xffffffff);
- } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5)
- }
- }
+ mg_ip_poll(ifp, expired_1000ms); // Handle IPv4
+ mg_ip6_poll(ifp, expired_1000ms); // Handle IPv6

+ if (ifp->state == MG_TCPIP_STATE_DOWN) return;
// Read data from the network
if (ifp->driver->rx != NULL) { // Simple polling driver, returns one frame
size_t len =
@@ -5194,10 +5942,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
// Process timeouts
for (c = ifp->mgr->conns; c != NULL; c = c->next) {
struct connstate *s = (struct connstate *) (c + 1);
- uint32_t rem_ip;
if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving)
continue;
- memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
if (ifp->now > s->timer) {
if (s->ttype == MIP_TTYPE_ARP) {
mg_error(c, "ARP timeout");
@@ -5205,8 +5951,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
continue;
} 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, rem_ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
+ tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq),
+ mg_htonl(s->ack), NULL, 0);
s->acked = s->ack;
} else if (s->ttype == MIP_TTYPE_SYN) {
mg_error(c, "Connection timeout");
@@ -5218,8 +5964,8 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
mg_error(c, "keepalive");
} else {
MG_VERBOSE(("%lu keepalive", c->id));
- tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0);
+ tx_tcp(ifp, s->mac, &c->loc, &c->rem, TH_ACK, mg_htonl(s->seq - 1),
+ mg_htonl(s->ack), NULL, 0);
}
}

@@ -5276,6 +6022,15 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
// MG_EPHEMERAL_PORT_BASE to 65535
if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM"));
+#if MG_ENABLE_IPV6
+ // If static configuration is used, link-local and global addresses,
+ // prefix length, and gw are already filled at this point.
+ if (ifp->ip6[0] == 0 && ifp->ip6[1] == 0) {
+ ifp->enable_slaac = true;
+ ip6genll((uint8_t *) ifp->ip6ll, ifp->mac); // build link-local address
+ }
+ memset(ifp->gw6mac, 255, sizeof(ifp->gw6mac)); // Set best-effort to bcast
+#endif
}
}

@@ -5288,10 +6043,7 @@ 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));
- uint32_t rem_ip;
- memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
- tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0,
- NULL, 0);
+ tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_SYN, isn, 0, NULL, 0);
}

static void mac_resolved(struct mg_connection *c) {
@@ -5313,35 +6065,72 @@ static void ip4_mcastmac(uint8_t *mac, uint32_t *ip) {

void mg_connect_resolved(struct mg_connection *c) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
- uint32_t rem_ip;
- memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
c->is_resolving = 0;
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
- memcpy(c->loc.ip, &ifp->ip, sizeof(uint32_t));
c->loc.port = mg_htons(ifp->eport++);
+#if MG_ENABLE_IPV6
+ if (c->rem.is_ip6) {
+ c->loc.ip6[0] = ifp->ip6[0], c->loc.ip6[1] = ifp->ip6[1],
+ c->loc.is_ip6 = true;
+ } else
+#endif
+ {
+ c->loc.ip4 = ifp->ip;
+ }
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
&c->rem));
mg_call(c, MG_EV_RESOLVE, NULL);
c->is_connecting = 1;
- if (c->is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) {
- struct connstate *s = (struct connstate *) (c + 1);
- memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast
- mac_resolved(c);
- } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) &&
- rem_ip != ifp->gw) { // skip if gw (onstatechange -> READY -> ARP)
- // If we're in the same LAN, 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 ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) {
- struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF
- ip4_mcastmac(s->mac, &rem_ip); // multicast group
- mac_resolved(c);
- } else {
- struct connstate *s = (struct connstate *) (c + 1);
- memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac));
- mac_resolved(c);
+#if MG_ENABLE_IPV6
+ if (c->rem.is_ip6) {
+ if (c->is_udp &&
+ MG_IP6MATCH(c->rem.ip6, ip6_allnodes.u)) { // local broadcast
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, ip6mac_allnodes, sizeof(s->mac));
+ mac_resolved(c);
+ } else if (c->rem.ip6[0] == ifp->ip6[0] &&
+ !MG_IP6MATCH(c->rem.ip6,
+ ifp->gw6)) { // skip if gw (onstate6change -> NS)
+ // If we're in the same LAN, fire a Neighbor Solicitation
+ MG_DEBUG(("%lu NS lookup...", c->id));
+ tx_ndp_ns(ifp, c->rem.ip6, ifp->mac);
+ settmout(c, MIP_TTYPE_ARP);
+ c->is_arplooking = 1;
+ } else if (*((uint8_t *) c->rem.ip6) == 0xFF) { // multicast
+ struct connstate *s = (struct connstate *) (c + 1);
+ ip6_mcastmac(s->mac, c->rem.ip6);
+ mac_resolved(c);
+ } else {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
+ mac_resolved(c);
+ }
+ } else
+#endif
+ {
+ uint32_t rem_ip = c->rem.ip4;
+ if (c->is_udp &&
+ (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast
+ mac_resolved(c);
+ } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) &&
+ rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
+ // If we're in the same LAN, 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 ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) {
+ struct connstate *s =
+ (struct connstate *) (c + 1); // 224 to 239, E0 to EF
+ ip4_mcastmac(s->mac, &rem_ip); // multicast group
+ mac_resolved(c);
+ } else {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac));
+ mac_resolved(c);
+ }
}
}

@@ -5357,6 +6146,7 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
static void write_conn(struct mg_connection *c) {
long 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
if (len == MG_IO_ERR) {
mg_error(c, "tx err");
} else if (len > 0) {
@@ -5369,10 +6159,8 @@ static void init_closure(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
if (c->is_udp == false && c->is_listening == false &&
c->is_connecting == false) { // For TCP conns,
- uint32_t rem_ip;
- memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
- tx_tcp(c->mgr->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);
+ tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, TH_FIN | TH_ACK,
+ mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
settmout(c, MIP_TTYPE_FIN);
}
}
@@ -5406,7 +6194,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
mg_tls_pending(c), c->rtls.len));
// order is important, TLS conn close with > 1 record in buffer (below)
if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0))
- handle_tls_recv(c);
+ c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
if (can_write(c)) write_conn(c);
if (is_tls && c->send.len == 0) mg_tls_flush(c);
if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN)
@@ -5424,27 +6212,29 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
bool res = false;
- uint32_t rem_ip;
- memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
- if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) {
+ if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
+ mg_error(c, "net down");
+#if MG_ENABLE_IPV6
+ } else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
mg_error(c, "net down");
+#endif
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
// Fail to send, no target MAC or IP
MG_VERBOSE(("still resolving..."));
} else if (c->is_udp) {
- struct connstate *s = (struct connstate *) (c + 1);
len = trim_len(c, len); // Trimming length if necessary
- res = tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf,
- len);
+ res = udp_send(c, buf, len);
} else {
- res = mg_iobuf_add(&c->send, c->send.len, buf, len);
+ res = len == 0 || mg_iobuf_add(&c->send, c->send.len, buf, len) > 0;
+ // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
+ // so far recoverable, let the caller decide
}
return res;
}

uint8_t mcast_addr[6] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb};
void mg_multicast_add(struct mg_connection *c, char *ip) {
- (void) ip; // ip4_mcastmac(mcast_mac, &ip);
+ (void) ip; // ip4/6_mcastmac(mcast_mac, &ip); ipv6 param
// TODO(): actual IP -> MAC; check database, update
c->mgr->ifp->update_mac_hash_table = true; // mark dirty
}
@@ -5460,7 +6250,7 @@ static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) {
MG_DEBUG(("Set DNS URL to %s", dnsc->url));
if (ifp->mgr->use_dns6) return;
if (dnsc->c != NULL) mg_close_conn(dnsc->c);
- if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
+ if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
MG_ERROR(("DNS connection creation failed"));
}

@@ -7632,24 +8422,19 @@ bool mg_ota_end(void) {



-size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) {
- size_t len = mg_snprintf(NULL, 0, fmt, ap);
- char *buf;
- if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) {
- len = 0; // Nah. Not enough space
- } else {
- len = mg_vsnprintf((char *) buf, len + 1, fmt, ap);
- mg_queue_add(q, len);
- }
- return len;
-}
-
size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) {
- va_list ap;
+ char *buf;
size_t len;
- va_start(ap, fmt);
- len = mg_queue_vprintf(q, fmt, &ap);
- va_end(ap);
+ va_list ap1, ap2;
+ va_start(ap1, fmt);
+ len = mg_vsnprintf(NULL, 0, fmt, &ap1);
+ va_end(ap1);
+ if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1)
+ return 0; // Nah. Not enough space
+ va_start(ap2, fmt);
+ len = mg_vsnprintf(buf, len + 1, fmt, &ap2);
+ mg_queue_add(q, len);
+ va_end(ap2);
return len;
}

@@ -8797,7 +9582,9 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
iolog(c, (char *) buf, n, false);
return n > 0;
} else {
- return mg_iobuf_add(&c->send, c->send.len, buf, len);
+ return len == 0 || mg_iobuf_add(&c->send, c->send.len, buf, len) > 0;
+ // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
+ // so far recoverable, let the caller decide
}
}

@@ -8819,7 +9606,7 @@ static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
#elif MG_ARCH == MG_ARCH_THREADX
// NetxDuo fails to send large blocks of data to the non-blocking sockets
(void) fd;
- //fcntl(fd, F_SETFL, O_NONBLOCK);
+ // fcntl(fd, F_SETFL, O_NONBLOCK);
#elif MG_ARCH == MG_ARCH_TIRTOS
int val = 0;
setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
@@ -8837,7 +9624,7 @@ static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
void mg_multicast_add(struct mg_connection *c, char *ip);
void mg_multicast_add(struct mg_connection *c, char *ip) {
#if MG_ENABLE_RL
-#error UNSUPPORTED
+ MG_ERROR(("unsupported"));
#elif MG_ENABLE_FREERTOS_TCP
// TODO(): prvAllowIPPacketIPv4()
#else
@@ -8851,9 +9638,10 @@ void mg_multicast_add(struct mg_connection *c, char *ip) {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(ip);
mreq.imr_interface.s_addr = mg_htonl(INADDR_ANY);
- setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq));
-#endif // !Zephyr
-#endif // !lwIP
+ setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq,
+ sizeof(mreq));
+#endif // !Zephyr
+#endif // !lwIP
#endif
}

@@ -8971,9 +9759,9 @@ static void read_conn(struct mg_connection *c) {
if (n == MG_IO_ERR || n == MG_IO_RESET) { // Windows, see #3031
if (c->rtls.len == 0 || m < 0) {
// Close only when we have fully drained both rtls and TLS buffers
- c->is_closing = 1; // or there's nothing we can do about it.
- if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104
- } else { // see #2885
+ c->is_closing = 1; // or there's nothing we can do about it.
+ if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104
+ } else { // see #2885
// TLS buffer is capped to max record size, even though, there can
// be more than one record, give TLS a chance to process them.
}
@@ -8994,6 +9782,7 @@ static void write_conn(struct mg_connection *c) {
char *buf = (char *) c->send.buf;
size_t len = c->send.len;
long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len);
+ // TODO(): mg_tls_send() may return 0 forever on steady OOM
MG_DEBUG(("%lu %ld snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd,
(long) c->send.len, (long) c->send.size, (long) c->recv.len,
(long) c->recv.size, n, MG_SOCK_ERR(n)));
@@ -9023,7 +9812,7 @@ static void connect_conn(struct mg_connection *c) {
mg_call(c, MG_EV_CONNECT, NULL);
MG_EPOLL_MOD(c, 0);
if (c->is_tls_hs) mg_tls_handshake(c);
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
+ if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
} else {
mg_error(c, "socket error");
}
@@ -9075,9 +9864,9 @@ void mg_connect_resolved(struct mg_connection *c) {
rc = connect(FD(c), &usa.sa, slen); // Attempt to connect
if (rc == 0) { // Success
setlocaddr(FD(c), &c->loc);
- mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
- } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
+ mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
+ if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
+ } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem));
c->is_connecting = 1;
} else {
@@ -9137,7 +9926,7 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
&c->rem, mg_print_ip_port, &c->loc));
mg_call(c, MG_EV_OPEN, NULL);
mg_call(c, MG_EV_ACCEPT, NULL);
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
+ if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
}
}

@@ -9453,31 +10242,35 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
if (depth < MG_MAX_SSI_DEPTH &&
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
- mg_iobuf_add(&b, b.len, data, strlen(data));
+ size_t datalen = strlen(data);
+ size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
mg_free(data);
+ if (datalen > 0 && ret == 0) goto fail;
} else {
MG_ERROR(("%s: file=%s error or too deep", path, arg));
- }
+ } // TODO(): or OOM at recursive call
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg) > 0) {
char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
if (depth < MG_MAX_SSI_DEPTH &&
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
- mg_iobuf_add(&b, b.len, data, strlen(data));
+ size_t datalen = strlen(data);
+ size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
mg_free(data);
+ if (datalen > 0 && ret == 0) goto fail;
} else {
MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
- }
+ } // TODO(): or OOM at recursive call
} else {
// Unknown SSI tag
MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
- mg_iobuf_add(&b, b.len, buf, len);
+ if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
}
intag = 0;
len = 0;
} else if (ch == '<') {
intag = 1;
- if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
+ if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
len = 0;
buf[len++] = (char) (ch & 0xff);
} else if (intag) {
@@ -9491,24 +10284,33 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
} else {
buf[len++] = (char) (ch & 0xff);
if (len >= sizeof(buf)) {
- mg_iobuf_add(&b, b.len, buf, len);
+ if (mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
len = 0;
}
}
}
- if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
- if (b.len > 0) mg_iobuf_add(&b, b.len, "", 1); // nul-terminate
+ if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
+ if (b.len > 0 && mg_iobuf_add(&b, b.len, "", 1) == 0) // nul-terminate
+ goto fail;
fclose(fp);
}
(void) depth;
(void) root;
return (char *) b.buf;
+
+fail:
+ fclose(fp);
+ return NULL;
}

void mg_http_serve_ssi(struct mg_connection *c, const char *root,
const char *fullpath) {
const char *headers = "Content-Type: text/html; charset=utf-8\r\n";
char *data = mg_ssi(fullpath, root, 0);
+ if (data == NULL) {
+ mg_error(c, "OOM");
+ return;
+ }
mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
mg_free(data);
}
@@ -10933,6 +11735,8 @@ enum mg_tls_hs_state {

// Server state machine:
MG_TLS_STATE_SERVER_START, // Wait for ClientHello
+ MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate
+ MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify
MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish
MG_TLS_STATE_SERVER_CONNECTED // Done
};
@@ -10968,20 +11772,22 @@ struct tls_data {
uint8_t x25519_cli[32]; // client X25519 key between the handshake states
uint8_t x25519_sec[32]; // x25519 secret between the handshake states

- int skip_verification; // perform checks on server certificate?
- int cert_requested; // client received a CertificateRequest?
+ bool skip_verification; // do not perform checks on server certificate
+ bool cert_requested; // client received a CertificateRequest
+ bool is_twoway; // server is configured to authenticate clients
struct mg_str cert_der; // certificate in DER format
struct mg_str ca_der; // CA certificate
uint8_t ec_key[32]; // EC private key
- char hostname[254]; // server hostname (client extension)
+ char hostname[254]; // matching hostname

- int is_ec_pubkey; // EC or RSA?
+ bool is_ec_pubkey; // EC or RSA
uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to
// verify cert
size_t pubkeysz; // size of the server public key
uint8_t sighash[32]; // calculated signature verification hash

- struct tls_enc enc;
+ struct tls_enc enc; // actual keys in use at this time
+ struct tls_enc app_keys; // storage during two-way auth handshake
};

#define TLS_RECHDR_SIZE 5 // 1 byte type, 2 bytes version, 2 bytes length
@@ -11286,7 +12092,7 @@ static void mg_tls_generate_application_keys(struct mg_connection *c) {
}

// AES GCM encryption of the message + put encoded data into the write buffer
-static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
+static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
size_t msgsz, uint8_t msgtype) {
struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *wio = &tls->send;
@@ -11306,6 +12112,11 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
uint8_t *iv =
c->is_client ? tls->enc.client_write_iv : tls->enc.server_write_iv;

+ if (msgsz > 16384) {
+ MG_ERROR(("msg longer than recordsz"));
+ return false;
+ }
+
#if MG_ENABLE_CHACHA20
#else
mg_gcm_initialize();
@@ -11317,8 +12128,9 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
nonce[10] ^= (uint8_t) ((seq >> 8) & 255U);
nonce[11] ^= (uint8_t) ((seq) & 255U);

- mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr));
- mg_iobuf_resize(wio, wio->len + encsz);
+ if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 ||
+ !mg_iobuf_resize(wio, wio->len + encsz))
+ return false;
outmsg = wio->buf + wio->len;
tag = wio->buf + wio->len + msgsz + 1;
memmove(outmsg, msg, msgsz);
@@ -11326,18 +12138,14 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
#if MG_ENABLE_CHACHA20
(void) tag; // tag is only used in aes gcm
{
- size_t maxlen = MG_IO_SIZE > 16384 ? 16384 : MG_IO_SIZE;
- uint8_t *enc = (uint8_t *) mg_calloc(1, maxlen + 256 + 1);
- if (enc == NULL) {
- mg_error(c, "TLS OOM");
- return;
- } else {
- size_t n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data,
- sizeof(associated_data), outmsg,
- msgsz + 1);
- memmove(outmsg, enc, n);
- mg_free(enc);
- }
+ size_t n;
+ uint8_t *enc = (uint8_t *) mg_calloc(1, msgsz + 256 + 1);
+ if (enc == NULL) return false;
+ n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data,
+ sizeof(associated_data), outmsg,
+ msgsz + 1);
+ memmove(outmsg, enc, n);
+ mg_free(enc);
}
#else
mg_aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, key, 16, nonce, sizeof(nonce),
@@ -11345,6 +12153,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
#endif
c->is_client ? tls->enc.cseq++ : tls->enc.sseq++;
wio->len += encsz;
+ return true;
}

// read an encrypted record, decrypt it in place
@@ -11371,8 +12180,7 @@ static int mg_tls_recv_record(struct mg_connection *c) {
}
if (rio->buf[0] == MG_TLS_APP_DATA) {
break;
- } else if (rio->buf[0] ==
- MG_TLS_CHANGE_CIPHER) { // Skip ChangeCipher messages
+ } else if (rio->buf[0] == MG_TLS_CHANGE_CIPHER) { // skip CCS
mg_tls_drop_record(c);
} else if (rio->buf[0] == MG_TLS_ALERT) { // Skip Alerts
MG_INFO(("TLS ALERT packet received"));
@@ -11404,6 +12212,10 @@ static int mg_tls_recv_record(struct mg_connection *c) {
return -1;
}
n = mg_chacha20_poly1305_decrypt(dec, key, nonce, msg, msgsz);
+ if (n == (size_t) -1) {
+ mg_error(c, "decryption error");
+ return -1;
+ }
memmove(msg, dec, n);
mg_free(dec);
}
@@ -11421,18 +12233,18 @@ static int mg_tls_recv_record(struct mg_connection *c) {
}

static void mg_tls_calc_cert_verify_hash(struct mg_connection *c,
- uint8_t hash[32], int is_client) {
+ uint8_t hash[32], bool is_client) {
struct tls_data *tls = (struct tls_data *) c->tls;
- uint8_t server_context[34] = "TLS 1.3, server CertificateVerify";
- uint8_t client_context[34] = "TLS 1.3, client CertificateVerify";
uint8_t sig_content[130];
mg_sha256_ctx sha256;

memset(sig_content, 0x20, 64);
if (is_client) {
- memmove(sig_content + 64, client_context, sizeof(client_context));
+ uint8_t client_context[34] = "TLS 1.3, client CertificateVerify";
+ memcpy(sig_content + 64, client_context, sizeof(client_context));
} else {
- memmove(sig_content + 64, server_context, sizeof(server_context));
+ uint8_t server_context[34] = "TLS 1.3, server CertificateVerify";
+ memcpy(sig_content + 64, server_context, sizeof(server_context));
}

memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
@@ -11517,7 +12329,7 @@ fail:
#define PLACEHOLDER_32B PLACEHOLDER_16B, PLACEHOLDER_16B

// put ServerHello record into wio buffer
-static void mg_tls_server_send_hello(struct mg_connection *c) {
+static bool mg_tls_server_send_hello(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *wio = &tls->send;

@@ -11558,33 +12370,58 @@ static void mg_tls_server_send_hello(struct mg_connection *c) {
memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub));

// server hello message
- mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5);
- mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello));
+ if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 ||
+ mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) ==
+ 0)
+ return false;
mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello));

// change cipher message
- mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6);
+ if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0)
+ return false;
+ return true;
}

-static void mg_tls_server_send_ext(struct mg_connection *c) {
+static bool mg_tls_server_send_ext(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
// server extensions
uint8_t ext[6] = {0x08, 0, 0, 2, 0, 0};
mg_sha256_update(&tls->sha256, ext, sizeof(ext));
- mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
+ return mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
}

-static void mg_tls_server_send_cert(struct mg_connection *c) {
+// signature algorithms we actually support:
+// rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256
+static const uint8_t secp256r1_sig_algs[12] = {
+ 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01
+};
+
+static bool mg_tls_server_send_cert_request(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ uint8_t req[13 + sizeof(secp256r1_sig_algs)];
+ req[0] = MG_TLS_CERTIFICATE_REQUEST; // handshake header
+ MG_STORE_BE24(req + 1, 9 + sizeof(secp256r1_sig_algs));
+ req[4] = 0; // context length
+ MG_STORE_BE16(req + 5, 6 + sizeof(secp256r1_sig_algs)); // extensions length
+ MG_STORE_BE16(req + 7, 13); // "signature algorithms"
+ MG_STORE_BE16(req + 9, 2 + sizeof(secp256r1_sig_algs)); // length
+ MG_STORE_BE16(
+ req + 11,
+ sizeof(secp256r1_sig_algs)); // signature hash algorithms length
+ memcpy(req + 13, (uint8_t *) secp256r1_sig_algs, sizeof(secp256r1_sig_algs));
+ mg_sha256_update(&tls->sha256, req, sizeof(req));
+ return mg_tls_encrypt(c, req, sizeof(req), MG_TLS_HANDSHAKE);
+}
+
+static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) {
struct tls_data *tls = (struct tls_data *) c->tls;
- int send_ca = !c->is_client && tls->ca_der.len > 0;
- // server DER certificate + CA (optional)
+ int send_ca = !is_client && tls->ca_der.len > 0;
+ // DER certificate + CA (server optional)
size_t n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0);
uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + n);
- if (cert == NULL) {
- mg_error(c, "tls cert oom");
- return;
- }
- cert[0] = 0x0b; // handshake header
+ bool res;
+ if (cert == NULL) return false;
+ cert[0] = MG_TLS_CERTIFICATE; // handshake header
MG_STORE_BE24(cert + 1, n + 9);
cert[4] = 0; // request context
MG_STORE_BE24(cert + 5, n + 5); // 3 bytes: cert (s) length
@@ -11601,8 +12438,9 @@ static void mg_tls_server_send_cert(struct mg_connection *c) {
MG_STORE_BE16(cert + 11 + n, 0); // certificate extensions (none)
}
mg_sha256_update(&tls->sha256, cert, 13 + n);
- mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
+ res = mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
mg_free(cert);
+ return res;
}

// type adapter between uECC hash context and our sha256 implementation
@@ -11627,7 +12465,7 @@ static void finish_SHA256(const MG_UECC_HashContext *base,
mg_sha256_final(hash_result, &c->ctx);
}

-static void mg_tls_send_cert_verify(struct mg_connection *c, int is_client) {
+static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
struct tls_data *tls = (struct tls_data *) c->tls;
// server certificate verify packet
uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
@@ -11661,10 +12499,10 @@ static void mg_tls_send_cert_verify(struct mg_connection *c, int is_client) {
verify[7] = (uint8_t) sigsz;

mg_sha256_update(&tls->sha256, verify, verifysz);
- mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
+ return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
}

-static void mg_tls_server_send_finish(struct mg_connection *c) {
+static bool mg_tls_server_send_finish(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
mg_sha256_ctx sha256;
uint8_t hash[32];
@@ -11672,8 +12510,10 @@ static void mg_tls_server_send_finish(struct mg_connection *c) {
memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
mg_sha256_final(hash, &sha256);
mg_hmac_sha256(finish + 4, tls->enc.server_finished_key, 32, hash, 32);
- mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
+ if (!mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE))
+ return false;
mg_sha256_update(&tls->sha256, finish, sizeof(finish));
+ return true;
}

static int mg_tls_server_recv_finish(struct mg_connection *c) {
@@ -11698,18 +12538,15 @@ static int mg_tls_server_recv_finish(struct mg_connection *c) {
return 0;
}

-static void mg_tls_client_send_hello(struct mg_connection *c) {
+static bool mg_tls_client_send_hello(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *wio = &tls->send;

uint8_t x25519_pub[X25519_BYTES];

- // signature algorithms we actually support:
- // rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256
- uint8_t secp256r1_sig_algs[12] = {
- 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01,
- };
- // all popular signature algorithms (if we don't care about verification)
+ // - "signature algorithms we actually support", see above
+ // uint8_t secp256r1_sig_algs[]
+ // - all popular signature algorithms (if we don't care about verification)
uint8_t all_sig_algs[34] = {
0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03,
0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04,
@@ -11753,7 +12590,8 @@ static void mg_tls_client_send_hello(struct mg_connection *c) {
const char *hostname = tls->hostname;
size_t hostnamesz = strlen(tls->hostname);
size_t hostname_extsz = hostnamesz ? hostnamesz + 9 : 0;
- uint8_t *sig_alg = tls->skip_verification ? all_sig_algs : secp256r1_sig_algs;
+ uint8_t *sig_alg =
+ tls->skip_verification ? all_sig_algs : (uint8_t *) secp256r1_sig_algs;
size_t sig_alg_sz = tls->skip_verification ? sizeof(all_sig_algs)
: sizeof(secp256r1_sig_algs);

@@ -11783,20 +12621,27 @@ static void mg_tls_client_send_hello(struct mg_connection *c) {
memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub));

// client hello message
- mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello));
+ if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) ==
+ 0)
+ return false;
mg_sha256_update(&tls->sha256, msg_client_hello + 5,
sizeof(msg_client_hello) - 5);
- mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz);
+ if (mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz) == 0) return false;
mg_sha256_update(&tls->sha256, sig_alg, sig_alg_sz);
if (hostnamesz > 0) {
- mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext));
- mg_iobuf_add(wio, wio->len, hostname, hostnamesz);
+ if (mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext)) ==
+ 0 ||
+ mg_iobuf_add(wio, wio->len, hostname, hostnamesz) == 0)
+ return false;
mg_sha256_update(&tls->sha256, server_name_ext, sizeof(server_name_ext));
mg_sha256_update(&tls->sha256, (uint8_t *) hostname, hostnamesz);
}

// change cipher message
- mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01", 6);
+ if (mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01",
+ 6) == 0)
+ return false;
+ return true;
}

static int mg_tls_client_recv_hello(struct mg_connection *c) {
@@ -12045,7 +12890,8 @@ static int mg_tls_parse_cert_der(void *buf, size_t dersz,
}

static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
- const char *server_name) {
+ const char *server_name,
+ struct mg_addr *server_ip) {
struct mg_der_tlv root, field, name;
if (mg_der_parse((uint8_t *) der, dersz, &root) < 0) {
MG_ERROR(("failed to parse certificate"));
@@ -12060,12 +12906,16 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
return -1;
}
while (mg_der_next(&field, &name) > 0) {
- MG_DEBUG(("Found SAN: %.*s", name.len, name.value));
- if (mg_match(mg_str(server_name), mg_str_n((char *) name.value, name.len),
- NULL)) {
- // Found SAN that matches the host name
- return 1;
- }
+ if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address
+ MG_DEBUG(("Found SAN, IP: %M", mg_print_ip4, name.value));
+ if (!server_ip->is_ip6 && *((uint32_t *) name.value) == server_ip->ip4)
+ return 1; // and matches the one we're connected to
+ } else { // this is a text SAN
+ MG_DEBUG(("Found SAN, (%u): %.*s", name.type, name.len, name.value));
+ if (mg_match(mg_str(server_name), mg_str_n((char *) name.value, name.len),
+ NULL))
+ return 1; // and matches the host name
+ } // TODO(): add IPv6 comparison, more items ?
}
return -1;
}
@@ -12128,7 +12978,7 @@ static int mg_tls_verify_cert_cn(struct mg_der_tlv *subj, const char *host) {
return matched;
}

-static int mg_tls_client_recv_cert(struct mg_connection *c) {
+static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
struct tls_data *tls = (struct tls_data *) c->tls;
unsigned char *recv_buf;

@@ -12146,7 +12996,8 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) {
}

if (recv_buf[0] != MG_TLS_CERTIFICATE) {
- mg_error(c, "expected server certificate but got msg 0x%02x", recv_buf[0]);
+ mg_error(c, "expected %s certificate but got msg 0x%02x",
+ is_client ? "server" : "client", recv_buf[0]);
return -1;
}

@@ -12156,7 +13007,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) {
}

{
- // Normally, there are 2-3 certs in a chain
+ // Normally, there are 2-3 certs in a chain (when is_client)
struct mg_tls_cert certs[8];
int certnum = 0;
uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1);
@@ -12201,9 +13052,10 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) {
}

if (ci == certs) {
- // First certificate in the chain is peer cert, check SAN and store
- // public key for further CertVerify step
- if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0 &&
+ // First certificate in the chain is peer cert, check SAN if requested,
+ // and store public key for further CertVerify step
+ if (tls->hostname[0] != '\0' &&
+ mg_tls_verify_cert_san(cert, certsz, tls->hostname, &c->rem) <= 0 &&
mg_tls_verify_cert_cn(&ci->subj, tls->hostname) <= 0) {
mg_error(c, "failed to verify hostname");
return -1;
@@ -12234,7 +13086,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) {
!mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) {
mg_error(c, "failed to verify CA");
return -1;
- } else {
+ } else if (is_client) {
MG_VERBOSE(
("CA was not in the chain, but verification with builtin CA "
"passed"));
@@ -12242,11 +13094,11 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) {
}
}
mg_tls_drop_message(c);
- mg_tls_calc_cert_verify_hash(c, tls->sighash, 0);
+ mg_tls_calc_cert_verify_hash(c, tls->sighash, !is_client);
return 0;
}

-static int mg_tls_client_recv_cert_verify(struct mg_connection *c) {
+static int mg_tls_recv_cert_verify(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
unsigned char *recv_buf;
if (mg_tls_recv_record(c) < 0) {
@@ -12254,8 +13106,8 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) {
}
recv_buf = &c->rtls.buf[tls->recv_offset];
if (recv_buf[0] != MG_TLS_CERTIFICATE_VERIFY) {
- mg_error(c, "expected server certificate verify but got msg 0x%02x",
- recv_buf[0]);
+ mg_error(c, "expected %s certificate verify but got msg 0x%02x",
+ c->is_client ? "server" : "client", recv_buf[0]);
return -1;
}
if (tls->recv_len < 8) {
@@ -12264,7 +13116,7 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) {
return -1;
}

- // Ignore CertificateVerify is strict checks are not required
+ // Ignore CertificateVerify if strict checks are not required
if (tls->skip_verification) {
mg_tls_drop_message(c);
return 0;
@@ -12364,7 +13216,7 @@ static int mg_tls_client_recv_finish(struct mg_connection *c) {
return 0;
}

-static void mg_tls_client_send_finish(struct mg_connection *c) {
+static bool mg_tls_client_send_finish(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
mg_sha256_ctx sha256;
uint8_t hash[32];
@@ -12372,104 +13224,119 @@ static void mg_tls_client_send_finish(struct mg_connection *c) {
memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
mg_sha256_final(hash, &sha256);
mg_hmac_sha256(finish + 4, tls->enc.client_finished_key, 32, hash, 32);
- mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
+ return mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
}

-static void mg_tls_client_handshake(struct mg_connection *c) {
+static bool mg_tls_client_handshake(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
switch (tls->state) {
case MG_TLS_STATE_CLIENT_START:
- mg_tls_client_send_hello(c);
+ if (!mg_tls_client_send_hello(c)) return false;
tls->state = MG_TLS_STATE_CLIENT_WAIT_SH;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_SH:
- if (mg_tls_client_recv_hello(c) < 0) {
- break;
- }
+ if (mg_tls_client_recv_hello(c) < 0) break;
tls->state = MG_TLS_STATE_CLIENT_WAIT_EE;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_EE:
- if (mg_tls_client_recv_ext(c) < 0) {
- break;
- }
+ if (mg_tls_client_recv_ext(c) < 0) break;
tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_CERT:
- if (mg_tls_client_recv_cert(c) < 0) {
- break;
- }
+ if (mg_tls_recv_cert(c, true) < 0) break;
tls->state = MG_TLS_STATE_CLIENT_WAIT_CV;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_CV:
- if (mg_tls_client_recv_cert_verify(c) < 0) {
- break;
- }
+ if (mg_tls_recv_cert_verify(c) < 0) break;
tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISH;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_FINISH:
- if (mg_tls_client_recv_finish(c) < 0) {
- break;
- }
- if (tls->cert_requested) {
- /* for mTLS we should generate application keys at this point
- * but then restore handshake keys and continue with
- * the rest of the handshake */
- struct tls_enc app_keys;
+ if (mg_tls_client_recv_finish(c) < 0) break;
+ if (tls->cert_requested && tls->cert_der.len > 0) { // two-way auth
+ // generate application keys at this point, keep using handshake keys
struct tls_enc hs_keys = tls->enc;
mg_tls_generate_application_keys(c);
- app_keys = tls->enc;
+ tls->app_keys = tls->enc;
tls->enc = hs_keys;
- mg_tls_server_send_cert(c);
- mg_tls_send_cert_verify(c, 1);
- mg_tls_client_send_finish(c);
- tls->enc = app_keys;
+ if (!mg_tls_send_cert(c, true) || !mg_tls_send_cert_verify(c, true) ||
+ !mg_tls_client_send_finish(c))
+ return false;
+ tls->enc = tls->app_keys;
} else {
- mg_tls_client_send_finish(c);
+ if (!mg_tls_client_send_finish(c)) return false;
mg_tls_generate_application_keys(c);
}
tls->state = MG_TLS_STATE_CLIENT_CONNECTED;
c->is_tls_hs = 0;
mg_call(c, MG_EV_TLS_HS, NULL);
break;
- default: mg_error(c, "unexpected client state: %d", tls->state); break;
+ default:
+ mg_error(c, "unexpected client state: %d", tls->state);
+ break;
}
+ return true;
}

-static void mg_tls_server_handshake(struct mg_connection *c) {
+static bool mg_tls_server_handshake(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
switch (tls->state) {
case MG_TLS_STATE_SERVER_START:
- if (mg_tls_server_recv_hello(c) < 0) {
- return;
- }
- mg_tls_server_send_hello(c);
+ if (mg_tls_server_recv_hello(c) < 0) break;
+ if (!mg_tls_server_send_hello(c)) return false;
mg_tls_generate_handshake_keys(c);
- mg_tls_server_send_ext(c);
- mg_tls_server_send_cert(c);
- mg_tls_send_cert_verify(c, 0);
- mg_tls_server_send_finish(c);
+ if (!mg_tls_server_send_ext(c)) return false;
+ if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false;
+ if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) || !mg_tls_server_send_finish(c)) return false;
+ if (tls->is_twoway) {
+ // generate application keys at this point, keep using handshake keys
+ struct tls_enc hs_keys = tls->enc;
+ mg_tls_generate_application_keys(c);
+ tls->app_keys = tls->enc;
+ tls->enc = hs_keys;
+ tls->state = MG_TLS_STATE_SERVER_WAIT_CERT;
+ break;
+ }
tls->state = MG_TLS_STATE_SERVER_NEGOTIATED;
// fallthrough
case MG_TLS_STATE_SERVER_NEGOTIATED:
- if (mg_tls_server_recv_finish(c) < 0) {
- return;
+ if (mg_tls_server_recv_finish(c) < 0) break;
+ if (tls->is_twoway) { // use previously generated keys
+ tls->enc = tls->app_keys;
+ } else { // generate keys now
+ mg_tls_generate_application_keys(c);
}
- mg_tls_generate_application_keys(c);
tls->state = MG_TLS_STATE_SERVER_CONNECTED;
c->is_tls_hs = 0;
- return;
- default: mg_error(c, "unexpected server state: %d", tls->state); break;
+ break;
+ case MG_TLS_STATE_SERVER_WAIT_CERT:
+ if (mg_tls_recv_cert(c, false) < 0) break;
+ tls->state = MG_TLS_STATE_SERVER_WAIT_CV;
+ // Fallthrough
+ case MG_TLS_STATE_SERVER_WAIT_CV:
+ if (mg_tls_recv_cert_verify(c) < 0) break;
+ tls->state = MG_TLS_STATE_SERVER_NEGOTIATED;
+ break;
+ default:
+ mg_error(c, "unexpected server state: %d", tls->state);
+ break;
}
+ return true;
}

void mg_tls_handshake(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
long n;
+ bool res;
+ if (c->is_closing) return; // we don't clear rx buf, so ignore what's left
if (c->is_client) {
// will clear is_hs when sending last chunk
- mg_tls_client_handshake(c);
+ res = mg_tls_client_handshake(c);
} else {
- mg_tls_server_handshake(c);
+ res = mg_tls_server_handshake(c);
+ }
+ if (!res) {
+ mg_error(c, "TLS OOM");
+ return;
}
while (tls->send.len > 0 &&
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
@@ -12545,6 +13412,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
MG_ERROR(("Failed to load certificate"));
return;
}
+ if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth
}

if (opts->cert.buf == NULL) {
@@ -12605,8 +13473,10 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
if (!was_throttled) { // encrypt new data
if (len > MG_IO_SIZE) len = MG_IO_SIZE;
if (len > 16384) len = 16384;
- mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA);
- } // else, resend outstanding encrypted data in tls->send
+ if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA))
+ return 0; // returning 0 means an OOM condition (iobuf couldn't resize),
+ // yet this is so far recoverable, let the caller decide
+ } // else, resend outstanding encrypted data in tls->send
while (tls->send.len > 0 &&
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
mg_iobuf_del(&tls->send, 0, (size_t) n);
@@ -14117,6 +14987,10 @@ void mg_tls_free(struct mg_connection *c) {
mbedtls_ssl_config_free(&tls->conf);
#ifdef MBEDTLS_SSL_SESSION_TICKETS
mbedtls_ssl_ticket_free(&tls->ticket);
+#endif
+#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \
+ defined(MBEDTLS_PSA_CRYPTO_C)
+ mbedtls_psa_crypto_free(); // https://github.com/Mbed-TLS/mbedtls/issues/9223#issuecomment-2144898336
#endif
mg_free(tls);
c->tls = NULL;
@@ -14360,6 +15234,7 @@ static STACK_OF(X509_INFO) * load_ca_certs(struct mg_str ca) {
static bool add_ca_certs(SSL_CTX *ctx, STACK_OF(X509_INFO) * certs) {
int i;
X509_STORE *cert_store = SSL_CTX_get_cert_store(ctx);
+ if (cert_store == NULL) return false;
for (i = 0; i < sk_X509_INFO_num(certs); i++) {
X509_INFO *cert_info = sk_X509_INFO_value(certs, i);
if (cert_info->x509 && !X509_STORE_add_cert(cert_store, cert_info->x509))
@@ -19994,6 +20869,10 @@ uint32_t mg_ntohl(uint32_t net) {
return MG_LOAD_BE32(&net);
}

+uint64_t mg_ntohll(uint64_t net) {
+ return MG_LOAD_BE64(&net);
+}
+
void mg_delayms(unsigned int ms) {
uint64_t to = mg_millis() + ms + 1;
while (mg_millis() < to) (void) 0;
@@ -20018,7 +20897,8 @@ void mg_free(void *ptr) {

#if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) && \
(!defined(MG_ENABLE_DRIVER_CYW) || !MG_ENABLE_DRIVER_CYW) && \
- (!defined(MG_ENABLE_DRIVER_CYW_SDIO) || !MG_ENABLE_DRIVER_CYW_SDIO)
+ (!defined(MG_ENABLE_DRIVER_CYW_SDIO) || !MG_ENABLE_DRIVER_CYW_SDIO) && \
+ (!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI)


bool mg_wifi_scan(void) {
@@ -20026,9 +20906,8 @@ bool mg_wifi_scan(void) {
return false;
}

-bool mg_wifi_connect(char *ssid, char *pass) {
- (void) ssid;
- (void) pass;
+bool mg_wifi_connect(struct mg_wifi_data *wifi) {
+ (void) wifi;
return mg_wifi_scan();
}

@@ -20036,10 +20915,8 @@ bool mg_wifi_disconnect(void) {
return mg_wifi_scan();
}

-bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
- (void) ssid;
- (void) pass;
- (void) channel;
+bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
+ (void) wifi;
return mg_wifi_scan();
}

@@ -20109,7 +20986,7 @@ static void ws_handshake(struct mg_connection *c, const struct mg_str *wskey,
mg_printf(c, "Sec-WebSocket-Protocol: %.*s\r\n", (int) wsproto->len,
wsproto->buf);
}
- mg_send(c, "\r\n", 2);
+ if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
}

static uint32_t be32(const uint8_t *p) {
@@ -20346,8 +21223,8 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
memmove(p, p - header_len, len); // Shift data
memcpy(p - header_len, header, header_len); // Prepend header
mg_ws_mask(c, len); // Mask data
- }
- return c->send.len;
+ } // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
+ return c->send.len; // so far recoverable, let the caller decide
}

#ifdef MG_ENABLE_LINES
@@ -20500,28 +21377,42 @@ static size_t cmsis_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
#endif

static struct mg_tcpip_if *s_ifp;
+static uint32_t s_ip, s_mask;
static bool s_link, s_auth, s_join;

+static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
+ struct mg_wifi_data *wifi = &((struct mg_tcpip_driver_cyw_data *) ifp->driver_data)->wifi;
+ if (wifi->apmode && ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
+ MG_DEBUG(("Access Point started"));
+ s_ip = ifp->ip, ifp->ip = wifi->apip;
+ s_mask = ifp->mask, ifp->mask = wifi->apmask;
+ ifp->enable_dhcp_client = false;
+ ifp->enable_dhcp_server = true;
+ }
+}
+
static bool cyw_init(uint8_t *mac);
static void cyw_poll(void);

static bool mg_tcpip_driver_cyw_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_cyw_data *d =
(struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
+ struct mg_wifi_data *wifi = &d->wifi;
if (MG_BIG_ENDIAN) {
MG_ERROR(("Big-endian host"));
return false;
}
s_ifp = ifp;
+ s_ip = ifp->ip;
+ s_mask = ifp->mask;
s_link = s_auth = s_join = false;
+ ifp->pfn = wifi_cb;
if (!cyw_init(ifp->mac)) return false;

- if (d->apmode) {
- MG_DEBUG(("Starting AP '%s' (%u)", d->apssid, d->apchannel));
- return mg_wifi_ap_start(d->apssid, d->appass, d->apchannel);
- } else if (d->ssid != NULL && d->pass != NULL) {
- MG_DEBUG(("Connecting to '%s'", d->ssid));
- return mg_wifi_connect(d->ssid, d->pass);
+ if (wifi->apmode) {
+ return mg_wifi_ap_start(wifi);
+ } else if (wifi->ssid != NULL && wifi->pass != NULL) {
+ return mg_wifi_connect(wifi);
}
return true;
}
@@ -20531,7 +21422,7 @@ size_t mg_tcpip_driver_cyw_output(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_cyw_data *d =
(struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
- return mg_cyw_tx(d->apmode ? 1 : 0, (void *) buf, len) >= len ? len : 0;
+ return mg_cyw_tx(d->wifi.apmode ? 1 : 0, (void *) buf, len) >= len ? len : 0;
}

static bool mg_tcpip_driver_cyw_poll(struct mg_tcpip_if *ifp, bool s1) {
@@ -20539,7 +21430,7 @@ static bool mg_tcpip_driver_cyw_poll(struct mg_tcpip_if *ifp, bool s1) {
if (!s1) return false;
struct mg_tcpip_driver_cyw_data *d =
(struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
- return d->apmode ? s_link : s_link && s_auth && s_join;
+ return d->wifi.apmode ? s_link : s_link && s_auth && s_join;
}

struct mg_tcpip_driver mg_tcpip_driver_cyw = {mg_tcpip_driver_cyw_init,
@@ -21522,7 +22413,7 @@ static size_t cyw_spi_poll(uint8_t *response) {
cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INT, &val, sizeof(val));
return 0;
}
- cyw_spi_read(CYW_SPID_FUNC_WLAN, 0, response, len);
+ cyw_spi_read(CYW_SPID_FUNC_WLAN, 0, response, (uint16_t)len);
return len;
}

@@ -21585,7 +22476,7 @@ static bool cyw_spi_init() {
cyw_set_backplane_window(CYW_CHIP_CHIPCOMMON); // set backplane window to start of CHIPCOMMON area
cyw_spi_read(CYW_SPID_FUNC_CHIP, (CYW_CHIP_CHIPCOMMON + 0x00) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 2);
if (val == 43430) val = 4343;
- MG_INFO(("WLAN chip is CYW%u%c", val), val == 4343 ? 'W' : ' '));
+ MG_INFO(("WLAN chip is CYW%u%c", val, val == 4343 ? 'W' : ' '));

// Load firmware (code and NVRAM)
if (!cyw_load_firmware(d->fw)) return false;
@@ -21863,16 +22754,22 @@ bool mg_wifi_scan(void) {
return cyw_wifi_scan();
}

-bool mg_wifi_connect(char *ssid, char *pass) {
- return cyw_wifi_connect(ssid, pass);
+bool mg_wifi_connect(struct mg_wifi_data *wifi) {
+ s_ifp->ip = s_ip;
+ s_ifp->mask = s_mask;
+ if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
+ s_ifp->enable_dhcp_server = false;
+ MG_DEBUG(("Connecting to '%s'", wifi->ssid));
+ return cyw_wifi_connect(wifi->ssid, wifi->pass);
}

bool mg_wifi_disconnect(void) {
return cyw_wifi_disconnect();
}

-bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
- return cyw_wifi_ap_start(ssid, pass, channel);
+bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
+ MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
+ return cyw_wifi_ap_start(wifi->apssid, wifi->appass, wifi->apchannel);
}

bool mg_wifi_ap_stop(void) {
@@ -21888,7 +22785,8 @@ bool mg_wifi_ap_stop(void) {

#if MG_ENABLE_TCPIP && \
(defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10) || \
- (defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11)
+ (defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11) || \
+ (defined(MG_ENABLE_DRIVER_MCXE) && MG_ENABLE_DRIVER_MCXE)
struct imxrt_enet {
volatile uint32_t RESERVED0, EIR, EIMR, RESERVED1, RDAR, TDAR, RESERVED2[3],
ECR, RESERVED3[6], MMFR, MSCR, RESERVED4[7], MIBC, RESERVED5[7], RCR,
@@ -21917,9 +22815,12 @@ struct imxrt_enet {
#if defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11
#define ENET ((struct imxrt_enet *) (uintptr_t) 0x40424000U)
#define ETH_DESC_CNT 5 // Descriptors count
-#else
+#elif defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10
#define ENET ((struct imxrt_enet *) (uintptr_t) 0x402D8000U)
#define ETH_DESC_CNT 4 // Descriptors count
+#else // MG_ENABLE_DRIVER_MCXE
+#define ENET ((struct imxrt_enet *) (uintptr_t) 0x40079000U)
+#define ETH_DESC_CNT 4 // Descriptor count
#endif

#define ETH_PKT_SIZE 1536 // Max frame size, 64-bit aligned
@@ -21930,15 +22831,12 @@ struct enet_desc {
uint32_t *buffer; // Data ptr
};

-// TODO(): handle these in a portable compiler-independent CMSIS-friendly way
-#define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
-
// Descriptors: in non-cached area (TODO(scaprile)), (37.5.1.22.2 37.5.1.23.2)
// Buffers: 64-byte aligned (37.3.14)
-static volatile struct enet_desc s_rxdesc[ETH_DESC_CNT] MG_64BYTE_ALIGNED;
-static volatile struct enet_desc s_txdesc[ETH_DESC_CNT] MG_64BYTE_ALIGNED;
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
+static volatile struct enet_desc s_rxdesc[ETH_DESC_CNT] MG_ETH_RAM MG_64BYTE_ALIGNED;
+static volatile struct enet_desc s_txdesc[ETH_DESC_CNT] MG_ETH_RAM MG_64BYTE_ALIGNED;
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_64BYTE_ALIGNED;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_64BYTE_ALIGNED;
static struct mg_tcpip_if *s_ifp; // MIP interface

static uint16_t enet_read_phy(uint8_t addr, uint8_t reg) {
@@ -22075,9 +22973,14 @@ static bool mg_tcpip_driver_imxrt_poll(struct mg_tcpip_if *ifp, bool s1) {
return up;
}

-void ENET_IRQHandler(void);
static uint32_t s_rxno;
+#if !defined(MG_ENABLE_DRIVER_MCXE)
+void ENET_IRQHandler(void);
void ENET_IRQHandler(void) {
+#else
+void ENET_Receive_IRQHandler(void);
+void ENET_Receive_IRQHandler(void) {
+#endif
ENET->EIR = MG_BIT(25); // Ack IRQ
// Frame received, loop
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
@@ -22102,6 +23005,101 @@ struct mg_tcpip_driver mg_tcpip_driver_imxrt = {mg_tcpip_driver_imxrt_init,

#endif

+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/nxp_wifi.c"
+#endif
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_NXP_WIFI) && \
+ MG_ENABLE_DRIVER_NXP_WIFI
+
+
+
+bool __attribute__((weak)) netif_init(struct mg_tcpip_if *ifp) {
+ (void) ifp;
+ MG_ERROR(("Please link wifi/port/net contents"));
+ return false;
+}
+size_t __attribute__((weak))
+netif_tx(const void *bfr, size_t len, struct mg_tcpip_if *ifp) {
+ (void) bfr;
+ (void) len;
+ netif_init(ifp);
+ return 0;
+}
+bool __attribute__((weak)) netif_connect(struct mg_wifi_data *wifi) {
+ (void) wifi;
+ return netif_init(NULL);
+}
+bool __attribute__((weak))
+netif_poll(struct mg_tcpip_if *ifp, bool s1, mg_tcpip_event_handler_t evcb) {
+ (void) ifp;
+ (void) s1;
+ (void) evcb;
+ return false;
+}
+
+static struct mg_tcpip_if *s_ifp;
+static uint32_t s_ip, s_mask;
+
+static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
+ struct mg_wifi_data *wifi =
+ &((struct mg_tcpip_driver_nxp_wifi_data *) ifp->driver_data)->wifi;
+ if (wifi->apmode && ev == MG_TCPIP_EV_ST_CHG &&
+ *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
+ MG_DEBUG(("Access Point started"));
+ s_ip = ifp->ip, ifp->ip = wifi->apip;
+ s_mask = ifp->mask, ifp->mask = wifi->apmask;
+ ifp->enable_dhcp_client = false;
+ ifp->enable_dhcp_server = true;
+ }
+}
+
+static bool nxp_wifi_init(struct mg_tcpip_if *ifp) {
+ struct mg_wifi_data *wifi =
+ &((struct mg_tcpip_driver_nxp_wifi_data *) ifp->driver_data)->wifi;
+ s_ifp = ifp;
+ s_ip = ifp->ip;
+ s_mask = ifp->mask;
+ ifp->pfn = wifi_cb;
+ if (!netif_init(ifp)) return false;
+ if (wifi->apmode) {
+ return mg_wifi_ap_start(wifi);
+ } else if (wifi->ssid != NULL && wifi->pass != NULL) {
+ return mg_wifi_connect(wifi);
+ }
+ return true;
+}
+
+bool nxp_wifi_poll(struct mg_tcpip_if *ifp, bool s1) {
+ return netif_poll(ifp, s1, mg_tcpip_call);
+}
+
+struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi = {nxp_wifi_init, netif_tx,
+ NULL, nxp_wifi_poll};
+
+bool mg_wifi_connect(struct mg_wifi_data *wifi) {
+ s_ifp->ip = s_ip;
+ s_ifp->mask = s_mask;
+ if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
+ s_ifp->enable_dhcp_server = false;
+ return netif_connect(wifi);
+}
+
+bool __attribute__((weak)) mg_wifi_scan(void) {
+ return netif_init(NULL);
+}
+bool __attribute__((weak)) mg_wifi_disconnect(void) {
+ return netif_init(NULL);
+}
+bool __attribute__((weak)) mg_wifi_ap_start(struct mg_wifi_data *wifi) {
+ (void) wifi;
+ return netif_init(NULL);
+}
+bool __attribute__((weak)) mg_wifi_ap_stop(void) {
+ return netif_init(NULL);
+}
+
+#endif
+
#ifdef MG_ENABLE_LINES
#line 1 "src/drivers/phy.c"
#endif
@@ -22160,7 +23158,6 @@ static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
default:
return "unknown";
}
- (void) id2;
}

void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) {
@@ -22268,23 +23265,41 @@ bool mg_phy_up(struct mg_phy *phy, uint8_t phy_addr, bool *full_duplex,


static struct mg_tcpip_if *s_ifp;
+static uint32_t s_ip, s_mask;
+static bool s_aplink = false, s_scanning = false;
+static bool s_stalink = false, s_connecting = false;
+
+static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
+ struct mg_wifi_data *wifi =
+ &((struct mg_tcpip_driver_pico_w_data *) ifp->driver_data)->wifi;
+ if (wifi->apmode && ev == MG_TCPIP_EV_ST_CHG &&
+ *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
+ MG_DEBUG(("Access Point started"));
+ s_ip = ifp->ip, ifp->ip = wifi->apip;
+ s_mask = ifp->mask, ifp->mask = wifi->apmask;
+ ifp->enable_dhcp_client = false;
+ ifp->enable_dhcp_server = true;
+ }
+}

static bool mg_tcpip_driver_pico_w_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_pico_w_data *d =
(struct mg_tcpip_driver_pico_w_data *) ifp->driver_data;
+ struct mg_wifi_data *wifi = &d->wifi;
s_ifp = ifp;
+ s_ip = ifp->ip;
+ s_mask = ifp->mask;
+ ifp->pfn = wifi_cb;
if (cyw43_arch_init() != 0)
return false; // initialize async_context and WiFi chip
- if (d->apmode && d->apssid != NULL) {
- MG_DEBUG(("Starting AP '%s' (%u)", d->apssid, d->apchannel));
- if (!mg_wifi_ap_start(d->apssid, d->appass, d->apchannel)) return false;
+ if (wifi->apmode && wifi->apssid != NULL) {
+ if (!mg_wifi_ap_start(wifi)) return false;
cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); // same MAC
} else {
cyw43_arch_enable_sta_mode();
cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac);
- if (d->ssid != NULL) {
- MG_DEBUG(("Connecting to '%s'", d->ssid));
- return mg_wifi_connect(d->ssid, d->pass);
+ if (wifi->ssid != NULL) {
+ return mg_wifi_connect(wifi);
} else {
cyw43_arch_disable_sta_mode();
}
@@ -22297,35 +23312,33 @@ static size_t mg_tcpip_driver_pico_w_tx(const void *buf, size_t len,
struct mg_tcpip_driver_pico_w_data *d =
(struct mg_tcpip_driver_pico_w_data *) ifp->driver_data;
return cyw43_send_ethernet(&cyw43_state,
- d->apmode ? CYW43_ITF_AP : CYW43_ITF_STA, len, buf,
- false) == 0
+ d->wifi.apmode ? CYW43_ITF_AP : CYW43_ITF_STA, len,
+ buf, false) == 0
? len
: 0;
}

-static bool s_aplink = false, s_scanning = false;
-static bool s_stalink = false, s_connecting = false;
-
static bool mg_tcpip_driver_pico_w_poll(struct mg_tcpip_if *ifp, bool s1) {
cyw43_arch_poll(); // not necessary, except when IRQs are disabled (OTA)
if (s_scanning && !cyw43_wifi_scan_active(&cyw43_state)) {
MG_VERBOSE(("scan complete"));
s_scanning = 0;
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
+ mg_tcpip_call(ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
}
if (ifp->update_mac_hash_table) {
// first call to _poll() is after _init(), so this is safe
- cyw43_wifi_update_multicast_filter(&cyw43_state, (uint8_t *)mcast_addr, true);
+ cyw43_wifi_update_multicast_filter(&cyw43_state, (uint8_t *) mcast_addr,
+ true);
ifp->update_mac_hash_table = false;
}
if (!s1) return false;
struct mg_tcpip_driver_pico_w_data *d =
(struct mg_tcpip_driver_pico_w_data *) ifp->driver_data;
- if (d->apmode) return s_aplink;
+ if (d->wifi.apmode) return s_aplink;
int sdkstate = cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA);
MG_VERBOSE(("conn: %c state: %d", s_connecting ? '1' : '0', sdkstate));
if (sdkstate < 0 && s_connecting) {
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, &sdkstate);
+ mg_tcpip_call(ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, &sdkstate);
s_connecting = false;
}
return s_stalink;
@@ -22356,7 +23369,7 @@ void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
}
void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
if (itf == CYW43_ITF_AP) {
- s_aplink = false;
+ s_aplink = false;
} else {
s_stalink = false;
// SDK calls this before we check status, don't clear s_connecting here
@@ -22396,9 +23409,15 @@ bool mg_wifi_scan(void) {
return res;
}

-bool mg_wifi_connect(char *ssid, char *pass) {
+bool mg_wifi_connect(struct mg_wifi_data *wifi) {
+ s_ifp->ip = s_ip;
+ s_ifp->mask = s_mask;
+ if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
+ s_ifp->enable_dhcp_server = false;
cyw43_arch_enable_sta_mode();
- int res = cyw43_arch_wifi_connect_async(ssid, pass, CYW43_AUTH_WPA2_AES_PSK);
+ MG_DEBUG(("Connecting to '%s'", wifi->ssid));
+ int res = cyw43_arch_wifi_connect_async(wifi->ssid, wifi->pass,
+ CYW43_AUTH_WPA2_AES_PSK);
MG_VERBOSE(("res: %d", res));
if (res == 0) s_connecting = true;
return (res == 0);
@@ -22410,9 +23429,11 @@ bool mg_wifi_disconnect(void) {
return true;
}

-bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
- cyw43_wifi_ap_set_channel(&cyw43_state, channel);
- cyw43_arch_enable_ap_mode(ssid, pass, CYW43_AUTH_WPA2_AES_PSK);
+bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
+ MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
+ cyw43_wifi_ap_set_channel(&cyw43_state, wifi->apchannel);
+ cyw43_arch_enable_ap_mode(wifi->apssid, wifi->appass,
+ CYW43_AUTH_WPA2_AES_PSK);
return true;
}

@@ -22798,16 +23819,12 @@ struct ra_edmac {
#define ETH_PKT_SIZE 1536 // Max frame size, multiple of 32
#define ETH_DESC_CNT 4 // Descriptors count

-// TODO(): handle these in a portable compiler-independent CMSIS-friendly way
-#define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
-#define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
-
// Descriptors: 16-byte aligned
// Buffers: 32-byte aligned (27.3.1)
-static volatile uint32_t s_rxdesc[ETH_DESC_CNT][4] MG_16BYTE_ALIGNED;
-static volatile uint32_t s_txdesc[ETH_DESC_CNT][4] MG_16BYTE_ALIGNED;
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
+static volatile uint32_t s_rxdesc[ETH_DESC_CNT][4] MG_ETH_RAM MG_16BYTE_ALIGNED;
+static volatile uint32_t s_txdesc[ETH_DESC_CNT][4] MG_ETH_RAM MG_16BYTE_ALIGNED;
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_32BYTE_ALIGNED;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_32BYTE_ALIGNED;
static struct mg_tcpip_if *s_ifp; // MIP interface

// fastest is 3 cycles (SUB + BNE) on a 3-stage pipeline or equivalent
@@ -23057,17 +24074,17 @@ struct ENET_Type {
CHANNEL_TCSR[4], CHANNEL_TCCR[4];
};

+#undef ENET
#define ENET ((struct ENET_Type *) 0x40138000)

#define ETH_PKT_SIZE 1536 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 2 // Descriptor size (words)

-#define MG_8BYTE_ALIGNED __attribute__((aligned((8U))))
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_8BYTE_ALIGNED;
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_8BYTE_ALIGNED;
-static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED;
-static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED;
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
static uint8_t s_txno; // Current TX descriptor
static uint8_t s_rxno; // Current RX descriptor

@@ -23681,10 +24698,10 @@ struct stm32f_eth {
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)

-static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
-static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM; // TX descriptors
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM; // RX ethernet buffers
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM; // TX ethernet buffers
static uint8_t s_txno; // Current TX descriptor
static uint8_t s_rxno; // Current RX descriptor

@@ -23911,9 +24928,10 @@ struct mg_tcpip_driver mg_tcpip_driver_stm32f = {
#endif


-#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN)
+#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
+ MG_ENABLE_DRIVER_STM32N)
// STM32H: vendor modded single-queue Synopsys v4.2
-// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
+// STM32N, MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
// RT1170 ENET_QOS: quad-queue Synopsys v5.1
struct synopsys_enet_qos {
volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
@@ -23952,17 +24970,19 @@ struct synopsys_enet_qos {
0x8000UL))
#elif MG_ENABLE_DRIVER_MCXN
#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL)
+#elif MG_ENABLE_DRIVER_STM32N
+#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x48036000UL)
#endif

#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)

-static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
-static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
-static struct mg_tcpip_if *s_ifp; // MIP interface
+static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static struct mg_tcpip_if *s_ifp; // MIP interface

static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMDIOAR &= (0xF << 8);
@@ -23988,12 +25008,14 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
uint8_t phy_conf = d == NULL ? MG_PHY_CLOCKS_MAC : d->phy_conf;

// Init RX descriptors
+ memset((char *) s_rxdesc, 0, sizeof(s_rxdesc)); // manual init
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
s_rxdesc[i][3] = MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V
}

// Init TX descriptors
+ memset((char *) s_txdesc, 0, sizeof(s_txdesc)); // manual init
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
}
@@ -24013,7 +25035,7 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
ETH->DMASBMR |= MG_BIT(12); // AAL NOTE(scaprile): is this actually needed
ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
ETH->MACTFCR = MG_BIT(7); // Disable zero-quanta pause
-#if !MG_ENABLE_DRIVER_MCXN
+#if MG_ENABLE_DRIVER_STM32H
ETH->MACPFR = MG_BIT(10); // Perfect filtering
#endif
struct mg_phy phy = {eth_read_phy, eth_write_phy};
@@ -24082,7 +25104,7 @@ static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
}

static void mg_tcpip_driver_stm32h_update_hash_table(struct mg_tcpip_if *ifp) {
-#if MG_ENABLE_DRIVER_MCXN
+#if MG_ENABLE_DRIVER_MCXN || MG_ENABLE_DRIVER_STM32N
ETH->MACPFR = MG_BIT(4); // Pass Multicast (pass all multicast frames)
#else
// TODO(): read database, rebuild hash table
@@ -24093,7 +25115,7 @@ static void mg_tcpip_driver_stm32h_update_hash_table(struct mg_tcpip_if *ifp) {
ETH->MACA1HR = (uint32_t) mcast_addr[5] << 8 | (uint32_t) mcast_addr[4];
ETH->MACA1HR |= MG_BIT(31); // AE
#endif
-(void) ifp;
+ (void) ifp;
}

static bool mg_tcpip_driver_stm32h_poll(struct mg_tcpip_if *ifp, bool s1) {
@@ -24127,9 +25149,12 @@ static uint32_t s_rxno;
#if MG_ENABLE_DRIVER_MCXN
void ETHERNET_IRQHandler(void);
void ETHERNET_IRQHandler(void) {
-#else
+#elif MG_ENABLE_DRIVER_STM32H
void ETH_IRQHandler(void);
void ETH_IRQHandler(void) {
+#else
+void ETH1_IRQHandler(void);
+void ETH1_IRQHandler(void) {
#endif
if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop
ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag
@@ -24275,11 +25300,11 @@ static int guess_mdc_cr(void) {
uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values
uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers
uint32_t sysclk = get_sysclk(); // Guess system SYSCLK
- int result = -1; // Invalid CR value
+ int i, result = -1; // Invalid CR value
if (sysclk < 25000000) {
MG_ERROR(("SYSCLK too low"));
} else {
- for (int i = 0; i < 4; i++) {
+ for (i = 0; i < 4; i++) {
if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
result = crs[i];
break;
@@ -24294,10 +25319,11 @@ static int guess_mdc_cr(void) {
static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_tm4c_data *d =
(struct mg_tcpip_driver_tm4c_data *) ifp->driver_data;
+ int i;
s_ifp = ifp;

// Init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
+ for (i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = MG_BIT(31); // Own
s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_BIT(14); // 2nd address chained
s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
@@ -24307,7 +25333,7 @@ static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) {
}

// Init TX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
+ for (i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
s_txdesc[i][3] =
(uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
@@ -24368,7 +25394,6 @@ static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
EMAC->EMACDMARIS = MG_BIT(2) | MG_BIT(5); // Clear any prior TU/UNF
EMAC->EMACTXPOLLD = 0; // and resume
return len;
- (void) ifp;
}

static void mg_tcpip_driver_tm4c_update_hash_table(struct mg_tcpip_if *ifp) {
@@ -24409,9 +25434,10 @@ static bool mg_tcpip_driver_tm4c_poll(struct mg_tcpip_if *ifp, bool s1) {
void EMAC0_IRQHandler(void);
static uint32_t s_rxno;
void EMAC0_IRQHandler(void) {
+ int i;
if (EMAC->EMACDMARIS & MG_BIT(6)) { // Frame received, loop
EMAC->EMACDMARIS = MG_BIT(16) | MG_BIT(6); // Clear flag
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ for (i = 0; i < 10; i++) { // read as they arrive but not forever
if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done
if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) ==
(MG_BIT(8) | MG_BIT(9))) &&
@@ -24933,18 +25959,12 @@ struct ETH_GLOBAL_TypeDef {
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)

-#ifndef ETH_RAM_SECTION
-// if no section is specified, then the data will be placed in the default
-// bss section
-#define ETH_RAM_SECTION
-#endif
-
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] ETH_RAM_SECTION;
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] ETH_RAM_SECTION;
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
static uint32_t s_rxdesc[ETH_DESC_CNT]
- [ETH_DS] ETH_RAM_SECTION; // RX descriptors
+ [ETH_DS] MG_ETH_RAM; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT]
- [ETH_DS] ETH_RAM_SECTION; // TX descriptors
+ [ETH_DS] MG_ETH_RAM; // TX descriptors
static uint8_t s_txno; // Current TX descriptor
static uint8_t s_rxno; // Current RX descriptor

@@ -25205,15 +26225,12 @@ struct ETH_Type {
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 2 // Descriptor size (words)

-// TODO(): handle these in a portable compiler-independent CMSIS-friendly way
-#define MG_8BYTE_ALIGNED __attribute__((aligned((8U))))
-
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
-static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED;
-static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED;
-static uint8_t s_txno MG_8BYTE_ALIGNED; // Current TX descriptor
-static uint8_t s_rxno MG_8BYTE_ALIGNED; // Current RX descriptor
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
+static uint8_t s_txno; // Current TX descriptor
+static uint8_t s_rxno; // Current RX descriptor

static struct mg_tcpip_if *s_ifp; // MIP interface
enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 };
diff --git a/mongoose/mongoose.h b/mongoose/mongoose.h
index c37b3b16..3a72304f 100644
--- a/mongoose/mongoose.h
+++ b/mongoose/mongoose.h
@@ -15,12 +15,12 @@
// Alternatively, you can license this software under a commercial
// license, as set out in https://www.mongoose.ws/licensing/
//
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0-only or commercial

#ifndef MONGOOSE_H
#define MONGOOSE_H

-#define MG_VERSION "7.19"
+#define MG_VERSION "7.20"

#ifdef __cplusplus
extern "C" {
@@ -285,6 +285,7 @@ extern "C" {
#define mode_t size_t
#include <alloca.h>
#include <time.h>
+#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
#elif defined(__CCRH__)
#else
#include <sys/stat.h>
@@ -1056,10 +1057,34 @@ struct timeval {
#define MG_TCPIP_GW MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
#endif

+#if MG_ENABLE_IPV6
+
+#ifndef MG_TCPIP_GLOBAL
+#define MG_TCPIP_GLOBAL MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
+#endif
+
+#ifndef MG_TCPIP_IPV6_LINKLOCAL
+#define MG_TCPIP_IPV6_LINKLOCAL MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
+#endif
+
+#ifndef MG_TCPIP_PREFIX_LEN
+#define MG_TCPIP_PREFIX_LEN 0
+#endif
+
+#ifndef MG_TCPIP_GW6
+#define MG_TCPIP_GW6 MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
+#endif
+
+#endif
+
#ifndef MG_SET_MAC_ADDRESS
#define MG_SET_MAC_ADDRESS(mac)
#endif

+#ifndef MG_TCPIP_DHCPNAME_SIZE
+#define MG_TCPIP_DHCPNAME_SIZE 18 // struct mg_tcpip_if :: dhcp_name size
+#endif
+
#ifndef MG_SET_WIFI_CONFIG
#define MG_SET_WIFI_CONFIG(data)
#endif
@@ -1206,7 +1231,6 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
size_t mg_snprintf(char *, size_t, const char *fmt, ...);
char *mg_vmprintf(const char *fmt, va_list *ap);
char *mg_mprintf(const char *fmt, ...);
-size_t mg_queue_vprintf(struct mg_queue *, const char *fmt, va_list *);
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);

// %M print helper functions
@@ -1364,6 +1388,16 @@ void mg_delayms(unsigned int ms);

#define MG_IPV4(a, b, c, d) mg_htonl(MG_U32(a, b, c, d))

+#define MG_IPV6(a, b, c, d, e, f, g ,h) \
+ { (uint8_t)((a)>>8),(uint8_t)(a), \
+ (uint8_t)((b)>>8),(uint8_t)(b), \
+ (uint8_t)((c)>>8),(uint8_t)(c), \
+ (uint8_t)((d)>>8),(uint8_t)(d), \
+ (uint8_t)((e)>>8),(uint8_t)(e), \
+ (uint8_t)((f)>>8),(uint8_t)(f), \
+ (uint8_t)((g)>>8),(uint8_t)(g), \
+ (uint8_t)((h)>>8),(uint8_t)(h) }
+
// For printing IPv4 addresses: printf("%d.%d.%d.%d\n", MG_IPADDR_PARTS(&ip))
#define MG_U8P(ADDR) ((uint8_t *) (ADDR))
#define MG_IPADDR_PARTS(ADDR) \
@@ -1378,6 +1412,14 @@ void mg_delayms(unsigned int ms);
((uint32_t) (((uint32_t) MG_U8P(p)[0] << 24U) | \
((uint32_t) MG_U8P(p)[1] << 16U) | \
((uint32_t) MG_U8P(p)[2] << 8U) | MG_U8P(p)[3]))
+#define MG_LOAD_BE64(p) \
+ ((uint64_t) (((uint64_t) MG_U8P(p)[0] << 56U) | \
+ ((uint64_t) MG_U8P(p)[1] << 48U) | \
+ ((uint64_t) MG_U8P(p)[2] << 40U) | \
+ ((uint64_t) MG_U8P(p)[3] << 32U) | \
+ ((uint64_t) MG_U8P(p)[4] << 24U) | \
+ ((uint64_t) MG_U8P(p)[5] << 16U) | \
+ ((uint64_t) MG_U8P(p)[6] << 8U) | MG_U8P(p)[7]))
#define MG_STORE_BE16(p, n) \
do { \
MG_U8P(p)[0] = ((n) >> 8U) & 255; \
@@ -1396,11 +1438,24 @@ void mg_delayms(unsigned int ms);
MG_U8P(p)[2] = ((n) >> 8U) & 255; \
MG_U8P(p)[3] = (n) &255; \
} while (0)
+#define MG_STORE_BE64(p, n) \
+ do { \
+ MG_U8P(p)[0] = ((n) >> 56U) & 255; \
+ MG_U8P(p)[1] = ((n) >> 48U) & 255; \
+ MG_U8P(p)[2] = ((n) >> 40U) & 255; \
+ MG_U8P(p)[3] = ((n) >> 32U) & 255; \
+ MG_U8P(p)[4] = ((n) >> 24U) & 255; \
+ MG_U8P(p)[5] = ((n) >> 16U) & 255; \
+ MG_U8P(p)[6] = ((n) >> 8U) & 255; \
+ MG_U8P(p)[7] = (n) &255; \
+ } while (0)

uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net);
+uint64_t mg_ntohll(uint64_t net);
#define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x)
+#define mg_htonll(x) mg_ntohll(x)

#define MG_REG(x) ((volatile uint32_t *) (x))[0]
#define MG_BIT(x) (((uint32_t) 1U) << (x))
@@ -1484,8 +1539,8 @@ struct mg_iobuf {
size_t align; // Alignment during allocation
};

-int mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
-int mg_iobuf_resize(struct mg_iobuf *, size_t);
+bool mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
+bool mg_iobuf_resize(struct mg_iobuf *, size_t);
void mg_iobuf_free(struct mg_iobuf *);
size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t);
size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len);
@@ -1604,7 +1659,11 @@ struct mg_dns {
};

struct mg_addr {
- uint8_t ip[16]; // Holds IPv4 or IPv6 address, in network byte order
+ union { // Holds IPv4 or IPv6 address, in network byte order
+ uint8_t ip[16];
+ uint32_t ip4;
+ uint64_t ip6[2];
+ };
uint16_t port; // TCP or UDP port in network byte order
uint8_t scope_id; // IPv6 scope ID
bool is_ip6; // True when address is IPv6 address
@@ -1740,7 +1799,6 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
-void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
@@ -3023,28 +3081,37 @@ bool mg_ota_flash_end(struct mg_flash *flash);



+struct mg_wifi_data {
+ char *ssid, *pass; // STA mode, SSID to connect to
+ char *apssid, *appass; // AP mode, our SSID
+ uint32_t apip, apmask; // AP mode, our IP address and mask
+ uint8_t security; // STA mode, TBD
+ uint8_t apsecurity; // AP mode, TBD
+ uint8_t apchannel; // AP mode, channel to use
+ bool apmode; // start in AP mode; 'false' -> connect to 'ssid' != NULL
+};
+
struct mg_wifi_scan_bss_data {
- struct mg_str SSID;
- char *BSSID;
- int16_t RSSI;
- uint8_t security;
+ struct mg_str SSID;
+ char *BSSID;
+ int16_t RSSI;
+ uint8_t security;
#define MG_WIFI_SECURITY_OPEN 0
-#define MG_WIFI_SECURITY_WEP MG_BIT(0)
-#define MG_WIFI_SECURITY_WPA MG_BIT(1)
+#define MG_WIFI_SECURITY_WEP MG_BIT(0)
+#define MG_WIFI_SECURITY_WPA MG_BIT(1)
#define MG_WIFI_SECURITY_WPA2 MG_BIT(2)
#define MG_WIFI_SECURITY_WPA3 MG_BIT(3)
- uint8_t channel;
- unsigned band :2;
+ uint8_t channel;
+ unsigned band : 2;
#define MG_WIFI_BAND_2G 0
#define MG_WIFI_BAND_5G 1
- unsigned has_n :1;
+ unsigned has_n : 1;
};

-
bool mg_wifi_scan(void);
-bool mg_wifi_connect(char *ssid, char *pass);
+bool mg_wifi_connect(struct mg_wifi_data *);
bool mg_wifi_disconnect(void);
-bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel);
+bool mg_wifi_ap_start(struct mg_wifi_data *);
bool mg_wifi_ap_stop(void);


@@ -3077,10 +3144,10 @@ enum {
MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL
MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and chip specific
MG_TCPIP_EV_DRIVER, // Driver event driver specific
+ MG_TCPIP_EV_ST6_CHG, // state6 change uint8_t * (&ifp->state6)
MG_TCPIP_EV_USER // Starting ID for user events
};

-
// Network interface
struct mg_tcpip_if {
uint8_t mac[6]; // MAC address. Must be set to a valid MAC
@@ -3096,12 +3163,20 @@ struct mg_tcpip_if {
bool update_mac_hash_table; // Signal drivers to update MAC controller
struct mg_tcpip_driver *driver; // Low level driver
void *driver_data; // Driver-specific data
+ mg_tcpip_event_handler_t pfn; // Driver-specific event handler function
mg_tcpip_event_handler_t fn; // User-specified event handler function
struct mg_mgr *mgr; // Mongoose event manager
struct mg_queue recv_queue; // Receive queue
- char dhcp_name[12]; // Name reported to DHCP, "mip" if unset
- uint16_t mtu; // Interface MTU
+ char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Name for DHCP, "mip" if unset
+ uint16_t mtu; // Interface MTU
#define MG_TCPIP_MTU_DEFAULT 1500
+#if MG_ENABLE_IPV6
+ uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses
+ uint8_t prefix_len; // Prefix length
+ uint64_t gw6[2]; // Default gateway
+ bool enable_slaac; // Enable IPv6 address autoconfiguration
+ bool enable_dhcp6_client; // Enable DCHPv6 client
+#endif

// Internal state, user can use it but should not change it
uint8_t gwmac[6]; // Router's MAC
@@ -3114,14 +3189,17 @@ struct mg_tcpip_if {
volatile uint32_t nrecv; // Number of received frames
volatile uint32_t nsent; // Number of transmitted frames
volatile uint32_t nerr; // Number of driver errors
- uint8_t state; // Current state
+ uint8_t state; // Current link and IPv4 state
#define MG_TCPIP_STATE_DOWN 0 // Interface is down
#define MG_TCPIP_STATE_UP 1 // Interface is up
#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state
#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned
#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work
+#if MG_ENABLE_IPV6
+ uint8_t gw6mac[6]; // IPv6 Router's MAC
+ uint8_t state6; // Current IPv6 state
+#endif
};
-
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
void mg_tcpip_free(struct mg_tcpip_if *);
void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp);
@@ -3143,6 +3221,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_ppp;
extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
+extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;

// Drivers that require SPI, can use this SPI abstraction
struct mg_tcpip_spi {
@@ -3153,6 +3232,43 @@ struct mg_tcpip_spi {
};


+// Alignment and memory section requirements
+#ifndef MG_8BYTE_ALIGNED
+#if defined(__GNUC__)
+#define MG_8BYTE_ALIGNED __attribute__((aligned((8U))))
+#else
+#define MG_8BYTE_ALIGNED
+#endif // compiler
+#endif // 8BYTE_ALIGNED
+
+#ifndef MG_16BYTE_ALIGNED
+#if defined(__GNUC__)
+#define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
+#else
+#define MG_16BYTE_ALIGNED
+#endif // compiler
+#endif // 16BYTE_ALIGNED
+
+#ifndef MG_32BYTE_ALIGNED
+#if defined(__GNUC__)
+#define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
+#else
+#define MG_32BYTE_ALIGNED
+#endif // compiler
+#endif // 32BYTE_ALIGNED
+
+#ifndef MG_64BYTE_ALIGNED
+#if defined(__GNUC__)
+#define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
+#else
+#define MG_64BYTE_ALIGNED
+#endif // compiler
+#endif // 64BYTE_ALIGNED
+
+#ifndef MG_ETH_RAM
+#define MG_ETH_RAM
+#endif
+
#endif


@@ -3186,43 +3302,35 @@ struct mg_tcpip_driver_cyw_firmware {
};

struct mg_tcpip_driver_cyw_data {
+ struct mg_wifi_data wifi;
void *bus;
struct mg_tcpip_driver_cyw_firmware *fw;
- char *ssid;
- char *pass;
- char *apssid;
- char *appass;
- uint8_t security; // TBD
- uint8_t apsecurity; // TBD
- uint8_t apchannel;
- bool apmode; // start in AP mode; 'false' starts connection to 'ssid' if not NULL
- bool hs; // use chip "high-speed" mode; otherwise SPI CPOL0 CPHA0 (DS 4.2.3 Table 6)
+ bool hs; // use chip "high-speed" mode; otherwise SPI CPOL0 CPHA0 (DS 4.2.3 Table 6)
};

-#if 0
-#define MG_TCPIP_DRIVER_INIT(mgr) \
- do { \
- static struct mg_tcpip_driver_cyw_data driver_data_; \
- static struct mg_tcpip_if mif_; \
- MG_SET_WIFI_CONFIG(&driver_data_); \
- mif_.ip = MG_TCPIP_IP; \
- mif_.mask = MG_TCPIP_MASK; \
- mif_.gw = MG_TCPIP_GW; \
- mif_.driver = &mg_tcpip_driver_pico_w; \
- mif_.driver_data = &driver_data_; \
- mif_.recv_queue.size = 8192; \
- mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
- mg_tcpip_init(mgr, &mif_); \
+#define MG_TCPIP_DRIVER_INIT(mgr) \
+ do { \
+ static struct mg_tcpip_driver_cyw_data driver_data_; \
+ static struct mg_tcpip_if mif_; \
+ MG_SET_WIFI_CONFIG(&driver_data_); \
+ mif_.ip = MG_TCPIP_IP; \
+ mif_.mask = MG_TCPIP_MASK; \
+ mif_.gw = MG_TCPIP_GW; \
+ mif_.driver = &mg_tcpip_driver_cyw; \
+ mif_.driver_data = &driver_data_; \
+ mif_.recv_queue.size = 8192; \
+ mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
+ mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: cyw, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
-#endif

#endif


#if MG_ENABLE_TCPIP && \
(defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10) || \
- (defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11)
+ (defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11) || \
+ (defined(MG_ENABLE_DRIVER_MCXE) && MG_ENABLE_DRIVER_MCXE)

struct mg_tcpip_driver_imxrt_data {
// MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk),
@@ -3268,6 +3376,34 @@ struct mg_tcpip_driver_imxrt_data {
#endif


+#if MG_ENABLE_TCPIP && \
+ defined(MG_ENABLE_DRIVER_NXP_WIFI) && MG_ENABLE_DRIVER_NXP_WIFI
+
+
+struct mg_tcpip_driver_nxp_wifi_data {
+ struct mg_wifi_data wifi;
+};
+
+
+#define MG_TCPIP_DRIVER_INIT(mgr) \
+ do { \
+ static struct mg_tcpip_driver_nxp_wifi_data driver_data_; \
+ static struct mg_tcpip_if mif_; \
+ MG_SET_WIFI_CONFIG(&driver_data_); \
+ mif_.ip = MG_TCPIP_IP; \
+ mif_.mask = MG_TCPIP_MASK; \
+ mif_.gw = MG_TCPIP_GW; \
+ mif_.driver = &mg_tcpip_driver_nxp_wifi; \
+ mif_.driver_data = &driver_data_; \
+ mif_.recv_queue.size = 8192; \
+ mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
+ mg_tcpip_init(mgr, &mif_); \
+ MG_INFO(("Driver: nxp wifi, MAC: %M", mg_print_mac, mif_.mac)); \
+ } while (0)
+
+#endif
+
+


struct mg_phy {
@@ -3298,14 +3434,7 @@ bool mg_phy_up(struct mg_phy *, uint8_t addr, bool *full_duplex,
#include "pico/unique_id.h" // keep this include

struct mg_tcpip_driver_pico_w_data {
- char *ssid;
- char *pass;
- char *apssid;
- char *appass;
- uint8_t security; // TBD
- uint8_t apsecurity; // TBD
- uint8_t apchannel;
- bool apmode; // start in AP mode; 'false' starts connection to 'ssid' if not NULL
+ struct mg_wifi_data wifi;
};

#define MG_TCPIP_DRIVER_INIT(mgr) \
@@ -3562,7 +3691,10 @@ struct mg_tcpip_driver_stm32f_data {
#if !defined(MG_ENABLE_DRIVER_MCXN)
#define MG_ENABLE_DRIVER_MCXN 0
#endif
-#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN
+#if !defined(MG_ENABLE_DRIVER_STM32N)
+#define MG_ENABLE_DRIVER_STM32N 0
+#endif
+#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || MG_ENABLE_DRIVER_STM32N

struct mg_tcpip_driver_stm32h_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
@@ -3601,6 +3733,18 @@ struct mg_tcpip_driver_stm32h_data {
#define MG_ENABLE_ETH_IRQ()
#endif

+#if MG_ENABLE_IPV6
+#define MG_IPV6_INIT(mif) \
+ do { \
+ memcpy(mif.ip6ll, (uint8_t[16]) MG_TCPIP_IPV6_LINKLOCAL, 16); \
+ memcpy(mif.ip6, (uint8_t[16]) MG_TCPIP_GLOBAL, 16); \
+ memcpy(mif.gw6, (uint8_t[16]) MG_TCPIP_GW6, 16); \
+ mif.prefix_len = MG_TCPIP_PREFIX_LEN; \
+ } while(0)
+#else
+#define MG_IPV6_INIT(mif)
+#endif
+
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_stm32h_data driver_data_; \
@@ -3614,6 +3758,7 @@ struct mg_tcpip_driver_stm32h_data {
mif_.driver = &mg_tcpip_driver_stm32h; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
+ MG_IPV6_INIT(mif_); \
mg_tcpip_init(mgr, &mif_); \
MG_ENABLE_ETH_IRQ(); \
MG_INFO(("Driver: stm32h, MAC: %M", mg_print_mac, mif_.mac)); \
@@ -3693,22 +3838,9 @@ struct mg_tcpip_driver_tms570_data {



-#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7

-struct mg_tcpip_driver_xmc_data {
- // 13.2.8.1 Station Management Functions
- // MDC clock divider (). MDC clock is derived from ETH MAC clock
- // It must not exceed 2.5MHz
- // ETH Clock range DIVIDER mdc_cr VALUE
- // --------------------------------------------
- // -1 <-- tell driver to guess the value
- // 60-100 MHz ETH Clock/42 0
- // 100-150 MHz ETH Clock/62 1
- // 20-35 MHz ETH Clock/16 2
- // 35-60 MHz ETH Clock/26 3
- // 150-250 MHz ETH Clock/102 4
- // 250-300 MHz ETH Clock/124 5
- // 110, 111 Reserved
+struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
@@ -3718,31 +3850,45 @@ struct mg_tcpip_driver_xmc_data {
#endif

#ifndef MG_DRIVER_MDC_CR
-#define MG_DRIVER_MDC_CR 4
+#define MG_DRIVER_MDC_CR 3
#endif

#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
- static struct mg_tcpip_driver_xmc_data driver_data_; \
+ static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
- mif_.driver = &mg_tcpip_driver_xmc; \
+ mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
- MG_INFO(("Driver: xmc, MAC: %M", mg_print_mac, mif_.mac)); \
+ MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)

#endif


-#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7

-struct mg_tcpip_driver_xmc7_data {
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
+
+struct mg_tcpip_driver_xmc_data {
+ // 13.2.8.1 Station Management Functions
+ // MDC clock divider (). MDC clock is derived from ETH MAC clock
+ // It must not exceed 2.5MHz
+ // ETH Clock range DIVIDER mdc_cr VALUE
+ // --------------------------------------------
+ // -1 <-- tell driver to guess the value
+ // 60-100 MHz ETH Clock/42 0
+ // 100-150 MHz ETH Clock/62 1
+ // 20-35 MHz ETH Clock/16 2
+ // 35-60 MHz ETH Clock/26 3
+ // 150-250 MHz ETH Clock/102 4
+ // 250-300 MHz ETH Clock/124 5
+ // 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
@@ -3752,28 +3898,27 @@ struct mg_tcpip_driver_xmc7_data {
#endif

#ifndef MG_DRIVER_MDC_CR
-#define MG_DRIVER_MDC_CR 3
+#define MG_DRIVER_MDC_CR 4
#endif

#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
- static struct mg_tcpip_driver_xmc7_data driver_data_; \
+ static struct mg_tcpip_driver_xmc_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
- mif_.driver = &mg_tcpip_driver_xmc7; \
+ mif_.driver = &mg_tcpip_driver_xmc; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
- MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
+ MG_INFO(("Driver: xmc, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)

#endif

-
#ifdef __cplusplus
}
#endif
--
2.43.0

Michael Glembotzki

unread,
Nov 26, 2025, 4:44:32 AM (7 days ago) Nov 26
to swupdate
Hi Stefano,

I have tested:
- http
- Websocket connection
- /swupdate/upload with small (2,5kB) and big (77MB) swu file
- /swupdate/restart

I have not tested:
- https

I stumbled across the fact that the Update-Type feature requires explicit specification for the
postupdatecmd in the global section to function correctly. It's not a release blocker, but it made
me wonder if /swupdate/restart shouldn't execute /sbin/reboot by default.

Tested-by: Michael Glembotzki <Michael.G...@iris-sensing.com>

Best regards,
Michael

Stefano Babic

unread,
Nov 26, 2025, 9:22:19 AM (7 days ago) Nov 26
to Michael Glembotzki, swupdate
Hi Michael,

On 11/26/25 10:44, Michael Glembotzki wrote:
> Hi Stefano,
>
> I have tested:
> - http
> - Websocket connection
> - /swupdate/upload with small (2,5kB) and big (77MB) swu file
> - /swupdate/restart
>
> I have not tested:
> - https
>
> I stumbled across the fact that the Update-Type feature requires
> explicit specification for the
> postupdatecmd in the global section to function correctly.

Well, it is independent from update-type. Even before, a postupdatecmd
must be set if you want it is executed. Default value is empty.

> It's not a
> release blocker, but it made
> me wonder if /swupdate/restart shouldn't execute /sbin/reboot by default. > Tested-by: Michael Glembotzki <Michael.G...@iris-sensing.com>

Thanks - I merged them for the release.

Best regards,
Stefano
> uint16_t id = mg_ntohs(mm.id <http://mm.id>);
> uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1
> - mg_mqtt_send_header(c, MQTT_CMD_PUBREL, 2, remaining_len);
> - mg_send(c, &id, sizeof(id)); // MQTT5 3.6.1-1, flags = 2
> + if (!mqtt_send_header(c, MQTT_CMD_PUBREL, 2,
> + remaining_len) // MQTT5 3.6.1-1, flags = 2
> + || !mg_send(c, &id, sizeof(id)))
> + goto fail;
> break;
> }
> case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc
> uint16_t id = mg_ntohs(mm.id <http://mm.id>);
> rc = connect(FD(c), &usa.sa <http://usa.sa>, slen); // Attempt to
> issues/9223#issuecomment-2144898336 <https://github.com/Mbed-TLS/
> mbedtls/issues/9223#issuecomment-2144898336>
> --
> You received this message because you are subscribed to the Google
> Groups "swupdate" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to swupdate+u...@googlegroups.com
> <mailto:swupdate+u...@googlegroups.com>.
> To view this discussion visit https://groups.google.com/d/msgid/
> swupdate/119cb848-3393-40e5-bd95-cd5ce0d71498n%40googlegroups.com
> <https://groups.google.com/d/msgid/swupdate/119cb848-3393-40e5-bd95-
> cd5ce0d71498n%40googlegroups.com?utm_medium=email&utm_source=footer>.

Reply all
Reply to author
Forward
0 new messages