]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / bsnmpd / modules / snmp_pf / pf_snmp.c
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                         INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index);
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 pft_entry));
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 };