Skip to content

Commit fecc573

Browse files
alexanderstephanwtarreau
authored andcommitted
MEDIUM: connection: Generic, list-based allocation and look-up of PPv2 TLVs
In order to be able to implement fetches in the future that allow retrieval of any TLVs, a new generic data structure for TLVs is introduced. Existing TLV fetches for PP2_TYPE_AUTHORITY and PP2_TYPE_UNIQUE_ID are migrated to use this new data structure. TLV related pools are updated to not rely on type, but only on size. Pools accomodate the TLV list element with their associated value. For now, two pools for 128 B and 256 B values are introduced. More fine-grained solutions are possible in the future, if necessary.
1 parent c9d4765 commit fecc573

File tree

3 files changed

+122
-48
lines changed

3 files changed

+122
-48
lines changed

include/haproxy/connection-t.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,19 @@ struct conn_hash_params {
503503
struct sockaddr_storage *dst_addr;
504504
};
505505

506+
/*
507+
* This structure describes an TLV entry consisting of its type
508+
* and corresponding payload. This can be used to construct a list
509+
* from which arbitrary TLV payloads can be fetched.
510+
* It might be possible to embed the 'tlv struct' here in the future.
511+
*/
512+
struct conn_tlv_list {
513+
struct list list;
514+
unsigned short len; // 65535 should be more than enough!
515+
unsigned char type;
516+
char value[0];
517+
} __attribute__((packed));
518+
506519
/* This structure describes a connection with its methods and data.
507520
* A connection may be performed to proxy or server via a local or remote
508521
* socket, and can also be made to an internal applet. It can support
@@ -541,8 +554,7 @@ struct connection {
541554
void (*destroy_cb)(struct connection *conn); /* callback to notify of imminent death of the connection */
542555
struct sockaddr_storage *src; /* source address (pool), when known, otherwise NULL */
543556
struct sockaddr_storage *dst; /* destination address (pool), when known, otherwise NULL */
544-
struct ist proxy_authority; /* Value of the authority TLV received via PROXYv2 */
545-
struct ist proxy_unique_id; /* Value of the unique ID TLV received via PROXYv2 */
557+
struct list tlv_list; /* list of TLVs received via PROXYv2 */
546558

547559
/* used to identify a backend connection for http-reuse,
548560
* thus only present if conn.target is of type OBJ_TYPE_SERVER
@@ -630,7 +642,10 @@ struct mux_proto_list {
630642

631643
#define TLV_HEADER_SIZE 3
632644

633-
#define HA_PP2_AUTHORITY_MAX 255 /* Maximum length of an authority TLV */
645+
#define HA_PP2_AUTHORITY_MAX 255 /* Maximum length of an authority TLV */
646+
#define HA_PP2_TLV_VALUE_128 128 /* E.g., accomodate unique IDs (128 B) */
647+
#define HA_PP2_TLV_VALUE_256 256 /* E.g., accomodate authority TLVs (currently, <= 255 B) */
648+
#define HA_PP2_MAX_ALLOC 1024 /* Maximum TLV value for PPv2 to prevent DoS */
634649

635650
struct proxy_hdr_v2 {
636651
uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */

include/haproxy/connection.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
extern struct pool_head *pool_head_connection;
4141
extern struct pool_head *pool_head_conn_hash_node;
4242
extern struct pool_head *pool_head_sockaddr;
43-
extern struct pool_head *pool_head_authority;
43+
extern struct pool_head *pool_head_pp_tlv_128;
44+
extern struct pool_head *pool_head_pp_tlv_256;
4445
extern struct pool_head *pool_head_uniqueid;
4546
extern struct xprt_ops *registered_xprt[XPRT_ENTRIES];
4647
extern struct mux_proto_list mux_proto_list;
@@ -52,6 +53,7 @@ extern struct mux_stopping_data mux_stopping_data[MAX_THREADS];
5253
int conn_recv_proxy(struct connection *conn, int flag);
5354
int conn_send_proxy(struct connection *conn, unsigned int flag);
5455
int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm);
56+
struct conn_tlv_list *conn_get_tlv(struct connection *conn, int type);
5557

