Compare commits

...

1 Commits

Author SHA1 Message Date
Frederic Lecaille
25b668d59a MINOR: stream: Add address filtering to "show sess" CLI command
This patch adds these four options to "show sess" CLI command:

  - fsrc <addr> to show only stream with <addr> as source address
  - fdst <addr> to show only stream with <addr> as destination address
  - bsrc <addr> to show only stream with <addr> as source address
  - bdst <addr> to show only stream with <addr> as destination address

The addresses are made of at least one parameter between the IP address and the
port for instance as follows:

  fsrc [::]:12345 # a port without address for an IPv6 address
  bsrc :12S45     # same as previous but for an IPv4 address
  bdst 10.10.10.10:12345
  fdst [::1]:12345

Only one option may be used by direction (source or destination). Indeed, at
this time only two sockaddr_storage structs have been added to the CLI I/O handler
context. This context has to be allocated because its size is for now on greater than
APPLET_MAX_SVCCTX bytes. This is done from new pool_head_show_sess_ctx() static
pool. ->src and ->dst new sockaddr_storage members are added to show_sess_ctx
struct to store these two addresses. CLI_SHOWSESS_F_NULL_SRC may be used when
the address stored into ->src is null. Same thing for CLI_SHOWSESS_F_NULL_DST
and ->dst. New CLI_SHOWSESS_F_[F|B](SRC|DST) new CLI context flags have been
added to identify the option used (at most two) on the CLI command line.

This patch also implements cli_sess_sk_to_be_skipped() to identify the
streams which must be skipped and not dumped by "show sess" depending on the
->src and ->dst addresses which have been parsed from fsrc, fdst, bsrc and
bdst new options. This is the role cli_parse_show_sess() modified by this
patch to parse these addresses calling str2sa_range().

cli_io_handler_dump_sess() is modified to call cli_sess_sk_to_be_skipped()
if one of the four CLI_SHOWSESS_F_[F|B](SRC|DST) new flags have been set
on the CLI I/O handler context flags for "show sess" command. The connection
is retrieved by strm_orig() for frontend connections if CLI_SHOWSESS_F_FSRC or
CLI_SHOWSESS_F_FDST have been set. sc_conn(strm->scb()) is called for
backend connections if CLI_SHOWSESS_F_BSRC or CLI_SHOWSESS_F_BDST have been set.
Then the current stream to be dumped is potentially skipped by cli_sess_sk_to_be_skipped()
by address direction.
2025-05-23 19:55:35 +02:00
2 changed files with 165 additions and 7 deletions

View File

@ -3402,6 +3402,19 @@ show sess [<id> | all | help] [<options>*]
analysis. It is only displayed if it was captured.
- susp only show streams considered as suspicious by the developers
based on criteria that may in time or vary along versions.
- fsrc [<addr>][:<port>] only show streams with <addr> as optional source
frontent address and <port> as optional port.
- fdst [<addr>][:<port>] only show streams with <addr> as optional destination
frontent address and <port> as optional port.
- bsrc [<addr>][:<port>] only show streams with <addr> as optional source
backend address and <port> as optional port.
- bdst [<addr>][:<port>] only show streams with <addr> as optional destination
backend address and <port> as optional port.
Note that for fsrc, fdst, bsrc and bdst options, at least one parameter must
be provided (the addresse or the port). Only one address may be used by
direction (only one option between fdst and bdst, and only one option between
fsrc and bsrc).
show stat [domain <resolvers|proxy>] [{<iid>|<proxy>} <type> <sid>] \
[typed|json] [desc] [up|no-maint]

View File

