Repository navigation
USB-NCM DHCP-Server shared with WiFi-AP DHCP-Server -> local link IP on AP #19685
Copy link
Copy link
Open
Labels
Milestone
Description
Activity
Possible patches (I currently use myself)
cyw32-driverFrom 982b0ece8661cb2c9029121e23e81066bbbab331 Mon Sep 17 00:00:00 2001 From: SciLor <[email protected]> Date: Sat, 5 Sep 2026 23:45:01 +0000 Subject: [PATCH] usb-ncm dhcp fix: pass netif pointer to dhcp_server_init Pass the AP interface's struct netif pointer to dhcp_server_init() to allow binding and pinning the DHCP socket to the specific netif. --- src/cyw43_lwip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cyw43_lwip.c b/src/cyw43_lwip.c index 1bb5c6b..788169b 100644 --- a/src/cyw43_lwip.c +++ b/src/cyw43_lwip.c @@ -235,7 +235,7 @@ void cyw43_cb_tcpip_init(cyw43_t *self, int itf) { #endif } else { #if CYW43_NETUTILS - dhcp_server_init(&self->dhcp_server, &ipconfig[0], &ipconfig[1]); + dhcp_server_init(&self->dhcp_server, &ipconfig[0], &ipconfig[1], n); #endif } -- 2.43.0Micropython
From 920995b18a59e3af594274e980ad0d67fa76d26b Mon Sep 17 00:00:00 2001 From: SciLor <[email protected]> Date: Sat, 5 Sep 2026 23:44:07 +0000 Subject: [PATCH] extmod/dhcp: Update dhcp_server_init to accept netif parameter for improved binding Bind the DHCP server socket to the specific server IP and netif rather than 0.0.0.0 (INADDR_ANY). This allows multiple DHCP servers (such as WiFi AP and USBD_NCM) to operate concurrently without UDP port 67 collisions. --- extmod/network_usbd_ncm.c | 2 +- shared/netutils/dhcpserver.c | 25 +++++++++++++++++++------ shared/netutils/dhcpserver.h | 4 +++- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/extmod/network_usbd_ncm.c b/extmod/network_usbd_ncm.c index d6ae152a8..ab1e5b402 100644 --- a/extmod/network_usbd_ncm.c +++ b/extmod/network_usbd_ncm.c @@ -201,7 +201,7 @@ static void ncm_init(void) { #endif #if MICROPY_PY_NETWORK_USBD_NCM_DHCP_SERVER - dhcp_server_init(&ncm_obj.dhcp_server, &ncm_obj.ipaddr, &ncm_obj.netmask); + dhcp_server_init(&ncm_obj.dhcp_server, &ncm_obj.ipaddr, &ncm_obj.netmask, netif); // Don't advertise a default gateway -- this is a point-to-point USB link // and advertising a router causes the host to route all traffic over USB. ncm_obj.dhcp_server.send_router = false; diff --git a/shared/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c index c54dad357..95680312f 100644 --- a/shared/netutils/dhcpserver.c +++ b/shared/netutils/dhcpserver.c @@ -109,11 +109,9 @@ static void dhcp_socket_free(struct udp_pcb **udp) { } } -static int dhcp_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) { - ip_addr_t addr; - IP_ADDR4(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); +static int dhcp_socket_bind(struct udp_pcb **udp, ip_addr_t *ip, uint16_t port) { // TODO convert lwIP errors to errno - return udp_bind(*udp, &addr, port); + return udp_bind(*udp, ip, port); } static int dhcp_socket_sendto(struct udp_pcb **udp, struct netif *netif, const void *buf, size_t len, uint32_t ip, uint16_t port) { @@ -293,7 +291,7 @@ ignore_request: pbuf_free(p); } -void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm, struct netif *netif) { ip_addr_copy(d->ip, *ip); ip_addr_copy(d->nm, *nm); memset(d->lease, 0, sizeof(d->lease)); @@ -301,7 +299,22 @@ void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { return; } - dhcp_socket_bind(&d->udp, 0, PORT_DHCP_SERVER); + // Bind to this server's own specific address, not the wildcard + // 0.0.0.0 the old `dhcp_socket_bind(&d->udp, 0, ...)` used - and pin + // the socket to its own netif. Without both of these, a second + // dhcp_server_init() call for a different interface (e.g. a WiFi AP + // and USBD_NCM both active at once) silently fails to bind at all: + // lwIP's udp_bind() rejects a second PCB on the same port whenever + // either side of the conflict is the wildcard address, so only the + // first-initialized server ever actually receives anything, and every + // client on every interface ends up served from that one server's + // pool regardless of which physical link it actually asked on - see + // the "AP-mode DHCP server hands out addresses from another active + // interface's DHCP server" issue this fixes. + dhcp_socket_bind(&d->udp, &d->ip, PORT_DHCP_SERVER); + if (netif != NULL) { + udp_bind_netif(d->udp, netif); + } } void dhcp_server_deinit(dhcp_server_t *d) { diff --git a/shared/netutils/dhcpserver.h b/shared/netutils/dhcpserver.h index 24224d6aa..f06794c6c 100644 --- a/shared/netutils/dhcpserver.h +++ b/shared/netutils/dhcpserver.h @@ -28,6 +28,8 @@ #include "lwip/ip_addr.h" +struct netif; + #define DHCPS_BASE_IP (16) #define DHCPS_MAX_IP (8) @@ -44,7 +46,7 @@ typedef struct _dhcp_server_t { bool send_router; // advertise server IP as default gateway } dhcp_server_t; -void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm, struct netif *netif); void dhcp_server_deinit(dhcp_server_t *d); #endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H -- 2.43.0
Port, board and/or hardware
pico2w
MicroPython version
v1.29.0
Reproduction
Build with USB-NCM enabled and run this on a Pico W/Pico 2 W:
Then connect any WiFi client (phone/laptop) to repro-ap. Expected: the client gets a 192.168.4.x lease. Actual: the client gets a 169.254.X.y lease (confirm via the client's own reported gateway/DNS, which will read back as the USB-NCM interface's own 169.254.X.1 address) and cannot reach 192.168.4.1.
Expected behaviour
Get an IP-Address in the 192.168.0.0/16 range
Observed behaviour
Getting a local link 169.254 IP via the WiFi AP DHCP, which is identical to the one on the USB-NCM
Additional Information
Suggested fix
Scope each DHCP server's UDP PCB to its own interface with the existing udp_bind_netif() API, so lwIP's first-line netif check in udp_input_local_match() (if (pcb->netif_idx != NETIF_NO_INDEX && pcb->netif_idx != netif_get_index(current_input_netif)) return 0;) rejects it outright for any broadcast arriving on a different interface:
Add a struct netif *netif parameter to dhcp_server_init() (shared/netutils/dhcpserver.h/.c).
In dhcp_server_init(), after dhcp_socket_bind()'s udp_bind() call, add udp_bind_netif(d->udp, netif);.
Update both call sites to pass their own netif:
extmod/network_cyw43.c's AP-mode cyw43_cb_tcpip_init() path (in lib/cyw43-driver/src/cyw43_lwip.c) — pass n (the netif it just created).
extmod/network_usbd_ncm.c's ncm_init() — pass &ncm_obj.netif.
This keeps both DHCP servers fully functional and correctly isolated to their own interface, rather than requiring either one to be disabled to avoid the collision.
Code of Conduct
Yes, I agree