5658
int conn_append_debug_info(struct buffer *buf, const struct connection *conn, const char *pfx);
5759

src/connection.c

Lines changed: 101 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
DECLARE_POOL(pool_head_connection, "connection", sizeof(struct connection));
3838
DECLARE_POOL(pool_head_conn_hash_node, "conn_hash_node", sizeof(struct conn_hash_node));
3939
DECLARE_POOL(pool_head_sockaddr, "sockaddr", sizeof(struct sockaddr_storage));
40-
DECLARE_POOL(pool_head_authority, "authority", HA_PP2_AUTHORITY_MAX);
40+
DECLARE_POOL(pool_head_pp_tlv_128, "pp_tlv_128", sizeof(struct conn_tlv_list) + HA_PP2_TLV_VALUE_128);
41+
DECLARE_POOL(pool_head_pp_tlv_256, "pp_tlv_256", sizeof(struct conn_tlv_list) + HA_PP2_TLV_VALUE_256);
4142

4243
struct idle_conns idle_conns[MAX_THREADS] = { };
4344
struct xprt_ops *registered_xprt[XPRT_ENTRIES] = { NULL, };
@@ -52,6 +53,22 @@ struct mux_stopping_data mux_stopping_data[MAX_THREADS];
5253
/* disables sending of proxy-protocol-v2's LOCAL command */
5354
static int pp2_never_send_local;
5455

