diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1610,6 +1610,20 @@ .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 @@ 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 @@ .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 @@ 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 @@ 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 @@ 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.h b/sbin/ipfw/ipfw2.h --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -177,6 +177,8 @@ TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, + TOK_DSTMAC, + TOK_SRCMAC, TOK_ALL, TOK_MASK, TOK_FLOW_MASK, diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -305,7 +305,7 @@ */ static int lookup_key[] = { TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, - TOK_UID, TOK_JAIL, TOK_DSCP, -1 }; + TOK_UID, TOK_JAIL, TOK_DSCP, TOK_DSTMAC, TOK_SRCMAC, -1 }; static struct _s_x rule_options[] = { { "tagged", TOK_TAGGED }, @@ -358,6 +358,8 @@ { "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 }, @@ -378,6 +380,8 @@ { "dst-ip6", TOK_DSTIP6}, { "src-ipv6", TOK_SRCIP6}, { "src-ip6", TOK_SRCIP6}, + { "dst-mac", TOK_DSTMAC}, + { "src-mac", TOK_SRCMAC}, { "lookup", TOK_LOOKUP}, { "flow", TOK_FLOW}, { "defer-action", TOK_SKIPACTION }, @@ -1331,6 +1335,22 @@ 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 +1538,14 @@ 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; @@ -3682,6 +3710,29 @@ 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 +4963,21 @@ } 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 || diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c --- 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 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 @@ 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 @@ 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 @@ { char tbuf[128], pval[128]; const char *comma; + const u_char *mac; void *paddr; struct tflow_entry *tfe; @@ -1884,6 +1906,13 @@ 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 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -295,6 +295,9 @@ 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! */ }; @@ -754,7 +757,8 @@ #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 +776,9 @@ #define IPFW_VTYPE_NH4 0x00000200 /* IPv4 nexthop */ #define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */ +/* MAC/InfiniBand/etc address length */ +#define L2_ADDR_LEN 20 + typedef struct _ipfw_table_entry { in_addr_t addr; /* network address */ u_int32_t value; /* value */ @@ -899,6 +906,7 @@ uint32_t key; /* uid/gid/port */ struct in6_addr addr6; /* IPv6 address */ char iface[IF_NAMESIZE]; /* interface name */ + u_char mac[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 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -2047,14 +2047,25 @@ vidx = ((ipfw_insn_u32 *)cmd)->d[1]; if (vidx != 4 /* uid */ && vidx != 5 /* jail */ && + vidx != 7 /* dst mac */ && + vidx != 8 /* src mac */ && is_ipv6 == 0 && is_ipv4 == 0) break; + + if ((vidx == 7 /* dst-mac */ || + vidx == 8 /* src-mac */) && + (args->flags & IPFW_ARGS_ETHER) == 0) + break; /* only for L2 */ + /* Determine key length */ if (vidx == 0 /* dst-ip */ || vidx == 1 /* src-ip */) keylen = is_ipv6 ? sizeof(struct in6_addr): sizeof(in_addr_t); + else if (vidx == 7 /* dst-mac */ || + vidx == 8 /* src-mac */) + keylen = ETHER_ADDR_LEN; else { keylen = sizeof(key); pkey = &key; @@ -2087,6 +2098,10 @@ key = dst_port; else key = src_port; + } else if (cmd->arg1 == 7 /* dst-mac */) { + pkey = eh->ether_dhost; + } else if (cmd->arg1 == 8 /* src-mac */) { + pkey = eh->ether_shost; } #ifndef USERSPACE else if (vidx == 4 /* uid */ || @@ -2155,6 +2170,35 @@ break; } + case O_MAC_SRC_LOOKUP: + case O_MAC_DST_LOOKUP: + { + void *pkey; + uint32_t vidx; + uint16_t keylen = ETHER_ADDR_LEN; + + if ((args->flags & IPFW_ARGS_ETHER) == 0) + break; /* only for L2 */ + + 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 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -1909,6 +1909,8 @@ 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 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -2773,6 +2773,12 @@ /* dscp */ *ptype = IPFW_TABLE_NUMBER; break; + case 7: + /* dst-mac */ + case 8: + /* src-mac */ + *ptype = IPFW_TABLE_MAC; + break; } } @@ -2805,6 +2811,14 @@ 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 +2969,26 @@ .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 --- a/sys/netpfil/ipfw/ip_fw_table_algo.c +++ b/sys/netpfil/ipfw/ip_fw_table_algo.c @@ -48,6 +48,7 @@ #include #include #include +#include #include /* ip_fw.h requires IFNAMSIZ */ #include #include @@ -315,15 +316,17 @@ */ #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, 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, 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 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 len; + struct ether_addr addr; +}; + struct ta_buf_radix { void *ent_ptr; @@ -365,62 +373,66 @@ 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, 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, +static void ta_flush_addr_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei, void *ta_buf); 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 @@ * 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 @@ 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 @@ 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 @@ } 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 @@ * 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 @@ 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 @@ } 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 @@ 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 @@ } 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 @@ } 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 @@ #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 @@ } 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 @@ 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 @@ } 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 @@ 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 @@ } 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 @@ } 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 @@ } 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 @@ /* 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 @@ .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 @@ .print_config = ta_print_kfib_config, }; +struct mac_radix_entry { + struct radix_node rn[2]; + struct sa_mac sa; + uint32_t value; + uint8_t masklen; +}; + +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.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->addr.octet; i >= 8; i -= 8) + *cp++ = 0xFF; + if (i > 0) + *cp = ~((1 << (8 - i)) - 1); + + addr->addr = *((struct ether_addr *)tei->paddr); + for (i = 0; i < ETHER_ADDR_LEN; ++i) + addr->addr.octet[i] &= mask->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.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.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_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_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); }