From 550e9327cccb75b60f66335d57d9501df202959a Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com> Date: Mon, 18 May 2026 22:17:53 +0200 Subject: [PATCH 1/3] Set `sa` before attempting to set `sa->sin6_family` Fixes #711. Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com> --- NEWS | 1 + src/worker-proxyproto.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index ca879ccf..dbd029ab 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ * /cert.cer * /ca.pem * /ca.cer +- Fix incorrect local address when using PROXY protocol with IPv6 (#711). * Version 1.4.2 (released 2026-04-16) diff --git a/src/worker-proxyproto.c b/src/worker-proxyproto.c index e91a181e..b48dedbb 100644 --- a/src/worker-proxyproto.c +++ b/src/worker-proxyproto.c @@ -473,8 +473,8 @@ int parse_proxy_proto_header(struct worker_st *ws, int fd) ws->remote_addr_len = sizeof(struct sockaddr_in6); memset(&ws->our_addr, 0, sizeof(ws->our_addr)); - sa->sin6_family = AF_INET6; sa = (void *)&ws->our_addr; + sa->sin6_family = AF_INET6; memcpy(&sa->sin6_addr, p + 16, 16); memcpy(&sa->sin6_port, p + 34, 2); ws->our_addr_len = sizeof(struct sockaddr_in6); From d95d1ac53e3784ee6c45a8586e3d05e656a89623 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com> Date: Mon, 18 May 2026 22:21:42 +0200 Subject: [PATCH 2/3] Consistency for readability Consistency across IPv4 and IPv6 paths. Set `sin_addr` first and then `sin_port`. Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com> --- src/worker-proxyproto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker-proxyproto.c b/src/worker-proxyproto.c index b48dedbb..abf4f0c6 100644 --- a/src/worker-proxyproto.c +++ b/src/worker-proxyproto.c @@ -443,8 +443,8 @@ int parse_proxy_proto_header(struct worker_st *ws, int fd) memset(&ws->remote_addr, 0, sizeof(ws->remote_addr)); sa->sin_family = AF_INET; - memcpy(&sa->sin_port, p + 8, 2); memcpy(&sa->sin_addr, p, 4); + memcpy(&sa->sin_port, p + 8, 2); ws->remote_addr_len = sizeof(struct sockaddr_in); memset(&ws->our_addr, 0, sizeof(ws->our_addr)); From 1b6e22246d538ca33b4e2aff5f8e256eb717ae85 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Sat, 23 May 2026 14:52:01 +0000 Subject: [PATCH 3/3] Test proxy protocol parser with IPv6 packet Relates: #711 Signed-off-by: Nikos Mavrogiannopoulos --- tests/proxyproto-v2.c | 122 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/tests/proxyproto-v2.c b/tests/proxyproto-v2.c index d1fed51f..3dca6400 100644 --- a/tests/proxyproto-v2.c +++ b/tests/proxyproto-v2.c @@ -130,6 +130,55 @@ static size_t build_v2_packet(uint8_t *buf, size_t bufsz, return pos; } +/* + * Build a proxy protocol v2 packet with an IPv6 address block into buf[]. + * Returns the total number of bytes written. + */ +static size_t build_v2_ipv6_packet(uint8_t *buf, size_t bufsz, + const struct in6_addr *src_addr, + uint16_t src_port, + const struct in6_addr *dst_addr, + uint16_t dst_port) +{ + size_t pos = 0; + uint16_t sp, dp, plen; + + memcpy(buf + pos, PP2_SIG, 12); + pos += 12; + buf[pos++] = 0x21; /* ver=2, PROXY command */ + buf[pos++] = 0x21; /* AF_INET6 (0x2) | TCP (0x1) */ + + size_t len_offset = pos; + buf[pos++] = 0; + buf[pos++] = 0; + + size_t payload_start = pos; + + /* IPv6 address block (36 bytes): + * src IP (16 bytes) + * dst IP (16 bytes) + * src port (2 bytes, network order) + * dst port (2 bytes, network order) + */ + memcpy(buf + pos, src_addr, 16); + pos += 16; + memcpy(buf + pos, dst_addr, 16); + pos += 16; + sp = htons(src_port); + dp = htons(dst_port); + memcpy(buf + pos, &sp, 2); + pos += 2; + memcpy(buf + pos, &dp, 2); + pos += 2; + + plen = (uint16_t)(pos - payload_start); + buf[len_offset] = (plen >> 8) & 0xff; + buf[len_offset + 1] = plen & 0xff; + + assert(pos <= bufsz); + return pos; +} + /* * Feed pkt into a pipe and call parse_proxy_proto_header. * ws->conn_type must be set by the caller. @@ -275,5 +324,78 @@ int main(void) return 1; } + /* ------------------------------------------------------------------ */ + /* Test 6: IPv6 proxy protocol v2 — both remote and local addr correct */ + /* */ + /* Reproducer for the bug where sa->sin6_family was assigned before sa */ + /* was redirected to &ws->our_addr, leaving our_addr.sin6_family == 0. */ + /* ------------------------------------------------------------------ */ + { + struct in6_addr src6, dst6; + struct sockaddr_in6 *rem6, *loc6; + + memset(&ws, 0, sizeof(ws)); + ws.conn_type = SOCK_TYPE_TCP; + + inet_pton(AF_INET6, "2001:db8::1", &src6); + inet_pton(AF_INET6, "2001:db8::2", &dst6); + + pkt_len = build_v2_ipv6_packet(pkt, sizeof(pkt), &src6, 1234, + &dst6, 443); + ret = run_parse(&ws, pkt, pkt_len); + if (ret != 0) { + fprintf(stderr, "Test 6: parse failed (%d)\n", ret); + return 1; + } + + if (ws.remote_addr_len != sizeof(struct sockaddr_in6)) { + fprintf(stderr, "Test 6: remote_addr_len wrong (%u)\n", + ws.remote_addr_len); + return 1; + } + if (ws.our_addr_len != sizeof(struct sockaddr_in6)) { + fprintf(stderr, "Test 6: our_addr_len wrong (%u)\n", + ws.our_addr_len); + return 1; + } + + rem6 = (void *)&ws.remote_addr; + loc6 = (void *)&ws.our_addr; + + if (rem6->sin6_family != AF_INET6) { + fprintf(stderr, + "Test 6: remote sin6_family wrong: %d\n", + rem6->sin6_family); + return 1; + } + /* This check catches the bug: sin6_family was written to + * remote_addr a second time instead of to our_addr. */ + if (loc6->sin6_family != AF_INET6) { + fprintf(stderr, + "Test 6: local sin6_family wrong: %d (expected %d)\n", + loc6->sin6_family, AF_INET6); + return 1; + } + + if (memcmp(&rem6->sin6_addr, &src6, 16) != 0) { + fprintf(stderr, "Test 6: remote address mismatch\n"); + return 1; + } + if (memcmp(&loc6->sin6_addr, &dst6, 16) != 0) { + fprintf(stderr, "Test 6: local address mismatch\n"); + return 1; + } + if (ntohs(rem6->sin6_port) != 1234) { + fprintf(stderr, "Test 6: remote port wrong: %u\n", + ntohs(rem6->sin6_port)); + return 1; + } + if (ntohs(loc6->sin6_port) != 443) { + fprintf(stderr, "Test 6: local port wrong: %u\n", + ntohs(loc6->sin6_port)); + return 1; + } + } + return 0; }