56+
/* find the value of a received TLV for a given type */
57+
struct conn_tlv_list *conn_get_tlv(struct connection *conn, int type)
58+
{
59+
struct conn_tlv_list *tlv = NULL;
60+
61+
if (!conn)
62+
return NULL;
63+
64+
list_for_each_entry(tlv, &conn->tlv_list, list) {
65+
if (tlv->type == type)
66+
return tlv;
67+
}
68+
69+
return NULL;
70+
}
71+
5572
/* Remove <conn> idle connection from its attached tree (idle, safe or avail).
5673
* If also present in the secondary server idle list, conn is removed from it.
5774
*
@@ -447,11 +464,10 @@ void conn_init(struct connection *conn, void *target)
447464
LIST_INIT(&conn->session_list);
448465
else
449466
LIST_INIT(&conn->stopping_list);
467+
LIST_INIT(&conn->tlv_list);
450468
conn->subs = NULL;
451469
conn->src = NULL;
452470
conn->dst = NULL;
453-
conn->proxy_authority = IST_NULL;
454-
conn->proxy_unique_id = IST_NULL;
455471
conn->hash_node = NULL;
456472
conn->xprt = NULL;
457473
conn->reverse.target = NULL;
@@ -531,6 +547,8 @@ struct connection *conn_new(void *target)
531547
/* Releases a connection previously allocated by conn_new() */
532548
void conn_free(struct connection *conn)
533549
{
550+
struct conn_tlv_list *tlv, *tlv_back = NULL;
551+
534552
if (conn_is_back(conn))
535553
conn_backend_deinit(conn);
536554

@@ -545,11 +563,16 @@ void conn_free(struct connection *conn)
545563
sockaddr_free(&conn->src);
546564
sockaddr_free(&conn->dst);
547565

548-
pool_free(pool_head_authority, istptr(conn->proxy_authority));
549-
conn->proxy_authority = IST_NULL;
550-
551-
pool_free(pool_head_uniqueid, istptr(conn->proxy_unique_id));
552-
conn->proxy_unique_id = IST_NULL;
566+
/* Free all previously allocated TLVs */
567+
list_for_each_entry_safe(tlv, tlv_back, &conn->tlv_list, list) {
568+
LIST_DELETE(&tlv->list);
569+
if (tlv->len > HA_PP2_TLV_VALUE_256)
570+
free(tlv);
571+
else if (tlv->len < HA_PP2_TLV_VALUE_128)
572+
pool_free(pool_head_pp_tlv_128, tlv);
573+
else
574+
pool_free(pool_head_pp_tlv_256, tlv);
575+
}
553576

554577
ha_free(&conn->reverse.name.area);
555578

@@ -1080,8 +1103,10 @@ int conn_recv_proxy(struct connection *conn, int flag)
10801103

10811104
/* TLV parsing */
10821105
while (tlv_offset < total_v2_len) {
1083-
struct tlv *tlv_packet;
10841106
struct ist tlv;
1107+
struct tlv *tlv_packet = NULL;
1108+
struct conn_tlv_list *new_tlv = NULL;
1109+
size_t data_len = 0;
10851110

10861111
/* Verify that we have at least TLV_HEADER_SIZE bytes left */
10871112
if (tlv_offset + TLV_HEADER_SIZE > total_v2_len)
@@ -1095,6 +1120,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
10951120
if (tlv_offset > total_v2_len)
10961121
goto bad_header;
10971122

1123+
/* Prepare known TLV types */
10981124
switch (tlv_packet->type) {
10991125
case PP2_TYPE_CRC32C: {
11001126
uint32_t n_crc32c;
@@ -1121,36 +1147,49 @@ int conn_recv_proxy(struct connection *conn, int flag)
11211147
}
11221148
#endif
11231149
case PP2_TYPE_AUTHORITY: {
1150+
/* For now, keep the length restriction by HAProxy */
11241151
if (istlen(tlv) > HA_PP2_AUTHORITY_MAX)
11251152
goto bad_header;
1126-
conn->proxy_authority = ist2(pool_alloc(pool_head_authority), 0);
1127-
if (!isttest(conn->proxy_authority))
1128-
goto fail;
1129-
if (istcpy(&conn->proxy_authority, tlv, HA_PP2_AUTHORITY_MAX) < 0) {
1130-
/* This is impossible, because we verified that the TLV value fits. */
1131-
my_unreachable();
1132-
goto fail;
1133-
}
1153+
11341154
break;
11351155
}
11361156
case PP2_TYPE_UNIQUE_ID: {
11371157
if (istlen(tlv) > UNIQUEID_LEN)
11381158
goto bad_header;
1139-
conn->proxy_unique_id = ist2(pool_alloc(pool_head_uniqueid), 0);
1140-
if (!isttest(conn->proxy_unique_id))
1141-
goto fail;
1142-
if (istcpy(&conn->proxy_unique_id, tlv, UNIQUEID_LEN) < 0) {
1143-
/* This is impossible, because we verified that the TLV value fits. */
1144-
my_unreachable();
1145-
goto fail;
1146-
}
11471159
break;
11481160
}
11491161
default:
11501162
break;
11511163
}
1164+
1165+
/* If we did not find a known TLV type that we can optimize for, we generically allocate it */
1166+
data_len = get_tlv_length(tlv_packet);
1167+
1168+
/* Prevent attackers from allocating too much memory */
1169+
if (unlikely(data_len > HA_PP2_MAX_ALLOC))
1170+
goto fail;
1171+
1172+
/* Alloc memory based on data_len */
1173+
if (data_len > HA_PP2_TLV_VALUE_256)
1174+
new_tlv = malloc(get_tlv_length(tlv_packet) + sizeof(struct conn_tlv_list));
1175+
else if (data_len <= HA_PP2_TLV_VALUE_128)
1176+
new_tlv = pool_alloc(pool_head_pp_tlv_128);
1177+
else
1178+
new_tlv = pool_alloc(pool_head_pp_tlv_256);
1179+
1180+
if (unlikely(!new_tlv))
1181+
goto fail;
1182+
1183+
new_tlv->type = tlv_packet->type;
1184+
1185+
/* Save TLV to make it accessible via sample fetch */
1186+
memcpy(new_tlv->value, tlv.ptr, data_len);
1187+
new_tlv->len = data_len;
1188+
1189+
LIST_APPEND(&conn->tlv_list, &new_tlv->list);
11521190
}
11531191

1192+
11541193
/* Verify that the PROXYv2 header ends at a TLV boundary.
11551194
* This is can not be true, because the TLV parsing already
11561195
* verifies that a TLV does not exceed the total length and
@@ -1969,10 +2008,12 @@ static int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct
19692008
}
19702009

19712010
if (srv->pp_opts & SRV_PP_V2_AUTHORITY) {
2011+
struct conn_tlv_list *tlv = conn_get_tlv(remote, PP2_TYPE_AUTHORITY);
2012+
19722013
value = NULL;
1973-
if (remote && isttest(remote->proxy_authority)) {
1974-
value = istptr(remote->proxy_authority);
1975-
value_len = istlen(remote->proxy_authority);
2014+
if (tlv) {
2015+
value_len = tlv->len;
2016+
value = tlv->value;
19762017
}
19772018
#ifdef USE_OPENSSL
19782019
else {
@@ -2220,7 +2261,8 @@ int smp_fetch_fc_rcvd_proxy(const struct arg *args, struct sample *smp, const ch
22202261
/* fetch the authority TLV from a PROXY protocol header */
22212262
int smp_fetch_fc_pp_authority(const struct arg *args, struct sample *smp, const char *kw, void *private)
22222263
{
2223-
struct connection *conn;
2264+
struct connection *conn = NULL;
2265+
struct conn_tlv_list *conn_tlv;
22242266

22252267
conn = objt_conn(smp->sess->origin);
22262268
if (!conn)
@@ -2231,21 +2273,29 @@ int smp_fetch_fc_pp_authority(const struct arg *args, struct sample *smp, const
22312273
return 0;
22322274
}
22332275

2234-
if (!isttest(conn->proxy_authority))
2235-
return 0;
2276+
conn_tlv = smp->ctx.p ? smp->ctx.p : LIST_ELEM(conn->tlv_list.n, struct conn_tlv_list *, list);
2277+
list_for_each_entry_from(conn_tlv, &conn->tlv_list, list) {
2278+
if (conn_tlv->type == PP2_TYPE_AUTHORITY) {
2279+
smp->flags |= SMP_F_NOT_LAST;
2280+
smp->data.type = SMP_T_STR;
2281+
smp->data.u.str.area = conn_tlv->value;
2282+
smp->data.u.str.data = conn_tlv->len;
2283+
smp->ctx.p = conn_tlv;
22362284

2237-
smp->flags = 0;
2238-
smp->data.type = SMP_T_STR;
2239-
smp->data.u.str.area = istptr(conn->proxy_authority);
2240-
smp->data.u.str.data = istlen(conn->proxy_authority);
2285+
return 1;
2286+
}
2287+
}
22412288

2242-
return 1;
2289+
smp->flags &= ~SMP_F_NOT_LAST;
2290+
2291+
return 0;
22432292
}
22442293

22452294
/* fetch the unique ID TLV from a PROXY protocol header */
22462295
int smp_fetch_fc_pp_unique_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
22472296
{
2248-
struct connection *conn;
2297+
struct connection *conn = NULL;
2298+
struct conn_tlv_list *conn_tlv;
22492299

22502300
conn = objt_conn(smp->sess->origin);
22512301
if (!conn)
@@ -2256,15 +2306,22 @@ int smp_fetch_fc_pp_unique_id(const struct arg *args, struct sample *smp, const
22562306
return 0;
22572307
}
22582308

2259-
if (!isttest(conn->proxy_unique_id))
2260-
return 0;
2309+
conn_tlv = smp->ctx.p ? smp->ctx.p : LIST_ELEM(conn->tlv_list.n, struct conn_tlv_list *, list);
2310+
list_for_each_entry_from(conn_tlv, &conn->tlv_list, list) {
2311+
if (conn_tlv->type == PP2_TYPE_UNIQUE_ID) {
2312+
smp->flags |= SMP_F_NOT_LAST;
2313+
smp->data.type = SMP_T_STR;
2314+
smp->data.u.str.area = conn_tlv->value;
2315+
smp->data.u.str.data = conn_tlv->len;
2316+
smp->ctx.p = conn_tlv;
22612317

2262-
smp->flags = 0;
2263-
smp->data.type = SMP_T_STR;
2264-
smp->data.u.str.area = istptr(conn->proxy_unique_id);
2265-
smp->data.u.str.data = istlen(conn->proxy_unique_id);
2318+
return 1;
2319+
}
2320+
}
22662321

2267-
return 1;
2322+
smp->flags &= ~SMP_F_NOT_LAST;
2323+
2324+
return 0;
22682325
}
22692326

22702327
/* fetch the error code of a connection */

0 commit comments

Comments
 (0)