ipfw: add support radix tables and table lookup for MAC addresses

By analogy with IP address matching, add a way to use ipfw radix
tables for MAC matching. This is implemented using new ipfw table
with mac:radix type. Also there are src-mac and dst-mac lookup
commands added.

Usage example:
  ipfw table 1 create type mac
  ipfw table 1 add 11:22:33:44:55:66/48
  ipfw add skipto tablearg src-mac 'table(1)'
  ipfw add deny src-mac 'table(1, 100)'
  ipfw add deny lookup dst-mac 1

Note: sysctl net.link.ether.ipfw=1 should be set to enable ipfw
filtering on L2.

Reviewed by:	melifaro
Obtained from:	Yandex LLC
Relnotes:	yes
Sponsored by:	Yandex LLC
Differential Revision:	https://reviews.freebsd.org/D35103

(cherry picked from commit 81cac3906e)
This commit is contained in:
Arseny Smalyuk 2022-06-04 19:12:29 +03:00 committed by Andrey V. Elsukov
parent 8e6cfc632c
commit c31f8b7bd8
9 changed files with 736 additions and 192 deletions

View file

@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 14, 2021
.Dd June 4, 2022
.Dt IPFW 8
.Os
.Sh NAME
@ -1610,6 +1610,20 @@ Matches IPv6 packets containing any of the flow labels given in
.Ar labels .
.Ar labels
is a comma separated list of numeric flow labels.
.It Cm dst-mac Ar table Ns Pq Ar name Ns Op , Ns Ar value
Search for the destination MAC address entry in lookup table
.Ar name .
If not found, the match fails.
Otherwise, the match succeeds and
.Cm tablearg
is set to the value extracted from the table.
.It Cm src-mac Ar table Ns Pq Ar name Ns Op , Ns Ar value
Search for the source MAC address entry in lookup table
.Ar name .
If not found, the match fails.
Otherwise, the match succeeds and
.Cm tablearg
is set to the value extracted from the table.
.It Cm frag Ar spec
Matches IPv4 packets whose
.Cm ip_off
@ -1823,7 +1837,7 @@ set of parameters as specified in the rule.
One or more
of source and destination addresses and ports can be
specified.
.It Cm lookup Bro Cm dst-ip | dst-port | src-ip | src-port | uid | jail Brc Ar name
.It Cm lookup Bro Cm dst-ip | dst-port | dst-mac | src-ip | src-port | src-mac | uid | jail Brc Ar name
Search an entry in lookup table
.Ar name
that matches the field specified as argument.
@ -2133,7 +2147,7 @@ There may be up to 65535 different lookup tables.
.Pp
The following table types are supported:
.Bl -tag -width indent
.It Ar table-type : Ar addr | iface | number | flow
.It Ar table-type : Ar addr | iface | number | flow | mac
.It Ar table-key : Ar addr Ns Oo / Ns Ar masklen Oc | iface-name | number | flow-spec
.It Ar flow-spec : Ar flow-field Ns Op , Ns Ar flow-spec
.It Ar flow-field : src-ip | proto | src-port | dst-ip | dst-port
@ -2163,6 +2177,20 @@ Ranges are not supported.
Matches packet fields specified by
.Ar flow
type suboptions with table entries.
.It Cm mac
Matches MAC address.
Each entry is represented by an
.Ar addr Ns Op / Ns Ar masklen
and will match all addresses with base
.Ar addr
and mask width of
.Ar masklen
bits.
If
.Ar masklen
is not specified, it defaults to 48.
When looking up an MAC address in a table, the most specific
entry will match.
.El
.Pp
Tables require explicit creation via
@ -2266,7 +2294,7 @@ Shows generic table information and algo-specific data.
The following lookup algorithms are supported:
.Bl -tag -width indent
.It Ar algo-desc : algo-name | "algo-name algo-data"
.It Ar algo-name : Ar addr: radix | addr: hash | iface: array | number: array | flow: hash
.It Ar algo-name : Ar addr: radix | addr: hash | iface: array | number: array | flow: hash | mac: radix
.It Cm addr: radix
Separate Radix trees for IPv4 and IPv6, the same way as the routing table (see
.Xr route 4 ) .
@ -2291,6 +2319,8 @@ Array storing sorted u32 numbers.
Auto-growing hash storing flow entries.
Search calculates hash on required packet fields and searches for matching
entries in selected bucket.
.It Cm mac: radix
Radix tree for MAC address
.El
.Pp
The

View file

