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