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_if 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.pfif_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.pfif_tzero) * 100; 554 break; 555 case LEAF_pfInterfacesIfRefsState: 556 val->v.uint32 = e->pfi.pfif_states; 557 break; 558 case LEAF_pfInterfacesIfRefsRule: 559 val->v.uint32 = e->pfi.pfif_rules; 560 break; 561 case LEAF_pfInterfacesIf4BytesInPass: 562 val->v.counter64 = 563 e->pfi.pfif_bytes[IPV4][IN][PASS]; 564 break; 565 case LEAF_pfInterfacesIf4BytesInBlock: 566 val->v.counter64 = 567 e->pfi.pfif_bytes[IPV4][IN][BLOCK]; 568 break; 569 case LEAF_pfInterfacesIf4BytesOutPass: 570 val->v.counter64 = 571 e->pfi.pfif_bytes[IPV4][OUT][PASS]; 572 break; 573 case LEAF_pfInterfacesIf4BytesOutBlock: 574 val->v.counter64 = 575 e->pfi.pfif_bytes[IPV4][OUT][BLOCK]; 576 break; 577 case LEAF_pfInterfacesIf4PktsInPass: 578 val->v.counter64 = 579 e->pfi.pfif_packets[IPV4][IN][PASS]; 580 break; 581 case LEAF_pfInterfacesIf4PktsInBlock: 582 val->v.counter64 = 583 e->pfi.pfif_packets[IPV4][IN][BLOCK]; 584 break; 585 case LEAF_pfInterfacesIf4PktsOutPass: 586 val->v.counter64 = 587 e->pfi.pfif_packets[IPV4][OUT][PASS]; 588 break; 589 case LEAF_pfInterfacesIf4PktsOutBlock: 590 val->v.counter64 = 591 e->pfi.pfif_packets[IPV4][OUT][BLOCK]; 592 break; 593 case LEAF_pfInterfacesIf6BytesInPass: 594 val->v.counter64 = 595 e->pfi.pfif_bytes[IPV6][IN][PASS]; 596 break; 597 case LEAF_pfInterfacesIf6BytesInBlock: 598 val->v.counter64 = 599 e->pfi.pfif_bytes[IPV6][IN][BLOCK]; 600 break; 601 case LEAF_pfInterfacesIf6BytesOutPass: 602 val->v.counter64 = 603 e->pfi.pfif_bytes[IPV6][OUT][PASS]; 604 break; 605 case LEAF_pfInterfacesIf6BytesOutBlock: 606 val->v.counter64 = 607 e->pfi.pfif_bytes[IPV6][OUT][BLOCK]; 608 break; 609 case LEAF_pfInterfacesIf6PktsInPass: 610 val->v.counter64 = 611 e->pfi.pfif_packets[IPV6][IN][PASS]; 612 break; 613 case LEAF_pfInterfacesIf6PktsInBlock: 614 val->v.counter64 = 615 e->pfi.pfif_packets[IPV6][IN][BLOCK]; 616 break; 617 case LEAF_pfInterfacesIf6PktsOutPass: 618 val->v.counter64 = 619 e->pfi.pfif_packets[IPV6][OUT][PASS]; 620 break; 621 case LEAF_pfInterfacesIf6PktsOutBlock: 622 val->v.counter64 = 623 e->pfi.pfif_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_if *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_flags = PFI_FLAG_INSTANCE; 929 io.pfiio_esize = sizeof(struct pfi_if); 930 931 for (;;) { 932 p = reallocf(p, numifs * sizeof(struct pfi_if)); 933 if (p == NULL) { 934 syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s", 935 numifs, strerror(errno)); 936 goto err2; 937 } 938 io.pfiio_size = numifs; 939 io.pfiio_buffer = p; 940 941 if (ioctl(dev, DIOCIGETIFACES, &io)) { 942 syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s", 943 strerror(errno)); 944 goto err2; 945 } 946 947 if (numifs >= io.pfiio_size) 948 break; 949 950 numifs = io.pfiio_size; 951 } 952 953 for (i = 0; i < numifs; i++) { 954 e = malloc(sizeof(struct pfi_entry)); 955 if (e == NULL) 956 goto err1; 957 e->index = i + 1; 958 memcpy(&e->pfi, p+i, sizeof(struct pfi_if)); 959 TAILQ_INSERT_TAIL(&pfi_table, e, link); 960 } 961 962 pfi_table_age = time(NULL); 963 pfi_table_count = numifs; 964 pf_tick = this_tick; 965 966 free(p); 967 return (0); 968 969 err1: 970 while (!TAILQ_EMPTY(&pfi_table)) { 971 e = TAILQ_FIRST(&pfi_table); 972 TAILQ_REMOVE(&pfi_table, e, link); 973 free(e); 974 } 975 err2: 976 free(p); 977 return(-1); 978 } 979 980 static int 981 pfq_refresh(void) 982 { 983 struct pfioc_altq pa; 984 struct pfq_entry *e; 985 int i, numqs, ticket; 986 987 if (started && this_tick <= pf_tick) 988 return (0); 989 990 while (!TAILQ_EMPTY(&pfq_table)) { 991 e = TAILQ_FIRST(&pfq_table); 992 TAILQ_REMOVE(&pfq_table, e, link); 993 free(e); 994 } 995 996 bzero(&pa, sizeof(pa)); 997 998 if (ioctl(dev, DIOCGETALTQS, &pa)) { 999 syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s", 1000 strerror(errno)); 1001 return (-1); 1002 } 1003 1004 numqs = pa.nr; 1005 ticket = pa.ticket; 1006 1007 for (i = 0; i < numqs; i++) { 1008 e = malloc(sizeof(struct pfq_entry)); 1009 if (e == NULL) { 1010 syslog(LOG_ERR, "pfq_refresh(): " 1011 "malloc(): %s", 1012 strerror(errno)); 1013 goto err; 1014 } 1015 pa.ticket = ticket; 1016 pa.nr = i; 1017 1018 if (ioctl(dev, DIOCGETALTQ, &pa)) { 1019 syslog(LOG_ERR, "pfq_refresh(): " 1020 "ioctl(DIOCGETALTQ): %s", 1021 strerror(errno)); 1022 goto err; 1023 } 1024 1025 if (pa.altq.qid > 0) { 1026 memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq)); 1027 e->index = pa.altq.qid; 1028 pfq_table_count = i; 1029 TAILQ_INSERT_TAIL(&pfq_table, e, link); 1030 } 1031 } 1032 1033 pfq_table_age = time(NULL); 1034 pf_tick = this_tick; 1035 1036 return (0); 1037 err: 1038 free(e); 1039 while (!TAILQ_EMPTY(&pfq_table)) { 1040 e = TAILQ_FIRST(&pfq_table); 1041 TAILQ_REMOVE(&pfq_table, e, link); 1042 free(e); 1043 } 1044 return(-1); 1045 } 1046 1047 static int 1048 pfs_refresh(void) 1049 { 1050 if (started && this_tick <= pf_tick) 1051 return (0); 1052 1053 bzero(&pfs, sizeof(struct pf_status)); 1054 1055 if (ioctl(dev, DIOCGETSTATUS, &pfs)) { 1056 syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s", 1057 strerror(errno)); 1058 return (-1); 1059 } 1060 1061 pf_tick = this_tick; 1062 return (0); 1063 } 1064 1065 static int 1066 pft_refresh(void) 1067 { 1068 struct pfioc_table io; 1069 struct pfr_tstats *t = NULL; 1070 struct pft_entry *e; 1071 int i, numtbls = 1; 1072 1073 if (started && this_tick <= pf_tick) 1074 return (0); 1075 1076 while (!TAILQ_EMPTY(&pft_table)) { 1077 e = TAILQ_FIRST(&pft_table); 1078 TAILQ_REMOVE(&pft_table, e, link); 1079 free(e); 1080 } 1081 1082 bzero(&io, sizeof(io)); 1083 io.pfrio_esize = sizeof(struct pfr_tstats); 1084 1085 for (;;) { 1086 t = reallocf(t, numtbls * sizeof(struct pfr_tstats)); 1087 if (t == NULL) { 1088 syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s", 1089 numtbls, strerror(errno)); 1090 goto err2; 1091 } 1092 io.pfrio_size = numtbls; 1093 io.pfrio_buffer = t; 1094 1095 if (ioctl(dev, DIOCRGETTSTATS, &io)) { 1096 syslog(LOG_ERR, "pft_refresh(): ioctl(): %s", 1097 strerror(errno)); 1098 goto err2; 1099 } 1100 1101 if (numtbls >= io.pfrio_size) 1102 break; 1103 1104 numtbls = io.pfrio_size; 1105 } 1106 1107 for (i = 0; i < numtbls; i++) { 1108 e = malloc(sizeof(struct pfr_tstats)); 1109 if (e == NULL) 1110 goto err1; 1111 e->index = i + 1; 1112 memcpy(&e->pft, t+i, sizeof(struct pfr_tstats)); 1113 TAILQ_INSERT_TAIL(&pft_table, e, link); 1114 } 1115 1116 pft_table_age = time(NULL); 1117 pft_table_count = numtbls; 1118 pf_tick = this_tick; 1119 1120 free(t); 1121 return (0); 1122 err1: 1123 while (!TAILQ_EMPTY(&pft_table)) { 1124 e = TAILQ_FIRST(&pft_table); 1125 TAILQ_REMOVE(&pft_table, e, link); 1126 free(e); 1127 } 1128 err2: 1129 free(t); 1130 return(-1); 1131 } 1132 1133 /* 1134 * check whether altq support is enabled in kernel 1135 */ 1136 1137 static int 1138 altq_is_enabled(int pfdev) 1139 { 1140 struct pfioc_altq pa; 1141 1142 errno = 0; 1143 if (ioctl(pfdev, DIOCGETALTQS, &pa)) { 1144 if (errno == ENODEV) { 1145 syslog(LOG_INFO, "No ALTQ support in kernel\n" 1146 "ALTQ related functions disabled\n"); 1147 return (0); 1148 } else 1149 syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s", 1150 strerror(errno)); 1151 return (-1); 1152 } 1153 return (1); 1154 } 1155 1156 /* 1157 * Implement the bsnmpd module interface 1158 */ 1159 static int 1160 pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[]) 1161 { 1162 module = mod; 1163 1164 if ((dev = open("/dev/pf", O_RDONLY)) == -1) { 1165 syslog(LOG_ERR, "pf_init(): open(): %s\n", 1166 strerror(errno)); 1167 return (-1); 1168 } 1169 1170 if ((altq_enabled = altq_is_enabled(dev)) == -1) { 1171 syslog(LOG_ERR, "pf_init(): altq test failed"); 1172 return (-1); 1173 } 1174 1175 /* Prepare internal state */ 1176 TAILQ_INIT(&pfi_table); 1177 TAILQ_INIT(&pfq_table); 1178 TAILQ_INIT(&pft_table); 1179 1180 pfi_refresh(); 1181 if (altq_enabled) { 1182 pfq_refresh(); 1183 } 1184 1185 pfs_refresh(); 1186 pft_refresh(); 1187 1188 started = 1; 1189 1190 return (0); 1191 } 1192 1193 static int 1194 pf_fini(void) 1195 { 1196 struct pfi_entry *i1, *i2; 1197 struct pfq_entry *q1, *q2; 1198 struct pft_entry *t1, *t2; 1199 1200 /* Empty the list of interfaces */ 1201 i1 = TAILQ_FIRST(&pfi_table); 1202 while (i1 != NULL) { 1203 i2 = TAILQ_NEXT(i1, link); 1204 free(i1); 1205 i1 = i2; 1206 } 1207 1208 /* List of queues */ 1209 q1 = TAILQ_FIRST(&pfq_table); 1210 while (q1 != NULL) { 1211 q2 = TAILQ_NEXT(q1, link); 1212 free(q1); 1213 q1 = q2; 1214 } 1215 1216 /* And the list of tables */ 1217 t1 = TAILQ_FIRST(&pft_table); 1218 while (t1 != NULL) { 1219 t2 = TAILQ_NEXT(t1, link); 1220 free(t1); 1221 t1 = t2; 1222 } 1223 1224 close(dev); 1225 return (0); 1226 } 1227 1228 static void 1229 pf_dump(void) 1230 { 1231 pfi_refresh(); 1232 if (altq_enabled) { 1233 pfq_refresh(); 1234 } 1235 pft_refresh(); 1236 1237 syslog(LOG_ERR, "Dump: pfi_table_age = %jd", 1238 (intmax_t)pfi_table_age); 1239 syslog(LOG_ERR, "Dump: pfi_table_count = %d", 1240 pfi_table_count); 1241 1242 syslog(LOG_ERR, "Dump: pfq_table_age = %jd", 1243 (intmax_t)pfq_table_age); 1244 syslog(LOG_ERR, "Dump: pfq_table_count = %d", 1245 pfq_table_count); 1246 1247 syslog(LOG_ERR, "Dump: pft_table_age = %jd", 1248 (intmax_t)pft_table_age); 1249 1250 syslog(LOG_ERR, "Dump: pft_table_count = %d", 1251 pft_table_count); 1252 } 1253 1254 const struct snmp_module config = { 1255 .comment = "This module implements a MIB for the pf packet filter.", 1256 .init = pf_init, 1257 .fini = pf_fini, 1258 .tree = pf_ctree, 1259 .dump = pf_dump, 1260 .tree_size = pf_CTREE_SIZE, 1261 }; 1262