From e1f7f3d7bb68deedad7c127a380cd382bb7fce35 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Thu, 22 Aug 2024 21:19:33 +0100 Subject: [PATCH 1/3] Make ARP probe public. Add ARP event --- mongoose.c | 9 ++++--- mongoose.h | 3 +++ src/net_builtin.c | 9 ++++--- src/net_builtin.h | 3 +++ tutorials/tcpip/pcap-driver/main.c | 39 +++++++++++++++++++++++++++--- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/mongoose.c b/mongoose.c index 000cbc58..732f8a50 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5107,7 +5107,7 @@ static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) { return n; } -static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) { +void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac) { struct eth *eth = (struct eth *) ifp->tx.buf; struct arp *arp = (struct arp *) (eth + 1); memset(eth->dst, 255, sizeof(eth->dst)); @@ -5118,6 +5118,7 @@ static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) { arp->plen = 4; arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip; memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); + if (mac != NULL) memcpy(arp->tha, mac, sizeof(arp->tha)); ether_output(ifp, PDIFF(eth, arp + 1)); } @@ -5126,7 +5127,7 @@ static void onstatechange(struct mg_tcpip_if *ifp) { MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); - arp_ask(ifp, ifp->gw); // unsolicited GW ARP request + mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { MG_ERROR(("Link up")); srand((unsigned int) mg_millis()); @@ -5785,6 +5786,7 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) { 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 + 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); @@ -5834,6 +5836,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { onstatechange(ifp); } 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; @@ -5996,7 +5999,7 @@ void mg_connect_resolved(struct mg_connection *c) { 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)); - arp_ask(ifp, rem_ip); + 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) { diff --git a/mongoose.h b/mongoose.h index de0982e4..b9826139 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2739,6 +2739,8 @@ enum { MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state) MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr + MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str * + MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL MG_TCPIP_EV_USER // Starting ID for user events }; @@ -2782,6 +2784,7 @@ struct mg_tcpip_if { 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); +void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac); extern struct mg_tcpip_driver mg_tcpip_driver_stm32f; extern struct mg_tcpip_driver mg_tcpip_driver_w5500; diff --git a/src/net_builtin.c b/src/net_builtin.c index 76c091ec..64e891f4 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -185,7 +185,7 @@ static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) { return n; } -static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) { +void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac) { struct eth *eth = (struct eth *) ifp->tx.buf; struct arp *arp = (struct arp *) (eth + 1); memset(eth->dst, 255, sizeof(eth->dst)); @@ -196,6 +196,7 @@ static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) { arp->plen = 4; arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip; memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); + if (mac != NULL) memcpy(arp->tha, mac, sizeof(arp->tha)); ether_output(ifp, PDIFF(eth, arp + 1)); } @@ -204,7 +205,7 @@ static void onstatechange(struct mg_tcpip_if *ifp) { MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); - arp_ask(ifp, ifp->gw); // unsolicited GW ARP request + mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { MG_ERROR(("Link up")); srand((unsigned int) mg_millis()); @@ -863,6 +864,7 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) { 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 + 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); @@ -912,6 +914,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { onstatechange(ifp); } 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; @@ -1074,7 +1077,7 @@ void mg_connect_resolved(struct mg_connection *c) { 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)); - arp_ask(ifp, rem_ip); + 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) { diff --git a/src/net_builtin.h b/src/net_builtin.h index e6e625d7..c672d989 100644 --- a/src/net_builtin.h +++ b/src/net_builtin.h @@ -22,6 +22,8 @@ enum { MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state) MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr + MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str * + MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL MG_TCPIP_EV_USER // Starting ID for user events }; @@ -65,6 +67,7 @@ struct mg_tcpip_if { 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); +void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac); extern struct mg_tcpip_driver mg_tcpip_driver_stm32f; extern struct mg_tcpip_driver mg_tcpip_driver_w5500; diff --git a/tutorials/tcpip/pcap-driver/main.c b/tutorials/tcpip/pcap-driver/main.c index dea210b3..7e93e79f 100644 --- a/tutorials/tcpip/pcap-driver/main.c +++ b/tutorials/tcpip/pcap-driver/main.c @@ -6,9 +6,9 @@ #include #include "mongoose.h" -#define MQTT_URL "mqtt://broker.emqx.io:1883" // MQTT broker URL -#define MQTTS_URL "mqtts://mongoose.ws:8883" // HiveMQ does not TLS1.3 -#define MQTT_TOPIC "mg/rx" // Topic to subscribe to +#define MQTT_URL "mqtt://broker.emqx.io:1883" // MQTT broker URL +#define MQTTS_URL "mqtts://mongoose.ws:8883" // HiveMQ does not TLS1.3 +#define MQTT_TOPIC "mg/rx" // Topic to subscribe to // // Taken from broker.emqx.io static const char *s_ca_cert = @@ -192,6 +192,35 @@ static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) { } } +static void if_ev_handler(struct mg_tcpip_if *ifp, int ev, void *ev_data) { + static uint32_t ip = 0; + static unsigned counter = 0; + + // Set initial self-assigned IP + if (ip == 0) ip = MG_IPV4(169, 254, 2, 100); + + // Catch ARP packets. Parse them yourself, that's easy. + if (ev == MG_TCPIP_EV_ARP) { + struct mg_str *frame = ev_data; + MG_INFO(("Iface %p: Got ARP frame", ifp)); + mg_hexdump(frame->buf, frame->len); + // TODO: check for conflict. On conflict, increment ip and reset counter + } + + // Catch 1 second timer events + if (ev == MG_TCPIP_EV_TIMER_1S && ifp->ip == 0) { + MG_INFO(("Sending ARP probe")); + mg_tcpip_arp_request(ifp, ip, NULL); + + // Seems to be no conflict. Assign us an IP + if (counter++ > 2) { + MG_INFO(("Assigning %M, sending ARP probe", mg_print_ip4, &ip)); + ifp->ip = ip; + mg_tcpip_arp_request(ifp, ip, ifp->mac); + } + } +} + int main(int argc, char *argv[]) { const char *iface = "lo0"; // Network iface const char *mac = "02:00:01:02:03:77"; // MAC address @@ -252,6 +281,10 @@ int main(int argc, char *argv[]) { &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); mg_tcpip_init(&mgr, &mif); + // Call order is important: call after mg_tcpip_init() + mif.fn = if_ev_handler; + mif.enable_dhcp_client = false; + MG_INFO(("Init done, starting main loop")); mg_http_listen(&mgr, "http://0.0.0.0:8000", http_ev_handler, NULL); mg_http_listen(&mgr, "https://0.0.0.0:8443", http_ev_handler, "tls enabled"); From 32d90c38dbdd3b9302d5f6b8ce34c9884216623b Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Mon, 7 Oct 2024 15:13:50 -0300 Subject: [PATCH 2/3] tweaks --- mongoose.c | 41 +++++++------ src/net_builtin.c | 41 +++++++------ tutorials/tcpip/pcap-driver/main.c | 92 ++++++++++++++++++------------ 3 files changed, 105 insertions(+), 69 deletions(-) diff --git a/mongoose.c b/mongoose.c index 732f8a50..3ac9e251 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5828,11 +5828,16 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { if (expired_1000ms && ifp->driver->up) { bool up = ifp->driver->up(ifp); bool current = ifp->state != MG_TCPIP_STATE_DOWN; - if (up != current) { - ifp->state = up == false ? MG_TCPIP_STATE_DOWN - : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP - : MG_TCPIP_STATE_READY; - if (!up && ifp->enable_dhcp_client) ifp->ip = 0; + 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_READY; + onstatechange(ifp); + } else if (!ifp->enable_dhcp_client && ifp->state == MG_TCPIP_STATE_UP && + ifp->ip) { + ifp->state = MG_TCPIP_STATE_READY; // ifp->fn has set an IP onstatechange(ifp); } if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down")); @@ -5841,18 +5846,20 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { if (ifp->state == MG_TCPIP_STATE_DOWN) return; // DHCP RFC-2131 (4.4) - if (ifp->state == MG_TCPIP_STATE_UP && expired_1000ms) { - tx_dhcp_discover(ifp); // INIT (4.4.1) - } else if (expired_1000ms && 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) + 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) + } } // Read data from the network diff --git a/src/net_builtin.c b/src/net_builtin.c index 64e891f4..f3384087 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -906,11 +906,16 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { if (expired_1000ms && ifp->driver->up) { bool up = ifp->driver->up(ifp); bool current = ifp->state != MG_TCPIP_STATE_DOWN; - if (up != current) { - ifp->state = up == false ? MG_TCPIP_STATE_DOWN - : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP - : MG_TCPIP_STATE_READY; - if (!up && ifp->enable_dhcp_client) ifp->ip = 0; + 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_READY; + onstatechange(ifp); + } else if (!ifp->enable_dhcp_client && ifp->state == MG_TCPIP_STATE_UP && + ifp->ip) { + ifp->state = MG_TCPIP_STATE_READY; // ifp->fn has set an IP onstatechange(ifp); } if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down")); @@ -919,18 +924,20 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { if (ifp->state == MG_TCPIP_STATE_DOWN) return; // DHCP RFC-2131 (4.4) - if (ifp->state == MG_TCPIP_STATE_UP && expired_1000ms) { - tx_dhcp_discover(ifp); // INIT (4.4.1) - } else if (expired_1000ms && 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) + 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) + } } // Read data from the network diff --git a/tutorials/tcpip/pcap-driver/main.c b/tutorials/tcpip/pcap-driver/main.c index 7e93e79f..01beacf1 100644 --- a/tutorials/tcpip/pcap-driver/main.c +++ b/tutorials/tcpip/pcap-driver/main.c @@ -165,6 +165,8 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { (void) ev_data; } +static struct mg_connection *mqtt_conn = NULL; + static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_OPEN) { MG_INFO(("%lu CREATED", c->id)); @@ -189,36 +191,58 @@ static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) { mm->data.buf, (int) mm->topic.len, mm->topic.buf)); } else if (ev == MG_EV_CLOSE) { MG_INFO(("%lu CLOSED", c->id)); + mqtt_conn = NULL; // Mark that we're closed } } static void if_ev_handler(struct mg_tcpip_if *ifp, int ev, void *ev_data) { - static uint32_t ip = 0; - static unsigned counter = 0; - - // Set initial self-assigned IP - if (ip == 0) ip = MG_IPV4(169, 254, 2, 100); - - // Catch ARP packets. Parse them yourself, that's easy. - if (ev == MG_TCPIP_EV_ARP) { - struct mg_str *frame = ev_data; - MG_INFO(("Iface %p: Got ARP frame", ifp)); - mg_hexdump(frame->buf, frame->len); - // TODO: check for conflict. On conflict, increment ip and reset counter - } - - // Catch 1 second timer events - if (ev == MG_TCPIP_EV_TIMER_1S && ifp->ip == 0) { - MG_INFO(("Sending ARP probe")); - mg_tcpip_arp_request(ifp, ip, NULL); - - // Seems to be no conflict. Assign us an IP - if (counter++ > 2) { - MG_INFO(("Assigning %M, sending ARP probe", mg_print_ip4, &ip)); - ifp->ip = ip; - mg_tcpip_arp_request(ifp, ip, ifp->mac); + // Trigger MQTT connection when we have an IP address + if (ifp->state == MG_TCPIP_STATE_READY && + (ev == MG_TCPIP_EV_ST_CHG || ev == MG_TCPIP_EV_TIMER_1S)) { + struct mg_mqtt_opts opts = {.clean = true}; + if (mqtt_conn == NULL) { + // mqtt_conn = mg_mqtt_connect(ifp->mgr, MQTT_URL, &opts, mqtt_ev_handler, + // NULL); + mqtt_conn = mg_mqtt_connect(ifp->mgr, MQTTS_URL, &opts, mqtt_ev_handler, + "tls enabled"); } } + +#if defined(DISABLE_DHCP) + { + static uint32_t ip = 0; + static unsigned counter = 0; + // Set initial self-assigned IP + if (ip == 0) ip = MG_IPV4(169, 254, 2, 100); + + // restart process on link change + if (ev == MG_TCPIP_EV_ST_CHG && ifp->state == MG_TCPIP_STATE_DOWN) + ifp->ip = 0; + + // Catch ARP packets. Parse them yourself, that's easy. + if (ev == MG_TCPIP_EV_ARP) { + struct mg_str *frame = ev_data; + MG_INFO(("Iface %p: Got ARP frame", ifp)); + mg_hexdump(frame->buf, frame->len); + // TODO: check for conflict. On conflict, increment ip and reset counter + } + + // Catch 1 second timer events + if (ev == MG_TCPIP_EV_TIMER_1S && ifp->state == MG_TCPIP_STATE_UP) { + MG_INFO(("Sending ARP probe")); + mg_tcpip_arp_request(ifp, ip, NULL); + + // Seems to be no conflict. Assign us an IP + if (counter++ > 2) { + MG_INFO(("Assigning %M, sending ARP probe", mg_print_ip4, &ip)); + ifp->ip = ip; // state will change to MG_TCPIP_STATE_READY on next poll + mg_tcpip_arp_request(ifp, ip, ifp->mac); + } + } + } +#else + (void) ev_data; +#endif } int main(int argc, char *argv[]) { @@ -276,31 +300,29 @@ int main(int argc, char *argv[]) { mg_log_set(MG_LL_DEBUG); // Set log level struct mg_tcpip_driver driver = {.tx = pcap_tx, .up = pcap_up, .rx = pcap_rx}; - struct mg_tcpip_if mif = {.driver = &driver, .driver_data = ph}; + struct mg_tcpip_if mif = {.driver = &driver, + .driver_data = ph, +#if defined(DISABLE_DHCP) + .mask = MG_IPV4(255, 255, 255, 0), + .gw = MG_IPV4(169, 254, 2, 1) +#endif +}; sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1], &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); mg_tcpip_init(&mgr, &mif); // Call order is important: call after mg_tcpip_init() mif.fn = if_ev_handler; +#if defined(DISABLE_DHCP) mif.enable_dhcp_client = false; +#endif MG_INFO(("Init done, starting main loop")); mg_http_listen(&mgr, "http://0.0.0.0:8000", http_ev_handler, NULL); mg_http_listen(&mgr, "https://0.0.0.0:8443", http_ev_handler, "tls enabled"); - bool got_ip = false; - while (s_signo == 0) { mg_mgr_poll(&mgr, 100); - - // Trigger MQTT connection when we receive IP address - if (mif.state == MG_TCPIP_STATE_READY && got_ip == false) { - struct mg_mqtt_opts opts = {.clean = true}; - // mg_mqtt_connect(&mgr, MQTT_URL, &opts, mqtt_ev_handler, NULL); - mg_mqtt_connect(&mgr, MQTTS_URL, &opts, mqtt_ev_handler, "tls enabled"); - got_ip = true; - } } mg_mgr_free(&mgr); From 504ef9422e65ac49847abfd800ccc40cab4d7126 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Wed, 9 Oct 2024 16:12:39 -0300 Subject: [PATCH 3/3] fix gw ARP on startup --- mongoose.c | 25 ++++++--- mongoose.h | 5 +- src/net_builtin.c | 25 ++++++--- src/net_builtin.h | 5 +- tutorials/tcpip/pcap-driver/main.c | 89 +++++++++++------------------- 5 files changed, 73 insertions(+), 76 deletions(-) diff --git a/mongoose.c b/mongoose.c index 3ac9e251..3f6ef069 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5127,6 +5127,8 @@ static void onstatechange(struct mg_tcpip_if *ifp) { MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); + } else if (ifp->state == MG_TCPIP_STATE_IP) { + MG_ERROR(("Got IP")); mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { MG_ERROR(("Link up")); @@ -5277,8 +5279,12 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { } else if (pkt->arp->op == mg_htons(2)) { if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return; if (pkt->arp->spa == ifp->gw) { - // Got response for the GW ARP request. Set ifp->gwmac + // Got response for the GW ARP request. Set ifp->gwmac and IP -> READY memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac)); + if (ifp->state == MG_TCPIP_STATE_IP) { + ifp->state = MG_TCPIP_STATE_READY; + onstatechange(ifp); + } } else { struct mg_connection *c = getpeer(ifp->mgr, pkt, false); if (c != NULL && c->is_arplooking) { @@ -5353,7 +5359,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { // assume DHCP server = router until ARP resolves memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; - ifp->state = MG_TCPIP_STATE_READY; // BOUND state + ifp->state = MG_TCPIP_STATE_IP; // BOUND state uint64_t rand; mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); @@ -5401,7 +5407,7 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { res.magic = pkt->dhcp->magic; res.xid = pkt->dhcp->xid; if (ifp->enable_get_gateway) { - ifp->gw = res.yiaddr; + 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), @@ -5818,12 +5824,17 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { #if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS if (expired_1000ms) { - const char *names[] = {"down", "up", "req", "ready"}; + 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)); } #endif + // 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); + } // Handle physical interface up/down status if (expired_1000ms && ifp->driver->up) { bool up = ifp->driver->up(ifp); @@ -5833,11 +5844,11 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { ifp->state = up == false ? MG_TCPIP_STATE_DOWN : ifp->enable_dhcp_client || ifp->ip == 0 ? MG_TCPIP_STATE_UP - : MG_TCPIP_STATE_READY; + : 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_READY; // ifp->fn has set an IP + ifp->state = MG_TCPIP_STATE_IP; // ifp->fn has set an IP onstatechange(ifp); } if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down")); @@ -5953,7 +5964,7 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) { ifp->mtu = MG_TCPIP_MTU_DEFAULT; mgr->extraconnsize = sizeof(struct connstate); if (ifp->ip == 0) ifp->enable_dhcp_client = true; - memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast + memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set best-effort to bcast mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535 ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from // MG_EPHEMERAL_PORT_BASE to 65535 diff --git a/mongoose.h b/mongoose.h index b9826139..727e5557 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2777,8 +2777,9 @@ struct mg_tcpip_if { uint8_t state; // Current 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 and has requested an IP -#define MG_TCPIP_STATE_READY 3 // Interface is up and has an IP assigned +#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 }; void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *); diff --git a/src/net_builtin.c b/src/net_builtin.c index f3384087..6576bde5 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -205,6 +205,8 @@ static void onstatechange(struct mg_tcpip_if *ifp) { MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); + } else if (ifp->state == MG_TCPIP_STATE_IP) { + MG_ERROR(("Got IP")); mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { MG_ERROR(("Link up")); @@ -355,8 +357,12 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { } else if (pkt->arp->op == mg_htons(2)) { if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return; if (pkt->arp->spa == ifp->gw) { - // Got response for the GW ARP request. Set ifp->gwmac + // Got response for the GW ARP request. Set ifp->gwmac and IP -> READY memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac)); + if (ifp->state == MG_TCPIP_STATE_IP) { + ifp->state = MG_TCPIP_STATE_READY; + onstatechange(ifp); + } } else { struct mg_connection *c = getpeer(ifp->mgr, pkt, false); if (c != NULL && c->is_arplooking) { @@ -431,7 +437,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { // assume DHCP server = router until ARP resolves memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; - ifp->state = MG_TCPIP_STATE_READY; // BOUND state + ifp->state = MG_TCPIP_STATE_IP; // BOUND state uint64_t rand; mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); @@ -479,7 +485,7 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { res.magic = pkt->dhcp->magic; res.xid = pkt->dhcp->xid; if (ifp->enable_get_gateway) { - ifp->gw = res.yiaddr; + 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), @@ -896,12 +902,17 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { #if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS if (expired_1000ms) { - const char *names[] = {"down", "up", "req", "ready"}; + 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)); } #endif + // 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); + } // Handle physical interface up/down status if (expired_1000ms && ifp->driver->up) { bool up = ifp->driver->up(ifp); @@ -911,11 +922,11 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { ifp->state = up == false ? MG_TCPIP_STATE_DOWN : ifp->enable_dhcp_client || ifp->ip == 0 ? MG_TCPIP_STATE_UP - : MG_TCPIP_STATE_READY; + : 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_READY; // ifp->fn has set an IP + ifp->state = MG_TCPIP_STATE_IP; // ifp->fn has set an IP onstatechange(ifp); } if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down")); @@ -1031,7 +1042,7 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) { ifp->mtu = MG_TCPIP_MTU_DEFAULT; mgr->extraconnsize = sizeof(struct connstate); if (ifp->ip == 0) ifp->enable_dhcp_client = true; - memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast + memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set best-effort to bcast mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535 ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from // MG_EPHEMERAL_PORT_BASE to 65535 diff --git a/src/net_builtin.h b/src/net_builtin.h index c672d989..75cad292 100644 --- a/src/net_builtin.h +++ b/src/net_builtin.h @@ -60,8 +60,9 @@ struct mg_tcpip_if { uint8_t state; // Current 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 and has requested an IP -#define MG_TCPIP_STATE_READY 3 // Interface is up and has an IP assigned +#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 }; void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *); diff --git a/tutorials/tcpip/pcap-driver/main.c b/tutorials/tcpip/pcap-driver/main.c index 01beacf1..b8646c9d 100644 --- a/tutorials/tcpip/pcap-driver/main.c +++ b/tutorials/tcpip/pcap-driver/main.c @@ -1,7 +1,8 @@ -// Copyright (c) 2022 Cesanta Software Limited +// Copyright (c) 2022-24 Cesanta Software Limited // All rights reserved // -// SNTP example using MIP and pcap driver +// example using MIP and pcap driver +// make CFLAGS_EXTRA="-DMG_TLS=MG_TLS_BUILTIN -DDISABLE_DHCP" for "auto-IP" #include #include "mongoose.h" @@ -10,7 +11,6 @@ #define MQTTS_URL "mqtts://mongoose.ws:8883" // HiveMQ does not TLS1.3 #define MQTT_TOPIC "mg/rx" // Topic to subscribe to -// // Taken from broker.emqx.io static const char *s_ca_cert = "-----BEGIN CERTIFICATE-----\n" "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" @@ -44,29 +44,6 @@ static const char *s_ca_cert = "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" "-----END CERTIFICATE-----\n"; -// "-----BEGIN CERTIFICATE-----\n" -// "MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh" -// "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" -// "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n" -// "MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n" -// "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" -// "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n" -// "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n" -// "2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n" -// "1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n" -// "q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n" -// "tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n" -// "vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n" -// "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n" -// "5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n" -// "1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n" -// "NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n" -// "Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n" -// "8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n" -// "pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n" -// "MrY=\n" -// "-----END CERTIFICATE-----\n"; - static const char *s_tls_cert = "-----BEGIN CERTIFICATE-----\n" "MIIBMTCB2aADAgECAgkAluqkgeuV/zUwCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI\n" @@ -165,8 +142,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { (void) ev_data; } -static struct mg_connection *mqtt_conn = NULL; - static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_OPEN) { MG_INFO(("%lu CREATED", c->id)); @@ -191,21 +166,15 @@ static void mqtt_ev_handler(struct mg_connection *c, int ev, void *ev_data) { mm->data.buf, (int) mm->topic.len, mm->topic.buf)); } else if (ev == MG_EV_CLOSE) { MG_INFO(("%lu CLOSED", c->id)); - mqtt_conn = NULL; // Mark that we're closed } } static void if_ev_handler(struct mg_tcpip_if *ifp, int ev, void *ev_data) { // Trigger MQTT connection when we have an IP address - if (ifp->state == MG_TCPIP_STATE_READY && - (ev == MG_TCPIP_EV_ST_CHG || ev == MG_TCPIP_EV_TIMER_1S)) { + if (ifp->state == MG_TCPIP_STATE_READY && ev == MG_TCPIP_EV_ST_CHG) { struct mg_mqtt_opts opts = {.clean = true}; - if (mqtt_conn == NULL) { - // mqtt_conn = mg_mqtt_connect(ifp->mgr, MQTT_URL, &opts, mqtt_ev_handler, - // NULL); - mqtt_conn = mg_mqtt_connect(ifp->mgr, MQTTS_URL, &opts, mqtt_ev_handler, - "tls enabled"); - } + // mg_mqtt_connect(ifp->mgr, MQTT_URL, &opts, mqtt_ev_handler, NULL); + mg_mqtt_connect(ifp->mgr, MQTTS_URL, &opts, mqtt_ev_handler, "tls enabled"); } #if defined(DISABLE_DHCP) @@ -216,27 +185,31 @@ static void if_ev_handler(struct mg_tcpip_if *ifp, int ev, void *ev_data) { if (ip == 0) ip = MG_IPV4(169, 254, 2, 100); // restart process on link change - if (ev == MG_TCPIP_EV_ST_CHG && ifp->state == MG_TCPIP_STATE_DOWN) + if (ev == MG_TCPIP_EV_ST_CHG && ifp->state == MG_TCPIP_STATE_DOWN) { ifp->ip = 0; - - // Catch ARP packets. Parse them yourself, that's easy. - if (ev == MG_TCPIP_EV_ARP) { - struct mg_str *frame = ev_data; - MG_INFO(("Iface %p: Got ARP frame", ifp)); - mg_hexdump(frame->buf, frame->len); - // TODO: check for conflict. On conflict, increment ip and reset counter + counter = 0; } - // Catch 1 second timer events - if (ev == MG_TCPIP_EV_TIMER_1S && ifp->state == MG_TCPIP_STATE_UP) { - MG_INFO(("Sending ARP probe")); - mg_tcpip_arp_request(ifp, ip, NULL); + if (ifp->state == MG_TCPIP_STATE_UP) { + // Catch ARP packets. Parse them yourself, that's easy. + if (ev == MG_TCPIP_EV_ARP) { + struct mg_str *frame = ev_data; + MG_INFO(("Iface %p: Got ARP frame", ifp)); + mg_hexdump(frame->buf, frame->len); + // TODO: check for conflict. On conflict, increment ip and reset counter + } - // Seems to be no conflict. Assign us an IP - if (counter++ > 2) { - MG_INFO(("Assigning %M, sending ARP probe", mg_print_ip4, &ip)); - ifp->ip = ip; // state will change to MG_TCPIP_STATE_READY on next poll - mg_tcpip_arp_request(ifp, ip, ifp->mac); + // Catch 1 second timer events + if (ev == MG_TCPIP_EV_TIMER_1S) { + if (++counter <= 3) { + MG_INFO(("Sending ARP probe")); + mg_tcpip_arp_request(ifp, ip, NULL); + } else { + // Seems to be no conflict. Assign us an IP + MG_INFO(("Assigning %M, sending gratuitous ARP", mg_print_ip4, &ip)); + ifp->ip = ip; // state will change to MG_TCPIP_STATE_IP on next poll + mg_tcpip_arp_request(ifp, ip, ifp->mac); + } } } } @@ -302,17 +275,17 @@ int main(int argc, char *argv[]) { struct mg_tcpip_driver driver = {.tx = pcap_tx, .up = pcap_up, .rx = pcap_rx}; struct mg_tcpip_if mif = {.driver = &driver, .driver_data = ph, + .enable_mac_check = true, #if defined(DISABLE_DHCP) .mask = MG_IPV4(255, 255, 255, 0), - .gw = MG_IPV4(169, 254, 2, 1) + .gw = MG_IPV4(169, 254, 2, 1), #endif -}; + .fn = if_ev_handler}; sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1], &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); mg_tcpip_init(&mgr, &mif); - // Call order is important: call after mg_tcpip_init() - mif.fn = if_ev_handler; + // order is important: set after calling mg_tcpip_init() #if defined(DISABLE_DHCP) mif.enable_dhcp_client = false; #endif