Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ typedef err_t (*wireguard_derp_output_fn)(const uint8_t *peer_public_key, const
// len: length of data
// Returns: ERR_OK on success, error code on failure
typedef err_t (*wireguard_udp_output_fn)(uint32_t dest_ip, uint16_t dest_port, const uint8_t *data, size_t len, void *ctx);
/* Same thing, but handed the encrypted pbuf itself. The byte-buffer form above
* forces a mem_malloc and a linearising copy here, and typically a second
* pbuf_alloc and copy inside the integrator's sender - two allocations and two
* ~1.3 KB copies per packet, all of it on the TCPIP thread. The encrypted pbuf
* is already PBUF_TRANSPORT with room for the UDP/IP headers, so udp_sendto()
* can take it as-is. */
typedef err_t (*wireguard_udp_output_pbuf_fn)(uint32_t dest_ip, uint16_t dest_port, struct pbuf *q, void *ctx);

struct wireguard_keypair {
bool valid;
Expand Down Expand Up @@ -207,6 +214,8 @@ struct wireguard_device {
// UDP output callback for magicsock mode (external unified socket)
wireguard_udp_output_fn udp_output_fn;
void *udp_output_ctx;
wireguard_udp_output_pbuf_fn udp_output_pbuf_fn;
void *udp_output_pbuf_ctx;

// Force all peer output through DERP relay (cellular mode)
bool force_derp_output;
Expand Down
22 changes: 22 additions & 0 deletions components/microlink/components/wireguard_lwip/src/wireguardif.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ static err_t wireguardif_peer_output(struct netif *netif, struct pbuf *q, struct
return ERR_RTE;
}

// Zero-copy magicsock output: hand the encrypted pbuf over untouched.
if (device->udp_output_pbuf_fn) {
uint32_t dest_ip = ip4_addr_get_u32(ip_2_ip4(&peer->ip));
return device->udp_output_pbuf_fn(dest_ip, peer->port, q, device->udp_output_pbuf_ctx);
}

// In magicsock mode, use external UDP output callback
if (device->udp_output_fn) {
WG_DEBUG("[WG_OUT] Using magicsock callback\n");
Expand Down Expand Up @@ -191,6 +197,11 @@ static err_t wireguardif_peer_output(struct netif *netif, struct pbuf *q, struct
}

static err_t wireguardif_device_output(struct wireguard_device *device, struct pbuf *q, const ip_addr_t *ipaddr, u16_t port) {
if (device->udp_output_pbuf_fn) {
uint32_t dest_ip = ip4_addr_get_u32(ip_2_ip4(ipaddr));
return device->udp_output_pbuf_fn(dest_ip, port, q, device->udp_output_pbuf_ctx);
}

// In magicsock mode, use external UDP output callback
if (device->udp_output_fn) {
uint8_t *data = (uint8_t *)mem_malloc(q->tot_len);
Expand Down Expand Up @@ -1376,6 +1387,17 @@ void wireguardif_set_udp_output(struct netif *netif, wireguard_udp_output_fn fn,
}
}

void wireguardif_set_udp_output_pbuf(struct netif *netif, wireguard_udp_output_pbuf_fn fn, void *ctx) {
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("state != NULL", (netif->state != NULL));
struct wireguard_device *device = (struct wireguard_device *)netif->state;
if (device->valid) {
device->udp_output_pbuf_fn = fn;
device->udp_output_pbuf_ctx = ctx;
WG_DEBUG("[WG] zero-copy UDP output callback registered\n");
}
}

void wireguardif_force_derp_output(struct netif *netif, bool force) {
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("state != NULL", (netif->state != NULL));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ void wireguardif_disable_socket_bind(void);
// via an external unified socket instead of the internal lwIP UDP PCB.
void wireguardif_set_udp_output(struct netif *netif, wireguard_udp_output_fn fn, void *ctx);

/* Zero-copy variant. Takes precedence over wireguardif_set_udp_output() when
* set; prefer it whenever the sender can consume a pbuf. */
void wireguardif_set_udp_output_pbuf(struct netif *netif, wireguard_udp_output_pbuf_fn fn, void *ctx);

// Force all peer output through DERP relay callback (cellular mode).
// When enabled, peer_output always uses DERP even if peer has a direct endpoint.
void wireguardif_force_derp_output(struct netif *netif, bool force);
Expand Down
18 changes: 18 additions & 0 deletions components/microlink/src/ml_wg_mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ static err_t wg_derp_output_cb(const uint8_t *peer_public_key,
* on that thread. */
static struct udp_pcb *s_wg_output_pcb = NULL;

/* Zero-copy WireGuard output. The encrypted pbuf is already PBUF_TRANSPORT
* with header room, so it goes straight to udp_sendto - no mem_malloc, no
* linearise, no second pbuf, no second copy. Ownership stays with
* wireguardif: udp_sendto does not consume q, and this callback is
* synchronous, so the caller's pbuf_free still applies. */
static err_t wg_udp_output_pbuf_cb(uint32_t dest_ip, uint16_t dest_port,
struct pbuf *q, void *ctx) {
(void)ctx;
if (!s_wg_output_pcb) return ERR_CONN;

ip_addr_t dst;
IP_SET_TYPE_VAL(dst, IPADDR_TYPE_V4);
ip4_addr_set_u32(ip_2_ip4(&dst), dest_ip); /* already network byte order */

return udp_sendto(s_wg_output_pcb, q, &dst, dest_port);
}

static err_t wg_udp_output_cb(uint32_t dest_ip, uint16_t dest_port,
const uint8_t *data, size_t len, void *ctx) {
microlink_t *ml = (microlink_t *)ctx;
Expand Down Expand Up @@ -291,6 +308,7 @@ static esp_err_t wg_init_interface(microlink_t *ml) {
/* Register output callbacks for magicsock mode */
wireguardif_set_derp_output(netif, wg_derp_output_cb, ml);
wireguardif_set_udp_output(netif, wg_udp_output_cb, ml);
wireguardif_set_udp_output_pbuf(netif, wg_udp_output_pbuf_cb, ml);

/* On cellular AT socket bridge, force all WG output through DERP relay.
* AT sockets are TCP-only, so direct UDP is impossible.
Expand Down