diff options
Diffstat (limited to 'sys/netpfil/ipfw/ip_fw2.c')
-rw-r--r-- | sys/netpfil/ipfw/ip_fw2.c | 278 |
1 files changed, 215 insertions, 63 deletions
diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 1a5b699..c07546b 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -140,8 +140,7 @@ VNET_DEFINE(int, fw_verbose); VNET_DEFINE(u_int64_t, norule_counter); VNET_DEFINE(int, verbose_limit); -/* layer3_chain contains the list of rules for layer 3 */ -VNET_DEFINE(struct ip_fw_chain, layer3_chain); +VNET_DEFINE(struct ip_fw_contextes, ip_fw_contexts); VNET_DEFINE(int, ipfw_nat_ready) = 0; @@ -182,9 +181,6 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN, "Make the default rule accept all packets."); TUNABLE_INT("net.inet.ip.fw.default_to_accept", &default_to_accept); TUNABLE_INT("net.inet.ip.fw.tables_max", (int *)&default_fw_tables); -SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, static_count, - CTLFLAG_RD, &VNET_NAME(layer3_chain.n_rules), 0, - "Number of static rules"); #ifdef INET6 SYSCTL_DECL(_net_inet6_ip6); @@ -358,8 +354,8 @@ iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uin /* Check by name or by IP address */ if (cmd->name[0] != '\0') { /* match by name */ if (cmd->name[0] == '\1') /* use tablearg to match */ - return ipfw_lookup_table_extended(chain, cmd->p.glob, - ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE); + return (ipfw_lookup_table_extended(chain, cmd->p.glob, + ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE, NULL) != NULL); /* Check name */ if (cmd->p.glob) { if (fnmatch(cmd->name, ifp->if_xname, 0) == 0) @@ -904,6 +900,9 @@ ipfw_chk(struct ip_fw_args *args) */ struct ifnet *oif = args->oif; + if (V_ip_fw_contexts.chain[oif->if_ispare[0]] == NULL) + return (IP_FW_PASS); + int f_pos = 0; /* index of current rule in the array */ int retval = 0; @@ -954,7 +953,14 @@ ipfw_chk(struct ip_fw_args *args) */ int dyn_dir = MATCH_UNKNOWN; ipfw_dyn_rule *q = NULL; - struct ip_fw_chain *chain = &V_layer3_chain; + void *tblent = NULL, *tblent2 = NULL; + + /* XXX: WARNING - The chain is accessed unlocked here. + * There is a potential race here with context handling. + * The chain pointer will get destroyed and a NULL + * pointer dereference can happen! + */ + struct ip_fw_chain *chain = V_ip_fw_contexts.chain[oif->if_ispare[0]]; /* * We store in ulp a pointer to the upper layer protocol header. @@ -1288,6 +1294,8 @@ do { \ continue; skip_or = 0; + tblent = NULL; + tblent2 = NULL; for (l = f->cmd_len, cmd = f->cmd ; l > 0 ; l -= cmdlen, cmd += cmdlen) { int match; @@ -1402,7 +1410,7 @@ do { \ break; case O_IN: /* "out" is "not in" */ - match = (oif == NULL); + match = (args->dir == DIR_IN); break; case O_LAYER2: @@ -1438,11 +1446,18 @@ do { \ case O_IP_SRC_LOOKUP: case O_IP_DST_LOOKUP: if (is_ipv4) { + struct ether_addr *ea = NULL; + uint32_t key = (cmd->opcode == O_IP_DST_LOOKUP) ? dst_ip.s_addr : src_ip.s_addr; uint32_t v = 0; + if (args->eh) { + ea = (struct ether_addr*)((cmd->opcode == O_IP_DST_LOOKUP) ? + args->eh->ether_dhost : + args->eh->ether_shost); + } if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) { /* generic lookup. The key must be * in 32bit big-endian format. @@ -1484,22 +1499,37 @@ do { \ } else break; } - match = ipfw_lookup_table(chain, - cmd->arg1, key, &v); - if (!match) + tblent2 = ipfw_lookup_table(chain, + cmd->arg1, key, &v, ea); + if (tblent2 == NULL) { + match = 0; break; + } else + match = 1; if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) match = ((ipfw_insn_u32 *)cmd)->d[0] == v; - else + if (match) tablearg = v; } else if (is_ipv6) { + struct ether_addr *ea = NULL; uint32_t v = 0; + + if (args->eh) { + ea = (struct ether_addr*)((cmd->opcode == O_IP_DST_LOOKUP) ? + args->eh->ether_dhost : + args->eh->ether_shost); + } void *pkey = (cmd->opcode == O_IP_DST_LOOKUP) ? &args->f_id.dst_ip6: &args->f_id.src_ip6; - match = ipfw_lookup_table_extended(chain, + tblent = ipfw_lookup_table_extended(chain, cmd->arg1, pkey, &v, - IPFW_TABLE_CIDR); + IPFW_TABLE_CIDR, ea); + if (tblent == NULL) { + match = 0; + break; + } else + match = 1; if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) match = ((ipfw_insn_u32 *)cmd)->d[0] == v; if (match) @@ -2314,8 +2344,7 @@ do { \ break; case O_FORWARD_IP: - if (args->eh) /* not valid on layer2 pkts */ - break; + if (!args->eh) {/* not valid on layer2 pkts */ if (q == NULL || q->rule != f || dyn_dir == MATCH_FORWARD) { struct sockaddr_in *sa; @@ -2330,6 +2359,48 @@ do { \ args->next_hop = sa; } } + } else if (args->eh) { + struct m_tag *fwd_tag; + struct sockaddr_in *sa; + u_short sum; + + /* + * Checksum correct? (from ip_fastfwd.c) + */ + if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) + sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); + else { + if (hlen == sizeof(struct ip)) + sum = in_cksum_hdr(ip); + else + sum = in_cksum(m, hlen); + } + if (sum) { + IPSTAT_INC(ips_badsum); + retval = IP_FW_DENY; + break; + } + + /* + * Remember that we have checked the IP header and found it valid. + */ + m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID); + + sa = &(((ipfw_insn_sa *)cmd)->sa); + fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, + sizeof(struct sockaddr_in), M_NOWAIT); + if (fwd_tag == NULL) + retval = IP_FW_DENY; + else { + bcopy(sa, (fwd_tag+1), sizeof(struct sockaddr_in)); + m_tag_prepend(m, fwd_tag); + + if (in_localip(sa->sin_addr)) + m->m_flags |= M_FASTFWD_OURS; + m->m_flags |= M_IP_NEXTHOP; + } + } + retval = IP_FW_PASS; l = 0; /* exit inner loop */ done = 1; /* exit outer loop */ @@ -2337,8 +2408,7 @@ do { \ #ifdef INET6 case O_FORWARD_IP6: - if (args->eh) /* not valid on layer2 pkts */ - break; + if (!args->eh) {/* not valid on layer2 pkts */ if (q == NULL || q->rule != f || dyn_dir == MATCH_FORWARD) { struct sockaddr_in6 *sin6; @@ -2346,6 +2416,24 @@ do { \ sin6 = &(((ipfw_insn_sa6 *)cmd)->sa); args->next_hop6 = sin6; } + } else if (args->eh) { + struct m_tag *fwd_tag; + struct sockaddr_in6 *sin6; + + sin6 = &(((ipfw_insn_sa6 *)cmd)->sa); + fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, + sizeof(struct sockaddr_in6), M_NOWAIT); + if (fwd_tag == NULL) + retval = IP_FW_DENY; + else { + bcopy(sin6, (fwd_tag+1), sizeof(struct sockaddr_in6)); + m_tag_prepend(m, fwd_tag); + + if (in6_localip(&sin6->sin6_addr)) + m->m_flags |= M_FASTFWD_OURS; + m->m_flags |= M_IP6_NEXTHOP; + } + } retval = IP_FW_PASS; l = 0; /* exit inner loop */ done = 1; /* exit outer loop */ @@ -2417,7 +2505,7 @@ do { \ set_match(args, f_pos, chain); /* Check if this is 'global' nat rule */ if (cmd->arg1 == 0) { - retval = ipfw_nat_ptr(args, NULL, m); + retval = ipfw_nat_ptr(args, NULL, m, chain); break; } t = ((ipfw_insn_nat *)cmd)->nat; @@ -2432,7 +2520,7 @@ do { \ if (cmd->arg1 != IP_FW_TABLEARG) ((ipfw_insn_nat *)cmd)->nat = t; } - retval = ipfw_nat_ptr(args, t, m); + retval = ipfw_nat_ptr(args, t, m, chain); break; case O_REASS: { @@ -2502,6 +2590,10 @@ do { \ struct ip_fw *rule = chain->map[f_pos]; /* Update statistics */ IPFW_INC_RULE_COUNTER(rule, pktlen); + if (tblent != NULL) + ipfw_count_table_xentry_stats(tblent, pktlen); + if (tblent2 != NULL) + ipfw_count_table_entry_stats(tblent2, pktlen); } else { retval = IP_FW_DENY; printf("ipfw: ouch!, skip past end of rules, denying packet\n"); @@ -2536,7 +2628,9 @@ sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS) if ((error != 0) || (req->newptr == NULL)) return (error); - return (ipfw_resize_tables(&V_layer3_chain, ntables)); + for (int i = 1; i < IP_FW_MAXCTX; i++) + error += ipfw_resize_tables(V_ip_fw_contexts.chain[i], ntables); + return (error); } #endif /* @@ -2614,11 +2708,6 @@ ipfw_destroy(void) static int vnet_ipfw_init(const void *unused) { - int error; - struct ip_fw *rule = NULL; - struct ip_fw_chain *chain; - - chain = &V_layer3_chain; /* First set up some values that are compile time options */ V_autoinc_step = 100; /* bounded to 1..1000 in add_rule() */ @@ -2629,10 +2718,55 @@ vnet_ipfw_init(const void *unused) #ifdef IPFIREWALL_VERBOSE_LIMIT V_verbose_limit = IPFIREWALL_VERBOSE_LIMIT; #endif + + for (int i = 0; i < IP_FW_MAXCTX; i++) + V_ip_fw_contexts.chain[i] = NULL; + + IPFW_CTX_LOCK_INIT(); + + V_ip_fw_contexts.ifnet_arrival = EVENTHANDLER_REGISTER(ifnet_arrival_event, + ipfw_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); + + ipfw_dyn_init(); + + /* First set up some values that are compile time options */ + V_ipfw_vnet_ready = 1; /* Open for business */ + + /* + * Hook the sockopt handler and pfil hooks for ipv4 and ipv6. + * Even if the latter two fail we still keep the module alive + * because the sockopt and layer2 paths are still useful. + * ipfw[6]_hook return 0 on success, ENOENT on failure, + * so we can ignore the exact return value and just set a flag. + * + * Note that V_fw[6]_enable are manipulated by a SYSCTL_PROC so + * changes in the underlying (per-vnet) variables trigger + * immediate hook()/unhook() calls. + * In layer2 we have the same behaviour, except that V_ether_ipfw + * is checked on each packet because there are no pfil hooks. + */ + V_ip_fw_ctl_ptr = ipfw_ctl; + return ipfw_attach_hooks(1); +} + +int +ipfw_context_init(int index) +{ + struct ip_fw_chain *chain; + struct ip_fw *rule = NULL; + + if (index >= IP_FW_MAXCTX) + return (-1); + + TAILQ_INIT(&V_ip_fw_contexts.iflist[index]); + + chain = V_ip_fw_contexts.chain[index]; + + IPFW_LOCK_INIT(chain); + #ifdef IPFIREWALL_NAT LIST_INIT(&chain->nat); #endif - /* insert the default rule and create the initial map */ chain->n_rules = 1; chain->static_len = sizeof(struct ip_fw); @@ -2642,13 +2776,7 @@ vnet_ipfw_init(const void *unused) /* Set initial number of tables */ V_fw_tables_max = default_fw_tables; - error = ipfw_init_tables(chain); - if (error) { - printf("ipfw2: setting up tables failed\n"); - free(chain->map, M_IPFW); - free(rule, M_IPFW); - return (ENOSPC); - } + ipfw_init_tables(chain); /* fill and insert the default rule */ rule->act_ofs = 0; @@ -2660,28 +2788,13 @@ vnet_ipfw_init(const void *unused) chain->default_rule = chain->map[0] = rule; chain->id = rule->id = 1; - IPFW_LOCK_INIT(chain); - ipfw_dyn_init(chain); - - /* First set up some values that are compile time options */ - V_ipfw_vnet_ready = 1; /* Open for business */ + /* + * This can potentially be done on first dynamic rule + * being added to chain. + */ + resize_dynamic_table(chain, V_curr_dyn_buckets); - /* - * Hook the sockopt handler and pfil hooks for ipv4 and ipv6. - * Even if the latter two fail we still keep the module alive - * because the sockopt and layer2 paths are still useful. - * ipfw[6]_hook return 0 on success, ENOENT on failure, - * so we can ignore the exact return value and just set a flag. - * - * Note that V_fw[6]_enable are manipulated by a SYSCTL_PROC so - * changes in the underlying (per-vnet) variables trigger - * immediate hook()/unhook() calls. - * In layer2 we have the same behaviour, except that V_ether_ipfw - * is checked on each packet because there are no pfil hooks. - */ - V_ip_fw_ctl_ptr = ipfw_ctl; - error = ipfw_attach_hooks(1); - return (error); + return (0); } /* @@ -2690,11 +2803,9 @@ vnet_ipfw_init(const void *unused) static int vnet_ipfw_uninit(const void *unused) { - struct ip_fw *reap, *rule; - struct ip_fw_chain *chain = &V_layer3_chain; - int i; V_ipfw_vnet_ready = 0; /* tell new callers to go away */ + /* * disconnect from ipv4, ipv6, layer2 and sockopt. * Then grab, release and grab again the WLOCK so we make @@ -2702,12 +2813,51 @@ vnet_ipfw_uninit(const void *unused) */ (void)ipfw_attach_hooks(0 /* detach */); V_ip_fw_ctl_ptr = NULL; - IPFW_UH_WLOCK(chain); - IPFW_UH_WUNLOCK(chain); ipfw_dyn_uninit(0); /* run the callout_drain */ + IPFW_CTX_WLOCK(); + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, V_ip_fw_contexts.ifnet_arrival); + for (int i = 0; i < IP_FW_MAXCTX; i++) { + ipfw_context_uninit(i); + } + IPFW_CTX_WUNLOCK(); + IPFW_CTX_LOCK_DESTROY(); + + ipfw_dyn_uninit(1); /* free the remaining parts */ + + return (0); +} + +int +ipfw_context_uninit(int index) +{ + struct ip_fw_chain *chain; + struct ip_fw_ctx_iflist *ifl; + struct ip_fw *reap, *rule; + struct ifnet *ifp; + int i; + + if (index >= IP_FW_MAXCTX) + return (-1); + + chain = V_ip_fw_contexts.chain[index]; + if (chain == NULL) + return (0); + + while (!TAILQ_EMPTY(&V_ip_fw_contexts.iflist[index])) { + ifl = TAILQ_FIRST(&V_ip_fw_contexts.iflist[index]); + TAILQ_REMOVE(&V_ip_fw_contexts.iflist[index], ifl, entry); + ifp = ifunit(ifl->ifname); + if (ifp != NULL) + ifp->if_ispare[0] = 0; + free(ifl, M_IPFW); + } + IPFW_UH_WLOCK(chain); + IPFW_UH_WUNLOCK(chain); + IPFW_UH_WLOCK(chain); + ipfw_destroy_tables(chain); reap = NULL; IPFW_WLOCK(chain); @@ -2723,8 +2873,10 @@ vnet_ipfw_uninit(const void *unused) if (reap != NULL) ipfw_reap_rules(reap); IPFW_LOCK_DESTROY(chain); - ipfw_dyn_uninit(1); /* free the remaining parts */ - return 0; + + free(chain, M_IPFW); + + return (0); } /* |