diff --git a/misc/Makefile b/misc/Makefile index 1debfb15..50dae79c 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -SSOBJ=ss.o ssfilter.tab.o +SSOBJ=ss.o ssfilter_check.o ssfilter.tab.o LNSTATOBJ=lnstat.o lnstat_util.o TARGETS=ss nstat ifstat rtacct lnstat diff --git a/misc/ss.c b/misc/ss.c index 2a71317d..71224218 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -29,6 +29,7 @@ #include #include +#include "ss_util.h" #include "utils.h" #include "rt_names.h" #include "ll_map.h" @@ -39,8 +40,6 @@ #include "cg_map.h" #include -#include -#include #include #include /* for MAX_ADDR_LEN */ #include @@ -64,24 +63,10 @@ #define AF_VSOCK PF_VSOCK #endif -#define MAGIC_SEQ 123456 #define BUF_CHUNK (1024 * 1024) /* Buffer chunk allocation size */ #define BUF_CHUNKS_MAX 5 /* Maximum number of allocated buffer chunks */ #define LEN_ALIGN(x) (((x) + 1) & ~1) -#define DIAG_REQUEST(_req, _r) \ - struct { \ - struct nlmsghdr nlh; \ - _r; \ - } _req = { \ - .nlh = { \ - .nlmsg_type = SOCK_DIAG_BY_FAMILY, \ - .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,\ - .nlmsg_seq = MAGIC_SEQ, \ - .nlmsg_len = sizeof(_req), \ - }, \ - } - #if HAVE_SELINUX #include #else diff --git a/misc/ss_util.h b/misc/ss_util.h new file mode 100644 index 00000000..f7e40bb9 --- /dev/null +++ b/misc/ss_util.h @@ -0,0 +1,22 @@ +#ifndef __SS_UTIL_H__ +#define __SS_UTIL_H__ + +#include +#include + +#define MAGIC_SEQ 123456 + +#define DIAG_REQUEST(_req, _r) \ + struct { \ + struct nlmsghdr nlh; \ + _r; \ + } _req = { \ + .nlh = { \ + .nlmsg_type = SOCK_DIAG_BY_FAMILY, \ + .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,\ + .nlmsg_seq = MAGIC_SEQ, \ + .nlmsg_len = sizeof(_req), \ + }, \ + } + +#endif /* __SS_UTIL_H__ */ diff --git a/misc/ssfilter.h b/misc/ssfilter.h index d85c084e..0be3b1e0 100644 --- a/misc/ssfilter.h +++ b/misc/ssfilter.h @@ -1,20 +1,24 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#define SSF_DCOND 0 -#define SSF_SCOND 1 -#define SSF_OR 2 -#define SSF_AND 3 -#define SSF_NOT 4 -#define SSF_D_GE 5 -#define SSF_D_LE 6 -#define SSF_S_GE 7 -#define SSF_S_LE 8 -#define SSF_S_AUTO 9 -#define SSF_DEVCOND 10 -#define SSF_MARKMASK 11 -#define SSF_CGROUPCOND 12 - #include +enum { + SSF_DCOND, + SSF_SCOND, + SSF_OR, + SSF_AND, + SSF_NOT, + SSF_D_GE, + SSF_D_LE, + SSF_S_GE, + SSF_S_LE, + SSF_S_AUTO, + SSF_DEVCOND, + SSF_MARKMASK, + SSF_CGROUPCOND, + SSF__MAX +}; + +bool ssfilter_is_supported(int type); + struct ssfilter { int type; diff --git a/misc/ssfilter.y b/misc/ssfilter.y index b4175795..8e16b446 100644 --- a/misc/ssfilter.y +++ b/misc/ssfilter.y @@ -12,7 +12,14 @@ typedef struct ssfilter * ssfilter_t; static struct ssfilter * alloc_node(int type, void *pred) { - struct ssfilter *n = malloc(sizeof(*n)); + struct ssfilter *n; + + if (!ssfilter_is_supported(type)) { + fprintf(stderr, "It looks like such filter is not supported! Too old kernel?\n"); + exit(-1); + } + + n = malloc(sizeof(*n)); if (n == NULL) abort(); n->type = type; diff --git a/misc/ssfilter_check.c b/misc/ssfilter_check.c new file mode 100644 index 00000000..38c960c1 --- /dev/null +++ b/misc/ssfilter_check.c @@ -0,0 +1,103 @@ +#include +#include +#include + +#include "libnetlink.h" +#include "ssfilter.h" +#include "ss_util.h" + +static int dummy_filter(struct nlmsghdr *n, void *arg) +{ + /* just stops rtnl_dump_filter() */ + return -1; +} + +static bool cgroup_filter_check(void) +{ + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + DIAG_REQUEST(req, struct inet_diag_req_v2 r); + struct instr { + struct inet_diag_bc_op op; + __u64 cgroup_id; + } __attribute__((packed)); + int inslen = sizeof(struct instr); + struct instr instr = { + { INET_DIAG_BC_CGROUP_COND, inslen, inslen + 4 }, + 0 + }; + struct rtnl_handle rth; + struct iovec iov[3]; + struct msghdr msg; + struct rtattr rta; + int ret = false; + int iovlen = 3; + + if (rtnl_open_byproto(&rth, 0, NETLINK_SOCK_DIAG)) + return false; + rth.dump = MAGIC_SEQ; + rth.flags = RTNL_HANDLE_F_SUPPRESS_NLERR; + + memset(&req.r, 0, sizeof(req.r)); + req.r.sdiag_family = AF_INET; + req.r.sdiag_protocol = IPPROTO_TCP; + req.nlh.nlmsg_len += RTA_LENGTH(inslen); + + rta.rta_type = INET_DIAG_REQ_BYTECODE; + rta.rta_len = RTA_LENGTH(inslen); + + iov[0] = (struct iovec) { &req, sizeof(req) }; + iov[1] = (struct iovec) { &rta, sizeof(rta) }; + iov[2] = (struct iovec) { &instr, inslen }; + + msg = (struct msghdr) { + .msg_name = (void *)&nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = iovlen, + }; + + if (sendmsg(rth.fd, &msg, 0) < 0) + goto out; + + if (rtnl_dump_filter(&rth, dummy_filter, NULL) < 0) { + ret = (errno != EINVAL); + goto out; + } + + ret = true; + +out: + rtnl_close(&rth); + + return ret; +} + + +struct filter_check_t { + bool (*check)(void); + int checked:1, + supported:1; +}; + +static struct filter_check_t filter_checks[SSF__MAX] = { + [SSF_CGROUPCOND] = { cgroup_filter_check, 0 }, +}; + +bool ssfilter_is_supported(int type) +{ + struct filter_check_t f; + + if (type >= SSF__MAX) + return false; + + f = filter_checks[type]; + if (!f.check) + return true; + + if (!f.checked) { + f.supported = f.check(); + f.checked = 1; + } + + return f.supported; +}