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.
835 lines
18 KiB
835 lines
18 KiB
/*
|
|
* lib/route/cls/u32.c u32 classifier
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation version 2.1
|
|
* of the License.
|
|
*
|
|
* Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
|
|
* Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
|
|
* Copyright (c) 2005-2006 Siemens AG Oesterreich
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cls
|
|
* @defgroup cls_u32 Universal 32-bit Classifier
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
#include <netlink-private/netlink.h>
|
|
#include <netlink-private/tc.h>
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/attr.h>
|
|
#include <netlink/utils.h>
|
|
#include <netlink-private/route/tc-api.h>
|
|
#include <netlink/route/classifier.h>
|
|
#include <netlink/route/cls/u32.h>
|
|
#include <netlink/route/action.h>
|
|
|
|
/** @cond SKIP */
|
|
#define U32_ATTR_DIVISOR 0x001
|
|
#define U32_ATTR_HASH 0x002
|
|
#define U32_ATTR_CLASSID 0x004
|
|
#define U32_ATTR_LINK 0x008
|
|
#define U32_ATTR_PCNT 0x010
|
|
#define U32_ATTR_SELECTOR 0x020
|
|
#define U32_ATTR_ACTION 0x040
|
|
#define U32_ATTR_POLICE 0x080
|
|
#define U32_ATTR_INDEV 0x100
|
|
#define U32_ATTR_MARK 0x200
|
|
/** @endcond */
|
|
|
|
static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
|
|
{
|
|
return (struct tc_u32_sel *) u->cu_selector->d_data;
|
|
}
|
|
|
|
static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u)
|
|
{
|
|
if (!u->cu_selector)
|
|
u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel));
|
|
|
|
return u32_selector(u);
|
|
}
|
|
|
|
static inline struct tc_u32_mark *u32_mark_alloc(struct rtnl_u32 *u)
|
|
{
|
|
if (!u->cu_mark)
|
|
u->cu_mark = nl_data_alloc(NULL, sizeof(struct tc_u32_mark));
|
|
|
|
return (struct tc_u32_mark *) u->cu_mark->d_data;
|
|
}
|
|
|
|
static struct nla_policy u32_policy[TCA_U32_MAX+1] = {
|
|
[TCA_U32_DIVISOR] = { .type = NLA_U32 },
|
|
[TCA_U32_HASH] = { .type = NLA_U32 },
|
|
[TCA_U32_CLASSID] = { .type = NLA_U32 },
|
|
[TCA_U32_LINK] = { .type = NLA_U32 },
|
|
[TCA_U32_INDEV] = { .type = NLA_STRING,
|
|
.maxlen = IFNAMSIZ },
|
|
[TCA_U32_SEL] = { .minlen = sizeof(struct tc_u32_sel) },
|
|
[TCA_U32_PCNT] = { .minlen = sizeof(struct tc_u32_pcnt) },
|
|
[TCA_U32_MARK] = { .minlen = sizeof(struct tc_u32_mark) }
|
|
};
|
|
|
|
static int u32_msg_parser(struct rtnl_tc *tc, void *data)
|
|
{
|
|
struct rtnl_u32 *u = data;
|
|
struct nlattr *tb[TCA_U32_MAX + 1];
|
|
int err;
|
|
|
|
err = tca_parse(tb, TCA_U32_MAX, tc, u32_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (tb[TCA_U32_DIVISOR]) {
|
|
u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
|
|
u->cu_mask |= U32_ATTR_DIVISOR;
|
|
}
|
|
|
|
if (tb[TCA_U32_SEL]) {
|
|
u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]);
|
|
if (!u->cu_selector)
|
|
goto errout_nomem;
|
|
u->cu_mask |= U32_ATTR_SELECTOR;
|
|
}
|
|
|
|
if (tb[TCA_U32_MARK]) {
|
|
u->cu_mark = nl_data_alloc_attr(tb[TCA_U32_MARK]);
|
|
if (!u->cu_mark)
|
|
goto errout_nomem;
|
|
u->cu_mask |= U32_ATTR_MARK;
|
|
}
|
|
|
|
if (tb[TCA_U32_HASH]) {
|
|
u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]);
|
|
u->cu_mask |= U32_ATTR_HASH;
|
|
}
|
|
|
|
if (tb[TCA_U32_CLASSID]) {
|
|
u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]);
|
|
u->cu_mask |= U32_ATTR_CLASSID;
|
|
}
|
|
|
|
if (tb[TCA_U32_LINK]) {
|
|
u->cu_link = nla_get_u32(tb[TCA_U32_LINK]);
|
|
u->cu_mask |= U32_ATTR_LINK;
|
|
}
|
|
|
|
if (tb[TCA_U32_ACT]) {
|
|
u->cu_mask |= U32_ATTR_ACTION;
|
|
err = rtnl_act_parse(&u->cu_act, tb[TCA_U32_ACT]);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (tb[TCA_U32_POLICE]) {
|
|
u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]);
|
|
if (!u->cu_police)
|
|
goto errout_nomem;
|
|
u->cu_mask |= U32_ATTR_POLICE;
|
|
}
|
|
|
|
if (tb[TCA_U32_PCNT]) {
|
|
struct tc_u32_sel *sel;
|
|
size_t pcnt_size;
|
|
|
|
if (!tb[TCA_U32_SEL]) {
|
|
err = -NLE_MISSING_ATTR;
|
|
goto errout;
|
|
}
|
|
|
|
sel = u->cu_selector->d_data;
|
|
pcnt_size = sizeof(struct tc_u32_pcnt) +
|
|
(sel->nkeys * sizeof(uint64_t));
|
|
if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) {
|
|
err = -NLE_INVAL;
|
|
goto errout;
|
|
}
|
|
|
|
u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]);
|
|
if (!u->cu_pcnt)
|
|
goto errout_nomem;
|
|
u->cu_mask |= U32_ATTR_PCNT;
|
|
}
|
|
|
|
if (tb[TCA_U32_INDEV]) {
|
|
nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ);
|
|
u->cu_mask |= U32_ATTR_INDEV;
|
|
}
|
|
|
|
return 0;
|
|
|
|
errout_nomem:
|
|
err = -NLE_NOMEM;
|
|
errout:
|
|
return err;
|
|
}
|
|
|
|
static void u32_free_data(struct rtnl_tc *tc, void *data)
|
|
{
|
|
struct rtnl_u32 *u = data;
|
|
|
|
if (u->cu_act)
|
|
rtnl_act_put_all(&u->cu_act);
|
|
nl_data_free(u->cu_mark);
|
|
nl_data_free(u->cu_selector);
|
|
nl_data_free(u->cu_police);
|
|
nl_data_free(u->cu_pcnt);
|
|
}
|
|
|
|
static int u32_clone(void *_dst, void *_src)
|
|
{
|
|
struct rtnl_u32 *dst = _dst, *src = _src;
|
|
|
|
if (src->cu_selector &&
|
|
!(dst->cu_selector = nl_data_clone(src->cu_selector)))
|
|
return -NLE_NOMEM;
|
|
|
|
if (src->cu_mark &&
|
|
!(dst->cu_mark = nl_data_clone(src->cu_mark)))
|
|
return -NLE_NOMEM;
|
|
|
|
if (src->cu_act) {
|
|
if (!(dst->cu_act = rtnl_act_alloc()))
|
|
return -NLE_NOMEM;
|
|
|
|
memcpy(dst->cu_act, src->cu_act, sizeof(struct rtnl_act));
|
|
}
|
|
|
|
if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police)))
|
|
return -NLE_NOMEM;
|
|
|
|
if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
|
|
return -NLE_NOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void u32_dump_line(struct rtnl_tc *tc, void *data,
|
|
struct nl_dump_params *p)
|
|
{
|
|
struct rtnl_u32 *u = data;
|
|
char buf[32];
|
|
|
|
if (!u)
|
|
return;
|
|
|
|
if (u->cu_mask & U32_ATTR_DIVISOR)
|
|
nl_dump(p, " divisor %u", u->cu_divisor);
|
|
else if (u->cu_mask & U32_ATTR_CLASSID)
|
|
nl_dump(p, " target %s",
|
|
rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf)));
|
|
}
|
|
|
|
static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
|
|
struct rtnl_u32 *u)
|
|
{
|
|
int i;
|
|
struct tc_u32_key *key;
|
|
|
|
if (sel->hmask || sel->hoff) {
|
|
/* I guess this will never be used since the kernel only
|
|
* exports the selector if no divisor is set but hash offset
|
|
* and hash mask make only sense in hash filters with divisor
|
|
* set */
|
|
nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
|
|
}
|
|
|
|
if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
|
|
nl_dump(p, " offset at %u", sel->off);
|
|
|
|
if (sel->flags & TC_U32_VAROFFSET)
|
|
nl_dump(p, " variable (at %u & 0x%x) >> %u",
|
|
sel->offoff, ntohs(sel->offmask), sel->offshift);
|
|
}
|
|
|
|
if (sel->flags) {
|
|
int flags = sel->flags;
|
|
nl_dump(p, " <");
|
|
|
|
#define PRINT_FLAG(f) if (flags & TC_U32_##f) { \
|
|
flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
|
|
|
|
PRINT_FLAG(TERMINAL);
|
|
PRINT_FLAG(OFFSET);
|
|
PRINT_FLAG(VAROFFSET);
|
|
PRINT_FLAG(EAT);
|
|
#undef PRINT_FLAG
|
|
|
|
nl_dump(p, ">");
|
|
}
|
|
|
|
|
|
for (i = 0; i < sel->nkeys; i++) {
|
|
key = &sel->keys[i];
|
|
|
|
nl_dump(p, "\n");
|
|
nl_dump_line(p, " match key at %s%u ",
|
|
key->offmask ? "nexthdr+" : "", key->off);
|
|
|
|
if (key->offmask)
|
|
nl_dump(p, "[0x%u] ", key->offmask);
|
|
|
|
nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
|
|
|
|
if (p->dp_type == NL_DUMP_STATS &&
|
|
(u->cu_mask & U32_ATTR_PCNT)) {
|
|
struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data;
|
|
nl_dump(p, " successful %" PRIu64, pcnt->kcnts[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void u32_dump_details(struct rtnl_tc *tc, void *data,
|
|
struct nl_dump_params *p)
|
|
{
|
|
struct rtnl_u32 *u = data;
|
|
struct tc_u32_sel *s = NULL;
|
|
struct tc_u32_mark *m;
|
|
|
|
if (!u)
|
|
return;
|
|
|
|
if (!(u->cu_mask & (U32_ATTR_SELECTOR & U32_ATTR_MARK))) {
|
|
nl_dump(p, "no-selector no-mark\n");
|
|
return;
|
|
}
|
|
|
|
if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
|
|
nl_dump(p, "no-selector");
|
|
} else {
|
|
s = u->cu_selector->d_data;
|
|
nl_dump(p, "nkeys %u", s->nkeys);
|
|
}
|
|
|
|
if (!(u->cu_mask & U32_ATTR_MARK)) {
|
|
nl_dump(p, " no-mark");
|
|
} else {
|
|
m = u->cu_mark->d_data;
|
|
nl_dump(p, " mark 0x%u 0x%u", m->val, m->mask);
|
|
}
|
|
|
|
if (u->cu_mask & U32_ATTR_HASH)
|
|
nl_dump(p, " ht key 0x%x hash 0x%u",
|
|
TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash));
|
|
|
|
if (u->cu_mask & U32_ATTR_LINK)
|
|
nl_dump(p, " link %u", u->cu_link);
|
|
|
|
if (u->cu_mask & U32_ATTR_INDEV)
|
|
nl_dump(p, " indev %s", u->cu_indev);
|
|
|
|
if (u->cu_mask & U32_ATTR_SELECTOR)
|
|
print_selector(p, s, u);
|
|
|
|
nl_dump(p, "\n");
|
|
}
|
|
|
|
static void u32_dump_stats(struct rtnl_tc *tc, void *data,
|
|
struct nl_dump_params *p)
|
|
{
|
|
struct rtnl_u32 *u = data;
|
|
|
|
if (!u)
|
|
return;
|
|
|
|
if (u->cu_mask & U32_ATTR_PCNT) {
|
|
struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
|
|
nl_dump(p, "\n");
|
|
nl_dump_line(p, " hit %8" PRIu64 " count %8" PRIu64 "\n",
|
|
pc->rhit, pc->rcnt);
|
|
}
|
|
}
|
|
|
|
static int u32_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
|
|
{
|
|
struct rtnl_u32 *u = data;
|
|
|
|
if (!u)
|
|
return 0;
|
|
|
|
if (u->cu_mask & U32_ATTR_DIVISOR)
|
|
NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
|
|
|
|
if (u->cu_mask & U32_ATTR_HASH)
|
|
NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash);
|
|
|
|
if (u->cu_mask & U32_ATTR_CLASSID)
|
|
NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid);
|
|
|
|
if (u->cu_mask & U32_ATTR_LINK)
|
|
NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link);
|
|
|
|
if (u->cu_mask & U32_ATTR_SELECTOR)
|
|
NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector);
|
|
|
|
if (u->cu_mask & U32_ATTR_MARK)
|
|
NLA_PUT_DATA(msg, TCA_U32_MARK, u->cu_mark);
|
|
|
|
if (u->cu_mask & U32_ATTR_ACTION) {
|
|
int err;
|
|
|
|
err = rtnl_act_fill(msg, TCA_U32_ACT, u->cu_act);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (u->cu_mask & U32_ATTR_POLICE)
|
|
NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police);
|
|
|
|
if (u->cu_mask & U32_ATTR_INDEV)
|
|
NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev);
|
|
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
return -NLE_NOMEM;
|
|
}
|
|
|
|
/**
|
|
* @name Attribute Modifications
|
|
* @{
|
|
*/
|
|
|
|
void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
|
|
int nodeid)
|
|
{
|
|
uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
|
|
|
|
rtnl_tc_set_handle((struct rtnl_tc *) cls, handle );
|
|
}
|
|
|
|
int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
u->cu_classid = classid;
|
|
u->cu_mask |= U32_ATTR_CLASSID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_get_classid(struct rtnl_cls *cls, uint32_t *classid)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = rtnl_tc_data_peek(TC_CAST(cls))))
|
|
return -NLE_INVAL;
|
|
|
|
if (!(u->cu_mask & U32_ATTR_CLASSID))
|
|
return -NLE_INVAL;
|
|
|
|
*classid = u->cu_classid;
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_set_divisor(struct rtnl_cls *cls, uint32_t divisor)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
u->cu_divisor = divisor;
|
|
u->cu_mask |= U32_ATTR_DIVISOR;
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_set_link(struct rtnl_cls *cls, uint32_t link)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
u->cu_link = link;
|
|
u->cu_mask |= U32_ATTR_LINK;
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_set_hashtable(struct rtnl_cls *cls, uint32_t ht)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
u->cu_hash = ht;
|
|
u->cu_mask |= U32_ATTR_HASH;
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_set_hashmask(struct rtnl_cls *cls, uint32_t hashmask, uint32_t offset)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
struct tc_u32_sel *sel;
|
|
|
|
hashmask = htonl(hashmask);
|
|
|
|
if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
sel = u32_selector_alloc(u);
|
|
if (!sel)
|
|
return -NLE_NOMEM;
|
|
|
|
sel->hmask = hashmask;
|
|
sel->hoff = offset;
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_set_selector(struct rtnl_cls *cls, int offoff, uint32_t offmask, char offshift, uint16_t off, char flags)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
struct tc_u32_sel *sel;
|
|
|
|
offmask = ntohs(offmask);
|
|
|
|
if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
sel = u32_selector_alloc(u);
|
|
if (!sel)
|
|
return -NLE_NOMEM;
|
|
|
|
sel->offoff = offoff;
|
|
sel->offmask = offmask;
|
|
sel->offshift = offshift;
|
|
sel->flags |= TC_U32_VAROFFSET;
|
|
sel->off = off;
|
|
sel->flags |= flags;
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
struct tc_u32_sel *sel;
|
|
|
|
if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
sel = u32_selector_alloc(u);
|
|
if (!sel)
|
|
return -NLE_NOMEM;
|
|
|
|
sel->flags |= TC_U32_TERMINAL;
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_add_action(struct rtnl_cls *cls, struct rtnl_act *act)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
int err;
|
|
|
|
if (!act)
|
|
return 0;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
u->cu_mask |= U32_ATTR_ACTION;
|
|
if ((err = rtnl_act_append(&u->cu_act, act)))
|
|
return err;
|
|
|
|
/* In case user frees it */
|
|
rtnl_act_get(act);
|
|
return 0;
|
|
}
|
|
|
|
struct rtnl_act* rtnl_u32_get_action(struct rtnl_cls *cls)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = rtnl_tc_data_peek(TC_CAST(cls))))
|
|
return NULL;
|
|
|
|
if (!(u->cu_mask & U32_ATTR_ACTION))
|
|
return NULL;
|
|
|
|
return u->cu_act;
|
|
}
|
|
|
|
int rtnl_u32_del_action(struct rtnl_cls *cls, struct rtnl_act *act)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
int ret;
|
|
|
|
if (!act)
|
|
return 0;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
if (!(u->cu_mask & U32_ATTR_ACTION))
|
|
return -NLE_INVAL;
|
|
|
|
ret = rtnl_act_remove(&u->cu_act, act);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!u->cu_act)
|
|
u->cu_mask &= ~U32_ATTR_ACTION;
|
|
rtnl_act_put(act);
|
|
return 0;
|
|
}
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Selector Modifications
|
|
* @{
|
|
*/
|
|
|
|
int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
|
|
{
|
|
struct tc_u32_sel *sel;
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
sel = u32_selector_alloc(u);
|
|
if (!sel)
|
|
return -NLE_NOMEM;
|
|
|
|
sel->flags |= flags;
|
|
u->cu_mask |= U32_ATTR_SELECTOR;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Append new 32-bit key to the selector
|
|
*
|
|
* @arg cls classifier to be modifier
|
|
* @arg val value to be matched (network byte-order)
|
|
* @arg mask mask to be applied before matching (network byte-order)
|
|
* @arg off offset, in bytes, to start matching
|
|
* @arg offmask offset mask
|
|
*
|
|
* General selectors define the pattern, mask and offset the pattern will be
|
|
* matched to the packet contents. Using the general selectors you can match
|
|
* virtually any single bit in the IP (or upper layer) header.
|
|
*
|
|
*/
|
|
int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
|
|
int off, int offmask)
|
|
{
|
|
struct tc_u32_sel *sel;
|
|
struct rtnl_u32 *u;
|
|
int err;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
sel = u32_selector_alloc(u);
|
|
if (!sel)
|
|
return -NLE_NOMEM;
|
|
|
|
if (sel->nkeys == UCHAR_MAX)
|
|
return -NLE_NOMEM;
|
|
|
|
err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
/* the selector might have been moved by realloc */
|
|
sel = u32_selector(u);
|
|
|
|
sel->keys[sel->nkeys].mask = mask;
|
|
sel->keys[sel->nkeys].val = val & mask;
|
|
sel->keys[sel->nkeys].off = off;
|
|
sel->keys[sel->nkeys].offmask = offmask;
|
|
sel->nkeys++;
|
|
u->cu_mask |= U32_ATTR_SELECTOR;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_add_mark(struct rtnl_cls *cls, uint32_t val, uint32_t mask)
|
|
{
|
|
struct tc_u32_mark *mark;
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
mark = u32_mark_alloc(u);
|
|
if (!mark)
|
|
return -NLE_NOMEM;
|
|
|
|
mark->mask = mask;
|
|
mark->val = val;
|
|
|
|
u->cu_mask |= U32_ATTR_MARK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rtnl_u32_del_mark(struct rtnl_cls *cls)
|
|
{
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
if (!(u->cu_mask))
|
|
return -NLE_INVAL;
|
|
|
|
if (!(u->cu_mask & U32_ATTR_MARK))
|
|
return -NLE_INVAL;
|
|
|
|
nl_data_free(u->cu_mark);
|
|
u->cu_mark = NULL;
|
|
u->cu_mask &= ~U32_ATTR_MARK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get the 32-bit key from the selector
|
|
*
|
|
* @arg cls classifier to be retrieve
|
|
* @arg index the index of the array of keys, start with 0
|
|
* @arg val pointer to store value after masked (network byte-order)
|
|
* @arg mask pointer to store the mask (network byte-order)
|
|
* @arg off pointer to store the offset
|
|
* @arg offmask pointer to store offset mask
|
|
*
|
|
*/
|
|
int rtnl_u32_get_key(struct rtnl_cls *cls, uint8_t index,
|
|
uint32_t *val, uint32_t *mask, int *off, int *offmask)
|
|
{
|
|
struct tc_u32_sel *sel;
|
|
struct rtnl_u32 *u;
|
|
|
|
if (!(u = rtnl_tc_data(TC_CAST(cls))))
|
|
return -NLE_NOMEM;
|
|
|
|
if (!(u->cu_mask & U32_ATTR_SELECTOR))
|
|
return -NLE_INVAL;
|
|
|
|
sel = u32_selector(u);
|
|
if (index >= sel->nkeys)
|
|
return -NLE_RANGE;
|
|
|
|
*mask = sel->keys[index].mask;
|
|
*val = sel->keys[index].val;
|
|
*off = sel->keys[index].off;
|
|
*offmask = sel->keys[index].offmask;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask,
|
|
int off, int offmask)
|
|
{
|
|
int shift = 24 - 8 * (off & 3);
|
|
|
|
return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
|
|
htonl((uint32_t)mask << shift),
|
|
off & ~3, offmask);
|
|
}
|
|
|
|
/**
|
|
* Append new selector key to match a 16-bit number
|
|
*
|
|
* @arg cls classifier to be modified
|
|
* @arg val value to be matched (host byte-order)
|
|
* @arg mask mask to be applied before matching (host byte-order)
|
|
* @arg off offset, in bytes, to start matching
|
|
* @arg offmask offset mask
|
|
*/
|
|
int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask,
|
|
int off, int offmask)
|
|
{
|
|
int shift = ((off & 3) == 0 ? 16 : 0);
|
|
if (off % 2)
|
|
return -NLE_INVAL;
|
|
|
|
return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
|
|
htonl((uint32_t)mask << shift),
|
|
off & ~3, offmask);
|
|
}
|
|
|
|
/**
|
|
* Append new selector key to match a 32-bit number
|
|
*
|
|
* @arg cls classifier to be modified
|
|
* @arg val value to be matched (host byte-order)
|
|
* @arg mask mask to be applied before matching (host byte-order)
|
|
* @arg off offset, in bytes, to start matching
|
|
* @arg offmask offset mask
|
|
*/
|
|
int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
|
|
int off, int offmask)
|
|
{
|
|
return rtnl_u32_add_key(cls, htonl(val), htonl(mask),
|
|
off & ~3, offmask);
|
|
}
|
|
|
|
int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, const struct in_addr *addr,
|
|
uint8_t bitmask, int off, int offmask)
|
|
{
|
|
uint32_t mask = 0xFFFFFFFF << (32 - bitmask);
|
|
return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask);
|
|
}
|
|
|
|
int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, const struct in6_addr *addr,
|
|
uint8_t bitmask, int off, int offmask)
|
|
{
|
|
int i, err;
|
|
|
|
for (i = 1; i <= 4; i++) {
|
|
if (32 * i - bitmask <= 0) {
|
|
if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
|
|
0xFFFFFFFF, off+4*(i-1), offmask)) < 0)
|
|
return err;
|
|
}
|
|
else if (32 * i - bitmask < 32) {
|
|
uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask);
|
|
if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
|
|
htonl(mask), off+4*(i-1), offmask)) < 0)
|
|
return err;
|
|
}
|
|
/* otherwise, if (32*i - bitmask >= 32) no key is generated */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
static struct rtnl_tc_ops u32_ops = {
|
|
.to_kind = "u32",
|
|
.to_type = RTNL_TC_TYPE_CLS,
|
|
.to_size = sizeof(struct rtnl_u32),
|
|
.to_msg_parser = u32_msg_parser,
|
|
.to_free_data = u32_free_data,
|
|
.to_clone = u32_clone,
|
|
.to_msg_fill = u32_msg_fill,
|
|
.to_dump = {
|
|
[NL_DUMP_LINE] = u32_dump_line,
|
|
[NL_DUMP_DETAILS] = u32_dump_details,
|
|
[NL_DUMP_STATS] = u32_dump_stats,
|
|
},
|
|
};
|
|
|
|
static void __init u32_init(void)
|
|
{
|
|
rtnl_tc_register(&u32_ops);
|
|
}
|
|
|
|
static void __exit u32_exit(void)
|
|
{
|
|
rtnl_tc_unregister(&u32_ops);
|
|
}
|
|
|
|
/** @} */
|