You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2021/02/23 17:38:58 UTC
[trafficserver] branch 9.1.x updated: Add PROXY Protocol Builder
(#7445)
This is an automated email from the ASF dual-hosted git repository.
zwoop pushed a commit to branch 9.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/9.1.x by this push:
new f46ce85 Add PROXY Protocol Builder (#7445)
f46ce85 is described below
commit f46ce85cfa86db9ea361659139f621b28dcd54c0
Author: Masaori Koshiba <ma...@apache.org>
AuthorDate: Fri Feb 5 08:19:34 2021 +0900
Add PROXY Protocol Builder (#7445)
(cherry picked from commit 1baffec5f2d5ce5272c9af5d46b2b54f4b9bbe6a)
---
include/tscore/ink_inet.h | 6 +-
iocore/net/ProxyProtocol.cc | 186 +++++++++++++++++++++++++++-
iocore/net/ProxyProtocol.h | 3 +
iocore/net/unit_tests/test_ProxyProtocol.cc | 100 +++++++++++++++
4 files changed, 292 insertions(+), 3 deletions(-)
diff --git a/include/tscore/ink_inet.h b/include/tscore/ink_inet.h
index c0a7cc1..c683643 100644
--- a/include/tscore/ink_inet.h
+++ b/include/tscore/ink_inet.h
@@ -181,8 +181,10 @@ inkcoreapi uint32_t ats_inet_addr(const char *s);
const char *ats_ip_ntop(const struct sockaddr *addr, char *dst, size_t size);
// --
-/// Size in bytes of an IPv6 address.
-static size_t const TS_IP6_SIZE = sizeof(in6_addr);
+/// Size in bytes of an port and IPv4/IPv6 address.
+static constexpr size_t TS_IP4_SIZE = sizeof(in_addr_t); ///< 4
+static constexpr size_t TS_IP6_SIZE = sizeof(in6_addr); ///< 16
+static constexpr size_t TS_PORT_SIZE = sizeof(in_port_t); ///< 2
/// Reset an address to invalid.
/// @note Useful for marking a member as not yet set.
diff --git a/iocore/net/ProxyProtocol.cc b/iocore/net/ProxyProtocol.cc
index 452f63b..1d406a7 100644
--- a/iocore/net/ProxyProtocol.cc
+++ b/iocore/net/ProxyProtocol.cc
@@ -26,7 +26,10 @@
#include "I_EventSystem.h"
#include "I_NetVConnection.h"
+#include "tscore/BufferWriter.h"
#include "tscore/ink_assert.h"
+#include "tscore/ink_string.h"
+#include "tscore/ink_inet.h"
#include "tscpp/util/TextView.h"
namespace
@@ -42,6 +45,8 @@ 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 std::string_view PPv1_DELIMITER = " "sv;
+
constexpr uint8_t PPv2_CMD_LOCAL = 0x20;
constexpr uint8_t PPv2_CMD_PROXY = 0x21;
@@ -55,7 +60,7 @@ 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;
+constexpr uint16_t PPv2_ADDR_LEN_UNIX = 108 + 108;
struct PPv2Hdr {
uint8_t sig[12]; ///< preface
@@ -302,6 +307,149 @@ proxy_protocol_v2_parse(ProxyProtocol *pp_info, const ts::TextView &msg)
return 0;
}
+/**
+ Build PROXY Protocol v1
+ */
+size_t
+proxy_protocol_v1_build(uint8_t *buf, size_t max_buf_len, const ProxyProtocol &pp_info)
+{
+ if (max_buf_len < PPv1_CONNECTION_HEADER_LEN_MAX) {
+ return 0;
+ }
+
+ ts::FixedBufferWriter bw{reinterpret_cast<char *>(buf), max_buf_len};
+
+ // preface
+ bw.write(PPv1_CONNECTION_PREFACE);
+ bw.write(PPv1_DELIMITER);
+
+ // the proxied INET protocol and family
+ if (pp_info.src_addr.isIp4()) {
+ bw.write(PPv1_PROTO_TCP4);
+ } else if (pp_info.src_addr.isIp6()) {
+ bw.write(PPv1_PROTO_TCP6);
+ } else {
+ bw.write(PPv1_PROTO_UNKNOWN);
+ }
+ bw.write(PPv1_DELIMITER);
+
+ // the layer 3 source address
+ char src_ip_buf[INET6_ADDRSTRLEN];
+ ats_ip_ntop(pp_info.src_addr, src_ip_buf, sizeof(src_ip_buf));
+ size_t src_ip_len = strnlen(src_ip_buf, sizeof(src_ip_buf));
+
+ bw.write(src_ip_buf, src_ip_len);
+ bw.write(PPv1_DELIMITER);
+
+ // the layer 3 destination address
+ char dst_ip_buf[INET6_ADDRSTRLEN];
+ ats_ip_ntop(pp_info.dst_addr, dst_ip_buf, sizeof(dst_ip_buf));
+ size_t dst_ip_len = strnlen(dst_ip_buf, sizeof(dst_ip_buf));
+
+ bw.write(dst_ip_buf, dst_ip_len);
+ bw.write(PPv1_DELIMITER);
+
+ // TCP source port
+ {
+ size_t len = ink_small_itoa(ats_ip_port_host_order(pp_info.src_addr), bw.auxBuffer(), bw.remaining());
+ bw.fill(len);
+ bw.write(PPv1_DELIMITER);
+ }
+
+ // TCP destination port
+ {
+ size_t len = ink_small_itoa(ats_ip_port_host_order(pp_info.dst_addr), bw.auxBuffer(), bw.remaining());
+ bw.fill(len);
+ }
+
+ bw.write("\r\n");
+
+ return bw.size();
+}
+
+/**
+ Build PROXY Protocol v2
+
+ UDP, Unix Domain Socket, and TLV fields are not supported yet
+ */
+size_t
+proxy_protocol_v2_build(uint8_t *buf, size_t max_buf_len, const ProxyProtocol &pp_info)
+{
+ if (max_buf_len < PPv2_CONNECTION_HEADER_LEN) {
+ return 0;
+ }
+
+ ts::FixedBufferWriter bw{reinterpret_cast<char *>(buf), max_buf_len};
+
+ // # proxy_hdr_v2
+ // ## preface
+ bw.write(PPv2_CONNECTION_PREFACE);
+
+ // ## version and command
+ // TODO: support PPv2_CMD_LOCAL for health check
+ bw.write(static_cast<char>(PPv2_CMD_PROXY));
+
+ // ## family & address
+ // TODO: support UDP
+ switch (pp_info.src_addr.family()) {
+ case AF_INET:
+ bw.write(static_cast<char>(PPv2_PROTO_TCP4));
+ break;
+ case AF_INET6:
+ bw.write(static_cast<char>(PPv2_PROTO_TCP6));
+ break;
+ case AF_UNIX:
+ bw.write(static_cast<char>(PPv2_PROTO_UNIX_STREAM));
+ break;
+ default:
+ bw.write(static_cast<char>(PPv2_PROTO_UNSPEC));
+ break;
+ }
+
+ // ## len field. this will be set at the end of this function
+ const size_t len_field_offset = bw.size();
+ bw.fill(2);
+
+ ink_release_assert(bw.size() == PPv2_CONNECTION_HEADER_LEN);
+
+ // # proxy_addr
+ // TODO: support UDP
+ switch (pp_info.src_addr.family()) {
+ case AF_INET: {
+ bw.write(&ats_ip4_addr_cast(pp_info.src_addr), TS_IP4_SIZE);
+ bw.write(&ats_ip4_addr_cast(pp_info.dst_addr), TS_IP4_SIZE);
+ bw.write(&ats_ip_port_cast(pp_info.src_addr), TS_PORT_SIZE);
+ bw.write(&ats_ip_port_cast(pp_info.dst_addr), TS_PORT_SIZE);
+
+ break;
+ }
+ case AF_INET6: {
+ bw.write(&ats_ip6_addr_cast(pp_info.src_addr), TS_IP6_SIZE);
+ bw.write(&ats_ip6_addr_cast(pp_info.dst_addr), TS_IP6_SIZE);
+ bw.write(&ats_ip_port_cast(pp_info.src_addr), TS_PORT_SIZE);
+ bw.write(&ats_ip_port_cast(pp_info.dst_addr), TS_PORT_SIZE);
+
+ break;
+ }
+ case AF_UNIX: {
+ // unsupported yet
+ bw.fill(PPv2_ADDR_LEN_UNIX);
+ break;
+ }
+ default:
+ // do nothing
+ break;
+ }
+
+ // # Additional TLVs (pp2_tlv)
+ // unsupported yet
+
+ // Set len field (number of following bytes part of the header) in the hdr
+ uint16_t len = htons(bw.size() - PPv2_CONNECTION_HEADER_LEN);
+ memcpy(buf + len_field_offset, &len, sizeof(uint16_t));
+ return bw.size();
+}
+
} // namespace
/**
@@ -326,3 +474,39 @@ proxy_protocol_parse(ProxyProtocol *pp_info, ts::TextView tv)
return len;
}
+
+/**
+ PROXY Protocol Builder
+ */
+size_t
+proxy_protocol_build(uint8_t *buf, size_t max_buf_len, const ProxyProtocol &pp_info, ProxyProtocolVersion force_version)
+{
+ ProxyProtocolVersion version = pp_info.version;
+ if (force_version != ProxyProtocolVersion::UNDEFINED) {
+ version = force_version;
+ }
+
+ size_t len = 0;
+
+ if (version == ProxyProtocolVersion::V1) {
+ len = proxy_protocol_v1_build(buf, max_buf_len, pp_info);
+ } else if (version == ProxyProtocolVersion::V2) {
+ len = proxy_protocol_v2_build(buf, max_buf_len, pp_info);
+ } else {
+ ink_abort("PROXY Protocol Version is undefined");
+ }
+
+ return len;
+}
+
+ProxyProtocolVersion
+proxy_protocol_version_cast(int i)
+{
+ switch (i) {
+ case 1:
+ case 2:
+ return static_cast<ProxyProtocolVersion>(i);
+ default:
+ return ProxyProtocolVersion::UNDEFINED;
+ }
+}
diff --git a/iocore/net/ProxyProtocol.h b/iocore/net/ProxyProtocol.h
index dedb31d..c0726e1 100644
--- a/iocore/net/ProxyProtocol.h
+++ b/iocore/net/ProxyProtocol.h
@@ -51,3 +51,6 @@ const size_t PPv1_CONNECTION_HEADER_LEN_MAX = 108;
const size_t PPv2_CONNECTION_HEADER_LEN = 16;
extern size_t proxy_protocol_parse(ProxyProtocol *pp_info, ts::TextView tv);
+extern size_t proxy_protocol_build(uint8_t *buf, size_t max_buf_len, const ProxyProtocol &pp_info,
+ ProxyProtocolVersion force_version = ProxyProtocolVersion::UNDEFINED);
+extern ProxyProtocolVersion proxy_protocol_version_cast(int i);
diff --git a/iocore/net/unit_tests/test_ProxyProtocol.cc b/iocore/net/unit_tests/test_ProxyProtocol.cc
index e4fbb03..9f8b7c4 100644
--- a/iocore/net/unit_tests/test_ProxyProtocol.cc
+++ b/iocore/net/unit_tests/test_ProxyProtocol.cc
@@ -427,3 +427,103 @@ TEST_CASE("PROXY Protocol v2 Parser", "[ProxyProtocol][ProxyProtocolv2]")
}
}
}
+
+TEST_CASE("ProxyProtocol v1 Builder", "[ProxyProtocol][ProxyProtocolv1]")
+{
+ SECTION("TCP over IPv4")
+ {
+ uint8_t buf[PPv1_CONNECTION_HEADER_LEN_MAX] = {0};
+
+ ProxyProtocol pp_info;
+ pp_info.version = ProxyProtocolVersion::V1;
+ pp_info.ip_family = AF_INET;
+ ats_ip_pton("192.0.2.1:50000", pp_info.src_addr);
+ ats_ip_pton("198.51.100.1:443", pp_info.dst_addr);
+
+ size_t len = proxy_protocol_build(buf, sizeof(buf), pp_info);
+
+ std::string_view expected = "PROXY TCP4 192.0.2.1 198.51.100.1 50000 443\r\n"sv;
+
+ CHECK(len == expected.size());
+ CHECK(memcmp(buf, expected.data(), expected.size()) == 0);
+ }
+
+ SECTION("TCP over IPv6")
+ {
+ uint8_t buf[PPv1_CONNECTION_HEADER_LEN_MAX] = {0};
+
+ ProxyProtocol pp_info;
+ pp_info.version = ProxyProtocolVersion::V1;
+ pp_info.ip_family = AF_INET6;
+ ats_ip_pton("[2001:db8:0:1::]:50000", pp_info.src_addr);
+ ats_ip_pton("[2001:db8:0:2::]:443", pp_info.dst_addr);
+
+ size_t len = proxy_protocol_build(buf, sizeof(buf), pp_info);
+
+ std::string_view expected = "PROXY TCP6 2001:db8:0:1:: 2001:db8:0:2:: 50000 443\r\n"sv;
+
+ CHECK(len == expected.size());
+ CHECK(memcmp(buf, expected.data(), expected.size()) == 0);
+ }
+}
+
+TEST_CASE("ProxyProtocol v2 Builder", "[ProxyProtocol][ProxyProtocolv2]")
+{
+ SECTION("TCP over IPv4 / no TLV")
+ {
+ uint8_t buf[1024] = {0};
+
+ ProxyProtocol pp_info;
+ pp_info.version = ProxyProtocolVersion::V2;
+ pp_info.ip_family = AF_INET;
+ ats_ip_pton("192.0.2.1:50000", pp_info.src_addr);
+ ats_ip_pton("198.51.100.1:443", pp_info.dst_addr);
+
+ size_t len = proxy_protocol_build(buf, sizeof(buf), pp_info);
+
+ uint8_t expected[] = {
+ 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< sig
+ 0x55, 0x49, 0x54, 0x0A, ///<
+ 0x21, ///< ver_vmd
+ 0x11, ///< fam
+ 0x00, 0x0C, ///< len
+ 0xC0, 0x00, 0x02, 0x01, ///< src_addr
+ 0xC6, 0x33, 0x64, 0x01, ///< dst_addr
+ 0xC3, 0x50, ///< src_port
+ 0x01, 0xBB, ///< dst_port
+ };
+
+ CHECK(len == sizeof(expected));
+ CHECK(memcmp(expected, buf, len) == 0);
+ }
+
+ SECTION("TCP over IPv6 / no TLV")
+ {
+ uint8_t buf[1024] = {0};
+
+ ProxyProtocol pp_info;
+ pp_info.version = ProxyProtocolVersion::V2;
+ pp_info.ip_family = AF_INET6;
+ ats_ip_pton("[2001:db8:0:1::]:50000", pp_info.src_addr);
+ ats_ip_pton("[2001:db8:0:2::]:443", pp_info.dst_addr);
+
+ size_t len = proxy_protocol_build(buf, sizeof(buf), pp_info);
+
+ uint8_t expected[] = {
+ 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< sig
+ 0x55, 0x49, 0x54, 0x0A, ///<
+ 0x21, ///< ver_vmd
+ 0x21, ///< fam
+ 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
+ };
+
+ CHECK(len == sizeof(expected));
+ CHECK(memcmp(expected, buf, len) == 0);
+ }
+}