diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 1483dd09b131..fc83ecc34a91 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1391,6 +1391,20 @@ of source and destination addresses and ports can be specified. Currently, only IPv4 flows are supported. +.It Cm lookup Bro Cm dst-ip | dst-port | src-ip | src-port | uid | jail Brc Ar N +Search an entry in lookup table +.Ar N +that matches the field specified as argument. +If not found, the match fails. +Otherwise, the match succeeds and +.Cm tablearg +is set to the value extracted from the table. +.Br +This option can be useful to quickly dispatch traffic based on +certain packet fields. +See the +.Sx LOOKUP TABLES +section below for more information on lookup tables. .It Cm { MAC | mac } Ar dst-mac src-mac Match packets with a given .Ar dst-mac diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 7edfbd56313b..d4740c9f8552 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -224,6 +224,15 @@ static struct _s_x rule_action_params[] = { { NULL, 0 } /* terminator */ }; +/* + * 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, -1 }; + static struct _s_x rule_options[] = { { "tagged", TOK_TAGGED }, { "uid", TOK_UID }, @@ -290,6 +299,7 @@ static struct _s_x rule_options[] = { { "dst-ip6", TOK_DSTIP6}, { "src-ipv6", TOK_SRCIP6}, { "src-ip6", TOK_SRCIP6}, + { "lookup", TOK_LOOKUP}, { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ @@ -742,6 +752,16 @@ print_ip(ipfw_insn_ip *cmd, char const *s) int len = F_LEN((ipfw_insn *)cmd); uint32_t *a = ((ipfw_insn_u32 *)cmd)->d; + if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) { + uint32_t d = a[1]; + const char *arg = ""; + + if (d < sizeof(lookup_key)/sizeof(lookup_key[0])) + arg = match_value(rule_options, lookup_key[d]); + printf("%s lookup %s %d", cmd->o.len & F_NOT ? " not": "", + arg, cmd->o.arg1); + return; + } printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { @@ -3479,6 +3499,31 @@ ipfw_add(int ac, char *av[]) ac--; av++; break; + case TOK_LOOKUP: { + ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; + char *p; + int j; + + if (ac < 2) + 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) + errx(EX_USAGE, "format: cannot lookup on %s", *av); + c->d[1] = j; // i converted to option + ac--; av++; + cmd->arg1 = strtoul(*av, &p, 0); + if (p && *p) + errx(EX_USAGE, "format: lookup argument tablenum"); + ac--; av++; + } + break; + default: errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); } diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index d3ce7fb6e08a..b393a7d62fe7 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -186,6 +186,7 @@ enum tokens { TOK_FIB, TOK_SETFIB, + TOK_LOOKUP, }; /* * the following macro returns an error message if we run out of diff --git a/sys/netinet/ipfw/ip_fw2.c b/sys/netinet/ipfw/ip_fw2.c index 601d80ed734f..7010e19a1a92 100644 --- a/sys/netinet/ipfw/ip_fw2.c +++ b/sys/netinet/ipfw/ip_fw2.c @@ -2819,6 +2819,36 @@ do { \ dst_ip.s_addr : src_ip.s_addr; uint32_t v = 0; + if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) { + /* generic lookup */ + v = ((ipfw_insn_u32 *)cmd)->d[1]; + if (v == 0) + a = dst_ip.s_addr; + else if (v == 1) + a = src_ip.s_addr; + else if (offset != 0) + break; + else if (proto != IPPROTO_TCP && + proto != IPPROTO_UDP) + break; + else if (v == 2) + a = dst_port; + else if (v == 3) + a = src_port; + else if (v == 4 || v == 5) { + check_uidgid( + (ipfw_insn_u32 *)cmd, + proto, oif, + dst_ip, dst_port, + src_ip, src_port, &ucred_cache, + &ucred_lookup, args->inp); + if (v == 4 /* O_UID */) + a = ucred_cache->cr_uid; + else if (v == 5 /* O_JAIL */) + a = ucred_cache->cr_prison->pr_id; + } else + break; + } match = lookup_table(chain, cmd->arg1, a, &v); if (!match) @@ -4160,6 +4190,7 @@ check_ipfw_struct(struct ip_fw *rule, int size) return (EINVAL); } if (cmdlen != F_INSN_SIZE(ipfw_insn) && + cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && cmdlen != F_INSN_SIZE(ipfw_insn_u32)) goto bad_size; break;