@ -300,12 +300,20 @@ static struct _s_x rule_action_params[] = {
/*
* The 'lookup' instruction accepts one of the following arguments.
* -1 is a terminator for the list.
* Arguments are passed as v[1] in O_DST_LOOKUP options.
*/
static int lookup_key[] = {
TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT,
TOK_UID, TOK_JAIL, TOK_DSCP, -1 };
static struct _s_x lookup_keys[] = {
{ "dst-ip", LOOKUP_DST_IP },
{ "src-ip", LOOKUP_SRC_IP },
{ "dst-port", LOOKUP_DST_PORT },
{ "src-port", LOOKUP_SRC_PORT },
{ "dst-mac", LOOKUP_DST_MAC },
{ "src-mac", LOOKUP_SRC_MAC },
{ "uid", LOOKUP_UID },
{ "jail", LOOKUP_JAIL },
{ "dscp", LOOKUP_DSCP },
{ NULL, 0 },
};
static struct _s_x rule_options[] = {
{ "tagged", TOK_TAGGED },
@ -358,6 +366,8 @@ static struct _s_x rule_options[] = {
{ "src-ip", TOK_SRCIP },
{ "dst-port", TOK_DSTPORT },
{ "src-port", TOK_SRCPORT },
{ "dst-mac", TOK_DSTMAC },
{ "src-mac", TOK_SRCMAC },
{ "proto", TOK_PROTO },
{ "MAC", TOK_MAC },
{ "mac", TOK_MAC },
@ -368,18 +378,18 @@ static struct _s_x rule_options[] = {
{ "ipsec", TOK_IPSEC },
{ "icmp6type", TOK_ICMP6TYPES },
{ "icmp6types", TOK_ICMP6TYPES },
{ "ext6hdr", TOK_EXT6HDR},
{ "flow-id", TOK_FLOWID},
{ "ipv6", TOK_IPV6},
{ "ip6", TOK_IPV6},
{ "ipv4", TOK_IPV4},
{ "ip4", TOK_IPV4},
{ "dst-ipv6", TOK_DSTIP6},
{ "dst-ip6", TOK_DSTIP6},
{ "src-ipv6", TOK_SRCIP6},
{ "src-ip6", TOK_SRCIP6},
{ "lookup", TOK_LOOKUP},
{ "flow", TOK_FLOW},
{ "ext6hdr", TOK_EXT6HDR },
{ "flow-id", TOK_FLOWID },
{ "ipv6", TOK_IPV6 },
{ "ip6", TOK_IPV6 },
{ "ipv4", TOK_IPV4 },
{ "ip4", TOK_IPV4 },
{ "dst-ipv6", TOK_DSTIP6 },
{ "dst-ip6", TOK_DSTIP6 },
{ "src-ipv6", TOK_SRCIP6 },
{ "src-ip6", TOK_SRCIP6 },
{ "lookup", TOK_LOOKUP },
{ "flow", TOK_FLOW },
{ "defer-action", TOK_SKIPACTION },
{ "defer-immediate-action", TOK_SKIPACTION },
{ "//", TOK_COMMENT },
@ -1211,11 +1221,9 @@ print_ip(struct buf_pr *bp, const struct format_opts *fo,
bprintf(bp, " ");
if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
uint32_t d = a[1];
const char *arg = "<invalid>";
const char *arg;
if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
arg = match_value(rule_options, lookup_key[d]);
arg = match_value(lookup_keys, a[1]);
t = table_search_ctlv(fo->tstate,
((const ipfw_insn *)cmd)->arg1);
bprintf(bp, "lookup %s %s", arg, t);
@ -1331,6 +1339,22 @@ print_mac(struct buf_pr *bp, const ipfw_insn_mac *mac)
format_mac(bp, mac->addr + 6, mac->mask + 6);
}
static void
print_mac_lookup(struct buf_pr *bp, const struct format_opts *fo,
const ipfw_insn *cmd)
{
uint32_t len = F_LEN(cmd);
char *t;
bprintf(bp, " ");
t = table_search_ctlv(fo->tstate, cmd->arg1);
bprintf(bp, "table(%s", t);
if (len == F_INSN_SIZE(ipfw_insn_u32))
bprintf(bp, ",%u", ((const ipfw_insn_u32 *)cmd)->d[0]);
bprintf(bp, ")");
}
static void
fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
{
@ -1518,6 +1542,14 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
bprintf(bp, " dst-ip6");
print_ip6(bp, insntod(cmd, ip6));
break;
case O_MAC_SRC_LOOKUP:
bprintf(bp, " src-mac");
print_mac_lookup(bp, fo, cmd);
break;
case O_MAC_DST_LOOKUP:
bprintf(bp, " dst-mac");
print_mac_lookup(bp, fo, cmd);
break;
case O_FLOW6ID:
print_flow6id(bp, insntod(cmd, u32));
break;
@ -2650,7 +2682,6 @@ list_static_range(struct cmdline_opts *co, struct format_opts *fo,
int n, seen;
struct ip_fw_rule *r;
struct ip_fw_bcounter *cntr;
int c = 0;
for (n = seen = 0; n < rcnt; n++,
rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
@ -2669,7 +2700,6 @@ list_static_range(struct cmdline_opts *co, struct format_opts *fo,
if (r->rulenum >= fo->first && r->rulenum <= fo->last) {
show_static_rule(co, fo, bp, r, cntr);
printf("%s", bp->buf);
c += rtlv->length;
bp_flush(bp);
seen++;
}
@ -2788,13 +2818,12 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
char *endptr;
size_t readsz;
struct buf_pr bp;
ipfw_obj_ctlv *ctlv, *tstate;
ipfw_obj_ctlv *ctlv;
ipfw_obj_tlv *rbase;
/*
* Handle tablenames TLV first, if any
*/
tstate = NULL;
rbase = NULL;
dynbase = NULL;
dynsz = 0;
@ -3682,6 +3711,29 @@ add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
return cmd;
}
static ipfw_insn *
add_srcmac(ipfw_insn *cmd, char *av, struct tidx *tstate)
{
if (strncmp(av, "table(", 6) == 0)
fill_table(cmd, av, O_MAC_SRC_LOOKUP, tstate);
else
errx(EX_DATAERR, "only mac table lookup is supported %s", av);
return cmd;
}
static ipfw_insn *
add_dstmac(ipfw_insn *cmd, char *av, struct tidx *tstate)
{
if (strncmp(av, "table(", 6) == 0)
fill_table(cmd, av, O_MAC_DST_LOOKUP, tstate);
else
errx(EX_DATAERR, "only mac table lookup is supported %s", av);
return cmd;
}
static struct _s_x f_reserved_keywords[] = {
{ "altq", TOK_OR },
{ "//", TOK_OR },
@ -4912,6 +4964,21 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
}
break;
case TOK_SRCMAC:
NEED1("missing source MAC");
if (add_srcmac(cmd, *av, tstate)) {
av++;
}
break;
case TOK_DSTMAC:
NEED1("missing destination MAC");
if (add_dstmac(cmd, *av, tstate)) {
av++;
}
break;
case TOK_SRCPORT:
NEED1("missing source port");
if (_substrcmp(*av, "any") == 0 ||
@ -5013,28 +5080,23 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
case TOK_LOOKUP: {
ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
int j;
if (!av[0] || !av[1])
errx(EX_USAGE, "format: lookup argument tablenum");
cmd->opcode = O_IP_DST_LOOKUP;
cmd->len |= F_INSN_SIZE(ipfw_insn) + 2;
i = match_token(rule_options, *av);
for (j = 0; lookup_key[j] >= 0 ; j++) {
if (i == lookup_key[j])
break;
}
if (lookup_key[j] <= 0)
i = match_token(lookup_keys, *av);
if (i == -1)
errx(EX_USAGE, "format: cannot lookup on %s", *av);
__PAST_END(c->d, 1) = j; // i converted to option
__PAST_END(c->d, 1) = i;
av++;
if ((j = pack_table(tstate, *av)) == 0)
if ((i = pack_table(tstate, *av)) == 0)
errx(EX_DATAERR, "Invalid table name: %s", *av);
cmd->arg1 = j;
cmd->arg1 = i;
av++;
}
}
break;
case TOK_FLOW:
NEED1("missing table name");

View file

@ -177,6 +177,8 @@ enum tokens {
TOK_SRCIP,
TOK_DSTPORT,
TOK_SRCPORT,
TOK_DSTMAC,
TOK_SRCMAC,
TOK_ALL,
TOK_MASK,
TOK_FLOW_MASK,

View file

@ -31,6 +31,7 @@
#include <string.h>
#include <sysexits.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip_fw.h>
@ -77,6 +78,7 @@ static int tables_foreach(table_cb_t *f, void *arg, int sort);
static struct _s_x tabletypes[] = {
{ "addr", IPFW_TABLE_ADDR },
{ "mac", IPFW_TABLE_MAC },
{ "iface", IPFW_TABLE_INTERFACE },
{ "number", IPFW_TABLE_NUMBER },
{ "flow", IPFW_TABLE_FLOW },
@ -1188,6 +1190,7 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
char *p, *pp;
int mask, af;
struct in6_addr *paddr, tmp;
struct ether_addr *mac;
struct tflow_entry *tfe;
uint32_t key, *pkey;
uint16_t port;
@ -1234,6 +1237,24 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
af = AF_INET;
}
break;
case IPFW_TABLE_MAC:
/* Remove / if exists */
if ((p = strchr(arg, '/')) != NULL) {
*p = '\0';
mask = atoi(p + 1);
}
if (p != NULL && mask > 8 * ETHER_ADDR_LEN)
errx(EX_DATAERR, "bad MAC mask width: %s",
p + 1);
if ((mac = ether_aton(arg)) == NULL)
errx(EX_DATAERR, "Incorrect MAC address");
memcpy(tentry->k.mac, mac->octet, ETHER_ADDR_LEN);
masklen = p ? mask : 8 * ETHER_ADDR_LEN;
af = AF_LINK;
break;
case IPFW_TABLE_INTERFACE:
/* Assume interface name. Copy significant data only */
mask = MIN(strlen(arg), IF_NAMESIZE - 1);
@ -1872,6 +1893,7 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
{
char tbuf[128], pval[128];
const char *comma;
const u_char *mac;
void *paddr;
struct tflow_entry *tfe;
@ -1884,6 +1906,13 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
printf("%s/%u %s\n", tbuf, tent->masklen, pval);
break;
case IPFW_TABLE_MAC:
/* MAC prefixes */
mac = tent->k.mac;
printf("%02x:%02x:%02x:%02x:%02x:%02x/%u %s\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
tent->masklen, pval);
break;
case IPFW_TABLE_INTERFACE:
/* Interface names */
printf("%s %s\n", tent->k.iface, pval);

View file

@ -295,9 +295,27 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_SKIP_ACTION, /* none */
O_TCPMSS, /* arg1=MSS value */
O_MAC_SRC_LOOKUP, /* arg1=table number, u32=value */
O_MAC_DST_LOOKUP, /* arg1=table number, u32=value */
O_LAST_OPCODE /* not an opcode! */
};
/*
* Defines key types used by lookup instruction
*/
enum ipfw_table_lookup_type {
LOOKUP_DST_IP,
LOOKUP_SRC_IP,
LOOKUP_DST_PORT,
LOOKUP_SRC_PORT,
LOOKUP_UID,
LOOKUP_JAIL,
LOOKUP_DSCP,
LOOKUP_DST_MAC,
LOOKUP_SRC_MAC,
};
/*
* The extension header are filtered only for presence using a bit
* vector with a flag for each header.
@ -754,7 +772,8 @@ struct _ipfw_dyn_rule {
#define IPFW_TABLE_INTERFACE 2 /* Table for holding interface names */
#define IPFW_TABLE_NUMBER 3 /* Table for holding ports/uid/gid/etc */
#define IPFW_TABLE_FLOW 4 /* Table for holding flow data */
#define IPFW_TABLE_MAXTYPE 4 /* Maximum valid number */
#define IPFW_TABLE_MAC 5 /* Table for holding mac address prefixes */
#define IPFW_TABLE_MAXTYPE 5 /* Maximum valid number */
#define IPFW_TABLE_CIDR IPFW_TABLE_ADDR /* compat */
@ -772,6 +791,9 @@ struct _ipfw_dyn_rule {
#define IPFW_VTYPE_NH4 0x00000200 /* IPv4 nexthop */
#define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */
/* MAC/InfiniBand/etc address length */
#define IPFW_MAX_L2_ADDR_LEN 20
typedef struct _ipfw_table_entry {
in_addr_t addr; /* network address */
u_int32_t value; /* value */
@ -895,10 +917,11 @@ typedef struct _ipfw_obj_tentry {
uint16_t spare1;
union {
/* Longest field needs to be aligned by 8-byte boundary */
struct in_addr addr; /* IPv4 address */
uint32_t key; /* uid/gid/port */
struct in6_addr addr6; /* IPv6 address */
char iface[IF_NAMESIZE]; /* interface name */
struct in_addr addr; /* IPv4 address */
uint32_t key; /* uid/gid/port */
struct in6_addr addr6; /* IPv6 address */
char iface[IF_NAMESIZE]; /* interface name */
u_char mac[IPFW_MAX_L2_ADDR_LEN]; /* MAC address */
struct tflow_entry flow;
} k;
union {

View file

@ -2034,78 +2034,87 @@ do { \
case O_IP_DST_LOOKUP:
{
void *pkey;
uint32_t vidx, key;
uint16_t keylen;
if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) {
void *pkey;
uint32_t vidx, key;
uint16_t keylen = 0; /* zero if can't match the packet */
/* Determine lookup key type */
vidx = ((ipfw_insn_u32 *)cmd)->d[1];
if (vidx != 4 /* uid */ &&
vidx != 5 /* jail */ &&
is_ipv6 == 0 && is_ipv4 == 0)
break;
/* Determine key length */
if (vidx == 0 /* dst-ip */ ||
vidx == 1 /* src-ip */)
keylen = is_ipv6 ?
sizeof(struct in6_addr):
sizeof(in_addr_t);
else {
keylen = sizeof(key);
pkey = &key;
}
if (vidx == 0 /* dst-ip */)
pkey = is_ipv4 ? (void *)&dst_ip:
(void *)&args->f_id.dst_ip6;
else if (vidx == 1 /* src-ip */)
pkey = is_ipv4 ? (void *)&src_ip:
(void *)&args->f_id.src_ip6;
else if (vidx == 6 /* dscp */) {
if (is_ipv4)
key = ip->ip_tos >> 2;
switch (vidx) {
case LOOKUP_DST_IP:
case LOOKUP_SRC_IP:
/* Need IP frame */
if (is_ipv6 == 0 && is_ipv4 == 0)
break;
if (vidx == LOOKUP_DST_IP)
pkey = is_ipv6 ?
(void *)&args->f_id.dst_ip6:
(void *)&dst_ip;
else
key = IPV6_DSCP(
(struct ip6_hdr *)ip) >> 2;
key &= 0x3f;
} else if (vidx == 2 /* dst-port */ ||
vidx == 3 /* src-port */) {
pkey = is_ipv6 ?
(void *)&args->f_id.src_ip6:
(void *)&src_ip;
keylen = is_ipv6 ?
sizeof(struct in6_addr):
sizeof(in_addr_t);
break;
case LOOKUP_DST_PORT:
case LOOKUP_SRC_PORT:
/* Need IP frame */
if (is_ipv6 == 0 && is_ipv4 == 0)
break;
/* Skip fragments */
if (offset != 0)
break;
/* Skip proto without ports */
if (proto != IPPROTO_TCP &&
proto != IPPROTO_UDP &&
proto != IPPROTO_UDPLITE &&
proto != IPPROTO_SCTP)
proto != IPPROTO_UDP &&
proto != IPPROTO_UDPLITE &&
proto != IPPROTO_SCTP)
break;
if (vidx == 2 /* dst-port */)
key = dst_port;
else
key = src_port;
}
#ifndef USERSPACE
else if (vidx == 4 /* uid */ ||
vidx == 5 /* jail */) {
key = vidx == LOOKUP_DST_PORT ?
dst_port:
src_port;
pkey = &key;
keylen = sizeof(key);
break;
case LOOKUP_UID:
case LOOKUP_JAIL:
check_uidgid(
(ipfw_insn_u32 *)cmd,
args, &ucred_lookup,
#ifdef __FreeBSD__
&ucred_cache);
if (vidx == 4 /* uid */)
key = ucred_cache->cr_uid;
else if (vidx == 5 /* jail */)
key = ucred_cache->cr_prison->pr_id;
#else /* !__FreeBSD__ */
(void *)&ucred_cache);
if (vidx == 4 /* uid */)
key = ucred_cache.uid;
else if (vidx == 5 /* jail */)
key = ucred_cache.xid;
#endif /* !__FreeBSD__ */
key = vidx == LOOKUP_UID ?
ucred_cache->cr_uid:
ucred_cache->cr_prison->pr_id;
pkey = &key;
keylen = sizeof(key);
break;
case LOOKUP_DSCP:
/* Need IP frame */
if (is_ipv6 == 0 && is_ipv4 == 0)
break;
if (is_ipv6)
key = IPV6_DSCP(
(struct ip6_hdr *)ip) >> 2;
else
key = ip->ip_tos >> 2;
pkey = &key;
keylen = sizeof(key);
break;
case LOOKUP_DST_MAC:
case LOOKUP_SRC_MAC:
/* Need ether frame */
if ((args->flags & IPFW_ARGS_ETHER) == 0)
break;
pkey = vidx == LOOKUP_DST_MAC ?
eh->ether_dhost:
eh->ether_shost;
keylen = ETHER_ADDR_LEN;
break;
}
#endif /* !USERSPACE */
else
if (keylen == 0)
break;
match = ipfw_lookup_table(chain,
cmd->arg1, keylen, pkey, &vidx);
@ -2151,6 +2160,36 @@ do { \
break;
}
case O_MAC_SRC_LOOKUP:
case O_MAC_DST_LOOKUP:
{
void *pkey;
uint32_t vidx;
uint16_t keylen = ETHER_ADDR_LEN;
/* Need ether frame */
if ((args->flags & IPFW_ARGS_ETHER) == 0)
break;
if (cmd->opcode == O_MAC_DST_LOOKUP)
pkey = eh->ether_dhost;
else
pkey = eh->ether_shost;
match = ipfw_lookup_table(chain, cmd->arg1,
keylen, pkey, &vidx);
if (!match)
break;
if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) {
match = ((ipfw_insn_u32 *)cmd)->d[0] ==
TARG_VAL(chain, vidx, tag);
if (!match)
break;
}
tablearg = vidx;
break;
}
case O_IP_FLOW_LOOKUP:
{
uint32_t v = 0;

View file

@ -1909,6 +1909,8 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
ci->object_opcodes++;
break;
case O_IP_FLOW_LOOKUP:
case O_MAC_DST_LOOKUP:
case O_MAC_SRC_LOOKUP:
if (cmd->arg1 >= V_fw_tables_max) {
printf("ipfw: invalid table number %d\n",
cmd->arg1);

View file

@ -2753,26 +2753,19 @@ classify_srcdst(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
*/
v = ((ipfw_insn_u32 *)cmd)->d[1];
switch (v) {
case 0:
case 1:
/* IPv4 src/dst */
case LOOKUP_DST_IP:
case LOOKUP_SRC_IP:
break;
case 2:
case 3:
/* src/dst port */
case LOOKUP_DST_PORT:
case LOOKUP_SRC_PORT:
case LOOKUP_UID:
case LOOKUP_JAIL:
case LOOKUP_DSCP:
*ptype = IPFW_TABLE_NUMBER;
break;
case 4:
/* uid/gid */
*ptype = IPFW_TABLE_NUMBER;
break;
case 5:
/* jid */
*ptype = IPFW_TABLE_NUMBER;
break;
case 6:
/* dscp */
*ptype = IPFW_TABLE_NUMBER;
case LOOKUP_DST_MAC:
case LOOKUP_SRC_MAC:
*ptype = IPFW_TABLE_MAC;
break;
}
}
@ -2806,6 +2799,14 @@ classify_flow(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
return (0);
}
static int
classify_mac_lookup(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
{
*puidx = cmd->arg1;
*ptype = IPFW_TABLE_MAC;
return (0);
}
static void
update_arg1(ipfw_insn *cmd, uint16_t idx)
{
@ -2956,6 +2957,26 @@ static struct opcode_obj_rewrite opcodes[] = {
.create_object = create_table_compat,
.manage_sets = table_manage_sets,
},
{
.opcode = O_MAC_SRC_LOOKUP,
.etlv = IPFW_TLV_TBL_NAME,
.classifier = classify_mac_lookup,
.update = update_arg1,
.find_byname = table_findbyname,
.find_bykidx = table_findbykidx,
.create_object = create_table_compat,
.manage_sets = table_manage_sets,
},
{
.opcode = O_MAC_DST_LOOKUP,
.etlv = IPFW_TLV_TBL_NAME,
.classifier = classify_mac_lookup,
.update = update_arg1,
.find_byname = table_findbyname,
.find_bykidx = table_findbykidx,
.create_object = create_table_compat,
.manage_sets = table_manage_sets,
},
{
.opcode = O_XMIT,
.etlv = IPFW_TLV_TBL_NAME,

View file

@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <net/ethernet.h>
#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */
#include <net/radix.h>
#include <net/route.h>
@ -315,15 +316,17 @@ static int bdel(const void *key, void *base, size_t nmemb, size_t size,
*/
#define KEY_LEN(v) *((uint8_t *)&(v))
/*
* Do not require radix to compare more than actual IPv4/IPv6 address
* Do not require radix to compare more than actual IPv4/IPv6/MAC address
*/
#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
#define KEY_LEN_INET6 (offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
#define KEY_LEN_MAC (offsetof(struct sa_mac, mac_addr) + ETHER_ADDR_LEN)
#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr))
#define OFF_LEN_INET6 (8 * offsetof(struct sa_in6, sin6_addr))
#define OFF_LEN_MAC (8 * offsetof(struct sa_mac, mac_addr))
struct radix_addr_entry {
struct addr_radix_entry {
struct radix_node rn[2];
struct sockaddr_in addr;
uint32_t value;
@ -337,20 +340,25 @@ struct sa_in6 {
struct in6_addr sin6_addr;
};
struct radix_addr_xentry {
struct addr_radix_xentry {
struct radix_node rn[2];
struct sa_in6 addr6;
uint32_t value;
uint8_t masklen;
};
struct radix_cfg {
struct addr_radix_cfg {
struct radix_node_head *head4;
struct radix_node_head *head6;
size_t count4;
size_t count6;
};
struct sa_mac {
uint8_t mac_len;
struct ether_addr mac_addr;
};
struct ta_buf_radix
{
void *ent_ptr;
@ -365,32 +373,36 @@ struct ta_buf_radix
struct sa_in6 sa;
struct sa_in6 ma;
} a6;
struct {
struct sa_mac sa;
struct sa_mac ma;
} mac;
} addr;
};
static int ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
static int ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t keylen,
uint32_t *val);
static int ta_init_radix(struct ip_fw_chain *ch, void **ta_state,
static int ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state,
struct table_info *ti, char *data, uint8_t tflags);
static int flush_radix_entry(struct radix_node *rn, void *arg);
static void ta_destroy_radix(void *ta_state, struct table_info *ti);
static void ta_dump_radix_tinfo(void *ta_state, struct table_info *ti,
static void ta_destroy_addr_radix(void *ta_state, struct table_info *ti);
static void ta_dump_addr_radix_tinfo(void *ta_state, struct table_info *ti,
ipfw_ta_tinfo *tinfo);
static int ta_dump_radix_tentry(void *ta_state, struct table_info *ti,
static int ta_dump_addr_radix_tentry(void *ta_state, struct table_info *ti,
void *e, ipfw_obj_tentry *tent);
static int ta_find_radix_tentry(void *ta_state, struct table_info *ti,
static int ta_find_addr_radix_tentry(void *ta_state, struct table_info *ti,
ipfw_obj_tentry *tent);
static void ta_foreach_radix(void *ta_state, struct table_info *ti,
static void ta_foreach_addr_radix(void *ta_state, struct table_info *ti,
ta_foreach_f *f, void *arg);
static void tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
static void tei_to_sockaddr_ent_addr(struct tentry_info *tei, struct sockaddr *sa,
struct sockaddr *ma, int *set_mask);
static int ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
static int ta_prepare_add_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf);
static int ta_add_radix(void *ta_state, struct table_info *ti,
static int ta_add_addr_radix(void *ta_state, struct table_info *ti,
struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
static int ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
static int ta_prepare_del_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf);
static int ta_del_radix(void *ta_state, struct table_info *ti,
static int ta_del_addr_radix(void *ta_state, struct table_info *ti,
struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
static void ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf);
@ -398,29 +410,29 @@ static int ta_need_modify_radix(void *ta_state, struct table_info *ti,
uint32_t count, uint64_t *pflags);
static int
ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t keylen,
uint32_t *val)
{
struct radix_node_head *rnh;
if (keylen == sizeof(in_addr_t)) {
struct radix_addr_entry *ent;
struct addr_radix_entry *ent;
struct sockaddr_in sa;
KEY_LEN(sa) = KEY_LEN_INET;
sa.sin_addr.s_addr = *((in_addr_t *)key);
rnh = (struct radix_node_head *)ti->state;
ent = (struct radix_addr_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh));
ent = (struct addr_radix_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh));
if (ent != NULL) {
*val = ent->value;
return (1);
}
} else {
struct radix_addr_xentry *xent;
} else if (keylen == sizeof(struct in6_addr)) {
struct addr_radix_xentry *xent;
struct sa_in6 sa6;
KEY_LEN(sa6) = KEY_LEN_INET6;
memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
rnh = (struct radix_node_head *)ti->xstate;
xent = (struct radix_addr_xentry *)(rnh->rnh_matchaddr(&sa6, &rnh->rh));
xent = (struct addr_radix_xentry *)(rnh->rnh_matchaddr(&sa6, &rnh->rh));
if (xent != NULL) {
*val = xent->value;
return (1);
@ -434,10 +446,10 @@ ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
* New table
*/
static int
ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
char *data, uint8_t tflags)
{
struct radix_cfg *cfg;
struct addr_radix_cfg *cfg;
if (!rn_inithead(&ti->state, OFF_LEN_INET))
return (ENOMEM);
@ -446,10 +458,10 @@ ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
return (ENOMEM);
}
cfg = malloc(sizeof(struct radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
cfg = malloc(sizeof(struct addr_radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
*ta_state = cfg;
ti->lookup = ta_lookup_radix;
ti->lookup = ta_lookup_addr_radix;
return (0);
}
@ -458,9 +470,9 @@ static int
flush_radix_entry(struct radix_node *rn, void *arg)
{
struct radix_node_head * const rnh = arg;
struct radix_addr_entry *ent;
struct addr_radix_entry *ent;
ent = (struct radix_addr_entry *)
ent = (struct addr_radix_entry *)
rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, &rnh->rh);
if (ent != NULL)
free(ent, M_IPFW_TBL);
@ -468,12 +480,12 @@ flush_radix_entry(struct radix_node *rn, void *arg)
}
static void
ta_destroy_radix(void *ta_state, struct table_info *ti)
ta_destroy_addr_radix(void *ta_state, struct table_info *ti)
{
struct radix_cfg *cfg;
struct addr_radix_cfg *cfg;
struct radix_node_head *rnh;
cfg = (struct radix_cfg *)ta_state;
cfg = (struct addr_radix_cfg *)ta_state;
rnh = (struct radix_node_head *)(ti->state);
rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);
@ -490,31 +502,31 @@ ta_destroy_radix(void *ta_state, struct table_info *ti)
* Provide algo-specific table info
*/
static void
ta_dump_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
ta_dump_addr_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
{
struct radix_cfg *cfg;
struct addr_radix_cfg *cfg;
cfg = (struct radix_cfg *)ta_state;
cfg = (struct addr_radix_cfg *)ta_state;
tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
tinfo->taclass4 = IPFW_TACLASS_RADIX;
tinfo->count4 = cfg->count4;
tinfo->itemsize4 = sizeof(struct radix_addr_entry);
tinfo->itemsize4 = sizeof(struct addr_radix_entry);
tinfo->taclass6 = IPFW_TACLASS_RADIX;
tinfo->count6 = cfg->count6;
tinfo->itemsize6 = sizeof(struct radix_addr_xentry);
tinfo->itemsize6 = sizeof(struct addr_radix_xentry);
}
static int
ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
ta_dump_addr_radix_tentry(void *ta_state, struct table_info *ti, void *e,
ipfw_obj_tentry *tent)
{
struct radix_addr_entry *n;
struct addr_radix_entry *n;
#ifdef INET6
struct radix_addr_xentry *xn;
struct addr_radix_xentry *xn;
#endif
n = (struct radix_addr_entry *)e;
n = (struct addr_radix_entry *)e;
/* Guess IPv4/IPv6 radix by sockaddr family */
if (n->addr.sin_family == AF_INET) {
@ -524,7 +536,7 @@ ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
tent->v.kidx = n->value;
#ifdef INET6
} else {
xn = (struct radix_addr_xentry *)e;
xn = (struct addr_radix_xentry *)e;
memcpy(&tent->k.addr6, &xn->addr6.sin6_addr,
sizeof(struct in6_addr));
tent->masklen = xn->masklen;
@ -537,7 +549,7 @@ ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
}
static int
ta_find_radix_tentry(void *ta_state, struct table_info *ti,
ta_find_addr_radix_tentry(void *ta_state, struct table_info *ti,
ipfw_obj_tentry *tent)
{
struct radix_node_head *rnh;
@ -550,7 +562,7 @@ ta_find_radix_tentry(void *ta_state, struct table_info *ti,
sa.sin_addr.s_addr = tent->k.addr.s_addr;
rnh = (struct radix_node_head *)ti->state;
e = rnh->rnh_matchaddr(&sa, &rnh->rh);
} else {
} else if (tent->subtype == AF_INET6) {
struct sa_in6 sa6;
KEY_LEN(sa6) = KEY_LEN_INET6;
memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));
@ -559,7 +571,7 @@ ta_find_radix_tentry(void *ta_state, struct table_info *ti,
}
if (e != NULL) {
ta_dump_radix_tentry(ta_state, ti, e, tent);
ta_dump_addr_radix_tentry(ta_state, ti, e, tent);
return (0);
}
@ -567,7 +579,7 @@ ta_find_radix_tentry(void *ta_state, struct table_info *ti,
}
static void
ta_foreach_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
ta_foreach_addr_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
void *arg)
{
struct radix_node_head *rnh;
@ -595,7 +607,7 @@ ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
#endif
static void
tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
tei_to_sockaddr_ent_addr(struct tentry_info *tei, struct sockaddr *sa,
struct sockaddr *ma, int *set_mask)
{
int mlen;
@ -647,13 +659,13 @@ tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
}
static int
ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
ta_prepare_add_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf)
{
struct ta_buf_radix *tb;
struct radix_addr_entry *ent;
struct addr_radix_entry *ent;
#ifdef INET6
struct radix_addr_xentry *xent;
struct addr_radix_xentry *xent;
#endif
struct sockaddr *addr, *mask;
int mlen, set_mask;
@ -691,7 +703,7 @@ ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
return (EINVAL);
}
tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
tei_to_sockaddr_ent_addr(tei, addr, mask, &set_mask);
/* Set pointers */
tb->addr_ptr = addr;
if (set_mask != 0)
@ -701,25 +713,25 @@ ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
}
static int
ta_add_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
ta_add_addr_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
void *ta_buf, uint32_t *pnum)
{
struct radix_cfg *cfg;
struct addr_radix_cfg *cfg;
struct radix_node_head *rnh;
struct radix_node *rn;
struct ta_buf_radix *tb;
uint32_t *old_value, value;
cfg = (struct radix_cfg *)ta_state;
cfg = (struct addr_radix_cfg *)ta_state;
tb = (struct ta_buf_radix *)ta_buf;
/* Save current entry value from @tei */
if (tei->subtype == AF_INET) {
rnh = ti->state;
((struct radix_addr_entry *)tb->ent_ptr)->value = tei->value;
((struct addr_radix_entry *)tb->ent_ptr)->value = tei->value;
} else {
rnh = ti->xstate;
((struct radix_addr_xentry *)tb->ent_ptr)->value = tei->value;
((struct addr_radix_xentry *)tb->ent_ptr)->value = tei->value;
}
/* Search for an entry first */
@ -729,9 +741,9 @@ ta_add_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
return (EEXIST);
/* Record already exists. Update value if we're asked to */
if (tei->subtype == AF_INET)
old_value = &((struct radix_addr_entry *)rn)->value;
old_value = &((struct addr_radix_entry *)rn)->value;
else
old_value = &((struct radix_addr_xentry *)rn)->value;
old_value = &((struct addr_radix_xentry *)rn)->value;
value = *old_value;
*old_value = tei->value;
@ -764,7 +776,7 @@ ta_add_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
}
static int
ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
ta_prepare_del_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf)
{
struct ta_buf_radix *tb;
@ -793,7 +805,7 @@ ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
} else
return (EINVAL);
tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
tei_to_sockaddr_ent_addr(tei, addr, mask, &set_mask);
tb->addr_ptr = addr;
if (set_mask != 0)
tb->mask_ptr = mask;
@ -802,15 +814,15 @@ ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
}
static int
ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
ta_del_addr_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
void *ta_buf, uint32_t *pnum)
{
struct radix_cfg *cfg;
struct addr_radix_cfg *cfg;
struct radix_node_head *rnh;
struct radix_node *rn;
struct ta_buf_radix *tb;
cfg = (struct radix_cfg *)ta_state;
cfg = (struct addr_radix_cfg *)ta_state;
tb = (struct ta_buf_radix *)ta_buf;
if (tei->subtype == AF_INET)
@ -825,9 +837,9 @@ ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
/* Save entry value to @tei */
if (tei->subtype == AF_INET)
tei->value = ((struct radix_addr_entry *)rn)->value;
tei->value = ((struct addr_radix_entry *)rn)->value;
else
tei->value = ((struct radix_addr_xentry *)rn)->value;
tei->value = ((struct addr_radix_xentry *)rn)->value;
tb->ent_ptr = rn;
@ -871,17 +883,17 @@ struct table_algo addr_radix = {
.type = IPFW_TABLE_ADDR,
.flags = TA_FLAG_DEFAULT,
.ta_buf_size = sizeof(struct ta_buf_radix),
.init = ta_init_radix,
.destroy = ta_destroy_radix,
.prepare_add = ta_prepare_add_radix,
.prepare_del = ta_prepare_del_radix,
.add = ta_add_radix,
.del = ta_del_radix,
.init = ta_init_addr_radix,
.destroy = ta_destroy_addr_radix,
.prepare_add = ta_prepare_add_addr_radix,
.prepare_del = ta_prepare_del_addr_radix,
.add = ta_add_addr_radix,
.del = ta_del_addr_radix,
.flush_entry = ta_flush_radix_entry,
.foreach = ta_foreach_radix,
.dump_tentry = ta_dump_radix_tentry,
.find_tentry = ta_find_radix_tentry,
.dump_tinfo = ta_dump_radix_tinfo,
.foreach = ta_foreach_addr_radix,
.dump_tentry = ta_dump_addr_radix_tentry,
.find_tentry = ta_find_addr_radix_tentry,
.dump_tinfo = ta_dump_addr_radix_tinfo,
.need_modify = ta_need_modify_radix,
};
@ -4014,6 +4026,328 @@ struct table_algo addr_kfib = {
.print_config = ta_print_kfib_config,
};
struct mac_radix_entry {
struct radix_node rn[2];
uint32_t value;
uint8_t masklen;
struct sa_mac sa;
};
struct mac_radix_cfg {
struct radix_node_head *head;
size_t count;
};
static int
ta_lookup_mac_radix(struct table_info *ti, void *key, uint32_t keylen,
uint32_t *val)
{
struct radix_node_head *rnh;
if (keylen == ETHER_ADDR_LEN) {
struct mac_radix_entry *ent;
struct sa_mac sa;
KEY_LEN(sa) = KEY_LEN_MAC;
memcpy(sa.mac_addr.octet, key, ETHER_ADDR_LEN);
rnh = (struct radix_node_head *)ti->state;
ent = (struct mac_radix_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh));
if (ent != NULL) {
*val = ent->value;
return (1);
}
}
return (0);
}
static int
ta_init_mac_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
char *data, uint8_t tflags)
{
struct mac_radix_cfg *cfg;
if (!rn_inithead(&ti->state, OFF_LEN_MAC))
return (ENOMEM);
cfg = malloc(sizeof(struct mac_radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
*ta_state = cfg;
ti->lookup = ta_lookup_mac_radix;
return (0);
}
static void
ta_destroy_mac_radix(void *ta_state, struct table_info *ti)
{
struct mac_radix_cfg *cfg;
struct radix_node_head *rnh;
cfg = (struct mac_radix_cfg *)ta_state;
rnh = (struct radix_node_head *)(ti->state);
rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);
rn_detachhead(&ti->state);
free(cfg, M_IPFW);
}
static void
tei_to_sockaddr_ent_mac(struct tentry_info *tei, struct sockaddr *sa,
struct sockaddr *ma, int *set_mask)
{
int mlen, i;
struct sa_mac *addr, *mask;
u_char *cp;
mlen = tei->masklen;
addr = (struct sa_mac *)sa;
mask = (struct sa_mac *)ma;
/* Set 'total' structure length */
KEY_LEN(*addr) = KEY_LEN_MAC;
KEY_LEN(*mask) = KEY_LEN_MAC;
for (i = mlen, cp = mask->mac_addr.octet; i >= 8; i -= 8)
*cp++ = 0xFF;
if (i > 0)
*cp = ~((1 << (8 - i)) - 1);
addr->mac_addr = *((struct ether_addr *)tei->paddr);
for (i = 0; i < ETHER_ADDR_LEN; ++i)
addr->mac_addr.octet[i] &= mask->mac_addr.octet[i];
if (mlen != 8 * ETHER_ADDR_LEN)
*set_mask = 1;
else
*set_mask = 0;
}
static int
ta_prepare_add_mac_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf)
{
struct ta_buf_radix *tb;
struct mac_radix_entry *ent;
struct sockaddr *addr, *mask;
int mlen, set_mask;
tb = (struct ta_buf_radix *)ta_buf;
mlen = tei->masklen;
set_mask = 0;
if (tei->subtype == AF_LINK) {
if (mlen > 8 * ETHER_ADDR_LEN)
return (EINVAL);
ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
ent->masklen = mlen;
addr = (struct sockaddr *)&ent->sa;
mask = (struct sockaddr *)&tb->addr.mac.ma;
tb->ent_ptr = ent;
} else {
/* Unknown CIDR type */
return (EINVAL);
}
tei_to_sockaddr_ent_mac(tei, addr, mask, &set_mask);
/* Set pointers */
tb->addr_ptr = addr;
if (set_mask != 0)
tb->mask_ptr = mask;
return (0);
}
static int
ta_add_mac_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
void *ta_buf, uint32_t *pnum)
{
struct mac_radix_cfg *cfg;
struct radix_node_head *rnh;
struct radix_node *rn;
struct ta_buf_radix *tb;
uint32_t *old_value, value;
cfg = (struct mac_radix_cfg *)ta_state;
tb = (struct ta_buf_radix *)ta_buf;
/* Save current entry value from @tei */
rnh = ti->state;
((struct mac_radix_entry *)tb->ent_ptr)->value = tei->value;
/* Search for an entry first */
rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, &rnh->rh);
if (rn != NULL) {
if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
return (EEXIST);
/* Record already exists. Update value if we're asked to */
old_value = &((struct mac_radix_entry *)rn)->value;
value = *old_value;
*old_value = tei->value;
tei->value = value;
/* Indicate that update has happened instead of addition */
tei->flags |= TEI_FLAGS_UPDATED;
*pnum = 0;
return (0);
}
if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
return (EFBIG);
rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, &rnh->rh, tb->ent_ptr);
if (rn == NULL) {
/* Unknown error */
return (EINVAL);
}
cfg->count++;
tb->ent_ptr = NULL;
*pnum = 1;
return (0);
}
static int
ta_prepare_del_mac_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf)
{
struct ta_buf_radix *tb;
struct sockaddr *addr, *mask;
int mlen, set_mask;
tb = (struct ta_buf_radix *)ta_buf;
mlen = tei->masklen;
set_mask = 0;
if (tei->subtype == AF_LINK) {
if (mlen > 8 * ETHER_ADDR_LEN)
return (EINVAL);
addr = (struct sockaddr *)&tb->addr.mac.sa;
mask = (struct sockaddr *)&tb->addr.mac.ma;
} else
return (EINVAL);
tei_to_sockaddr_ent_mac(tei, addr, mask, &set_mask);
tb->addr_ptr = addr;
if (set_mask != 0)
tb->mask_ptr = mask;
return (0);
}
static int
ta_del_mac_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
void *ta_buf, uint32_t *pnum)
{
struct mac_radix_cfg *cfg;
struct radix_node_head *rnh;
struct radix_node *rn;
struct ta_buf_radix *tb;
cfg = (struct mac_radix_cfg *)ta_state;
tb = (struct ta_buf_radix *)ta_buf;
rnh = ti->state;
rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, &rnh->rh);
if (rn == NULL)
return (ENOENT);
/* Save entry value to @tei */
tei->value = ((struct mac_radix_entry *)rn)->value;
tb->ent_ptr = rn;
cfg->count--;
*pnum = 1;
return (0);
}
static void
ta_foreach_mac_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
void *arg)
{
struct radix_node_head *rnh;
rnh = (struct radix_node_head *)(ti->state);
rnh->rnh_walktree(&rnh->rh, (walktree_f_t *)f, arg);
}
static void
ta_dump_mac_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
{
struct mac_radix_cfg *cfg;
cfg = (struct mac_radix_cfg *)ta_state;
tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
tinfo->taclass4 = IPFW_TACLASS_RADIX;
tinfo->count4 = cfg->count;
tinfo->itemsize4 = sizeof(struct mac_radix_entry);
}
static int
ta_dump_mac_radix_tentry(void *ta_state, struct table_info *ti, void *e,
ipfw_obj_tentry *tent)
{
struct mac_radix_entry *n = (struct mac_radix_entry *)e;
memcpy(tent->k.mac, n->sa.mac_addr.octet, ETHER_ADDR_LEN);
tent->masklen = n->masklen;
tent->subtype = AF_LINK;
tent->v.kidx = n->value;
return (0);
}
static int
ta_find_mac_radix_tentry(void *ta_state, struct table_info *ti,
ipfw_obj_tentry *tent)
{
struct radix_node_head *rnh;
void *e;
e = NULL;
if (tent->subtype == AF_LINK) {
struct sa_mac sa;
KEY_LEN(sa) = KEY_LEN_MAC;
memcpy(tent->k.mac, sa.mac_addr.octet, ETHER_ADDR_LEN);
rnh = (struct radix_node_head *)ti->state;
e = rnh->rnh_matchaddr(&sa, &rnh->rh);
}
if (e != NULL) {
ta_dump_mac_radix_tentry(ta_state, ti, e, tent);
return (0);
}
return (ENOENT);
}
struct table_algo mac_radix = {
.name = "mac:radix",
.type = IPFW_TABLE_MAC,
.flags = TA_FLAG_DEFAULT,
.ta_buf_size = sizeof(struct ta_buf_radix),
.init = ta_init_mac_radix,
.destroy = ta_destroy_mac_radix,
.prepare_add = ta_prepare_add_mac_radix,
.prepare_del = ta_prepare_del_mac_radix,
.add = ta_add_mac_radix,
.del = ta_del_mac_radix,
.flush_entry = ta_flush_radix_entry,
.foreach = ta_foreach_mac_radix,
.dump_tentry = ta_dump_mac_radix_tentry,
.find_tentry = ta_find_mac_radix_tentry,
.dump_tinfo = ta_dump_mac_radix_tinfo,
.need_modify = ta_need_modify_radix,
};
void
ipfw_table_algo_init(struct ip_fw_chain *ch)
{
@ -4029,6 +4363,7 @@ ipfw_table_algo_init(struct ip_fw_chain *ch)
ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);
ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.idx);
ipfw_add_table_algo(ch, &addr_kfib, sz, &addr_kfib.idx);
ipfw_add_table_algo(ch, &mac_radix, sz, &mac_radix.idx);
}
void
@ -4041,4 +4376,5 @@ ipfw_table_algo_destroy(struct ip_fw_chain *ch)
ipfw_del_table_algo(ch, number_array.idx);
ipfw_del_table_algo(ch, flow_hash.idx);
ipfw_del_table_algo(ch, addr_kfib.idx);
ipfw_del_table_algo(ch, mac_radix.idx);
}