From nobody Sat Jun 04 16:20:29 2022 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 2627D1BDAE06; Sat, 4 Jun 2022 16:20:30 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4LFlNF72Klz3h5S; Sat, 4 Jun 2022 16:20:29 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1654359630; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=WshcweHXbDoecnonqZUKJlqk5eKnWO16FWmWTgTtnNo=; b=iyaCwCgQoYjzxewK1q1OMoFUKraqSNisIMV7Un6qwRa4diKfB3/0JwzFjDW3FG6IwhPgu+ PNisyEmDTZ2D9koYqAWvQt6ioECYAqESPSuF4T0LXMbTAGP27MeloPBFhn643ZYxtKg18c khsbclm0XNH08U9jskpIceJ29349VUkk7ZIzqWVghSbtwHe5s+27ZQCudGei72mExsKBGp ZQGybs+lEDCusIMxUCWXhbQC+FS3x7X+nXD1biqYcBZ8gjQejIhmu8Nxn8FT7YWle36KkW ghfEKPgvngrd+BvTLaXqpqkACf/NiEMAHuqAXKVshszTq3hrerrflXC8scrLsQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id A729E20A5D; Sat, 4 Jun 2022 16:20:29 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 254GKTcg044184; Sat, 4 Jun 2022 16:20:29 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 254GKT60044183; Sat, 4 Jun 2022 16:20:29 GMT (envelope-from git) Date: Sat, 4 Jun 2022 16:20:29 GMT Message-Id: <202206041620.254GKT60044183@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: "Andrey V. Elsukov" Subject: git: 81cac3906eb9 - main - ipfw: add support radix tables and table lookup for MAC addresses List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: ae X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 81cac3906eb9c14f81e03b7bcb6893b8d30e5432 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1654359630; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=WshcweHXbDoecnonqZUKJlqk5eKnWO16FWmWTgTtnNo=; b=Wnl/jB6wSvRr9m3CWmn9AVypuDANKNQy7IMwUU/yOnEoL4zuAJEEsYO65Eod/b84ETUdlN LVL/m+bNQUBqUtKB0K/sw/tASi740OpOsmYP7q4dTsHJTTOm3i4SOcTIZavSqzYpP7/kxj +9I787fyQlC927Yn0QNbca4Exwr4jFqPD2U1iWFM9wzugChEmTQaIYvEjRoCRjMvCjFmt5 SjrVvmo6z98ejo7wC4lX1a75j5HXwgW/7nQuTqcxcKzskVl+ZJQ7KOYxZcCKgjLFacwTju FFD+wHbyXCcci51wdR1gfSnEU8b3nhHOFyUeFxZgL2XvpD0xhNabTampNfQ4Kw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1654359630; a=rsa-sha256; cv=none; b=xJnbc458GzTB9acd3bG1h9KXK7+D0bzp2sNhhqVw6WTA3yx1ZvLmGAuK1WPFjJZPPmDKZ7 Pv2M0BUHPz9u4lIh46Ef+SRWNEWGWaKJL5rj6E/d89ADznJl+uNF02DWJtJAQyUjEL5xOP WrUGx7oF6co4gfnaut6PpcbrmK59hA1/QOHQXdd6noTNJ7Yo2qzLaCrLjnBVO8O170EAhZ TZ/b+mFbnrdgtaT8fGgyTVVa32O6NvlkjmMfVDqLqXTJqd3e/SMOltVktwDOg4Fv90HcVI jJB9ECrzP6W5RCtAdDq0ZM/K0n0Bj1vHLguJlSdpxaLAP3GBn7tcBMy92Hjiuw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by ae: URL: https://cgit.FreeBSD.org/src/commit/?id=81cac3906eb9c14f81e03b7bcb6893b8d30e5432 commit 81cac3906eb9c14f81e03b7bcb6893b8d30e5432 Author: Arseny Smalyuk AuthorDate: 2022-06-04 16:12:29 +0000 Commit: Andrey V. Elsukov 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 = ""; + const char *arg; - if (d < sizeof(lookup_key)/sizeof(lookup_key[0])) - arg = match_value(rule_options, lookup_key[d]); + arg = match_value(lookup_keys, a[1]); t = table_search_ctlv(fo->tstate, ((const ipfw_insn *)cmd)->arg1); bprintf(bp, "lookup %s %s", arg, t); @@ -1331,6 +1339,22 @@ print_mac(struct buf_pr *bp, const ipfw_insn_mac *mac) format_mac(bp, mac->addr + 6, mac->mask + 6); } +static void +print_mac_lookup(struct buf_pr *bp, const struct format_opts *fo, + const ipfw_insn *cmd) +{ + uint32_t len = F_LEN(cmd); + char *t; + + bprintf(bp, " "); + + t = table_search_ctlv(fo->tstate, cmd->arg1); + bprintf(bp, "table(%s", t); + if (len == F_INSN_SIZE(ipfw_insn_u32)) + bprintf(bp, ",%u", ((const ipfw_insn_u32 *)cmd)->d[0]); + bprintf(bp, ")"); +} + static void fill_icmptypes(ipfw_insn_u32 *cmd, char *av) { @@ -1518,6 +1542,14 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, " dst-ip6"); print_ip6(bp, insntod(cmd, ip6)); break; + case O_MAC_SRC_LOOKUP: + bprintf(bp, " src-mac"); + print_mac_lookup(bp, fo, cmd); + break; + case O_MAC_DST_LOOKUP: + bprintf(bp, " dst-mac"); + print_mac_lookup(bp, fo, cmd); + break; case O_FLOW6ID: print_flow6id(bp, insntod(cmd, u32)); break; @@ -2650,7 +2682,6 @@ list_static_range(struct cmdline_opts *co, struct format_opts *fo, int n, seen; struct ip_fw_rule *r; struct ip_fw_bcounter *cntr; - int c = 0; for (n = seen = 0; n < rcnt; n++, rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { @@ -2669,7 +2700,6 @@ list_static_range(struct cmdline_opts *co, struct format_opts *fo, if (r->rulenum >= fo->first && r->rulenum <= fo->last) { show_static_rule(co, fo, bp, r, cntr); printf("%s", bp->buf); - c += rtlv->length; bp_flush(bp); seen++; } @@ -2788,13 +2818,12 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, char *endptr; size_t readsz; struct buf_pr bp; - ipfw_obj_ctlv *ctlv, *tstate; + ipfw_obj_ctlv *ctlv; ipfw_obj_tlv *rbase; /* * Handle tablenames TLV first, if any */ - tstate = NULL; rbase = NULL; dynbase = NULL; dynsz = 0; @@ -3682,6 +3711,29 @@ add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) return cmd; } +static ipfw_insn * +add_srcmac(ipfw_insn *cmd, char *av, struct tidx *tstate) +{ + + if (strncmp(av, "table(", 6) == 0) + fill_table(cmd, av, O_MAC_SRC_LOOKUP, tstate); + else + errx(EX_DATAERR, "only mac table lookup is supported %s", av); + return cmd; +} + +static ipfw_insn * +add_dstmac(ipfw_insn *cmd, char *av, struct tidx *tstate) +{ + + if (strncmp(av, "table(", 6) == 0) + fill_table(cmd, av, O_MAC_DST_LOOKUP, tstate); + else + errx(EX_DATAERR, "only mac table lookup is supported %s", av); + return cmd; +} + + static struct _s_x f_reserved_keywords[] = { { "altq", TOK_OR }, { "//", TOK_OR }, @@ -4912,6 +4964,21 @@ read_options: } break; + + case TOK_SRCMAC: + NEED1("missing source MAC"); + if (add_srcmac(cmd, *av, tstate)) { + av++; + } + break; + + case TOK_DSTMAC: + NEED1("missing destination MAC"); + if (add_dstmac(cmd, *av, tstate)) { + av++; + } + break; + case TOK_SRCPORT: NEED1("missing source port"); if (_substrcmp(*av, "any") == 0 || @@ -5013,28 +5080,23 @@ read_options: case TOK_LOOKUP: { ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; - int j; if (!av[0] || !av[1]) errx(EX_USAGE, "format: lookup argument tablenum"); cmd->opcode = O_IP_DST_LOOKUP; cmd->len |= F_INSN_SIZE(ipfw_insn) + 2; - i = match_token(rule_options, *av); - for (j = 0; lookup_key[j] >= 0 ; j++) { - if (i == lookup_key[j]) - break; - } - if (lookup_key[j] <= 0) + i = match_token(lookup_keys, *av); + if (i == -1) errx(EX_USAGE, "format: cannot lookup on %s", *av); - __PAST_END(c->d, 1) = j; // i converted to option + __PAST_END(c->d, 1) = i; av++; - if ((j = pack_table(tstate, *av)) == 0) + if ((i = pack_table(tstate, *av)) == 0) errx(EX_DATAERR, "Invalid table name: %s", *av); - cmd->arg1 = j; + cmd->arg1 = i; av++; - } + } break; case TOK_FLOW: NEED1("missing table name"); diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 9a39c215692d..dd7699987434 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -177,6 +177,8 @@ enum tokens { TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, + TOK_DSTMAC, + TOK_SRCMAC, TOK_ALL, TOK_MASK, TOK_FLOW_MASK, diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c index 81cf7e392586..9e6390492e96 100644 --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,7 @@ static int tables_foreach(table_cb_t *f, void *arg, int sort); static struct _s_x tabletypes[] = { { "addr", IPFW_TABLE_ADDR }, + { "mac", IPFW_TABLE_MAC }, { "iface", IPFW_TABLE_INTERFACE }, { "number", IPFW_TABLE_NUMBER }, { "flow", IPFW_TABLE_FLOW }, @@ -1188,6 +1190,7 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type, char *p, *pp; int mask, af; struct in6_addr *paddr, tmp; + struct ether_addr *mac; struct tflow_entry *tfe; uint32_t key, *pkey; uint16_t port; @@ -1234,6 +1237,24 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type, af = AF_INET; } break; + case IPFW_TABLE_MAC: + /* Remove / if exists */ + if ((p = strchr(arg, '/')) != NULL) { + *p = '\0'; + mask = atoi(p + 1); + } + + if (p != NULL && mask > 8 * ETHER_ADDR_LEN) + errx(EX_DATAERR, "bad MAC mask width: %s", + p + 1); + + if ((mac = ether_aton(arg)) == NULL) + errx(EX_DATAERR, "Incorrect MAC address"); + + memcpy(tentry->k.mac, mac->octet, ETHER_ADDR_LEN); + masklen = p ? mask : 8 * ETHER_ADDR_LEN; + af = AF_LINK; + break; case IPFW_TABLE_INTERFACE: /* Assume interface name. Copy significant data only */ mask = MIN(strlen(arg), IF_NAMESIZE - 1); @@ -1872,6 +1893,7 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent) { char tbuf[128], pval[128]; const char *comma; + const u_char *mac; void *paddr; struct tflow_entry *tfe; @@ -1884,6 +1906,13 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent) inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf)); printf("%s/%u %s\n", tbuf, tent->masklen, pval); break; + case IPFW_TABLE_MAC: + /* MAC prefixes */ + mac = tent->k.mac; + printf("%02x:%02x:%02x:%02x:%02x:%02x/%u %s\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + tent->masklen, pval); + break; case IPFW_TABLE_INTERFACE: /* Interface names */ printf("%s %s\n", tent->k.iface, pval); diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 4d3099a781a0..cbf03a5a6f8e 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -295,9 +295,27 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_SKIP_ACTION, /* none */ O_TCPMSS, /* arg1=MSS value */ + O_MAC_SRC_LOOKUP, /* arg1=table number, u32=value */ + O_MAC_DST_LOOKUP, /* arg1=table number, u32=value */ + O_LAST_OPCODE /* not an opcode! */ }; +/* + * Defines key types used by lookup instruction + */ +enum ipfw_table_lookup_type { + LOOKUP_DST_IP, + LOOKUP_SRC_IP, + LOOKUP_DST_PORT, + LOOKUP_SRC_PORT, + LOOKUP_UID, + LOOKUP_JAIL, + LOOKUP_DSCP, + LOOKUP_DST_MAC, + LOOKUP_SRC_MAC, +}; + /* * The extension header are filtered only for presence using a bit * vector with a flag for each header. @@ -754,7 +772,8 @@ struct _ipfw_dyn_rule { #define IPFW_TABLE_INTERFACE 2 /* Table for holding interface names */ #define IPFW_TABLE_NUMBER 3 /* Table for holding ports/uid/gid/etc */ #define IPFW_TABLE_FLOW 4 /* Table for holding flow data */ -#define IPFW_TABLE_MAXTYPE 4 /* Maximum valid number */ +#define IPFW_TABLE_MAC 5 /* Table for holding mac address prefixes */ +#define IPFW_TABLE_MAXTYPE 5 /* Maximum valid number */ #define IPFW_TABLE_CIDR IPFW_TABLE_ADDR /* compat */ @@ -772,6 +791,9 @@ struct _ipfw_dyn_rule { #define IPFW_VTYPE_NH4 0x00000200 /* IPv4 nexthop */ #define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */ +/* MAC/InfiniBand/etc address length */ +#define IPFW_MAX_L2_ADDR_LEN 20 + typedef struct _ipfw_table_entry { in_addr_t addr; /* network address */ u_int32_t value; /* value */ @@ -895,10 +917,11 @@ typedef struct _ipfw_obj_tentry { uint16_t spare1; union { /* Longest field needs to be aligned by 8-byte boundary */ - struct in_addr addr; /* IPv4 address */ - uint32_t key; /* uid/gid/port */ - struct in6_addr addr6; /* IPv6 address */ - char iface[IF_NAMESIZE]; /* interface name */ + struct in_addr addr; /* IPv4 address */ + uint32_t key; /* uid/gid/port */ + struct in6_addr addr6; /* IPv6 address */ + char iface[IF_NAMESIZE]; /* interface name */ + u_char mac[IPFW_MAX_L2_ADDR_LEN]; /* MAC address */ struct tflow_entry flow; } k; union { diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index aaca2e2b2fcd..99d3a9c58cb7 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -2038,78 +2038,87 @@ do { \ case O_IP_DST_LOOKUP: { - void *pkey; - uint32_t vidx, key; - uint16_t keylen; - if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) { + void *pkey; + uint32_t vidx, key; + uint16_t keylen = 0; /* zero if can't match the packet */ + /* Determine lookup key type */ vidx = ((ipfw_insn_u32 *)cmd)->d[1]; - if (vidx != 4 /* uid */ && - vidx != 5 /* jail */ && - is_ipv6 == 0 && is_ipv4 == 0) - break; - /* Determine key length */ - if (vidx == 0 /* dst-ip */ || - vidx == 1 /* src-ip */) - keylen = is_ipv6 ? - sizeof(struct in6_addr): - sizeof(in_addr_t); - else { - keylen = sizeof(key); - pkey = &key; - } - if (vidx == 0 /* dst-ip */) - pkey = is_ipv4 ? (void *)&dst_ip: - (void *)&args->f_id.dst_ip6; - else if (vidx == 1 /* src-ip */) - pkey = is_ipv4 ? (void *)&src_ip: - (void *)&args->f_id.src_ip6; - else if (vidx == 6 /* dscp */) { - if (is_ipv4) - key = ip->ip_tos >> 2; + switch (vidx) { + case LOOKUP_DST_IP: + case LOOKUP_SRC_IP: + /* Need IP frame */ + if (is_ipv6 == 0 && is_ipv4 == 0) + break; + if (vidx == LOOKUP_DST_IP) + pkey = is_ipv6 ? + (void *)&args->f_id.dst_ip6: + (void *)&dst_ip; else - key = IPV6_DSCP( - (struct ip6_hdr *)ip) >> 2; - key &= 0x3f; - } else if (vidx == 2 /* dst-port */ || - vidx == 3 /* src-port */) { + pkey = is_ipv6 ? + (void *)&args->f_id.src_ip6: + (void *)&src_ip; + keylen = is_ipv6 ? + sizeof(struct in6_addr): + sizeof(in_addr_t); + break; + case LOOKUP_DST_PORT: + case LOOKUP_SRC_PORT: + /* Need IP frame */ + if (is_ipv6 == 0 && is_ipv4 == 0) + break; /* Skip fragments */ if (offset != 0) break; /* Skip proto without ports */ if (proto != IPPROTO_TCP && - proto != IPPROTO_UDP && - proto != IPPROTO_UDPLITE && - proto != IPPROTO_SCTP) + proto != IPPROTO_UDP && + proto != IPPROTO_UDPLITE && + proto != IPPROTO_SCTP) break; - if (vidx == 2 /* dst-port */) - key = dst_port; - else - key = src_port; - } -#ifndef USERSPACE - else if (vidx == 4 /* uid */ || - vidx == 5 /* jail */) { + key = vidx == LOOKUP_DST_PORT ? + dst_port: + src_port; + pkey = &key; + keylen = sizeof(key); + break; + case LOOKUP_UID: + case LOOKUP_JAIL: check_uidgid( (ipfw_insn_u32 *)cmd, args, &ucred_lookup, -#ifdef __FreeBSD__ &ucred_cache); - if (vidx == 4 /* uid */) - key = ucred_cache->cr_uid; - else if (vidx == 5 /* jail */) - key = ucred_cache->cr_prison->pr_id; -#else /* !__FreeBSD__ */ - (void *)&ucred_cache); - if (vidx == 4 /* uid */) - key = ucred_cache.uid; - else if (vidx == 5 /* jail */) - key = ucred_cache.xid; -#endif /* !__FreeBSD__ */ + key = vidx == LOOKUP_UID ? + ucred_cache->cr_uid: + ucred_cache->cr_prison->pr_id; + pkey = &key; + keylen = sizeof(key); + break; + case LOOKUP_DSCP: + /* Need IP frame */ + if (is_ipv6 == 0 && is_ipv4 == 0) + break; + if (is_ipv6) + key = IPV6_DSCP( + (struct ip6_hdr *)ip) >> 2; + else + key = ip->ip_tos >> 2; + pkey = &key; + keylen = sizeof(key); + break; + case LOOKUP_DST_MAC: + case LOOKUP_SRC_MAC: + /* Need ether frame */ + if ((args->flags & IPFW_ARGS_ETHER) == 0) + break; + pkey = vidx == LOOKUP_DST_MAC ? + eh->ether_dhost: + eh->ether_shost; + keylen = ETHER_ADDR_LEN; + break; } -#endif /* !USERSPACE */ - else + if (keylen == 0) break; match = ipfw_lookup_table(chain, cmd->arg1, keylen, pkey, &vidx); @@ -2155,6 +2164,36 @@ do { \ break; } + case O_MAC_SRC_LOOKUP: + case O_MAC_DST_LOOKUP: + { + void *pkey; + uint32_t vidx; + uint16_t keylen = ETHER_ADDR_LEN; + + /* Need ether frame */ + if ((args->flags & IPFW_ARGS_ETHER) == 0) + break; + + if (cmd->opcode == O_MAC_DST_LOOKUP) + pkey = eh->ether_dhost; + else + pkey = eh->ether_shost; + + match = ipfw_lookup_table(chain, cmd->arg1, + keylen, pkey, &vidx); + if (!match) + break; + if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) { + match = ((ipfw_insn_u32 *)cmd)->d[0] == + TARG_VAL(chain, vidx, tag); + if (!match) + break; + } + tablearg = vidx; + break; + } + case O_IP_FLOW_LOOKUP: { uint32_t v = 0; diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 2658c371091d..9505fa70f69a 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -1909,6 +1909,8 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) ci->object_opcodes++; break; case O_IP_FLOW_LOOKUP: + case O_MAC_DST_LOOKUP: + case O_MAC_SRC_LOOKUP: if (cmd->arg1 >= V_fw_tables_max) { printf("ipfw: invalid table number %d\n", cmd->arg1); diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index 19bedbbe9b50..202a49840b38 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -2752,26 +2752,19 @@ classify_srcdst(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) */ v = ((ipfw_insn_u32 *)cmd)->d[1]; switch (v) { - case 0: - case 1: - /* IPv4 src/dst */ + case LOOKUP_DST_IP: + case LOOKUP_SRC_IP: break; - case 2: - case 3: - /* src/dst port */ + case LOOKUP_DST_PORT: + case LOOKUP_SRC_PORT: + case LOOKUP_UID: + case LOOKUP_JAIL: + case LOOKUP_DSCP: *ptype = IPFW_TABLE_NUMBER; break; - case 4: - /* uid/gid */ - *ptype = IPFW_TABLE_NUMBER; - break; - case 5: - /* jid */ - *ptype = IPFW_TABLE_NUMBER; - break; - case 6: - /* dscp */ - *ptype = IPFW_TABLE_NUMBER; + case LOOKUP_DST_MAC: + case LOOKUP_SRC_MAC: + *ptype = IPFW_TABLE_MAC; break; } } @@ -2805,6 +2798,14 @@ classify_flow(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) return (0); } +static int +classify_mac_lookup(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +{ + *puidx = cmd->arg1; + *ptype = IPFW_TABLE_MAC; + return (0); +} + static void update_arg1(ipfw_insn *cmd, uint16_t idx) { @@ -2955,6 +2956,26 @@ static struct opcode_obj_rewrite opcodes[] = { .create_object = create_table_compat, .manage_sets = table_manage_sets, }, + { + .opcode = O_MAC_SRC_LOOKUP, + .etlv = IPFW_TLV_TBL_NAME, + .classifier = classify_mac_lookup, + .update = update_arg1, + .find_byname = table_findbyname, + .find_bykidx = table_findbykidx, + .create_object = create_table_compat, + .manage_sets = table_manage_sets, + }, + { + .opcode = O_MAC_DST_LOOKUP, + .etlv = IPFW_TLV_TBL_NAME, + .classifier = classify_mac_lookup, + .update = update_arg1, + .find_byname = table_findbyname, + .find_bykidx = table_findbykidx, + .create_object = create_table_compat, + .manage_sets = table_manage_sets, + }, { .opcode = O_XMIT, .etlv = IPFW_TLV_TBL_NAME, diff --git a/sys/netpfil/ipfw/ip_fw_table_algo.c b/sys/netpfil/ipfw/ip_fw_table_algo.c index cc5cba386ace..8f8604af44d9 100644 --- a/sys/netpfil/ipfw/ip_fw_table_algo.c +++ b/sys/netpfil/ipfw/ip_fw_table_algo.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include /* ip_fw.h requires IFNAMSIZ */ #include #include @@ -315,15 +316,17 @@ static int bdel(const void *key, void *base, size_t nmemb, size_t size, */ #define KEY_LEN(v) *((uint8_t *)&(v)) /* - * Do not require radix to compare more than actual IPv4/IPv6 address + * Do not require radix to compare more than actual IPv4/IPv6/MAC address */ #define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t)) #define KEY_LEN_INET6 (offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr)) +#define KEY_LEN_MAC (offsetof(struct sa_mac, mac_addr) + ETHER_ADDR_LEN) #define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr)) #define OFF_LEN_INET6 (8 * offsetof(struct sa_in6, sin6_addr)) +#define OFF_LEN_MAC (8 * offsetof(struct sa_mac, mac_addr)) -struct radix_addr_entry { +struct addr_radix_entry { struct radix_node rn[2]; struct sockaddr_in addr; uint32_t value; @@ -337,20 +340,25 @@ struct sa_in6 { struct in6_addr sin6_addr; }; -struct radix_addr_xentry { +struct addr_radix_xentry { struct radix_node rn[2]; struct sa_in6 addr6; uint32_t value; uint8_t masklen; }; -struct radix_cfg { +struct addr_radix_cfg { struct radix_node_head *head4; struct radix_node_head *head6; size_t count4; size_t count6; }; +struct sa_mac { + uint8_t mac_len; + struct ether_addr mac_addr; +}; + struct ta_buf_radix { void *ent_ptr; @@ -365,32 +373,36 @@ struct ta_buf_radix struct sa_in6 sa; struct sa_in6 ma; } a6; + struct { + struct sa_mac sa; + struct sa_mac ma; + } mac; } addr; }; -static int ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen, +static int ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t keylen, uint32_t *val); -static int ta_init_radix(struct ip_fw_chain *ch, void **ta_state, +static int ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti, char *data, uint8_t tflags); static int flush_radix_entry(struct radix_node *rn, void *arg); -static void ta_destroy_radix(void *ta_state, struct table_info *ti); -static void ta_dump_radix_tinfo(void *ta_state, struct table_info *ti, +static void ta_destroy_addr_radix(void *ta_state, struct table_info *ti); +static void ta_dump_addr_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo); -static int ta_dump_radix_tentry(void *ta_state, struct table_info *ti, +static int ta_dump_addr_radix_tentry(void *ta_state, struct table_info *ti, void *e, ipfw_obj_tentry *tent); -static int ta_find_radix_tentry(void *ta_state, struct table_info *ti, +static int ta_find_addr_radix_tentry(void *ta_state, struct table_info *ti, ipfw_obj_tentry *tent); -static void ta_foreach_radix(void *ta_state, struct table_info *ti, +static void ta_foreach_addr_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f, void *arg); -static void tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa, +static void tei_to_sockaddr_ent_addr(struct tentry_info *tei, struct sockaddr *sa, struct sockaddr *ma, int *set_mask); -static int ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei, +static int ta_prepare_add_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei, void *ta_buf); -static int ta_add_radix(void *ta_state, struct table_info *ti, +static int ta_add_addr_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei, void *ta_buf, uint32_t *pnum); -static int ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei, +static int ta_prepare_del_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei, void *ta_buf); -static int ta_del_radix(void *ta_state, struct table_info *ti, +static int ta_del_addr_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei, void *ta_buf, uint32_t *pnum); static void ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei, void *ta_buf); @@ -398,29 +410,29 @@ static int ta_need_modify_radix(void *ta_state, struct table_info *ti, uint32_t count, uint64_t *pflags); static int -ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen, +ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t keylen, uint32_t *val) { struct radix_node_head *rnh; if (keylen == sizeof(in_addr_t)) { - struct radix_addr_entry *ent; + struct addr_radix_entry *ent; struct sockaddr_in sa; KEY_LEN(sa) = KEY_LEN_INET; sa.sin_addr.s_addr = *((in_addr_t *)key); rnh = (struct radix_node_head *)ti->state; - ent = (struct radix_addr_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh)); + ent = (struct addr_radix_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh)); if (ent != NULL) { *val = ent->value; return (1); } - } else { - struct radix_addr_xentry *xent; + } else if (keylen == sizeof(struct in6_addr)) { + struct addr_radix_xentry *xent; struct sa_in6 sa6; KEY_LEN(sa6) = KEY_LEN_INET6; memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr)); rnh = (struct radix_node_head *)ti->xstate; - xent = (struct radix_addr_xentry *)(rnh->rnh_matchaddr(&sa6, &rnh->rh)); + xent = (struct addr_radix_xentry *)(rnh->rnh_matchaddr(&sa6, &rnh->rh)); if (xent != NULL) { *val = xent->value; return (1); @@ -434,10 +446,10 @@ ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen, * New table */ static int -ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti, +ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti, char *data, uint8_t tflags) { - struct radix_cfg *cfg; + struct addr_radix_cfg *cfg; if (!rn_inithead(&ti->state, OFF_LEN_INET)) return (ENOMEM); @@ -446,10 +458,10 @@ ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti, return (ENOMEM); } - cfg = malloc(sizeof(struct radix_cfg), M_IPFW, M_WAITOK | M_ZERO); + cfg = malloc(sizeof(struct addr_radix_cfg), M_IPFW, M_WAITOK | M_ZERO); *ta_state = cfg; - ti->lookup = ta_lookup_radix; + ti->lookup = ta_lookup_addr_radix; return (0); } @@ -458,9 +470,9 @@ static int flush_radix_entry(struct radix_node *rn, void *arg) { *** 609 LINES SKIPPED ***