xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 30d239bc4c510432e65a84fa1c14ed67a3ab1c92)
1 /*-
2  * Copyright (c) 2005 Philip Paeps <philip@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <bsnmp/snmpmod.h>
30 
31 #include <net/pfvar.h>
32 #include <sys/ioctl.h>
33 
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <syslog.h>
41 #include <unistd.h>
42 
43 #include "pf_oid.h"
44 #include "pf_tree.h"
45 
46 struct lmodule *module;
47 
48 static int dev = -1;
49 static int started;
50 static uint64_t pf_tick;
51 
52 static struct pf_status pfs;
53 
54 enum { IN, OUT };
55 enum { IPV4, IPV6 };
56 enum { PASS, BLOCK };
57 
58 #define PFI_IFTYPE_GROUP	0
59 #define PFI_IFTYPE_INSTANCE	1
60 #define PFI_IFTYPE_DETACHED	2
61 
62 struct pfi_entry {
63 	struct pfi_kif	pfi;
64 	u_int		index;
65 	TAILQ_ENTRY(pfi_entry) link;
66 };
67 TAILQ_HEAD(pfi_table, pfi_entry);
68 
69 static struct pfi_table pfi_table;
70 static time_t pfi_table_age;
71 static int pfi_table_count;
72 
73 #define PFI_TABLE_MAXAGE	5
74 
75 struct pft_entry {
76 	struct pfr_tstats pft;
77 	u_int		index;
78 	TAILQ_ENTRY(pft_entry) link;
79 };
80 TAILQ_HEAD(pft_table, pft_entry);
81 
82 static struct pft_table pft_table;
83 static time_t pft_table_age;
84 static int pft_table_count;
85 
86 #define PFT_TABLE_MAXAGE	5
87 
88 struct pfq_entry {
89 	struct pf_altq	altq;
90 	u_int		index;
91 	TAILQ_ENTRY(pfq_entry) link;
92 };
93 TAILQ_HEAD(pfq_table, pfq_entry);
94 
95 static struct pfq_table pfq_table;
96 static time_t pfq_table_age;
97 static int pfq_table_count;
98 
99 static int altq_enabled = 0;
100 
101 #define PFQ_TABLE_MAXAGE	5
102 
103 /* Forward declarations */
104 static int pfi_refresh(void);
105 static int pfq_refresh(void);
106 static int pfs_refresh(void);
107 static int pft_refresh(void);
108 static struct pfi_entry * pfi_table_find(u_int idx);
109 static struct pfq_entry * pfq_table_find(u_int idx);
110 static struct pft_entry * pft_table_find(u_int idx);
111 
112 static int altq_is_enabled(int pfdevice);
113 
114 int
115 pf_status(struct snmp_context __unused *ctx, struct snmp_value *val,
116 	u_int sub, u_int __unused vindex, enum snmp_op op)
117 {
118 	asn_subid_t	which = val->var.subs[sub - 1];
119 	time_t		runtime;
120 	unsigned char	str[128];
121 
122 	if (op == SNMP_OP_SET)
123 		return (SNMP_ERR_NOT_WRITEABLE);
124 
125 	if (op == SNMP_OP_GET) {
126 		if (pfs_refresh() == -1)
127 			return (SNMP_ERR_GENERR);
128 
129 		switch (which) {
130 			case LEAF_pfStatusRunning:
131 			    val->v.uint32 = pfs.running;
132 			    break;
133 			case LEAF_pfStatusRuntime:
134 			    runtime = (pfs.since > 0) ?
135 				time(NULL) - pfs.since : 0;
136 			    val->v.uint32 = runtime * 100;
137 			    break;
138 			case LEAF_pfStatusDebug:
139 			    val->v.uint32 = pfs.debug;
140 			    break;
141 			case LEAF_pfStatusHostId:
142 			    sprintf(str, "0x%08x", ntohl(pfs.hostid));
143 			    return (string_get(val, str, strlen(str)));
144 
145 			default:
146 			    return (SNMP_ERR_NOSUCHNAME);
147 		}
148 
149 		return (SNMP_ERR_NOERROR);
150 	}
151 
152 	abort();
153 }
154 
155 int
156 pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val,
157 	u_int sub, u_int __unused vindex, enum snmp_op op)
158 {
159 	asn_subid_t	which = val->var.subs[sub - 1];
160 
161 	if (op == SNMP_OP_SET)
162 		return (SNMP_ERR_NOT_WRITEABLE);
163 
164 	if (op == SNMP_OP_GET) {
165 		if (pfs_refresh() == -1)
166 			return (SNMP_ERR_GENERR);
167 
168 		switch (which) {
169 			case LEAF_pfCounterMatch:
170 				val->v.counter64 = pfs.counters[PFRES_MATCH];
171 				break;
172 			case LEAF_pfCounterBadOffset:
173 				val->v.counter64 = pfs.counters[PFRES_BADOFF];
174 				break;
175 			case LEAF_pfCounterFragment:
176 				val->v.counter64 = pfs.counters[PFRES_FRAG];
177 				break;
178 			case LEAF_pfCounterShort:
179 				val->v.counter64 = pfs.counters[PFRES_SHORT];
180 				break;
181 			case LEAF_pfCounterNormalize:
182 				val->v.counter64 = pfs.counters[PFRES_NORM];
183 				break;
184 			case LEAF_pfCounterMemDrop:
185 				val->v.counter64 = pfs.counters[PFRES_MEMORY];
186 				break;
187 
188 			default:
189 				return (SNMP_ERR_NOSUCHNAME);
190 		}
191 
192 		return (SNMP_ERR_NOERROR);
193 	}
194 
195 	abort();
196 }
197 
198 int
199 pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val,
200 	u_int sub, u_int __unused vindex, enum snmp_op op)
201 {
202 	asn_subid_t	which = val->var.subs[sub - 1];
203 
204 	if (op == SNMP_OP_SET)
205 		return (SNMP_ERR_NOT_WRITEABLE);
206 
207 	if (op == SNMP_OP_GET) {
208 		if (pfs_refresh() == -1)
209 			return (SNMP_ERR_GENERR);
210 
211 		switch (which) {
212 			case LEAF_pfStateTableCount:
213 				val->v.uint32 = pfs.states;
214 				break;
215 			case LEAF_pfStateTableSearches:
216 				val->v.counter64 =
217 				    pfs.fcounters[FCNT_STATE_SEARCH];
218 				break;
219 			case LEAF_pfStateTableInserts:
220 				val->v.counter64 =
221 				    pfs.fcounters[FCNT_STATE_INSERT];
222 				break;
223 			case LEAF_pfStateTableRemovals:
224 				val->v.counter64 =
225 				    pfs.fcounters[FCNT_STATE_REMOVALS];
226 				break;
227 
228 			default:
229 				return (SNMP_ERR_NOSUCHNAME);
230 		}
231 
232 		return (SNMP_ERR_NOERROR);
233 	}
234 
235 	abort();
236 }
237 
238 int
239 pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val,
240 	u_int sub, u_int __unused vindex, enum snmp_op op)
241 {
242 	asn_subid_t	which = val->var.subs[sub - 1];
243 
244 	if (op == SNMP_OP_SET)
245 		return (SNMP_ERR_NOT_WRITEABLE);
246 
247 	if (op == SNMP_OP_GET) {
248 		if (pfs_refresh() == -1)
249 			return (SNMP_ERR_GENERR);
250 
251 		switch (which) {
252 			case LEAF_pfSrcNodesCount:
253 				val->v.uint32 = pfs.src_nodes;
254 				break;
255 			case LEAF_pfSrcNodesSearches:
256 				val->v.counter64 =
257 				    pfs.scounters[SCNT_SRC_NODE_SEARCH];
258 				break;
259 			case LEAF_pfSrcNodesInserts:
260 				val->v.counter64 =
261 				    pfs.scounters[SCNT_SRC_NODE_INSERT];
262 				break;
263 			case LEAF_pfSrcNodesRemovals:
264 				val->v.counter64 =
265 				    pfs.scounters[SCNT_SRC_NODE_REMOVALS];
266 				break;
267 
268 			default:
269 				return (SNMP_ERR_NOSUCHNAME);
270 		}
271 
272 		return (SNMP_ERR_NOERROR);
273 	}
274 
275 	abort();
276 }
277 
278 int
279 pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val,
280 	u_int sub, u_int __unused vindex, enum snmp_op op)
281 {
282 	asn_subid_t		which = val->var.subs[sub - 1];
283 	struct pfioc_limit	pl;
284 
285 	if (op == SNMP_OP_SET)
286 		return (SNMP_ERR_NOT_WRITEABLE);
287 
288 	if (op == SNMP_OP_GET) {
289 		bzero(&pl, sizeof(struct pfioc_limit));
290 
291 		switch (which) {
292 			case LEAF_pfLimitsStates:
293 				pl.index = PF_LIMIT_STATES;
294 				break;
295 			case LEAF_pfLimitsSrcNodes:
296 				pl.index = PF_LIMIT_SRC_NODES;
297 				break;
298 			case LEAF_pfLimitsFrags:
299 				pl.index = PF_LIMIT_FRAGS;
300 				break;
301 
302 			default:
303 				return (SNMP_ERR_NOSUCHNAME);
304 		}
305 
306 		if (ioctl(dev, DIOCGETLIMIT, &pl)) {
307 			syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
308 			    strerror(errno));
309 			return (SNMP_ERR_GENERR);
310 		}
311 
312 		val->v.uint32 = pl.limit;
313 
314 		return (SNMP_ERR_NOERROR);
315 	}
316 
317 	abort();
318 }
319 
320 int
321 pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val,
322 	u_int sub, u_int __unused vindex, enum snmp_op op)
323 {
324 	asn_subid_t	which = val->var.subs[sub - 1];
325 	struct pfioc_tm	pt;
326 
327 	if (op == SNMP_OP_SET)
328 		return (SNMP_ERR_NOT_WRITEABLE);
329 
330 	if (op == SNMP_OP_GET) {
331 		bzero(&pt, sizeof(struct pfioc_tm));
332 
333 		switch (which) {
334 			case LEAF_pfTimeoutsTcpFirst:
335 				pt.timeout = PFTM_TCP_FIRST_PACKET;
336 				break;
337 			case LEAF_pfTimeoutsTcpOpening:
338 				pt.timeout = PFTM_TCP_OPENING;
339 				break;
340 			case LEAF_pfTimeoutsTcpEstablished:
341 				pt.timeout = PFTM_TCP_ESTABLISHED;
342 				break;
343 			case LEAF_pfTimeoutsTcpClosing:
344 				pt.timeout = PFTM_TCP_CLOSING;
345 				break;
346 			case LEAF_pfTimeoutsTcpFinWait:
347 				pt.timeout = PFTM_TCP_FIN_WAIT;
348 				break;
349 			case LEAF_pfTimeoutsTcpClosed:
350 				pt.timeout = PFTM_TCP_CLOSED;
351 				break;
352 			case LEAF_pfTimeoutsUdpFirst:
353 				pt.timeout = PFTM_UDP_FIRST_PACKET;
354 				break;
355 			case LEAF_pfTimeoutsUdpSingle:
356 				pt.timeout = PFTM_UDP_SINGLE;
357 				break;
358 			case LEAF_pfTimeoutsUdpMultiple:
359 				pt.timeout = PFTM_UDP_MULTIPLE;
360 				break;
361 			case LEAF_pfTimeoutsIcmpFirst:
362 				pt.timeout = PFTM_ICMP_FIRST_PACKET;
363 				break;
364 			case LEAF_pfTimeoutsIcmpError:
365 				pt.timeout = PFTM_ICMP_ERROR_REPLY;
366 				break;
367 			case LEAF_pfTimeoutsOtherFirst:
368 				pt.timeout = PFTM_OTHER_FIRST_PACKET;
369 				break;
370 			case LEAF_pfTimeoutsOtherSingle:
371 				pt.timeout = PFTM_OTHER_SINGLE;
372 				break;
373 			case LEAF_pfTimeoutsOtherMultiple:
374 				pt.timeout = PFTM_OTHER_MULTIPLE;
375 				break;
376 			case LEAF_pfTimeoutsFragment:
377 				pt.timeout = PFTM_FRAG;
378 				break;
379 			case LEAF_pfTimeoutsInterval:
380 				pt.timeout = PFTM_INTERVAL;
381 				break;
382 			case LEAF_pfTimeoutsAdaptiveStart:
383 				pt.timeout = PFTM_ADAPTIVE_START;
384 				break;
385 			case LEAF_pfTimeoutsAdaptiveEnd:
386 				pt.timeout = PFTM_ADAPTIVE_END;
387 				break;
388 			case LEAF_pfTimeoutsSrcNode:
389 				pt.timeout = PFTM_SRC_NODE;
390 				break;
391 
392 			default:
393 				return (SNMP_ERR_NOSUCHNAME);
394 		}
395 
396 		if (ioctl(dev, DIOCGETTIMEOUT, &pt)) {
397 			syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s",
398 			    strerror(errno));
399 			return (SNMP_ERR_GENERR);
400 		}
401 
402 		val->v.integer = pt.seconds;
403 
404 		return (SNMP_ERR_NOERROR);
405 	}
406 
407 	abort();
408 }
409 
410 int
411 pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val,
412 	u_int sub, u_int __unused vindex, enum snmp_op op)
413 {
414 	asn_subid_t	which = val->var.subs[sub - 1];
415 	unsigned char	str[IFNAMSIZ];
416 
417 	if (op == SNMP_OP_SET)
418 		return (SNMP_ERR_NOT_WRITEABLE);
419 
420 	if (op == SNMP_OP_GET) {
421 		if (pfs_refresh() == -1)
422 			return (SNMP_ERR_GENERR);
423 
424 		switch (which) {
425 	 		case LEAF_pfLogInterfaceName:
426 				strlcpy(str, pfs.ifname, sizeof str);
427 				return (string_get(val, str, strlen(str)));
428 			case LEAF_pfLogInterfaceIp4BytesIn:
429 				val->v.counter64 = pfs.bcounters[IPV4][IN];
430 				break;
431 			case LEAF_pfLogInterfaceIp4BytesOut:
432 				val->v.counter64 = pfs.bcounters[IPV4][OUT];
433 				break;
434 			case LEAF_pfLogInterfaceIp4PktsInPass:
435 				val->v.counter64 =
436 				    pfs.pcounters[IPV4][IN][PF_PASS];
437 				break;
438 			case LEAF_pfLogInterfaceIp4PktsInDrop:
439 				val->v.counter64 =
440 				    pfs.pcounters[IPV4][IN][PF_DROP];
441 				break;
442 			case LEAF_pfLogInterfaceIp4PktsOutPass:
443 				val->v.counter64 =
444 				    pfs.pcounters[IPV4][OUT][PF_PASS];
445 				break;
446 			case LEAF_pfLogInterfaceIp4PktsOutDrop:
447 				val->v.counter64 =
448 				    pfs.pcounters[IPV4][OUT][PF_DROP];
449 				break;
450 			case LEAF_pfLogInterfaceIp6BytesIn:
451 				val->v.counter64 = pfs.bcounters[IPV6][IN];
452 				break;
453 			case LEAF_pfLogInterfaceIp6BytesOut:
454 				val->v.counter64 = pfs.bcounters[IPV6][OUT];
455 				break;
456 			case LEAF_pfLogInterfaceIp6PktsInPass:
457 				val->v.counter64 =
458 				    pfs.pcounters[IPV6][IN][PF_PASS];
459 				break;
460 			case LEAF_pfLogInterfaceIp6PktsInDrop:
461 				val->v.counter64 =
462 				    pfs.pcounters[IPV6][IN][PF_DROP];
463 				break;
464 			case LEAF_pfLogInterfaceIp6PktsOutPass:
465 				val->v.counter64 =
466 				    pfs.pcounters[IPV6][OUT][PF_PASS];
467 				break;
468 			case LEAF_pfLogInterfaceIp6PktsOutDrop:
469 				val->v.counter64 =
470 				    pfs.pcounters[IPV6][OUT][PF_DROP];
471 				break;
472 
473 			default:
474 				return (SNMP_ERR_NOSUCHNAME);
475 		}
476 
477 		return (SNMP_ERR_NOERROR);
478 	}
479 
480 	abort();
481 }
482 
483 int
484 pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val,
485 	u_int sub, u_int __unused vindex, enum snmp_op op)
486 {
487 	asn_subid_t	which = val->var.subs[sub - 1];
488 
489 	if (op == SNMP_OP_SET)
490 		return (SNMP_ERR_NOT_WRITEABLE);
491 
492 	if (op == SNMP_OP_GET) {
493 		if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
494 			if (pfi_refresh() == -1)
495 			    return (SNMP_ERR_GENERR);
496 
497 		switch (which) {
498 			case LEAF_pfInterfacesIfNumber:
499 				val->v.uint32 = pfi_table_count;
500 				break;
501 
502 			default:
503 				return (SNMP_ERR_NOSUCHNAME);
504 		}
505 
506 		return (SNMP_ERR_NOERROR);
507 	}
508 
509 	abort();
510 }
511 
512 int
513 pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val,
514 	u_int sub, u_int __unused vindex, enum snmp_op op)
515 {
516 	asn_subid_t	which = val->var.subs[sub - 1];
517 	struct pfi_entry *e = NULL;
518 
519 	switch (op) {
520 		case SNMP_OP_SET:
521 			return (SNMP_ERR_NOT_WRITEABLE);
522 		case SNMP_OP_GETNEXT:
523 			if ((e = NEXT_OBJECT_INT(&pfi_table,
524 			    &val->var, sub)) == NULL)
525 				return (SNMP_ERR_NOSUCHNAME);
526 			val->var.len = sub + 1;
527 			val->var.subs[sub] = e->index;
528 			break;
529 		case SNMP_OP_GET:
530 			if (val->var.len - sub != 1)
531 				return (SNMP_ERR_NOSUCHNAME);
532 			if ((e = pfi_table_find(val->var.subs[sub])) == NULL)
533 				return (SNMP_ERR_NOSUCHNAME);
534 			break;
535 
536 		case SNMP_OP_COMMIT:
537 		case SNMP_OP_ROLLBACK:
538 		default:
539 			abort();
540 	}
541 
542 	if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
543 		pfi_refresh();
544 
545 	switch (which) {
546 		case LEAF_pfInterfacesIfDescr:
547 			return (string_get(val, e->pfi.pfik_name, -1));
548 		case LEAF_pfInterfacesIfType:
549 			val->v.integer = PFI_IFTYPE_INSTANCE;
550 			break;
551 		case LEAF_pfInterfacesIfTZero:
552 			val->v.uint32 =
553 			    (time(NULL) - e->pfi.pfik_tzero) * 100;
554 			break;
555 		case LEAF_pfInterfacesIfRefsState:
556 			val->v.uint32 = e->pfi.pfik_states;
557 			break;
558 		case LEAF_pfInterfacesIfRefsRule:
559 			val->v.uint32 = e->pfi.pfik_rules;
560 			break;
561 		case LEAF_pfInterfacesIf4BytesInPass:
562 			val->v.counter64 =
563 			    e->pfi.pfik_bytes[IPV4][IN][PASS];
564 			break;
565 		case LEAF_pfInterfacesIf4BytesInBlock:
566 			val->v.counter64 =
567 			    e->pfi.pfik_bytes[IPV4][IN][BLOCK];
568 			break;
569 		case LEAF_pfInterfacesIf4BytesOutPass:
570 			val->v.counter64 =
571 			    e->pfi.pfik_bytes[IPV4][OUT][PASS];
572 			break;
573 		case LEAF_pfInterfacesIf4BytesOutBlock:
574 			val->v.counter64 =
575 			    e->pfi.pfik_bytes[IPV4][OUT][BLOCK];
576 			break;
577 		case LEAF_pfInterfacesIf4PktsInPass:
578 			val->v.counter64 =
579 			    e->pfi.pfik_packets[IPV4][IN][PASS];
580 			break;
581 		case LEAF_pfInterfacesIf4PktsInBlock:
582 			val->v.counter64 =
583 			    e->pfi.pfik_packets[IPV4][IN][BLOCK];
584 			break;
585 		case LEAF_pfInterfacesIf4PktsOutPass:
586 			val->v.counter64 =
587 			    e->pfi.pfik_packets[IPV4][OUT][PASS];
588 			break;
589 		case LEAF_pfInterfacesIf4PktsOutBlock:
590 			val->v.counter64 =
591 			    e->pfi.pfik_packets[IPV4][OUT][BLOCK];
592 			break;
593 		case LEAF_pfInterfacesIf6BytesInPass:
594 			val->v.counter64 =
595 			    e->pfi.pfik_bytes[IPV6][IN][PASS];
596 			break;
597 		case LEAF_pfInterfacesIf6BytesInBlock:
598 			val->v.counter64 =
599 			    e->pfi.pfik_bytes[IPV6][IN][BLOCK];
600 			break;
601 		case LEAF_pfInterfacesIf6BytesOutPass:
602 			val->v.counter64 =
603 			    e->pfi.pfik_bytes[IPV6][OUT][PASS];
604 			break;
605 		case LEAF_pfInterfacesIf6BytesOutBlock:
606 			val->v.counter64 =
607 			    e->pfi.pfik_bytes[IPV6][OUT][BLOCK];
608 			break;
609 		case LEAF_pfInterfacesIf6PktsInPass:
610 			val->v.counter64 =
611 			    e->pfi.pfik_packets[IPV6][IN][PASS];
612 			break;
613 		case LEAF_pfInterfacesIf6PktsInBlock:
614 			val->v.counter64 =
615 			    e->pfi.pfik_packets[IPV6][IN][BLOCK];
616 			break;
617 		case LEAF_pfInterfacesIf6PktsOutPass:
618 			val->v.counter64 =
619 			    e->pfi.pfik_packets[IPV6][OUT][PASS];
620 			break;
621 		case LEAF_pfInterfacesIf6PktsOutBlock:
622 			val->v.counter64 =
623 			    e->pfi.pfik_packets[IPV6][OUT][BLOCK];
624 			break;
625 
626 		default:
627 			return (SNMP_ERR_NOSUCHNAME);
628 	}
629 
630 	return (SNMP_ERR_NOERROR);
631 }
632 
633 int
634 pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
635 	u_int sub, u_int __unused vindex, enum snmp_op op)
636 {
637 	asn_subid_t	which = val->var.subs[sub - 1];
638 
639 	if (op == SNMP_OP_SET)
640 		return (SNMP_ERR_NOT_WRITEABLE);
641 
642 	if (op == SNMP_OP_GET) {
643 		if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
644 			if (pft_refresh() == -1)
645 			    return (SNMP_ERR_GENERR);
646 
647 		switch (which) {
648 			case LEAF_pfTablesTblNumber:
649 				val->v.uint32 = pft_table_count;
650 				break;
651 
652 			default:
653 				return (SNMP_ERR_NOSUCHNAME);
654 		}
655 
656 		return (SNMP_ERR_NOERROR);
657 	}
658 
659 	abort();
660 }
661 
662 int
663 pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
664 	u_int sub, u_int __unused vindex, enum snmp_op op)
665 {
666 	asn_subid_t	which = val->var.subs[sub - 1];
667 	struct pft_entry *e = NULL;
668 
669 	switch (op) {
670 		case SNMP_OP_SET:
671 			return (SNMP_ERR_NOT_WRITEABLE);
672 		case SNMP_OP_GETNEXT:
673 			if ((e = NEXT_OBJECT_INT(&pft_table,
674 			    &val->var, sub)) == NULL)
675 				return (SNMP_ERR_NOSUCHNAME);
676 			val->var.len = sub + 1;
677 			val->var.subs[sub] = e->index;
678 			break;
679 		case SNMP_OP_GET:
680 			if (val->var.len - sub != 1)
681 				return (SNMP_ERR_NOSUCHNAME);
682 			if ((e = pft_table_find(val->var.subs[sub])) == NULL)
683 				return (SNMP_ERR_NOSUCHNAME);
684 			break;
685 
686 		case SNMP_OP_COMMIT:
687 		case SNMP_OP_ROLLBACK:
688 		default:
689 			abort();
690 	}
691 
692 	if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
693 		pft_refresh();
694 
695 	switch (which) {
696 		case LEAF_pfTablesTblDescr:
697 			return (string_get(val, e->pft.pfrts_name, -1));
698 		case LEAF_pfTablesTblCount:
699 			val->v.integer = e->pft.pfrts_cnt;
700 			break;
701 		case LEAF_pfTablesTblTZero:
702 			val->v.uint32 =
703 			    (time(NULL) - e->pft.pfrts_tzero) * 100;
704 			break;
705 		case LEAF_pfTablesTblRefsAnchor:
706 			val->v.integer =
707 			    e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
708 			break;
709 		case LEAF_pfTablesTblRefsRule:
710 			val->v.integer =
711 			    e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
712 			break;
713 		case LEAF_pfTablesTblEvalMatch:
714 			val->v.counter64 = e->pft.pfrts_match;
715 			break;
716 		case LEAF_pfTablesTblEvalNoMatch:
717 			val->v.counter64 = e->pft.pfrts_nomatch;
718 			break;
719 		case LEAF_pfTablesTblBytesInPass:
720 			val->v.counter64 =
721 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
722 			break;
723 		case LEAF_pfTablesTblBytesInBlock:
724 			val->v.counter64 =
725 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
726 			break;
727 		case LEAF_pfTablesTblBytesInXPass:
728 			val->v.counter64 =
729 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
730 			break;
731 		case LEAF_pfTablesTblBytesOutPass:
732 			val->v.counter64 =
733 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
734 			break;
735 		case LEAF_pfTablesTblBytesOutBlock:
736 			val->v.counter64 =
737 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
738 			break;
739 		case LEAF_pfTablesTblBytesOutXPass:
740 			val->v.counter64 =
741 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
742 			break;
743 		case LEAF_pfTablesTblPktsInPass:
744 			val->v.counter64 =
745 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
746 			break;
747 		case LEAF_pfTablesTblPktsInBlock:
748 			val->v.counter64 =
749 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
750 			break;
751 		case LEAF_pfTablesTblPktsInXPass:
752 			val->v.counter64 =
753 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
754 			break;
755 		case LEAF_pfTablesTblPktsOutPass:
756 			val->v.counter64 =
757 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
758 			break;
759 		case LEAF_pfTablesTblPktsOutBlock:
760 			val->v.counter64 =
761 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
762 			break;
763 		case LEAF_pfTablesTblPktsOutXPass:
764 			val->v.counter64 =
765 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
766 			break;
767 
768 		default:
769 			return (SNMP_ERR_NOSUCHNAME);
770 	}
771 
772 	return (SNMP_ERR_NOERROR);
773 }
774 
775 int
776 pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
777 	u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
778 {
779 	return (SNMP_ERR_GENERR);
780 }
781 
782 int
783 pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val,
784 	u_int sub, u_int __unused vindex, enum snmp_op op)
785 {
786 	asn_subid_t	which = val->var.subs[sub - 1];
787 
788 	if (!altq_enabled) {
789 	   return (SNMP_ERR_NOERROR);
790 	}
791 
792 	if (op == SNMP_OP_SET)
793 		return (SNMP_ERR_NOT_WRITEABLE);
794 
795 	if (op == SNMP_OP_GET) {
796 		if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
797 			if (pfq_refresh() == -1)
798 			    return (SNMP_ERR_GENERR);
799 
800 		switch (which) {
801 			case LEAF_pfAltqQueueNumber:
802 				val->v.uint32 = pfq_table_count;
803 				break;
804 
805 			default:
806 				return (SNMP_ERR_NOSUCHNAME);
807 		}
808 
809 		return (SNMP_ERR_NOERROR);
810 	}
811 
812 	abort();
813 	return (SNMP_ERR_GENERR);
814 }
815 
816 int
817 pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
818 	u_int sub, u_int __unused vindex, enum snmp_op op)
819 {
820 	asn_subid_t	which = val->var.subs[sub - 1];
821 	struct pfq_entry *e = NULL;
822 
823 	if (!altq_enabled) {
824 	   return (SNMP_ERR_NOERROR);
825 	}
826 
827 	switch (op) {
828 		case SNMP_OP_SET:
829 			return (SNMP_ERR_NOT_WRITEABLE);
830 		case SNMP_OP_GETNEXT:
831 			if ((e = NEXT_OBJECT_INT(&pfq_table,
832 			    &val->var, sub)) == NULL)
833 				return (SNMP_ERR_NOSUCHNAME);
834 			val->var.len = sub + 1;
835 			val->var.subs[sub] = e->index;
836 			break;
837 		case SNMP_OP_GET:
838 			if (val->var.len - sub != 1)
839 				return (SNMP_ERR_NOSUCHNAME);
840 			if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
841 				return (SNMP_ERR_NOSUCHNAME);
842 			break;
843 
844 		case SNMP_OP_COMMIT:
845 		case SNMP_OP_ROLLBACK:
846 		default:
847 			abort();
848 	}
849 
850 	if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
851 		pfq_refresh();
852 
853 	switch (which) {
854 		case LEAF_pfAltqQueueDescr:
855 			return (string_get(val, e->altq.qname, -1));
856 		case LEAF_pfAltqQueueParent:
857 			return (string_get(val, e->altq.parent, -1));
858 		case LEAF_pfAltqQueueScheduler:
859 			val->v.integer = e->altq.scheduler;
860 			break;
861 		case LEAF_pfAltqQueueBandwidth:
862 			val->v.uint32 = e->altq.bandwidth;
863 			break;
864 		case LEAF_pfAltqQueuePriority:
865 			val->v.integer = e->altq.priority;
866 			break;
867 		case LEAF_pfAltqQueueLimit:
868 			val->v.integer = e->altq.qlimit;
869 			break;
870 
871 		default:
872 			return (SNMP_ERR_NOSUCHNAME);
873 	}
874 
875 	return (SNMP_ERR_NOERROR);
876 }
877 
878 static struct pfi_entry *
879 pfi_table_find(u_int idx)
880 {
881 	struct pfi_entry *e;
882 
883 	TAILQ_FOREACH(e, &pfi_table, link)
884 		if (e->index == idx)
885 			return (e);
886 	return (NULL);
887 }
888 
889 static struct pfq_entry *
890 pfq_table_find(u_int idx)
891 {
892 	struct pfq_entry *e;
893 	TAILQ_FOREACH(e, &pfq_table, link)
894 		if (e->index == idx)
895 			return (e);
896 	return (NULL);
897 }
898 
899 static struct pft_entry *
900 pft_table_find(u_int idx)
901 {
902 	struct pft_entry *e;
903 
904 	TAILQ_FOREACH(e, &pft_table, link)
905 		if (e->index == idx)
906 			return (e);
907 	return (NULL);
908 }
909 
910 static int
911 pfi_refresh(void)
912 {
913 	struct pfioc_iface io;
914 	struct pfi_kif *p = NULL;
915 	struct pfi_entry *e;
916 	int i, numifs = 1;
917 
918 	if (started && this_tick <= pf_tick)
919 		return (0);
920 
921 	while (!TAILQ_EMPTY(&pfi_table)) {
922 		e = TAILQ_FIRST(&pfi_table);
923 		TAILQ_REMOVE(&pfi_table, e, link);
924 		free(e);
925 	}
926 
927 	bzero(&io, sizeof(io));
928 	io.pfiio_esize = sizeof(struct pfi_kif);
929 
930 	for (;;) {
931 		p = reallocf(p, numifs * sizeof(struct pfi_kif));
932 		if (p == NULL) {
933 			syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
934 			    numifs, strerror(errno));
935 			goto err2;
936 		}
937 		io.pfiio_size = numifs;
938 		io.pfiio_buffer = p;
939 
940 		if (ioctl(dev, DIOCIGETIFACES, &io)) {
941 			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
942 			    strerror(errno));
943 			goto err2;
944 		}
945 
946 		if (numifs >= io.pfiio_size)
947 			break;
948 
949 		numifs = io.pfiio_size;
950 	}
951 
952 	for (i = 0; i < numifs; i++) {
953 		e = malloc(sizeof(struct pfi_entry));
954 		if (e == NULL)
955 			goto err1;
956 		e->index = i + 1;
957 		memcpy(&e->pfi, p+i, sizeof(struct pfi_kif));
958 		TAILQ_INSERT_TAIL(&pfi_table, e, link);
959 	}
960 
961 	pfi_table_age = time(NULL);
962 	pfi_table_count = numifs;
963 	pf_tick = this_tick;
964 
965 	free(p);
966 	return (0);
967 
968 err1:
969 	while (!TAILQ_EMPTY(&pfi_table)) {
970 		e = TAILQ_FIRST(&pfi_table);
971 		TAILQ_REMOVE(&pfi_table, e, link);
972 		free(e);
973 	}
974 err2:
975 	free(p);
976 	return(-1);
977 }
978 
979 static int
980 pfq_refresh(void)
981 {
982 	struct pfioc_altq pa;
983 	struct pfq_entry *e;
984 	int i, numqs, ticket;
985 
986 	if (started && this_tick <= pf_tick)
987 		return (0);
988 
989 	while (!TAILQ_EMPTY(&pfq_table)) {
990 		e = TAILQ_FIRST(&pfq_table);
991 		TAILQ_REMOVE(&pfq_table, e, link);
992 		free(e);
993 	}
994 
995 	bzero(&pa, sizeof(pa));
996 
997 	if (ioctl(dev, DIOCGETALTQS, &pa)) {
998 		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
999 		    strerror(errno));
1000 		return (-1);
1001 	}
1002 
1003 	numqs = pa.nr;
1004 	ticket = pa.ticket;
1005 
1006 	for (i = 0; i < numqs; i++) {
1007 		e = malloc(sizeof(struct pfq_entry));
1008 		if (e == NULL) {
1009 			syslog(LOG_ERR, "pfq_refresh(): "
1010 			    "malloc(): %s",
1011 			    strerror(errno));
1012 			goto err;
1013 		}
1014 		pa.ticket = ticket;
1015 		pa.nr = i;
1016 
1017 		if (ioctl(dev, DIOCGETALTQ, &pa)) {
1018 			syslog(LOG_ERR, "pfq_refresh(): "
1019 			    "ioctl(DIOCGETALTQ): %s",
1020 			    strerror(errno));
1021 			goto err;
1022 		}
1023 
1024 		if (pa.altq.qid > 0) {
1025 			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
1026 			e->index = pa.altq.qid;
1027 			pfq_table_count = i;
1028 			TAILQ_INSERT_TAIL(&pfq_table, e, link);
1029 		}
1030 	}
1031 
1032 	pfq_table_age = time(NULL);
1033 	pf_tick = this_tick;
1034 
1035 	return (0);
1036 err:
1037 	free(e);
1038 	while (!TAILQ_EMPTY(&pfq_table)) {
1039 		e = TAILQ_FIRST(&pfq_table);
1040 		TAILQ_REMOVE(&pfq_table, e, link);
1041 		free(e);
1042 	}
1043 	return(-1);
1044 }
1045 
1046 static int
1047 pfs_refresh(void)
1048 {
1049 	if (started && this_tick <= pf_tick)
1050 		return (0);
1051 
1052 	bzero(&pfs, sizeof(struct pf_status));
1053 
1054 	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1055 		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1056 		    strerror(errno));
1057 		return (-1);
1058 	}
1059 
1060 	pf_tick = this_tick;
1061 	return (0);
1062 }
1063 
1064 static int
1065 pft_refresh(void)
1066 {
1067 	struct pfioc_table io;
1068 	struct pfr_tstats *t = NULL;
1069 	struct pft_entry *e;
1070 	int i, numtbls = 1;
1071 
1072 	if (started && this_tick <= pf_tick)
1073 		return (0);
1074 
1075 	while (!TAILQ_EMPTY(&pft_table)) {
1076 		e = TAILQ_FIRST(&pft_table);
1077 		TAILQ_REMOVE(&pft_table, e, link);
1078 		free(e);
1079 	}
1080 
1081 	bzero(&io, sizeof(io));
1082 	io.pfrio_esize = sizeof(struct pfr_tstats);
1083 
1084 	for (;;) {
1085 		t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
1086 		if (t == NULL) {
1087 			syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
1088 			    numtbls, strerror(errno));
1089 			goto err2;
1090 		}
1091 		io.pfrio_size = numtbls;
1092 		io.pfrio_buffer = t;
1093 
1094 		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1095 			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1096 			    strerror(errno));
1097 			goto err2;
1098 		}
1099 
1100 		if (numtbls >= io.pfrio_size)
1101 			break;
1102 
1103 		numtbls = io.pfrio_size;
1104 	}
1105 
1106 	for (i = 0; i < numtbls; i++) {
1107 		e = malloc(sizeof(struct pfr_tstats));
1108 		if (e == NULL)
1109 			goto err1;
1110 		e->index = i + 1;
1111 		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1112 		TAILQ_INSERT_TAIL(&pft_table, e, link);
1113 	}
1114 
1115 	pft_table_age = time(NULL);
1116 	pft_table_count = numtbls;
1117 	pf_tick = this_tick;
1118 
1119 	free(t);
1120 	return (0);
1121 err1:
1122 	while (!TAILQ_EMPTY(&pft_table)) {
1123 		e = TAILQ_FIRST(&pft_table);
1124 		TAILQ_REMOVE(&pft_table, e, link);
1125 		free(e);
1126 	}
1127 err2:
1128 	free(t);
1129 	return(-1);
1130 }
1131 
1132 /*
1133  * check whether altq support is enabled in kernel
1134  */
1135 
1136 static int
1137 altq_is_enabled(int pfdev)
1138 {
1139         struct pfioc_altq pa;
1140 
1141 	errno = 0;
1142         if (ioctl(pfdev, DIOCGETALTQS, &pa)) {
1143                 if (errno == ENODEV) {
1144 			syslog(LOG_INFO, "No ALTQ support in kernel\n"
1145 			    "ALTQ related functions disabled\n");
1146                         return (0);
1147                 } else
1148                         syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s",
1149 			    strerror(errno));
1150 			return (-1);
1151         }
1152         return (1);
1153 }
1154 
1155 /*
1156  * Implement the bsnmpd module interface
1157  */
1158 static int
1159 pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1160 {
1161 	module = mod;
1162 
1163 	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1164 		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1165 		    strerror(errno));
1166 		return (-1);
1167 	}
1168 
1169 	if ((altq_enabled = altq_is_enabled(dev)) == -1) {
1170 		syslog(LOG_ERR, "pf_init(): altq test failed");
1171 		return (-1);
1172 	}
1173 
1174 	/* Prepare internal state */
1175 	TAILQ_INIT(&pfi_table);
1176 	TAILQ_INIT(&pfq_table);
1177 	TAILQ_INIT(&pft_table);
1178 
1179 	pfi_refresh();
1180 	if (altq_enabled) {
1181 		pfq_refresh();
1182 	}
1183 
1184 	pfs_refresh();
1185 	pft_refresh();
1186 
1187 	started = 1;
1188 
1189 	return (0);
1190 }
1191 
1192 static int
1193 pf_fini(void)
1194 {
1195 	struct pfi_entry *i1, *i2;
1196 	struct pfq_entry *q1, *q2;
1197 	struct pft_entry *t1, *t2;
1198 
1199 	/* Empty the list of interfaces */
1200 	i1 = TAILQ_FIRST(&pfi_table);
1201 	while (i1 != NULL) {
1202 		i2 = TAILQ_NEXT(i1, link);
1203 		free(i1);
1204 		i1 = i2;
1205 	}
1206 
1207 	/* List of queues */
1208 	q1 = TAILQ_FIRST(&pfq_table);
1209 	while (q1 != NULL) {
1210 		q2 = TAILQ_NEXT(q1, link);
1211 		free(q1);
1212 		q1 = q2;
1213 	}
1214 
1215 	/* And the list of tables */
1216 	t1 = TAILQ_FIRST(&pft_table);
1217 	while (t1 != NULL) {
1218 		t2 = TAILQ_NEXT(t1, link);
1219 		free(t1);
1220 		t1 = t2;
1221 	}
1222 
1223 	close(dev);
1224 	return (0);
1225 }
1226 
1227 static void
1228 pf_dump(void)
1229 {
1230 	pfi_refresh();
1231 	if (altq_enabled) {
1232 		pfq_refresh();
1233 	}
1234 	pft_refresh();
1235 
1236 	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1237 	    (intmax_t)pfi_table_age);
1238 	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1239 	    pfi_table_count);
1240 
1241 	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1242 	    (intmax_t)pfq_table_age);
1243 	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1244 	    pfq_table_count);
1245 
1246 	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1247 	    (intmax_t)pft_table_age);
1248 
1249 	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1250 	    pft_table_count);
1251 }
1252 
1253 const struct snmp_module config = {
1254 	.comment = "This module implements a MIB for the pf packet filter.",
1255 	.init =		pf_init,
1256 	.fini =		pf_fini,
1257 	.tree =		pf_ctree,
1258 	.dump =		pf_dump,
1259 	.tree_size =	pf_CTREE_SIZE,
1260 };
1261