diff --git a/sys/netpfil/ipfw/ip_fw_bpf.c b/sys/netpfil/ipfw/ip_fw_bpf.c --- a/sys/netpfil/ipfw/ip_fw_bpf.c +++ b/sys/netpfil/ipfw/ip_fw_bpf.c @@ -31,13 +31,14 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -53,48 +54,104 @@ .bif_chkdir = bpf_ipfw_chkdir, }; -static const char ipfwname[] = "ipfw0"; -static const char ipfwlogname[] = "ipfwlog0"; +struct ipfw_tap { + RB_ENTRY(ipfw_tap) entry; + uint32_t rule; + struct bpf_if *bpf; + char name[sizeof("ipfw65535")]; +}; -VNET_DEFINE_STATIC(struct bpf_if *, bpf_en10mb); -VNET_DEFINE_STATIC(struct bpf_if *, bpf_pflog); -#define V_bpf_en10mb VNET(bpf_en10mb) -#define V_bpf_pflog VNET(bpf_pflog) +static int32_t +tap_compare(const struct ipfw_tap *a, const struct ipfw_tap *b) +{ + return ((int32_t)(a->rule/2 - b->rule/2)); +} +RB_HEAD(tap_tree, ipfw_tap); +VNET_DEFINE_STATIC(struct tap_tree, tap_tree); +#define V_tap_tree VNET(tap_tree) +RB_GENERATE_STATIC(tap_tree, ipfw_tap, entry, tap_compare); +VNET_DEFINE_STATIC(struct ipfw_tap *, default_tap); +#define V_default_tap VNET(default_tap) void -ipfw_bpf_tap(u_char *pkt, u_int pktlen) +ipfw_tap_alloc(uint32_t rule) { - bpf_tap(V_bpf_en10mb, pkt, pktlen); + struct ipfw_tap *tap, *dup __diagused; + int n __diagused; + + tap = malloc(sizeof(*tap), M_IPFW, M_WAITOK); + tap->rule = rule; + /* Note: the default rule logs to "ipfw0". */ + if (__predict_false(rule == IPFW_DEFAULT_RULE)) { + V_default_tap = tap; + rule = 0; + } + n = snprintf(tap->name, sizeof(tap->name), "ipfw%u", rule); + MPASS(n > 4 && n < sizeof("ipfw65535")); + tap->bpf = bpf_attach(tap->name, DLT_EN10MB, PFLOG_HDRLEN, + &bpf_ipfw_methods, NULL); + dup = RB_INSERT(tap_tree, &V_tap_tree, tap); + MPASS(dup == NULL); } void -ipfw_bpf_mtap(struct mbuf *m) +ipfw_tap_free(uint32_t rule) { - bpf_mtap(V_bpf_en10mb, m); + + struct ipfw_tap *tap, key = { .rule = rule }; + + tap = RB_FIND(tap_tree, &V_tap_tree, &key); + MPASS(tap != NULL); + bpf_detach(tap->bpf); + RB_REMOVE(tap_tree, &V_tap_tree, tap); + free(tap, M_IPFW); } void -ipfw_bpf_mtap2(void *data, u_int dlen, struct mbuf *m) +ipfw_bpf_tap(struct ip_fw_args *args, struct ip *ip, uint32_t rulenum) { - switch (dlen) { - case (ETHER_HDR_LEN): - bpf_mtap2(V_bpf_en10mb, data, dlen, m); - break; - case (PFLOG_HDRLEN): - bpf_mtap2(V_bpf_pflog, data, dlen, m); - break; - default: - MPASS(0); + struct ipfw_tap *tap, key = { .rule = rulenum }; + + tap = RB_FIND(tap_tree, &V_tap_tree, &key); + MPASS(tap != NULL); + if (!bpf_peers_present(tap->bpf)) + tap = V_default_tap; + if (args->flags & IPFW_ARGS_LENMASK) { + bpf_tap(tap->bpf, args->mem, IPFW_ARGS_LENGTH(args->flags)); + } else if (args->flags & IPFW_ARGS_ETHER) { + /* layer2, use orig hdr */ + bpf_mtap(tap->bpf, args->m); + } else { + char *fakehdr; + + /* Add fake header. Later we will store + * more info in the header. + */ + if (ip->ip_v == 4) + fakehdr = "DDDDDDSSSSSS\x08\x00"; + else if (ip->ip_v == 6) + fakehdr = "DDDDDDSSSSSS\x86\xdd"; + else + /* Obviously bogus EtherType. */ + fakehdr = "DDDDDDSSSSSS\xff\xff"; + + bpf_mtap2(tap->bpf, fakehdr, ETHER_HDR_LEN, args->m); } } +VNET_DEFINE_STATIC(struct bpf_if *, bpf_pflog); +#define V_bpf_pflog VNET(bpf_pflog) void -ipfw_bpf_init(int first __unused) +ipfw_pflog_tap(void *data, struct mbuf *m) { + bpf_mtap2(V_bpf_pflog, data, PFLOG_HDRLEN, m); +} - V_bpf_en10mb = bpf_attach(ipfwname, DLT_EN10MB, ETHER_HDR_LEN, - &bpf_ipfw_methods, NULL); - V_bpf_pflog = bpf_attach(ipfwlogname, DLT_PFLOG, PFLOG_HDRLEN, +void +ipfw_bpf_init(int first __unused) +{ + ipfw_tap_alloc(IPFW_DEFAULT_RULE); + V_bpf_pflog = bpf_attach("ipfwlog0", DLT_PFLOG, PFLOG_HDRLEN, &bpf_ipfw_methods, NULL); } @@ -102,6 +159,6 @@ ipfw_bpf_uninit(int last __unused) { - bpf_detach(V_bpf_en10mb); + ipfw_tap_free(IPFW_DEFAULT_RULE); bpf_detach(V_bpf_pflog); } diff --git a/sys/netpfil/ipfw/ip_fw_log.c b/sys/netpfil/ipfw/ip_fw_log.c --- a/sys/netpfil/ipfw/ip_fw_log.c +++ b/sys/netpfil/ipfw/ip_fw_log.c @@ -96,31 +96,6 @@ #define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) -static void -ipfw_log_ipfw0(struct ip_fw_args *args, struct ip *ip) -{ - if (args->flags & IPFW_ARGS_LENMASK) - ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags)); - else if (args->flags & IPFW_ARGS_ETHER) - /* layer2, use orig hdr */ - ipfw_bpf_mtap(args->m); - else { - /* Add fake header. Later we will store - * more info in the header. - */ - if (ip->ip_v == 4) - ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00", - ETHER_HDR_LEN, args->m); - else if (ip->ip_v == 6) - ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd", - ETHER_HDR_LEN, args->m); - else - /* Obviously bogus EtherType. */ - ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff", - ETHER_HDR_LEN, args->m); - } -} - /* * XXX this function alone takes about 2Kbytes of code! */ @@ -747,7 +722,8 @@ /* O_LOG is the first action */ ((cmd = ACTION_PTR(f)) && cmd->arg1 == IPFW_LOG_DEFAULT)) { if (V_fw_verbose == 0) { - ipfw_log_ipfw0(args, ip); + ipfw_bpf_tap(args, ip, + f != NULL ? f->rulenum : IPFW_DEFAULT_RULE); return; } ipfw_log_syslog(chain, f, hlen, args, offset, tablearg, ip); @@ -761,6 +737,6 @@ ipfw_log_rtsock(chain, f, hlen, args, offset, tablearg, eh); if (cmd->arg1 & IPFW_LOG_IPFW0) - ipfw_log_ipfw0(args, ip); + ipfw_bpf_tap(args, ip, f->rulenum); } /* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -161,9 +161,10 @@ void ipfw_bpf_init(int); void ipfw_bpf_uninit(int); -void ipfw_bpf_tap(u_char *, u_int); -void ipfw_bpf_mtap(struct mbuf *); -void ipfw_bpf_mtap2(void *, u_int, struct mbuf *); +void ipfw_tap_alloc(uint32_t); +void ipfw_tap_free(uint32_t); +void ipfw_bpf_tap(struct ip_fw_args *, struct ip *, uint32_t); +void ipfw_pflog_tap(void *, struct mbuf *); void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip, void *eh); diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -210,6 +210,8 @@ */ if (rule->refcnt > 1) return; + if (ACTION_PTR(rule)->opcode == O_LOG) + ipfw_tap_free(rule->rulenum); uma_zfree_pcpu(V_ipfw_cntr_zone, rule->cntr); free(rule, M_IPFW); } @@ -2511,6 +2513,9 @@ /* Copy opcodes */ memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); + + if (ACTION_PTR(krule)->opcode == O_LOG) + ipfw_tap_alloc(krule->rulenum); } /* diff --git a/sys/netpfil/ipfw/nat64/nat64_translate.c b/sys/netpfil/ipfw/nat64/nat64_translate.c --- a/sys/netpfil/ipfw/nat64/nat64_translate.c +++ b/sys/netpfil/ipfw/nat64/nat64_translate.c @@ -151,7 +151,7 @@ logdata->dir = PF_OUT; logdata->af = family; - ipfw_bpf_mtap2(logdata, PFLOG_HDRLEN, m); + ipfw_pflog_tap(logdata, m); } static int diff --git a/sys/netpfil/ipfw/nat64/nat64clat.c b/sys/netpfil/ipfw/nat64/nat64clat.c --- a/sys/netpfil/ipfw/nat64/nat64clat.c +++ b/sys/netpfil/ipfw/nat64/nat64clat.c @@ -77,7 +77,7 @@ plog->subrulenr = htonl(pktid); plog->ruleset[0] = '\0'; strlcpy(plog->ifname, "NAT64CLAT", sizeof(plog->ifname)); - ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m); + ipfw_pflog_tap(plog, m); } static int diff --git a/sys/netpfil/ipfw/nat64/nat64lsn.c b/sys/netpfil/ipfw/nat64/nat64lsn.c --- a/sys/netpfil/ipfw/nat64/nat64lsn.c +++ b/sys/netpfil/ipfw/nat64/nat64lsn.c @@ -187,7 +187,7 @@ (state->proto << 8) | (state->ip_dst & 0xff)); plog->ruleset[0] = '\0'; strlcpy(plog->ifname, "NAT64LSN", sizeof(plog->ifname)); - ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m); + ipfw_pflog_tap(plog, m); } #define HVAL(p, n, s) jenkins_hash32((const uint32_t *)(p), (n), (s)) diff --git a/sys/netpfil/ipfw/nat64/nat64stl.c b/sys/netpfil/ipfw/nat64/nat64stl.c --- a/sys/netpfil/ipfw/nat64/nat64stl.c +++ b/sys/netpfil/ipfw/nat64/nat64stl.c @@ -76,7 +76,7 @@ plog->subrulenr = htonl(pktid); plog->ruleset[0] = '\0'; strlcpy(plog->ifname, "NAT64STL", sizeof(plog->ifname)); - ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m); + ipfw_pflog_tap(plog, m); } static int