| /* |
| * em_u32.c U32 Ematch |
| * |
| * This program is free software; you can distribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version |
| * 2 of the License, or (at your option) any later version. |
| * |
| * Authors: Thomas Graf <tgraf@suug.ch> |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <syslog.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; |
| memcpy(buf, a->data + nh_len, a->len - nh_len); |
| 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 |
| }; |