From da1e5438bd2c363678a023deb56dce5d16511af0 Mon Sep 17 00:00:00 2001 From: "Deomid \"rojer\" Ryabkov" Date: Sat, 1 May 2021 05:23:07 +0100 Subject: [PATCH] LWIP: (Ab)use mg_conection.priv_2 to pass input interface address This is required to distinguish input interface for multicast traffic and select correct output interface for outgoing traffic (advertisements). http://savannah.nongnu.org/bugs/?49662 removed ability to receive multicast traffic on on interface-bound sockets (making the previous change useless, hence the revert), so for LWIP 2.0.1 and newer multicast packets can only be received on INADDR_ANY listeners. User is then required to distinguish input interface in the receive path. This information is only available in the udp_recv callback, so we must pass this down to dns-sd library somehow, and priv_2 is the how. It is normally used for DNS resolver state, and is never used on listening sockets. We thus avoid growing the mg_connection structure. --- mongoose.c | 46 ++++++++++++++++++---- src/common/platforms/lwip/mg_lwip_net_if.c | 39 +++++++++++++++--- src/mg_net.c | 7 +++- 3 files changed, 78 insertions(+), 14 deletions(-) diff --git a/mongoose.c b/mongoose.c index eb14853c..f32529a9 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2907,10 +2907,13 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) { mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); } } + /* Copy input interface address. */ + if (nc != NULL) nc->priv_2 = lc->priv_2; } if (nc != NULL) { - DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); + DBG(("%p <- %d bytes from %s:%d ia %#x", nc, n, + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port), + (int) (intptr_t) nc->priv_2)); if (nc == lc) { nc->recv_mbuf.len += n; } else { @@ -15171,6 +15174,11 @@ void mg_lwip_if_connect_tcp(struct mg_connection *nc, mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_tcp_tcpip, &ctx); } +struct udp_info { + union socket_address remote_addr; + uint32_t local_ip4; +}; + /* * Lwip included in the SDKs for nRF5x chips has different type for the * callback of `udp_recv()` @@ -15187,18 +15195,22 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len)); /* Put address in a separate pbuf and tack it onto the packet. */ struct pbuf *sap = - pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM); + pbuf_alloc(PBUF_RAW, sizeof(struct udp_info), PBUF_RAM); if (sap == NULL) { pbuf_free(p); return; } - union socket_address *sa = (union socket_address *) sap->payload; + struct udp_info *ui = (struct udp_info *) sap->payload; + struct netif *nif = ip_current_netif(); #if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 - sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; + ui->remote_addr.sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; + ui->local_ip4 = ip_2_ip4(&nif->ip_addr)->addr; #else - sa->sin.sin_addr.s_addr = addr->addr; + ui->remote_addr.sin.sin_addr.s_addr = addr->addr; + ui->local_ip4 = nif->ip_addr.addr; #endif - sa->sin.sin_port = htons(port); + ui->remote_addr.sin.sin_port = htons(port); + /* Logic in the recv handler requires that there be exactly one data pbuf. */ p = pbuf_coalesce(p, PBUF_RAW); pbuf_chain(sap, p); @@ -15238,6 +15250,13 @@ static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len, res = MIN(dp->len, len); pbuf_copy_partial(dp, buf, res, 0); pbuf_free(dp); + const struct udp_info *ui = (struct udp_info *) ap->payload; + *sa = ui->remote_addr; + /* Store local interface address for incoming packets. + * Only for listener because outgoing connections may use priv_2 for DNS. */ + if (nc->listener == NULL) { + nc->priv_2 = (void *) (uintptr_t) ui->local_ip4; + } pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0); pbuf_free(ap); } @@ -15451,11 +15470,19 @@ struct udp_sendto_ctx { struct pbuf *p; ip_addr_t *ip; uint16_t port; + uint32_t mcast_ip4; int ret; }; static void udp_sendto_tcpip(void *arg) { struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg; + if (ctx->mcast_ip4 != 0) { +#if LWIP_VERSION_MAJOR > 1 && LWIP_MULTICAST_TX_OPTIONS + ctx->upcb->mcast_ip4.addr = ctx->mcast_ip4; +#elif LWIP_VERSION_MAJOR == 1 && LWIP_IGMP + ctx->upcb->multicast_ip.addr = ctx->mcast_ip4; +#endif + } ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); } @@ -15474,8 +15501,13 @@ static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data, if (p == NULL) return 0; memcpy(p->payload, data, len); struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; + if (ip_addr_ismulticast(&ip)) { + ctx.mcast_ip4 = (uint32_t) (uintptr_t) nc->priv_2; + } mg_lwip_netif_run_on_tcpip(udp_sendto_tcpip, &ctx); cs->err = ctx.ret; + DBG(("%p udp_sendto %x mc4 %x res %d", nc, (int) ip_2_ip4(ctx.ip)->addr, + (int) ctx.mcast_ip4, (int) cs->err)); pbuf_free(p); return (cs->err == ERR_OK ? (int) len : -2); } diff --git a/src/common/platforms/lwip/mg_lwip_net_if.c b/src/common/platforms/lwip/mg_lwip_net_if.c index 281ddd3f..a83cba92 100644 --- a/src/common/platforms/lwip/mg_lwip_net_if.c +++ b/src/common/platforms/lwip/mg_lwip_net_if.c @@ -250,6 +250,11 @@ void mg_lwip_if_connect_tcp(struct mg_connection *nc, mg_lwip_netif_run_on_tcpip(mg_lwip_if_connect_tcp_tcpip, &ctx); } +struct udp_info { + union socket_address remote_addr; + uint32_t local_ip4; +}; + /* * Lwip included in the SDKs for nRF5x chips has different type for the * callback of `udp_recv()` @@ -266,18 +271,22 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len)); /* Put address in a separate pbuf and tack it onto the packet. */ struct pbuf *sap = - pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM); + pbuf_alloc(PBUF_RAW, sizeof(struct udp_info), PBUF_RAM); if (sap == NULL) { pbuf_free(p); return; } - union socket_address *sa = (union socket_address *) sap->payload; + struct udp_info *ui = (struct udp_info *) sap->payload; + struct netif *nif = ip_current_netif(); #if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105 - sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; + ui->remote_addr.sin.sin_addr.s_addr = ip_2_ip4(addr)->addr; + ui->local_ip4 = ip_2_ip4(&nif->ip_addr)->addr; #else - sa->sin.sin_addr.s_addr = addr->addr; + ui->remote_addr.sin.sin_addr.s_addr = addr->addr; + ui->local_ip4 = nif->ip_addr.addr; #endif - sa->sin.sin_port = htons(port); + ui->remote_addr.sin.sin_port = htons(port); + /* Logic in the recv handler requires that there be exactly one data pbuf. */ p = pbuf_coalesce(p, PBUF_RAW); pbuf_chain(sap, p); @@ -317,6 +326,13 @@ static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len, res = MIN(dp->len, len); pbuf_copy_partial(dp, buf, res, 0); pbuf_free(dp); + const struct udp_info *ui = (struct udp_info *) ap->payload; + *sa = ui->remote_addr; + /* Store local interface address for incoming packets. + * Only for listener because outgoing connections may use priv_2 for DNS. */ + if (nc->listener == NULL) { + nc->priv_2 = (void *) (uintptr_t) ui->local_ip4; + } pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0); pbuf_free(ap); } @@ -530,11 +546,19 @@ struct udp_sendto_ctx { struct pbuf *p; ip_addr_t *ip; uint16_t port; + uint32_t mcast_ip4; int ret; }; static void udp_sendto_tcpip(void *arg) { struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg; + if (ctx->mcast_ip4 != 0) { +#if LWIP_VERSION_MAJOR > 1 && LWIP_MULTICAST_TX_OPTIONS + ctx->upcb->mcast_ip4.addr = ctx->mcast_ip4; +#elif LWIP_VERSION_MAJOR == 1 && LWIP_IGMP + ctx->upcb->multicast_ip.addr = ctx->mcast_ip4; +#endif + } ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); } @@ -553,8 +577,13 @@ static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data, if (p == NULL) return 0; memcpy(p->payload, data, len); struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; + if (ip_addr_ismulticast(&ip)) { + ctx.mcast_ip4 = (uint32_t) (uintptr_t) nc->priv_2; + } mg_lwip_netif_run_on_tcpip(udp_sendto_tcpip, &ctx); cs->err = ctx.ret; + DBG(("%p udp_sendto %x mc4 %x res %d", nc, (int) ip_2_ip4(ctx.ip)->addr, + (int) ctx.mcast_ip4, (int) cs->err)); pbuf_free(p); return (cs->err == ERR_OK ? (int) len : -2); } diff --git a/src/mg_net.c b/src/mg_net.c index f890a7f9..040e5cd6 100644 --- a/src/mg_net.c +++ b/src/mg_net.c @@ -680,10 +680,13 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) { mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); } } + /* Copy input interface address. */ + if (nc != NULL) nc->priv_2 = lc->priv_2; } if (nc != NULL) { - DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); + DBG(("%p <- %d bytes from %s:%d ia %#x", nc, n, + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port), + (int) (intptr_t) nc->priv_2)); if (nc == lc) { nc->recv_mbuf.len += n; } else {