mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-19 08:13:57 +00:00
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:
parent
8e6cfc632c
commit
c31f8b7bd8
|
@ -1,7 +1,7 @@
|
||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd June 14, 2021
|
.Dd June 4, 2022
|
||||||
.Dt IPFW 8
|
.Dt IPFW 8
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -1610,6 +1610,20 @@ Matches IPv6 packets containing any of the flow labels given in
|
||||||
.Ar labels .
|
.Ar labels .
|
||||||
.Ar labels
|
.Ar labels
|
||||||
is a comma separated list of numeric flow 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
|
.It Cm frag Ar spec
|
||||||
Matches IPv4 packets whose
|
Matches IPv4 packets whose
|
||||||
.Cm ip_off
|
.Cm ip_off
|
||||||
|
@ -1823,7 +1837,7 @@ set of parameters as specified in the rule.
|
||||||
One or more
|
One or more
|
||||||
of source and destination addresses and ports can be
|
of source and destination addresses and ports can be
|
||||||
specified.
|
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
|
Search an entry in lookup table
|
||||||
.Ar name
|
.Ar name
|
||||||
that matches the field specified as argument.
|
that matches the field specified as argument.
|
||||||
|
@ -2133,7 +2147,7 @@ There may be up to 65535 different lookup tables.
|
||||||
.Pp
|
.Pp
|
||||||
The following table types are supported:
|
The following table types are supported:
|
||||||
.Bl -tag -width indent
|
.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 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-spec : Ar flow-field Ns Op , Ns Ar flow-spec
|
||||||
.It Ar flow-field : src-ip | proto | src-port | dst-ip | dst-port
|
.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
|
Matches packet fields specified by
|
||||||
.Ar flow
|
.Ar flow
|
||||||
type suboptions with table entries.
|
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
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Tables require explicit creation via
|
Tables require explicit creation via
|
||||||
|
@ -2266,7 +2294,7 @@ Shows generic table information and algo-specific data.
|
||||||
The following lookup algorithms are supported:
|
The following lookup algorithms are supported:
|
||||||
.Bl -tag -width indent
|
.Bl -tag -width indent
|
||||||
.It Ar algo-desc : algo-name | "algo-name algo-data"
|
.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
|
.It Cm addr: radix
|
||||||
Separate Radix trees for IPv4 and IPv6, the same way as the routing table (see
|
Separate Radix trees for IPv4 and IPv6, the same way as the routing table (see
|
||||||
.Xr route 4 ) .
|
.Xr route 4 ) .
|
||||||
|
@ -2291,6 +2319,8 @@ Array storing sorted u32 numbers.
|
||||||
Auto-growing hash storing flow entries.
|
Auto-growing hash storing flow entries.
|
||||||
Search calculates hash on required packet fields and searches for matching
|
Search calculates hash on required packet fields and searches for matching
|
||||||
entries in selected bucket.
|
entries in selected bucket.
|
||||||
|
.It Cm mac: radix
|
||||||
|
Radix tree for MAC address
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
|
|
|
@ -300,12 +300,20 @@ static struct _s_x rule_action_params[] = {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The 'lookup' instruction accepts one of the following arguments.
|
* 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.
|
* Arguments are passed as v[1] in O_DST_LOOKUP options.
|
||||||
*/
|
*/
|
||||||
static int lookup_key[] = {
|
static struct _s_x lookup_keys[] = {
|
||||||
TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT,
|
{ "dst-ip", LOOKUP_DST_IP },
|
||||||
TOK_UID, TOK_JAIL, TOK_DSCP, -1 };
|
{ "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[] = {
|
static struct _s_x rule_options[] = {
|
||||||
{ "tagged", TOK_TAGGED },
|
{ "tagged", TOK_TAGGED },
|
||||||
|
@ -358,6 +366,8 @@ static struct _s_x rule_options[] = {
|
||||||
{ "src-ip", TOK_SRCIP },
|
{ "src-ip", TOK_SRCIP },
|
||||||
{ "dst-port", TOK_DSTPORT },
|
{ "dst-port", TOK_DSTPORT },
|
||||||
{ "src-port", TOK_SRCPORT },
|
{ "src-port", TOK_SRCPORT },
|
||||||
|
{ "dst-mac", TOK_DSTMAC },
|
||||||
|
{ "src-mac", TOK_SRCMAC },
|
||||||
{ "proto", TOK_PROTO },
|
{ "proto", TOK_PROTO },
|
||||||
{ "MAC", TOK_MAC },
|
{ "MAC", TOK_MAC },
|
||||||
{ "mac", TOK_MAC },
|
{ "mac", TOK_MAC },
|
||||||
|
@ -368,18 +378,18 @@ static struct _s_x rule_options[] = {
|
||||||
{ "ipsec", TOK_IPSEC },
|
{ "ipsec", TOK_IPSEC },
|
||||||
{ "icmp6type", TOK_ICMP6TYPES },
|
{ "icmp6type", TOK_ICMP6TYPES },
|
||||||
{ "icmp6types", TOK_ICMP6TYPES },
|
{ "icmp6types", TOK_ICMP6TYPES },
|
||||||
{ "ext6hdr", TOK_EXT6HDR},
|
{ "ext6hdr", TOK_EXT6HDR },
|
||||||
{ "flow-id", TOK_FLOWID},
|
{ "flow-id", TOK_FLOWID },
|
||||||
{ "ipv6", TOK_IPV6},
|
{ "ipv6", TOK_IPV6 },
|
||||||
{ "ip6", TOK_IPV6},
|
{ "ip6", TOK_IPV6 },
|
||||||
{ "ipv4", TOK_IPV4},
|
{ "ipv4", TOK_IPV4 },
|
||||||
{ "ip4", TOK_IPV4},
|
{ "ip4", TOK_IPV4 },
|
||||||
{ "dst-ipv6", TOK_DSTIP6},
|
{ "dst-ipv6", TOK_DSTIP6 },
|
||||||
{ "dst-ip6", TOK_DSTIP6},
|
{ "dst-ip6", TOK_DSTIP6 },
|
||||||
{ "src-ipv6", TOK_SRCIP6},
|
{ "src-ipv6", TOK_SRCIP6 },
|
||||||
{ "src-ip6", TOK_SRCIP6},
|
{ "src-ip6", TOK_SRCIP6 },
|
||||||
{ "lookup", TOK_LOOKUP},
|
{ "lookup", TOK_LOOKUP },
|
||||||
{ "flow", TOK_FLOW},
|
{ "flow", TOK_FLOW },
|
||||||
{ "defer-action", TOK_SKIPACTION },
|
{ "defer-action", TOK_SKIPACTION },
|
||||||
{ "defer-immediate-action", TOK_SKIPACTION },
|
{ "defer-immediate-action", TOK_SKIPACTION },
|
||||||
{ "//", TOK_COMMENT },
|
{ "//", TOK_COMMENT },
|
||||||
|
@ -1211,11 +1221,9 @@ print_ip(struct buf_pr *bp, const struct format_opts *fo,
|
||||||
|
|
||||||
bprintf(bp, " ");
|
bprintf(bp, " ");
|
||||||
if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
|
if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
|
||||||
uint32_t d = a[1];
|
const char *arg;
|
||||||
const char *arg = "<invalid>";
|
|
||||||
|
|
||||||
if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
|
arg = match_value(lookup_keys, a[1]);
|
||||||
arg = match_value(rule_options, lookup_key[d]);
|
|
||||||
t = table_search_ctlv(fo->tstate,
|
t = table_search_ctlv(fo->tstate,
|
||||||
((const ipfw_insn *)cmd)->arg1);
|
((const ipfw_insn *)cmd)->arg1);
|
||||||
bprintf(bp, "lookup %s %s", arg, t);
|
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);
|
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
|
static void
|
||||||
fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
|
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");
|
bprintf(bp, " dst-ip6");
|
||||||
print_ip6(bp, insntod(cmd, ip6));
|
print_ip6(bp, insntod(cmd, ip6));
|
||||||
break;
|
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:
|
case O_FLOW6ID:
|
||||||
print_flow6id(bp, insntod(cmd, u32));
|
print_flow6id(bp, insntod(cmd, u32));
|
||||||
break;
|
break;
|
||||||
|
@ -2650,7 +2682,6 @@ list_static_range(struct cmdline_opts *co, struct format_opts *fo,
|
||||||
int n, seen;
|
int n, seen;
|
||||||
struct ip_fw_rule *r;
|
struct ip_fw_rule *r;
|
||||||
struct ip_fw_bcounter *cntr;
|
struct ip_fw_bcounter *cntr;
|
||||||
int c = 0;
|
|
||||||
|
|
||||||
for (n = seen = 0; n < rcnt; n++,
|
for (n = seen = 0; n < rcnt; n++,
|
||||||
rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
|
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) {
|
if (r->rulenum >= fo->first && r->rulenum <= fo->last) {
|
||||||
show_static_rule(co, fo, bp, r, cntr);
|
show_static_rule(co, fo, bp, r, cntr);
|
||||||
printf("%s", bp->buf);
|
printf("%s", bp->buf);
|
||||||
c += rtlv->length;
|
|
||||||
bp_flush(bp);
|
bp_flush(bp);
|
||||||
seen++;
|
seen++;
|
||||||
}
|
}
|
||||||
|
@ -2788,13 +2818,12 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
|
||||||
char *endptr;
|
char *endptr;
|
||||||
size_t readsz;
|
size_t readsz;
|
||||||
struct buf_pr bp;
|
struct buf_pr bp;
|
||||||
ipfw_obj_ctlv *ctlv, *tstate;
|
ipfw_obj_ctlv *ctlv;
|
||||||
ipfw_obj_tlv *rbase;
|
ipfw_obj_tlv *rbase;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle tablenames TLV first, if any
|
* Handle tablenames TLV first, if any
|
||||||
*/
|
*/
|
||||||
tstate = NULL;
|
|
||||||
rbase = NULL;
|
rbase = NULL;
|
||||||
dynbase = NULL;
|
dynbase = NULL;
|
||||||
dynsz = 0;
|
dynsz = 0;
|
||||||
|
@ -3682,6 +3711,29 @@ add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
|
||||||
return cmd;
|
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[] = {
|
static struct _s_x f_reserved_keywords[] = {
|
||||||
{ "altq", TOK_OR },
|
{ "altq", TOK_OR },
|
||||||
{ "//", TOK_OR },
|
{ "//", TOK_OR },
|
||||||
|
@ -4912,6 +4964,21 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case TOK_SRCPORT:
|
||||||
NEED1("missing source port");
|
NEED1("missing source port");
|
||||||
if (_substrcmp(*av, "any") == 0 ||
|
if (_substrcmp(*av, "any") == 0 ||
|
||||||
|
@ -5013,28 +5080,23 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
|
||||||
|
|
||||||
case TOK_LOOKUP: {
|
case TOK_LOOKUP: {
|
||||||
ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
|
ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
|
||||||
int j;
|
|
||||||
|
|
||||||
if (!av[0] || !av[1])
|
if (!av[0] || !av[1])
|
||||||
errx(EX_USAGE, "format: lookup argument tablenum");
|
errx(EX_USAGE, "format: lookup argument tablenum");
|
||||||
cmd->opcode = O_IP_DST_LOOKUP;
|
cmd->opcode = O_IP_DST_LOOKUP;
|
||||||
cmd->len |= F_INSN_SIZE(ipfw_insn) + 2;
|
cmd->len |= F_INSN_SIZE(ipfw_insn) + 2;
|
||||||
i = match_token(rule_options, *av);
|
i = match_token(lookup_keys, *av);
|
||||||
for (j = 0; lookup_key[j] >= 0 ; j++) {
|
if (i == -1)
|
||||||
if (i == lookup_key[j])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (lookup_key[j] <= 0)
|
|
||||||
errx(EX_USAGE, "format: cannot lookup on %s", *av);
|
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++;
|
av++;
|
||||||
|
|
||||||
if ((j = pack_table(tstate, *av)) == 0)
|
if ((i = pack_table(tstate, *av)) == 0)
|
||||||
errx(EX_DATAERR, "Invalid table name: %s", *av);
|
errx(EX_DATAERR, "Invalid table name: %s", *av);
|
||||||
|
|
||||||
cmd->arg1 = j;
|
cmd->arg1 = i;
|
||||||
av++;
|
av++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TOK_FLOW:
|
case TOK_FLOW:
|
||||||
NEED1("missing table name");
|
NEED1("missing table name");
|
||||||
|
|
|
@ -177,6 +177,8 @@ enum tokens {
|
||||||
TOK_SRCIP,
|
TOK_SRCIP,
|
||||||
TOK_DSTPORT,
|
TOK_DSTPORT,
|
||||||
TOK_SRCPORT,
|
TOK_SRCPORT,
|
||||||
|
TOK_DSTMAC,
|
||||||
|
TOK_SRCMAC,
|
||||||
TOK_ALL,
|
TOK_ALL,
|
||||||
TOK_MASK,
|
TOK_MASK,
|
||||||
TOK_FLOW_MASK,
|
TOK_FLOW_MASK,
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
|
|
||||||
|
#include <net/ethernet.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/ip_fw.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[] = {
|
static struct _s_x tabletypes[] = {
|
||||||
{ "addr", IPFW_TABLE_ADDR },
|
{ "addr", IPFW_TABLE_ADDR },
|
||||||
|
{ "mac", IPFW_TABLE_MAC },
|
||||||
{ "iface", IPFW_TABLE_INTERFACE },
|
{ "iface", IPFW_TABLE_INTERFACE },
|
||||||
{ "number", IPFW_TABLE_NUMBER },
|
{ "number", IPFW_TABLE_NUMBER },
|
||||||
{ "flow", IPFW_TABLE_FLOW },
|
{ "flow", IPFW_TABLE_FLOW },
|
||||||
|
@ -1188,6 +1190,7 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
|
||||||
char *p, *pp;
|
char *p, *pp;
|
||||||
int mask, af;
|
int mask, af;
|
||||||
struct in6_addr *paddr, tmp;
|
struct in6_addr *paddr, tmp;
|
||||||
|
struct ether_addr *mac;
|
||||||
struct tflow_entry *tfe;
|
struct tflow_entry *tfe;
|
||||||
uint32_t key, *pkey;
|
uint32_t key, *pkey;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
@ -1234,6 +1237,24 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
|
||||||
af = AF_INET;
|
af = AF_INET;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case IPFW_TABLE_INTERFACE:
|
||||||
/* Assume interface name. Copy significant data only */
|
/* Assume interface name. Copy significant data only */
|
||||||
mask = MIN(strlen(arg), IF_NAMESIZE - 1);
|
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];
|
char tbuf[128], pval[128];
|
||||||
const char *comma;
|
const char *comma;
|
||||||
|
const u_char *mac;
|
||||||
void *paddr;
|
void *paddr;
|
||||||
struct tflow_entry *tfe;
|
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));
|
inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
|
||||||
printf("%s/%u %s\n", tbuf, tent->masklen, pval);
|
printf("%s/%u %s\n", tbuf, tent->masklen, pval);
|
||||||
break;
|
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:
|
case IPFW_TABLE_INTERFACE:
|
||||||
/* Interface names */
|
/* Interface names */
|
||||||
printf("%s %s\n", tent->k.iface, pval);
|
printf("%s %s\n", tent->k.iface, pval);
|
||||||
|
|
|
@ -295,9 +295,27 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
|
||||||
O_SKIP_ACTION, /* none */
|
O_SKIP_ACTION, /* none */
|
||||||
O_TCPMSS, /* arg1=MSS value */
|
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! */
|
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
|
* The extension header are filtered only for presence using a bit
|
||||||
* vector with a flag for each header.
|
* 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_INTERFACE 2 /* Table for holding interface names */
|
||||||
#define IPFW_TABLE_NUMBER 3 /* Table for holding ports/uid/gid/etc */
|
#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_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 */
|
#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_NH4 0x00000200 /* IPv4 nexthop */
|
||||||
#define IPFW_VTYPE_NH6 0x00000400 /* IPv6 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 {
|
typedef struct _ipfw_table_entry {
|
||||||
in_addr_t addr; /* network address */
|
in_addr_t addr; /* network address */
|
||||||
u_int32_t value; /* value */
|
u_int32_t value; /* value */
|
||||||
|
@ -895,10 +917,11 @@ typedef struct _ipfw_obj_tentry {
|
||||||
uint16_t spare1;
|
uint16_t spare1;
|
||||||
union {
|
union {
|
||||||
/* Longest field needs to be aligned by 8-byte boundary */
|
/* Longest field needs to be aligned by 8-byte boundary */
|
||||||
struct in_addr addr; /* IPv4 address */
|
struct in_addr addr; /* IPv4 address */
|
||||||
uint32_t key; /* uid/gid/port */
|
uint32_t key; /* uid/gid/port */
|
||||||
struct in6_addr addr6; /* IPv6 address */
|
struct in6_addr addr6; /* IPv6 address */
|
||||||
char iface[IF_NAMESIZE]; /* interface name */
|
char iface[IF_NAMESIZE]; /* interface name */
|
||||||
|
u_char mac[IPFW_MAX_L2_ADDR_LEN]; /* MAC address */
|
||||||
struct tflow_entry flow;
|
struct tflow_entry flow;
|
||||||
} k;
|
} k;
|
||||||
union {
|
union {
|
||||||
|
|
|
@ -2034,78 +2034,87 @@ do { \
|
||||||
|
|
||||||
case O_IP_DST_LOOKUP:
|
case O_IP_DST_LOOKUP:
|
||||||
{
|
{
|
||||||
void *pkey;
|
|
||||||
uint32_t vidx, key;
|
|
||||||
uint16_t keylen;
|
|
||||||
|
|
||||||
if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) {
|
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 */
|
/* Determine lookup key type */
|
||||||
vidx = ((ipfw_insn_u32 *)cmd)->d[1];
|
vidx = ((ipfw_insn_u32 *)cmd)->d[1];
|
||||||
if (vidx != 4 /* uid */ &&
|
switch (vidx) {
|
||||||
vidx != 5 /* jail */ &&
|
case LOOKUP_DST_IP:
|
||||||
is_ipv6 == 0 && is_ipv4 == 0)
|
case LOOKUP_SRC_IP:
|
||||||
break;
|
/* Need IP frame */
|
||||||
/* Determine key length */
|
if (is_ipv6 == 0 && is_ipv4 == 0)
|
||||||
if (vidx == 0 /* dst-ip */ ||
|
break;
|
||||||
vidx == 1 /* src-ip */)
|
if (vidx == LOOKUP_DST_IP)
|
||||||
keylen = is_ipv6 ?
|
pkey = is_ipv6 ?
|
||||||
sizeof(struct in6_addr):
|
(void *)&args->f_id.dst_ip6:
|
||||||
sizeof(in_addr_t);
|
(void *)&dst_ip;
|
||||||
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;
|
|
||||||
else
|
else
|
||||||
key = IPV6_DSCP(
|
pkey = is_ipv6 ?
|
||||||
(struct ip6_hdr *)ip) >> 2;
|
(void *)&args->f_id.src_ip6:
|
||||||
key &= 0x3f;
|
(void *)&src_ip;
|
||||||
} else if (vidx == 2 /* dst-port */ ||
|
keylen = is_ipv6 ?
|
||||||
vidx == 3 /* src-port */) {
|
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 */
|
/* Skip fragments */
|
||||||
if (offset != 0)
|
if (offset != 0)
|
||||||
break;
|
break;
|
||||||
/* Skip proto without ports */
|
/* Skip proto without ports */
|
||||||
if (proto != IPPROTO_TCP &&
|
if (proto != IPPROTO_TCP &&
|
||||||
proto != IPPROTO_UDP &&
|
proto != IPPROTO_UDP &&
|
||||||
proto != IPPROTO_UDPLITE &&
|
proto != IPPROTO_UDPLITE &&
|
||||||
proto != IPPROTO_SCTP)
|
proto != IPPROTO_SCTP)
|
||||||
break;
|
break;
|
||||||
if (vidx == 2 /* dst-port */)
|
key = vidx == LOOKUP_DST_PORT ?
|
||||||
key = dst_port;
|
dst_port:
|
||||||
else
|
src_port;
|
||||||
key = src_port;
|
pkey = &key;
|
||||||
}
|
keylen = sizeof(key);
|
||||||
#ifndef USERSPACE
|
break;
|
||||||
else if (vidx == 4 /* uid */ ||
|
case LOOKUP_UID:
|
||||||
vidx == 5 /* jail */) {
|
case LOOKUP_JAIL:
|
||||||
check_uidgid(
|
check_uidgid(
|
||||||
(ipfw_insn_u32 *)cmd,
|
(ipfw_insn_u32 *)cmd,
|
||||||
args, &ucred_lookup,
|
args, &ucred_lookup,
|
||||||
#ifdef __FreeBSD__
|
|
||||||
&ucred_cache);
|
&ucred_cache);
|
||||||
if (vidx == 4 /* uid */)
|
key = vidx == LOOKUP_UID ?
|
||||||
key = ucred_cache->cr_uid;
|
ucred_cache->cr_uid:
|
||||||
else if (vidx == 5 /* jail */)
|
ucred_cache->cr_prison->pr_id;
|
||||||
key = ucred_cache->cr_prison->pr_id;
|
pkey = &key;
|
||||||
#else /* !__FreeBSD__ */
|
keylen = sizeof(key);
|
||||||
(void *)&ucred_cache);
|
break;
|
||||||
if (vidx == 4 /* uid */)
|
case LOOKUP_DSCP:
|
||||||
key = ucred_cache.uid;
|
/* Need IP frame */
|
||||||
else if (vidx == 5 /* jail */)
|
if (is_ipv6 == 0 && is_ipv4 == 0)
|
||||||
key = ucred_cache.xid;
|
break;
|
||||||
#endif /* !__FreeBSD__ */
|
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 */
|
if (keylen == 0)
|
||||||
else
|
|
||||||
break;
|
break;
|
||||||
match = ipfw_lookup_table(chain,
|
match = ipfw_lookup_table(chain,
|
||||||
cmd->arg1, keylen, pkey, &vidx);
|
cmd->arg1, keylen, pkey, &vidx);
|
||||||
|
@ -2151,6 +2160,36 @@ do { \
|
||||||
break;
|
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:
|
case O_IP_FLOW_LOOKUP:
|
||||||
{
|
{
|
||||||
uint32_t v = 0;
|
uint32_t v = 0;
|
||||||
|
|
|
@ -1909,6 +1909,8 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
|
||||||
ci->object_opcodes++;
|
ci->object_opcodes++;
|
||||||
break;
|
break;
|
||||||
case O_IP_FLOW_LOOKUP:
|
case O_IP_FLOW_LOOKUP:
|
||||||
|
case O_MAC_DST_LOOKUP:
|
||||||
|
case O_MAC_SRC_LOOKUP:
|
||||||
if (cmd->arg1 >= V_fw_tables_max) {
|
if (cmd->arg1 >= V_fw_tables_max) {
|
||||||
printf("ipfw: invalid table number %d\n",
|
printf("ipfw: invalid table number %d\n",
|
||||||
cmd->arg1);
|
cmd->arg1);
|
||||||
|
|
|
@ -2753,26 +2753,19 @@ classify_srcdst(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
|
||||||
*/
|
*/
|
||||||
v = ((ipfw_insn_u32 *)cmd)->d[1];
|
v = ((ipfw_insn_u32 *)cmd)->d[1];
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case 0:
|
case LOOKUP_DST_IP:
|
||||||
case 1:
|
case LOOKUP_SRC_IP:
|
||||||
/* IPv4 src/dst */
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case LOOKUP_DST_PORT:
|
||||||
case 3:
|
case LOOKUP_SRC_PORT:
|
||||||
/* src/dst port */
|
case LOOKUP_UID:
|
||||||
|
case LOOKUP_JAIL:
|
||||||
|
case LOOKUP_DSCP:
|
||||||
*ptype = IPFW_TABLE_NUMBER;
|
*ptype = IPFW_TABLE_NUMBER;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case LOOKUP_DST_MAC:
|
||||||
/* uid/gid */
|
case LOOKUP_SRC_MAC:
|
||||||
*ptype = IPFW_TABLE_NUMBER;
|
*ptype = IPFW_TABLE_MAC;
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
/* jid */
|
|
||||||
*ptype = IPFW_TABLE_NUMBER;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
/* dscp */
|
|
||||||
*ptype = IPFW_TABLE_NUMBER;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2806,6 +2799,14 @@ classify_flow(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
|
||||||
return (0);
|
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
|
static void
|
||||||
update_arg1(ipfw_insn *cmd, uint16_t idx)
|
update_arg1(ipfw_insn *cmd, uint16_t idx)
|
||||||
{
|
{
|
||||||
|
@ -2956,6 +2957,26 @@ static struct opcode_obj_rewrite opcodes[] = {
|
||||||
.create_object = create_table_compat,
|
.create_object = create_table_compat,
|
||||||
.manage_sets = table_manage_sets,
|
.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,
|
.opcode = O_XMIT,
|
||||||
.etlv = IPFW_TLV_TBL_NAME,
|
.etlv = IPFW_TLV_TBL_NAME,
|
||||||
|
|
|
@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
|
||||||
#include <sys/rmlock.h>
|
#include <sys/rmlock.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
|
#include <net/ethernet.h>
|
||||||
#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */
|
#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */
|
||||||
#include <net/radix.h>
|
#include <net/radix.h>
|
||||||
#include <net/route.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))
|
#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_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_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_INET (8 * offsetof(struct sockaddr_in, sin_addr))
|
||||||
#define OFF_LEN_INET6 (8 * offsetof(struct sa_in6, sin6_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 radix_node rn[2];
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
|
@ -337,20 +340,25 @@ struct sa_in6 {
|
||||||
struct in6_addr sin6_addr;
|
struct in6_addr sin6_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct radix_addr_xentry {
|
struct addr_radix_xentry {
|
||||||
struct radix_node rn[2];
|
struct radix_node rn[2];
|
||||||
struct sa_in6 addr6;
|
struct sa_in6 addr6;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
uint8_t masklen;
|
uint8_t masklen;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct radix_cfg {
|
struct addr_radix_cfg {
|
||||||
struct radix_node_head *head4;
|
struct radix_node_head *head4;
|
||||||
struct radix_node_head *head6;
|
struct radix_node_head *head6;
|
||||||
size_t count4;
|
size_t count4;
|
||||||
size_t count6;
|
size_t count6;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sa_mac {
|
||||||
|
uint8_t mac_len;
|
||||||
|
struct ether_addr mac_addr;
|
||||||
|
};
|
||||||
|
|
||||||
struct ta_buf_radix
|
struct ta_buf_radix
|
||||||
{
|
{
|
||||||
void *ent_ptr;
|
void *ent_ptr;
|
||||||
|
@ -365,32 +373,36 @@ struct ta_buf_radix
|
||||||
struct sa_in6 sa;
|
struct sa_in6 sa;
|
||||||
struct sa_in6 ma;
|
struct sa_in6 ma;
|
||||||
} a6;
|
} a6;
|
||||||
|
struct {
|
||||||
|
struct sa_mac sa;
|
||||||
|
struct sa_mac ma;
|
||||||
|
} mac;
|
||||||
} addr;
|
} 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);
|
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);
|
struct table_info *ti, char *data, uint8_t tflags);
|
||||||
static int flush_radix_entry(struct radix_node *rn, void *arg);
|
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_destroy_addr_radix(void *ta_state, struct table_info *ti);
|
||||||
static void ta_dump_radix_tinfo(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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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,
|
static void ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
void *ta_buf);
|
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);
|
uint32_t count, uint64_t *pflags);
|
||||||
|
|
||||||
static int
|
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)
|
uint32_t *val)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
|
|
||||||
if (keylen == sizeof(in_addr_t)) {
|
if (keylen == sizeof(in_addr_t)) {
|
||||||
struct radix_addr_entry *ent;
|
struct addr_radix_entry *ent;
|
||||||
struct sockaddr_in sa;
|
struct sockaddr_in sa;
|
||||||
KEY_LEN(sa) = KEY_LEN_INET;
|
KEY_LEN(sa) = KEY_LEN_INET;
|
||||||
sa.sin_addr.s_addr = *((in_addr_t *)key);
|
sa.sin_addr.s_addr = *((in_addr_t *)key);
|
||||||
rnh = (struct radix_node_head *)ti->state;
|
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) {
|
if (ent != NULL) {
|
||||||
*val = ent->value;
|
*val = ent->value;
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (keylen == sizeof(struct in6_addr)) {
|
||||||
struct radix_addr_xentry *xent;
|
struct addr_radix_xentry *xent;
|
||||||
struct sa_in6 sa6;
|
struct sa_in6 sa6;
|
||||||
KEY_LEN(sa6) = KEY_LEN_INET6;
|
KEY_LEN(sa6) = KEY_LEN_INET6;
|
||||||
memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
|
memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
|
||||||
rnh = (struct radix_node_head *)ti->xstate;
|
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) {
|
if (xent != NULL) {
|
||||||
*val = xent->value;
|
*val = xent->value;
|
||||||
return (1);
|
return (1);
|
||||||
|
@ -434,10 +446,10 @@ ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
|
||||||
* New table
|
* New table
|
||||||
*/
|
*/
|
||||||
static int
|
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)
|
char *data, uint8_t tflags)
|
||||||
{
|
{
|
||||||
struct radix_cfg *cfg;
|
struct addr_radix_cfg *cfg;
|
||||||
|
|
||||||
if (!rn_inithead(&ti->state, OFF_LEN_INET))
|
if (!rn_inithead(&ti->state, OFF_LEN_INET))
|
||||||
return (ENOMEM);
|
return (ENOMEM);
|
||||||
|
@ -446,10 +458,10 @@ ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
|
||||||
return (ENOMEM);
|
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;
|
*ta_state = cfg;
|
||||||
ti->lookup = ta_lookup_radix;
|
ti->lookup = ta_lookup_addr_radix;
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
@ -458,9 +470,9 @@ static int
|
||||||
flush_radix_entry(struct radix_node *rn, void *arg)
|
flush_radix_entry(struct radix_node *rn, void *arg)
|
||||||
{
|
{
|
||||||
struct radix_node_head * const rnh = 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);
|
rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, &rnh->rh);
|
||||||
if (ent != NULL)
|
if (ent != NULL)
|
||||||
free(ent, M_IPFW_TBL);
|
free(ent, M_IPFW_TBL);
|
||||||
|
@ -468,12 +480,12 @@ flush_radix_entry(struct radix_node *rn, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
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 = (struct radix_node_head *)(ti->state);
|
||||||
rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);
|
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
|
* Provide algo-specific table info
|
||||||
*/
|
*/
|
||||||
static void
|
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->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
|
||||||
tinfo->taclass4 = IPFW_TACLASS_RADIX;
|
tinfo->taclass4 = IPFW_TACLASS_RADIX;
|
||||||
tinfo->count4 = cfg->count4;
|
tinfo->count4 = cfg->count4;
|
||||||
tinfo->itemsize4 = sizeof(struct radix_addr_entry);
|
tinfo->itemsize4 = sizeof(struct addr_radix_entry);
|
||||||
tinfo->taclass6 = IPFW_TACLASS_RADIX;
|
tinfo->taclass6 = IPFW_TACLASS_RADIX;
|
||||||
tinfo->count6 = cfg->count6;
|
tinfo->count6 = cfg->count6;
|
||||||
tinfo->itemsize6 = sizeof(struct radix_addr_xentry);
|
tinfo->itemsize6 = sizeof(struct addr_radix_xentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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)
|
ipfw_obj_tentry *tent)
|
||||||
{
|
{
|
||||||
struct radix_addr_entry *n;
|
struct addr_radix_entry *n;
|
||||||
#ifdef INET6
|
#ifdef INET6
|
||||||
struct radix_addr_xentry *xn;
|
struct addr_radix_xentry *xn;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
n = (struct radix_addr_entry *)e;
|
n = (struct addr_radix_entry *)e;
|
||||||
|
|
||||||
/* Guess IPv4/IPv6 radix by sockaddr family */
|
/* Guess IPv4/IPv6 radix by sockaddr family */
|
||||||
if (n->addr.sin_family == AF_INET) {
|
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;
|
tent->v.kidx = n->value;
|
||||||
#ifdef INET6
|
#ifdef INET6
|
||||||
} else {
|
} else {
|
||||||
xn = (struct radix_addr_xentry *)e;
|
xn = (struct addr_radix_xentry *)e;
|
||||||
memcpy(&tent->k.addr6, &xn->addr6.sin6_addr,
|
memcpy(&tent->k.addr6, &xn->addr6.sin6_addr,
|
||||||
sizeof(struct in6_addr));
|
sizeof(struct in6_addr));
|
||||||
tent->masklen = xn->masklen;
|
tent->masklen = xn->masklen;
|
||||||
|
@ -537,7 +549,7 @@ ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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)
|
ipfw_obj_tentry *tent)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
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;
|
sa.sin_addr.s_addr = tent->k.addr.s_addr;
|
||||||
rnh = (struct radix_node_head *)ti->state;
|
rnh = (struct radix_node_head *)ti->state;
|
||||||
e = rnh->rnh_matchaddr(&sa, &rnh->rh);
|
e = rnh->rnh_matchaddr(&sa, &rnh->rh);
|
||||||
} else {
|
} else if (tent->subtype == AF_INET6) {
|
||||||
struct sa_in6 sa6;
|
struct sa_in6 sa6;
|
||||||
KEY_LEN(sa6) = KEY_LEN_INET6;
|
KEY_LEN(sa6) = KEY_LEN_INET6;
|
||||||
memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));
|
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) {
|
if (e != NULL) {
|
||||||
ta_dump_radix_tentry(ta_state, ti, e, tent);
|
ta_dump_addr_radix_tentry(ta_state, ti, e, tent);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,7 +579,7 @@ ta_find_radix_tentry(void *ta_state, struct table_info *ti,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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)
|
void *arg)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
|
@ -595,7 +607,7 @@ ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
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)
|
struct sockaddr *ma, int *set_mask)
|
||||||
{
|
{
|
||||||
int mlen;
|
int mlen;
|
||||||
|
@ -647,13 +659,13 @@ tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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)
|
void *ta_buf)
|
||||||
{
|
{
|
||||||
struct ta_buf_radix *tb;
|
struct ta_buf_radix *tb;
|
||||||
struct radix_addr_entry *ent;
|
struct addr_radix_entry *ent;
|
||||||
#ifdef INET6
|
#ifdef INET6
|
||||||
struct radix_addr_xentry *xent;
|
struct addr_radix_xentry *xent;
|
||||||
#endif
|
#endif
|
||||||
struct sockaddr *addr, *mask;
|
struct sockaddr *addr, *mask;
|
||||||
int mlen, set_mask;
|
int mlen, set_mask;
|
||||||
|
@ -691,7 +703,7 @@ ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
|
tei_to_sockaddr_ent_addr(tei, addr, mask, &set_mask);
|
||||||
/* Set pointers */
|
/* Set pointers */
|
||||||
tb->addr_ptr = addr;
|
tb->addr_ptr = addr;
|
||||||
if (set_mask != 0)
|
if (set_mask != 0)
|
||||||
|
@ -701,25 +713,25 @@ ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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)
|
void *ta_buf, uint32_t *pnum)
|
||||||
{
|
{
|
||||||
struct radix_cfg *cfg;
|
struct addr_radix_cfg *cfg;
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
struct radix_node *rn;
|
struct radix_node *rn;
|
||||||
struct ta_buf_radix *tb;
|
struct ta_buf_radix *tb;
|
||||||
uint32_t *old_value, value;
|
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;
|
tb = (struct ta_buf_radix *)ta_buf;
|
||||||
|
|
||||||
/* Save current entry value from @tei */
|
/* Save current entry value from @tei */
|
||||||
if (tei->subtype == AF_INET) {
|
if (tei->subtype == AF_INET) {
|
||||||
rnh = ti->state;
|
rnh = ti->state;
|
||||||
((struct radix_addr_entry *)tb->ent_ptr)->value = tei->value;
|
((struct addr_radix_entry *)tb->ent_ptr)->value = tei->value;
|
||||||
} else {
|
} else {
|
||||||
rnh = ti->xstate;
|
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 */
|
/* 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);
|
return (EEXIST);
|
||||||
/* Record already exists. Update value if we're asked to */
|
/* Record already exists. Update value if we're asked to */
|
||||||
if (tei->subtype == AF_INET)
|
if (tei->subtype == AF_INET)
|
||||||
old_value = &((struct radix_addr_entry *)rn)->value;
|
old_value = &((struct addr_radix_entry *)rn)->value;
|
||||||
else
|
else
|
||||||
old_value = &((struct radix_addr_xentry *)rn)->value;
|
old_value = &((struct addr_radix_xentry *)rn)->value;
|
||||||
|
|
||||||
value = *old_value;
|
value = *old_value;
|
||||||
*old_value = tei->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
|
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)
|
void *ta_buf)
|
||||||
{
|
{
|
||||||
struct ta_buf_radix *tb;
|
struct ta_buf_radix *tb;
|
||||||
|
@ -793,7 +805,7 @@ ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
} else
|
} else
|
||||||
return (EINVAL);
|
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;
|
tb->addr_ptr = addr;
|
||||||
if (set_mask != 0)
|
if (set_mask != 0)
|
||||||
tb->mask_ptr = mask;
|
tb->mask_ptr = mask;
|
||||||
|
@ -802,15 +814,15 @@ ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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)
|
void *ta_buf, uint32_t *pnum)
|
||||||
{
|
{
|
||||||
struct radix_cfg *cfg;
|
struct addr_radix_cfg *cfg;
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
struct radix_node *rn;
|
struct radix_node *rn;
|
||||||
struct ta_buf_radix *tb;
|
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;
|
tb = (struct ta_buf_radix *)ta_buf;
|
||||||
|
|
||||||
if (tei->subtype == AF_INET)
|
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 */
|
/* Save entry value to @tei */
|
||||||
if (tei->subtype == AF_INET)
|
if (tei->subtype == AF_INET)
|
||||||
tei->value = ((struct radix_addr_entry *)rn)->value;
|
tei->value = ((struct addr_radix_entry *)rn)->value;
|
||||||
else
|
else
|
||||||
tei->value = ((struct radix_addr_xentry *)rn)->value;
|
tei->value = ((struct addr_radix_xentry *)rn)->value;
|
||||||
|
|
||||||
tb->ent_ptr = rn;
|
tb->ent_ptr = rn;
|
||||||
|
|
||||||
|
@ -871,17 +883,17 @@ struct table_algo addr_radix = {
|
||||||
.type = IPFW_TABLE_ADDR,
|
.type = IPFW_TABLE_ADDR,
|
||||||
.flags = TA_FLAG_DEFAULT,
|
.flags = TA_FLAG_DEFAULT,
|
||||||
.ta_buf_size = sizeof(struct ta_buf_radix),
|
.ta_buf_size = sizeof(struct ta_buf_radix),
|
||||||
.init = ta_init_radix,
|
.init = ta_init_addr_radix,
|
||||||
.destroy = ta_destroy_radix,
|
.destroy = ta_destroy_addr_radix,
|
||||||
.prepare_add = ta_prepare_add_radix,
|
.prepare_add = ta_prepare_add_addr_radix,
|
||||||
.prepare_del = ta_prepare_del_radix,
|
.prepare_del = ta_prepare_del_addr_radix,
|
||||||
.add = ta_add_radix,
|
.add = ta_add_addr_radix,
|
||||||
.del = ta_del_radix,
|
.del = ta_del_addr_radix,
|
||||||
.flush_entry = ta_flush_radix_entry,
|
.flush_entry = ta_flush_radix_entry,
|
||||||
.foreach = ta_foreach_radix,
|
.foreach = ta_foreach_addr_radix,
|
||||||
.dump_tentry = ta_dump_radix_tentry,
|
.dump_tentry = ta_dump_addr_radix_tentry,
|
||||||
.find_tentry = ta_find_radix_tentry,
|
.find_tentry = ta_find_addr_radix_tentry,
|
||||||
.dump_tinfo = ta_dump_radix_tinfo,
|
.dump_tinfo = ta_dump_addr_radix_tinfo,
|
||||||
.need_modify = ta_need_modify_radix,
|
.need_modify = ta_need_modify_radix,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4014,6 +4026,328 @@ struct table_algo addr_kfib = {
|
||||||
.print_config = ta_print_kfib_config,
|
.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
|
void
|
||||||
ipfw_table_algo_init(struct ip_fw_chain *ch)
|
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, &number_array, sz, &number_array.idx);
|
||||||
ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.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, &addr_kfib, sz, &addr_kfib.idx);
|
||||||
|
ipfw_add_table_algo(ch, &mac_radix, sz, &mac_radix.idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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, number_array.idx);
|
||||||
ipfw_del_table_algo(ch, flow_hash.idx);
|
ipfw_del_table_algo(ch, flow_hash.idx);
|
||||||
ipfw_del_table_algo(ch, addr_kfib.idx);
|
ipfw_del_table_algo(ch, addr_kfib.idx);
|
||||||
|
ipfw_del_table_algo(ch, mac_radix.idx);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue