This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/10.1.x by this push: new 9573b6e6d1 Add TSVConnPPInfoGet (#12032) 9573b6e6d1 is described below commit 9573b6e6d12161d8c1c6dcdd92306b542d5a4108 Author: Masakazu Kitajo <mas...@apache.org> AuthorDate: Wed May 14 16:53:13 2025 -0600 Add TSVConnPPInfoGet (#12032) * Add TSVConnPPInfoGet * Set socket type in PPv1 parser as well * Fix nullptr dereference * Change tye type of key parameter to uint16_t (cherry picked from commit c4c31de48bfcfb4a7ce58e7a16804a2dc37ab57d) --- include/ts/apidefs.h.in | 10 ++ include/ts/ts.h | 25 +++++ src/api/InkAPI.cc | 70 ++++++++++++ src/iocore/net/ProxyProtocol.cc | 2 + tests/gold_tests/pluginTest/tsapi/CMakeLists.txt | 1 + .../pluginTest/tsapi/test_TSVConnPPInfo.cc | 118 +++++++++++++++++++++ .../pluginTest/tsapi/test_TSVConnPPInfo.test.py | 98 +++++++++++++++++ .../pluginTest/tsapi/test_TSVConnPPInfo_curl0.gold | 1 + .../pluginTest/tsapi/test_TSVConnPPInfo_curl1.gold | 1 + .../tsapi/test_TSVConnPPInfo_plugin_log.gold | 2 + 10 files changed, 328 insertions(+) diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in index 58a2f55bb8..bd37127970 100644 --- a/include/ts/apidefs.h.in +++ b/include/ts/apidefs.h.in @@ -649,6 +649,16 @@ enum TSHttpSsnInfoKey { TS_SSN_INFO_RECEIVED_FRAME_COUNT, }; +enum TSVConnPPInfoKey { + TS_PP_INFO_VERSION = 0x100, + TS_PP_INFO_SRC_ADDR, + TS_PP_INFO_SRC_PORT, + TS_PP_INFO_DST_ADDR, + TS_PP_INFO_DST_PORT, + TS_PP_INFO_PROTOCOL, + TS_PP_INFO_SOCK_TYPE, +}; + #define TS_SSN_INFO_RECEIVED_FRAME_COUNT_H2_UNKNOWN 999 #define TS_SSN_INFO_RECEIVED_FRAME_COUNT_H3_UNKNOWN 0x21 diff --git a/include/ts/ts.h b/include/ts/ts.h index 76030df541..42998a756f 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -3077,3 +3077,28 @@ TSRalloc(size_t count = 1 /**< Number of instances of T to allocate storage for. { return static_cast<std::remove_cv_t<T> *>(TSmalloc(count * sizeof(T))); } + +/** + Return the particular PROXY protocol info requested. + + @param vconn the vconection pointer + @param key the requested PROXY protocol info. One of TSVConnPPInfoKey or TLV type ID + @param value a pointer to a const char pointer where the return value is stored + @param length a pointer to a integer where the length of return value is stored + + @return @c TS_SUCCESS if the requested info is supported, TS_ERROR otherwise + +*/ +TSReturnCode TSVConnPPInfoGet(TSVConn vconn, uint16_t key, const char **value, int *length); + +/** + Return the particular PROXY protocol info requested. + + @param vconn the vconection pointer + @param key the requested PROXY protocol info. One of TSVConnPPInfoKey or TLV type ID + @param value a pointer to a integer where the return value is stored + + @return @c TS_SUCCESS if the requested info is supported, TS_ERROR otherwise + +*/ +TSReturnCode TSVConnPPInfoIntGet(TSVConn vconn, uint16_t key, TSMgmtInt *value); diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc index dbd68d6fc9..234d77c06a 100644 --- a/src/api/InkAPI.cc +++ b/src/api/InkAPI.cc @@ -8417,6 +8417,76 @@ TSVConnReenableEx(TSVConn vconn, TSEvent event) } } +TSReturnCode +TSVConnPPInfoGet(TSVConn vconn, uint16_t key, const char **value, int *length) +{ + NetVConnection *vc = reinterpret_cast<NetVConnection *>(vconn); + + if (key < 0x100) { + auto &tlv = vc->get_proxy_protocol_info().tlv; + if (auto ite = tlv.find(key); ite != tlv.end()) { + *value = ite->second.data(); + *length = ite->second.length(); + } else { + return TS_ERROR; + } + } else { + switch (key) { + case TS_PP_INFO_SRC_ADDR: + *value = reinterpret_cast<const char *>(vc->get_proxy_protocol_src_addr()); + if (*value == nullptr) { + return TS_ERROR; + } + *length = ats_ip_size(reinterpret_cast<const sockaddr *>(*value)); + break; + case TS_PP_INFO_DST_ADDR: + *value = reinterpret_cast<const char *>(vc->get_proxy_protocol_dst_addr()); + if (*value == nullptr) { + return TS_ERROR; + } + *length = ats_ip_size(reinterpret_cast<const sockaddr *>(*value)); + break; + default: + return TS_ERROR; + } + } + + return TS_SUCCESS; +} + +TSReturnCode +TSVConnPPInfoIntGet(TSVConn vconn, uint16_t key, TSMgmtInt *value) +{ + NetVConnection *vc = reinterpret_cast<NetVConnection *>(vconn); + + if (key < 0x100) { + // Unknown type value cannot be returned as an integer + return TS_ERROR; + } else { + switch (key) { + case TS_PP_INFO_VERSION: + *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_version()); + break; + case TS_PP_INFO_SRC_PORT: + *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_src_port()); + break; + case TS_PP_INFO_DST_PORT: + *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_dst_port()); + break; + case TS_PP_INFO_PROTOCOL: + *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_info().ip_family); + break; + case TS_PP_INFO_SOCK_TYPE: + *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_info().type); + break; + default: + return TS_ERROR; + } + } + + return TS_SUCCESS; +} + TSSslSession TSSslSessionGet(const TSSslSessionID *session_id) { diff --git a/src/iocore/net/ProxyProtocol.cc b/src/iocore/net/ProxyProtocol.cc index e775c99357..27b1c84301 100644 --- a/src/iocore/net/ProxyProtocol.cc +++ b/src/iocore/net/ProxyProtocol.cc @@ -145,6 +145,7 @@ proxy_protocol_v1_parse(ProxyProtocol *pp_info, swoc::TextView hdr) } pp_info->ip_family = AF_INET; + pp_info->type = SOCK_STREAM; } else if (hdr.starts_with(PPv1_PROTO_TCP6)) { token = hdr.split_prefix_at(' '); if (0 == token.size()) { @@ -152,6 +153,7 @@ proxy_protocol_v1_parse(ProxyProtocol *pp_info, swoc::TextView hdr) } pp_info->ip_family = AF_INET6; + pp_info->type = SOCK_STREAM; } else { return 0; } diff --git a/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt b/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt index a03fa94037..d5e3a088fa 100644 --- a/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt +++ b/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt @@ -18,3 +18,4 @@ add_autest_plugin(test_tsapi test_tsapi.cc) add_autest_plugin(test_TSHttpTxnServerAddrSet test_TSHttpTxnServerAddrSet.cc) add_autest_plugin(test_TSHttpSsnInfo test_TSHttpSsnInfo.cc) +add_autest_plugin(test_TSVConnPPInfo test_TSVConnPPInfo.cc) diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.cc b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.cc new file mode 100644 index 0000000000..6aa78118ac --- /dev/null +++ b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.cc @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fstream> +#include <cstdlib> +#include <arpa/inet.h> + +#include <ts/ts.h> + +namespace +{ +#define PINAME "test_TSVConnPPInfo" +char PIName[] = PINAME; + +DbgCtl dbg_ctl{PIName}; + +// NOTE: It's important to flush this after writing so that a gold test using this plugin can examine the log before TS +// terminates. +// +std::fstream logFile; + +void +handle_ssn_start(TSHttpSsn ssn) +{ + TSVConn vconn = TSHttpSsnClientVConnGet(ssn); + TSMgmtInt pp_ver; + auto ret = TSVConnPPInfoIntGet(vconn, TS_PP_INFO_VERSION, &pp_ver); + if (ret == TS_SUCCESS && pp_ver != 0) { + TSMgmtInt info[2]; + const struct sockaddr_in *addr[2]; + int addr_len[2]; + TSVConnPPInfoIntGet(vconn, TS_PP_INFO_PROTOCOL, &info[0]); + TSVConnPPInfoIntGet(vconn, TS_PP_INFO_SOCK_TYPE, &info[1]); + TSVConnPPInfoGet(vconn, TS_PP_INFO_SRC_ADDR, reinterpret_cast<const char **>(&addr[0]), &addr_len[0]); + TSVConnPPInfoGet(vconn, TS_PP_INFO_DST_ADDR, reinterpret_cast<const char **>(&addr[1]), &addr_len[1]); + + logFile << "PP Info Received:" << "V" << pp_ver << "," << "P" << info[0] << "," << "T" << info[1] << "," << "SRC" + << inet_ntoa(addr[0]->sin_addr) << "," << "DST" << inet_ntoa(addr[1]->sin_addr) << std::endl; + } + + TSHttpSsnReenable(ssn, TS_EVENT_HTTP_CONTINUE); +} + +int +globalContFunc(TSCont, TSEvent event, void *eventData) +{ + logFile << "Global: event=" << TSHttpEventNameLookup(event) << std::endl; + + Dbg(dbg_ctl, "Global: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); + + switch (event) { + case TS_EVENT_HTTP_SSN_START: + handle_ssn_start(static_cast<TSHttpSsn>(eventData)); + break; + default: + break; + } // end switch + + return 0; +} + +TSCont gCont; + +} // end anonymous namespace + +void +TSPluginInit(int /* argc ATS_UNUSED */, const char ** /* argv ATS_UNUSED */) +{ + TSPluginRegistrationInfo info; + + info.plugin_name = PIName; + info.vendor_name = "Apache Software Foundation"; + info.support_email = "d...@trafficserver.apache.org"; + + if (TSPluginRegister(&info) != TS_SUCCESS) { + TSError(PINAME ": Plugin registration failed"); + + return; + } + + const char *fileSpec = std::getenv("OUTPUT_FILE"); + + if (nullptr == fileSpec) { + TSError(PINAME ": Environment variable OUTPUT_FILE not found."); + + return; + } + + // Disable output buffering for logFile, so that explicit flushing is not necessary. + logFile.rdbuf()->pubsetbuf(nullptr, 0); + + logFile.open(fileSpec, std::ios::out); + if (!logFile.is_open()) { + TSError(PINAME ": could not open log file \"%s\"", fileSpec); + + return; + } + + // Mutex to protect the logFile object. + TSMutex mtx = TSMutexCreate(); + gCont = TSContCreate(globalContFunc, mtx); + TSHttpHookAdd(TS_HTTP_SSN_START_HOOK, gCont); +} diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.test.py b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.test.py new file mode 100644 index 0000000000..2564d28814 --- /dev/null +++ b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.test.py @@ -0,0 +1,98 @@ +''' +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +Test.Summary = ''' +Test TS API to get PROXY protocol info +''' + +Test.SkipUnless(Condition.HasProgram("nghttp", "Nghttp need to be installed on system for this test to work"),) +Test.ContinueOnFail = True + +# ---- +# Setup Origin Server +# ---- +httpbin = Test.MakeHttpBinServer("httpbin") + +# 128ytes +post_body = "0123456789abcdef" * 8 +post_body_file = open(os.path.join(Test.RunDirectory, "post_body"), "w") +post_body_file.write(post_body) +post_body_file.close() + +# ---- +# Setup ATS +# ---- +ts = Test.MakeATSProcess("ts", enable_tls=True, enable_proxy_protocol=True) + +# add ssl materials like key, certificates for the server +ts.addDefaultSSLFiles() + +ts.Disk.remap_config.AddLines(['map /httpbin/ http://127.0.0.1:{0}/'.format(httpbin.Variables.Port)]) + +ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key') + +Test.PrepareTestPlugin( + os.path.join(Test.Variables.AtsBuildGoldTestsDir, 'pluginTest', 'tsapi', '.libs', 'test_TSVConnPPInfo.so'), ts) + +ts.Disk.records_config.update( + { + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'http|proxyprotocol|test_TSVConnPPInfo', + 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir) + }) + +# http2_info.so will output test logging to this file. +log_path = os.path.join(ts.Variables.LOGDIR, "test_TSVConnPPInfo_plugin_log.txt") +Test.Env["OUTPUT_FILE"] = log_path + +# ---- +# Test Cases +# ---- + +# plaintext HTTP +tr = Test.AddTestRun() +tr.TimeOut = 10 +tr.Processes.Default.Command = f"curl --haproxy-protocol --haproxy-clientip 1.2.3.4 'http://127.0.0.1:{ts.Variables.proxy_protocol_port}/httpbin/get'" +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.StartBefore(httpbin, ready=When.PortOpen(httpbin.Variables.Port)) +tr.Processes.Default.StartBefore(Test.Processes.ts) +tr.Processes.Default.Streams.stdout = "test_TSVConnPPInfo_curl0.gold" +tr.StillRunningAfter = httpbin +tr.StillRunningAfter = ts + +# HTTPS +tr = Test.AddTestRun() +tr.TimeOut = 10 +tr.Processes.Default.Command = f"curl --haproxy-protocol --haproxy-clientip 5.6.7.8 -k 'https://127.0.0.1:{ts.Variables.proxy_protocol_ssl_port}/httpbin/get'" +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Streams.stdout = "test_TSVConnPPInfo_curl1.gold" +tr.StillRunningAfter = httpbin +tr.StillRunningAfter = ts + +tr = Test.AddTestRun() +tr.Processes.Default.Command = "echo check log" +tr.Processes.Default.ReturnCode = 0 +f = tr.Disk.File(log_path) +f.Content = "test_TSVConnPPInfo_plugin_log.gold" +f.Content += Testers.ContainsExpression( + "PP Info Received:V1,P2,T1,SRC1.2.3.4,DST127.0.0.1", "Expected information should be received") +f.Content += Testers.ContainsExpression( + "PP Info Received:V1,P2,T1,SRC5.6.7.8,DST127.0.0.1", "Expected information should be received") diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_curl0.gold b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_curl0.gold new file mode 100644 index 0000000000..cf637de588 --- /dev/null +++ b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_curl0.gold @@ -0,0 +1 @@ +`` diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_curl1.gold b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_curl1.gold new file mode 100644 index 0000000000..cf637de588 --- /dev/null +++ b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_curl1.gold @@ -0,0 +1 @@ +`` diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_plugin_log.gold b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_plugin_log.gold new file mode 100644 index 0000000000..fd14052482 --- /dev/null +++ b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo_plugin_log.gold @@ -0,0 +1,2 @@ +Global: event=TS_EVENT_HTTP_SSN_START +``