diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:14:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:14:35 +0000 |
commit | 9b8a97db9ec4b795e29e72289005fbc58484ebeb (patch) | |
tree | e24ca2d68215e57b4759fe5c032629821eabb250 /tc/em_u32.c | |
parent | Initial commit. (diff) | |
download | iproute2-9b8a97db9ec4b795e29e72289005fbc58484ebeb.tar.xz iproute2-9b8a97db9ec4b795e29e72289005fbc58484ebeb.zip |
Adding upstream version 6.8.0.upstream/6.8.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | tc/em_u32.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/tc/em_u32.c b/tc/em_u32.c new file mode 100644 index 0000000..a83382b --- /dev/null +++ b/tc/em_u32.c @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * em_u32.c U32 Ematch + * + * Authors: Thomas Graf <tgraf@suug.ch> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <errno.h> + +#include "m_ematch.h" + +extern struct ematch_util u32_ematch_util; + +static void u32_print_usage(FILE *fd) +{ + fprintf(fd, + "Usage: u32(ALIGN VALUE MASK at [ nexthdr+ ] OFFSET)\n" \ + "where: ALIGN := { u8 | u16 | u32 }\n" \ + "\n" \ + "Example: u32(u16 0x1122 0xffff at nexthdr+4)\n"); +} + +static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, + struct bstr *args) +{ + struct bstr *a; + int align, nh_len; + unsigned long key, mask, offmask = 0, offset; + struct tc_u32_key u_key = {}; + +#define PARSE_ERR(CARG, FMT, ARGS...) \ + em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT, ##ARGS) + + if (args == NULL) + return PARSE_ERR(args, "u32: missing arguments"); + + if (!bstrcmp(args, "u8")) + align = 1; + else if (!bstrcmp(args, "u16")) + align = 2; + else if (!bstrcmp(args, "u32")) + align = 4; + else + return PARSE_ERR(args, "u32: invalid alignment"); + + a = bstr_next(args); + if (a == NULL) + return PARSE_ERR(a, "u32: missing key"); + + key = bstrtoul(a); + if (key == ULONG_MAX) + return PARSE_ERR(a, "u32: invalid key, must be numeric"); + + a = bstr_next(a); + if (a == NULL) + return PARSE_ERR(a, "u32: missing mask"); + + mask = bstrtoul(a); + if (mask == ULONG_MAX) + return PARSE_ERR(a, "u32: invalid mask, must be numeric"); + + a = bstr_next(a); + if (a == NULL || bstrcmp(a, "at") != 0) + return PARSE_ERR(a, "u32: missing \"at\""); + + a = bstr_next(a); + if (a == NULL) + return PARSE_ERR(a, "u32: missing offset"); + + nh_len = strlen("nexthdr+"); + if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) { + char buf[a->len - nh_len + 1]; + + offmask = -1; + strncpy(buf, a->data + nh_len, a->len - nh_len + 1); + offset = strtoul(buf, NULL, 0); + } else if (!bstrcmp(a, "nexthdr+")) { + a = bstr_next(a); + if (a == NULL) + return PARSE_ERR(a, "u32: missing offset"); + offset = bstrtoul(a); + } else + offset = bstrtoul(a); + + if (offset == ULONG_MAX) + return PARSE_ERR(a, "u32: invalid offset"); + + if (a->next) + return PARSE_ERR(a->next, "u32: unexpected trailer"); + + switch (align) { + case 1: + if (key > 0xFF) + return PARSE_ERR(a, "Illegal key (>0xFF)"); + if (mask > 0xFF) + return PARSE_ERR(a, "Illegal mask (>0xFF)"); + + key <<= 24 - ((offset & 3) * 8); + mask <<= 24 - ((offset & 3) * 8); + offset &= ~3; + break; + + case 2: + if (key > 0xFFFF) + return PARSE_ERR(a, "Illegal key (>0xFFFF)"); + if (mask > 0xFFFF) + return PARSE_ERR(a, "Illegal mask (>0xFFFF)"); + + if ((offset & 3) == 0) { + key <<= 16; + mask <<= 16; + } + offset &= ~3; + break; + } + + key = htonl(key); + mask = htonl(mask); + + if (offset % 4) + return PARSE_ERR(a, "u32: invalid offset alignment, " \ + "must be aligned to 4."); + + key &= mask; + + u_key.mask = mask; + u_key.val = key; + u_key.off = offset; + u_key.offmask = offmask; + + addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); + addraw_l(n, MAX_MSG, &u_key, sizeof(u_key)); + +#undef PARSE_ERR + return 0; +} + +static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, + int data_len) +{ + struct tc_u32_key *u_key = data; + + if (data_len < sizeof(*u_key)) { + fprintf(stderr, "U32 header size mismatch\n"); + return -1; + } + + fprintf(fd, "%08x/%08x at %s%d", + (unsigned int) ntohl(u_key->val), + (unsigned int) ntohl(u_key->mask), + u_key->offmask ? "nexthdr+" : "", + u_key->off); + + return 0; +} + +struct ematch_util u32_ematch_util = { + .kind = "u32", + .kind_num = TCF_EM_U32, + .parse_eopt = u32_parse_eopt, + .print_eopt = u32_print_eopt, + .print_usage = u32_print_usage +}; |