diff --git a/mongoose.c b/mongoose.c index c820e0a2..90d1ef40 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2067,8 +2067,8 @@ bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) { if (dnsc->c == NULL) { dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL); if (dnsc->c == NULL) return false; - dnsc->c->pfn = dns_cb; } + dnsc->c->pfn = dns_cb; // do it always, DHCP-assigned DNS bootstrap needs it return true; } @@ -8871,6 +8871,8 @@ static bool tcpcsum_ok(void *d, void *t) { 0); } +#define MG_IPISLOCAL(i, a) ((a & i->mask) == (i->ip & i->mask)) + #if MG_ENABLE_IPV6 static uint16_t p6csum(void *d, void *p, uint8_t proto, size_t plen) { uint32_t sum; @@ -8979,8 +8981,11 @@ static void onstatechange(struct mg_tcpip_if *ifp) { ifp->is_ip_changed = false; } } else if (ifp->state == MG_TCPIP_STATE_IP) { - if (ifp->gw != 0 && ifp->l2type == MG_TCPIP_L2_ETH) - mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request + if (ifp->l2type == MG_TCPIP_L2_ETH) { + ifp->gw_ready = false; // remove any prior state + if (ifp->gw != 0) // send an unsolicited GW ARP request + mg_tcpip_arp_request(ifp, ifp->gw, NULL); + } } else if (ifp->state == MG_TCPIP_STATE_UP) { srand((unsigned int) mg_millis()); } @@ -9231,6 +9236,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { } static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip); +static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip); static bool dhcp_opt_len_ok(uint8_t len, uint8_t *p, uint8_t *end) { return (len >= 4 && (len & 3) == 0 && p + 6 < end); @@ -9291,16 +9297,15 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint64_t rand; ifp->lease_expire = ifp->now + (uint64_t) lease * 1000; MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - // assume DHCP server = router until ARP resolves - memcpy(ifp->gwmac, mg_l2_getaddr(ifp, pkt->l2), sizeof(ifp->gwmac)); - ifp->gw_ready = true; // NOTE(): actual gw ARP won't retry now if (ifp->ip != ip) ifp->is_ip_changed = true; ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; ifp->state = MG_TCPIP_STATE_IP; // BOUND state mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); if (ifp->enable_req_dns && dns != 0) { - setdns4(ifp, &dns); + setdns4(ifp, &dns); // configure the provided DNS + if (MG_IPISLOCAL(ifp, dns)) // connect to a local DNS at this point + conndns4(ifp, dns); mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns); } if (ifp->enable_req_sntp && sntp != 0) @@ -9351,7 +9356,7 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { res.xid = pkt->dhcp->xid; mac = mg_l2_getaddr(ifp, pkt->l2); if (ifp->enable_get_gateway) { - ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's + ifp->gw = res.yiaddr; // set gw IP, and MAC memcpy(ifp->gwmac, mac, sizeof(ifp->gwmac)); } tx_udp4(ifp, mac, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr, @@ -9608,6 +9613,7 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return; if (changed) ifp->is_ip6_changed = true; ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1]; + ifp->gw6_ready = false; // Remove any prior state if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac)); if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE) { @@ -9622,10 +9628,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { mtu)); ifp->mtu = (uint16_t) mtu; } - if (ifp->state6 != MG_TCPIP_STATE_READY) { - tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution + if (ifp->state6 != MG_TCPIP_STATE_READY) ifp->state6 = MG_TCPIP_STATE_IP; - } onstate6change(ifp); } } @@ -9741,7 +9745,7 @@ static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem, } else #endif { - if (ifp->ip != 0 && ((rem->addr.ip4 & ifp->mask) == (ifp->ip & ifp->mask))) + if (ifp->ip != 0 && MG_IPISLOCAL(ifp, rem->addr.ip4)) return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame if (ifp->gw_ready) // use the router return ifp->gwmac; // ignore source address in frame @@ -10693,7 +10697,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { } // 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 or poison mark + ifp->state = MG_TCPIP_STATE_READY; // gw MAC not resolved, we'll retry onstatechange(ifp); } if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready && @@ -10702,10 +10706,10 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { #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 or poison mark + ifp->state6 = MG_TCPIP_STATE_READY; // gw6 MAC not resolved, we'll retry onstate6change(ifp); } - if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw6_ready && + if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_READY && !ifp->gw6_ready && (ifp->gw6[0] != 0 || ifp->gw6[1] != 0)) tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution #endif @@ -10921,7 +10925,7 @@ void mg_connect_resolved(struct mg_connection *c) { #endif } else { uint32_t rem_ip = c->rem.addr.ip4; - if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) && + if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) && 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)); @@ -11066,10 +11070,31 @@ 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 - MG_ERROR(("DNS connection creation failed")); } +static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip) { + struct mg_connection *c; + struct mg_dns *dnsc = &ifp->mgr->dns4; + if (ifp->mgr->use_dns6) return; + if ((c = mg_alloc_conn(ifp->mgr)) == NULL) { + MG_ERROR(("OOM, DNS connection creation failed")); + return; + } // mg_connect_svc() would block us, so we bypass it here + LIST_ADD_HEAD(struct mg_connection, &ifp->mgr->conns, c); + c->is_udp = true; + c->fd = (void *) (size_t) MG_INVALID_SOCKET; + c->is_client = true; + dnsc->c = c; + mg_call(c, MG_EV_OPEN, (void *) dnsc->url); + MG_DEBUG(("%lu %ld %s", c->id, c->fd, dnsc->url)); + c->rem.addr.ip4 = ip; + c->rem.port = mg_htons(53); + mg_connect_resolved(c); + mg_dnsc_init(ifp->mgr, dnsc); // will fill c->pfn = dns_cb +} + + + void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) { struct connstate *s = (struct connstate *) (c + 1); uint8_t *l2addr = tcpip_mapip(c->mgr->ifp, ip); diff --git a/src/dns.c b/src/dns.c index 54ef627c..8ccb43af 100644 --- a/src/dns.c +++ b/src/dns.c @@ -269,8 +269,8 @@ bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) { if (dnsc->c == NULL) { dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL); if (dnsc->c == NULL) return false; - dnsc->c->pfn = dns_cb; } + dnsc->c->pfn = dns_cb; // do it always, DHCP-assigned DNS bootstrap needs it return true; } diff --git a/src/net_builtin.c b/src/net_builtin.c index c51a2449..95f29e93 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -313,6 +313,8 @@ static bool tcpcsum_ok(void *d, void *t) { 0); } +#define MG_IPISLOCAL(i, a) ((a & i->mask) == (i->ip & i->mask)) + #if MG_ENABLE_IPV6 static uint16_t p6csum(void *d, void *p, uint8_t proto, size_t plen) { uint32_t sum; @@ -421,8 +423,11 @@ static void onstatechange(struct mg_tcpip_if *ifp) { ifp->is_ip_changed = false; } } else if (ifp->state == MG_TCPIP_STATE_IP) { - if (ifp->gw != 0 && ifp->l2type == MG_TCPIP_L2_ETH) - mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request + if (ifp->l2type == MG_TCPIP_L2_ETH) { + ifp->gw_ready = false; // remove any prior state + if (ifp->gw != 0) // send an unsolicited GW ARP request + mg_tcpip_arp_request(ifp, ifp->gw, NULL); + } } else if (ifp->state == MG_TCPIP_STATE_UP) { srand((unsigned int) mg_millis()); } @@ -673,6 +678,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { } static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip); +static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip); static bool dhcp_opt_len_ok(uint8_t len, uint8_t *p, uint8_t *end) { return (len >= 4 && (len & 3) == 0 && p + 6 < end); @@ -733,16 +739,15 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint64_t rand; ifp->lease_expire = ifp->now + (uint64_t) lease * 1000; MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - // assume DHCP server = router until ARP resolves - memcpy(ifp->gwmac, mg_l2_getaddr(ifp, pkt->l2), sizeof(ifp->gwmac)); - ifp->gw_ready = true; // NOTE(): actual gw ARP won't retry now if (ifp->ip != ip) ifp->is_ip_changed = true; ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; ifp->state = MG_TCPIP_STATE_IP; // BOUND state mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); if (ifp->enable_req_dns && dns != 0) { - setdns4(ifp, &dns); + setdns4(ifp, &dns); // configure the provided DNS + if (MG_IPISLOCAL(ifp, dns)) // connect to a local DNS at this point + conndns4(ifp, dns); mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns); } if (ifp->enable_req_sntp && sntp != 0) @@ -793,7 +798,7 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { res.xid = pkt->dhcp->xid; mac = mg_l2_getaddr(ifp, pkt->l2); if (ifp->enable_get_gateway) { - ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's + ifp->gw = res.yiaddr; // set gw IP, and MAC memcpy(ifp->gwmac, mac, sizeof(ifp->gwmac)); } tx_udp4(ifp, mac, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr, @@ -1050,6 +1055,7 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return; if (changed) ifp->is_ip6_changed = true; ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1]; + ifp->gw6_ready = false; // Remove any prior state if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac)); if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE) { @@ -1064,10 +1070,8 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { mtu)); ifp->mtu = (uint16_t) mtu; } - if (ifp->state6 != MG_TCPIP_STATE_READY) { - tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution + if (ifp->state6 != MG_TCPIP_STATE_READY) ifp->state6 = MG_TCPIP_STATE_IP; - } onstate6change(ifp); } } @@ -1183,7 +1187,7 @@ static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem, } else #endif { - if (ifp->ip != 0 && ((rem->addr.ip4 & ifp->mask) == (ifp->ip & ifp->mask))) + if (ifp->ip != 0 && MG_IPISLOCAL(ifp, rem->addr.ip4)) return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame if (ifp->gw_ready) // use the router return ifp->gwmac; // ignore source address in frame @@ -2135,7 +2139,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { } // 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 or poison mark + ifp->state = MG_TCPIP_STATE_READY; // gw MAC not resolved, we'll retry onstatechange(ifp); } if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready && @@ -2144,10 +2148,10 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { #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 or poison mark + ifp->state6 = MG_TCPIP_STATE_READY; // gw6 MAC not resolved, we'll retry onstate6change(ifp); } - if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw6_ready && + if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_READY && !ifp->gw6_ready && (ifp->gw6[0] != 0 || ifp->gw6[1] != 0)) tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution #endif @@ -2363,7 +2367,7 @@ void mg_connect_resolved(struct mg_connection *c) { #endif } else { uint32_t rem_ip = c->rem.addr.ip4; - if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) && + if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) && 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)); @@ -2508,10 +2512,31 @@ 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 - MG_ERROR(("DNS connection creation failed")); } +static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip) { + struct mg_connection *c; + struct mg_dns *dnsc = &ifp->mgr->dns4; + if (ifp->mgr->use_dns6) return; + if ((c = mg_alloc_conn(ifp->mgr)) == NULL) { + MG_ERROR(("OOM, DNS connection creation failed")); + return; + } // mg_connect_svc() would block us, so we bypass it here + LIST_ADD_HEAD(struct mg_connection, &ifp->mgr->conns, c); + c->is_udp = true; + c->fd = (void *) (size_t) MG_INVALID_SOCKET; + c->is_client = true; + dnsc->c = c; + mg_call(c, MG_EV_OPEN, (void *) dnsc->url); + MG_DEBUG(("%lu %ld %s", c->id, c->fd, dnsc->url)); + c->rem.addr.ip4 = ip; + c->rem.port = mg_htons(53); + mg_connect_resolved(c); + mg_dnsc_init(ifp->mgr, dnsc); // will fill c->pfn = dns_cb +} + + + void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) { struct connstate *s = (struct connstate *) (c + 1); uint8_t *l2addr = tcpip_mapip(c->mgr->ifp, ip);