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