1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2005 Philip Paeps <philip@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#define PFIOC_USE_LATEST
30
31#include <sys/queue.h>
32#include <bsnmp/snmpmod.h>
33
34#include <net/pfvar.h>
35#include <sys/ioctl.h>
36
37#include <errno.h>
38#include <fcntl.h>
39#include <libpfctl.h>
40#include <stdint.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <syslog.h>
45#include <unistd.h>
46
47#define	SNMPTREE_TYPES
48#include "pf_oid.h"
49#include "pf_tree.h"
50
51struct lmodule *module;
52
53static struct pfctl_handle *pfh;
54static int started;
55static uint64_t pf_tick;
56
57static struct pfctl_status *pfs;
58
59enum { IN, OUT };
60enum { IPV4, IPV6 };
61enum { PASS, BLOCK };
62
63#define PFI_IFTYPE_GROUP	0
64#define PFI_IFTYPE_INSTANCE	1
65#define PFI_IFTYPE_DETACHED	2
66
67struct pfi_entry {
68	struct pfi_kif	pfi;
69	u_int		index;
70	TAILQ_ENTRY(pfi_entry) link;
71};
72TAILQ_HEAD(pfi_table, pfi_entry);
73
74static struct pfi_table pfi_table;
75static time_t pfi_table_age;
76static int pfi_table_count;
77
78#define PFI_TABLE_MAXAGE	5
79
80struct pft_entry {
81	struct pfr_tstats pft;
82	u_int		index;
83	TAILQ_ENTRY(pft_entry) link;
84};
85TAILQ_HEAD(pft_table, pft_entry);
86
87static struct pft_table pft_table;
88static time_t pft_table_age;
89static int pft_table_count;
90
91#define PFT_TABLE_MAXAGE	5
92
93struct pfa_entry {
94	struct pfr_astats pfas;
95	u_int		index;
96	TAILQ_ENTRY(pfa_entry) link;
97};
98TAILQ_HEAD(pfa_table, pfa_entry);
99
100static struct pfa_table pfa_table;
101static time_t pfa_table_age;
102static int pfa_table_count;
103
104#define	PFA_TABLE_MAXAGE	5
105
106struct pfq_entry {
107	struct pf_altq	altq;
108	u_int		index;
109	TAILQ_ENTRY(pfq_entry) link;
110};
111TAILQ_HEAD(pfq_table, pfq_entry);
112
113static struct pfq_table pfq_table;
114static time_t pfq_table_age;
115static int pfq_table_count;
116
117static int altq_enabled = 0;
118
119#define PFQ_TABLE_MAXAGE	5
120
121struct pfl_entry {
122	char		name[MAXPATHLEN + PF_RULE_LABEL_SIZE];
123	u_int64_t	evals;
124	u_int64_t	bytes[2];
125	u_int64_t	pkts[2];
126	u_int		index;
127	TAILQ_ENTRY(pfl_entry) link;
128};
129TAILQ_HEAD(pfl_table, pfl_entry);
130
131static struct pfl_table pfl_table;
132static time_t pfl_table_age;
133static int pfl_table_count;
134
135#define	PFL_TABLE_MAXAGE	5
136
137/* Forward declarations */
138static int pfi_refresh(void);
139static int pfq_refresh(void);
140static int pfs_refresh(void);
141static int pft_refresh(void);
142static int pfa_refresh(void);
143static int pfl_refresh(void);
144static struct pfi_entry * pfi_table_find(u_int idx);
145static struct pfq_entry * pfq_table_find(u_int idx);
146static struct pft_entry * pft_table_find(u_int idx);
147static struct pfa_entry * pfa_table_find(u_int idx);
148static struct pfl_entry * pfl_table_find(u_int idx);
149
150static int altq_is_enabled(int pfdevice);
151
152int
153pf_status(struct snmp_context __unused *ctx, struct snmp_value *val,
154	u_int sub, u_int __unused vindex, enum snmp_op op)
155{
156	asn_subid_t	which = val->var.subs[sub - 1];
157	time_t		runtime;
158	unsigned char	str[128];
159
160	if (op == SNMP_OP_SET)
161		return (SNMP_ERR_NOT_WRITEABLE);
162
163	if (op == SNMP_OP_GET) {
164		if (pfs_refresh() == -1)
165			return (SNMP_ERR_GENERR);
166
167		switch (which) {
168			case LEAF_pfStatusRunning:
169			    val->v.uint32 = pfs->running;
170			    break;
171			case LEAF_pfStatusRuntime:
172			    runtime = (pfs->since > 0) ?
173				time(NULL) - pfs->since : 0;
174			    val->v.uint32 = runtime * 100;
175			    break;
176			case LEAF_pfStatusDebug:
177			    val->v.uint32 = pfs->debug;
178			    break;
179			case LEAF_pfStatusHostId:
180			    sprintf(str, "0x%08x", ntohl(pfs->hostid));
181			    return (string_get(val, str, strlen(str)));
182
183			default:
184			    return (SNMP_ERR_NOSUCHNAME);
185		}
186
187		return (SNMP_ERR_NOERROR);
188	}
189
190	abort();
191}
192
193int
194pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val,
195	u_int sub, u_int __unused vindex, enum snmp_op op)
196{
197	asn_subid_t	which = val->var.subs[sub - 1];
198
199	if (op == SNMP_OP_SET)
200		return (SNMP_ERR_NOT_WRITEABLE);
201
202	if (op == SNMP_OP_GET) {
203		if (pfs_refresh() == -1)
204			return (SNMP_ERR_GENERR);
205
206		switch (which) {
207			case LEAF_pfCounterMatch:
208				val->v.counter64 = pfctl_status_counter(pfs, PFRES_MATCH);
209				break;
210			case LEAF_pfCounterBadOffset:
211				val->v.counter64 = pfctl_status_counter(pfs, PFRES_BADOFF);
212				break;
213			case LEAF_pfCounterFragment:
214				val->v.counter64 = pfctl_status_counter(pfs, PFRES_FRAG);
215				break;
216			case LEAF_pfCounterShort:
217				val->v.counter64 = pfctl_status_counter(pfs, PFRES_SHORT);
218				break;
219			case LEAF_pfCounterNormalize:
220				val->v.counter64 = pfctl_status_counter(pfs, PFRES_NORM);
221				break;
222			case LEAF_pfCounterMemDrop:
223				val->v.counter64 = pfctl_status_counter(pfs, PFRES_MEMORY);
224				break;
225
226			default:
227				return (SNMP_ERR_NOSUCHNAME);
228		}
229
230		return (SNMP_ERR_NOERROR);
231	}
232
233	abort();
234}
235
236int
237pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val,
238	u_int sub, u_int __unused vindex, enum snmp_op op)
239{
240	asn_subid_t	which = val->var.subs[sub - 1];
241
242	if (op == SNMP_OP_SET)
243		return (SNMP_ERR_NOT_WRITEABLE);
244
245	if (op == SNMP_OP_GET) {
246		if (pfs_refresh() == -1)
247			return (SNMP_ERR_GENERR);
248
249		switch (which) {
250			case LEAF_pfStateTableCount:
251				val->v.uint32 = pfs->states;
252				break;
253			case LEAF_pfStateTableSearches:
254				val->v.counter64 =
255				    pfctl_status_fcounter(pfs, FCNT_STATE_SEARCH);
256				break;
257			case LEAF_pfStateTableInserts:
258				val->v.counter64 =
259				    pfctl_status_fcounter(pfs, FCNT_STATE_INSERT);
260				break;
261			case LEAF_pfStateTableRemovals:
262				val->v.counter64 =
263				    pfctl_status_fcounter(pfs, FCNT_STATE_REMOVALS);
264				break;
265
266			default:
267				return (SNMP_ERR_NOSUCHNAME);
268		}
269
270		return (SNMP_ERR_NOERROR);
271	}
272
273	abort();
274}
275
276int
277pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val,
278	u_int sub, u_int __unused vindex, enum snmp_op op)
279{
280	asn_subid_t	which = val->var.subs[sub - 1];
281
282	if (op == SNMP_OP_SET)
283		return (SNMP_ERR_NOT_WRITEABLE);
284
285	if (op == SNMP_OP_GET) {
286		if (pfs_refresh() == -1)
287			return (SNMP_ERR_GENERR);
288
289		switch (which) {
290			case LEAF_pfSrcNodesCount:
291				val->v.uint32 = pfs->src_nodes;
292				break;
293			case LEAF_pfSrcNodesSearches:
294				val->v.counter64 =
295				    pfctl_status_scounter(pfs, SCNT_SRC_NODE_SEARCH);
296				break;
297			case LEAF_pfSrcNodesInserts:
298				val->v.counter64 =
299				    pfctl_status_scounter(pfs, SCNT_SRC_NODE_INSERT);
300				break;
301			case LEAF_pfSrcNodesRemovals:
302				val->v.counter64 =
303				    pfctl_status_scounter(pfs, SCNT_SRC_NODE_REMOVALS);
304				break;
305
306			default:
307				return (SNMP_ERR_NOSUCHNAME);
308		}
309
310		return (SNMP_ERR_NOERROR);
311	}
312
313	abort();
314}
315
316int
317pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val,
318	u_int sub, u_int __unused vindex, enum snmp_op op)
319{
320	asn_subid_t		which = val->var.subs[sub - 1];
321	struct pfioc_limit	pl;
322
323	if (op == SNMP_OP_SET)
324		return (SNMP_ERR_NOT_WRITEABLE);
325
326	if (op == SNMP_OP_GET) {
327		bzero(&pl, sizeof(struct pfioc_limit));
328
329		switch (which) {
330			case LEAF_pfLimitsStates:
331				pl.index = PF_LIMIT_STATES;
332				break;
333			case LEAF_pfLimitsSrcNodes:
334				pl.index = PF_LIMIT_SRC_NODES;
335				break;
336			case LEAF_pfLimitsFrags:
337				pl.index = PF_LIMIT_FRAGS;
338				break;
339
340			default:
341				return (SNMP_ERR_NOSUCHNAME);
342		}
343
344		if (ioctl(pfctl_fd(pfh), DIOCGETLIMIT, &pl)) {
345			syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
346			    strerror(errno));
347			return (SNMP_ERR_GENERR);
348		}
349
350		val->v.uint32 = pl.limit;
351
352		return (SNMP_ERR_NOERROR);
353	}
354
355	abort();
356}
357
358int
359pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val,
360	u_int sub, u_int __unused vindex, enum snmp_op op)
361{
362	asn_subid_t	which = val->var.subs[sub - 1];
363	struct pfioc_tm	pt;
364
365	if (op == SNMP_OP_SET)
366		return (SNMP_ERR_NOT_WRITEABLE);
367
368	if (op == SNMP_OP_GET) {
369		bzero(&pt, sizeof(struct pfioc_tm));
370
371		switch (which) {
372			case LEAF_pfTimeoutsTcpFirst:
373				pt.timeout = PFTM_TCP_FIRST_PACKET;
374				break;
375			case LEAF_pfTimeoutsTcpOpening:
376				pt.timeout = PFTM_TCP_OPENING;
377				break;
378			case LEAF_pfTimeoutsTcpEstablished:
379				pt.timeout = PFTM_TCP_ESTABLISHED;
380				break;
381			case LEAF_pfTimeoutsTcpClosing:
382				pt.timeout = PFTM_TCP_CLOSING;
383				break;
384			case LEAF_pfTimeoutsTcpFinWait:
385				pt.timeout = PFTM_TCP_FIN_WAIT;
386				break;
387			case LEAF_pfTimeoutsTcpClosed:
388				pt.timeout = PFTM_TCP_CLOSED;
389				break;
390			case LEAF_pfTimeoutsUdpFirst:
391				pt.timeout = PFTM_UDP_FIRST_PACKET;
392				break;
393			case LEAF_pfTimeoutsUdpSingle:
394				pt.timeout = PFTM_UDP_SINGLE;
395				break;
396			case LEAF_pfTimeoutsUdpMultiple:
397				pt.timeout = PFTM_UDP_MULTIPLE;
398				break;
399			case LEAF_pfTimeoutsIcmpFirst:
400				pt.timeout = PFTM_ICMP_FIRST_PACKET;
401				break;
402			case LEAF_pfTimeoutsIcmpError:
403				pt.timeout = PFTM_ICMP_ERROR_REPLY;
404				break;
405			case LEAF_pfTimeoutsOtherFirst:
406				pt.timeout = PFTM_OTHER_FIRST_PACKET;
407				break;
408			case LEAF_pfTimeoutsOtherSingle:
409				pt.timeout = PFTM_OTHER_SINGLE;
410				break;
411			case LEAF_pfTimeoutsOtherMultiple:
412				pt.timeout = PFTM_OTHER_MULTIPLE;
413				break;
414			case LEAF_pfTimeoutsFragment:
415				pt.timeout = PFTM_FRAG;
416				break;
417			case LEAF_pfTimeoutsInterval:
418				pt.timeout = PFTM_INTERVAL;
419				break;
420			case LEAF_pfTimeoutsAdaptiveStart:
421				pt.timeout = PFTM_ADAPTIVE_START;
422				break;
423			case LEAF_pfTimeoutsAdaptiveEnd:
424				pt.timeout = PFTM_ADAPTIVE_END;
425				break;
426			case LEAF_pfTimeoutsSrcNode:
427				pt.timeout = PFTM_SRC_NODE;
428				break;
429
430			default:
431				return (SNMP_ERR_NOSUCHNAME);
432		}
433
434		if (ioctl(pfctl_fd(pfh), DIOCGETTIMEOUT, &pt)) {
435			syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s",
436			    strerror(errno));
437			return (SNMP_ERR_GENERR);
438		}
439
440		val->v.integer = pt.seconds;
441
442		return (SNMP_ERR_NOERROR);
443	}
444
445	abort();
446}
447
448int
449pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val,
450	u_int sub, u_int __unused vindex, enum snmp_op op)
451{
452	asn_subid_t	which = val->var.subs[sub - 1];
453	unsigned char	str[IFNAMSIZ];
454
455	if (op == SNMP_OP_SET)
456		return (SNMP_ERR_NOT_WRITEABLE);
457
458	if (op == SNMP_OP_GET) {
459		if (pfs_refresh() == -1)
460			return (SNMP_ERR_GENERR);
461
462		switch (which) {
463	 		case LEAF_pfLogInterfaceName:
464				strlcpy(str, pfs->ifname, sizeof str);
465				return (string_get(val, str, strlen(str)));
466			case LEAF_pfLogInterfaceIp4BytesIn:
467				val->v.counter64 = pfs->bcounters[IPV4][IN];
468				break;
469			case LEAF_pfLogInterfaceIp4BytesOut:
470				val->v.counter64 = pfs->bcounters[IPV4][OUT];
471				break;
472			case LEAF_pfLogInterfaceIp4PktsInPass:
473				val->v.counter64 =
474				    pfs->pcounters[IPV4][IN][PF_PASS];
475				break;
476			case LEAF_pfLogInterfaceIp4PktsInDrop:
477				val->v.counter64 =
478				    pfs->pcounters[IPV4][IN][PF_DROP];
479				break;
480			case LEAF_pfLogInterfaceIp4PktsOutPass:
481				val->v.counter64 =
482				    pfs->pcounters[IPV4][OUT][PF_PASS];
483				break;
484			case LEAF_pfLogInterfaceIp4PktsOutDrop:
485				val->v.counter64 =
486				    pfs->pcounters[IPV4][OUT][PF_DROP];
487				break;
488			case LEAF_pfLogInterfaceIp6BytesIn:
489				val->v.counter64 = pfs->bcounters[IPV6][IN];
490				break;
491			case LEAF_pfLogInterfaceIp6BytesOut:
492				val->v.counter64 = pfs->bcounters[IPV6][OUT];
493				break;
494			case LEAF_pfLogInterfaceIp6PktsInPass:
495				val->v.counter64 =
496				    pfs->pcounters[IPV6][IN][PF_PASS];
497				break;
498			case LEAF_pfLogInterfaceIp6PktsInDrop:
499				val->v.counter64 =
500				    pfs->pcounters[IPV6][IN][PF_DROP];
501				break;
502			case LEAF_pfLogInterfaceIp6PktsOutPass:
503				val->v.counter64 =
504				    pfs->pcounters[IPV6][OUT][PF_PASS];
505				break;
506			case LEAF_pfLogInterfaceIp6PktsOutDrop:
507				val->v.counter64 =
508				    pfs->pcounters[IPV6][OUT][PF_DROP];
509				break;
510
511			default:
512				return (SNMP_ERR_NOSUCHNAME);
513		}
514
515		return (SNMP_ERR_NOERROR);
516	}
517
518	abort();
519}
520
521int
522pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val,
523	u_int sub, u_int __unused vindex, enum snmp_op op)
524{
525	asn_subid_t	which = val->var.subs[sub - 1];
526
527	if (op == SNMP_OP_SET)
528		return (SNMP_ERR_NOT_WRITEABLE);
529
530	if (op == SNMP_OP_GET) {
531		if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
532			if (pfi_refresh() == -1)
533			    return (SNMP_ERR_GENERR);
534
535		switch (which) {
536			case LEAF_pfInterfacesIfNumber:
537				val->v.uint32 = pfi_table_count;
538				break;
539
540			default:
541				return (SNMP_ERR_NOSUCHNAME);
542		}
543
544		return (SNMP_ERR_NOERROR);
545	}
546
547	abort();
548}
549
550int
551pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val,
552	u_int sub, u_int __unused vindex, enum snmp_op op)
553{
554	asn_subid_t	which = val->var.subs[sub - 1];
555	struct pfi_entry *e = NULL;
556
557	if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
558		pfi_refresh();
559
560	switch (op) {
561		case SNMP_OP_SET:
562			return (SNMP_ERR_NOT_WRITEABLE);
563		case SNMP_OP_GETNEXT:
564			if ((e = NEXT_OBJECT_INT(&pfi_table,
565			    &val->var, sub)) == NULL)
566				return (SNMP_ERR_NOSUCHNAME);
567			val->var.len = sub + 1;
568			val->var.subs[sub] = e->index;
569			break;
570		case SNMP_OP_GET:
571			if (val->var.len - sub != 1)
572				return (SNMP_ERR_NOSUCHNAME);
573			if ((e = pfi_table_find(val->var.subs[sub])) == NULL)
574				return (SNMP_ERR_NOSUCHNAME);
575			break;
576
577		case SNMP_OP_COMMIT:
578		case SNMP_OP_ROLLBACK:
579		default:
580			abort();
581	}
582
583	switch (which) {
584		case LEAF_pfInterfacesIfDescr:
585			return (string_get(val, e->pfi.pfik_name, -1));
586		case LEAF_pfInterfacesIfType:
587			val->v.integer = PFI_IFTYPE_INSTANCE;
588			break;
589		case LEAF_pfInterfacesIfTZero:
590			val->v.uint32 =
591			    (time(NULL) - e->pfi.pfik_tzero) * 100;
592			break;
593		case LEAF_pfInterfacesIfRefsRule:
594			val->v.uint32 = e->pfi.pfik_rulerefs;
595			break;
596		case LEAF_pfInterfacesIf4BytesInPass:
597			val->v.counter64 =
598			    e->pfi.pfik_bytes[IPV4][IN][PASS];
599			break;
600		case LEAF_pfInterfacesIf4BytesInBlock:
601			val->v.counter64 =
602			    e->pfi.pfik_bytes[IPV4][IN][BLOCK];
603			break;
604		case LEAF_pfInterfacesIf4BytesOutPass:
605			val->v.counter64 =
606			    e->pfi.pfik_bytes[IPV4][OUT][PASS];
607			break;
608		case LEAF_pfInterfacesIf4BytesOutBlock:
609			val->v.counter64 =
610			    e->pfi.pfik_bytes[IPV4][OUT][BLOCK];
611			break;
612		case LEAF_pfInterfacesIf4PktsInPass:
613			val->v.counter64 =
614			    e->pfi.pfik_packets[IPV4][IN][PASS];
615			break;
616		case LEAF_pfInterfacesIf4PktsInBlock:
617			val->v.counter64 =
618			    e->pfi.pfik_packets[IPV4][IN][BLOCK];
619			break;
620		case LEAF_pfInterfacesIf4PktsOutPass:
621			val->v.counter64 =
622			    e->pfi.pfik_packets[IPV4][OUT][PASS];
623			break;
624		case LEAF_pfInterfacesIf4PktsOutBlock:
625			val->v.counter64 =
626			    e->pfi.pfik_packets[IPV4][OUT][BLOCK];
627			break;
628		case LEAF_pfInterfacesIf6BytesInPass:
629			val->v.counter64 =
630			    e->pfi.pfik_bytes[IPV6][IN][PASS];
631			break;
632		case LEAF_pfInterfacesIf6BytesInBlock:
633			val->v.counter64 =
634			    e->pfi.pfik_bytes[IPV6][IN][BLOCK];
635			break;
636		case LEAF_pfInterfacesIf6BytesOutPass:
637			val->v.counter64 =
638			    e->pfi.pfik_bytes[IPV6][OUT][PASS];
639			break;
640		case LEAF_pfInterfacesIf6BytesOutBlock:
641			val->v.counter64 =
642			    e->pfi.pfik_bytes[IPV6][OUT][BLOCK];
643			break;
644		case LEAF_pfInterfacesIf6PktsInPass:
645			val->v.counter64 =
646			    e->pfi.pfik_packets[IPV6][IN][PASS];
647			break;
648		case LEAF_pfInterfacesIf6PktsInBlock:
649			val->v.counter64 =
650			    e->pfi.pfik_packets[IPV6][IN][BLOCK];
651			break;
652		case LEAF_pfInterfacesIf6PktsOutPass:
653			val->v.counter64 =
654			    e->pfi.pfik_packets[IPV6][OUT][PASS];
655			break;
656		case LEAF_pfInterfacesIf6PktsOutBlock:
657			val->v.counter64 =
658			    e->pfi.pfik_packets[IPV6][OUT][BLOCK];
659			break;
660
661		default:
662			return (SNMP_ERR_NOSUCHNAME);
663	}
664
665	return (SNMP_ERR_NOERROR);
666}
667
668int
669pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
670	u_int sub, u_int __unused vindex, enum snmp_op op)
671{
672	asn_subid_t	which = val->var.subs[sub - 1];
673
674	if (op == SNMP_OP_SET)
675		return (SNMP_ERR_NOT_WRITEABLE);
676
677	if (op == SNMP_OP_GET) {
678		if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
679			if (pft_refresh() == -1)
680			    return (SNMP_ERR_GENERR);
681
682		switch (which) {
683			case LEAF_pfTablesTblNumber:
684				val->v.uint32 = pft_table_count;
685				break;
686
687			default:
688				return (SNMP_ERR_NOSUCHNAME);
689		}
690
691		return (SNMP_ERR_NOERROR);
692	}
693
694	abort();
695}
696
697int
698pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
699	u_int sub, u_int __unused vindex, enum snmp_op op)
700{
701	asn_subid_t	which = val->var.subs[sub - 1];
702	struct pft_entry *e = NULL;
703
704	if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
705		pft_refresh();
706
707	switch (op) {
708		case SNMP_OP_SET:
709			return (SNMP_ERR_NOT_WRITEABLE);
710		case SNMP_OP_GETNEXT:
711			if ((e = NEXT_OBJECT_INT(&pft_table,
712			    &val->var, sub)) == NULL)
713				return (SNMP_ERR_NOSUCHNAME);
714			val->var.len = sub + 1;
715			val->var.subs[sub] = e->index;
716			break;
717		case SNMP_OP_GET:
718			if (val->var.len - sub != 1)
719				return (SNMP_ERR_NOSUCHNAME);
720			if ((e = pft_table_find(val->var.subs[sub])) == NULL)
721				return (SNMP_ERR_NOSUCHNAME);
722			break;
723
724		case SNMP_OP_COMMIT:
725		case SNMP_OP_ROLLBACK:
726		default:
727			abort();
728	}
729
730	switch (which) {
731		case LEAF_pfTablesTblDescr:
732			return (string_get(val, e->pft.pfrts_name, -1));
733		case LEAF_pfTablesTblCount:
734			val->v.integer = e->pft.pfrts_cnt;
735			break;
736		case LEAF_pfTablesTblTZero:
737			val->v.uint32 =
738			    (time(NULL) - e->pft.pfrts_tzero) * 100;
739			break;
740		case LEAF_pfTablesTblRefsAnchor:
741			val->v.integer =
742			    e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
743			break;
744		case LEAF_pfTablesTblRefsRule:
745			val->v.integer =
746			    e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
747			break;
748		case LEAF_pfTablesTblEvalMatch:
749			val->v.counter64 = e->pft.pfrts_match;
750			break;
751		case LEAF_pfTablesTblEvalNoMatch:
752			val->v.counter64 = e->pft.pfrts_nomatch;
753			break;
754		case LEAF_pfTablesTblBytesInPass:
755			val->v.counter64 =
756			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
757			break;
758		case LEAF_pfTablesTblBytesInBlock:
759			val->v.counter64 =
760			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
761			break;
762		case LEAF_pfTablesTblBytesInXPass:
763			val->v.counter64 =
764			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
765			break;
766		case LEAF_pfTablesTblBytesOutPass:
767			val->v.counter64 =
768			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
769			break;
770		case LEAF_pfTablesTblBytesOutBlock:
771			val->v.counter64 =
772			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
773			break;
774		case LEAF_pfTablesTblBytesOutXPass:
775			val->v.counter64 =
776			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
777			break;
778		case LEAF_pfTablesTblPktsInPass:
779			val->v.counter64 =
780			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
781			break;
782		case LEAF_pfTablesTblPktsInBlock:
783			val->v.counter64 =
784			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
785			break;
786		case LEAF_pfTablesTblPktsInXPass:
787			val->v.counter64 =
788			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
789			break;
790		case LEAF_pfTablesTblPktsOutPass:
791			val->v.counter64 =
792			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
793			break;
794		case LEAF_pfTablesTblPktsOutBlock:
795			val->v.counter64 =
796			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
797			break;
798		case LEAF_pfTablesTblPktsOutXPass:
799			val->v.counter64 =
800			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
801			break;
802
803		default:
804			return (SNMP_ERR_NOSUCHNAME);
805	}
806
807	return (SNMP_ERR_NOERROR);
808}
809
810int
811pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
812	u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
813{
814	asn_subid_t	which = val->var.subs[sub - 1];
815	struct pfa_entry *e = NULL;
816
817	if ((time(NULL) - pfa_table_age) > PFA_TABLE_MAXAGE)
818		pfa_refresh();
819
820	switch (op) {
821		case SNMP_OP_SET:
822			return (SNMP_ERR_NOT_WRITEABLE);
823		case SNMP_OP_GETNEXT:
824			if ((e = NEXT_OBJECT_INT(&pfa_table,
825			    &val->var, sub)) == NULL)
826				return (SNMP_ERR_NOSUCHNAME);
827			val->var.len = sub + 1;
828			val->var.subs[sub] = e->index;
829			break;
830		case SNMP_OP_GET:
831			if (val->var.len - sub != 1)
832				return (SNMP_ERR_NOSUCHNAME);
833			if ((e = pfa_table_find(val->var.subs[sub])) == NULL)
834				return (SNMP_ERR_NOSUCHNAME);
835			break;
836
837		case SNMP_OP_COMMIT:
838		case SNMP_OP_ROLLBACK:
839		default:
840			abort();
841	}
842
843	switch (which) {
844		case LEAF_pfTablesAddrNetType:
845			if (e->pfas.pfras_a.pfra_af == AF_INET)
846				val->v.integer = pfTablesAddrNetType_ipv4;
847			else if (e->pfas.pfras_a.pfra_af == AF_INET6)
848				val->v.integer = pfTablesAddrNetType_ipv6;
849			else
850				return (SNMP_ERR_GENERR);
851			break;
852		case LEAF_pfTablesAddrNet:
853			if (e->pfas.pfras_a.pfra_af == AF_INET) {
854				return (string_get(val,
855				    (u_char *)&e->pfas.pfras_a.pfra_ip4addr, 4));
856			} else if (e->pfas.pfras_a.pfra_af == AF_INET6)
857				return (string_get(val,
858				    (u_char *)&e->pfas.pfras_a.pfra_ip6addr, 16));
859			else
860				return (SNMP_ERR_GENERR);
861			break;
862		case LEAF_pfTablesAddrPrefix:
863			val->v.integer = (int32_t) e->pfas.pfras_a.pfra_net;
864			break;
865		case LEAF_pfTablesAddrTZero:
866			val->v.uint32 =
867			    (time(NULL) - e->pfas.pfras_tzero) * 100;
868			break;
869		case LEAF_pfTablesAddrBytesInPass:
870			val->v.counter64 =
871			    e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_PASS];
872			break;
873		case LEAF_pfTablesAddrBytesInBlock:
874			val->v.counter64 =
875			    e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
876			break;
877		case LEAF_pfTablesAddrBytesOutPass:
878			val->v.counter64 =
879			    e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_PASS];
880			break;
881		case LEAF_pfTablesAddrBytesOutBlock:
882			val->v.counter64 =
883			    e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
884			break;
885		case LEAF_pfTablesAddrPktsInPass:
886			val->v.counter64 =
887			    e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_PASS];
888			break;
889		case LEAF_pfTablesAddrPktsInBlock:
890			val->v.counter64 =
891			    e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_BLOCK];
892			break;
893		case LEAF_pfTablesAddrPktsOutPass:
894			val->v.counter64 =
895			    e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_PASS];
896			break;
897		case LEAF_pfTablesAddrPktsOutBlock:
898			val->v.counter64 =
899			    e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
900			break;
901		default:
902			return (SNMP_ERR_NOSUCHNAME);
903	}
904
905	return (SNMP_ERR_NOERROR);
906}
907
908int
909pf_altq_num(struct snmp_context __unused *ctx, struct snmp_value *val,
910	u_int sub, u_int __unused vindex, enum snmp_op op)
911{
912	asn_subid_t	which = val->var.subs[sub - 1];
913
914	if (!altq_enabled)
915	   return (SNMP_ERR_NOSUCHNAME);
916
917	if (op == SNMP_OP_SET)
918		return (SNMP_ERR_NOT_WRITEABLE);
919
920	if (op == SNMP_OP_GET) {
921		if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
922			if (pfq_refresh() == -1)
923			    return (SNMP_ERR_GENERR);
924
925		switch (which) {
926			case LEAF_pfAltqQueueNumber:
927				val->v.uint32 = pfq_table_count;
928				break;
929
930			default:
931				return (SNMP_ERR_NOSUCHNAME);
932		}
933
934		return (SNMP_ERR_NOERROR);
935	}
936
937	abort();
938	return (SNMP_ERR_GENERR);
939}
940
941int
942pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
943	u_int sub, u_int __unused vindex, enum snmp_op op)
944{
945	asn_subid_t	which = val->var.subs[sub - 1];
946	struct pfq_entry *e = NULL;
947
948	if (!altq_enabled)
949	   return (SNMP_ERR_NOSUCHNAME);
950
951	if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
952		pfq_refresh();
953
954	switch (op) {
955		case SNMP_OP_SET:
956			return (SNMP_ERR_NOT_WRITEABLE);
957		case SNMP_OP_GETNEXT:
958			if ((e = NEXT_OBJECT_INT(&pfq_table,
959			    &val->var, sub)) == NULL)
960				return (SNMP_ERR_NOSUCHNAME);
961			val->var.len = sub + 1;
962			val->var.subs[sub] = e->index;
963			break;
964		case SNMP_OP_GET:
965			if (val->var.len - sub != 1)
966				return (SNMP_ERR_NOSUCHNAME);
967			if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
968				return (SNMP_ERR_NOSUCHNAME);
969			break;
970
971		case SNMP_OP_COMMIT:
972		case SNMP_OP_ROLLBACK:
973		default:
974			abort();
975	}
976
977	switch (which) {
978		case LEAF_pfAltqQueueDescr:
979			return (string_get(val, e->altq.qname, -1));
980		case LEAF_pfAltqQueueParent:
981			return (string_get(val, e->altq.parent, -1));
982		case LEAF_pfAltqQueueScheduler:
983			val->v.integer = e->altq.scheduler;
984			break;
985		case LEAF_pfAltqQueueBandwidth:
986			val->v.uint32 = (e->altq.bandwidth > UINT_MAX) ?
987			    UINT_MAX : (u_int32_t)e->altq.bandwidth;
988			break;
989		case LEAF_pfAltqQueuePriority:
990			val->v.integer = e->altq.priority;
991			break;
992		case LEAF_pfAltqQueueLimit:
993			val->v.integer = e->altq.qlimit;
994			break;
995
996		default:
997			return (SNMP_ERR_NOSUCHNAME);
998	}
999
1000	return (SNMP_ERR_NOERROR);
1001}
1002
1003int
1004pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val,
1005	u_int sub, u_int __unused vindex, enum snmp_op op)
1006{
1007	asn_subid_t	which = val->var.subs[sub - 1];
1008
1009	if (op == SNMP_OP_SET)
1010		return (SNMP_ERR_NOT_WRITEABLE);
1011
1012	if (op == SNMP_OP_GET) {
1013		if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
1014			if (pfl_refresh() == -1)
1015				return (SNMP_ERR_GENERR);
1016
1017		switch (which) {
1018			case LEAF_pfLabelsLblNumber:
1019				val->v.uint32 = pfl_table_count;
1020				break;
1021
1022			default:
1023				return (SNMP_ERR_NOSUCHNAME);
1024		}
1025
1026		return (SNMP_ERR_NOERROR);
1027	}
1028
1029	abort();
1030	return (SNMP_ERR_GENERR);
1031}
1032
1033int
1034pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
1035	u_int sub, u_int __unused vindex, enum snmp_op op)
1036{
1037	asn_subid_t	which = val->var.subs[sub - 1];
1038	struct pfl_entry *e = NULL;
1039
1040	if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
1041		pfl_refresh();
1042
1043	switch (op) {
1044		case SNMP_OP_SET:
1045			return (SNMP_ERR_NOT_WRITEABLE);
1046		case SNMP_OP_GETNEXT:
1047			if ((e = NEXT_OBJECT_INT(&pfl_table,
1048			    &val->var, sub)) == NULL)
1049				return (SNMP_ERR_NOSUCHNAME);
1050			val->var.len = sub + 1;
1051			val->var.subs[sub] = e->index;
1052			break;
1053		case SNMP_OP_GET:
1054			if (val->var.len - sub != 1)
1055				return (SNMP_ERR_NOSUCHNAME);
1056			if ((e = pfl_table_find(val->var.subs[sub])) == NULL)
1057				return (SNMP_ERR_NOSUCHNAME);
1058			break;
1059
1060		case SNMP_OP_COMMIT:
1061		case SNMP_OP_ROLLBACK:
1062		default:
1063			abort();
1064	}
1065
1066	switch (which) {
1067		case LEAF_pfLabelsLblName:
1068			return (string_get(val, e->name, -1));
1069		case LEAF_pfLabelsLblEvals:
1070			val->v.counter64 = e->evals;
1071			break;
1072		case LEAF_pfLabelsLblBytesIn:
1073			val->v.counter64 = e->bytes[IN];
1074			break;
1075		case LEAF_pfLabelsLblBytesOut:
1076			val->v.counter64 = e->bytes[OUT];
1077			break;
1078		case LEAF_pfLabelsLblPktsIn:
1079			val->v.counter64 = e->pkts[IN];
1080			break;
1081		case LEAF_pfLabelsLblPktsOut:
1082			val->v.counter64 = e->pkts[OUT];
1083			break;
1084		default:
1085			return (SNMP_ERR_NOSUCHNAME);
1086	}
1087
1088	return (SNMP_ERR_NOERROR);
1089}
1090
1091static struct pfi_entry *
1092pfi_table_find(u_int idx)
1093{
1094	struct pfi_entry *e;
1095
1096	TAILQ_FOREACH(e, &pfi_table, link)
1097		if (e->index == idx)
1098			return (e);
1099	return (NULL);
1100}
1101
1102static struct pfq_entry *
1103pfq_table_find(u_int idx)
1104{
1105	struct pfq_entry *e;
1106
1107	TAILQ_FOREACH(e, &pfq_table, link)
1108		if (e->index == idx)
1109			return (e);
1110	return (NULL);
1111}
1112
1113static struct pft_entry *
1114pft_table_find(u_int idx)
1115{
1116	struct pft_entry *e;
1117
1118	TAILQ_FOREACH(e, &pft_table, link)
1119		if (e->index == idx)
1120			return (e);
1121	return (NULL);
1122}
1123
1124static struct pfa_entry *
1125pfa_table_find(u_int idx)
1126{
1127	struct pfa_entry *e;
1128
1129	TAILQ_FOREACH(e, &pfa_table, link)
1130		if (e->index == idx)
1131			return (e);
1132	return (NULL);
1133}
1134
1135static struct pfl_entry *
1136pfl_table_find(u_int idx)
1137{
1138	struct pfl_entry *e;
1139
1140	TAILQ_FOREACH(e, &pfl_table, link)
1141		if (e->index == idx)
1142			return (e);
1143
1144	return (NULL);
1145}
1146
1147static int
1148pfi_refresh(void)
1149{
1150	struct pfioc_iface io;
1151	struct pfi_kif *p = NULL;
1152	struct pfi_entry *e;
1153	int i, numifs = 1;
1154
1155	if (started && this_tick <= pf_tick)
1156		return (0);
1157
1158	while (!TAILQ_EMPTY(&pfi_table)) {
1159		e = TAILQ_FIRST(&pfi_table);
1160		TAILQ_REMOVE(&pfi_table, e, link);
1161		free(e);
1162	}
1163
1164	bzero(&io, sizeof(io));
1165	io.pfiio_esize = sizeof(struct pfi_kif);
1166
1167	for (;;) {
1168		p = reallocf(p, numifs * sizeof(struct pfi_kif));
1169		if (p == NULL) {
1170			syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
1171			    numifs, strerror(errno));
1172			goto err2;
1173		}
1174		io.pfiio_size = numifs;
1175		io.pfiio_buffer = p;
1176
1177		if (ioctl(pfctl_fd(pfh), DIOCIGETIFACES, &io)) {
1178			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
1179			    strerror(errno));
1180			goto err2;
1181		}
1182
1183		if (numifs >= io.pfiio_size)
1184			break;
1185
1186		numifs = io.pfiio_size;
1187	}
1188
1189	for (i = 0; i < numifs; i++) {
1190		e = malloc(sizeof(struct pfi_entry));
1191		if (e == NULL)
1192			goto err1;
1193		e->index = i + 1;
1194		memcpy(&e->pfi, p+i, sizeof(struct pfi_kif));
1195		TAILQ_INSERT_TAIL(&pfi_table, e, link);
1196	}
1197
1198	pfi_table_age = time(NULL);
1199	pfi_table_count = numifs;
1200	pf_tick = this_tick;
1201
1202	free(p);
1203	return (0);
1204
1205err1:
1206	while (!TAILQ_EMPTY(&pfi_table)) {
1207		e = TAILQ_FIRST(&pfi_table);
1208		TAILQ_REMOVE(&pfi_table, e, link);
1209		free(e);
1210	}
1211err2:
1212	free(p);
1213	return(-1);
1214}
1215
1216static int
1217pfq_refresh(void)
1218{
1219	struct pfioc_altq pa;
1220	struct pfq_entry *e;
1221	int i, numqs, ticket;
1222
1223	if (started && this_tick <= pf_tick)
1224		return (0);
1225
1226	while (!TAILQ_EMPTY(&pfq_table)) {
1227		e = TAILQ_FIRST(&pfq_table);
1228		TAILQ_REMOVE(&pfq_table, e, link);
1229		free(e);
1230	}
1231
1232	bzero(&pa, sizeof(pa));
1233	pa.version = PFIOC_ALTQ_VERSION;
1234	if (ioctl(pfctl_fd(pfh), DIOCGETALTQS, &pa)) {
1235		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
1236		    strerror(errno));
1237		return (-1);
1238	}
1239
1240	numqs = pa.nr;
1241	ticket = pa.ticket;
1242
1243	for (i = 0; i < numqs; i++) {
1244		e = malloc(sizeof(struct pfq_entry));
1245		if (e == NULL) {
1246			syslog(LOG_ERR, "pfq_refresh(): "
1247			    "malloc(): %s",
1248			    strerror(errno));
1249			goto err;
1250		}
1251		pa.ticket = ticket;
1252		pa.nr = i;
1253
1254		if (ioctl(pfctl_fd(pfh), DIOCGETALTQ, &pa)) {
1255			syslog(LOG_ERR, "pfq_refresh(): "
1256			    "ioctl(DIOCGETALTQ): %s",
1257			    strerror(errno));
1258			goto err;
1259		}
1260
1261		if (pa.altq.qid > 0) {
1262			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
1263			e->index = pa.altq.qid;
1264			pfq_table_count = i;
1265			INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index);
1266		}
1267	}
1268
1269	pfq_table_age = time(NULL);
1270	pf_tick = this_tick;
1271
1272	return (0);
1273err:
1274	free(e);
1275	while (!TAILQ_EMPTY(&pfq_table)) {
1276		e = TAILQ_FIRST(&pfq_table);
1277		TAILQ_REMOVE(&pfq_table, e, link);
1278		free(e);
1279	}
1280	return(-1);
1281}
1282
1283static int
1284pfs_refresh(void)
1285{
1286	if (started && this_tick <= pf_tick)
1287		return (0);
1288
1289	pfctl_free_status(pfs);
1290	pfs = pfctl_get_status_h(pfh);
1291
1292	if (pfs == NULL) {
1293		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1294		    strerror(errno));
1295		return (-1);
1296	}
1297
1298	pf_tick = this_tick;
1299	return (0);
1300}
1301
1302static int
1303pft_refresh(void)
1304{
1305	struct pfioc_table io;
1306	struct pfr_tstats *t = NULL;
1307	struct pft_entry *e;
1308	int i, numtbls = 1;
1309
1310	if (started && this_tick <= pf_tick)
1311		return (0);
1312
1313	while (!TAILQ_EMPTY(&pft_table)) {
1314		e = TAILQ_FIRST(&pft_table);
1315		TAILQ_REMOVE(&pft_table, e, link);
1316		free(e);
1317	}
1318
1319	bzero(&io, sizeof(io));
1320	io.pfrio_esize = sizeof(struct pfr_tstats);
1321
1322	for (;;) {
1323		t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
1324		if (t == NULL) {
1325			syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
1326			    numtbls, strerror(errno));
1327			goto err2;
1328		}
1329		io.pfrio_size = numtbls;
1330		io.pfrio_buffer = t;
1331
1332		if (ioctl(pfctl_fd(pfh), DIOCRGETTSTATS, &io)) {
1333			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1334			    strerror(errno));
1335			goto err2;
1336		}
1337
1338		if (numtbls >= io.pfrio_size)
1339			break;
1340
1341		numtbls = io.pfrio_size;
1342	}
1343
1344	for (i = 0; i < numtbls; i++) {
1345		e = malloc(sizeof(struct pft_entry));
1346		if (e == NULL)
1347			goto err1;
1348		e->index = i + 1;
1349		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1350		TAILQ_INSERT_TAIL(&pft_table, e, link);
1351	}
1352
1353	pft_table_age = time(NULL);
1354	pft_table_count = numtbls;
1355	pf_tick = this_tick;
1356
1357	free(t);
1358	return (0);
1359err1:
1360	while (!TAILQ_EMPTY(&pft_table)) {
1361		e = TAILQ_FIRST(&pft_table);
1362		TAILQ_REMOVE(&pft_table, e, link);
1363		free(e);
1364	}
1365err2:
1366	free(t);
1367	return(-1);
1368}
1369
1370static int
1371pfa_table_addrs(u_int sidx, struct pfr_table *pt)
1372{
1373	struct pfioc_table io;
1374	struct pfr_astats *t = NULL;
1375	struct pfa_entry *e;
1376	int i, numaddrs = 1;
1377
1378	if (pt == NULL)
1379		return (-1);
1380
1381	memset(&io, 0, sizeof(io));
1382	strlcpy(io.pfrio_table.pfrt_name, pt->pfrt_name,
1383	    sizeof(io.pfrio_table.pfrt_name));
1384
1385	for (;;) {
1386		t = reallocf(t, numaddrs * sizeof(struct pfr_astats));
1387		if (t == NULL) {
1388			syslog(LOG_ERR, "pfa_table_addrs(): reallocf(): %s",
1389			    strerror(errno));
1390			numaddrs = -1;
1391			goto error;
1392		}
1393
1394		memset(t, 0, sizeof(*t));
1395		io.pfrio_size = numaddrs;
1396		io.pfrio_buffer = t;
1397		io.pfrio_esize = sizeof(struct pfr_astats);
1398
1399		if (ioctl(pfctl_fd(pfh), DIOCRGETASTATS, &io)) {
1400			syslog(LOG_ERR, "pfa_table_addrs(): ioctl() on %s: %s",
1401			    pt->pfrt_name, strerror(errno));
1402			numaddrs = -1;
1403			break;
1404		}
1405
1406		if (numaddrs >= io.pfrio_size)
1407			break;
1408
1409		numaddrs = io.pfrio_size;
1410	}
1411
1412	for (i = 0; i < numaddrs; i++) {
1413		if ((t + i)->pfras_a.pfra_af != AF_INET &&
1414		    (t + i)->pfras_a.pfra_af != AF_INET6) {
1415			numaddrs = i;
1416			break;
1417		}
1418
1419		e = (struct pfa_entry *)malloc(sizeof(struct pfa_entry));
1420		if (e == NULL) {
1421			syslog(LOG_ERR, "pfa_table_addrs(): malloc(): %s",
1422			    strerror(errno));
1423			numaddrs = -1;
1424			break;
1425		}
1426		e->index = sidx + i;
1427		memcpy(&e->pfas, t + i, sizeof(struct pfr_astats));
1428		TAILQ_INSERT_TAIL(&pfa_table, e, link);
1429	}
1430
1431	free(t);
1432error:
1433	return (numaddrs);
1434}
1435
1436static int
1437pfa_refresh(void)
1438{
1439	struct pfioc_table io;
1440	struct pfr_table *pt = NULL, *it = NULL;
1441	struct pfa_entry *e;
1442	int i, numtbls = 1, cidx, naddrs;
1443
1444	if (started && this_tick <= pf_tick)
1445		return (0);
1446
1447	while (!TAILQ_EMPTY(&pfa_table)) {
1448		e = TAILQ_FIRST(&pfa_table);
1449		TAILQ_REMOVE(&pfa_table, e, link);
1450		free(e);
1451	}
1452
1453	memset(&io, 0, sizeof(io));
1454	io.pfrio_esize = sizeof(struct pfr_table);
1455
1456	for (;;) {
1457		pt = reallocf(pt, numtbls * sizeof(struct pfr_table));
1458		if (pt == NULL) {
1459			syslog(LOG_ERR, "pfa_refresh(): reallocf() %s",
1460			    strerror(errno));
1461			return (-1);
1462		}
1463		memset(pt, 0, sizeof(*pt));
1464		io.pfrio_size = numtbls;
1465		io.pfrio_buffer = pt;
1466
1467		if (ioctl(pfctl_fd(pfh), DIOCRGETTABLES, &io)) {
1468			syslog(LOG_ERR, "pfa_refresh(): ioctl(): %s",
1469			    strerror(errno));
1470			goto err2;
1471		}
1472
1473		if (numtbls >= io.pfrio_size)
1474			break;
1475
1476		numtbls = io.pfrio_size;
1477	}
1478
1479	cidx = 1;
1480
1481	for (it = pt, i = 0; i < numtbls; it++, i++) {
1482		/*
1483		 * Skip the table if not active - ioctl(DIOCRGETASTATS) will
1484		 * return ESRCH for this entry anyway.
1485		 */
1486		if (!(it->pfrt_flags & PFR_TFLAG_ACTIVE))
1487			continue;
1488
1489		if ((naddrs = pfa_table_addrs(cidx, it)) < 0)
1490			goto err1;
1491
1492		cidx += naddrs;
1493	}
1494
1495	pfa_table_age = time(NULL);
1496	pfa_table_count = cidx;
1497	pf_tick = this_tick;
1498
1499	free(pt);
1500	return (0);
1501err1:
1502	while (!TAILQ_EMPTY(&pfa_table)) {
1503		e = TAILQ_FIRST(&pfa_table);
1504		TAILQ_REMOVE(&pfa_table, e, link);
1505		free(e);
1506	}
1507
1508err2:
1509	free(pt);
1510	return (-1);
1511}
1512
1513static int
1514pfl_scan_ruleset(const char *path)
1515{
1516	struct pfctl_rules_info rules;
1517	struct pfctl_rule rule;
1518	char anchor_call[MAXPATHLEN] = "";
1519	struct pfl_entry *e;
1520	u_int32_t nr, i;
1521
1522	if (pfctl_get_rules_info_h(pfh, &rules, PF_PASS, path)) {
1523		syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s",
1524		    strerror(errno));
1525		goto err;
1526	}
1527
1528	for (nr = rules.nr, i = 0; i < nr; i++) {
1529		if (pfctl_get_rule_h(pfh, i, rules.ticket, path,
1530		    PF_PASS, &rule, anchor_call)) {
1531			syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):"
1532			    " %s", strerror(errno));
1533			goto err;
1534		}
1535
1536		if (rule.label[0]) {
1537			e = (struct pfl_entry *)malloc(sizeof(*e));
1538			if (e == NULL)
1539				goto err;
1540
1541			strlcpy(e->name, path, sizeof(e->name));
1542			if (path[0])
1543				strlcat(e->name, "/", sizeof(e->name));
1544			strlcat(e->name, rule.label[0], sizeof(e->name));
1545
1546			e->evals = rule.evaluations;
1547			e->bytes[IN] = rule.bytes[IN];
1548			e->bytes[OUT] = rule.bytes[OUT];
1549			e->pkts[IN] = rule.packets[IN];
1550			e->pkts[OUT] = rule.packets[OUT];
1551			e->index = ++pfl_table_count;
1552
1553			TAILQ_INSERT_TAIL(&pfl_table, e, link);
1554		}
1555	}
1556
1557	return (0);
1558
1559err:
1560	return (-1);
1561}
1562
1563static int
1564pfl_walk_rulesets(const char *path)
1565{
1566	struct pfioc_ruleset prs;
1567	char newpath[MAXPATHLEN];
1568	u_int32_t nr, i;
1569
1570	if (pfl_scan_ruleset(path))
1571		goto err;
1572
1573	bzero(&prs, sizeof(prs));
1574	strlcpy(prs.path, path, sizeof(prs.path));
1575	if (ioctl(pfctl_fd(pfh), DIOCGETRULESETS, &prs)) {
1576		syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s",
1577		    strerror(errno));
1578		goto err;
1579	}
1580
1581	for (nr = prs.nr, i = 0; i < nr; i++) {
1582		prs.nr = i;
1583		if (ioctl(pfctl_fd(pfh), DIOCGETRULESET, &prs)) {
1584			syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):"
1585			    " %s", strerror(errno));
1586			goto err;
1587		}
1588
1589		if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0)
1590			continue;
1591
1592		strlcpy(newpath, path, sizeof(newpath));
1593		if (path[0])
1594			strlcat(newpath, "/", sizeof(newpath));
1595
1596		strlcat(newpath, prs.name, sizeof(newpath));
1597		if (pfl_walk_rulesets(newpath))
1598			goto err;
1599	}
1600
1601	return (0);
1602
1603err:
1604	return (-1);
1605}
1606
1607static int
1608pfl_refresh(void)
1609{
1610	struct pfl_entry *e;
1611
1612	if (started && this_tick <= pf_tick)
1613		return (0);
1614
1615	while (!TAILQ_EMPTY(&pfl_table)) {
1616		e = TAILQ_FIRST(&pfl_table);
1617		TAILQ_REMOVE(&pfl_table, e, link);
1618		free(e);
1619	}
1620	pfl_table_count = 0;
1621
1622	if (pfl_walk_rulesets(""))
1623		goto err;
1624
1625	pfl_table_age = time(NULL);
1626	pf_tick = this_tick;
1627
1628	return (0);
1629
1630err:
1631	while (!TAILQ_EMPTY(&pfl_table)) {
1632		e = TAILQ_FIRST(&pfl_table);
1633		TAILQ_REMOVE(&pfl_table, e, link);
1634		free(e);
1635	}
1636	pfl_table_count = 0;
1637
1638	return (-1);
1639}
1640
1641/*
1642 * check whether altq support is enabled in kernel
1643 */
1644
1645static int
1646altq_is_enabled(int pfdev)
1647{
1648	struct pfioc_altq pa;
1649
1650	errno = 0;
1651	pa.version = PFIOC_ALTQ_VERSION;
1652	if (ioctl(pfdev, DIOCGETALTQS, &pa)) {
1653		if (errno == ENODEV) {
1654			syslog(LOG_INFO, "No ALTQ support in kernel\n"
1655			    "ALTQ related functions disabled\n");
1656			return (0);
1657		} else {
1658			syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s",
1659			    strerror(errno));
1660			return (-1);
1661		}
1662	}
1663	return (1);
1664}
1665
1666/*
1667 * Implement the bsnmpd module interface
1668 */
1669static int
1670pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1671{
1672	module = mod;
1673
1674	if ((pfh = pfctl_open(PF_DEVICE)) == NULL) {
1675		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1676		    strerror(errno));
1677		return (-1);
1678	}
1679
1680	if ((altq_enabled = altq_is_enabled(pfctl_fd(pfh))) == -1) {
1681		syslog(LOG_ERR, "pf_init(): altq test failed");
1682		return (-1);
1683	}
1684
1685	/* Prepare internal state */
1686	TAILQ_INIT(&pfi_table);
1687	TAILQ_INIT(&pfq_table);
1688	TAILQ_INIT(&pft_table);
1689	TAILQ_INIT(&pfa_table);
1690	TAILQ_INIT(&pfl_table);
1691
1692	pfi_refresh();
1693	if (altq_enabled) {
1694		pfq_refresh();
1695	}
1696
1697	pfs_refresh();
1698	pft_refresh();
1699	pfa_refresh();
1700	pfl_refresh();
1701
1702	started = 1;
1703
1704	return (0);
1705}
1706
1707static int
1708pf_fini(void)
1709{
1710	struct pfi_entry *i1, *i2;
1711	struct pfq_entry *q1, *q2;
1712	struct pft_entry *t1, *t2;
1713	struct pfa_entry *a1, *a2;
1714	struct pfl_entry *l1, *l2;
1715
1716	/* Empty the list of interfaces */
1717	i1 = TAILQ_FIRST(&pfi_table);
1718	while (i1 != NULL) {
1719		i2 = TAILQ_NEXT(i1, link);
1720		free(i1);
1721		i1 = i2;
1722	}
1723
1724	/* List of queues */
1725	q1 = TAILQ_FIRST(&pfq_table);
1726	while (q1 != NULL) {
1727		q2 = TAILQ_NEXT(q1, link);
1728		free(q1);
1729		q1 = q2;
1730	}
1731
1732	/* List of tables */
1733	t1 = TAILQ_FIRST(&pft_table);
1734	while (t1 != NULL) {
1735		t2 = TAILQ_NEXT(t1, link);
1736		free(t1);
1737		t1 = t2;
1738	}
1739
1740	/* List of table addresses */
1741	a1 = TAILQ_FIRST(&pfa_table);
1742	while (a1 != NULL) {
1743		a2 = TAILQ_NEXT(a1, link);
1744		free(a1);
1745		a1 = a2;
1746	}
1747
1748	/* And the list of labeled filter rules */
1749	l1 = TAILQ_FIRST(&pfl_table);
1750	while (l1 != NULL) {
1751		l2 = TAILQ_NEXT(l1, link);
1752		free(l1);
1753		l1 = l2;
1754	}
1755
1756	pfctl_free_status(pfs);
1757	pfs = NULL;
1758
1759	pfctl_close(pfh);
1760
1761	return (0);
1762}
1763
1764static void
1765pf_dump(void)
1766{
1767	pfi_refresh();
1768	if (altq_enabled) {
1769		pfq_refresh();
1770	}
1771	pft_refresh();
1772	pfa_refresh();
1773	pfl_refresh();
1774
1775	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1776	    (intmax_t)pfi_table_age);
1777	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1778	    pfi_table_count);
1779
1780	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1781	    (intmax_t)pfq_table_age);
1782	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1783	    pfq_table_count);
1784
1785	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1786	    (intmax_t)pft_table_age);
1787	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1788	    pft_table_count);
1789
1790	syslog(LOG_ERR, "Dump: pfa_table_age = %jd",
1791	    (intmax_t)pfa_table_age);
1792	syslog(LOG_ERR, "Dump: pfa_table_count = %d",
1793	    pfa_table_count);
1794
1795	syslog(LOG_ERR, "Dump: pfl_table_age = %jd",
1796	    (intmax_t)pfl_table_age);
1797	syslog(LOG_ERR, "Dump: pfl_table_count = %d",
1798	    pfl_table_count);
1799}
1800
1801const struct snmp_module config = {
1802	.comment = "This module implements a MIB for the pf packet filter.",
1803	.init =		pf_init,
1804	.fini =		pf_fini,
1805	.tree =		pf_ctree,
1806	.dump =		pf_dump,
1807	.tree_size =	pf_CTREE_SIZE,
1808};
1809