From 88203b1140961bf9dbf1c167717dd2fc5f4fde81 Mon Sep 17 00:00:00 2001 From: melifaro Date: Thu, 8 May 2014 19:11:41 +0000 Subject: [PATCH] Merge r258708, r258711, r260247, r261117. r258708: Check ipfw table numbers in both user and kernel space before rule addition. Found by: Saychik Pavel r258711: Simplify O_NAT opcode handling. r260247: Use rnh_matchaddr instead of rnh_lookup for longest-prefix match. rnh_lookup is effectively the same as rnh_matchaddr if called with empy network mask. r261117: Reorder struct ip_fw_chain: * move rarely-used fields down * move uh_lock to different cacheline * remove some usused fields git-svn-id: svn://svn.freebsd.org/base/stable/10@265700 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sbin/ipfw/ipfw2.c | 45 +++++++++++++++++++++++++------- sbin/ipfw/ipfw2.h | 2 ++ sys/netpfil/ipfw/ip_fw2.c | 37 ++++++++++++-------------- sys/netpfil/ipfw/ip_fw_private.h | 18 +++++++------ sys/netpfil/ipfw/ip_fw_sockopt.c | 4 +-- sys/netpfil/ipfw/ip_fw_table.c | 6 ++--- 6 files changed, 69 insertions(+), 43 deletions(-) diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 196067d73..98b25b320 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -60,6 +60,8 @@ int resvd_set_number = RESVD_SET; int ipfw_socket = -1; +uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */ + #ifndef s6_addr32 #define s6_addr32 __u6_addr.__u6_addr32 #endif @@ -2203,6 +2205,7 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen) { int len = 0; uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; + uint32_t tables_max; cmd->o.len &= ~F_LEN_MASK; /* zero len */ @@ -2221,6 +2224,10 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen) *p++ = '\0'; cmd->o.opcode = O_IP_DST_LOOKUP; cmd->o.arg1 = strtoul(av + 6, NULL, 0); + tables_max = ipfw_get_tables_max(); + if (cmd->o.arg1 > tables_max) + errx(EX_USAGE, "The table number exceeds the maximum " + "allowed value (%u)", tables_max - 1); if (p) { cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); d[0] = strtoul(p, NULL, 0); @@ -4118,6 +4125,33 @@ ipfw_flush(int force) static void table_list(uint16_t num, int need_header); static void table_fill_xentry(char *arg, ipfw_table_xentry *xent); +/* + * Retrieve maximum number of tables supported by ipfw(4) module. + */ +uint32_t +ipfw_get_tables_max() +{ + size_t len; + uint32_t tables_max; + + if (ipfw_tables_max != 0) + return (ipfw_tables_max); + + len = sizeof(tables_max); + if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, + NULL, 0) == -1) { + if (co.test_only) + tables_max = 128; /* Old conservative default */ + else + errx(1, "Can't determine maximum number of ipfw tables." + " Perhaps you forgot to load ipfw module?"); + } + + ipfw_tables_max = tables_max; + + return (ipfw_tables_max); +} + /* * This one handles all table-related commands * ipfw table N add addr[/masklen] [value] @@ -4131,19 +4165,10 @@ ipfw_table_handler(int ac, char *av[]) ipfw_table_xentry xent; int do_add; int is_all; - size_t len; uint32_t a; uint32_t tables_max; - len = sizeof(tables_max); - if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, - NULL, 0) == -1) { - if (co.test_only) - tables_max = 128; /* Old conservative default */ - else - errx(1, "Can't determine maximum number of ipfw tables." - " Perhaps you forgot to load ipfw module?"); - } + tables_max = ipfw_get_tables_max(); memset(&xent, 0, sizeof(xent)); diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 9abe6ee8b..6e895b862 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -228,6 +228,8 @@ char const *match_value(struct _s_x *p, int value); int do_cmd(int optname, void *optval, uintptr_t optlen); +uint32_t ipfw_get_tables_max(void); + struct in6_addr; void n2mask(struct in6_addr *mask, int n); int contigmask(uint8_t *p, int len); diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index cd466bd4e..764696cf8 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -2404,38 +2404,35 @@ do { \ } case O_NAT: + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ if (!IPFW_NAT_LOADED) { retval = IP_FW_DENY; - } else { - struct cfg_nat *t; - int nat_id; + break; + } - set_match(args, f_pos, chain); - /* Check if this is 'global' nat rule */ - if (cmd->arg1 == 0) { - retval = ipfw_nat_ptr(args, NULL, m); - l = 0; - done = 1; - break; - } - t = ((ipfw_insn_nat *)cmd)->nat; - if (t == NULL) { + struct cfg_nat *t; + int nat_id; + + set_match(args, f_pos, chain); + /* Check if this is 'global' nat rule */ + if (cmd->arg1 == 0) { + retval = ipfw_nat_ptr(args, NULL, m); + break; + } + t = ((ipfw_insn_nat *)cmd)->nat; + if (t == NULL) { nat_id = IP_FW_ARG_TABLEARG(cmd->arg1); t = (*lookup_nat_ptr)(&chain->nat, nat_id); if (t == NULL) { retval = IP_FW_DENY; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ break; } if (cmd->arg1 != IP_FW_TABLEARG) ((ipfw_insn_nat *)cmd)->nat = t; - } - retval = ipfw_nat_ptr(args, t, m); } - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ + retval = ipfw_nat_ptr(args, t, m); break; case O_REASS: { @@ -2660,7 +2657,7 @@ vnet_ipfw_init(const void *unused) rule->set = RESVD_SET; rule->cmd[0].len = 1; rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY; - chain->rules = chain->default_rule = chain->map[0] = rule; + chain->default_rule = chain->map[0] = rule; chain->id = rule->id = 1; IPFW_LOCK_INIT(chain); diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index a8d7eea4e..e4a2f3195 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -213,25 +213,27 @@ VNET_DECLARE(unsigned int, fw_tables_max); #define V_fw_tables_max VNET(fw_tables_max) struct ip_fw_chain { - struct ip_fw *rules; /* list of rules */ - struct ip_fw *reap; /* list of rules to reap */ - struct ip_fw *default_rule; - int n_rules; /* number of static rules */ - int static_len; /* total len of static rules */ struct ip_fw **map; /* array of rule ptrs to ease lookup */ + uint32_t id; /* ruleset id */ + int n_rules; /* number of static rules */ LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */ struct radix_node_head **tables; /* IPv4 tables */ struct radix_node_head **xtables; /* extended tables */ uint8_t *tabletype; /* Array of table types */ #if defined( __linux__ ) || defined( _WIN32 ) spinlock_t rwmtx; - spinlock_t uh_lock; #else struct rwlock rwmtx; +#endif + int static_len; /* total len of static rules */ + uint32_t gencnt; /* NAT generation count */ + struct ip_fw *reap; /* list of rules to reap */ + struct ip_fw *default_rule; +#if defined( __linux__ ) || defined( _WIN32 ) + spinlock_t uh_lock; +#else struct rwlock uh_lock; /* lock for upper half */ #endif - uint32_t id; /* ruleset id */ - uint32_t gencnt; /* generation count */ }; struct sockopt; /* used by tcp_var.h */ diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index cb9c89cc6..3c342f709 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -159,7 +159,7 @@ ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) int i, l, insert_before; struct ip_fw **map; /* the new array of pointers */ - if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1) + if (chain->map == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE - 1) return (EINVAL); l = RULESIZE(input_rule); @@ -653,7 +653,7 @@ check_ipfw_struct(struct ip_fw *rule, int size) case O_IP_SRC_LOOKUP: case O_IP_DST_LOOKUP: - if (cmd->arg1 >= IPFW_TABLES_MAX) { + if (cmd->arg1 >= V_fw_tables_max) { printf("ipfw: invalid table number %d\n", cmd->arg1); return (EINVAL); diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index 95cff5c6a..31eebfe80 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -544,7 +544,7 @@ ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, return (0); KEY_LEN(sa) = KEY_LEN_INET; sa.sin_addr.s_addr = addr; - ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); + ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh)); if (ent != NULL) { *val = ent->value; return (1); @@ -570,7 +570,7 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, case IPFW_TABLE_CIDR: KEY_LEN(sa6) = KEY_LEN_INET6; memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); - xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); + xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh)); break; case IPFW_TABLE_INTERFACE: @@ -578,7 +578,7 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; /* Assume direct match */ /* FIXME: Add interface pattern matching */ - xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + xent = (struct table_xentry *)(rnh->rnh_matchaddr(&iface, rnh)); break; default: -- 2.45.0