You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
3.9 KiB
177 lines
3.9 KiB
/*
|
|
* 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
|
|
};
|