src/openvpn/pf.c
47ae8457
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
47ae8457
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
47ae8457
  */
 
 /* packet filter functions */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
47ae8457
 #include "syshead.h"
 
 #if defined(ENABLE_PF)
 
 #include "init.h"
 #include "memdbg.h"
a54f37d8
 #include "pf.h"
63dc03d0
 #include "ssl_verify.h"
47ae8457
 
90efcacb
 
47ae8457
 static void
81d882d5
 pf_destroy(struct pf_set *pfs)
47ae8457
 {
81d882d5
     if (pfs)
     {
         if (pfs->cns.hash_table)
         {
             hash_free(pfs->cns.hash_table);
         }
 
         {
             struct pf_cn_elem *l = pfs->cns.list;
             while (l)
             {
                 struct pf_cn_elem *next = l->next;
                 free(l->rule.cn);
                 free(l);
                 l = next;
             }
         }
         {
             struct pf_subnet *l = pfs->sns.list;
             while (l)
             {
                 struct pf_subnet *next = l->next;
                 free(l);
                 l = next;
             }
         }
         free(pfs);
47ae8457
     }
 }
 
 static bool
81d882d5
 add_client(const char *line, const char *prefix, const int line_num, struct pf_cn_elem ***next, const bool exclude)
47ae8457
 {
81d882d5
     struct pf_cn_elem *e;
     ALLOC_OBJ_CLEAR(e, struct pf_cn_elem);
     e->rule.exclude = exclude;
     e->rule.cn = string_alloc(line, NULL);
     **next = e;
     *next = &e->next;
     return true;
47ae8457
 }
 
 static bool
81d882d5
 add_subnet(const char *line, const char *prefix, const int line_num, struct pf_subnet ***next, const bool exclude)
