From 504ef9422e65ac49847abfd800ccc40cab4d7126 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Wed, 9 Oct 2024 16:12:39 -0300 Subject: [PATCH] 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