The branch main has been updated by ae: URL: https://cgit.FreeBSD.org/src/commit/?id=32cd3ee5901ea33d41ff550e5f40ce743c8d4165
commit 32cd3ee5901ea33d41ff550e5f40ce743c8d4165 Author: Boris Lytochkin <[email protected]> AuthorDate: 2026-03-01 18:54:24 +0000 Commit: Andrey V. Elsukov <[email protected]> CommitDate: 2026-03-01 19:04:58 +0000 ipfw: add support for masked ip-address lookups Current radix-based implementation of lookup tables in ipfw does not support non-contiguous prefixes while this type of lookup is needed to write CPU-effective firewall configurations. For some of the cases we can reach the goal using a masked table lookup by adding masked (e.g. zero non-significant bits) records into a table and then zero non-significant bits in lookup key prior to making a table lookup. Obtained from: Yandex LLC MFC after: 3 weeks Relnotes: yes Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D53694 --- sbin/ipfw/ipfw.8 | 151 ++++++-- sbin/ipfw/ipfw2.c | 193 ++++++++-- sbin/ipfw/tests/test_add_rule.py | 243 +++++++++++- sys/netinet/ip_fw.h | 81 +++- sys/netpfil/ipfw/ip_fw2.c | 231 ++++++----- sys/netpfil/ipfw/ip_fw_sockopt.c | 32 ++ sys/netpfil/ipfw/ip_fw_table.c | 26 +- tests/atf_python/sys/netpfil/ipfw/insn_headers.py | 37 +- tests/atf_python/sys/netpfil/ipfw/insns.py | 179 ++++++++- tests/atf_python/sys/netpfil/ipfw/ioctl.py | 1 + tests/atf_python/sys/netpfil/ipfw/ioctl_headers.py | 41 +- tests/sys/netpfil/common/utils.subr | 3 + tests/sys/netpfil/ipfw/Makefile | 4 +- tests/sys/netpfil/ipfw/lookup.sh | 428 +++++++++++++++++++++ tests/sys/netpfil/ipfw/lookup_inetd.conf | 1 + 15 files changed, 1417 insertions(+), 234 deletions(-) diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 867a43868ecc..2d3b0722cc42 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,5 +1,5 @@ .\" -.Dd December 29, 2025 +.Dd March 1, 2026 .Dt IPFW 8 .Os .Sh NAME @@ -1428,8 +1428,7 @@ The second format with multiple addresses) is provided for convenience only and its use is discouraged. .It Ar addr : Oo Cm not Oc Bro -.Cm any | me | me6 | -.Cm table Ns Pq Ar name Ns Op , Ns Ar value +.Cm any | me | me6 | Ar table-ref .Ar | addr-list | addr-set .Brc .Bl -tag -width indent @@ -1441,26 +1440,32 @@ Matches any IP address configured on an interface in the system. Matches any IPv6 address configured on an interface in the system. The address list is evaluated at the time the packet is analysed. -.It Cm table Ns Pq Ar name Ns Op , Ns Ar value +.El +.It Ar table-ref : +A table lookup can be specified in one of the following ways: +.Bl -tag -width indent +.It table Ns Pq Ar name Ns Matches any IPv4 or IPv6 address for which an entry exists in the lookup table .Ar number . -If an optional 32-bit unsigned +.It table Ns Pq Ar name , Ns Ar value +Matches any IPv4 or IPv6 address for which an entry exists in the lookup table +.Ar number +and 32-bit unsigned .Ar value -is also specified, an entry will match only if it has this value. -If +specified matchess entry value. +.It table Ns Pq Ar name , Ns Ar value-type Ns = Ns Ar value +Matches any IPv4 or IPv6 address for which an entry exists in the lookup table +.Ar number +and 32-bit unsigned .Ar value -is specified in form -.Ar valtype=value , -then specified value type field will be checked. -It can be -.Ar skipto, pipe, fib, nat, dscp, tag, divert, netgraph, limit, nh4 -and -.Ar mark. - +specified matches corresponding +.Ar value-type +field for the record found. +.El +.Pp See the .Sx LOOKUP TABLES section below for more information on lookup tables. -.El .It Ar addr-list : ip-addr Ns Op , Ns Ar addr-list .It Ar ip-addr : A host or subnet address specified in one of the following ways: @@ -1681,9 +1686,9 @@ and IPsec encapsulating security payload headers .It Cm fib Ar fibnum Matches a packet that has been tagged to use the given FIB (routing table) number. -.It Cm flow Ar table Ns Pq Ar name Ns Op , Ns Ar value -Search for the flow entry in lookup table -.Ar name . +.It Cm flow Ar table-ref +Search for the flow entry in lookup table specified by +.Ar table-ref . If not found, the match fails. Otherwise, the match succeeds and .Cm tablearg @@ -1699,16 +1704,16 @@ 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 . +.It Cm dst-mac Ar table-ref +Search for the destination MAC address entry in lookup table specified by +.Ar table-ref . 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 . +.It Cm src-mac Ar table-ref +Search for the source MAC address entry in lookup table specified by +.Ar table-ref . If not found, the match fails. Otherwise, the match succeeds and .Cm tablearg @@ -1926,8 +1931,10 @@ 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 | dst-mac | src-ip | src-port | src-mac | uid | -.Cm jail | dscp | mark | rulenum Brc Ar name +.It Cm lookup Bro Cm dst-ip | dst-ip4 | dst-ip6 | dst-port | dst-mac | src-ip | +.Cm src-ip4 | src-ip6 | src-port | src-mac | uid | jail | dscp | mark | +.Cm rulenum +.Brc Ns Oo : Ns Ar bitmask Oc Ar name Search an entry in lookup table .Ar name that matches the field specified as argument. @@ -1936,8 +1943,56 @@ Otherwise, the match succeeds and .Cm tablearg is set to the value extracted from the table. .Pp +If an optional +.Ar bitmask +is specified, value of the field is altered by bitwize AND with +.Ar bitmask +and resulting value is being searched instead of the original one. +The +.Ar bitmask +is accepted in the following formats: +.Bl -enum -width indent +.It +In a dotted-quad form, e.g. 127.88.34.0. +This form can be used for IPv4 lookups as well as for all numeric lookup +types. +.It +As a 32-bit number, e.g. 0xf00baa1 or 255. +This form can be used for IPv4 lookups as well as for all numeric lookup +types. +.It +As an IPv6 address when specified alongwith +.Cm dst-ip6 +or +.Cm src-ip6 +field. +If used, the rule will match IPv6 packets only. +Example: src-ip6:afff:ff00:ffff:ffff:0:0:0:0f0f. +.It +As a Ethernet mac address when specified alongwith +.Cm dst-mac +or +.Cm src-mac +field. E.g. 00:11:22:33:44:55. +.El +.Pp +The +.Ar bitmask +can not be specified for +.Cm dst-ip +or +.Cm src-ip +as these field specifiers lookup both IPv4 and IPv6 addresses. +.Pp This option can be useful to quickly dispatch traffic based on certain packet fields. +The +.Ar bitmask +allows to implement wildcard lookups by inserting into table masked prefix and +appying +.Ar bitmask +upon each lookup. +.Pp See the .Sx LOOKUP TABLES section below for more information on lookup tables. @@ -2000,7 +2055,7 @@ However, this option doesn't imply an implicit .Cm check-state in contrast to .Cm keep-state . -.It Cm recv | xmit | via Brq Ar ifX | Ar ifmask | Ar table Ns Po Ar name Ns Oo , Ns Ar value Oc Pc | Ar ipno | Ar any +.It Cm recv | xmit | via Brq Ar ifX | Ar ifmask | Ar table-ref | Ar ipno | Ar any Matches packets received, transmitted or going through, respectively, the interface specified by exact name .Po Ar ifX Pc , @@ -2018,8 +2073,8 @@ See also the .Sx EXAMPLES section. .Pp -Table -.Ar name +A lookup table specified by +.Ar table-ref may be used to match interface by its kernel ifindex. See the .Sx LOOKUP TABLES @@ -4350,7 +4405,8 @@ Capture messages from .Xr route 4 socket, that were logged using rules with .Cm log Cm logdst Ar rtsock -opcode. Optional +opcode. +Optional .Ar filter-comment can be specified to show only those messages, that were logged by rules with specific rule comment. @@ -4705,10 +4761,41 @@ In the following example per-interface firewall is created: The following example illustrate usage of flow tables: .Pp .Dl "ipfw table fl create type flow:src-ip,proto,dst-ip,dst-port" -.Dl "ipfw table fl add 2a02:6b8:77::88,tcp,2a02:6b8:77::99,80 11" +.Dl "ipfw table fl add 2001:db8:77::88,tcp,2001:db8:77::99,80 11" .Dl "ipfw table fl add 10.0.0.1,udp,10.0.0.2,53 12" .Dl ".." .Dl "ipfw add 100 allow ip from any to any flow 'table(fl,11)' recv ix0" +.Pp +The following example illustrate masked table lookups to aid uniform client +distribution among multiple NAT instances: +.Bd -literal -offset indent +# Configure NAT instances +ipfw nat 10 config ip 192.0.2.0 +ipfw nat 11 config ip 192.0.2.1 +ipfw nat 12 config ip 192.0.2.2 +ipfw nat 13 config ip 192.0.2.3 + +ipfw table mynats create type addr valtype nat +# Map external NAT address to NAT instance +ipfw table mynats add 192.0.2.0 10 +ipfw table mynats add 192.0.2.1 11 +ipfw table mynats add 192.0.2.2 12 +ipfw table mynats add 192.0.2.3 13 + +# Map last 2 bits of client's IP address to NAT instance +ipfw table mynats add 0.0.0.0 10 +ipfw table mynats add 0.0.0.1 11 +ipfw table mynats add 0.0.0.2 12 +ipfw table mynats add 0.0.0.3 13 + +# In -> Out NAT, zero out all bits in a client's IP exept +# 2 least significant prior to table lookup +ipfw add nat tablearg ip from 10.0.0.0/24 to any + lookup src-ip4:0.0.0.3 mynats +# Out -> In NAT +ipfw add nat tablearg ip from any to 192.0.2.0/30 + lookup dst-ip mynats +.Ed .Ss SETS OF RULES To add a set of rules atomically, e.g.\& set 18: .Pp diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 26baa596cc89..e0e1339a1dce 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -313,6 +313,10 @@ static struct _s_x rule_action_params[] = { static struct _s_x lookup_keys[] = { { "dst-ip", LOOKUP_DST_IP }, { "src-ip", LOOKUP_SRC_IP }, + { "dst-ip6", LOOKUP_DST_IP6 }, + { "src-ip6", LOOKUP_SRC_IP6 }, + { "dst-ip4", LOOKUP_DST_IP4 }, + { "src-ip4", LOOKUP_SRC_IP4 }, { "dst-port", LOOKUP_DST_PORT }, { "src-port", LOOKUP_SRC_PORT }, { "dst-mac", LOOKUP_DST_MAC }, @@ -338,6 +342,7 @@ static struct _s_x tvalue_names[] = { { "fib", TVALUE_FIB }, { "nat", TVALUE_NAT }, { "nh4", TVALUE_NH4 }, + { "nh6", TVALUE_NH6 }, { "dscp", TVALUE_DSCP }, { "limit", TVALUE_LIMIT }, { "mark", TVALUE_MARK }, @@ -1311,12 +1316,27 @@ print_flags(struct buf_pr *bp, char const *name, const ipfw_insn *cmd, } static void -print_tvalue(struct buf_pr *bp, const ipfw_insn_table *cmd) +print_tvalue(struct buf_pr *bp, const ipfw_insn_lookup *cmd) { + char maskbuf[INET6_ADDRSTRLEN]; const char *name; name = match_value(tvalue_names, IPFW_TVALUE_TYPE(&cmd->o)); - bprintf(bp, ",%s=%u", name != NULL ? name: "<invalid>", cmd->value); + switch(IPFW_TVALUE_TYPE(&cmd->o)) { + case TVALUE_NH6: + if (inet_ntop(AF_INET6, &insntoc(&cmd->o, lookup)->ip6, + maskbuf, sizeof(maskbuf)) == NULL) + strcpy(maskbuf, "<invalid>"); + bprintf(bp, ",%s=%s", name != NULL ? name: "<invalid>", + maskbuf); + return; + case TVALUE_NH4: + bprintf(bp, ",%s=%s", name != NULL ? name: "<invalid>", + inet_ntoa(cmd->ip4)); + return; + } + bprintf(bp, ",%s=%u", name != NULL ? name: "<invalid>", + cmd->u32); } @@ -1327,11 +1347,14 @@ static void print_ip(struct buf_pr *bp, const struct format_opts *fo, const ipfw_insn_ip *cmd) { + char maskbuf[INET6_ADDRSTRLEN]; + const uint32_t *a = insntoc(cmd, u32)->d; struct hostent *he = NULL; const struct in_addr *ia; - const uint32_t *a = ((const ipfw_insn_u32 *)cmd)->d; - uint32_t len = F_LEN(&cmd->o); + const ipfw_insn_lookup *l = insntoc(cmd, lookup); + const char *key; char *t; + uint32_t len = F_LEN(&cmd->o); bprintf(bp, " "); switch (cmd->o.opcode) { @@ -1340,32 +1363,65 @@ print_ip(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, "me"); return; - case O_IP_DST_LOOKUP: - if ((len == F_INSN_SIZE(ipfw_insn_kidx) || - len == F_INSN_SIZE(ipfw_insn_table)) && - IPFW_LOOKUP_TYPE(&cmd->o) != LOOKUP_NONE) { - const char *key; - - key = match_value(lookup_keys, - IPFW_LOOKUP_TYPE(&cmd->o)); - t = table_search_ctlv(fo->tstate, - insntoc(&cmd->o, kidx)->kidx); - if (len == F_INSN_SIZE(ipfw_insn_table)) { - bprintf(bp, "lookup %s:%#x %s", - (key != NULL ? key : "<invalid>"), - insntoc(&cmd->o, table)->value, t); - } else - bprintf(bp, "lookup %s %s", key != NULL ? key: - "<invalid>", t); + case O_TABLE_LOOKUP: { + key = match_value(lookup_keys, + IPFW_LOOKUP_TYPE(&cmd->o)); + t = table_search_ctlv(fo->tstate, + insntoc(&cmd->o, kidx)->kidx); + if (IPFW_LOOKUP_MASKING(&cmd->o) == 0 || + len != F_INSN_SIZE(ipfw_insn_lookup)) { + bprintf(bp, "lookup %s %s", + (key != NULL ? key : "<invalid>"), t); return; } - /* FALLTHROUGH */ + switch (IPFW_LOOKUP_TYPE(&cmd->o)) { + case LOOKUP_DST_IP6: + case LOOKUP_SRC_IP6: + if (inet_ntop(AF_INET6, &l->ip6, + maskbuf, sizeof(maskbuf)) == NULL) + strcpy(maskbuf, "<invalid>"); + bprintf(bp, "lookup %s:%s %s", key, maskbuf, t); + break; + case LOOKUP_DST_IP: + case LOOKUP_SRC_IP: + case LOOKUP_DST_IP4: + case LOOKUP_SRC_IP4: + bprintf(bp, "lookup %s:%s %s", key, + inet_ntoa(l->ip4), t); + break; + case LOOKUP_DST_MAC: + case LOOKUP_SRC_MAC: + bprintf(bp, "lookup %s:%s %s", key, + ether_ntoa((const struct ether_addr *)&l->mac), t); + break; + default: + bprintf(bp, "lookup %s:%#x %s", + (key != NULL ? key : "<invalid>"), + l->u32, t); + } + return; + } + case O_IP_DST_LOOKUP: case O_IP_SRC_LOOKUP: t = table_search_ctlv(fo->tstate, insntoc(&cmd->o, kidx)->kidx); + /* + * XXX: compatibility layer, to be removed. + * Properly show rules loaded into new kernel modules by + * an old ipfw binary. + */ + if (IPFW_LOOKUP_MASKING(&cmd->o) != 0 && + len == F_INSN_SIZE(ipfw_insn_table)) { + key = match_value(lookup_keys, + IPFW_LOOKUP_TYPE(&cmd->o)); + bprintf(bp, "lookup %s:%#x %s", + (key != NULL ? key : "<invalid>"), + insntoc(&cmd->o, table)->value, t); + return; + } bprintf(bp, "table(%s", t); - if (len == F_INSN_SIZE(ipfw_insn_table)) - print_tvalue(bp, insntoc(&cmd->o, table)); + if (IPFW_LOOKUP_MATCH_TVALUE(&cmd->o) != 0) + print_tvalue(bp, l); bprintf(bp, ")"); return; } @@ -1470,15 +1526,14 @@ 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, insntoc(cmd, kidx)->kidx); bprintf(bp, "table(%s", t); - if (len == F_INSN_SIZE(ipfw_insn_table)) - print_tvalue(bp, insntoc(cmd, table)); + if (IPFW_LOOKUP_MATCH_TVALUE(cmd) != 0) + print_tvalue(bp, insntoc(cmd, lookup)); bprintf(bp, ")"); } @@ -1643,6 +1698,8 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, case O_IP_SRC_SET: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip"); + /* FALLTHROUGH */ + case O_TABLE_LOOKUP: print_ip(bp, fo, insntoc(cmd, ip)); break; case O_IP_DST: @@ -1767,8 +1824,8 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, s = table_search_ctlv(fo->tstate, insntoc(cmd, kidx)->kidx); bprintf(bp, " flow table(%s", s); - if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_table)) - print_tvalue(bp, insntoc(cmd, table)); + if (IPFW_LOOKUP_MATCH_TVALUE(cmd) != 0) + print_tvalue(bp, insntoc(cmd, lookup)); bprintf(bp, ")"); break; case O_IPID: @@ -3304,8 +3361,11 @@ pack_table(struct tidx *tstate, const char *name) return (pack_object(tstate, name, IPFW_TLV_TBL_NAME)); } +/* + * Parse table(NAME, value) and table(NAME,key=value) + */ static void -fill_table_value(ipfw_insn *cmd, char *s) +fill_table_value(ipfw_insn_lookup *cmd, char *s) { char *p; int i; @@ -3322,8 +3382,20 @@ fill_table_value(ipfw_insn *cmd, char *s) p = s; } - IPFW_SET_TVALUE_TYPE(cmd, i); - insntod(cmd, table)->value = strtoul(p, NULL, 0); + IPFW_SET_TVALUE_TYPE(&cmd->o, i); + + if (i == TVALUE_NH6) { + if (inet_pton(AF_INET6, p, &cmd->ip6) != 1) + errx(EX_USAGE, "invalid IPv6 address provided"); + /* mask in a dotted-quad notation */ + } else if (strchr(p, '.') != NULL) { + if (inet_aton(p, &cmd->ip4) != 1) + errx(EX_USAGE, "invalid IPv4 address provided"); + if (i == TVALUE_NH4) + return; + cmd->u32 = ntohl(cmd->u32); + } else + cmd->u32 = strtoul(p, NULL, 0); } void @@ -3344,9 +3416,11 @@ fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) cmd->opcode = opcode; if (p) { - cmd->len |= F_INSN_SIZE(ipfw_insn_table); - fill_table_value(cmd, p); + cmd->len |= F_INSN_SIZE(ipfw_insn_lookup); + IPFW_SET_LOOKUP_MATCH_TVALUE(cmd, 1); + fill_table_value(insntod(cmd, lookup), p); } else { + /* table(NAME) */ IPFW_SET_LOOKUP_TYPE(cmd, LOOKUP_NONE); cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); } @@ -4126,6 +4200,38 @@ get_divert_port(const char *arg, const char *action) return (ntohs(s->s_port)); } +static void +get_lookup_bitmask(int ltype, ipfw_insn_lookup *cmd, const char *src) +{ + struct ether_addr *mac; + const char *macset = "0123456789abcdefABCDEF:"; + + if (ltype == LOOKUP_SRC_IP6 || ltype == LOOKUP_DST_IP6) { + if (inet_pton(AF_INET6, src, &cmd->ip6) != 1) + errx(EX_USAGE, "invalid IPv6 mask provided"); + return; + } else if (ltype == LOOKUP_SRC_MAC || ltype == LOOKUP_DST_MAC) { + if (strspn(src, macset) != strlen(src) || + (mac = ether_aton(src)) == NULL) + errx(EX_DATAERR, "Incorrect MAC address"); + + bcopy(mac, cmd->mac, ETHER_ADDR_LEN); + return; + /* mask in a dotted-quad notation */ + } else if (strchr(src, '.') != NULL) { + if (inet_aton(src, &cmd->ip4) != 1) + errx(EX_USAGE, "invalid dotted-quad mask provided"); + switch (ltype) { + case LOOKUP_SRC_IP4: + case LOOKUP_DST_IP4: + return; + } + cmd->u32 = ntohl(cmd->u32); + return; + } + cmd->u32 = strtoul(src, NULL, 0); +} + /* * Parse arguments and assemble the microinstructions which make up a rule. * Rules are added into the 'rulebuf' and then copied in the correct order @@ -5448,13 +5554,13 @@ read_options: case TOK_LOOKUP: { /* optional mask for some LOOKUP types */ - ipfw_insn_table *c = insntod(cmd, table); + ipfw_insn_lookup *c = insntod(cmd, lookup); char *lkey; if (!av[0] || !av[1]) errx(EX_USAGE, "format: lookup argument tablenum"); - cmd->opcode = O_IP_DST_LOOKUP; + cmd->opcode = O_TABLE_LOOKUP; lkey = strsep(av, ":"); i = match_token(lookup_keys, lkey); @@ -5471,18 +5577,21 @@ read_options: case LOOKUP_DSCP: case LOOKUP_MARK: case LOOKUP_RULENUM: + case LOOKUP_SRC_MAC: + case LOOKUP_DST_MAC: + case LOOKUP_SRC_IP6: + case LOOKUP_DST_IP6: + case LOOKUP_SRC_IP4: + case LOOKUP_DST_IP4: break; default: errx(EX_USAGE, "masked lookup is not supported " "for %s", lkey); } - cmd->len |= F_INSN_SIZE(ipfw_insn_table); - c->value = strtoul(*av, NULL, 0); - if (c->value == 0) - errx(EX_USAGE, - "all-zeroes bitmask for lookup " - "is meaningless"); + cmd->len |= F_INSN_SIZE(ipfw_insn_lookup); + IPFW_SET_LOOKUP_MASKING(cmd, 1); + get_lookup_bitmask(i, c, *av); } else { cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); } diff --git a/sbin/ipfw/tests/test_add_rule.py b/sbin/ipfw/tests/test_add_rule.py index c2c4bf0b360c..4360c5f87c15 100755 --- a/sbin/ipfw/tests/test_add_rule.py +++ b/sbin/ipfw/tests/test_add_rule.py @@ -37,7 +37,11 @@ from atf_python.sys.netpfil.ipfw.insns import InsnProto from atf_python.sys.netpfil.ipfw.insns import InsnReject from atf_python.sys.netpfil.ipfw.insns import InsnTable from atf_python.sys.netpfil.ipfw.insns import InsnU32 +from atf_python.sys.netpfil.ipfw.insns import InsnKidx +from atf_python.sys.netpfil.ipfw.insns import InsnLookup from atf_python.sys.netpfil.ipfw.insns import IpFwOpcode +from atf_python.sys.netpfil.ipfw.insn_headers import IpFwTableLookupType +from atf_python.sys.netpfil.ipfw.insn_headers import IpFwTableValueType from atf_python.sys.netpfil.ipfw.ioctl import CTlv from atf_python.sys.netpfil.ipfw.ioctl import CTlvRule from atf_python.sys.netpfil.ipfw.ioctl import IpFwTlvType @@ -153,14 +157,226 @@ class TestAddRule(BaseTest): NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), ], "insns": [ - InsnU32(IpFwOpcode.O_IP_SRC_LOOKUP, u32=1), - InsnU32(IpFwOpcode.O_IP_DST_LOOKUP, u32=2), + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnKidx(IpFwOpcode.O_IP_DST_LOOKUP, kidx=2), InsnEmpty(IpFwOpcode.O_ACCEPT), ], }, }, id="test_tables", ), + pytest.param( + { + "in": "add allow ip from table(AAA) to any lookup dst-ip BBB", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), + ], + "insns": [ + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnLookup(IpFwOpcode.O_TABLE_LOOKUP, + kidx=2, + arg1=InsnLookup.compile_arg1(lookup_type=IpFwTableLookupType.LOOKUP_DST_IP), + ), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_lookup_no_mask", + ), + pytest.param( + { + "in": "add allow ip from table(AAA) to any lookup mark:0xf00baa1 BBB", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), + ], + "insns": [ + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnLookup(IpFwOpcode.O_TABLE_LOOKUP, + kidx=2, + arg1=InsnLookup.compile_arg1(lookup_type=IpFwTableLookupType.LOOKUP_MARK, bitmask=True), + bitmask=0xf00baa1), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_lookup_u32_mask", + ), + pytest.param( + { + "in": "add allow ip from table(AAA) to any lookup src-mac:1a:2b:3c:4d:5e:6f BBB", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), + ], + "insns": [ + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnLookup(IpFwOpcode.O_TABLE_LOOKUP, + kidx=2, + arg1=InsnLookup.compile_arg1(lookup_type=IpFwTableLookupType.LOOKUP_SRC_MAC, bitmask=True), + bitmask="1a:2b:3c:4d:5e:6f"), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_lookup_mac_mask", + ), + pytest.param( + { + "in": "add allow ip from table(AAA) to any lookup dst-ip4:1715004 BBB", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), + ], + "insns": [ + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnLookup(IpFwOpcode.O_TABLE_LOOKUP, + kidx=2, + arg1=InsnLookup.compile_arg1(lookup_type=IpFwTableLookupType.LOOKUP_DST_IP4, bitmask=True), + bitmask="60.43.26.0"), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_lookup_dst_ip4_numeric", + ), + pytest.param( + { + "in": "add allow ip from table(AAA) to any lookup src-ip4:0.0.0.255 BBB", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), + ], + "insns": [ + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnLookup(IpFwOpcode.O_TABLE_LOOKUP, + kidx=2, + arg1=InsnLookup.compile_arg1(lookup_type=IpFwTableLookupType.LOOKUP_SRC_IP4, bitmask=True), + bitmask="0.0.0.255"), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_lookup_src_ip4_addr", + ), + pytest.param( + { + "in": "add allow ip from table(AAA) to any lookup jail:0.0.252.128 BBB", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), + ], + "insns": [ + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnLookup(IpFwOpcode.O_TABLE_LOOKUP, + kidx=2, + arg1=InsnLookup.compile_arg1(lookup_type=IpFwTableLookupType.LOOKUP_JAIL, bitmask=True), + bitmask=64640), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_lookup_jail_addr", + ), + pytest.param( + { + "in": "add allow ip from table(AAA) to any lookup dst-ip6:ffff:ffff:f00:baaa:b00c:: BBB", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"), + ], + "insns": [ + InsnKidx(IpFwOpcode.O_IP_SRC_LOOKUP, kidx=1), + InsnLookup(IpFwOpcode.O_TABLE_LOOKUP, + kidx=2, + arg1=InsnLookup.compile_arg1(lookup_type=IpFwTableLookupType.LOOKUP_DST_IP6, bitmask=True), + bitmask="ffff:ffff:f00:baaa:b00c::"), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_lookup_dst_ip6", + ), + pytest.param( + { + "in": "add allow ip from table(AAA,16777215) to any", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + ], + "insns": [ + InsnLookup(IpFwOpcode.O_IP_SRC_LOOKUP, + kidx=1, + arg1=InsnLookup.compile_arg1(value_type=IpFwTableValueType.TVALUE_TAG), + value=16777215), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_check_value_legacy", + ), + pytest.param( + { + "in": "add allow ip from table(AAA,nat=1231) to any", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + ], + "insns": [ + InsnLookup(IpFwOpcode.O_IP_SRC_LOOKUP, + kidx=1, + arg1=InsnLookup.compile_arg1(value_type=IpFwTableValueType.TVALUE_NAT), + value=1231), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_check_value_nat", + ), + pytest.param( + { + "in": "add allow ip from table(AAA,nh4=10.20.30.40) to any", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + ], + "insns": [ + InsnLookup(IpFwOpcode.O_IP_SRC_LOOKUP, + kidx=1, + arg1=InsnLookup.compile_arg1(value_type=IpFwTableValueType.TVALUE_NH4), + value="10.20.30.40"), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_check_value_nh4", + ), + pytest.param( + { + "in": "add allow ip from table(AAA,nh6=ff02:1234:b00c::abcd) to any", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=1, name="AAA"), + ], + "insns": [ + InsnLookup(IpFwOpcode.O_IP_SRC_LOOKUP, + kidx=1, + arg1=InsnLookup.compile_arg1(value_type=IpFwTableValueType.TVALUE_NH6), + value="ff02:1234:b00c::abcd"), + InsnEmpty(IpFwOpcode.O_ACCEPT), + ], + }, + }, + id="test_tables_check_value_nh6", + ), pytest.param( { "in": "add allow ip from any to 1.2.3.4 // test comment", @@ -183,7 +399,7 @@ class TestAddRule(BaseTest): ], "insns": [ InsnIp(IpFwOpcode.O_IP_DST, ip="1.2.3.4"), - InsnU32(IpFwOpcode.O_EXTERNAL_ACTION, u32=1), + InsnKidx(IpFwOpcode.O_EXTERNAL_ACTION, kidx=1), Insn(IpFwOpcode.O_EXTERNAL_DATA, arg1=123), ], }, @@ -192,20 +408,20 @@ class TestAddRule(BaseTest): ), pytest.param( { - "in": "add eaction ntpv6 AAA ip from any to 1.2.3.4", + "in": "add eaction nptv6 AAA ip from any to 1.2.3.4", "out": { "objs": [ - NTlv(IpFwTlvType.IPFW_TLV_EACTION, idx=1, name="ntpv6"), + NTlv(IpFwTlvType.IPFW_TLV_EACTION, idx=1, name="nptv6"), NTlv(0, idx=2, name="AAA"), ], "insns": [ InsnIp(IpFwOpcode.O_IP_DST, ip="1.2.3.4"), - InsnU32(IpFwOpcode.O_EXTERNAL_ACTION, u32=1), - InsnU32(IpFwOpcode.O_EXTERNAL_INSTANCE, u32=2), + InsnKidx(IpFwOpcode.O_EXTERNAL_ACTION, kidx=1), + InsnKidx(IpFwOpcode.O_EXTERNAL_INSTANCE, kidx=2), ], }, }, - id="test_eaction_ntp", + id="test_eaction_nptv6", ), pytest.param( { @@ -228,7 +444,7 @@ class TestAddRule(BaseTest): ], "insns": [ InsnComment(comment="test comment"), - InsnU32(IpFwOpcode.O_CHECK_STATE, u32=1), + InsnKidx(IpFwOpcode.O_CHECK_STATE, kidx=1), ], }, }, @@ -242,9 +458,9 @@ class TestAddRule(BaseTest): NTlv(IpFwTlvType.IPFW_TLV_STATE_NAME, idx=1, name="OUT"), ], "insns": [ - InsnU32(IpFwOpcode.O_PROBE_STATE, u32=1), + InsnKidx(IpFwOpcode.O_PROBE_STATE, kidx=1), Insn(IpFwOpcode.O_PROTO, arg1=6), - InsnU32(IpFwOpcode.O_KEEP_STATE, u32=1), + InsnKidx(IpFwOpcode.O_KEEP_STATE, kidx=1), InsnEmpty(IpFwOpcode.O_ACCEPT), ], }, @@ -260,7 +476,7 @@ class TestAddRule(BaseTest): ], "insns": [ Insn(IpFwOpcode.O_PROTO, arg1=6), - InsnU32(IpFwOpcode.O_KEEP_STATE, u32=1), + InsnKidx(IpFwOpcode.O_KEEP_STATE, kidx=1), InsnEmpty(IpFwOpcode.O_ACCEPT), ], }, @@ -373,6 +589,9 @@ class TestAddRule(BaseTest): pytest.param( ("skipto 42", InsnU32(IpFwOpcode.O_SKIPTO, u32=42)), id="skipto_42" ), + pytest.param( + ("skipto 4200", InsnU32(IpFwOpcode.O_SKIPTO, u32=4200)), id="skipto_4200" + ), pytest.param( ("netgraph 42", Insn(IpFwOpcode.O_NETGRAPH, arg1=42)), id="netgraph_42" ), diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index f3b2dc051c7d..f7372f41a48a 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -216,8 +216,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_VERREVPATH = 36, /* none */ O_VERSRCREACH = 37, /* none */ - O_PROBE_STATE = 38, /* v0:arg1=kidx, v1:kidx=kidx */ - O_KEEP_STATE = 39, /* v0:arg1=kidx, v1:kidx=kidx */ + O_PROBE_STATE = 38, /* kidx=kidx */ + O_KEEP_STATE = 39, /* kidx=kidx */ O_LIMIT = 40, /* ipfw_insn_limit */ O_LIMIT_PARENT = 41, /* dyn_type, not an opcode. */ @@ -228,13 +228,12 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_LOG = 42, /* ipfw_insn_log */ O_PROB = 43, /* u32 = match probability */ - O_CHECK_STATE = 44, /* v0:arg1=kidx, v1:kidx=kidx */ + O_CHECK_STATE = 44, /* kidx=kidx */ O_ACCEPT = 45, /* none */ O_DENY = 46, /* none */ O_REJECT = 47, /* arg1=icmp arg (same as deny) */ O_COUNT = 48, /* none */ - O_SKIPTO = 49, /* v0:arg1=next rule number */ - /* v1:kidx= next rule number */ + O_SKIPTO = 49, /* u32= next rule number */ O_PIPE = 50, /* arg1=pipe number */ O_QUEUE = 51, /* arg1=queue number */ O_DIVERT = 52, /* arg1=port number */ @@ -248,10 +247,12 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ * More opcodes. */ O_IPSEC = 58, /* has ipsec history */ - O_IP_SRC_LOOKUP = 59, /* v0:arg1=table number, u32=value */ - /* v1:kidx=name, u32=value, arg1=key */ - O_IP_DST_LOOKUP = 60, /* arg1=table number, u32=value */ - /* v1:kidx=name, u32=value, arg1=key */ + O_IP_SRC_LOOKUP = 59, /* kidx=name */ + /* lookup: kidx=name, arg1=key */ + /* and flags, bitmask */ + O_IP_DST_LOOKUP = 60, /* kidx=name */ + /* lookup: kidx=name, arg1=key */ + /* and flags, bitmask */ O_ANTISPOOF = 61, /* none */ O_JAIL = 62, /* u32 = id */ O_ALTQ = 63, /* u32 = altq classif. qid */ @@ -286,31 +287,36 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ *** 1425 LINES SKIPPED ***
