blob: 4fbd98c1c4eb76e850c93424714df245cb81bbbc [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>
36#include <haproxy/global.h>
37#include <haproxy/listener-t.h>
38#include <haproxy/namespace.h>
39#include <haproxy/proxy-t.h>
40#include <haproxy/sample.h>
41#include <haproxy/tools.h>
42
43
Christopher Faulet7d081f02021-04-15 09:38:37 +020044/* Fetch the connection's source IPv4/IPv6 address. Depending on the keyword, it
45 * may be the frontend or the backend connection.
Willy Tarreau8987e7a2020-08-28 11:37:21 +020046 */
47static int
48smp_fetch_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
49{
Christopher Faulet7d081f02021-04-15 09:38:37 +020050 struct connection *conn = (kw[0] != 'b') ? objt_conn(smp->sess->origin) :
51 smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
Willy Tarreau8987e7a2020-08-28 11:37:21 +020052
Christopher Faulet7d081f02021-04-15 09:38:37 +020053 if (!conn)
Willy Tarreau8987e7a2020-08-28 11:37:21 +020054 return 0;
55
Christopher Faulet7d081f02021-04-15 09:38:37 +020056 if (!conn_get_src(conn))
Willy Tarreau8987e7a2020-08-28 11:37:21 +020057 return 0;
58
Christopher Faulet7d081f02021-04-15 09:38:37 +020059 switch (conn->src->ss_family) {
Willy Tarreau8987e7a2020-08-28 11:37:21 +020060 case AF_INET:
Christopher Faulet7d081f02021-04-15 09:38:37 +020061 smp->data.u.ipv4 = ((struct sockaddr_in *)conn->src)->sin_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +020062 smp->data.type = SMP_T_IPV4;
63 break;
64 case AF_INET6:
Christopher Faulet7d081f02021-04-15 09:38:37 +020065 smp->data.u.ipv6 = ((struct sockaddr_in6 *)conn->src)->sin6_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +020066 smp->data.type = SMP_T_IPV6;
67 break;
68 default:
69 return 0;
70 }
71
72 smp->flags = 0;
73 return 1;
74}
75
Christopher Faulet7d081f02021-04-15 09:38:37 +020076/* set temp integer to the connection's source port. Depending on the
77 * keyword, it may be the frontend or the backend connection.
78 */
Willy Tarreau8987e7a2020-08-28 11:37:21 +020079static int
Christopher Faulet7d081f02021-04-15 09:38:37 +020080smp_fetch_sport(const struct arg *args, struct sample *smp, const char *kw, void *private)
Willy Tarreau8987e7a2020-08-28 11:37:21 +020081{
Christopher Faulet7d081f02021-04-15 09:38:37 +020082 struct connection *conn = (kw[0] != 'b') ? objt_conn(smp->sess->origin) :
83 smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
Willy Tarreau8987e7a2020-08-28 11:37:21 +020084
Christopher Faulet7d081f02021-04-15 09:38:37 +020085 if (!conn)
Willy Tarreau8987e7a2020-08-28 11:37:21 +020086 return 0;
87
Christopher Faulet7d081f02021-04-15 09:38:37 +020088 if (!conn_get_src(conn))
Willy Tarreau8987e7a2020-08-28 11:37:21 +020089 return 0;
90
91 smp->data.type = SMP_T_SINT;
Christopher Faulet7d081f02021-04-15 09:38:37 +020092 if (!(smp->data.u.sint = get_host_port(conn->src)))
Willy Tarreau8987e7a2020-08-28 11:37:21 +020093 return 0;
94
95 smp->flags = 0;
96 return 1;
97}
98
Christopher Faulet7d081f02021-04-15 09:38:37 +020099/* fetch the connection's destination IPv4/IPv6 address. Depending on the
100 * keyword, it may be the frontend or the backend connection.
101 */
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200102static int
103smp_fetch_dst(const struct arg *args, struct sample *smp, const char *kw, void *private)
104{
Christopher Faulet7d081f02021-04-15 09:38:37 +0200105 struct connection *conn = (kw[0] != 'b') ? objt_conn(smp->sess->origin) :
106 smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200107
Christopher Faulet7d081f02021-04-15 09:38:37 +0200108 if (!conn)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200109 return 0;
110
Christopher Faulet7d081f02021-04-15 09:38:37 +0200111 if (!conn_get_dst(conn))
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200112 return 0;
113
Christopher Faulet7d081f02021-04-15 09:38:37 +0200114 switch (conn->dst->ss_family) {
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200115 case AF_INET:
Christopher Faulet7d081f02021-04-15 09:38:37 +0200116 smp->data.u.ipv4 = ((struct sockaddr_in *)conn->dst)->sin_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200117 smp->data.type = SMP_T_IPV4;
118 break;
119 case AF_INET6:
Christopher Faulet7d081f02021-04-15 09:38:37 +0200120 smp->data.u.ipv6 = ((struct sockaddr_in6 *)conn->dst)->sin6_addr;
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200121 smp->data.type = SMP_T_IPV6;
122 break;
123 default:
124 return 0;
125 }
126
127 smp->flags = 0;
128 return 1;
129}
130
131/* check if the destination address of the front connection is local to the
132 * system or if it was intercepted.
133 */
134int smp_fetch_dst_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
135{
136 struct connection *conn = objt_conn(smp->sess->origin);
137 struct listener *li = smp->sess->listener;
138
139 if (!conn)
140 return 0;
141
142 if (!conn_get_dst(conn))
143 return 0;
144
145 smp->data.type = SMP_T_BOOL;
146 smp->flags = 0;
Willy Tarreau818a92e2020-09-03 07:50:19 +0200147 smp->data.u.sint = addr_is_local(li->rx.settings->netns, conn->dst);
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200148 return smp->data.u.sint >= 0;
149}
150
151/* check if the source address of the front connection is local to the system
152 * or not.
153 */
154int smp_fetch_src_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
155{
156 struct connection *conn = objt_conn(smp->sess->origin);
157 struct listener *li = smp->sess->listener;
158
159 if (!conn)
160 return 0;
161
162 if (!conn_get_src(conn))
163 return 0;
164
165 smp->data.type = SMP_T_BOOL;
166 smp->flags = 0;
Willy Tarreau818a92e2020-09-03 07:50:19 +0200167 smp->data.u.sint = addr_is_local(li->rx.settings->netns, conn->src);
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200168 return smp->data.u.sint >= 0;
169}
170
Christopher Faulet7d081f02021-04-15 09:38:37 +0200171/* set temp integer to the connexion's destination port. Depending on the
172 * keyword, it may be the frontend or the backend connection.
173 */
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200174static int
175smp_fetch_dport(const struct arg *args, struct sample *smp, const char *kw, void *private)
176{
Christopher Faulet7d081f02021-04-15 09:38:37 +0200177 struct connection *conn = (kw[0] != 'b') ? objt_conn(smp->sess->origin) :
178 smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200179
Christopher Faulet7d081f02021-04-15 09:38:37 +0200180 if (!conn)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200181 return 0;
182
Christopher Faulet7d081f02021-04-15 09:38:37 +0200183 if (!conn_get_dst(conn))
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200184 return 0;
185
186 smp->data.type = SMP_T_SINT;
Christopher Faulet7d081f02021-04-15 09:38:37 +0200187 if (!(smp->data.u.sint = get_host_port(conn->dst)))
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200188 return 0;
189
190 smp->flags = 0;
191 return 1;
192}
193
194#ifdef TCP_INFO
195
196
197/* Validates the arguments passed to "fc_*" fetch keywords returning a time
198 * value. These keywords support an optional string representing the unit of the
199 * result: "us" for microseconds and "ms" for milliseconds". Returns 0 on error
200 * and non-zero if OK.
201 */
202static int val_fc_time_value(struct arg *args, char **err)
203{
204 if (args[0].type == ARGT_STR) {
205 if (strcmp(args[0].data.str.area, "us") == 0) {
206 chunk_destroy(&args[0].data.str);
207 args[0].type = ARGT_SINT;
208 args[0].data.sint = TIME_UNIT_US;
209 }
210 else if (strcmp(args[0].data.str.area, "ms") == 0) {
211 chunk_destroy(&args[0].data.str);
212 args[0].type = ARGT_SINT;
213 args[0].data.sint = TIME_UNIT_MS;
214 }
215 else {
216 memprintf(err, "expects 'us' or 'ms', got '%s'",
217 args[0].data.str.area);
218 return 0;
219 }
220 }
221 else {
222 memprintf(err, "Unexpected arg type");
223 return 0;
224 }
225
226 return 1;
227}
228
229/* Validates the arguments passed to "fc_*" fetch keywords returning a
230 * counter. These keywords should be used without any keyword, but because of a
231 * bug in previous versions, an optional string argument may be passed. In such
232 * case, the argument is ignored and a warning is emitted. Returns 0 on error
233 * and non-zero if OK.
234 */
235static int var_fc_counter(struct arg *args, char **err)
236{
237 if (args[0].type != ARGT_STOP) {
238 ha_warning("no argument supported for 'fc_*' sample expressions returning counters.\n");
239 if (args[0].type == ARGT_STR)
240 chunk_destroy(&args[0].data.str);
241 args[0].type = ARGT_STOP;
242 }
243
244 return 1;
245}
246
247/* Returns some tcp_info data if it's available. "dir" must be set to 0 if
248 * the client connection is required, otherwise it is set to 1. "val" represents
249 * the required value.
250 * If the function fails it returns 0, otherwise it returns 1 and "result" is filled.
251 */
252static inline int get_tcp_info(const struct arg *args, struct sample *smp,
253 int dir, int val)
254{
255 struct connection *conn;
256 struct tcp_info info;
257 socklen_t optlen;
258
259 /* strm can be null. */
260 if (!smp->strm)
261 return 0;
262
263 /* get the object associated with the stream interface.The
264 * object can be other thing than a connection. For example,
265 * it be a appctx. */
266 conn = cs_conn(objt_cs(smp->strm->si[dir].end));
267 if (!conn)
268 return 0;
269
270 /* The fd may not be available for the tcp_info struct, and the
271 syscal can fail. */
272 optlen = sizeof(info);
Willy Tarreau4bfc6632021-03-31 08:45:47 +0200273 if (getsockopt(conn->handle.fd, IPPROTO_TCP, TCP_INFO, &info, &optlen) == -1)
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200274 return 0;
275
276 /* extract the value. */
277 smp->data.type = SMP_T_SINT;
278 switch (val) {
279 case 0: smp->data.u.sint = info.tcpi_rtt; break;
280 case 1: smp->data.u.sint = info.tcpi_rttvar; break;
281#if defined(__linux__)
282 /* these ones are common to all Linux versions */
283 case 2: smp->data.u.sint = info.tcpi_unacked; break;
284 case 3: smp->data.u.sint = info.tcpi_sacked; break;
285 case 4: smp->data.u.sint = info.tcpi_lost; break;
286 case 5: smp->data.u.sint = info.tcpi_retrans; break;
287 case 6: smp->data.u.sint = info.tcpi_fackets; break;
288 case 7: smp->data.u.sint = info.tcpi_reordering; break;
289#elif defined(__FreeBSD__) || defined(__NetBSD__)
290 /* the ones are found on FreeBSD and NetBSD featuring TCP_INFO */
291 case 2: smp->data.u.sint = info.__tcpi_unacked; break;
292 case 3: smp->data.u.sint = info.__tcpi_sacked; break;
293 case 4: smp->data.u.sint = info.__tcpi_lost; break;
294 case 5: smp->data.u.sint = info.__tcpi_retrans; break;
295 case 6: smp->data.u.sint = info.__tcpi_fackets; break;
296 case 7: smp->data.u.sint = info.__tcpi_reordering; break;
297#endif
298 default: return 0;
299 }
300
301 return 1;
302}
303
304/* get the mean rtt of a client connection */
305static int
306smp_fetch_fc_rtt(const struct arg *args, struct sample *smp, const char *kw, void *private)
307{
308 if (!get_tcp_info(args, smp, 0, 0))
309 return 0;
310
311 /* By default or if explicitly specified, convert rtt to ms */
312 if (!args || args[0].type == ARGT_STOP || args[0].data.sint == TIME_UNIT_MS)
313 smp->data.u.sint = (smp->data.u.sint + 500) / 1000;
314
315 return 1;
316}
317
318/* get the variance of the mean rtt of a client connection */
319static int
320smp_fetch_fc_rttvar(const struct arg *args, struct sample *smp, const char *kw, void *private)
321{
322 if (!get_tcp_info(args, smp, 0, 1))
323 return 0;
324
325 /* By default or if explicitly specified, convert rttvar to ms */
326 if (!args || args[0].type == ARGT_STOP || args[0].data.sint == TIME_UNIT_MS)
327 smp->data.u.sint = (smp->data.u.sint + 500) / 1000;
328
329 return 1;
330}
331
332#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
333
334/* get the unacked counter on a client connection */
335static int
336smp_fetch_fc_unacked(const struct arg *args, struct sample *smp, const char *kw, void *private)
337{
338 if (!get_tcp_info(args, smp, 0, 2))
339 return 0;
340 return 1;
341}
342
343/* get the sacked counter on a client connection */
344static int
345smp_fetch_fc_sacked(const struct arg *args, struct sample *smp, const char *kw, void *private)
346{
347 if (!get_tcp_info(args, smp, 0, 3))
348 return 0;
349 return 1;
350}
351
352/* get the lost counter on a client connection */
353static int
354smp_fetch_fc_lost(const struct arg *args, struct sample *smp, const char *kw, void *private)
355{
356 if (!get_tcp_info(args, smp, 0, 4))
357 return 0;
358 return 1;
359}
360
361/* get the retrans counter on a client connection */
362static int
363smp_fetch_fc_retrans(const struct arg *args, struct sample *smp, const char *kw, void *private)
364{
365 if (!get_tcp_info(args, smp, 0, 5))
366 return 0;
367 return 1;
368}
369
370/* get the fackets counter on a client connection */
371static int
372smp_fetch_fc_fackets(const struct arg *args, struct sample *smp, const char *kw, void *private)
373{
374 if (!get_tcp_info(args, smp, 0, 6))
375 return 0;
376 return 1;
377}
378
379/* get the reordering counter on a client connection */
380static int
381smp_fetch_fc_reordering(const struct arg *args, struct sample *smp, const char *kw, void *private)
382{
383 if (!get_tcp_info(args, smp, 0, 7))
384 return 0;
385 return 1;
386}
387#endif // linux || freebsd || netbsd
388#endif // TCP_INFO
389
390/* Note: must not be declared <const> as its list will be overwritten.
391 * Note: fetches that may return multiple types must be declared as the lowest
392 * common denominator, the type that can be casted into all other ones. For
393 * instance v4/v6 must be declared v4.
394 */
395static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet7d081f02021-04-15 09:38:37 +0200396 { "bc_dst", smp_fetch_dst, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
397 { "bc_dst_port", smp_fetch_dport, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
398 { "bc_src", smp_fetch_src, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
399 { "bc_src_port", smp_fetch_sport, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
400
Willy Tarreau8987e7a2020-08-28 11:37:21 +0200401 { "dst", smp_fetch_dst, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
402 { "dst_is_local", smp_fetch_dst_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
403 { "dst_port", smp_fetch_dport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
404 { "src", smp_fetch_src, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
405 { "src_is_local", smp_fetch_src_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
406 { "src_port", smp_fetch_sport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
407#ifdef TCP_INFO
408 { "fc_rtt", smp_fetch_fc_rtt, ARG1(0,STR), val_fc_time_value, SMP_T_SINT, SMP_USE_L4CLI },
409 { "fc_rttvar", smp_fetch_fc_rttvar, ARG1(0,STR), val_fc_time_value, SMP_T_SINT, SMP_USE_L4CLI },
410#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
411 { "fc_unacked", smp_fetch_fc_unacked, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
412 { "fc_sacked", smp_fetch_fc_sacked, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
413 { "fc_retrans", smp_fetch_fc_retrans, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
414 { "fc_fackets", smp_fetch_fc_fackets, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
415 { "fc_lost", smp_fetch_fc_lost, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
416 { "fc_reordering", smp_fetch_fc_reordering, ARG1(0,STR), var_fc_counter, SMP_T_SINT, SMP_USE_L4CLI },
417#endif // linux || freebsd || netbsd
418#endif // TCP_INFO
419 { /* END */ },
420}};
421
422INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
423
424
425/*
426 * Local variables:
427 * c-indent-level: 8
428 * c-basic-offset: 8
429 * End:
430 */