diff options
Diffstat (limited to 'sys/netpfil/ipfw')
-rw-r--r-- | sys/netpfil/ipfw/ip_dn_io.c | 1 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_dummynet.c | 3 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw2.c | 278 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_dynamic.c | 23 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_nat.c | 85 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_pfil.c | 22 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_private.h | 57 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_sockopt.c | 330 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_table.c | 240 |
9 files changed, 875 insertions, 164 deletions
diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c index 9c985d1..b7213ce 100644 --- a/sys/netpfil/ipfw/ip_dn_io.c +++ b/sys/netpfil/ipfw/ip_dn_io.c @@ -750,6 +750,7 @@ dummynet_send(struct mbuf *m) * to carry reinject info. */ dst = pkt->dn_dir; + pkt->rule.info |= IPFW_IS_DUMMYNET; ifp = pkt->ifp; tag->m_tag_cookie = MTAG_IPFW_RULE; tag->m_tag_id = 0; diff --git a/sys/netpfil/ipfw/ip_dummynet.c b/sys/netpfil/ipfw/ip_dummynet.c index c8331e9..09fbe84 100644 --- a/sys/netpfil/ipfw/ip_dummynet.c +++ b/sys/netpfil/ipfw/ip_dummynet.c @@ -152,7 +152,7 @@ ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg) op = "Clamp"; } else return *v; - if (op && msg) + if (op && msg && bootverbose) printf("%s %s to %d (was %d)\n", op, msg, *v, oldv); return *v; } @@ -2631,7 +2631,6 @@ static moduledata_t dummynet_mod = { #define DN_SI_SUB SI_SUB_PROTO_IFATTACHDOMAIN #define DN_MODEV_ORD (SI_ORDER_ANY - 128) /* after ipfw */ DECLARE_MODULE(dummynet, dummynet_mod, DN_SI_SUB, DN_MODEV_ORD); -MODULE_DEPEND(dummynet, ipfw, 2, 2, 2); MODULE_VERSION(dummynet, 3); /* 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); } /* diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c index ad957e9..2f802fc 100644 --- a/sys/netpfil/ipfw/ip_fw_dynamic.c +++ b/sys/netpfil/ipfw/ip_fw_dynamic.c @@ -121,11 +121,9 @@ struct ipfw_dyn_bucket { */ static VNET_DEFINE(struct ipfw_dyn_bucket *, ipfw_dyn_v); static VNET_DEFINE(u_int32_t, dyn_buckets_max); -static VNET_DEFINE(u_int32_t, curr_dyn_buckets); static VNET_DEFINE(struct callout, ipfw_timeout); #define V_ipfw_dyn_v VNET(ipfw_dyn_v) #define V_dyn_buckets_max VNET(dyn_buckets_max) -#define V_curr_dyn_buckets VNET(curr_dyn_buckets) #define V_ipfw_timeout VNET(ipfw_timeout) static VNET_DEFINE(uma_zone_t, ipfw_dyn_rule_zone); @@ -181,6 +179,8 @@ static VNET_DEFINE(u_int32_t, dyn_max); /* max # of dynamic rules */ static int last_log; /* Log ratelimiting */ +VNET_DEFINE(u_int32_t, curr_dyn_buckets); + static void ipfw_dyn_tick(void *vnetx); static void check_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int, int, int); @@ -472,7 +472,7 @@ ipfw_dyn_unlock(ipfw_dyn_rule *q) IPFW_BUCK_UNLOCK(q->bucket); } -static int +int resize_dynamic_table(struct ip_fw_chain *chain, int nbuckets) { int i, k, nbuckets_old; @@ -975,7 +975,6 @@ ipfw_dyn_send_ka(struct mbuf **mtailp, ipfw_dyn_rule *q) static void ipfw_dyn_tick(void * vnetx) { - struct ip_fw_chain *chain; int check_ka = 0; #ifdef VIMAGE struct vnet *vp = vnetx; @@ -983,7 +982,6 @@ ipfw_dyn_tick(void * vnetx) CURVNET_SET(vp); - chain = &V_layer3_chain; /* Run keepalive checks every keepalive_period iff ka is enabled */ if ((V_dyn_keepalive_last + V_dyn_keepalive_period <= time_uptime) && @@ -992,7 +990,12 @@ ipfw_dyn_tick(void * vnetx) check_ka = 1; } - check_dyn_rules(chain, NULL, RESVD_SET, check_ka, 1); + IPFW_CTX_RLOCK(); + for (int i = 1; i < IP_FW_MAXCTX; i++) { + if (V_ip_fw_contexts.chain[i] != NULL) + check_dyn_rules(V_ip_fw_contexts.chain[i], NULL, RESVD_SET, check_ka, 1); + } + IPFW_CTX_RUNLOCK(); callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, vnetx, 0); @@ -1308,7 +1311,7 @@ ipfw_expire_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, int set) } void -ipfw_dyn_init(struct ip_fw_chain *chain) +ipfw_dyn_init() { V_ipfw_dyn_v = NULL; @@ -1337,12 +1340,6 @@ ipfw_dyn_init(struct ip_fw_chain *chain) uma_zone_set_max(V_ipfw_dyn_rule_zone, V_dyn_max); callout_init(&V_ipfw_timeout, CALLOUT_MPSAFE); - - /* - * This can potentially be done on first dynamic rule - * being added to chain. - */ - resize_dynamic_table(chain, V_curr_dyn_buckets); } void diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c index 0fb4534..627603d 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -64,26 +64,33 @@ ifaddr_change(void *arg __unused, struct ifnet *ifp) KASSERT(curvnet == ifp->if_vnet, ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); - chain = &V_layer3_chain; - IPFW_WLOCK(chain); - /* Check every nat entry... */ - LIST_FOREACH(ptr, &chain->nat, _next) { - /* ...using nic 'ifp->if_xname' as dynamic alias address. */ - if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) + + IPFW_CTX_RLOCK(); + for (int i = 1; i < IP_FW_MAXCTX; i++) { + chain = V_ip_fw_contexts.chain[i]; + if (chain == NULL) continue; - if_addr_rlock(ifp); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr == NULL) - continue; - if (ifa->ifa_addr->sa_family != AF_INET) + IPFW_WLOCK(chain); + /* Check every nat entry... */ + LIST_FOREACH(ptr, &chain->nat, _next) { + /* ...using nic 'ifp->if_xname' as dynamic alias address. */ + if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) continue; - ptr->ip = ((struct sockaddr_in *) - (ifa->ifa_addr))->sin_addr; - LibAliasSetAddress(ptr->lib, ptr->ip); + if_addr_rlock(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + ptr->ip = ((struct sockaddr_in *) + (ifa->ifa_addr))->sin_addr; + LibAliasSetAddress(ptr->lib, ptr->ip); + } + if_addr_runlock(ifp); } - if_addr_runlock(ifp); + IPFW_WUNLOCK(chain); } - IPFW_WUNLOCK(chain); + IPFW_CTX_RUNLOCK(); } /* @@ -206,18 +213,18 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) /* * ipfw_nat - perform mbuf header translation. * - * Note V_layer3_chain has to be locked while calling ipfw_nat() in + * Note *chain has to be locked while calling ipfw_nat() in * 'global' operation mode (t == NULL). * */ static int -ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) +ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m, + struct ip_fw_chain *chain) { struct mbuf *mcl; struct ip *ip; /* XXX - libalias duct tape */ int ldt, retval, found; - struct ip_fw_chain *chain; char *c; ldt = 0; @@ -276,7 +283,6 @@ ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) } found = 0; - chain = &V_layer3_chain; IPFW_RLOCK_ASSERT(chain); /* Check every nat entry... */ LIST_FOREACH(t, &chain->nat, _next) { @@ -391,11 +397,10 @@ lookup_nat(struct nat_list *l, int nat_id) } static int -ipfw_nat_cfg(struct sockopt *sopt) +ipfw_nat_cfg(struct sockopt *sopt, struct ip_fw_chain *chain) { struct cfg_nat *cfg, *ptr; char *buf; - struct ip_fw_chain *chain = &V_layer3_chain; size_t len; int gencnt, error = 0; @@ -468,10 +473,9 @@ out: } static int -ipfw_nat_del(struct sockopt *sopt) +ipfw_nat_del(struct sockopt *sopt, struct ip_fw_chain *chain) { struct cfg_nat *ptr; - struct ip_fw_chain *chain = &V_layer3_chain; int i; sooptcopyin(sopt, &i, sizeof i, sizeof i); @@ -492,9 +496,8 @@ ipfw_nat_del(struct sockopt *sopt) } static int -ipfw_nat_get_cfg(struct sockopt *sopt) +ipfw_nat_get_cfg(struct sockopt *sopt, struct ip_fw_chain *chain) { - struct ip_fw_chain *chain = &V_layer3_chain; struct cfg_nat *n; struct cfg_redir *r; struct cfg_spool *s; @@ -552,14 +555,11 @@ retry: } static int -ipfw_nat_get_log(struct sockopt *sopt) +ipfw_nat_get_log(struct sockopt *sopt, struct ip_fw_chain *chain) { uint8_t *data; struct cfg_nat *ptr; int i, size; - struct ip_fw_chain *chain; - - chain = &V_layer3_chain; IPFW_RLOCK(chain); /* one pass to count, one to copy the data */ @@ -604,17 +604,22 @@ vnet_ipfw_nat_uninit(const void *arg __unused) struct cfg_nat *ptr, *ptr_temp; struct ip_fw_chain *chain; - chain = &V_layer3_chain; - IPFW_WLOCK(chain); - LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { - LIST_REMOVE(ptr, _next); - del_redir_spool_cfg(ptr, &ptr->redir_chain); - LibAliasUninit(ptr->lib); - free(ptr, M_IPFW); + IPFW_CTX_RLOCK(); + for (int i = 1; i < IP_FW_MAXCTX; i++) { + chain = V_ip_fw_contexts.chain[i]; + IPFW_WLOCK(chain); + LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { + LIST_REMOVE(ptr, _next); + del_redir_spool_cfg(ptr, &ptr->redir_chain); + LibAliasUninit(ptr->lib); + free(ptr, M_IPFW); + } + flush_nat_ptrs(chain, -1 /* flush all */); + V_ipfw_nat_ready = 0; + IPFW_WUNLOCK(chain); } - flush_nat_ptrs(chain, -1 /* flush all */); - V_ipfw_nat_ready = 0; - IPFW_WUNLOCK(chain); + IPFW_CTX_RUNLOCK(); + return (0); } diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c index 2bcd1dd..bf225b8 100644 --- a/sys/netpfil/ipfw/ip_fw_pfil.c +++ b/sys/netpfil/ipfw/ip_fw_pfil.c @@ -143,8 +143,9 @@ again: } args.m = *m0; - args.oif = dir == DIR_OUT ? ifp : NULL; + args.oif = ifp; args.inp = inp; + args.dir = dir; ipfw = ipfw_chk(&args); *m0 = args.m; @@ -314,9 +315,8 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, /* XXX can we free it after use ? */ mtag->m_tag_id = PACKET_TAG_NONE; r = (struct ipfw_rule_ref *)(mtag + 1); - if (r->info & IPFW_ONEPASS) - return (0); - args.rule = *r; + m_tag_delete(*m0, mtag); + return (0); } /* I need some amt of data to be contiguous */ @@ -333,12 +333,15 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, save_eh = *eh; /* save copy for restore below */ m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ + dir = dir == PFIL_IN ? DIR_IN : DIR_OUT; + args.m = m; /* the packet we are looking at */ - args.oif = dir == PFIL_OUT ? dst: NULL; /* destination, if any */ + args.oif = dst; /* destination, if any */ args.next_hop = NULL; /* we do not support forward yet */ args.next_hop6 = NULL; /* we do not support forward yet */ args.eh = &save_eh; /* MAC header for bridged/MAC packets */ args.inp = NULL; /* used by ipfw uid/gid/jail rules */ + args.dir = dir; /* pfSense addition */ i = ipfw_chk(&args); m = args.m; if (m != NULL) { @@ -369,13 +372,12 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, case IP_FW_DUMMYNET: ret = EACCES; - int dir; if (ip_dn_io_ptr == NULL) break; /* i.e. drop */ *m0 = NULL; - dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN); + dir = PROTO_LAYER2 | dir; ip_dn_io_ptr(&m, dir, &args); return 0; @@ -499,7 +501,11 @@ ipfw_hook(int onoff, int pf) hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet; - (void) (onoff ? pfil_add_hook : pfil_remove_hook) + if (onoff) + (void) pfil_add_named_hook + (hook_func, NULL, "ipfw", PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); + else + (void) pfil_remove_hook (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); return 0; diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index e4a2f31..4f4cf93 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -101,6 +101,7 @@ struct ip_fw_args { struct ipfw_flow_id f_id; /* grabbed from IP header */ //uint32_t cookie; /* a cookie depending on rule action */ + uint32_t dir; /* direction */ struct inpcb *inp; struct _ip6dn_args dummypar; /* dummynet->ip6_output */ @@ -170,6 +171,9 @@ enum { /* result for matching dynamic rules */ MATCH_UNKNOWN, }; +VNET_DECLARE(u_int32_t, curr_dyn_buckets); +#define V_curr_dyn_buckets VNET(curr_dyn_buckets) + /* * The lock for dynamic rules is only used once outside the file, * and only to release the result of lookup_dyn_rule(). @@ -178,6 +182,7 @@ enum { /* result for matching dynamic rules */ struct ip_fw_chain; void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int); void ipfw_dyn_unlock(ipfw_dyn_rule *q); +int resize_dynamic_table(struct ip_fw_chain *, int); struct tcphdr; struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *, @@ -189,7 +194,7 @@ ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, void ipfw_remove_dyn_children(struct ip_fw *rule); void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep); -void ipfw_dyn_init(struct ip_fw_chain *); /* per-vnet initialization */ +void ipfw_dyn_init(void); /* per-vnet initialization */ void ipfw_dyn_uninit(int); /* per-vnet deinitialization */ int ipfw_dyn_len(void); @@ -200,9 +205,6 @@ VNET_DECLARE(int, fw_one_pass); VNET_DECLARE(int, fw_verbose); #define V_fw_verbose VNET(fw_verbose) -VNET_DECLARE(struct ip_fw_chain, layer3_chain); -#define V_layer3_chain VNET(layer3_chain) - VNET_DECLARE(u_int32_t, set_disable); #define V_set_disable VNET(set_disable) @@ -236,6 +238,33 @@ struct ip_fw_chain { #endif }; +struct ip_fw_ctx_iflist { + TAILQ_ENTRY(ip_fw_ctx_iflist) entry; + char ifname[IFNAMSIZ]; +}; + +#define IP_FW_MAXCTX 4096 +struct ip_fw_contextes { + struct ip_fw_chain *chain[IP_FW_MAXCTX]; /* Arrays of contextes */ + TAILQ_HEAD(, ip_fw_ctx_iflist) iflist[IP_FW_MAXCTX]; + struct rwlock rwctx; + eventhandler_tag ifnet_arrival; +}; + +VNET_DECLARE(struct ip_fw_contextes, ip_fw_contexts); +#define V_ip_fw_contexts VNET(ip_fw_contexts) + +#define IPFW_CTX_LOCK_INIT() rw_init(&V_ip_fw_contexts.rwctx, "IPFW context") +#define IPFW_CTX_LOCK_DESTROY() rw_destroy(&V_ip_fw_contexts.rwctx) +#define IPFW_CTX_WLOCK() rw_wlock(&V_ip_fw_contexts.rwctx) +#define IPFW_CTX_WUNLOCK() rw_wunlock(&V_ip_fw_contexts.rwctx) +#define IPFW_CTX_RLOCK() rw_rlock(&V_ip_fw_contexts.rwctx) +#define IPFW_CTX_RUNLOCK() rw_runlock(&V_ip_fw_contexts.rwctx) + +void ipfw_attach_ifnet_event(void *, struct ifnet *); +int ipfw_context_init(int); +int ipfw_context_uninit(int); + struct sockopt; /* used by tcp_var.h */ /* Macro for working with various counters */ @@ -303,16 +332,21 @@ int ipfw_chk(struct ip_fw_args *args); void ipfw_reap_rules(struct ip_fw *head); /* In ip_fw_table.c */ +struct ether_addr; struct radix_node; -int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, - uint32_t *val); -int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint32_t *val, int type); +void *ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, + uint32_t *val, struct ether_addr *); +void *ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint32_t *val, int type, struct ether_addr *); +void ipfw_count_table_entry_stats(void *, int); +void ipfw_count_table_xentry_stats(void *, int); +int ipfw_zero_table_xentry_stats(struct ip_fw_chain *, ipfw_table_xentry *); +int ipfw_lookup_table_xentry(struct ip_fw_chain *, ipfw_table_xentry *); int ipfw_init_tables(struct ip_fw_chain *ch); void ipfw_destroy_tables(struct ip_fw_chain *ch); int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl); int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value); + uint8_t plen, uint8_t mlen, uint8_t type, u_int64_t mac_addr, uint32_t value); int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, uint8_t plen, uint8_t mlen, uint8_t type); int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt); @@ -326,8 +360,9 @@ int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables); extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); -typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *); -typedef int ipfw_nat_cfg_t(struct sockopt *); +typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *, + struct ip_fw_chain *); +typedef int ipfw_nat_cfg_t(struct sockopt *, struct ip_fw_chain *); VNET_DECLARE(int, ipfw_nat_ready); #define V_ipfw_nat_ready VNET(ipfw_nat_ready) diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 3c342f7..2776e4c 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -736,8 +736,8 @@ check_ipfw_struct(struct ip_fw *rule, int size) if (!IPFW_NAT_LOADED) return EINVAL; if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) - goto bad_size; - goto check_action; + goto bad_size; + goto check_action; case O_FORWARD_MAC: /* XXX not implemented yet */ case O_CHECK_STATE: case O_COUNT: @@ -943,12 +943,15 @@ ipfw_ctl(struct sockopt *sopt) #define RULE_MAXSIZE (256*sizeof(u_int32_t)) int error; size_t size, len, valsize; + struct ifnet *ifp; struct ip_fw *buf, *rule; - struct ip_fw_chain *chain; + static struct ip_fw_chain *chain; + struct ip_fw_ctx_iflist *tmpifl, *tmpifl2 = NULL; + ip_fw3_opheader *op3 = NULL; u_int32_t rulenum[2]; uint32_t opt; char xbuf[128]; - ip_fw3_opheader *op3 = NULL; + char *ifname; error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); if (error) @@ -965,7 +968,6 @@ ipfw_ctl(struct sockopt *sopt) return (error); } - chain = &V_layer3_chain; error = 0; /* Save original valsize before it is altered via sooptcopyin() */ @@ -980,9 +982,238 @@ ipfw_ctl(struct sockopt *sopt) return (error); op3 = (ip_fw3_opheader *)xbuf; opt = op3->opcode; + + if (op3->ctxid >= IP_FW_MAXCTX) + return (EINVAL); + + if (opt != IP_FW_CTX_GET && opt != IP_FW_CTX_ADD) { + if (op3->ctxid == 0) + return (ENOENT); + + IPFW_CTX_RLOCK(); + chain = V_ip_fw_contexts.chain[op3->ctxid]; + IPFW_CTX_RUNLOCK(); + if (chain == NULL) + return (ENOENT); + } } + /* Verification needed to avoid problems */ switch (opt) { + case IP_FW_CTX_GET: + case IP_FW_CTX_ADD: + case IP_FW_CTX_DEL: + break; + default: + if (chain == NULL) + return (EINVAL); + /* NOTREACHED */ + } + + switch (opt) { + case IP_FW_CTX_ADD: + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] != NULL) { + IPFW_CTX_WUNLOCK(); + return (EEXIST); + } + + chain = malloc(sizeof(struct ip_fw_chain), M_IPFW, M_WAITOK | M_ZERO); + TAILQ_INIT(&V_ip_fw_contexts.iflist[op3->ctxid]); + V_ip_fw_contexts.chain[op3->ctxid] = chain; + ipfw_context_init(op3->ctxid); /* XXX: error checking */ + IPFW_CTX_WUNLOCK(); + break; + + case IP_FW_CTX_DEL: + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) { + IPFW_CTX_WUNLOCK(); + return (ENOENT); + } + + ipfw_context_uninit(op3->ctxid); + V_ip_fw_contexts.chain[op3->ctxid] = NULL; + IPFW_CTX_WUNLOCK(); + break; + + case IP_FW_CTX_GET: + { + int i, n, len = 0, want; + char *bufout, *tmpbuf; + + sopt->sopt_valsize = valsize; + + IPFW_CTX_RLOCK(); + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (op3->ctxid > 0 && op3->ctxid != i) + continue; + if (op3->ctxid > 0 && op3->ctxid < i) + break; + + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + /* Calculate number of bytes for the integer */ + n = i; + while (n > 0) { + n /= 10; + len++; + } + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + len += strlen(tmpifl->ifname) + 1; + } + len += 3; // newline, :, space + } + IPFW_CTX_RUNLOCK(); + + if (len > sopt->sopt_valsize) { + sopt->sopt_valsize = len; + break; + } + + bufout = malloc(len, M_TEMP, M_WAITOK | M_ZERO); + if (bufout == NULL) + break; + + /* Record our size for later checks */ + want = len; + len = 0; + IPFW_CTX_RLOCK(); + /* Recalculate length to detect if smth changed */ + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (op3->ctxid > 0 && op3->ctxid != i) + continue; + if (op3->ctxid > 0 && op3->ctxid < i) + break; + + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + /* Calculate number of bytes for the integer */ + n = i; + while (n > 0) { + n /= 10; + len++; + } + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + len += strlen(tmpifl->ifname) + 1; + } + len += 3; // newline, :, space + } + + if (want >= len) { + tmpbuf = bufout; + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (op3->ctxid > 0 && op3->ctxid != i) + continue; + if (op3->ctxid > 0 && op3->ctxid < i) + break; + + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + sprintf(tmpbuf, "%d: ", i); + tmpbuf += strlen(tmpbuf); + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + sprintf(tmpbuf, "%s,", tmpifl->ifname); + tmpbuf += strlen(tmpifl->ifname) + 1; + } + sprintf(tmpbuf, "\n"); + tmpbuf++; + } + } + IPFW_CTX_RUNLOCK(); + + if (want >= len) + error = sooptcopyout(sopt, bufout, len); + else + len = 0; + free(bufout, M_TEMP); + } + break; + + case IP_FW_CTX_SET: + /* XXX: Maybe not use this option at all? */ + IPFW_CTX_RLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) + error = ENOENT; + else + chain = V_ip_fw_contexts.chain[op3->ctxid]; + IPFW_CTX_RUNLOCK(); + break; + + case IP_FW_CTX_ADDMEMBER: + { + int i; + + ifname = (char *)(op3 + 1); + ifp = ifunit(ifname); + if (ifp == NULL) + return (ENOENT); + + tmpifl = malloc(sizeof(*tmpifl), M_IPFW, M_WAITOK | M_ZERO); + + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) { + IPFW_CTX_WUNLOCK(); + free(tmpifl, M_IPFW); + return (ENOENT); + } + + for (i = 1; i < IP_FW_MAXCTX; i++) { + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + + TAILQ_FOREACH(tmpifl2, &V_ip_fw_contexts.iflist[i], entry) { + if (strlen(tmpifl2->ifname) != strlen(ifname)) + continue; + if (!strcmp(tmpifl2->ifname, ifname)) + goto ctxifacefound; + } + } +ctxifacefound: + if (tmpifl2 != NULL) { + IPFW_CTX_WUNLOCK(); + free(tmpifl, M_IPFW); + return (EEXIST); + } + + strlcpy(tmpifl->ifname, ifname, IFNAMSIZ); + TAILQ_INSERT_HEAD(&V_ip_fw_contexts.iflist[op3->ctxid], tmpifl, entry); + ifp->if_ispare[0] = op3->ctxid; + IPFW_CTX_WUNLOCK(); + } + break; + + case IP_FW_CTX_DELMEMBER: + IPFW_CTX_WLOCK(); + if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) { + IPFW_CTX_WUNLOCK(); + return (ENOENT); + } + + ifname = (char *)(op3 + 1); + TAILQ_FOREACH(tmpifl2, &V_ip_fw_contexts.iflist[op3->ctxid], entry) { + if (strlen(tmpifl2->ifname) != strlen(ifname)) + continue; + if (!strcmp(tmpifl2->ifname, ifname)) + break; + } + if (tmpifl2 == NULL) { + IPFW_CTX_WUNLOCK(); + return (ENOENT); + } + + TAILQ_REMOVE(&V_ip_fw_contexts.iflist[op3->ctxid], tmpifl2, entry); + IPFW_CTX_WUNLOCK(); + free(tmpifl2, M_IPFW); + + ifp = ifunit(ifname); + if (ifp != NULL) + ifp->if_ispare[0] = 0; + break; + case IP_FW_GET: /* * pass up a copy of the current rules. Static rules @@ -1124,7 +1355,7 @@ ipfw_ctl(struct sockopt *sopt) break; error = ipfw_add_table_entry(chain, ent.tbl, &ent.addr, sizeof(ent.addr), ent.masklen, - IPFW_TABLE_CIDR, ent.value); + IPFW_TABLE_CIDR, ent.mac_addr, ent.value); } break; @@ -1157,12 +1388,12 @@ ipfw_ctl(struct sockopt *sopt) error = EINVAL; break; } - + len = xent->len - offsetof(ipfw_table_xentry, k); error = (opt == IP_FW_TABLE_XADD) ? ipfw_add_table_entry(chain, xent->tbl, &xent->k, - len, xent->masklen, xent->type, xent->value) : + len, xent->masklen, xent->type, xent->mac_addr, xent->value) : ipfw_del_table_entry(chain, xent->tbl, &xent->k, len, xent->masklen, xent->type); } @@ -1245,6 +1476,54 @@ ipfw_ctl(struct sockopt *sopt) } break; + case IP_FW_TABLE_XZEROENTRY: /* IP_FW3 */ + { + ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error = EINVAL; + break; + } + + /* Check if len field is valid */ + if (xent->len > sizeof(ipfw_table_xentry)) { + error = EINVAL; + break; + } + + error = ipfw_zero_table_xentry_stats(chain, xent); + if (!error) { + xent->timestamp += boottime.tv_sec; + error = sooptcopyout(sopt, xent, sizeof(*xent)); + } + } + break; + + case IP_FW_TABLE_XLISTENTRY: /* IP_FW3 */ + { + ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error = EINVAL; + break; + } + + /* Check if len field is valid */ + if (xent->len > sizeof(ipfw_table_xentry)) { + error = EINVAL; + break; + } + + error = ipfw_lookup_table_xentry(chain, xent); + if (!error) { + xent->timestamp += boottime.tv_sec; + error = sooptcopyout(sopt, xent, sizeof(*xent)); + } + } + break; + case IP_FW_TABLE_XLIST: /* IP_FW3 */ { ipfw_xtable *tbl; @@ -1284,7 +1563,7 @@ ipfw_ctl(struct sockopt *sopt) /*--- NAT operations are protected by the IPFW_LOCK ---*/ case IP_FW_NAT_CFG: if (IPFW_NAT_LOADED) - error = ipfw_nat_cfg_ptr(sopt); + error = ipfw_nat_cfg_ptr(sopt, chain); else { printf("IP_FW_NAT_CFG: %s\n", "ipfw_nat not present, please load it"); @@ -1294,7 +1573,7 @@ ipfw_ctl(struct sockopt *sopt) case IP_FW_NAT_DEL: if (IPFW_NAT_LOADED) - error = ipfw_nat_del_ptr(sopt); + error = ipfw_nat_del_ptr(sopt, chain); else { printf("IP_FW_NAT_DEL: %s\n", "ipfw_nat not present, please load it"); @@ -1304,7 +1583,7 @@ ipfw_ctl(struct sockopt *sopt) case IP_FW_NAT_GET_CONFIG: if (IPFW_NAT_LOADED) - error = ipfw_nat_get_cfg_ptr(sopt); + error = ipfw_nat_get_cfg_ptr(sopt, chain); else { printf("IP_FW_NAT_GET_CFG: %s\n", "ipfw_nat not present, please load it"); @@ -1314,7 +1593,7 @@ ipfw_ctl(struct sockopt *sopt) case IP_FW_NAT_GET_LOG: if (IPFW_NAT_LOADED) - error = ipfw_nat_get_log_ptr(sopt); + error = ipfw_nat_get_log_ptr(sopt, chain); else { printf("IP_FW_NAT_GET_LOG: %s\n", "ipfw_nat not present, please load it"); @@ -1331,6 +1610,33 @@ ipfw_ctl(struct sockopt *sopt) #undef RULE_MAXSIZE } +void +ipfw_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) +{ + struct ip_fw_ctx_iflist *tmpifl; + + CURVNET_SET(ifp->if_vnet); + + IPFW_CTX_RLOCK(); + for (int i = 1; i < IP_FW_MAXCTX; i++) { + if (V_ip_fw_contexts.chain[i] == NULL) + continue; + TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) { + if (strlen(tmpifl->ifname) != strlen(ifp->if_xname)) + continue; + if (!strcmp(tmpifl->ifname, ifp->if_xname)) { + printf("Restoring context for interface %s to %d\n", ifp->if_xname, i); + ifp->if_ispare[0] = i; + goto ifctxdone; + break; + } + } + } +ifctxdone: + IPFW_CTX_RUNLOCK(); + + CURVNET_RESTORE(); +} #define RULE_MAXSIZE (256*sizeof(u_int32_t)) diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index 760a10c..689596f 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include <net/route.h> #include <net/vnet.h> +#include <net/ethernet.h> #include <netinet/in.h> #include <netinet/ip_var.h> /* struct ipfw_rule_ref */ #include <netinet/ip_fw.h> @@ -74,7 +75,11 @@ static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); struct table_entry { struct radix_node rn[2]; struct sockaddr_in addr, mask; + u_int64_t mac_addr; u_int32_t value; + u_int32_t timestamp; + u_int64_t bytes; + u_int64_t packets; }; struct xaddr_iface { @@ -97,7 +102,11 @@ struct table_xentry { #endif struct xaddr_iface ifmask; } m; + u_int64_t mac_addr; u_int32_t value; + u_int32_t timestamp; + u_int64_t bytes; + u_int64_t packets; }; /* @@ -137,7 +146,7 @@ ipv6_writemask(struct in6_addr *addr6, uint8_t mask) int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) + uint8_t plen, uint8_t mlen, uint8_t type, u_int64_t mac_addr, uint32_t value) { struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; @@ -161,6 +170,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, return (EINVAL); ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); ent->value = value; + ent->mac_addr = mac_addr; /* Set 'total' structure length */ KEY_LEN(ent->addr) = KEY_LEN_INET; KEY_LEN(ent->mask) = KEY_LEN_INET; @@ -182,6 +192,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, return (EINVAL); xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); xent->value = value; + xent->mac_addr = mac_addr; /* Set 'total' structure length */ KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; @@ -281,6 +292,28 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, IPFW_WUNLOCK(ch); if (rn == NULL) { + if (type == IPFW_TABLE_CIDR) { + /* Just update if any new value needed */ + if (plen == sizeof(in_addr_t)) { + ent = (struct table_entry *)(rnh->rnh_matchaddr(addr_ptr, rnh)); + if (ent != NULL) { + if (ent->mac_addr) { + if (!bcmp(&mac_addr, &ent->mac_addr, ETHER_ADDR_LEN)) + ent->value = value; + } else + ent->value = value; + } + } else { + xent = (struct table_xentry *)(rnh->rnh_matchaddr(addr_ptr, rnh)); + if (xent != NULL) { + if (xent->mac_addr) { + if (!bcmp(&mac_addr, &xent->mac_addr, ETHER_ADDR_LEN)) + xent->value = value; + } else + xent->value = value; + } + } + } free(ent_ptr, M_IPFW_TBL); return (EEXIST); } @@ -530,31 +563,194 @@ ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) return (0); } -int +void * ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, - uint32_t *val) + uint32_t *val, struct ether_addr *ea) { struct radix_node_head *rnh; struct table_entry *ent; struct sockaddr_in sa; if (tbl >= V_fw_tables_max) - return (0); + return (NULL); if ((rnh = ch->tables[tbl]) == NULL) - return (0); + return (NULL); KEY_LEN(sa) = KEY_LEN_INET; sa.sin_addr.s_addr = addr; ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh)); if (ent != NULL) { - *val = ent->value; - return (1); + if (ea && ent->mac_addr) { + if (bcmp((u_char *)&ent->mac_addr, ea->octet, ETHER_ADDR_LEN) != 0) + ent = NULL; + } + if (ent != NULL) { + *val = ent->value; + return (ent); + } } - return (0); + return (NULL); +} + +void +ipfw_count_table_entry_stats(void *arg, int pktlen) +{ + struct table_entry *xent = arg; + + xent->packets++; + xent->bytes += pktlen; + xent->timestamp = time_uptime; +} + +void +ipfw_count_table_xentry_stats(void *arg, int pktlen) +{ + ipfw_table_xentry *xent= arg; + + xent->packets++; + xent->bytes += pktlen; + xent->timestamp = time_uptime; } int +ipfw_zero_table_xentry_stats(struct ip_fw_chain *ch, ipfw_table_xentry *arg) +{ + struct radix_node_head *rnh; + struct table_xentry *xent; + struct sockaddr_in6 sa6; + struct xaddr_iface iface; + + if (arg->tbl >= V_fw_tables_max) + return (EINVAL); + if (ch->tables[arg->tbl] != NULL) + rnh = ch->tables[arg->tbl]; + else if (ch->xtables[arg->tbl] != NULL) + rnh = ch->xtables[arg->tbl]; + else + return (EINVAL); + + switch (arg->type) { + case IPFW_TABLE_CIDR: + if (ch->tables[arg->tbl] != NULL) { + /* XXX: Maybe better by FreeBSD 11!! */ + struct sockaddr_in sa; + struct table_entry *ent; + + KEY_LEN(sa) = KEY_LEN_INET; + sa.sin_addr.s_addr = *((in_addr_t *)&arg->k.addr6); + ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh)); + if (ent == NULL) + return (EINVAL); + + arg->bytes = 0; + arg->packets = 0; + arg->value = ent->value; + arg->timestamp = time_uptime; + + return (0); + } else { + KEY_LEN(sa6) = KEY_LEN_INET6; + memcpy(&sa6.sin6_addr, &arg->k.addr6, sizeof(struct in6_addr)); + xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh)); + } + break; + + case IPFW_TABLE_INTERFACE: + KEY_LEN(iface) = KEY_LEN_IFACE + + strlcpy(iface.ifname, arg->k.iface, IF_NAMESIZE) + 1; + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ + xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + break; + + default: + return (EINVAL); + } + + if (xent != NULL) { + xent->bytes = 0; + xent->packets = 0; + xent->timestamp = time_uptime; + + return (0); + } + return (EINVAL); +} + +int +ipfw_lookup_table_xentry(struct ip_fw_chain *ch, ipfw_table_xentry *arg) +{ + struct radix_node_head *rnh; + struct table_xentry *xent; + + if (arg->tbl >= V_fw_tables_max) + return (EINVAL); + if (ch->tables[arg->tbl] != NULL) + rnh = ch->tables[arg->tbl]; + else if (ch->xtables[arg->tbl] != NULL) + rnh = ch->xtables[arg->tbl]; + else + return (EINVAL); + + switch (arg->type) { + case IPFW_TABLE_CIDR: + { + if (ch->tables[arg->tbl] != NULL) { + /* XXX: Maybe better by FreeBSD 11!! */ + struct sockaddr_in sa; + struct table_entry *ent; + + KEY_LEN(sa) = KEY_LEN_INET; + sa.sin_addr.s_addr = *((in_addr_t *)&arg->k.addr6); + ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh)); + if (ent == NULL) + return (EINVAL); + + arg->bytes = ent->bytes; + arg->packets = ent->packets; + arg->value = ent->value; + arg->timestamp = ent->timestamp; + arg->mac_addr = ent->mac_addr; + return (0); + } else { + struct sockaddr_in6 sa6; + KEY_LEN(sa6) = KEY_LEN_INET6; + memcpy(&sa6.sin6_addr, &arg->k.addr6, sizeof(struct in6_addr)); + xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh)); + } + } + break; + + case IPFW_TABLE_INTERFACE: + { + struct xaddr_iface iface; + + KEY_LEN(iface) = KEY_LEN_IFACE + + strlcpy(iface.ifname, arg->k.iface, IF_NAMESIZE) + 1; + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ + xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + } + break; + + default: + return (0); + } + + if (xent != NULL) { + arg->bytes = xent->bytes; + arg->packets = xent->packets; + arg->value = xent->value; + arg->timestamp = xent->timestamp; + arg->mac_addr = xent->mac_addr; + + return (0); + } + return (EINVAL); +} + +void * ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint32_t *val, int type) + uint32_t *val, int type, struct ether_addr *ea) { struct radix_node_head *rnh; struct table_xentry *xent; @@ -562,15 +758,21 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, struct xaddr_iface iface; if (tbl >= V_fw_tables_max) - return (0); + return (NULL); if ((rnh = ch->xtables[tbl]) == NULL) - return (0); + return (NULL); switch (type) { 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_matchaddr(&sa6, rnh)); + if (xent != NULL) { + if (ea && xent->mac_addr) { + if (bcmp((u_char *)&xent->mac_addr, ea->octet, ETHER_ADDR_LEN) != 0) + xent = NULL; + } + } break; case IPFW_TABLE_INTERFACE: @@ -582,14 +784,14 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, break; default: - return (0); + return (NULL); } if (xent != NULL) { *val = xent->value; - return (1); + return (xent); } - return (0); + return (NULL); } static int @@ -696,9 +898,13 @@ dump_table_xentry_base(struct radix_node *rn, void *arg) else xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); /* Save IPv4 address as deprecated IPv6 compatible */ - xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr; + xent->k.addr6.s6_addr32[0] = n->addr.sin_addr.s_addr; xent->flags = IPFW_TCF_INET; xent->value = n->value; + xent->bytes = n->bytes; + xent->packets = n->packets; + xent->timestamp = n->timestamp; + xent->mac_addr = n->mac_addr; tbl->cnt++; return (0); } @@ -742,6 +948,10 @@ dump_table_xentry_extended(struct radix_node *rn, void *arg) } xent->value = n->value; + xent->bytes = n->bytes; + xent->packets = n->packets; + xent->timestamp = n->timestamp; + xent->mac_addr = n->mac_addr; tbl->cnt++; return (0); } |