From 81cac3906eb9c14f81e03b7bcb6893b8d30e5432 Mon Sep 17 00:00:00 2001 From: Arseny Smalyuk Date: Sat, 4 Jun 2022 19:12:29 +0300 Subject: [PATCH] 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 MFC after: 1 month Relnotes: yes Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D35103 --- sbin/ipfw/ipfw.8 | 38 ++- sbin/ipfw/ipfw2.c | 132 ++++++-- sbin/ipfw/ipfw2.h | 2 + sbin/ipfw/tables.c | 29 ++ sys/netinet/ip_fw.h | 33 +- sys/netpfil/ipfw/ip_fw2.c | 153 +++++---- sys/netpfil/ipfw/ip_fw_sockopt.c | 2 + sys/netpfil/ipfw/ip_fw_table.c | 55 +++- sys/netpfil/ipfw/ip_fw_table_algo.c | 484 +++++++++++++++++++++++----- 9 files changed, 736 insertions(+), 192 deletions(-) diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 0e663fa44bdf..db8a11525b4d 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -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 diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index e210cbfd02ec..7d820698a25d 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -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 = ""; + 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 @@ read_options: } 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 @@ read_options: 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"); diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 9a39c215692d..dd7699987434 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -177,6 +177,8 @@ enum tokens { TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, + TOK_DSTMAC, + TOK_SRCMAC, TOK_ALL, TOK_MASK, TOK_FLOW_MASK, diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c index 81cf7e392586..9e6390492e96 100644 --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -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); diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 4d3099a781a0..cbf03a5a6f8e 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -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 { diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index aaca2e2b2fcd..99d3a9c58cb7 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -2038,78 +2038,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); @@ -2155,6 +2164,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; diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 2658c371091d..9505fa70f69a 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -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); diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index 19bedbbe9b50..202a49840b38 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -2752,26 +2752,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; } } @@ -2805,6 +2798,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) { @@ -2955,6 +2956,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, diff --git a/sys/netpfil/ipfw/ip_fw_table_algo.c b/sys/netpfil/ipfw/ip_fw_table_algo.c index cc5cba386ace..8f8604af44d9 100644 --- a/sys/netpfil/ipfw/ip_fw_table_algo.c +++ b/sys/netpfil/ipfw/ip_fw_table_algo.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include /* ip_fw.h requires IFNAMSIZ */ #include #include @@ -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, }; @@ -4006,6 +4018,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) { @@ -4021,6 +4355,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 @@ -4033,4 +4368,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); }