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 pfr_tstats)); 1108 if (e == NULL) 1109 goto err1; 1110 e->index = i + 1; 1111 memcpy(&e->pft, t+i, sizeof(struct pfr_tstats)); 1112 TAILQ_INSERT_TAIL(&pft_table, e, link); 1113 } 1114 1115 pft_table_age = time(NULL); 1116 pft_table_count = numtbls; 1117 pf_tick = this_tick; 1118 1119 free(t); 1120 return (0); 1121 err1: 1122 while (!TAILQ_EMPTY(&pft_table)) { 1123 e = TAILQ_FIRST(&pft_table); 1124 TAILQ_REMOVE(&pft_table, e, link); 1125 free(e); 1126 } 1127 err2: 1128 free(t); 1129 return(-1); 1130 } 1131 1132 /* 1133 * check whether altq support is enabled in kernel 1134 */ 1135 1136 static int 1137 altq_is_enabled(int pfdev) 1138 { 1139 struct pfioc_altq pa; 1140 1141 errno = 0; 1142 if (ioctl(pfdev, DIOCGETALTQS, &pa)) { 1143 if (errno == ENODEV) { 1144 syslog(LOG_INFO, "No ALTQ support in kernel\n" 1145 "ALTQ related functions disabled\n"); 1146 return (0); 1147 } else 1148 syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s", 1149 strerror(errno)); 1150 return (-1); 1151 } 1152 return (1); 1153 } 1154 1155 /* 1156 * Implement the bsnmpd module interface 1157 */ 1158 static int 1159 pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[]) 1160 { 1161 module = mod; 1162 1163 if ((dev = open("/dev/pf", O_RDONLY)) == -1) { 1164 syslog(LOG_ERR, "pf_init(): open(): %s\n", 1165 strerror(errno)); 1166 return (-1); 1167 } 1168 1169 if ((altq_enabled = altq_is_enabled(dev)) == -1) { 1170 syslog(LOG_ERR, "pf_init(): altq test failed"); 1171 return (-1); 1172 } 1173 1174 /* Prepare internal state */ 1175 TAILQ_INIT(&pfi_table); 1176 TAILQ_INIT(&pfq_table); 1177 TAILQ_INIT(&pft_table); 1178 1179 pfi_refresh(); 1180 if (altq_enabled) { 1181 pfq_refresh(); 1182 } 1183 1184 pfs_refresh(); 1185 pft_refresh(); 1186 1187 started = 1; 1188 1189 return (0); 1190 } 1191 1192 static int 1193 pf_fini(void) 1194 { 1195 struct pfi_entry *i1, *i2; 1196 struct pfq_entry *q1, *q2; 1197 struct pft_entry *t1, *t2; 1198 1199 /* Empty the list of interfaces */ 1200 i1 = TAILQ_FIRST(&pfi_table); 1201 while (i1 != NULL) { 1202 i2 = TAILQ_NEXT(i1, link); 1203 free(i1); 1204 i1 = i2; 1205 } 1206 1207 /* List of queues */ 1208 q1 = TAILQ_FIRST(&pfq_table); 1209 while (q1 != NULL) { 1210 q2 = TAILQ_NEXT(q1, link); 1211 free(q1); 1212 q1 = q2; 1213 } 1214 1215 /* And the list of tables */ 1216 t1 = TAILQ_FIRST(&pft_table); 1217 while (t1 != NULL) { 1218 t2 = TAILQ_NEXT(t1, link); 1219 free(t1); 1220 t1 = t2; 1221 } 1222 1223 close(dev); 1224 return (0); 1225 } 1226 1227 static void 1228 pf_dump(void) 1229 { 1230 pfi_refresh(); 1231 if (altq_enabled) { 1232 pfq_refresh(); 1233 } 1234 pft_refresh(); 1235 1236 syslog(LOG_ERR, "Dump: pfi_table_age = %jd", 1237 (intmax_t)pfi_table_age); 1238 syslog(LOG_ERR, "Dump: pfi_table_count = %d", 1239 pfi_table_count); 1240 1241 syslog(LOG_ERR, "Dump: pfq_table_age = %jd", 1242 (intmax_t)pfq_table_age); 1243 syslog(LOG_ERR, "Dump: pfq_table_count = %d", 1244 pfq_table_count); 1245 1246 syslog(LOG_ERR, "Dump: pft_table_age = %jd", 1247 (intmax_t)pft_table_age); 1248 1249 syslog(LOG_ERR, "Dump: pft_table_count = %d", 1250 pft_table_count); 1251 } 1252 1253 const struct snmp_module config = { 1254 .comment = "This module implements a MIB for the pf packet filter.", 1255 .init = pf_init, 1256 .fini = pf_fini, 1257 .tree = pf_ctree, 1258 .dump = pf_dump, 1259 .tree_size = pf_CTREE_SIZE, 1260 }; 1261