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