From cc8fe58a8d2f8d47b03d03fd1048fe1b9babca70 Mon Sep 17 00:00:00 2001 From: Alexander Stephan <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