diff options
| author | Arseny Smalyuk <smalukav@gmail.com> | 2022-06-04 16:12:29 +0000 |
|---|---|---|
| committer | Andrey V. Elsukov <ae@FreeBSD.org> | 2022-06-04 16:12:29 +0000 |
| commit | 81cac3906eb9c14f81e03b7bcb6893b8d30e5432 (patch) | |
| tree | 9aa441c93a516a29ab333ae3ca8b1f7bdda9dc09 /sbin | |
| parent | 32a01b2b8691453b59a26cf273a5a5eae7643985 (diff) | |
Diffstat (limited to 'sbin')
| -rw-r--r-- | sbin/ipfw/ipfw.8 | 38 | ||||
| -rw-r--r-- | sbin/ipfw/ipfw2.c | 132 | ||||
| -rw-r--r-- | sbin/ipfw/ipfw2.h | 2 | ||||
| -rw-r--r-- | sbin/ipfw/tables.c | 29 |
4 files changed, 162 insertions, 39 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); @@ -1332,6 +1340,22 @@ print_mac(struct buf_pr *bp, const ipfw_insn_mac *mac) } 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) { uint8_t type; @@ -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); |
