From cc8fe58a8d2f8d47b03d03fd1048fe1b9babca70 Mon Sep 17 00:00:00 2001
From: Alexander Stephan
<[email protected]<mailto:[email protected]>>
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