@ -3293,6 +3293,8 @@ void list_services(FILE *out)
fprintf(out, " none\n");
}
static const struct sockaddr_storage sk_null;
/* appctx context used by the "show sess" command */
/* flags used for show_sess_ctx.flags */
#define CLI_SHOWSESS_F_SUSP 0x00000001 /* show only suspicious streams */
@ -3300,6 +3302,12 @@ void list_services(FILE *out)
#define CLI_SHOWSESS_F_SERVER 0x00000004 /* show only streams attached to this server */
#define CLI_SHOWSESS_F_BACKEND 0x00000008 /* show only streams attached to this backend */
#define CLI_SHOWSESS_F_FRONTEND 0x00000010 /* show only streams attached to this frontend */
#define CLI_SHOWSESS_F_FSRC 0x00000020 /* show only streams with this frontend source address */
#define CLI_SHOWSESS_F_FDST 0x00000040 /* show only streams with this frontend destination address */
#define CLI_SHOWSESS_F_BSRC 0x00000080 /* show only streams with this backend source address */
#define CLI_SHOWSESS_F_BDST 0x00000100 /* show only streams with this backend destination address */
#define CLI_SHOWSESS_F_NULL_SRC 0x00000200 /* filter with a null source address */
#define CLI_SHOWSESS_F_NULL_DST 0x00000400 /* filter with a null destination address */
struct show_sess_ctx {
struct bref bref; /* back-reference from the session being dumped */
@ -3309,10 +3317,14 @@ struct show_sess_ctx {
unsigned int uid; /* if non-null, the uniq_id of the session being dumped */
unsigned int min_age; /* minimum age of streams to dump */
unsigned int flags; /* CLI_SHOWSESS_* */
struct sockaddr_storage src; /* frontend or backend source address to be used as filter */
struct sockaddr_storage dst; /* frontend or backend destination address to be used as filter */
int section; /* section of the session being dumped */
int pos; /* last position of the current session's buffer */
};
DECLARE_STATIC_POOL(pool_head_show_sess_ctx, "show_sess_ctx", sizeof(struct show_sess_ctx));
/* This function appends a complete dump of a stream state onto the buffer,
* possibly anonymizing using the specified anon_key. The caller is responsible
* for ensuring that enough room remains in the buffer to dump a complete stream
@ -3768,18 +3780,20 @@ static int stats_dump_full_strm_to_buffer(struct appctx *appctx, struct stream *
static int cli_parse_show_sess(char **args, char *payload, struct appctx *appctx, void *private)
{
struct show_sess_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
struct show_sess_ctx *ctx;
int cur_arg = 2;
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
return 1;
/* now all sessions by default */
ctx->target = NULL;
ctx->min_age = 0;
ctx->section = 0; /* start with stream status */
ctx->pos = 0;
ctx->thr = 0;
ctx = appctx->svcctx;
if (!ctx) {
ctx = pool_zalloc(pool_head_show_sess_ctx);
if (!ctx)
return cli_err(appctx, "show sess context allocation failed.\n");
appctx->svcctx = ctx;
}
if (*args[cur_arg] && strcmp(args[cur_arg], "older") == 0) {
unsigned timeout;
@ -3814,6 +3828,10 @@ static int cli_parse_show_sess(char **args, char *payload, struct appctx *appctx
" server <b/s> only show streams attached to this backend+server\n"
" backend <b> only show streams attached to this backend\n"
" frontend <f> only show streams attached to this frontend\n"
" fsrc [<addr>][:<port>] only show streams with this frontend source address\n"
" fdst [<addr>][:<port>] only show streams with this frontend destination address\n"
" bsrc [<addr>][:<port>] only show streams with this backend source address\n"
" bdst [<addr>][:<port>] only show streams with this backend destination address\n"
"Without any argument, all streams are dumped in a shorter format.");
return cli_err(appctx, trash.area);
}
@ -3888,6 +3906,73 @@ static int cli_parse_show_sess(char **args, char *payload, struct appctx *appctx
ctx->filter = fe;
cur_arg++;
}
else if (*args[cur_arg] &&
(strcmp(args[cur_arg], "fsrc") == 0 ||
strcmp(args[cur_arg], "fdst") == 0 ||
strcmp(args[cur_arg], "bsrc") == 0 ||
strcmp(args[cur_arg], "bdst") == 0)) {
char *err = NULL;
unsigned int sk_null_flag = 0;
struct sockaddr_storage *sk, *ctx_sk;
const char *type = args[cur_arg][0] == 'f' ? "frontend" : "backend";
const char *dir = args[cur_arg][1] == 's' ? "source" : "destination";
if (!*args[cur_arg + 1]) {
chunk_printf(&trash, "Missing %s %s address and/or port\n", type, dir);
return cli_err(appctx, trash.area);
}
/* Only one address by direction */
if (*dir == 's' &&
(ctx->flags & (CLI_SHOWSESS_F_FSRC|CLI_SHOWSESS_F_BSRC))) {
return cli_err(appctx, "Only one of backend or frontend "
"source address may be filtered.\n");
}
else if (*dir == 'd' &&
(ctx->flags & (CLI_SHOWSESS_F_FDST|CLI_SHOWSESS_F_BDST))) {
return cli_err(appctx, "Only one of backend or frontend "
"destination address may be filtered.\n");
}
sk = str2sa_range(args[cur_arg + 1], NULL, NULL, NULL, NULL, NULL, NULL,
&err, NULL, NULL, NULL, PA_O_STREAM | PA_O_PORT_OK);
if (!sk) {
chunk_printf(&trash, "Wrong %s %s address: %s\n", type, dir, err);
free(err);
return cli_err(appctx, trash.area);
}
if ((sk->ss_family != AF_INET) && (sk->ss_family != AF_INET6)) {
chunk_printf(&trash, "Wrong %s %s address: must be an IPv4"
" or an IPv6 address\n", type, dir);
return cli_err(appctx, trash.area);
}
/* Address nullity check */
if ((sk->ss_family == AF_INET &&
!memcmp(&((struct sockaddr_in *)sk)->sin_addr,
&((struct sockaddr_in *)&sk_null)->sin_addr,
sizeof(struct in_addr))) ||
(sk->ss_family == AF_INET6 &&
!memcmp(&((struct sockaddr_in6 *)sk)->sin6_addr,
&((struct sockaddr_in6 *)&sk_null)->sin6_addr,
sizeof(struct in6_addr)))) {
sk_null_flag = *dir == 's' ?
CLI_SHOWSESS_F_NULL_SRC : CLI_SHOWSESS_F_NULL_DST;
}
/* Store the address */
ctx_sk = *dir == 's' ? &ctx->src : &ctx->dst;
*ctx_sk = *sk;
/* Store the flags */
ctx->flags |= sk_null_flag |
(args[cur_arg][0] == 'f' ?
/* frontend */
(args[cur_arg][1] == 's' ? CLI_SHOWSESS_F_FSRC : CLI_SHOWSESS_F_FDST) :
/* backend */
(args[cur_arg][1] == 's' ? CLI_SHOWSESS_F_BSRC : CLI_SHOWSESS_F_BDST));
cur_arg++;
}
else {
chunk_printf(&trash, "Unsupported option '%s', try 'help' for more info.\n", args[cur_arg]);
return cli_err(appctx, trash.area);
@ -3907,6 +3992,40 @@ static int cli_parse_show_sess(char **args, char *payload, struct appctx *appctx
return 0;
}
/* Compare <fsk> address and port only when not null with <sk> depending on
* <null_fsk> boolean which is true if <fsk> address is null.
* Note that <fsk> port and address cannot be both null.
* Return 1 if matched, 0 if not.
* Return 1 if <sk> is NULL (could not be checked => not filtered)
*/
static inline int cli_sess_sk_to_be_skipped(struct sockaddr_storage *sk,
struct sockaddr_storage *fsk,
int null_fsk)
{
int check_port = get_host_port(fsk);
/* Cannot compare. Do not skip */
if (!sk)
return 1;
/* Try first with ipcmp() as it can check the family, the address
* and the port.
*/
if (!null_fsk)
return !ipcmp(sk, fsk, check_port);
/* Null address, the port must not be null. */
BUG_ON(!check_port);
if (sk->ss_family != fsk->ss_family)
return 0;
if (check_port != get_host_port(sk))
return 0;
return 1;
}
/* This function dumps all streams' states onto the stream connector's
* read buffer. It returns 0 if the output buffer is full and it needs
* to be called again, otherwise non-zero. It proceeds in an isolated
@ -3985,6 +4104,30 @@ static int cli_io_handler_dump_sess(struct appctx *appctx)
goto next_sess;
}
if (ctx->flags & (CLI_SHOWSESS_F_FSRC|CLI_SHOWSESS_F_FDST|
CLI_SHOWSESS_F_BSRC|CLI_SHOWSESS_F_BDST)) {
conn = ctx->flags & (CLI_SHOWSESS_F_FSRC|CLI_SHOWSESS_F_FDST) ?
objt_conn(strm_orig(curr_strm)) : sc_conn(curr_strm->scb);
if (conn) {
if (ctx->flags & (CLI_SHOWSESS_F_FSRC|CLI_SHOWSESS_F_BSRC)) {
if (!conn->src && !(th_ctx->flags & TH_FL_IN_ANY_HANDLER))
conn_get_src(conn);
if (!cli_sess_sk_to_be_skipped(conn->src, &ctx->src,
ctx->flags & CLI_SHOWSESS_F_NULL_SRC))
goto next_sess;
}
if (ctx->flags & (CLI_SHOWSESS_F_FDST|CLI_SHOWSESS_F_BDST)) {
if (!conn->dst && !(th_ctx->flags & TH_FL_IN_ANY_HANDLER))
conn_get_dst(conn);
if (!cli_sess_sk_to_be_skipped(conn->dst, &ctx->dst,
ctx->flags & CLI_SHOWSESS_F_NULL_DST))
goto next_sess;
}
}
}
if (ctx->target) {
if (ctx->target != (void *)-1 && ctx->target != curr_strm)
goto next_sess;
@ -4159,6 +4302,8 @@ static void cli_release_show_sess(struct appctx *appctx)
LIST_DELETE(&ctx->bref.users);
thread_release();
}
pool_free(pool_head_show_sess_ctx, ctx);
appctx->svcctx = NULL;
}
/* Parses the "shutdown session" directive, it always returns 1 */