summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Otavio O Souza <luiz@netgate.com>2016-10-17 11:42:07 -0500
committerLuiz Souza <luiz@netgate.com>2017-07-15 10:58:45 -0500
commit0b480d7d582297a3f8afd34ea8be702bba050a8f (patch)
tree6674f20faafd646d2af6c11e4bb292984311afbe
parent97e300f65864a873eddba40a0b7b291eb04a6be7 (diff)
downloadFreeBSD-src-0b480d7d582297a3f8afd34ea8be702bba050a8f.zip
FreeBSD-src-0b480d7d582297a3f8afd34ea8be702bba050a8f.tar.gz
Add ipfw support to MAC address tables.
The l2 filter implementation on ipfw works with MAC address pairs as it happens on wire (first destination and then source). The table entries works in the same way, but the MAC address pair has to be passed in a single argument: $ ipfw table create l2 type mac $ ipfw table add "00:01:02:03:04:05 0a:0b:0c:0d:0e:0f" added: 00:01:02:03:04:05 0a:0b:0c:0d:0e:0f 0 $ ipfw table add "00:01:02:03:04:05 any" added: 00:01:02:03:04:05 any 0 $ ipfw table l2 add "any 0a:0b:0c:0d:0e:0f" added: any 0a:0b:0c:0d:0e:0f 0 The MAC tables can also hold an optinal value used to implement additional features (skipto, fib, pipe, tag, nat, ...). $ ipfw table l2 add "00:01:02:03:04:05 0a:0b:0c:0d:0e:ff" 1234 added: 00:01:02:03:04:05 0a:0b:0c:0d:0e:ff 1234 $ ipfw table l2 list --- table(l2), set(0) --- 00:01:02:03:04:05 0a:0b:0c:0d:0e:0f 0 any 0a:0b:0c:0d:0e:0f 0 00:01:02:03:04:05 any 0 00:01:02:03:04:05 0a:0b:0c:0d:0e:ff 1234 Rule example: $ ipfw add pass MAC 1:2:3:4:5:6 2:3:4:5:6:7 via igb0 00100 allow ip from any to any MAC 01:02:03:04:05:06 02:03:04:05:06:07 via igb0 $ ipfw add pass MAC table\(l2\) via igb0 00000 allow ip from any to any MAC table(l2) via igb0 $ ipfw list 00100 allow ip from any to any MAC 01:02:03:04:05:06 02:03:04:05:06:07 via igb0 00200 allow ip from any to any MAC table(l2) via igb0 00300 allow ip from any to any 65535 deny ip from any to any (cherry picked from commit 1fc9408b335ef6e8863019212c12a4bc99ed8e75)
-rw-r--r--sbin/ipfw/ipfw2.c27
-rw-r--r--sbin/ipfw/ipfw2.h2
-rw-r--r--sbin/ipfw/tables.c48
-rw-r--r--sys/netinet/ip_fw.h11
-rw-r--r--sys/netpfil/ipfw/ip_fw2.c12
-rw-r--r--sys/netpfil/ipfw/ip_fw_sockopt.c12
-rw-r--r--sys/netpfil/ipfw/ip_fw_table.c19
-rw-r--r--sys/netpfil/ipfw/ip_fw_table_algo.c400
8 files changed, 523 insertions, 8 deletions
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 29f7384..91cb21a 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -1875,6 +1875,18 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo,
if (cmd->len & F_NOT && cmd->opcode != O_IN)
bprintf(bp, " not");
switch(cmd->opcode) {
+ case O_MACADDR2_LOOKUP: {
+ char *t;
+ uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
+
+ t = table_search_ctlv(fo->tstate, cmd->arg1);
+ bprintf(bp, " MAC table(%s", t);
+ if (cmd->len == F_INSN_SIZE(ipfw_insn_u32))
+ bprintf(bp, ",%u", *a);
+ bprintf(bp, ")");
+ }
+ break;
+
case O_MACADDR2: {
ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
@@ -3298,7 +3310,7 @@ fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate)
errx(EX_DATAERR, "bad ip address ``%s''", arg);
}
-static void
+void
get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
{
int i;
@@ -3410,13 +3422,19 @@ fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
* two microinstructions, and returns the pointer to the last one.
*/
static ipfw_insn *
-add_mac(ipfw_insn *cmd, char *av[], int cblen)
+add_mac(ipfw_insn *cmd, char *av[], int cblen, struct tidx *tstate)
{
ipfw_insn_mac *mac;
if ( ( av[0] == NULL ) || ( av[1] == NULL ) )
errx(EX_DATAERR, "MAC dst src");
+ if (strncmp(av[0], "table(", 6) == 0) {
+ fill_table(cmd, av[0], O_MACADDR2_LOOKUP, tstate);
+ CHECK_CMDLEN;
+ return (cmd);
+ }
+
cmd->opcode = O_MACADDR2;
cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
CHECK_CMDLEN;
@@ -4730,8 +4748,9 @@ read_options:
break;
case TOK_MAC:
- if (add_mac(cmd, av, cblen))
- av += 2;
+ if (add_mac(cmd, av, cblen, tstate))
+ av += ((strncmp(*av, "table(", 6) == 0) ?
+ 1 : 2);
break;
case TOK_MACTYPE:
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 13018f8..facf674 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -338,6 +338,8 @@ struct in6_addr;
void n2mask(struct in6_addr *mask, int n);
int contigmask(uint8_t *p, int len);
+void get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask);
+
/*
* Forward declarations to avoid include way too many headers.
* C does not allow duplicated typedefs, so we use the base struct
diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c
index 9aad4d9..5be4d4d 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>
@@ -78,6 +79,7 @@ static int tables_foreach(table_cb_t *f, void *arg, int sort);
static struct _s_x tabletypes[] = {
{ "addr", IPFW_TABLE_ADDR },
{ "iface", IPFW_TABLE_INTERFACE },
+ { "mac", IPFW_TABLE_MAC2 },
{ "number", IPFW_TABLE_NUMBER },
{ "flow", IPFW_TABLE_FLOW },
{ NULL, 0 }
@@ -1185,6 +1187,26 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
af = AF_INET;
}
break;
+ case IPFW_TABLE_MAC2: {
+ char *src, *dst;
+ struct mac_entry *mac;
+
+ dst = arg;
+ if ((p = strchr(arg, ' ')) == NULL)
+ errx(EX_DATAERR, "bad mac address pair: %s", arg);
+ *p = '\0';
+ src = p + 1;
+
+ mac = (struct mac_entry *)&tentry->k.mac;
+ get_mac_addr_mask(dst, mac->addr, mac->mask); /* dst */
+ get_mac_addr_mask(src, &(mac->addr[ETHER_ADDR_LEN]),
+ &(mac->mask[ETHER_ADDR_LEN])); /* src */
+
+ masklen = ETHER_ADDR_LEN * 8;
+ type = IPFW_TABLE_MAC2;
+ af = AF_LINK;
+ }
+ break;
case IPFW_TABLE_INTERFACE:
/* Assume interface name. Copy significant data only */
mask = MIN(strlen(arg), IF_NAMESIZE - 1);
@@ -1814,6 +1836,26 @@ table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
}
static void
+print_mac(uint8_t *addr, uint8_t *mask)
+{
+ int l;
+
+ l = contigmask(mask, 48);
+ if (l == 0)
+ printf(" any");
+ else {
+ printf(" %02x:%02x:%02x:%02x:%02x:%02x",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ if (l == -1)
+ printf("&%02x:%02x:%02x:%02x:%02x:%02x",
+ mask[0], mask[1], mask[2],
+ mask[3], mask[4], mask[5]);
+ else if (l < 48)
+ printf("/%d", l);
+ }
+}
+
+static void
table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
{
char *comma, tbuf[128], pval[128];
@@ -1829,6 +1871,12 @@ 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_MAC2:
+ /* Ethernet MAC address */
+ print_mac(tent->k.mac.addr, tent->k.mac.mask);
+ print_mac(tent->k.mac.addr + 6, tent->k.mac.mask + 6);
+ printf(" %s\n", 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 ddee5bf..adbc41b 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -283,6 +283,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_EXTERNAL_INSTANCE, /* arg1=id of eaction handler instance */
O_EXTERNAL_DATA, /* variable length data */
+ O_MACADDR2_LOOKUP, /* arg1=table number, u32=value */
+
O_LAST_OPCODE /* not an opcode! */
};
@@ -737,7 +739,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_MAC2 5 /* Table for holding 2 mac addresses */
+#define IPFW_TABLE_MAXTYPE 5 /* Maximum valid number */
#define IPFW_TABLE_CIDR IPFW_TABLE_ADDR /* compat */
@@ -849,6 +852,11 @@ struct tflow_entry {
} a;
};
+struct mac_entry {
+ u_char addr[12]; /* dst[6] + src[6] */
+ u_char mask[12]; /* dst[6] + src[6] */
+};
+
typedef struct _ipfw_table_value {
uint32_t tag; /* O_TAG/O_TAGGED */
uint32_t pipe; /* O_PIPE/O_QUEUE */
@@ -882,6 +890,7 @@ typedef struct _ipfw_obj_tentry {
uint32_t key; /* uid/gid/port */
struct in6_addr addr6; /* IPv6 address */
char iface[IF_NAMESIZE]; /* interface name */
+ struct mac_entry mac; /* 2 mac addr:mask */
struct tflow_entry flow;
} k;
union {
diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c
index a66d5e7..1228f47 100644
--- a/sys/netpfil/ipfw/ip_fw2.c
+++ b/sys/netpfil/ipfw/ip_fw2.c
@@ -1388,6 +1388,18 @@ do { \
chain, &tablearg);
break;
+ case O_MACADDR2_LOOKUP:
+ if (args->eh != NULL) { /* have MAC header */
+ uint32_t v = 0;
+ match = ipfw_lookup_table_extended(chain,
+ cmd->arg1, 0, args->eh, &v);
+ if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
+ match = ((ipfw_insn_u32 *)cmd)->d[0] == v;
+ if (match)
+ tablearg = v;
+ }
+ break;
+
case O_MACADDR2:
if (args->eh != NULL) { /* have MAC header */
u_int32_t *want = (u_int32_t *)
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c
index e6d7487..a9f8e70 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -1857,10 +1857,22 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
goto bad_size;
ci->object_opcodes++;
break;
+
case O_MACADDR2:
if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
goto bad_size;
break;
+ case O_MACADDR2_LOOKUP:
+ if (cmd->arg1 >= V_fw_tables_max) {
+ printf("ipfw: invalid table number %d\n",
+ cmd->arg1);
+ return (EINVAL);
+ }
+ if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
+ cmdlen != F_INSN_SIZE(ipfw_insn_u32))
+ goto bad_size;
+ ci->object_opcodes++;
+ break;
case O_NOP:
case O_IPID:
diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c
index 4445683..ed500b1 100644
--- a/sys/netpfil/ipfw/ip_fw_table.c
+++ b/sys/netpfil/ipfw/ip_fw_table.c
@@ -2808,6 +2808,15 @@ classify_flow(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
return (0);
}
+static int
+classify_mac(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
+{
+ *puidx = cmd->arg1;
+ *ptype = IPFW_TABLE_MAC2;
+
+ return (0);
+}
+
static void
update_arg1(ipfw_insn *cmd, uint16_t idx)
{
@@ -2959,6 +2968,16 @@ static struct opcode_obj_rewrite opcodes[] = {
.manage_sets = table_manage_sets,
},
{
+ .opcode = O_MACADDR2_LOOKUP,
+ .etlv = IPFW_TLV_TBL_NAME,
+ .classifier = classify_mac,
+ .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,
.classifier = classify_via,
diff --git a/sys/netpfil/ipfw/ip_fw_table_algo.c b/sys/netpfil/ipfw/ip_fw_table_algo.c
index 97bc879..0444e2a 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>
@@ -71,7 +72,8 @@ __FBSDID("$FreeBSD$");
* Algo init:
* * struct table_algo has to be filled with:
* name: "type:algoname" format, e.g. "addr:radix". Currently
- * there are the following types: "addr", "iface", "number" and "flow".
+ * there are the following types: "addr", "iface", "mac", "number" and
+ * "flow".
* type: one of IPFW_TABLE_* types
* flags: one or more TA_FLAGS_*
* ta_buf_size: size of structure used to store add/del item state.
@@ -4078,6 +4080,398 @@ struct table_algo addr_kfib = {
.print_config = ta_print_kfib_config,
};
+/*
+ * mac:hash cmds
+ *
+ * ti->data:
+ * [unused][log2hsize]
+ * [ 24][ 8]
+ *
+ */
+
+struct mhashentry;
+
+SLIST_HEAD(mhashbhead, mhashentry);
+
+struct mhash_cfg {
+ struct mhashbhead *head;
+ size_t size;
+ size_t items;
+};
+
+struct macdata {
+ u_char addr[12]; /* dst[6] + src[6] */
+ u_char mask[12]; /* dst[6] + src[6] */
+ uint32_t value;
+};
+
+struct mhashentry {
+ SLIST_ENTRY(mhashentry) next;
+ struct macdata *mac;
+};
+
+struct ta_buf_mhash {
+ void *ent_ptr;
+ struct macdata mac;
+};
+
+static __inline uint32_t
+hash_mac2(u_char *mac, int hsize)
+{
+ uint32_t i;
+
+ i = ((mac[2] << 16) | (mac[1] << 8) | (mac[0] << 0)) ^
+ ((mac[5] << 16) | (mac[4] << 8) | (mac[3] << 0)) ^
+ ((mac[8] << 16) | (mac[7] << 8) | (mac[6] << 0)) ^
+ ((mac[11] << 16) | (mac[10] << 8) | (mac[9] << 0));
+
+ return (i % (hsize - 1));
+}
+
+static void
+ta_print_mhash_config(void *ta_state, struct table_info *ti, char *buf,
+ size_t bufsize)
+{
+ snprintf(buf, bufsize, "%s", "mac:hash");
+}
+
+static __inline int
+ta_lookup_find_mhash(struct mhashbhead *head, uint32_t hash2, struct macdata *mac,
+ uint32_t *val)
+{
+ struct mhashentry *ent;
+
+ SLIST_FOREACH(ent, &head[hash2], next) {
+ if (memcmp(&ent->mac->addr, mac->addr, sizeof(mac->addr)) != 0)
+ continue;
+ *val = ent->mac->value;
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+ta_lookup_mhash(struct table_info *ti, void *key, uint32_t keylen,
+ uint32_t *val)
+{
+ struct macdata mac;
+ struct mhashbhead *head;
+ uint32_t hash2, hsize;
+
+ /*
+ * Look three times for a MAC is still faster than looking at whole
+ * table (128 entries by default).
+ */
+ head = (struct mhashbhead *)ti->state;
+ hsize = 1 << (ti->data & 0xFF);
+ hash2 = hash_mac2(key, hsize);
+ if (ta_lookup_find_mhash(head, hash2, (struct macdata *)key, val) == 1)
+ return (1);
+
+ /* src any */
+ memcpy(mac.addr, key, 6);
+ memset(mac.addr + 6, 0, 6);
+ hash2 = hash_mac2(mac.addr, hsize);
+ if (ta_lookup_find_mhash(head, hash2, &mac, val) == 1)
+ return (1);
+
+ /* dst any */
+ memset(mac.addr, 0, 6);
+ memcpy(mac.addr + 6, (uintptr_t *)key + 6, 6);
+ hash2 = hash_mac2(mac.addr, hsize);
+ if (ta_lookup_find_mhash(head, hash2, &mac, val) == 1)
+ return (1);
+
+ return (0);
+}
+
+static int
+ta_init_mhash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
+ char *data, uint8_t tflags)
+{
+ int i;
+ struct mhash_cfg *cfg;
+
+ cfg = malloc(sizeof(struct mhash_cfg), M_IPFW, M_WAITOK | M_ZERO);
+
+ cfg->size = 128;
+ cfg->head = malloc(sizeof(struct mhashbhead) * cfg->size, M_IPFW,
+ M_WAITOK | M_ZERO);
+ for (i = 0; i < cfg->size; i++)
+ SLIST_INIT(&cfg->head[i]);
+ *ta_state = cfg;
+ ti->state = cfg->head;
+ ti->data = ta_log2(cfg->size);
+ ti->lookup = ta_lookup_mhash;
+
+ return (0);
+}
+
+static void
+ta_destroy_mhash(void *ta_state, struct table_info *ti)
+{
+ int i;
+ struct mhash_cfg *cfg;
+ struct mhashentry *ent, *ent_next;
+
+ cfg = (struct mhash_cfg *)ta_state;
+
+ for (i = 0; i < cfg->size; i++)
+ SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next) {
+ free(ent->mac, M_IPFW_TBL);
+ free(ent, M_IPFW_TBL);
+ }
+
+ free(cfg->head, M_IPFW);
+
+ free(cfg, M_IPFW);
+}
+
+static void
+ta_foreach_mhash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
+ void *arg)
+{
+ struct mhash_cfg *cfg;
+ struct mhashentry *ent, *ent_next;
+ int i;
+
+ cfg = (struct mhash_cfg *)ta_state;
+
+ for (i = 0; i < cfg->size; i++)
+ SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
+ f(ent, arg);
+}
+
+static int
+ta_dump_mhash_tentry(void *ta_state, struct table_info *ti, void *e,
+ ipfw_obj_tentry *tent)
+{
+ struct macdata *mac;
+ struct mhash_cfg *cfg;
+
+ cfg = (struct mhash_cfg *)ta_state;
+ mac = ((struct mhashentry *)e)->mac;
+
+ memcpy(&tent->k.mac, mac->addr, sizeof(*mac) - sizeof(uint32_t));
+ tent->masklen = ETHER_ADDR_LEN * 8;
+ tent->subtype = AF_LINK;
+ tent->v.kidx = mac->value;
+
+ return (0);
+}
+
+static int
+ta_find_mhash_tentry(void *ta_state, struct table_info *ti,
+ ipfw_obj_tentry *tent)
+{
+ struct macdata mac;
+ struct mhash_cfg *cfg;
+ struct mhashentry ent, *tmp;
+ struct tentry_info tei;
+ uint32_t hash2;
+
+ cfg = (struct mhash_cfg *)ta_state;
+
+ memset(&ent, 0, sizeof(ent));
+ memset(&mac, 0, sizeof(mac));
+ memset(&tei, 0, sizeof(tei));
+
+ tei.paddr = &tent->k.mac;
+ tei.subtype = AF_LINK;
+ ent.mac = &mac;
+
+ memcpy(mac.addr, tei.paddr, sizeof(mac) - sizeof(uint32_t));
+
+ /* Check for existence */
+ hash2 = hash_mac2(mac.addr, cfg->size);
+ SLIST_FOREACH(tmp, &cfg->head[hash2], next) {
+ if (memcmp(&tmp->mac->addr, &mac.addr, sizeof(mac.addr)) != 0)
+ continue;
+ ta_dump_mhash_tentry(ta_state, ti, tmp, tent);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static void
+ta_dump_mhash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
+{
+ struct mhash_cfg *cfg;
+
+ cfg = (struct mhash_cfg *)ta_state;
+
+ tinfo->taclass4 = IPFW_TACLASS_HASH;
+ tinfo->size4 = cfg->size;
+ tinfo->count4 = cfg->items;
+ tinfo->itemsize4 = sizeof(struct mhashentry) + sizeof(struct macdata) -
+ sizeof(void *);
+}
+
+static int
+ta_prepare_add_mhash(struct ip_fw_chain *ch, struct tentry_info *tei,
+ void *ta_buf)
+{
+ struct ta_buf_mhash *tb;
+ struct mhashentry *ent;
+ struct macdata *mac;
+
+ if (tei->subtype != AF_LINK)
+ return (EINVAL);
+
+ tb = (struct ta_buf_mhash *)ta_buf;
+
+ ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
+ mac = malloc(sizeof(*mac), M_IPFW_TBL, M_WAITOK | M_ZERO);
+
+ memcpy(mac->addr, tei->paddr, sizeof(*mac) - sizeof(uint32_t));
+
+ ent->mac = mac;
+ tb->ent_ptr = ent;
+
+ return (0);
+}
+
+static int
+ta_add_mhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
+ void *ta_buf, uint32_t *pnum)
+{
+ int exists;
+ struct macdata *mac;
+ struct mhash_cfg *cfg;
+ struct mhashentry *ent, *tmp;
+ struct ta_buf_mhash *tb;
+ uint32_t hash2, value;
+
+ cfg = (struct mhash_cfg *)ta_state;
+ tb = (struct ta_buf_mhash *)ta_buf;
+ ent = (struct mhashentry *)tb->ent_ptr;
+ mac = ent->mac;
+ exists = 0;
+
+ /* Read current value from @tei */
+ mac->value = tei->value;
+
+ if (tei->subtype != AF_LINK)
+ return (EINVAL);
+
+ /* Check for existence */
+ hash2 = hash_mac2(mac->addr, cfg->size);
+ SLIST_FOREACH(tmp, &cfg->head[hash2], next) {
+ if (memcmp(&tmp->mac->addr, &mac->addr,
+ sizeof(mac->addr)) == 0) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (exists == 1) {
+ if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
+ return (EEXIST);
+ /* Record already exists. Update value if we're asked to */
+ value = tmp->mac->value;
+ tmp->mac->value = tei->value;
+ tei->value = value;
+ /* Indicate that update has happened instead of addition */
+ tei->flags |= TEI_FLAGS_UPDATED;
+ *pnum = 0;
+ } else {
+ if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+ return (EFBIG);
+ SLIST_INSERT_HEAD(&cfg->head[hash2], ent, next);
+ tb->ent_ptr = NULL;
+ *pnum = 1;
+
+ /* Update counters */
+ cfg->items++;
+ }
+
+ return (0);
+}
+
+static int
+ta_prepare_del_mhash(struct ip_fw_chain *ch, struct tentry_info *tei,
+ void *ta_buf)
+{
+ struct ta_buf_mhash *tb;
+
+ tb = (struct ta_buf_mhash *)ta_buf;
+
+ memcpy(tb->mac.addr, tei->paddr, sizeof(tb->mac.addr));
+
+ return (0);
+}
+
+static int
+ta_del_mhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
+ void *ta_buf, uint32_t *pnum)
+{
+ struct macdata *mac;
+ struct mhash_cfg *cfg;
+ struct mhashentry *tmp, *tmp_next;
+ struct ta_buf_mhash *tb;
+ uint32_t hash2;
+
+ cfg = (struct mhash_cfg *)ta_state;
+ tb = (struct ta_buf_mhash *)ta_buf;
+ mac = &tb->mac;
+
+ if (tei->masklen != ETHER_ADDR_LEN * 8)
+ return (EINVAL);
+
+ hash2 = hash_mac2(mac->addr, cfg->size);
+ SLIST_FOREACH_SAFE(tmp, &cfg->head[hash2], next, tmp_next) {
+ if (memcmp(&tmp->mac->addr, &mac->addr, sizeof(mac->addr)) != 0)
+ continue;
+
+ SLIST_REMOVE(&cfg->head[hash2], tmp, mhashentry, next);
+ cfg->items--;
+ tb->ent_ptr = tmp;
+ tei->value = tmp->mac->value;
+ *pnum = 1;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static void
+ta_flush_mhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
+ void *ta_buf)
+{
+ struct mhashentry *ent;
+ struct ta_buf_mhash *tb;
+
+ tb = (struct ta_buf_mhash *)ta_buf;
+
+ if (tb->ent_ptr != NULL) {
+ ent = (struct mhashentry *)tb->ent_ptr;
+ free(ent->mac, M_IPFW_TBL);
+ free(tb->ent_ptr, M_IPFW_TBL);
+ tb->ent_ptr = NULL;
+ }
+}
+
+struct table_algo mac_hash = {
+ .name = "mac:hash",
+ .type = IPFW_TABLE_MAC2,
+ .flags = TA_FLAG_DEFAULT,
+ .ta_buf_size = sizeof(struct ta_buf_mhash),
+ .print_config = ta_print_mhash_config,
+ .init = ta_init_mhash,
+ .destroy = ta_destroy_mhash,
+ .prepare_add = ta_prepare_add_mhash,
+ .prepare_del = ta_prepare_del_mhash,
+ .add = ta_add_mhash,
+ .del = ta_del_mhash,
+ .flush_entry = ta_flush_mhash_entry,
+ .foreach = ta_foreach_mhash,
+ .dump_tentry = ta_dump_mhash_tentry,
+ .find_tentry = ta_find_mhash_tentry,
+ .dump_tinfo = ta_dump_mhash_tinfo,
+};
+
void
ipfw_table_algo_init(struct ip_fw_chain *ch)
{
@@ -4090,6 +4484,7 @@ ipfw_table_algo_init(struct ip_fw_chain *ch)
ipfw_add_table_algo(ch, &addr_radix, sz, &addr_radix.idx);
ipfw_add_table_algo(ch, &addr_hash, sz, &addr_hash.idx);
ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);
+ ipfw_add_table_algo(ch, &mac_hash, sz, &mac_hash.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, &addr_kfib, sz, &addr_kfib.idx);
@@ -4102,9 +4497,8 @@ ipfw_table_algo_destroy(struct ip_fw_chain *ch)
ipfw_del_table_algo(ch, addr_radix.idx);
ipfw_del_table_algo(ch, addr_hash.idx);
ipfw_del_table_algo(ch, iface_idx.idx);
+ ipfw_del_table_algo(ch, mac_hash.idx);
ipfw_del_table_algo(ch, number_array.idx);
ipfw_del_table_algo(ch, flow_hash.idx);
ipfw_del_table_algo(ch, addr_kfib.idx);
}
-
-
OpenPOWER on IntegriCloud