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
+``

Reply via email to