From cc8fe58a8d2f8d47b03d03fd1048fe1b9babca70 Mon Sep 17 00:00:00 2001
From: Alexander Stephan 
<alexander.step...@sap.com<mailto:alexander.step...@sap.com>>
Date: Fri, 15 Sep 2023 12:18:10 +0200
Subject: [PATCH 3/4] LOW: connection: Add TLV update function

Until now, it was not possible to deliberatily change received TLVs
that are stored within a connection. An HAProxy backend server reads
TLVs out the (remote) connection. This function allows us to update
TLVs in-place before they are forwarded, given that the new and the
old value map to the same pool. Besides, if a TLV does not already
exist, it is created and added to the list. As TLV values often have
similiar length this is more efficient than allocating a new TLV for
each value.
---
 include/haproxy/connection.h | 73 ++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
index fb14eed12..b775996ba 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -451,6 +451,79 @@ static inline void conn_set_tos(const struct connection 
*conn, int tos)
 #endif
 }

+/* Adds or updates a TLV item on an existing connection.
+ * The connection is tested and if it is null, nothing is done.
+ */
+static inline int conn_set_tlv(struct connection *conn, int type, void *value, 
int len)
+{
+ struct conn_tlv_list *tlv = NULL, *tlv_back = NULL;
+ int reuse = 0, found = 0;
+
+ if (!conn || !conn_ctrl_ready(conn) || len > HA_PP2_MAX_ALLOC)
+   return -1;
+ /* search whether we already have a TLV of the requested type */
+ list_for_each_entry(tlv, &conn->tlv_list, list) {
+   if (tlv->type != type)
+     continue;
+   /* Set reuse flag according to whether both, new and old TLV,
+    * are in the same interval.
+    */
+   if (len >= 0) {
+     if (tlv->len <= HA_PP2_TLV_VALUE_128 && len <= HA_PP2_TLV_VALUE_128)
+       reuse = 1;
+     else if (tlv->len > HA_PP2_TLV_VALUE_128 && len > HA_PP2_TLV_VALUE_128) {
+       if ((tlv->len <= HA_PP2_TLV_VALUE_256 && len <= HA_PP2_TLV_VALUE_256) ||
+           (tlv->len > HA_PP2_TLV_VALUE_256 && len > HA_PP2_TLV_VALUE_256))
+         reuse = 1;
+     }
+   }
+   found = 1;
+   break;
+ }
+ if (found && reuse) {
+   /* We have found the TLV and it fits in the already allocated
+    * memory, in-place editing is enough. freeing logic stays
+    * identical since the length still maps to the old pool.
+    */
+   memcpy(tlv->value, value, len);
+   tlv->len = len;
+   return 0;
+ }
+
+ if (found) {
+   /* first, we need to free the previous TLV item */
+   list_for_each_entry_safe(tlv, tlv_back, &conn->tlv_list, list) {
+     if (tlv->type != type)
+       continue;
+
+     LIST_DELETE(&tlv->list);
+     if (tlv->len > HA_PP2_TLV_VALUE_256)
+       free(tlv);
+     else if (tlv->len < HA_PP2_TLV_VALUE_128)
+       pool_free(pool_head_pp_tlv_128, tlv);
+     else
+       pool_free(pool_head_pp_tlv_256, tlv);
+   }
+ }
+
+ if (len > HA_PP2_TLV_VALUE_256)
+   tlv = malloc(len + sizeof(struct conn_tlv_list));
+ else if (len <= HA_PP2_TLV_VALUE_128)
+   tlv = pool_alloc(pool_head_pp_tlv_128);
+ else
+   tlv = pool_alloc(pool_head_pp_tlv_256);
+
+ if (unlikely(!tlv))
+   return -1;
+
+ tlv->len = len;
+ tlv->type = type;
+ memcpy(tlv->value, value, len);
+
+ LIST_APPEND(&conn->tlv_list, &tlv->list);
+ return 0;
+}
+
 /* Sets the netfilter mark on the connection's socket. The connection is tested
  * and if it is null, nothing is done.
  */
--
2.35.3


Reply via email to