47ae8457
 {
81d882d5
     struct in_addr network;
     in_addr_t netmask = 0;
 
     if (strcmp(line, "unknown"))
     {
         int netbits = 32;
         char *div = strchr(line, '/');
 
         if (div)
         {
             *div++ = '\0';
             if (sscanf(div, "%d", &netbits) != 1)
             {
                 msg(D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: '%s'", prefix, line_num, div);
                 return false;
             }
             if (netbits < 0 || netbits > 32)
             {
                 msg(D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: must be between 0 and 32: '%s'", prefix, line_num, div);
                 return false;
             }
         }
 
         if (openvpn_inet_aton(line, &network) != OIA_IP)
         {
             msg(D_PF_INFO, "PF: %s/%d: bad network address: '%s'", prefix, line_num, line);
             return false;
         }
         netmask = netbits_to_netmask(netbits);
         if ((network.s_addr & htonl(netmask)) != network.s_addr)
d2ad24c0
         {
81d882d5
             network.s_addr &= htonl(netmask);
             msg(M_WARN, "WARNING: PF: %s/%d: incorrect subnet %s/%d changed to %s/%d", prefix, line_num, line, netbits, inet_ntoa(network), netbits);
d2ad24c0
         }
47ae8457
     }
81d882d5
     else
47ae8457
     {
81d882d5
         /* match special "unknown" tag for addresses unrecognized by mroute */
         network.s_addr = htonl(0);
         netmask = IPV4_NETMASK_HOST;
47ae8457
     }
 
81d882d5
     {
         struct pf_subnet *e;
         ALLOC_OBJ_CLEAR(e, struct pf_subnet);
         e->rule.exclude = exclude;
         e->rule.network = ntohl(network.s_addr);
         e->rule.netmask = netmask;
         **next = e;
         *next = &e->next;
         return true;
     }
47ae8457
 }
 
 static uint32_t
81d882d5
 cn_hash_function(const void *key, uint32_t iv)
47ae8457
 {
81d882d5
     return hash_func((uint8_t *)key, strlen((char *)key) + 1, iv);
47ae8457
 }
 
 static bool
81d882d5
 cn_compare_function(const void *key1, const void *key2)
47ae8457
 {
81d882d5
     return !strcmp((const char *)key1, (const char *)key2);
47ae8457
 }
 
 static bool
81d882d5
 genhash(struct pf_cn_set *cns, const char *prefix, const int n_clients)
47ae8457
 {
81d882d5
     struct pf_cn_elem *e;
     bool status = true;
     int n_buckets = n_clients;
 
     if (n_buckets < 16)
     {
         n_buckets = 16;
     }
     cns->hash_table = hash_init(n_buckets, 0, cn_hash_function, cn_compare_function);
     for (e = cns->list; e != NULL; e = e->next)
     {
         if (!hash_add(cns->hash_table, e->rule.cn, &e->rule, false))
         {
             msg(D_PF_INFO, "PF: %s: duplicate common name in [clients] section: '%s'", prefix, e->rule.cn);
             status = false;
         }
     }
 
     return status;
47ae8457
 }
 
 static struct pf_set *
81d882d5
 pf_init(const struct buffer_list *bl, const char *prefix, const bool allow_kill)
47ae8457
 {
81d882d5
 #define MODE_UNDEF   0
 #define MODE_CLIENTS 1
 #define MODE_SUBNETS 2
     int mode = MODE_UNDEF;
     int line_num = 0;
     int n_clients = 0;
     int n_subnets = 0;
     int n_errors = 0;
     struct pf_set *pfs = NULL;
     char line[PF_MAX_LINE_LEN];
 
     ALLOC_OBJ_CLEAR(pfs, struct pf_set);
     if (bl)
     {
         struct pf_cn_elem **cl = &pfs->cns.list;
         struct pf_subnet **sl = &pfs->sns.list;
         struct buffer_entry *be;
 
         for (be = bl->head; be != NULL; be = be->next)
         {
             ++line_num;
             strncpynt(line, BSTR(&be->buf), sizeof(line));
             rm_trailing_chars(line, "\r\n\t ");
             if (line[0] == '\0' || line[0] == '#')
             {
             }
             else if (line[0] == '+' || line[0] == '-')
             {
                 bool exclude = (line[0] == '-');
 
                 if (line[1] =='\0')
                 {
                     msg(D_PF_INFO, "PF: %s/%d: no data after +/-: '%s'", prefix, line_num, line);
                     ++n_errors;
                 }
                 else if (mode == MODE_CLIENTS)
                 {
                     if (add_client(&line[1], prefix, line_num, &cl, exclude))
                     {
                         ++n_clients;
                     }
                     else
                     {
                         ++n_errors;
                     }
                 }
                 else if (mode == MODE_SUBNETS)
                 {
                     if (add_subnet(&line[1], prefix, line_num, &sl, exclude))
                     {
                         ++n_subnets;
                     }
                     else
                     {
                         ++n_errors;
                     }
                 }
                 else if (mode == MODE_UNDEF)
                 {
                 }
                 else
                 {
                     ASSERT(0);
                 }
             }
             else if (line[0] == '[')
             {
                 if (!strcasecmp(line, "[clients accept]"))
                 {
                     mode = MODE_CLIENTS;
                     pfs->cns.default_allow = true;
                 }
                 else if (!strcasecmp(line, "[clients drop]"))
                 {
                     mode = MODE_CLIENTS;
                     pfs->cns.default_allow = false;
                 }
                 else if (!strcasecmp(line, "[subnets accept]"))
                 {
                     mode = MODE_SUBNETS;
                     pfs->sns.default_allow = true;
                 }
                 else if (!strcasecmp(line, "[subnets drop]"))
                 {
                     mode = MODE_SUBNETS;
                     pfs->sns.default_allow = false;
                 }
                 else if (!strcasecmp(line, "[end]"))
                 {
                     goto done;
                 }
                 else if (allow_kill && !strcasecmp(line, "[kill]"))
                 {
                     goto kill;
                 }
                 else
                 {
                     mode = MODE_UNDEF;
                     msg(D_PF_INFO, "PF: %s/%d unknown tag: '%s'", prefix, line_num, line);
                     ++n_errors;
                 }
             }
             else
             {
                 msg(D_PF_INFO, "PF: %s/%d line must begin with '+', '-', or '[' : '%s'", prefix, line_num, line);
                 ++n_errors;
             }
         }
         ++n_errors;
         msg(D_PF_INFO, "PF: %s: missing [end]", prefix);
     }
     else
     {
         msg(D_PF_INFO, "PF: %s: cannot open", prefix);
         ++n_errors;
     }
 
 done:
     if (bl)
     {
         if (!n_errors)
         {
             if (!genhash(&pfs->cns, prefix, n_clients))
             {
                 ++n_errors;
             }
         }
         if (n_errors)
         {
             msg(D_PF_INFO, "PF: %s rejected due to %d error(s)", prefix, n_errors);
         }
     }
     if (n_errors)
     {
         pf_destroy(pfs);
         pfs = NULL;
     }
     return pfs;
 
 kill:
     pf_destroy(pfs);
     ALLOC_OBJ_CLEAR(pfs, struct pf_set);
     pfs->kill = true;
     return pfs;
47ae8457
 }
 
90efcacb
 #ifdef PLUGIN_PF
 static struct pf_set *
81d882d5
 pf_init_from_file(const char *fn)
90efcacb
 {
81d882d5
     struct buffer_list *bl = buffer_list_file(fn, PF_MAX_LINE_LEN);
     if (bl)
90efcacb
     {
81d882d5
         struct pf_set *pfs = pf_init(bl, fn, true);
         buffer_list_free(bl);
         return pfs;
90efcacb
     }
81d882d5
     else
90efcacb
     {
81d882d5
         msg(D_PF_INFO|M_ERRNO, "PF: %s: cannot open", fn);
         return NULL;
90efcacb
     }
 }
 #endif
 
 #ifdef ENABLE_DEBUG
47ae8457
 
 static const char *
81d882d5
 drop_accept(const bool accept)
47ae8457
 {
81d882d5
     return accept ? "ACCEPT" : "DROP";
47ae8457
 }
 
90efcacb
 static const char *
81d882d5
 pct_name(const int type)
90efcacb
 {
81d882d5
     switch (type)
90efcacb
     {
81d882d5
         case PCT_SRC:
             return "SRC";
 
         case PCT_DEST:
             return "DEST";
 
         default:
             return "???";
90efcacb
     }
 }
47ae8457
 
 static void
81d882d5
 pf_cn_test_print(const char *prefix,
                  const int type,
                  const char *prefix2,
                  const char *cn,
                  const bool allow,
                  const struct pf_cn *rule)
47ae8457
 {
81d882d5
     if (rule)
47ae8457
     {
81d882d5
         dmsg(D_PF_DEBUG, "PF: %s/%s/%s %s %s rule=[%s %s]",
              prefix, prefix2, pct_name(type),
              cn, drop_accept(allow),
              rule->cn, drop_accept(!rule->exclude));
47ae8457
     }
81d882d5
     else
47ae8457
     {
81d882d5
         dmsg(D_PF_DEBUG, "PF: %s/%s/%s %s %s",
              prefix, prefix2, pct_name(type),
              cn, drop_accept(allow));
47ae8457
     }
 }
 
 static void
81d882d5
 pf_addr_test_print(const char *prefix,
                    const char *prefix2,
                    const struct context *src,
                    const struct mroute_addr *dest,
                    const bool allow,
                    const struct ipv4_subnet *rule)
47ae8457
 {
81d882d5
     struct gc_arena gc = gc_new();
     if (rule)
     {
         dmsg(D_PF_DEBUG, "PF: %s/%s %s %s %s rule=[%s/%s %s]",
              prefix,
              prefix2,
              tls_common_name(src->c2.tls_multi, false),
              mroute_addr_print_ex(dest, MAPF_SHOW_ARP, &gc),
              drop_accept(allow),
              print_in_addr_t(rule->network, 0, &gc),
              print_in_addr_t(rule->netmask, 0, &gc),
              drop_accept(!rule->exclude));
     }
     else
     {
         dmsg(D_PF_DEBUG, "PF: %s/%s %s %s %s",
              prefix,
              prefix2,
              tls_common_name(src->c2.tls_multi, false),
              mroute_addr_print_ex(dest, MAPF_SHOW_ARP, &gc),
              drop_accept(allow));
     }
     gc_free(&gc);
47ae8457
 }
 
81d882d5
 #endif /* ifdef ENABLE_DEBUG */
47ae8457
 
 static inline struct pf_cn *
81d882d5
 lookup_cn_rule(struct hash *h, const char *cn, const uint32_t cn_hash)
47ae8457
 {
81d882d5
     struct hash_element *he = hash_lookup_fast(h, hash_bucket(h, cn_hash), cn, cn_hash);
     if (he)
     {
         return (struct pf_cn *) he->value;
     }
     else
     {
         return NULL;
     }
47ae8457
 }
 
90efcacb
 bool
81d882d5
 pf_cn_test(struct pf_set *pfs, const struct tls_multi *tm, const int type, const char *prefix)
47ae8457
 {
81d882d5
     if (pfs && !pfs->kill)
     {
         const char *cn;
         uint32_t cn_hash;
         if (tls_common_name_hash(tm, &cn, &cn_hash))
         {
             const struct pf_cn *rule = lookup_cn_rule(pfs->cns.hash_table, cn, cn_hash);
             if (rule)
             {
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
                 if (check_debug_level(D_PF_DEBUG))
                 {
                     pf_cn_test_print("PF_CN_MATCH", type, prefix, cn, !rule->exclude, rule);
                 }
47ae8457
 #endif
81d882d5
                 if (!rule->exclude)
                 {
                     return true;
                 }
                 else
                 {
                     return false;
                 }
             }
             else
             {
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
                 if (check_debug_level(D_PF_DEBUG))
                 {
                     pf_cn_test_print("PF_CN_DEFAULT", type, prefix, cn, pfs->cns.default_allow, NULL);
                 }
47ae8457
 #endif
81d882d5
                 if (pfs->cns.default_allow)
                 {
                     return true;
                 }
                 else
                 {
                     return false;
                 }
             }
         }
47ae8457
     }
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
     if (check_debug_level(D_PF_DEBUG))
     {
         pf_cn_test_print("PF_CN_FAULT", type, prefix, tls_common_name(tm, false), false, NULL);
     }
47ae8457
 #endif
81d882d5
     return false;
47ae8457
 }
 
 bool
81d882d5
 pf_addr_test_dowork(const struct context *src, const struct mroute_addr *dest, const char *prefix)
47ae8457
 {
81d882d5
     struct pf_set *pfs = src->c2.pf.pfs;
     if (pfs && !pfs->kill)
     {
         const in_addr_t addr = in_addr_t_from_mroute_addr(dest);
         const struct pf_subnet *se = pfs->sns.list;
         while (se)
         {
             if ((addr & se->rule.netmask) == se->rule.network)
             {
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
                 if (check_debug_level(D_PF_DEBUG))
                 {
                     pf_addr_test_print("PF_ADDR_MATCH", prefix, src, dest, !se->rule.exclude, &se->rule);
                 }
47ae8457
 #endif
81d882d5
                 return !se->rule.exclude;
             }
             se = se->next;
         }
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
         if (check_debug_level(D_PF_DEBUG))
         {
             pf_addr_test_print("PF_ADDR_DEFAULT", prefix, src, dest, pfs->sns.default_allow, NULL);
         }
47ae8457
 #endif
81d882d5
         return pfs->sns.default_allow;
47ae8457
     }
81d882d5
     else
47ae8457
     {
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
         if (check_debug_level(D_PF_DEBUG))
         {
             pf_addr_test_print("PF_ADDR_FAULT", prefix, src, dest, false, NULL);
         }
90efcacb
 #endif
81d882d5
         return false;
47ae8457
     }
 }
 
90efcacb
 #ifdef PLUGIN_PF
47ae8457
 void
81d882d5
 pf_check_reload(struct context *c)
47ae8457
 {
81d882d5
     const int slow_wakeup = 15;
     const int fast_wakeup = 1;
     const int wakeup_transition = 60;
     bool reloaded = false;
 
     if (c->c2.pf.enabled
         && c->c2.pf.filename
         && event_timeout_trigger(&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT))
     {
         platform_stat_t s;
         if (!platform_stat(c->c2.pf.filename, &s))
         {
             if (s.st_mtime > c->c2.pf.file_last_mod)
             {
                 struct pf_set *pfs = pf_init_from_file(c->c2.pf.filename);
                 if (pfs)
                 {
                     if (c->c2.pf.pfs)
                     {
                         pf_destroy(c->c2.pf.pfs);
                     }
                     c->c2.pf.pfs = pfs;
                     reloaded = true;
                     if (pf_kill_test(pfs))
                     {
                         c->sig->signal_received = SIGTERM;
                         c->sig->signal_text = "pf-kill";
                     }
                 }
                 c->c2.pf.file_last_mod = s.st_mtime;
             }
         }
         {
             int wakeup = slow_wakeup;
             if (!c->c2.pf.pfs && c->c2.pf.n_check_reload < wakeup_transition)
             {
                 wakeup = fast_wakeup;
             }
             event_timeout_init(&c->c2.pf.reload, wakeup, now);
             reset_coarse_timers(c);
             c->c2.pf.n_check_reload++;
         }
47ae8457
     }
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
     if (reloaded && check_debug_level(D_PF_DEBUG))
     {
         pf_context_print(&c->c2.pf, "pf_check_reload", D_PF_DEBUG);
     }
47ae8457
 #endif
 }
81d882d5
 #endif /* ifdef PLUGIN_PF */
90efcacb
 
 #ifdef MANAGEMENT_PF
 bool
81d882d5
 pf_load_from_buffer_list(struct context *c, const struct buffer_list *config)
90efcacb
 {
81d882d5
     struct pf_set *pfs = pf_init(config, "[SERVER-PF]", false);
     if (pfs)
90efcacb
     {
81d882d5
         if (c->c2.pf.pfs)
         {
             pf_destroy(c->c2.pf.pfs);
         }
         c->c2.pf.pfs = pfs;
         return true;
     }
     else
     {
         return false;
90efcacb
     }
 }
 #endif
47ae8457
 
 void
81d882d5
 pf_init_context(struct context *c)
47ae8457
 {
90efcacb
 #ifdef PLUGIN_PF
81d882d5
     if (plugin_defined(c->plugins, OPENVPN_PLUGIN_ENABLE_PF))
47ae8457
     {
b7bea782
         c->c2.pf.filename = platform_create_temp_file(c->options.tmp_dir, "pf",
                                                       &c->c2.gc);
d2342067
         if (c->c2.pf.filename)
81d882d5
         {
d2342067
             setenv_str(c->c2.es, "pf_file", c->c2.pf.filename);
495e3cec
 
81d882d5
             if (plugin_call(c->plugins, OPENVPN_PLUGIN_ENABLE_PF, NULL, NULL, c->c2.es) == OPENVPN_PLUGIN_FUNC_SUCCESS)
             {
                 event_timeout_init(&c->c2.pf.reload, 1, now);
                 c->c2.pf.enabled = true;
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
                 if (check_debug_level(D_PF_DEBUG))
                 {
                     pf_context_print(&c->c2.pf, "pf_init_context#1", D_PF_DEBUG);
                 }
47ae8457
 #endif
81d882d5
             }
492e42d3
         }
         if (!c->c2.pf.enabled)
         {
             msg(M_WARN, "WARNING: failed to init PF plugin, rejecting client.");
             register_signal(c, SIGUSR1, "plugin-pf-init-failed");
             return;
81d882d5
         }
47ae8457
     }
81d882d5
 #endif /* ifdef PLUGIN_PF */
90efcacb
 #ifdef MANAGEMENT_PF
81d882d5
     if (!c->c2.pf.enabled && management_enable_pf(management))
90efcacb
     {
81d882d5
         c->c2.pf.enabled = true;
90efcacb
 #ifdef ENABLE_DEBUG
81d882d5
         if (check_debug_level(D_PF_DEBUG))
         {
             pf_context_print(&c->c2.pf, "pf_init_context#2", D_PF_DEBUG);
         }
90efcacb
 #endif
     }
 #endif
47ae8457
 }
 
 void
81d882d5
 pf_destroy_context(struct pf_context *pfc)
47ae8457
 {
90efcacb
 #ifdef PLUGIN_PF
81d882d5
     if (pfc->filename)
47ae8457
     {
81d882d5
         platform_unlink(pfc->filename);
47ae8457
     }
90efcacb
 #endif
81d882d5
     if (pfc->pfs)
     {
         pf_destroy(pfc->pfs);
     }
47ae8457
 }
 
90efcacb
 #ifdef ENABLE_DEBUG
47ae8457
 
 static void
81d882d5
 pf_subnet_set_print(const struct pf_subnet_set *s, const int lev)
47ae8457
 {
81d882d5
     struct gc_arena gc = gc_new();
     if (s)
47ae8457
     {
81d882d5
         struct pf_subnet *e;
47ae8457
 
81d882d5
         msg(lev, "  ----- struct pf_subnet_set -----");
         msg(lev, "  default_allow=%s", drop_accept(s->default_allow));
47ae8457
 
81d882d5
         for (e = s->list; e != NULL; e = e->next)
         {
             msg(lev, "   %s/%s %s",
                 print_in_addr_t(e->rule.network, 0, &gc),
                 print_in_addr_t(e->rule.netmask, 0, &gc),
                 drop_accept(!e->rule.exclude));
         }
47ae8457
     }
81d882d5
     gc_free(&gc);
47ae8457
 }
 
 static void
81d882d5
 pf_cn_set_print(const struct pf_cn_set *s, const int lev)
47ae8457
 {
81d882d5
     if (s)
     {
         struct hash_iterator hi;
         struct hash_element *he;
 
         msg(lev, "  ----- struct pf_cn_set -----");
         msg(lev, "  default_allow=%s", drop_accept(s->default_allow));
 
         if (s->hash_table)
         {
             hash_iterator_init(s->hash_table, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct pf_cn *e = (struct pf_cn *)he->value;
                 msg(lev, "   %s %s",
                     e->cn,
                     drop_accept(!e->exclude));
             }
 
             msg(lev, "  ----------");
 
             {
                 struct pf_cn_elem *ce;
                 for (ce = s->list; ce != NULL; ce = ce->next)
                 {
                     struct pf_cn *e = lookup_cn_rule(s->hash_table, ce->rule.cn, cn_hash_function(ce->rule.cn, 0));
                     if (e)
                     {
                         msg(lev, "   %s %s",
                             e->cn,
                             drop_accept(!e->exclude));
                     }
                     else
                     {
                         msg(lev, "   %s LOOKUP FAILED", ce->rule.cn);
                     }
                 }
             }
         }
47ae8457
     }
 }
 
 static void
81d882d5
 pf_set_print(const struct pf_set *pfs, const int lev)
47ae8457
 {
81d882d5
     if (pfs)
47ae8457
     {
81d882d5
         msg(lev, " ----- struct pf_set -----");
         msg(lev, " kill=%d", pfs->kill);
         pf_subnet_set_print(&pfs->sns, lev);
         pf_cn_set_print(&pfs->cns, lev);
47ae8457
     }
 }
 
 void
81d882d5
 pf_context_print(const struct pf_context *pfc, const char *prefix, const int lev)
47ae8457
 {
81d882d5
     msg(lev, "----- %s : struct pf_context -----", prefix);
     if (pfc)
47ae8457
     {
81d882d5
         msg(lev, "enabled=%d", pfc->enabled);
90efcacb
 #ifdef PLUGIN_PF
81d882d5
         msg(lev, "filename='%s'", np(pfc->filename));
         msg(lev, "file_last_mod=%u", (unsigned int)pfc->file_last_mod);
         msg(lev, "n_check_reload=%u", pfc->n_check_reload);
         msg(lev, "reload=[%d,%u,%u]", pfc->reload.defined, pfc->reload.n, (unsigned int)pfc->reload.last);
90efcacb
 #endif
81d882d5
         pf_set_print(pfc->pfs, lev);
47ae8457
     }
81d882d5
     msg(lev, "--------------------");
47ae8457
 }
 
81d882d5
 #endif /* ifdef ENABLE_DEBUG */
47ae8457
 
81d882d5
 #endif /* if defined(ENABLE_PF) */