The branch main has been updated by ae:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=81cac3906eb9c14f81e03b7bcb6893b8d30e5432

commit 81cac3906eb9c14f81e03b7bcb6893b8d30e5432
Author:     Arseny Smalyuk <smalu...@gmail.com>
AuthorDate: 2022-06-04 16:12:29 +0000
Commit:     Andrey V. Elsukov <a...@freebsd.org>
CommitDate: 2022-06-04 16:12:29 +0000

    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 = "<invalid>";
+               const char *arg;
 
-               if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
-                       arg = match_value(rule_options, lookup_key[d]);
+               arg = match_value(lookup_keys, a[1]);
                t = table_search_ctlv(fo->tstate,
                    ((const ipfw_insn *)cmd)->arg1);
                bprintf(bp, "lookup %s %s", arg, t);
@@ -1331,6 +1339,22 @@ print_mac(struct buf_pr *bp, const ipfw_insn_mac *mac)
        format_mac(bp, mac->addr + 6, mac->mask + 6);
 }
 
+static void
+print_mac_lookup(struct buf_pr *bp, const struct format_opts *fo,
+    const ipfw_insn *cmd)
+{
+       uint32_t len = F_LEN(cmd);
+       char *t;
+
+       bprintf(bp, " ");
+
+       t = table_search_ctlv(fo->tstate, cmd->arg1);
+       bprintf(bp, "table(%s", t);
+       if (len == F_INSN_SIZE(ipfw_insn_u32))
+               bprintf(bp, ",%u", ((const ipfw_insn_u32 *)cmd)->d[0]);
+       bprintf(bp, ")");
+}
+
 static void
 fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
 {
@@ -1518,6 +1542,14 @@ print_instruction(struct buf_pr *bp, const struct 
format_opts *fo,
                        bprintf(bp, " dst-ip6");
                print_ip6(bp, insntod(cmd, ip6));
                break;
+       case O_MAC_SRC_LOOKUP:
+               bprintf(bp, " src-mac");
+               print_mac_lookup(bp, fo, cmd);
+               break;
+       case O_MAC_DST_LOOKUP:
+               bprintf(bp, " dst-mac");
+               print_mac_lookup(bp, fo, cmd);
+               break;
        case O_FLOW6ID:
                print_flow6id(bp, insntod(cmd, u32));
                break;
@@ -2650,7 +2682,6 @@ list_static_range(struct cmdline_opts *co, struct 
format_opts *fo,
        int n, seen;
        struct ip_fw_rule *r;
        struct ip_fw_bcounter *cntr;
-       int c = 0;
 
        for (n = seen = 0; n < rcnt; n++,
            rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
@@ -2669,7 +2700,6 @@ list_static_range(struct cmdline_opts *co, struct 
format_opts *fo,
                if (r->rulenum >= fo->first && r->rulenum <= fo->last) {
                        show_static_rule(co, fo, bp, r, cntr);
                        printf("%s", bp->buf);
-                       c += rtlv->length;
                        bp_flush(bp);
                        seen++;
                }
@@ -2788,13 +2818,12 @@ ipfw_show_config(struct cmdline_opts *co, struct 
format_opts *fo,
        char *endptr;
        size_t readsz;
        struct buf_pr bp;
-       ipfw_obj_ctlv *ctlv, *tstate;
+       ipfw_obj_ctlv *ctlv;
        ipfw_obj_tlv *rbase;
 
        /*
         * Handle tablenames TLV first, if any
         */
-       tstate = NULL;
        rbase = NULL;
        dynbase = NULL;
        dynsz = 0;
@@ -3682,6 +3711,29 @@ add_dstip(ipfw_insn *cmd, char *av, int cblen, struct 
tidx *tstate)
        return cmd;
 }
 
+static ipfw_insn *
+add_srcmac(ipfw_insn *cmd, char *av, struct tidx *tstate)
+{
+
+       if (strncmp(av, "table(", 6) == 0)
+               fill_table(cmd, av, O_MAC_SRC_LOOKUP, tstate);
+       else
+               errx(EX_DATAERR, "only mac table lookup is supported %s", av);
+       return cmd;
+}
+
+static ipfw_insn *
+add_dstmac(ipfw_insn *cmd, char *av, struct tidx *tstate)
+{
+
+       if (strncmp(av, "table(", 6) == 0)
+               fill_table(cmd, av, O_MAC_DST_LOOKUP, tstate);
+       else
+               errx(EX_DATAERR, "only mac table lookup is supported %s", av);
+       return cmd;
+}
+
+
 static struct _s_x f_reserved_keywords[] = {
        { "altq",       TOK_OR },
        { "//",         TOK_OR },
@@ -4912,6 +4964,21 @@ 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 <string.h>
 #include <sysexits.h>
 
+#include <net/ethernet.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/ip_fw.h>
@@ -77,6 +78,7 @@ static int tables_foreach(table_cb_t *f, void *arg, int sort);
 
 static struct _s_x tabletypes[] = {
       { "addr",                IPFW_TABLE_ADDR },
+      { "mac",         IPFW_TABLE_MAC },
       { "iface",       IPFW_TABLE_INTERFACE },
       { "number",      IPFW_TABLE_NUMBER },
       { "flow",                IPFW_TABLE_FLOW },
@@ -1188,6 +1190,7 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, 
uint8_t type,
        char *p, *pp;
        int mask, af;
        struct in6_addr *paddr, tmp;
+       struct ether_addr *mac;
        struct tflow_entry *tfe;
        uint32_t key, *pkey;
        uint16_t port;
@@ -1234,6 +1237,24 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, 
uint8_t type,
                        af = AF_INET;
                }
                break;
+       case IPFW_TABLE_MAC:
+               /* Remove / if exists */
+               if ((p = strchr(arg, '/')) != NULL) {
+                       *p = '\0';
+                       mask = atoi(p + 1);
+               }
+
+               if (p != NULL && mask > 8 * ETHER_ADDR_LEN)
+                       errx(EX_DATAERR, "bad MAC mask width: %s",
+                           p + 1);
+
+               if ((mac = ether_aton(arg)) == NULL)
+                       errx(EX_DATAERR, "Incorrect MAC address");
+
+               memcpy(tentry->k.mac, mac->octet, ETHER_ADDR_LEN);
+               masklen = p ? mask : 8 * ETHER_ADDR_LEN;
+               af = AF_LINK;
+               break;
        case IPFW_TABLE_INTERFACE:
                /* Assume interface name. Copy significant data only */
                mask = MIN(strlen(arg), IF_NAMESIZE - 1);
@@ -1872,6 +1893,7 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry 
*tent)
 {
        char tbuf[128], pval[128];
        const char *comma;
+       const u_char *mac;
        void *paddr;
        struct tflow_entry *tfe;
 
@@ -1884,6 +1906,13 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry 
*tent)
                inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
                printf("%s/%u %s\n", tbuf, tent->masklen, pval);
                break;
+       case IPFW_TABLE_MAC:
+               /* MAC prefixes */
+               mac = tent->k.mac;
+               printf("%02x:%02x:%02x:%02x:%02x:%02x/%u %s\n",
+                   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+                   tent->masklen, pval);
+               break;
        case IPFW_TABLE_INTERFACE:
                /* Interface names */
                printf("%s %s\n", tent->k.iface, pval);
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 <sys/rmlock.h>
 #include <sys/socket.h>
 #include <sys/queue.h>
+#include <net/ethernet.h>
 #include <net/if.h>    /* ip_fw.h requires IFNAMSIZ */
 #include <net/radix.h>
 #include <net/route.h>
@@ -315,15 +316,17 @@ static int bdel(const void *key, void *base, size_t 
nmemb, size_t size,
  */
 #define KEY_LEN(v)     *((uint8_t *)&(v))
 /*
- * Do not require radix to compare more than actual IPv4/IPv6 address
+ * Do not require radix to compare more than actual IPv4/IPv6/MAC address
  */
 #define KEY_LEN_INET   (offsetof(struct sockaddr_in, sin_addr) + 
sizeof(in_addr_t))
 #define KEY_LEN_INET6  (offsetof(struct sa_in6, sin6_addr) + sizeof(struct 
in6_addr))
+#define KEY_LEN_MAC    (offsetof(struct sa_mac, mac_addr) + ETHER_ADDR_LEN)
 
 #define OFF_LEN_INET   (8 * offsetof(struct sockaddr_in, sin_addr))
 #define OFF_LEN_INET6  (8 * offsetof(struct sa_in6, sin6_addr))
+#define OFF_LEN_MAC    (8 * offsetof(struct sa_mac, mac_addr))
 
-struct radix_addr_entry {
+struct addr_radix_entry {
        struct radix_node       rn[2];
        struct sockaddr_in      addr;
        uint32_t                value;
@@ -337,20 +340,25 @@ struct sa_in6 {
        struct in6_addr         sin6_addr;
 };
 
-struct radix_addr_xentry {
+struct addr_radix_xentry {
        struct radix_node       rn[2];
        struct sa_in6           addr6;
        uint32_t                value;
        uint8_t                 masklen;
 };
 
-struct radix_cfg {
+struct addr_radix_cfg {
        struct radix_node_head  *head4;
        struct radix_node_head  *head6;
        size_t                  count4;
        size_t                  count6;
 };
 
+struct sa_mac {
+       uint8_t                 mac_len;
+       struct ether_addr       mac_addr;
+};
+
 struct ta_buf_radix
 {
        void *ent_ptr;
@@ -365,32 +373,36 @@ struct ta_buf_radix
                        struct sa_in6 sa;
                        struct sa_in6 ma;
                } a6;
+               struct {
+                       struct sa_mac sa;
+                       struct sa_mac ma;
+               } mac;
        } addr;
 };
 
-static int ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
+static int ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t 
keylen,
     uint32_t *val);
-static int ta_init_radix(struct ip_fw_chain *ch, void **ta_state,
+static int ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state,
     struct table_info *ti, char *data, uint8_t tflags);
 static int flush_radix_entry(struct radix_node *rn, void *arg);
-static void ta_destroy_radix(void *ta_state, struct table_info *ti);
-static void ta_dump_radix_tinfo(void *ta_state, struct table_info *ti,
+static void ta_destroy_addr_radix(void *ta_state, struct table_info *ti);
+static void ta_dump_addr_radix_tinfo(void *ta_state, struct table_info *ti,
     ipfw_ta_tinfo *tinfo);
-static int ta_dump_radix_tentry(void *ta_state, struct table_info *ti,
+static int ta_dump_addr_radix_tentry(void *ta_state, struct table_info *ti,
     void *e, ipfw_obj_tentry *tent);
-static int ta_find_radix_tentry(void *ta_state, struct table_info *ti,
+static int ta_find_addr_radix_tentry(void *ta_state, struct table_info *ti,
     ipfw_obj_tentry *tent);
-static void ta_foreach_radix(void *ta_state, struct table_info *ti,
+static void ta_foreach_addr_radix(void *ta_state, struct table_info *ti,
     ta_foreach_f *f, void *arg);
-static void tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
+static void tei_to_sockaddr_ent_addr(struct tentry_info *tei, struct sockaddr 
*sa,
     struct sockaddr *ma, int *set_mask);
-static int ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info 
*tei,
+static int ta_prepare_add_addr_radix(struct ip_fw_chain *ch, struct 
tentry_info *tei,
     void *ta_buf);
-static int ta_add_radix(void *ta_state, struct table_info *ti,
+static int ta_add_addr_radix(void *ta_state, struct table_info *ti,
     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
-static int ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info 
*tei,
+static int ta_prepare_del_addr_radix(struct ip_fw_chain *ch, struct 
tentry_info *tei,
     void *ta_buf);
-static int ta_del_radix(void *ta_state, struct table_info *ti,
+static int ta_del_addr_radix(void *ta_state, struct table_info *ti,
     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
 static void ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info 
*tei,
     void *ta_buf);
@@ -398,29 +410,29 @@ static int ta_need_modify_radix(void *ta_state, struct 
table_info *ti,
     uint32_t count, uint64_t *pflags);
 
 static int
-ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
+ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t keylen,
     uint32_t *val)
 {
        struct radix_node_head *rnh;
 
        if (keylen == sizeof(in_addr_t)) {
-               struct radix_addr_entry *ent;
+               struct addr_radix_entry *ent;
                struct sockaddr_in sa;
                KEY_LEN(sa) = KEY_LEN_INET;
                sa.sin_addr.s_addr = *((in_addr_t *)key);
                rnh = (struct radix_node_head *)ti->state;
-               ent = (struct radix_addr_entry *)(rnh->rnh_matchaddr(&sa, 
&rnh->rh));
+               ent = (struct addr_radix_entry *)(rnh->rnh_matchaddr(&sa, 
&rnh->rh));
                if (ent != NULL) {
                        *val = ent->value;
                        return (1);
                }
-       } else {
-               struct radix_addr_xentry *xent;
+       } else if (keylen == sizeof(struct in6_addr)) {
+               struct addr_radix_xentry *xent;
                struct sa_in6 sa6;
                KEY_LEN(sa6) = KEY_LEN_INET6;
                memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
                rnh = (struct radix_node_head *)ti->xstate;
-               xent = (struct radix_addr_xentry *)(rnh->rnh_matchaddr(&sa6, 
&rnh->rh));
+               xent = (struct addr_radix_xentry *)(rnh->rnh_matchaddr(&sa6, 
&rnh->rh));
                if (xent != NULL) {
                        *val = xent->value;
                        return (1);
@@ -434,10 +446,10 @@ ta_lookup_radix(struct table_info *ti, void *key, 
uint32_t keylen,
  * New table
  */
 static int
-ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
+ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info 
*ti,
     char *data, uint8_t tflags)
 {
-       struct radix_cfg *cfg;
+       struct addr_radix_cfg *cfg;
 
        if (!rn_inithead(&ti->state, OFF_LEN_INET))
                return (ENOMEM);
@@ -446,10 +458,10 @@ ta_init_radix(struct ip_fw_chain *ch, void **ta_state, 
struct table_info *ti,
                return (ENOMEM);
        }
 
-       cfg = malloc(sizeof(struct radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
+       cfg = malloc(sizeof(struct addr_radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
 
        *ta_state = cfg;
-       ti->lookup = ta_lookup_radix;
+       ti->lookup = ta_lookup_addr_radix;
 
        return (0);
 }
@@ -458,9 +470,9 @@ static int
 flush_radix_entry(struct radix_node *rn, void *arg)
 {
*** 609 LINES SKIPPED ***

Reply via email to