This is an automated email from the ASF dual-hosted git repository. masaori pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push: new f7bdee6 Add incoming PROXY Protocol v2 support (#7340) f7bdee6 is described below commit f7bdee616ebec26b2746ce50c75eaacab7571554 Author: Masaori Koshiba <masa...@apache.org> AuthorDate: Tue Jan 26 08:04:44 2021 +0900 Add incoming PROXY Protocol v2 support (#7340) TCP support only for now. UDP, UNIX Domain Socket, and TLVs are out of scope. --- .../configuration/proxy-protocol.en.rst | 6 +- iocore/net/ProxyProtocol.cc | 137 +++++++++- iocore/net/ProxyProtocol.h | 2 +- iocore/net/unit_tests/test_ProxyProtocol.cc | 292 ++++++++++++++++++++- 4 files changed, 424 insertions(+), 13 deletions(-) diff --git a/doc/admin-guide/configuration/proxy-protocol.en.rst b/doc/admin-guide/configuration/proxy-protocol.en.rst index 8df27d1..adf61f6 100644 --- a/doc/admin-guide/configuration/proxy-protocol.en.rst +++ b/doc/admin-guide/configuration/proxy-protocol.en.rst @@ -31,7 +31,7 @@ TLS connections. .. note:: - The current version only supports transforming client IP from PROXY Version 1 + The current version only supports transforming client IP from PROXY Version 1/2 header to the Forwarded: header. In the current implementation, the client IP address in the PROXY protocol header @@ -41,7 +41,7 @@ is passed to the origin server via an HTTP `Forwarded: The Proxy Protocol must be enabled on each port. See :ts:cv:`proxy.config.http.server_ports` for information on how to enable the Proxy Protocol on a port. Once enabled, all incoming requests must be prefaced -with the PROXY v1 header. Any request not preface by this header will be +with the PROXY v1/v2 header. Any request not preface by this header will be dropped. As a security measure, an optional list of trusted IP addresses may be @@ -50,7 +50,7 @@ configured with :ts:cv:`proxy.config.http.proxy_protocol_allowlist`. .. important:: If the allowlist is configured, requests will only be accepted from these - IP addresses and must be prefaced with the PROXY v1 header. + IP addresses and must be prefaced with the PROXY v1/v2 header. See :ts:cv:`proxy.config.http.insert_forwarded` for configuration information. Detection of the PROXY protocol header is automatic. If the PROXY header diff --git a/iocore/net/ProxyProtocol.cc b/iocore/net/ProxyProtocol.cc index 2de8673..452f63b 100644 --- a/iocore/net/ProxyProtocol.cc +++ b/iocore/net/ProxyProtocol.cc @@ -34,15 +34,57 @@ namespace using namespace std::literals; constexpr ts::TextView PPv1_CONNECTION_PREFACE = "PROXY"sv; -constexpr ts::TextView PPv2_CONNECTION_PREFACE = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x02"sv; +constexpr ts::TextView PPv2_CONNECTION_PREFACE = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"sv; constexpr size_t PPv1_CONNECTION_HEADER_LEN_MIN = 15; -constexpr size_t PPv2_CONNECTION_HEADER_LEN_MIN = 16; constexpr ts::TextView PPv1_PROTO_UNKNOWN = "UNKNOWN"sv; constexpr ts::TextView PPv1_PROTO_TCP4 = "TCP4"sv; constexpr ts::TextView PPv1_PROTO_TCP6 = "TCP6"sv; +constexpr uint8_t PPv2_CMD_LOCAL = 0x20; +constexpr uint8_t PPv2_CMD_PROXY = 0x21; + +constexpr uint8_t PPv2_PROTO_UNSPEC = 0x00; +constexpr uint8_t PPv2_PROTO_TCP4 = 0x11; +constexpr uint8_t PPv2_PROTO_UDP4 = 0x12; +constexpr uint8_t PPv2_PROTO_TCP6 = 0x21; +constexpr uint8_t PPv2_PROTO_UDP6 = 0x22; +constexpr uint8_t PPv2_PROTO_UNIX_STREAM = 0x31; +constexpr uint8_t PPv2_PROTO_UNIX_DATAGRAM = 0x32; + +constexpr uint16_t PPv2_ADDR_LEN_INET = 4 + 4 + 2 + 2; +constexpr uint16_t PPv2_ADDR_LEN_INET6 = 16 + 16 + 2 + 2; +// constexpr uint16_t PPv2_ADDR_LEN_UNIX = 108 + 108; + +struct PPv2Hdr { + uint8_t sig[12]; ///< preface + uint8_t ver_cmd; ///< protocol version and command + uint8_t fam; ///< protocol family and transport + uint16_t len; ///< number of following bytes part of the header + union { + // for TCP/UDP over IPv4, len = 12 (PPv2_ADDR_LEN_INET) + struct { + uint32_t src_addr; + uint32_t dst_addr; + uint16_t src_port; + uint16_t dst_port; + } ip4; + // for TCP/UDP over IPv6, len = 36 (PPv2_ADDR_LEN_INET6) + struct { + uint8_t src_addr[16]; + uint8_t dst_addr[16]; + uint16_t src_port; + uint16_t dst_port; + } ip6; + // for AF_UNIX sockets, len = 216 (PPv2_ADDR_LEN_UNIX) + struct { + uint8_t src_addr[108]; + uint8_t dst_addr[108]; + } unix; + } addr; +}; + /** PROXY Protocol v1 Parser @@ -166,13 +208,100 @@ proxy_protocol_v1_parse(ProxyProtocol *pp_info, ts::TextView hdr) /** PROXY Protocol v2 Parser + TODO: TLVs Support + @return read length */ size_t -proxy_protocol_v2_parse(ProxyProtocol * /*pp_info*/, ts::TextView /*hdr*/) +proxy_protocol_v2_parse(ProxyProtocol *pp_info, const ts::TextView &msg) { + ink_release_assert(msg.size() >= PPv2_CONNECTION_HEADER_LEN); + + const PPv2Hdr *hdr_v2 = reinterpret_cast<const PPv2Hdr *>(msg.data()); + + // Assuming PREFACE check is done + + // length check + const uint16_t len = ntohs(hdr_v2->len); + const size_t total_len = PPv2_CONNECTION_HEADER_LEN + len; + + if (msg.size() < total_len) { + return 0; + } + + // protocol version and command + switch (hdr_v2->ver_cmd) { + case PPv2_CMD_LOCAL: { + // protocol byte should be UNSPEC (\x00) with LOCAL command + if (hdr_v2->fam != PPv2_PROTO_UNSPEC) { + return 0; + } + + pp_info->version = ProxyProtocolVersion::V2; + pp_info->ip_family = AF_UNSPEC; + + return total_len; + } + case PPv2_CMD_PROXY: { + switch (hdr_v2->fam) { + case PPv2_PROTO_TCP4: { + if (len < PPv2_ADDR_LEN_INET) { + return 0; + } + + IpAddr src_addr(reinterpret_cast<in_addr_t>(hdr_v2->addr.ip4.src_addr)); + pp_info->src_addr.assign(src_addr, hdr_v2->addr.ip4.src_port); + + IpAddr dst_addr(reinterpret_cast<in_addr_t>(hdr_v2->addr.ip4.dst_addr)); + pp_info->dst_addr.assign(dst_addr, hdr_v2->addr.ip4.dst_port); + + pp_info->version = ProxyProtocolVersion::V2; + pp_info->ip_family = AF_INET; + + break; + } + case PPv2_PROTO_TCP6: { + if (len < PPv2_ADDR_LEN_INET6) { + return 0; + } + + IpAddr src_addr(reinterpret_cast<in6_addr const &>(hdr_v2->addr.ip6.src_addr)); + pp_info->src_addr.assign(src_addr, hdr_v2->addr.ip6.src_port); + + IpAddr dst_addr(reinterpret_cast<in6_addr const &>(hdr_v2->addr.ip6.dst_addr)); + pp_info->dst_addr.assign(dst_addr, hdr_v2->addr.ip6.dst_port); + + pp_info->version = ProxyProtocolVersion::V2; + pp_info->ip_family = AF_INET6; + + break; + } + case PPv2_PROTO_UDP4: + [[fallthrough]]; + case PPv2_PROTO_UDP6: + [[fallthrough]]; + case PPv2_PROTO_UNIX_STREAM: + [[fallthrough]]; + case PPv2_PROTO_UNIX_DATAGRAM: + [[fallthrough]]; + case PPv2_PROTO_UNSPEC: + [[fallthrough]]; + default: + // unsupported + return 0; + } + + // TODO: Parse TLVs + + return total_len; + } + default: + break; + } + return 0; } + } // namespace /** @@ -187,7 +316,7 @@ proxy_protocol_parse(ProxyProtocol *pp_info, ts::TextView tv) if (tv.size() >= PPv1_CONNECTION_HEADER_LEN_MIN && PPv1_CONNECTION_PREFACE.isPrefixOf(tv)) { // Client must send at least 15 bytes to get a reasonable match. len = proxy_protocol_v1_parse(pp_info, tv); - } else if (tv.size() >= PPv2_CONNECTION_HEADER_LEN_MIN && PPv2_CONNECTION_PREFACE.isPrefixOf(tv)) { + } else if (tv.size() >= PPv2_CONNECTION_HEADER_LEN && PPv2_CONNECTION_PREFACE.isPrefixOf(tv)) { len = proxy_protocol_v2_parse(pp_info, tv); } else { // if we don't have the PROXY preface, we don't have a ProxyProtocol header diff --git a/iocore/net/ProxyProtocol.h b/iocore/net/ProxyProtocol.h index 84491bc..dedb31d 100644 --- a/iocore/net/ProxyProtocol.h +++ b/iocore/net/ProxyProtocol.h @@ -48,6 +48,6 @@ struct ProxyProtocol { }; const size_t PPv1_CONNECTION_HEADER_LEN_MAX = 108; -const size_t PPv2_CONNECTION_HEADER_LEN_MAX = 16; +const size_t PPv2_CONNECTION_HEADER_LEN = 16; extern size_t proxy_protocol_parse(ProxyProtocol *pp_info, ts::TextView tv); diff --git a/iocore/net/unit_tests/test_ProxyProtocol.cc b/iocore/net/unit_tests/test_ProxyProtocol.cc index da2b754..e4fbb03 100644 --- a/iocore/net/unit_tests/test_ProxyProtocol.cc +++ b/iocore/net/unit_tests/test_ProxyProtocol.cc @@ -124,13 +124,16 @@ TEST_CASE("PROXY Protocol v1 Parser", "[ProxyProtocol][ProxyProtocolv1]") TEST_CASE("PROXY Protocol v2 Parser", "[ProxyProtocol][ProxyProtocolv2]") { - SECTION("TCP over IPv4") + IpEndpoint src_addr; + IpEndpoint dst_addr; + + SECTION("TCP over IPv4 without TLVs") { uint8_t raw_data[] = { - 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< sig + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface 0x55, 0x49, 0x54, 0x0A, ///< - 0x02, ///< ver_vmd - 0x11, ///< fam + 0x21, ///< version & command + 0x11, ///< protocol & family 0x00, 0x0C, ///< len 0xC0, 0x00, 0x02, 0x01, ///< src_addr 0xC6, 0x33, 0x64, 0x01, ///< dst_addr @@ -141,7 +144,286 @@ TEST_CASE("PROXY Protocol v2 Parser", "[ProxyProtocol][ProxyProtocolv2]") ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); ProxyProtocol pp_info; - // TODO: add test when implemented. Just checking this doesn't crash for now + REQUIRE(proxy_protocol_parse(&pp_info, tv) == tv.size()); + + REQUIRE(ats_ip_pton("192.0.2.1:50000", src_addr) == 0); + REQUIRE(ats_ip_pton("198.51.100.1:443", dst_addr) == 0); + + CHECK(pp_info.version == ProxyProtocolVersion::V2); + CHECK(pp_info.ip_family == AF_INET); + CHECK(pp_info.src_addr == src_addr); + CHECK(pp_info.dst_addr == dst_addr); + } + + SECTION("TCP over IPv6 without TLVs") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0x21, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + ProxyProtocol pp_info; + REQUIRE(proxy_protocol_parse(&pp_info, tv) == tv.size()); + + REQUIRE(ats_ip_pton("[2001:db8:0:1::]:50000", src_addr) == 0); + REQUIRE(ats_ip_pton("[2001:db8:0:2::]:443", dst_addr) == 0); + + CHECK(pp_info.version == ProxyProtocolVersion::V2); + CHECK(pp_info.ip_family == AF_INET6); + CHECK(pp_info.src_addr == src_addr); + CHECK(pp_info.dst_addr == dst_addr); + } + + SECTION("LOCAL command - health check") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x20, ///< version & command + 0x00, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + ProxyProtocol pp_info; + REQUIRE(proxy_protocol_parse(&pp_info, tv) == tv.size()); + + CHECK(pp_info.version == ProxyProtocolVersion::V2); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + SECTION("UNSPEC - unknownun/specified/unsupported transport protocol & address family") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0x00, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + ProxyProtocol pp_info; REQUIRE(proxy_protocol_parse(&pp_info, tv) == 0); + + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + // TLVs are not supported yet. Checking TLVs are skipped as expected for now. + SECTION("TLVs") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0x11, ///< protocol & family + 0x00, 0x11, ///< len + 0xC0, 0x00, 0x02, 0x01, ///< src_addr + 0xC6, 0x33, 0x64, 0x01, ///< dst_addr + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + 0x01, 0x00, 0x02, 0x68, 0x32, /// PP2_TYPE_ALPN (h2) + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + ProxyProtocol pp_info; + REQUIRE(proxy_protocol_parse(&pp_info, tv) == tv.size()); + + REQUIRE(ats_ip_pton("192.0.2.1:50000", src_addr) == 0); + REQUIRE(ats_ip_pton("198.51.100.1:443", dst_addr) == 0); + + CHECK(pp_info.version == ProxyProtocolVersion::V2); + CHECK(pp_info.ip_family == AF_INET); + CHECK(pp_info.src_addr == src_addr); + CHECK(pp_info.dst_addr == dst_addr); + } + + SECTION("Malformed Headers") + { + ProxyProtocol pp_info; + + SECTION("invalid preface") + { + uint8_t raw_data[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, ///< preface + 0xDE, 0xAD, 0xBE, 0xEF, ///< + 0x21, ///< version & command + 0x21, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + CHECK(proxy_protocol_parse(&pp_info, tv) == 0); + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + SECTION("unsupported version & command") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0xFF, ///< version & command + 0x21, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + CHECK(proxy_protocol_parse(&pp_info, tv) == 0); + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + SECTION("unsupported protocol & family") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0xFF, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + CHECK(proxy_protocol_parse(&pp_info, tv) == 0); + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + SECTION("invalid len value - too long") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0x21, ///< protocol & family + 0x00, 0x25, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + CHECK(proxy_protocol_parse(&pp_info, tv) == 0); + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + SECTION("invalid len - actual buffer is shorter than the value") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0x21, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///< + 0xC3, 0x50, ///< src_port + 0x01, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + CHECK(proxy_protocol_parse(&pp_info, tv) == 0); + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + SECTION("invalid len - too short for INET") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0x11, ///< protocol & family + 0x00, 0x0C, ///< len + 0xC0, 0x00, ///< src_addr + 0xC6, 0x33, ///< dst_addr + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + CHECK(proxy_protocol_parse(&pp_info, tv) == 0); + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } + + SECTION("invalid len - too short for INET6") + { + uint8_t raw_data[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface + 0x55, 0x49, 0x54, 0x0A, ///< + 0x21, ///< version & command + 0x21, ///< protocol & family + 0x00, 0x24, ///< len + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x01, ///< src_addr + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x02, ///< dst_addr + 0xC3, 0x50, ///< src_port + 0x01, 0xBB, ///< dst_port + }; + + ts::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data)); + + CHECK(proxy_protocol_parse(&pp_info, tv) == 0); + CHECK(pp_info.version == ProxyProtocolVersion::UNDEFINED); + CHECK(pp_info.ip_family == AF_UNSPEC); + } } }