1 /*
2  *  OpenVPN -- An application to securely tunnel IP networks
3  *             over a single TCP/UDP port, with support for SSL/TLS-based
4  *             session authentication and key exchange,
5  *             packet encryption, packet authentication, and
6  *             packet compression.
7  *
8  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2
12  *  as published by the Free Software Foundation.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License along
20  *  with this program; if not, write to the Free Software Foundation, Inc.,
21  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 /* packet filter functions */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #elif defined(_MSC_VER)
29 #include "config-msvc.h"
30 #endif
31 
32 #include "syshead.h"
33 
34 #if defined(ENABLE_PF)
35 
36 #include "init.h"
37 #include "memdbg.h"
38 #include "pf.h"
39 #include "ssl_verify.h"
40 
41 
42 static void
pf_destroy(struct pf_set * pfs)43 pf_destroy(struct pf_set *pfs)
44 {
45     if (pfs)
46     {
47         if (pfs->cns.hash_table)
48         {
49             hash_free(pfs->cns.hash_table);
50         }
51 
52         {
53             struct pf_cn_elem *l = pfs->cns.list;
54             while (l)
55             {
56                 struct pf_cn_elem *next = l->next;
57                 free(l->rule.cn);
58                 free(l);
59                 l = next;
60             }
61         }
62         {
63             struct pf_subnet *l = pfs->sns.list;
64             while (l)
65             {
66                 struct pf_subnet *next = l->next;
67                 free(l);
68                 l = next;
69             }
70         }
71         free(pfs);
72     }
73 }
74 
75 static bool
add_client(const char * line,const char * prefix,const int line_num,struct pf_cn_elem *** next,const bool exclude)76 add_client(const char *line, const char *prefix, const int line_num, struct pf_cn_elem ***next, const bool exclude)
77 {
78     struct pf_cn_elem *e;
79     ALLOC_OBJ_CLEAR(e, struct pf_cn_elem);
80     e->rule.exclude = exclude;
81     e->rule.cn = string_alloc(line, NULL);
82     **next = e;
83     *next = &e->next;
84     return true;
85 }
86 
87 static bool
add_subnet(const char * line,const char * prefix,const int line_num,struct pf_subnet *** next,const bool exclude)88 add_subnet(const char *line, const char *prefix, const int line_num, struct pf_subnet ***next, const bool exclude)
89 {
90     struct in_addr network;
91     in_addr_t netmask = 0;
92 
93     if (strcmp(line, "unknown"))
94     {
95         int netbits = 32;
96         char *div = strchr(line, '/');
97 
98         if (div)
99         {
100             *div++ = '\0';
101             if (sscanf(div, "%d", &netbits) != 1)
102             {
103                 msg(D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: '%s'", prefix, line_num, div);
104                 return false;
105             }
106             if (netbits < 0 || netbits > 32)
107             {
108                 msg(D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: must be between 0 and 32: '%s'", prefix, line_num, div);
109                 return false;
110             }
111         }
112 
113         if (openvpn_inet_aton(line, &network) != OIA_IP)
114         {
115             msg(D_PF_INFO, "PF: %s/%d: bad network address: '%s'", prefix, line_num, line);
116             return false;
117         }
118         netmask = netbits_to_netmask(netbits);
119         if ((network.s_addr & htonl(netmask)) != network.s_addr)
120         {
121             network.s_addr &= htonl(netmask);
122             msg(M_WARN, "WARNING: PF: %s/%d: incorrect subnet %s/%d changed to %s/%d", prefix, line_num, line, netbits, inet_ntoa(network), netbits);
123         }
124     }
125     else
126     {
127         /* match special "unknown" tag for addresses unrecognized by mroute */
128         network.s_addr = htonl(0);
129         netmask = IPV4_NETMASK_HOST;
130     }
131 
132     {
133         struct pf_subnet *e;
134         ALLOC_OBJ_CLEAR(e, struct pf_subnet);
135         e->rule.exclude = exclude;
136         e->rule.network = ntohl(network.s_addr);
137         e->rule.netmask = netmask;
138         **next = e;
139         *next = &e->next;
140         return true;
141     }
142 }
143 
144 static uint32_t
cn_hash_function(const void * key,uint32_t iv)145 cn_hash_function(const void *key, uint32_t iv)
146 {
147     return hash_func((uint8_t *)key, strlen((char *)key) + 1, iv);
148 }
149 
150 static bool
cn_compare_function(const void * key1,const void * key2)151 cn_compare_function(const void *key1, const void *key2)
152 {
153     return !strcmp((const char *)key1, (const char *)key2);
154 }
155 
156 static bool
genhash(struct pf_cn_set * cns,const char * prefix,const int n_clients)157 genhash(struct pf_cn_set *cns, const char *prefix, const int n_clients)
158 {
159     struct pf_cn_elem *e;
160     bool status = true;
161     int n_buckets = n_clients;
162 
163     if (n_buckets < 16)
164     {
165         n_buckets = 16;
166     }
167     cns->hash_table = hash_init(n_buckets, 0, cn_hash_function, cn_compare_function);
168     for (e = cns->list; e != NULL; e = e->next)
169     {
170         if (!hash_add(cns->hash_table, e->rule.cn, &e->rule, false))
171         {
172             msg(D_PF_INFO, "PF: %s: duplicate common name in [clients] section: '%s'", prefix, e->rule.cn);
173             status = false;
174         }
175     }
176 
177     return status;
178 }
179 
180 static struct pf_set *
pf_init(const struct buffer_list * bl,const char * prefix,const bool allow_kill)181 pf_init(const struct buffer_list *bl, const char *prefix, const bool allow_kill)
182 {
183 #define MODE_UNDEF   0
184 #define MODE_CLIENTS 1
185 #define MODE_SUBNETS 2
186     int mode = MODE_UNDEF;
187     int line_num = 0;
188     int n_clients = 0;
189     int n_subnets = 0;
190     int n_errors = 0;
191     struct pf_set *pfs = NULL;
192     char line[PF_MAX_LINE_LEN];
193 
194     ALLOC_OBJ_CLEAR(pfs, struct pf_set);
195     if (bl)
196     {
197         struct pf_cn_elem **cl = &pfs->cns.list;
198         struct pf_subnet **sl = &pfs->sns.list;
199         struct buffer_entry *be;
200 
201         for (be = bl->head; be != NULL; be = be->next)
202         {
203             ++line_num;
204             strncpynt(line, BSTR(&be->buf), sizeof(line));
205             rm_trailing_chars(line, "\r\n\t ");
206             if (line[0] == '\0' || line[0] == '#')
207             {
208             }
209             else if (line[0] == '+' || line[0] == '-')
210             {
211                 bool exclude = (line[0] == '-');
212 
213                 if (line[1] =='\0')
214                 {
215                     msg(D_PF_INFO, "PF: %s/%d: no data after +/-: '%s'", prefix, line_num, line);
216                     ++n_errors;
217                 }
218                 else if (mode == MODE_CLIENTS)
219                 {
220                     if (add_client(&line[1], prefix, line_num, &cl, exclude))
221                     {
222                         ++n_clients;
223                     }
224                     else
225                     {
226                         ++n_errors;
227                     }
228                 }
229                 else if (mode == MODE_SUBNETS)
230                 {
231                     if (add_subnet(&line[1], prefix, line_num, &sl, exclude))
232                     {
233                         ++n_subnets;
234                     }
235                     else
236                     {
237                         ++n_errors;
238                     }
239                 }
240                 else if (mode == MODE_UNDEF)
241                 {
242                 }
243                 else
244                 {
245                     ASSERT(0);
246                 }
247             }
248             else if (line[0] == '[')
249             {
250                 if (!strcasecmp(line, "[clients accept]"))
251                 {
252                     mode = MODE_CLIENTS;
253                     pfs->cns.default_allow = true;
254                 }
255                 else if (!strcasecmp(line, "[clients drop]"))
256                 {
257                     mode = MODE_CLIENTS;
258                     pfs->cns.default_allow = false;
259                 }
260                 else if (!strcasecmp(line, "[subnets accept]"))
261                 {
262                     mode = MODE_SUBNETS;
263                     pfs->sns.default_allow = true;
264                 }
265                 else if (!strcasecmp(line, "[subnets drop]"))
266                 {
267                     mode = MODE_SUBNETS;
268                     pfs->sns.default_allow = false;
269                 }
270                 else if (!strcasecmp(line, "[end]"))
271                 {
272                     goto done;
273                 }
274                 else if (allow_kill && !strcasecmp(line, "[kill]"))
275                 {
276                     goto kill;
277                 }
278                 else
279                 {
280                     mode = MODE_UNDEF;
281                     msg(D_PF_INFO, "PF: %s/%d unknown tag: '%s'", prefix, line_num, line);
282                     ++n_errors;
283                 }
284             }
285             else
286             {
287                 msg(D_PF_INFO, "PF: %s/%d line must begin with '+', '-', or '[' : '%s'", prefix, line_num, line);
288                 ++n_errors;
289             }
290         }
291         ++n_errors;
292         msg(D_PF_INFO, "PF: %s: missing [end]", prefix);
293     }
294     else
295     {
296         msg(D_PF_INFO, "PF: %s: cannot open", prefix);
297         ++n_errors;
298     }
299 
300 done:
301     if (bl)
302     {
303         if (!n_errors)
304         {
305             if (!genhash(&pfs->cns, prefix, n_clients))
306             {
307                 ++n_errors;
308             }
309         }
310         if (n_errors)
311         {
312             msg(D_PF_INFO, "PF: %s rejected due to %d error(s)", prefix, n_errors);
313         }
314     }
315     if (n_errors)
316     {
317         pf_destroy(pfs);
318         pfs = NULL;
319     }
320     return pfs;
321 
322 kill:
323     pf_destroy(pfs);
324     ALLOC_OBJ_CLEAR(pfs, struct pf_set);
325     pfs->kill = true;
326     return pfs;
327 }
328 
329 #ifdef PLUGIN_PF
330 static struct pf_set *
pf_init_from_file(const char * fn)331 pf_init_from_file(const char *fn)
332 {
333     struct buffer_list *bl = buffer_list_file(fn, PF_MAX_LINE_LEN);
334     if (bl)
335     {
336         struct pf_set *pfs = pf_init(bl, fn, true);
337         buffer_list_free(bl);
338         return pfs;
339     }
340     else
341     {
342         msg(D_PF_INFO|M_ERRNO, "PF: %s: cannot open", fn);
343         return NULL;
344     }
345 }
346 #endif
347 
348 #ifdef ENABLE_DEBUG
349 
350 static const char *
drop_accept(const bool accept)351 drop_accept(const bool accept)
352 {
353     return accept ? "ACCEPT" : "DROP";
354 }
355 
356 static const char *
pct_name(const int type)357 pct_name(const int type)
358 {
359     switch (type)
360     {
361         case PCT_SRC:
362             return "SRC";
363 
364         case PCT_DEST:
365             return "DEST";
366 
367         default:
368             return "???";
369     }
370 }
371 
372 static void
pf_cn_test_print(const char * prefix,const int type,const char * prefix2,const char * cn,const bool allow,const struct pf_cn * rule)373 pf_cn_test_print(const char *prefix,
374                  const int type,
375                  const char *prefix2,
376                  const char *cn,
377                  const bool allow,
378                  const struct pf_cn *rule)
379 {
380     if (rule)
381     {
382         dmsg(D_PF_DEBUG, "PF: %s/%s/%s %s %s rule=[%s %s]",
383              prefix, prefix2, pct_name(type),
384              cn, drop_accept(allow),
385              rule->cn, drop_accept(!rule->exclude));
386     }
387     else
388     {
389         dmsg(D_PF_DEBUG, "PF: %s/%s/%s %s %s",
390              prefix, prefix2, pct_name(type),
391              cn, drop_accept(allow));
392     }
393 }
394 
395 static void
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)396 pf_addr_test_print(const char *prefix,
397                    const char *prefix2,
398                    const struct context *src,
399                    const struct mroute_addr *dest,
400                    const bool allow,
401                    const struct ipv4_subnet *rule)
402 {
403     struct gc_arena gc = gc_new();
404     if (rule)
405     {
406         dmsg(D_PF_DEBUG, "PF: %s/%s %s %s %s rule=[%s/%s %s]",
407              prefix,
408              prefix2,
409              tls_common_name(src->c2.tls_multi, false),
410              mroute_addr_print_ex(dest, MAPF_SHOW_ARP, &gc),
411              drop_accept(allow),
412              print_in_addr_t(rule->network, 0, &gc),
413              print_in_addr_t(rule->netmask, 0, &gc),
414              drop_accept(!rule->exclude));
415     }
416     else
417     {
418         dmsg(D_PF_DEBUG, "PF: %s/%s %s %s %s",
419              prefix,
420              prefix2,
421              tls_common_name(src->c2.tls_multi, false),
422              mroute_addr_print_ex(dest, MAPF_SHOW_ARP, &gc),
423              drop_accept(allow));
424     }
425     gc_free(&gc);
426 }
427 
428 #endif /* ifdef ENABLE_DEBUG */
429 
430 static inline struct pf_cn *
lookup_cn_rule(struct hash * h,const char * cn,const uint32_t cn_hash)431 lookup_cn_rule(struct hash *h, const char *cn, const uint32_t cn_hash)
432 {
433     struct hash_element *he = hash_lookup_fast(h, hash_bucket(h, cn_hash), cn, cn_hash);
434     if (he)
435     {
436         return (struct pf_cn *) he->value;
437     }
438     else
439     {
440         return NULL;
441     }
442 }
443 
444 bool
pf_cn_test(struct pf_set * pfs,const struct tls_multi * tm,const int type,const char * prefix)445 pf_cn_test(struct pf_set *pfs, const struct tls_multi *tm, const int type, const char *prefix)
446 {
447     if (pfs && !pfs->kill)
448     {
449         const char *cn;
450         uint32_t cn_hash;
451         if (tls_common_name_hash(tm, &cn, &cn_hash))
452         {
453             const struct pf_cn *rule = lookup_cn_rule(pfs->cns.hash_table, cn, cn_hash);
454             if (rule)
455             {
456 #ifdef ENABLE_DEBUG
457                 if (check_debug_level(D_PF_DEBUG))
458                 {
459                     pf_cn_test_print("PF_CN_MATCH", type, prefix, cn, !rule->exclude, rule);
460                 }
461 #endif
462                 if (!rule->exclude)
463                 {
464                     return true;
465                 }
466                 else
467                 {
468                     return false;
469                 }
470             }
471             else
472             {
473 #ifdef ENABLE_DEBUG
474                 if (check_debug_level(D_PF_DEBUG))
475                 {
476                     pf_cn_test_print("PF_CN_DEFAULT", type, prefix, cn, pfs->cns.default_allow, NULL);
477                 }
478 #endif
479                 if (pfs->cns.default_allow)
480                 {
481                     return true;
482                 }
483                 else
484                 {
485                     return false;
486                 }
487             }
488         }
489     }
490 #ifdef ENABLE_DEBUG
491     if (check_debug_level(D_PF_DEBUG))
492     {
493         pf_cn_test_print("PF_CN_FAULT", type, prefix, tls_common_name(tm, false), false, NULL);
494     }
495 #endif
496     return false;
497 }
498 
499 bool
pf_addr_test_dowork(const struct context * src,const struct mroute_addr * dest,const char * prefix)500 pf_addr_test_dowork(const struct context *src, const struct mroute_addr *dest, const char *prefix)
501 {
502     struct pf_set *pfs = src->c2.pf.pfs;
503     if (pfs && !pfs->kill)
504     {
505         const in_addr_t addr = in_addr_t_from_mroute_addr(dest);
506         const struct pf_subnet *se = pfs->sns.list;
507         while (se)
508         {
509             if ((addr & se->rule.netmask) == se->rule.network)
510             {
511 #ifdef ENABLE_DEBUG
512                 if (check_debug_level(D_PF_DEBUG))
513                 {
514                     pf_addr_test_print("PF_ADDR_MATCH", prefix, src, dest, !se->rule.exclude, &se->rule);
515                 }
516 #endif
517                 return !se->rule.exclude;
518             }
519             se = se->next;
520         }
521 #ifdef ENABLE_DEBUG
522         if (check_debug_level(D_PF_DEBUG))
523         {
524             pf_addr_test_print("PF_ADDR_DEFAULT", prefix, src, dest, pfs->sns.default_allow, NULL);
525         }
526 #endif
527         return pfs->sns.default_allow;
528     }
529     else
530     {
531 #ifdef ENABLE_DEBUG
532         if (check_debug_level(D_PF_DEBUG))
533         {
534             pf_addr_test_print("PF_ADDR_FAULT", prefix, src, dest, false, NULL);
535         }
536 #endif
537         return false;
538     }
539 }
540 
541 #ifdef PLUGIN_PF
542 void
pf_check_reload(struct context * c)543 pf_check_reload(struct context *c)
544 {
545     const int slow_wakeup = 15;
546     const int fast_wakeup = 1;
547     const int wakeup_transition = 60;
548     bool reloaded = false;
549 
550     if (c->c2.pf.filename)
551     {
552         platform_stat_t s;
553         if (!platform_stat(c->c2.pf.filename, &s))
554         {
555             if (s.st_mtime > c->c2.pf.file_last_mod)
556             {
557                 struct pf_set *pfs = pf_init_from_file(c->c2.pf.filename);
558                 if (pfs)
559                 {
560                     if (c->c2.pf.pfs)
561                     {
562                         pf_destroy(c->c2.pf.pfs);
563                     }
564                     c->c2.pf.pfs = pfs;
565                     reloaded = true;
566                     if (pf_kill_test(pfs))
567                     {
568                         c->sig->signal_received = SIGTERM;
569                         c->sig->signal_text = "pf-kill";
570                     }
571                 }
572                 c->c2.pf.file_last_mod = s.st_mtime;
573             }
574         }
575         {
576             int wakeup = slow_wakeup;
577             if (!c->c2.pf.pfs && c->c2.pf.n_check_reload < wakeup_transition)
578             {
579                 wakeup = fast_wakeup;
580             }
581             event_timeout_init(&c->c2.pf.reload, wakeup, now);
582             reset_coarse_timers(c);
583             c->c2.pf.n_check_reload++;
584         }
585     }
586 #ifdef ENABLE_DEBUG
587     if (reloaded && check_debug_level(D_PF_DEBUG))
588     {
589         pf_context_print(&c->c2.pf, "pf_check_reload", D_PF_DEBUG);
590     }
591 #endif
592 }
593 #endif /* ifdef PLUGIN_PF */
594 
595 #ifdef MANAGEMENT_PF
596 bool
pf_load_from_buffer_list(struct context * c,const struct buffer_list * config)597 pf_load_from_buffer_list(struct context *c, const struct buffer_list *config)
598 {
599     struct pf_set *pfs = pf_init(config, "[SERVER-PF]", false);
600     if (pfs)
601     {
602         if (c->c2.pf.pfs)
603         {
604             pf_destroy(c->c2.pf.pfs);
605         }
606         c->c2.pf.pfs = pfs;
607         return true;
608     }
609     else
610     {
611         return false;
612     }
613 }
614 #endif
615 
616 void
pf_init_context(struct context * c)617 pf_init_context(struct context *c)
618 {
619 #ifdef PLUGIN_PF
620     if (plugin_defined(c->plugins, OPENVPN_PLUGIN_ENABLE_PF))
621     {
622         c->c2.pf.filename = platform_create_temp_file(c->options.tmp_dir, "pf",
623                                                       &c->c2.gc);
624         if (c->c2.pf.filename)
625         {
626             setenv_str(c->c2.es, "pf_file", c->c2.pf.filename);
627 
628             if (plugin_call(c->plugins, OPENVPN_PLUGIN_ENABLE_PF, NULL, NULL, c->c2.es) == OPENVPN_PLUGIN_FUNC_SUCCESS)
629             {
630                 event_timeout_init(&c->c2.pf.reload, 1, now);
631                 c->c2.pf.enabled = true;
632 #ifdef ENABLE_DEBUG
633                 if (check_debug_level(D_PF_DEBUG))
634                 {
635                     pf_context_print(&c->c2.pf, "pf_init_context#1", D_PF_DEBUG);
636                 }
637 #endif
638             }
639         }
640         if (!c->c2.pf.enabled)
641         {
642             /* At some point in openvpn history, this code just printed a
643              * warning and signalled itself (SIGUSR1, "plugin-pf-init-failed")
644              * to terminate the client instance.  This got broken at one of
645              * the client auth state refactorings (leading to SIGSEGV crashes)
646              * and due to "pf will be removed anyway" reasons the easiest way
647              * to prevent crashes is to REQUIRE that plugins succeed - so if
648              * the plugin fails, we cleanly abort OpenVPN
649              *
650              * see also: https://community.openvpn.net/openvpn/ticket/1377
651              */
652             msg(M_FATAL, "FATAL: failed to init PF plugin, must succeed.");
653             return;
654         }
655     }
656 #endif /* ifdef PLUGIN_PF */
657 #ifdef MANAGEMENT_PF
658     if (!c->c2.pf.enabled && management_enable_pf(management))
659     {
660         c->c2.pf.enabled = true;
661 #ifdef ENABLE_DEBUG
662         if (check_debug_level(D_PF_DEBUG))
663         {
664             pf_context_print(&c->c2.pf, "pf_init_context#2", D_PF_DEBUG);
665         }
666 #endif
667     }
668 #endif
669 }
670 
671 void
pf_destroy_context(struct pf_context * pfc)672 pf_destroy_context(struct pf_context *pfc)
673 {
674 #ifdef PLUGIN_PF
675     if (pfc->filename)
676     {
677         platform_unlink(pfc->filename);
678     }
679 #endif
680     if (pfc->pfs)
681     {
682         pf_destroy(pfc->pfs);
683     }
684 }
685 
686 #ifdef ENABLE_DEBUG
687 
688 static void
pf_subnet_set_print(const struct pf_subnet_set * s,const int lev)689 pf_subnet_set_print(const struct pf_subnet_set *s, const int lev)
690 {
691     struct gc_arena gc = gc_new();
692     if (s)
693     {
694         struct pf_subnet *e;
695 
696         msg(lev, "  ----- struct pf_subnet_set -----");
697         msg(lev, "  default_allow=%s", drop_accept(s->default_allow));
698 
699         for (e = s->list; e != NULL; e = e->next)
700         {
701             msg(lev, "   %s/%s %s",
702                 print_in_addr_t(e->rule.network, 0, &gc),
703                 print_in_addr_t(e->rule.netmask, 0, &gc),
704                 drop_accept(!e->rule.exclude));
705         }
706     }
707     gc_free(&gc);
708 }
709 
710 static void
pf_cn_set_print(const struct pf_cn_set * s,const int lev)711 pf_cn_set_print(const struct pf_cn_set *s, const int lev)
712 {
713     if (s)
714     {
715         struct hash_iterator hi;
716         struct hash_element *he;
717 
718         msg(lev, "  ----- struct pf_cn_set -----");
719         msg(lev, "  default_allow=%s", drop_accept(s->default_allow));
720 
721         if (s->hash_table)
722         {
723             hash_iterator_init(s->hash_table, &hi);
724             while ((he = hash_iterator_next(&hi)))
725             {
726                 struct pf_cn *e = (struct pf_cn *)he->value;
727                 msg(lev, "   %s %s",
728                     e->cn,
729                     drop_accept(!e->exclude));
730             }
731 
732             msg(lev, "  ----------");
733 
734             {
735                 struct pf_cn_elem *ce;
736                 for (ce = s->list; ce != NULL; ce = ce->next)
737                 {
738                     struct pf_cn *e = lookup_cn_rule(s->hash_table, ce->rule.cn, cn_hash_function(ce->rule.cn, 0));
739                     if (e)
740                     {
741                         msg(lev, "   %s %s",
742                             e->cn,
743                             drop_accept(!e->exclude));
744                     }
745                     else
746                     {
747                         msg(lev, "   %s LOOKUP FAILED", ce->rule.cn);
748                     }
749                 }
750             }
751         }
752     }
753 }
754 
755 static void
pf_set_print(const struct pf_set * pfs,const int lev)756 pf_set_print(const struct pf_set *pfs, const int lev)
757 {
758     if (pfs)
759     {
760         msg(lev, " ----- struct pf_set -----");
761         msg(lev, " kill=%d", pfs->kill);
762         pf_subnet_set_print(&pfs->sns, lev);
763         pf_cn_set_print(&pfs->cns, lev);
764     }
765 }
766 
767 void
pf_context_print(const struct pf_context * pfc,const char * prefix,const int lev)768 pf_context_print(const struct pf_context *pfc, const char *prefix, const int lev)
769 {
770     msg(lev, "----- %s : struct pf_context -----", prefix);
771     if (pfc)
772     {
773         msg(lev, "enabled=%d", pfc->enabled);
774 #ifdef PLUGIN_PF
775         msg(lev, "filename='%s'", np(pfc->filename));
776         msg(lev, "file_last_mod=%u", (unsigned int)pfc->file_last_mod);
777         msg(lev, "n_check_reload=%u", pfc->n_check_reload);
778         msg(lev, "reload=[%d,%u,%u]", pfc->reload.defined, pfc->reload.n, (unsigned int)pfc->reload.last);
779 #endif
780         pf_set_print(pfc->pfs, lev);
781     }
782     msg(lev, "--------------------");
783 }
784 
785 #endif /* ifdef ENABLE_DEBUG */
786 
787 #endif /* if defined(ENABLE_PF) */
788