blob: d167bde86b33eda58987fa2d10a5b24dd346dc61 [file] [log] [blame]
Willy Tarreau8987e7a2020-08-28 11:37:21 +02001/*
2 * AF_INET/AF_INET6 SOCK_STREAM protocol layer (tcp)
3 *
4 * Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13/* this is to have tcp_info defined on systems using musl
14 * library, such as Alpine Linux.
15 */
16#define _GNU_SOURCE
17
18#include <ctype.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <time.h>
25
26#include <sys/param.h>
27#include <sys/socket.h>
28#include <sys/types.h>
29
30#include <netinet/tcp.h>
31#include <netinet/in.h>
32
33#include <haproxy/api.h>
34#include <haproxy/arg.h>
35#include <haproxy/connection.h>
Willy Tarreau6cd007d2021-10-06 19:01:21 +020036#include <haproxy/errors.h>
Willy Tarreau8987e7a2020-08-28 11:37:21 +020037#include <haproxy/global.h>
38#include <haproxy/listener-t.h>
39#include <haproxy/namespace.h>
40#include <haproxy/proxy-t.h>
41#include <haproxy/sample.h>
Christopher Fauletc03be1a2021-10-25 08:01:20 +020042#include <haproxy/session.h>
43#include <haproxy/stream_interface.h>
Willy Tarreau8987e7a2020-08-28 11:37:21 +020044#include <haproxy/tools.h>
45
Christopher Faulet7d081f02021-04-15 09:38:37 +020046/* Fetch the connection's source IPv4/IPv6 address. Depending on the keyword, it
47 * may be the frontend or the backend connection.
Willy Tarreau8987e7a2020-08-28 11:37:21 +020048 */
49static int
50smp_fetch_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
51{
Christopher Fauletc03be1a2021-10-25 08:01:20 +020052 const struct sockaddr_storage *src = NULL;
Christopher Faulet003df1c2021-04-15 09:39:38 +020053
Christopher Fauletc03be1a2021-10-25 08:01:20 +020054 if (kw[0] == 'b') {
55 struct connection *conn = ((obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
56 ? cs_conn(__objt_check(smp->sess->origin)->cs)
57 : (smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)): NULL));
58 if (conn && conn_get_src(conn))
59 src = conn_src(conn);
60 }
Christopher Faulet003df1c2021-04-15 09:39:38 +020061 else
Christopher Fauletc03be1a2021-10-25 08:01:20 +020062 src = (smp->strm ? si_src(&smp->strm->si[0]) : sess_src(smp->sess));
Willy Tarreau8987e7a2020-08-28 11:37:21 +020063
Christopher Fauletc03be1a2021-10-25 08:01:20 +020064 if (!src)
Willy Tarreau8987e7a2020-08-28 11:37:21 +020065 return 0;
66
Christopher Fauletc03be1a2021-10-25 08:01:20 +020067 switch (src->ss_family) {
Willy Tarreau8987e7a2020-08-28 11:37:21 +020068 case AF_INET:
Christopher Fauletc03be1a2021-10-25 08:01:20 +020069 smp->data.u.ipv4 = ((struct sockaddr_in *)src)->sin_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +020070 smp->data.type = SMP_T_IPV4;
71 break;
72 case AF_INET6:
Christopher Fauletc03be1a2021-10-25 08:01:20 +020073 smp->data.u.ipv6 = ((struct sockaddr_in6 *)src)->sin6_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +020074 smp->data.type = SMP_T_IPV6;
75 break;
76 default:
77 return 0;
78 }
79
80 smp->flags = 0;
81 return 1;
82}
83
Christopher Faulet7d081f02021-04-15 09:38:37 +020084/* set temp integer to the connection's source port. Depending on the
85 * keyword, it may be the frontend or the backend connection.
86 */
Willy Tarreau8987e7a2020-08-28 11:37:21 +020087static int
Christopher Faulet7d081f02021-04-15 09:38:37 +020088smp_fetch_sport(const struct arg *args, struct sample *smp, const char *kw, void *private)
Willy Tarreau8987e7a2020-08-28 11:37:21 +020089{
Christopher Fauletc03be1a2021-10-25 08:01:20 +020090 const struct sockaddr_storage *src = NULL;
Christopher Faulet003df1c2021-04-15 09:39:38 +020091
Christopher Fauletc03be1a2021-10-25 08:01:20 +020092 if (kw[0] == 'b') {
93 struct connection *conn = ((obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
94 ? cs_conn(__objt_check(smp->sess->origin)->cs)
95 : (smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)): NULL));
96 if (conn && conn_get_src(conn))
97 src = conn_src(conn);
98 }
Christopher Faulet003df1c2021-04-15 09:39:38 +020099 else
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200100 src = (smp->strm ? si_src(&smp->strm->si[0]) : sess_src(smp->sess));
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200101
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200102 if (!src)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200103 return 0;
104
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200105 smp->data.type = SMP_T_SINT;
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200106 if (!(smp->data.u.sint = get_host_port(src)))
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200107 return 0;
108
109 smp->flags = 0;
110 return 1;
111}
112
Christopher Faulet7d081f02021-04-15 09:38:37 +0200113/* fetch the connection's destination IPv4/IPv6 address. Depending on the
114 * keyword, it may be the frontend or the backend connection.
115 */
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200116static int
117smp_fetch_dst(const struct arg *args, struct sample *smp, const char *kw, void *private)
118{
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200119 const struct sockaddr_storage *dst = NULL;
Christopher Faulet003df1c2021-04-15 09:39:38 +0200120
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200121 if (kw[0] == 'b') {
122 struct connection *conn = ((obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
123 ? cs_conn(__objt_check(smp->sess->origin)->cs)
124 : (smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)): NULL));
125 if (conn && conn_get_dst(conn))
126 dst = conn_dst(conn);
127 }
Christopher Faulet003df1c2021-04-15 09:39:38 +0200128 else
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200129 dst = (smp->strm ? si_dst(&smp->strm->si[0]) : sess_dst(smp->sess));
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200130
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200131 if (!dst)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200132 return 0;
133
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200134 switch (dst->ss_family) {
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200135 case AF_INET:
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200136 smp->data.u.ipv4 = ((struct sockaddr_in *)dst)->sin_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200137 smp->data.type = SMP_T_IPV4;
138 break;
139 case AF_INET6:
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200140 smp->data.u.ipv6 = ((struct sockaddr_in6 *)dst)->sin6_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200141 smp->data.type = SMP_T_IPV6;
142 break;
143 default:
144 return 0;
145 }
146
147 smp->flags = 0;
148 return 1;
149}
150
151/* check if the destination address of the front connection is local to the
152 * system or if it was intercepted.
153 */
154int smp_fetch_dst_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
155{
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200156 struct listener *li = smp->sess->listener;
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200157 const struct sockaddr_storage *dst = (smp->strm ? si_dst(&smp->strm->si[0]) : sess_dst(smp->sess));
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200158
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200159 if (!dst)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200160 return 0;
161
162 smp->data.type = SMP_T_BOOL;
163 smp->flags = 0;
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200164 smp->data.u.sint = addr_is_local(li->rx.settings->netns, dst);
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200165 return smp->data.u.sint >= 0;
166}
167
168/* check if the source address of the front connection is local to the system
169 * or not.
170 */
171int smp_fetch_src_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
172{
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200173 struct listener *li = smp->sess->listener;
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200174 const struct sockaddr_storage *src = (smp->strm ? si_src(&smp->strm->si[0]) : sess_src(smp->sess));
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200175
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200176 if (!src)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200177 return 0;
178
179 smp->data.type = SMP_T_BOOL;
180 smp->flags = 0;
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200181 smp->data.u.sint = addr_is_local(li->rx.settings->netns, src);
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200182 return smp->data.u.sint >= 0;
183}
184
Christopher Faulet7d081f02021-04-15 09:38:37 +0200185/* set temp integer to the connexion's destination port. Depending on the
186 * keyword, it may be the frontend or the backend connection.
187 */
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200188static int
189smp_fetch_dport(const struct arg *args, struct sample *smp, const char *kw, void *private)
190{
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200191 const struct sockaddr_storage *dst = NULL;
Christopher Faulet003df1c2021-04-15 09:39:38 +0200192
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200193 if (kw[0] == 'b') {
194 struct connection *conn = ((obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
195 ? cs_conn(__objt_check(smp->sess->origin)->cs)
196 : (smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)): NULL));
197 if (conn && conn_get_dst(conn))
198 dst = conn_dst(conn);
199 }
Christopher Faulet003df1c2021-04-15 09:39:38 +0200200 else
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200201 dst = (smp->strm ? si_dst(&smp->strm->si[0]) : sess_dst(smp->sess));
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200202
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200203 if (!dst)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200204 return 0;
205
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200206 smp->data.type = SMP_T_SINT;
Christopher Fauletc03be1a2021-10-25 08:01:20 +0200207 if (!(smp->data.u.sint = get_host_port(dst)))
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200208 return 0;
209
210 smp->flags = 0;
211 return 1;
212}
213
214#ifdef TCP_INFO
215
216
217/* Validates the arguments passed to "fc_*" fetch keywords returning a time
218 * value. These keywords support an optional string representing the unit of the
219 * result: "us" for microseconds and "ms" for milliseconds". Returns 0 on error
220 * and non-zero if OK.
221 */
222static int val_fc_time_value(struct arg *args, char **err)
223{
224 if (args[0].type == ARGT_STR) {
225 if (strcmp(args[0].data.str.area, "us") == 0) {
226 chunk_destroy(&args[0].data.str);
227 args[0].type = ARGT_SINT;
228 args[0].data.sint = TIME_UNIT_US;
229 }
230 else if (strcmp(args[0].data.str.area, "ms") == 0) {
231 chunk_destroy(&args[0].data.str);
232 args[0].type = ARGT_SINT;
233 args[0].data.sint = TIME_UNIT_MS;
234 }
235 else {
236 memprintf(err, "expects 'us' or 'ms', got '%s'",
237 args[0].data.str.area);
238 return 0;
239 }
240 }
241 else {
242 memprintf(err, "Unexpected arg type");
243 return 0;
244 }
245
246 return 1;
247}
248
249/* Validates the arguments passed to "fc_*" fetch keywords returning a
250 * counter. These keywords should be used without any keyword, but because of a
251 * bug in previous versions, an optional string argument may be passed. In such
252 * case, the argument is ignored and a warning is emitted. Returns 0 on error
253 * and non-zero if OK.
254 */
255static int var_fc_counter(struct arg *args, char **err)
256{
257 if (args[0].type != ARGT_STOP) {
258 ha_warning("no argument supported for 'fc_*' sample expressions returning counters.\n");
259 if (args[0].type == ARGT_STR)
260 chunk_destroy(&args[0].data.str);
261 args[0].type = ARGT_STOP;
262 }
263
264 return 1;
265}
266
267/* Returns some tcp_info data if it's available. "dir" must be set to 0 if
268 * the client connection is required, otherwise it is set to 1. "val" represents
269 * the required value.
270 * If the function fails it returns 0, otherwise it returns 1 and "result" is filled.
271 */
272static inline int get_tcp_info(const struct arg *args, struct sample *smp,
273 int dir, int val)
274{
275 struct connection *conn;
276 struct tcp_info info;
277 socklen_t optlen;
278
279 /* strm can be null. */
280 if (!smp->strm)
281 return 0;
282
283 /* get the object associated with the stream interface.The
284 * object can be other thing than a connection. For example,
285 * it be a appctx. */
286 conn = cs_conn(objt_cs(smp->strm->si[dir].end));
287 if (!conn)
288 return 0;
289
290 /* The fd may not be available for the tcp_info struct, and the
291 syscal can fail. */
292 optlen = sizeof(info);
Willy Tarreau4bfc6632021-03-31 08:45:47 +0200293 if (getsockopt(conn->handle.fd, IPPROTO_TCP, TCP_INFO, &info, &optlen) == -1)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200294 return 0;
295
296 /* extract the value. */
297 smp->data.type = SMP_T_SINT;
298 switch (val) {
299 case 0: smp->data.u.sint = info.tcpi_rtt; break;
300 case 1: smp->data.u.sint = info.tcpi_rttvar; break;
301#if defined(__linux__)
302 /* these ones are common to all Linux versions */
303 case 2: smp->data.u.sint = info.tcpi_unacked; break;
304 case 3: smp->data.u.sint = info.tcpi_sacked; break;
305 case 4: smp->data.u.sint = info.tcpi_lost; break;
306 case 5: smp->data.u.sint = info.tcpi_retrans; break;
307 case 6: smp->data.u.sint = info.tcpi_fackets; break;
308 case 7: smp->data.u.sint = info.tcpi_reordering; break;
309#elif defined(__FreeBSD__) || defined(__NetBSD__)
310 /* the ones are found on FreeBSD and NetBSD featuring TCP_INFO */
311 case 2: smp->data.u.sint = info.__tcpi_unacked; break;
312 case 3: smp->data.u.sint = info.__tcpi_sacked; break;
313 case 4: smp->data.u.sint = info.__tcpi_lost; break;
314 case 5: smp->data.u.sint = info.__tcpi_retrans; break;
315 case 6: smp->data.u.sint = info.__tcpi_fackets; break;
316 case 7: smp->data.u.sint = info.__tcpi_reordering; break;
317#endif
318 default: return 0;
319 }
320
321 return 1;
322}
323
324/* get the mean rtt of a client connection */
325static int
326smp_fetch_fc_rtt(const struct arg *args, struct sample *smp, const char *kw, void *private)
327{
328 if (!get_tcp_info(args, smp, 0, 0))
329 return 0;
330
331 /* By default or if explicitly specified, convert rtt to ms */
332 if (!args || args[0].type == ARGT_STOP || args[0].data.sint == TIME_UNIT_MS)
333 smp->data.u.sint = (smp->data.u.sint + 500) / 1000;
334
335 return 1;
336}
337
338/* get the variance of the mean rtt of a client connection */
339static int
340smp_fetch_fc_rttvar(const struct arg *args, struct sample *smp, const char *kw, void *private)
341{
342 if (!get_tcp_info(args, smp, 0, 1))
343 return 0;
344
345 /* By default or if explicitly specified, convert rttvar to ms */
346 if (!args || args[0].type == ARGT_STOP || args[0].data.sint == TIME_UNIT_MS)
347 smp->data.u.sint = (smp->data.u.sint + 500) / 1000;
348
349 return 1;
350}
351
352#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
353
354/* get the unacked counter on a client connection */
355static int
356smp_fetch_fc_unacked(const struct arg *args, struct sample *smp, const char *kw, void *private)
357{
358 if (!get_tcp_info(args, smp, 0, 2))
359 return 0;
360 return 1;
361}
362
363/* get the sacked counter on a client connection */
364static int
365smp_fetch_fc_sacked(const struct arg *args, struct sample *smp, const char *kw, void *private)
366{
367 if (!get_tcp_info(args, smp, 0, 3))
368 return 0;
369 return 1;
370}
371
372/* get the lost counter on a client connection */
373static int
374smp_fetch_fc_lost(const struct arg *args, struct sample *smp, const char *kw, void *private)
375{
376 if (!get_tcp_info(args, smp, 0, 4))
377 return 0;
378 return 1;
379}
380
381/* get the retrans counter on a client connection */
382static int
383smp_fetch_fc_retrans(const struct arg *args, struct sample *smp, const char *kw, void *private)
384{
385 if (!get_tcp_info(args, smp, 0, 5))
386 return 0;
387 return 1;
388}
389
390/* get the fackets counter on a client connection */
391static int
392smp_fetch_fc_fackets(const struct arg *args, struct sample *smp, const char *kw, void *private)
393{
394 if (!get_tcp_info(args, smp, 0, 6))
395 return 0;
396 return 1;
397}
398
399/* get the reordering counter on a client connection */
400static int
401smp_fetch_fc_reordering(const struct arg *args, struct sample *smp, const char *kw, void *private)
402{
403 if (!get_tcp_info(args, smp, 0, 7))
404 return 0;
405 return 1;
406}
407#endif // linux || freebsd || netbsd
408#endif // TCP_INFO
409
410/* Note: must not be declared <const> as its list will be overwritten.
411 * Note: fetches that may return multiple types must be declared as the lowest
412 * common denominator, the type that can be casted into all other ones. For
413 * instance v4/v6 must be declared v4.
414 */
415static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet7d081f02021-04-15 09:38:37 +0200416 { "bc_dst", smp_fetch_dst, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
417 { "bc_dst_port", smp_fetch_dport, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
418 { "bc_src", smp_fetch_src, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
419 { "bc_src_port", smp_fetch_sport, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
420
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200421 { "dst", smp_fetch_dst, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
422 { "dst_is_local", smp_fetch_dst_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
423 { "dst_port", smp_fetch_dport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
424 { "src", smp_fetch_src, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
425 { "src_is_local", smp_fetch_src_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
426 { "src_port", smp_fetch_sport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
427#ifdef TCP_INFO
428 { "fc_rtt", smp_fetch_fc_rtt, ARG1(0,STR), val_fc_time_value, SMP_T_SINT, SMP_USE_L4CLI },
429 { "fc_rttvar", smp_fetch_fc_rttvar, ARG1(0,STR), val_fc_time_value, SMP_T_SINT, SMP_USE_L4CLI },
430#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
431 { "fc_unacked", smp_fetch_fc_unacked, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
432 { "fc_sacked", smp_fetch_fc_sacked, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
433 { "fc_retrans", smp_fetch_fc_retrans, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
434 { "fc_fackets", smp_fetch_fc_fackets, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
435 { "fc_lost", smp_fetch_fc_lost, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
436 { "fc_reordering", smp_fetch_fc_reordering, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
437#endif // linux || freebsd || netbsd
438#endif // TCP_INFO
439 { /* END */ },
440}};
441
442INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
443
444
445/*
446 * Local variables:
447 * c-indent-level: 8
448 * c-basic-offset: 8
449 * End:
450 */