1 /*- 2 * Copyright (c) 2006 Shteryana Shopova <syrinx@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 * Bridge MIB implementation for SNMPd. 27 * Bridge interface objects. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <sys/queue.h> 33 #include <sys/socket.h> 34 #include <sys/time.h> 35 #include <sys/types.h> 36 37 #include <net/ethernet.h> 38 #include <net/if.h> 39 #include <net/if_mib.h> 40 #include <net/if_types.h> 41 42 #include <errno.h> 43 #include <stdarg.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <syslog.h> 47 48 #include <bsnmp/snmpmod.h> 49 #include <bsnmp/snmp_mibII.h> 50 51 #include "bridge_tree.h" 52 #include "bridge_snmp.h" 53 #include "bridge_oid.h" 54 55 static const struct asn_oid oid_newRoot = OIDX_newRoot; 56 static const struct asn_oid oid_TopologyChange = OIDX_topologyChange; 57 static const struct asn_oid oid_begemotBrigeName = \ 58 OIDX_begemotBridgeBaseName; 59 static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot; 60 static const struct asn_oid oid_begemotTopologyChange = \ 61 OIDX_begemotBridgeTopologyChange; 62 63 TAILQ_HEAD(bridge_ifs, bridge_if); 64 65 /* 66 * Free the bridge interface list. 67 */ 68 static void 69 bridge_ifs_free(struct bridge_ifs *headp) 70 { 71 struct bridge_if *b; 72 73 while ((b = TAILQ_FIRST(headp)) != NULL) { 74 TAILQ_REMOVE(headp, b, b_if); 75 free(b); 76 } 77 } 78 79 /* 80 * Insert an entry in the bridge interface TAILQ. Keep the 81 * TAILQ sorted by the bridge's interface name. 82 */ 83 static void 84 bridge_ifs_insert(struct bridge_ifs *headp, 85 struct bridge_if *b) 86 { 87 struct bridge_if *temp; 88 89 if ((temp = TAILQ_FIRST(headp)) == NULL || 90 strcmp(b->bif_name, temp->bif_name) < 0) { 91 TAILQ_INSERT_HEAD(headp, b, b_if); 92 return; 93 } 94 95 TAILQ_FOREACH(temp, headp, b_if) 96 if(strcmp(b->bif_name, temp->bif_name) < 0) 97 TAILQ_INSERT_BEFORE(temp, b, b_if); 98 99 TAILQ_INSERT_TAIL(headp, b, b_if); 100 } 101 102 /* The global bridge interface list. */ 103 static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs); 104 static time_t bridge_list_age; 105 106 /* 107 * Free the global list. 108 */ 109 void 110 bridge_ifs_fini(void) 111 { 112 bridge_ifs_free(&bridge_ifs); 113 } 114 115 /* 116 * Find a bridge interface entry by the bridge interface system index. 117 */ 118 struct bridge_if * 119 bridge_if_find_ifs(uint32_t sysindex) 120 { 121 struct bridge_if *b; 122 123 TAILQ_FOREACH(b, &bridge_ifs, b_if) 124 if (b->sysindex == sysindex) 125 return (b); 126 127 return (NULL); 128 } 129 130 /* 131 * Find a bridge interface entry by the bridge interface name. 132 */ 133 struct bridge_if * 134 bridge_if_find_ifname(const char *b_name) 135 { 136 struct bridge_if *b; 137 138 TAILQ_FOREACH(b, &bridge_ifs, b_if) 139 if (strcmp(b_name, b->bif_name) == 0) 140 return (b); 141 142 return (NULL); 143 } 144 145 /* 146 * Find a bridge name by the bridge interface system index. 147 */ 148 const char * 149 bridge_if_find_name(uint32_t sysindex) 150 { 151 struct bridge_if *b; 152 153 TAILQ_FOREACH(b, &bridge_ifs, b_if) 154 if (b->sysindex == sysindex) 155 return (b->bif_name); 156 157 return (NULL); 158 } 159 160 /* 161 * Given two bridge interfaces' system indexes, find their 162 * corresponding names and return the result of the name 163 * comparison. Returns: 164 * error : -2 165 * i1 < i2 : -1 166 * i1 > i2 : +1 167 * i1 = i2 : 0 168 */ 169 int 170 bridge_compare_sysidx(uint32_t i1, uint32_t i2) 171 { 172 int c; 173 const char *b1, *b2; 174 175 if (i1 == i2) 176 return (0); 177 178 if ((b1 = bridge_if_find_name(i1)) == NULL) { 179 syslog(LOG_ERR, "Bridge interface %d does not exist", i1); 180 return (-2); 181 } 182 183 if ((b2 = bridge_if_find_name(i2)) == NULL) { 184 syslog(LOG_ERR, "Bridge interface %d does not exist", i2); 185 return (-2); 186 } 187 188 if ((c = strcmp(b1, b2)) < 0) 189 return (-1); 190 else if (c > 0) 191 return (1); 192 193 return (0); 194 } 195 196 /* 197 * Fetch the first bridge interface from the list. 198 */ 199 struct bridge_if * 200 bridge_first_bif(void) 201 { 202 return (TAILQ_FIRST(&bridge_ifs)); 203 } 204 205 /* 206 * Fetch the next bridge interface from the list. 207 */ 208 struct bridge_if * 209 bridge_next_bif(struct bridge_if *b_pr) 210 { 211 return (TAILQ_NEXT(b_pr, b_if)); 212 } 213 214 /* 215 * Create a new entry for a bridge interface and insert 216 * it in the list. 217 */ 218 static struct bridge_if * 219 bridge_new_bif(const char *bif_n, uint32_t sysindex, const u_char *physaddr) 220 { 221 struct bridge_if *bif; 222 223 if ((bif = (struct bridge_if *) malloc(sizeof(*bif)))== NULL) { 224 syslog(LOG_ERR, "bridge new interface failed: %s", 225 strerror(errno)); 226 return (NULL); 227 } 228 229 bzero(bif, sizeof(struct bridge_if)); 230 strlcpy(bif->bif_name, bif_n, IFNAMSIZ); 231 bcopy(physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); 232 bif->sysindex = sysindex; 233 bif->br_type = BaseType_transparent_only; 234 /* 1 - all bridges default hold time * 100 - centi-seconds */ 235 bif->hold_time = 1 * 100; 236 bif->prot_spec = dot1dStpProtocolSpecification_ieee8021d; 237 bridge_ifs_insert(&bridge_ifs, bif); 238 239 return (bif); 240 } 241 242 /* 243 * Remove a bridge interface from the list, freeing all it's ports 244 * and address entries. 245 */ 246 void 247 bridge_remove_bif(struct bridge_if *bif) 248 { 249 bridge_members_free(bif); 250 bridge_addrs_free(bif); 251 TAILQ_REMOVE(&bridge_ifs, bif, b_if); 252 free(bif); 253 } 254 255 256 /* 257 * Prepare the variable (bridge interface name) for the private 258 * begemot notifications. 259 */ 260 static struct snmp_value* 261 bridge_basename_var(struct bridge_if *bif, struct snmp_value* b_val) 262 { 263 uint i; 264 265 b_val->var = oid_begemotBrigeName; 266 b_val->var.subs[b_val->var.len++] = strlen(bif->bif_name); 267 268 if ((b_val->v.octetstring.octets = (u_char *) 269 malloc(strlen(bif->bif_name))) == NULL) 270 return (NULL); 271 272 for (i = 0; i < strlen(bif->bif_name); i++) 273 b_val->var.subs[b_val->var.len++] = bif->bif_name[i]; 274 275 b_val->v.octetstring.len = strlen(bif->bif_name); 276 bcopy(bif->bif_name, b_val->v.octetstring.octets, 277 strlen(bif->bif_name)); 278 b_val->syntax = SNMP_SYNTAX_OCTETSTRING; 279 280 return (b_val); 281 } 282 283 /* 284 * Compare the values of the old and the new root port and 285 * send a new root notification, if they are not matching. 286 */ 287 static void 288 bridge_new_root(struct bridge_if *bif) 289 { 290 struct snmp_value bif_idx; 291 292 if (bridge_get_default() == bif) 293 snmp_send_trap(&oid_newRoot, (struct snmp_value *) NULL); 294 295 if (bridge_basename_var(bif, &bif_idx) == NULL) 296 return; 297 298 snmp_send_trap(&oid_begemotTopologyChange, 299 &bif_idx, (struct snmp_value *) NULL); 300 } 301 302 /* 303 * Compare the new and old topology change times and send a 304 * topology change notification if necessary. 305 */ 306 static void 307 bridge_top_change(struct bridge_if *bif) 308 { 309 struct snmp_value bif_idx; 310 311 if (bridge_get_default() == bif) 312 snmp_send_trap(&oid_TopologyChange, 313 (struct snmp_value *) NULL); 314 315 if (bridge_basename_var(bif, &bif_idx) == NULL) 316 return; 317 318 snmp_send_trap(&oid_begemotNewRoot, 319 &bif_idx, (struct snmp_value *) NULL); 320 } 321 322 static int 323 bridge_if_create(const char* b_name, int8_t up) 324 { 325 if (bridge_create(b_name) < 0) 326 return (-1); 327 328 if (up == 1 && (bridge_set_if_up(b_name, 1) < 0)) 329 return (-1); 330 331 /* 332 * Do not create a new bridge entry here - 333 * wait until the mibII module notifies us. 334 */ 335 return (0); 336 } 337 338 static int 339 bridge_if_destroy(struct bridge_if *bif) 340 { 341 if (bridge_destroy(bif->bif_name) < 0) 342 return (-1); 343 344 bridge_remove_bif(bif); 345 346 return (0); 347 } 348 349 /* 350 * Calculate the timeticks since the last topology change. 351 */ 352 static int 353 bridge_get_time_since_tc(struct bridge_if *bif, uint32_t *ticks) 354 { 355 struct timeval ct; 356 357 if (gettimeofday(&ct, NULL) < 0) { 358 syslog(LOG_ERR, "bridge get time since last TC:" 359 "getttimeofday failed: %s", strerror(errno)); 360 return (-1); 361 } 362 363 if (ct.tv_usec - bif->last_tc_time.tv_usec < 0) { 364 ct.tv_sec -= 1; 365 ct.tv_usec += 1000000; 366 } 367 368 ct.tv_sec -= bif->last_tc_time.tv_sec; 369 ct.tv_usec -= bif->last_tc_time.tv_usec; 370 371 *ticks = ct.tv_sec * 100 + ct.tv_usec/10000; 372 373 return (0); 374 } 375 376 /* 377 * Update the info we have for a single bridge interface. 378 * Return: 379 * 1, if successful 380 * 0, if the interface was deleted 381 * -1, error occured while fetching the info from the kernel. 382 */ 383 static int 384 bridge_update_bif(struct bridge_if *bif) 385 { 386 struct mibif *ifp; 387 388 /* Walk through the mibII interface list. */ 389 for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) 390 if (strcmp(ifp->name, bif->bif_name) == 0) 391 break; 392 393 if (ifp == NULL) { 394 /* Ops, we do not exist anymore. */ 395 bridge_remove_bif(bif); 396 return (0); 397 } 398 399 if (ifp->physaddr != NULL ) 400 bcopy(ifp->physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); 401 else 402 bridge_get_basemac(bif->bif_name, bif->br_addr.octet); 403 404 if (ifp->mib.ifmd_flags & IFF_RUNNING) 405 bif->if_status = RowStatus_active; 406 else 407 bif->if_status = RowStatus_notInService; 408 409 switch (bridge_getinfo_bif(bif)) { 410 case 2: 411 bridge_new_root(bif); 412 break; 413 case 1: 414 bridge_top_change(bif); 415 break; 416 case -1: 417 bridge_remove_bif(bif); 418 return (-1); 419 default: 420 break; 421 } 422 423 /* 424 * The number of ports is accessible via SNMP - 425 * update the ports each time the bridge interface data 426 * is refreshed too. 427 */ 428 bif->num_ports = bridge_update_memif(bif); 429 bif->entry_age = time(NULL); 430 431 return (1); 432 } 433 434 /* 435 * Update all bridge interfaces' ports only - 436 * make sure each bridge interface exists first. 437 */ 438 void 439 bridge_update_all_ports(void) 440 { 441 struct mibif *ifp; 442 struct bridge_if *bif, *t_bif; 443 444 for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { 445 t_bif = bridge_next_bif(bif); 446 447 for (ifp = mib_first_if(); ifp != NULL; 448 ifp = mib_next_if(ifp)) 449 if (strcmp(ifp->name, bif->bif_name) == 0) 450 break; 451 452 if (ifp != NULL) 453 bif->num_ports = bridge_update_memif(bif); 454 else /* Ops, we do not exist anymore. */ 455 bridge_remove_bif(bif); 456 } 457 458 bridge_ports_update_listage(); 459 } 460 461 /* 462 * Update all addresses only. 463 */ 464 void 465 bridge_update_all_addrs(void) 466 { 467 struct mibif *ifp; 468 struct bridge_if *bif, *t_bif; 469 470 for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { 471 t_bif = bridge_next_bif(bif); 472 473 for (ifp = mib_first_if(); ifp != NULL; 474 ifp = mib_next_if(ifp)) 475 if (strcmp(ifp->name, bif->bif_name) == 0) 476 break; 477 478 if (ifp != NULL) 479 bif->num_addrs = bridge_update_addrs(bif); 480 else /* Ops, we don't exist anymore. */ 481 bridge_remove_bif(bif); 482 } 483 484 bridge_addrs_update_listage(); 485 } 486 487 /* 488 * Update only the bridge interfaces' data - skip addresses. 489 */ 490 void 491 bridge_update_all_ifs(void) 492 { 493 struct bridge_if *bif, *t_bif; 494 495 for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { 496 t_bif = bridge_next_bif(bif); 497 bridge_update_bif(bif); 498 } 499 500 bridge_ports_update_listage(); 501 bridge_list_age = time(NULL); 502 } 503 504 /* 505 * Update all info we have for all bridges. 506 */ 507 void 508 bridge_update_all(void *arg __unused) 509 { 510 struct bridge_if *bif, *t_bif; 511 512 for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { 513 t_bif = bridge_next_bif(bif); 514 if (bridge_update_bif(bif) <= 0) 515 continue; 516 517 /* Update our learnt addresses. */ 518 bif->num_addrs = bridge_update_addrs(bif); 519 } 520 521 bridge_list_age = time(NULL); 522 bridge_ports_update_listage(); 523 bridge_addrs_update_listage(); 524 } 525 526 /* 527 * Callback for polling our last topology change time - 528 * check whether we are root or whether a TC was detected once every 529 * 30 seconds, so that we can send the newRoot and TopologyChange traps 530 * on time. The rest of the data is polled only once every 5 min. 531 */ 532 void 533 bridge_update_tc_time(void *arg __unused) 534 { 535 struct bridge_if *bif; 536 struct mibif *ifp; 537 538 TAILQ_FOREACH(bif, &bridge_ifs, b_if) { 539 /* Walk through the mibII interface list. */ 540 for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) 541 if (strcmp(ifp->name, bif->bif_name) == 0) 542 break; 543 544 if (ifp == NULL) { 545 bridge_remove_bif(bif); 546 continue; 547 } 548 549 switch (bridge_get_op_param(bif)) { 550 case 2: 551 bridge_new_root(bif); 552 break; 553 case 1: 554 bridge_top_change(bif); 555 break; 556 } 557 } 558 } 559 560 /* 561 * Callback for handling new bridge interface creation. 562 */ 563 int 564 bridge_attach_newif(struct mibif *ifp) 565 { 566 u_char *p_mac, mac[ETHER_ADDR_LEN]; 567 struct bridge_if *bif; 568 569 if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE) 570 return (0); 571 572 /* Make sure it does not exist in our list. */ 573 TAILQ_FOREACH(bif, &bridge_ifs, b_if) 574 if(strcmp(bif->bif_name, ifp->name) == 0) { 575 syslog(LOG_ERR, "bridge interface %s already " 576 "in list", bif->bif_name); 577 return (-1); 578 } 579 580 if ((p_mac = ifp->physaddr) == NULL && 581 (p_mac = bridge_get_basemac(ifp->name, mac)) == NULL) { 582 syslog(LOG_ERR, "bridge attach new %s failed - " 583 "no bridge mac address", ifp->name); 584 return (-1); 585 } 586 587 if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, p_mac)) == NULL) 588 return (-1); 589 590 if (ifp->mib.ifmd_flags & IFF_RUNNING) 591 bif->if_status = RowStatus_active; 592 else 593 bif->if_status = RowStatus_notInService; 594 595 /* Skip sending notifications if the interface was just created. */ 596 if (bridge_getinfo_bif(bif) < 0 || 597 (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 || 598 (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) { 599 bridge_remove_bif(bif); 600 return (-1); 601 } 602 603 /* Check whether we are the default bridge interface. */ 604 if (strcmp(ifp->name, bridge_get_default_name()) == 0) 605 bridge_set_default(bif); 606 607 return (0); 608 } 609 610 void 611 bridge_ifs_dump(void) 612 { 613 struct bridge_if *bif; 614 615 for (bif = bridge_first_bif(); bif != NULL; 616 bif = bridge_next_bif(bif)) { 617 syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name, 618 bif->sysindex); 619 bridge_ports_dump(bif); 620 bridge_addrs_dump(bif); 621 } 622 } 623 624 /* 625 * RFC4188 specifics. 626 */ 627 int 628 op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value, 629 uint sub, uint iidx __unused, enum snmp_op op) 630 { 631 int ret; 632 struct bridge_if *bif; 633 634 if ((bif = bridge_get_default()) == NULL) 635 return (SNMP_ERR_NOSUCHNAME); 636 637 if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && 638 bridge_update_bif(bif) <= 0) /* It was just deleted. */ 639 return (SNMP_ERR_NOSUCHNAME); 640 641 ret = SNMP_ERR_NOERROR; 642 643 switch (op) { 644 case SNMP_OP_GET: 645 switch (value->var.subs[sub - 1]) { 646 case LEAF_dot1dBaseBridgeAddress: 647 ret = string_get(value, bif->br_addr.octet, 648 ETHER_ADDR_LEN); 649 break; 650 case LEAF_dot1dBaseNumPorts: 651 value->v.integer = bif->num_ports; 652 break; 653 case LEAF_dot1dBaseType: 654 value->v.integer = bif->br_type; 655 break; 656 abort(); 657 } 658 break; 659 660 case SNMP_OP_SET: 661 ret = SNMP_ERR_NOT_WRITEABLE; 662 break; 663 664 case SNMP_OP_GETNEXT: 665 case SNMP_OP_ROLLBACK: 666 case SNMP_OP_COMMIT: 667 abort(); 668 } 669 670 return (ret); 671 } 672 673 int 674 op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *value, 675 uint sub, uint iidx __unused, enum snmp_op op) 676 { 677 int ret; 678 struct bridge_if *bif; 679 680 if ((bif = bridge_get_default()) == NULL) 681 return (SNMP_ERR_NOSUCHNAME); 682 683 if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && 684 bridge_update_bif(bif) <= 0) /* It was just deleted. */ 685 return (SNMP_ERR_NOSUCHNAME); 686 687 switch (op) { 688 case SNMP_OP_GET: 689 switch (value->var.subs[sub - 1]) { 690 case LEAF_dot1dStpProtocolSpecification: 691 value->v.integer = bif->prot_spec; 692 break; 693 694 case LEAF_dot1dStpPriority: 695 value->v.integer = bif->priority; 696 break; 697 698 case LEAF_dot1dStpTimeSinceTopologyChange: 699 if (bridge_get_time_since_tc(bif, 700 &(value->v.uint32)) < 0) 701 return (SNMP_ERR_GENERR); 702 break; 703 704 case LEAF_dot1dStpTopChanges: 705 value->v.uint32 = bif->top_changes; 706 break; 707 708 case LEAF_dot1dStpDesignatedRoot: 709 return (string_get(value, bif->design_root, 710 SNMP_BRIDGE_ID_LEN)); 711 712 case LEAF_dot1dStpRootCost: 713 value->v.integer = bif->root_cost; 714 break; 715 716 case LEAF_dot1dStpRootPort: 717 value->v.integer = bif->root_port; 718 break; 719 720 case LEAF_dot1dStpMaxAge: 721 value->v.integer = bif->max_age; 722 break; 723 724 case LEAF_dot1dStpHelloTime: 725 value->v.integer = bif->hello_time; 726 break; 727 728 case LEAF_dot1dStpHoldTime: 729 value->v.integer = bif->hold_time; 730 break; 731 732 case LEAF_dot1dStpForwardDelay: 733 value->v.integer = bif->fwd_delay; 734 break; 735 736 case LEAF_dot1dStpBridgeMaxAge: 737 value->v.integer = bif->bridge_max_age; 738 break; 739 740 case LEAF_dot1dStpBridgeHelloTime: 741 value->v.integer = bif->bridge_hello_time; 742 break; 743 744 case LEAF_dot1dStpBridgeForwardDelay: 745 value->v.integer = bif->bridge_fwd_delay; 746 break; 747 case LEAF_dot1dStpVersion: 748 value->v.integer = bif->stp_version; 749 break; 750 case LEAF_dot1dStpTxHoldCount: 751 value->v.integer = bif->tx_hold_count; 752 } 753 754 return (SNMP_ERR_NOERROR); 755 756 case SNMP_OP_GETNEXT: 757 abort(); 758 759 case SNMP_OP_SET: 760 switch (value->var.subs[sub - 1]) { 761 case LEAF_dot1dStpPriority: 762 ctx->scratch->int1 = bif->priority; 763 ret = bridge_set_priority(bif, value->v.integer); 764 break; 765 766 case LEAF_dot1dStpBridgeMaxAge: 767 ctx->scratch->int1 = bif->bridge_max_age; 768 ret = bridge_set_maxage(bif, value->v.integer); 769 break; 770 771 case LEAF_dot1dStpBridgeHelloTime: 772 ctx->scratch->int1 = bif->bridge_hello_time; 773 ret = bridge_set_hello_time(bif, value->v.integer); 774 break; 775 776 case LEAF_dot1dStpBridgeForwardDelay: 777 ctx->scratch->int1 = bif->bridge_fwd_delay; 778 ret = bridge_set_forward_delay(bif, value->v.integer); 779 break; 780 781 case LEAF_dot1dStpVersion: 782 ctx->scratch->int1 = bif->stp_version; 783 ret = bridge_set_stp_version(bif, value->v.integer); 784 break; 785 786 case LEAF_dot1dStpTxHoldCount: 787 ctx->scratch->int1 = bif->tx_hold_count; 788 ret = bridge_set_tx_hold_count(bif, value->v.integer); 789 break; 790 791 case LEAF_dot1dStpProtocolSpecification: 792 case LEAF_dot1dStpTimeSinceTopologyChange: 793 case LEAF_dot1dStpTopChanges: 794 case LEAF_dot1dStpDesignatedRoot: 795 case LEAF_dot1dStpRootCost: 796 case LEAF_dot1dStpRootPort: 797 case LEAF_dot1dStpMaxAge: 798 case LEAF_dot1dStpHelloTime: 799 case LEAF_dot1dStpHoldTime: 800 case LEAF_dot1dStpForwardDelay: 801 return (SNMP_ERR_NOT_WRITEABLE); 802 default: 803 return (SNMP_ERR_NOSUCHNAME); 804 } 805 806 if (ret == -2) 807 return (SNMP_ERR_WRONG_VALUE); 808 else if (ret < 0) 809 return (SNMP_ERR_GENERR); 810 return (SNMP_ERR_NOERROR); 811 812 case SNMP_OP_ROLLBACK: 813 switch (value->var.subs[sub - 1]) { 814 case LEAF_dot1dStpPriority: 815 bridge_set_priority(bif, ctx->scratch->int1); 816 break; 817 case LEAF_dot1dStpBridgeMaxAge: 818 bridge_set_maxage(bif, ctx->scratch->int1); 819 break; 820 case LEAF_dot1dStpBridgeHelloTime: 821 bridge_set_hello_time(bif, ctx->scratch->int1); 822 break; 823 case LEAF_dot1dStpBridgeForwardDelay: 824 bridge_set_forward_delay(bif, ctx->scratch->int1); 825 break; 826 case LEAF_dot1dStpVersion: 827 bridge_set_stp_version(bif, ctx->scratch->int1); 828 break; 829 case LEAF_dot1dStpTxHoldCount: 830 bridge_set_tx_hold_count(bif, ctx->scratch->int1); 831 break; 832 } 833 return (SNMP_ERR_NOERROR); 834 835 case SNMP_OP_COMMIT: 836 return (SNMP_ERR_NOERROR); 837 } 838 839 return (SNMP_ERR_NOERROR); 840 } 841 842 int 843 op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value, 844 uint sub, uint iidx __unused, enum snmp_op op) 845 { 846 struct bridge_if *bif; 847 848 if ((bif = bridge_get_default()) == NULL) 849 return (SNMP_ERR_NOSUCHNAME); 850 851 if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && 852 bridge_update_bif(bif) <= 0) /* It was just deleted. */ 853 return (SNMP_ERR_NOSUCHNAME); 854 855 switch (op) { 856 case SNMP_OP_GET: 857 switch (value->var.subs[sub - 1]) { 858 case LEAF_dot1dTpLearnedEntryDiscards: 859 value->v.uint32 = bif->lrnt_drops; 860 break; 861 case LEAF_dot1dTpAgingTime: 862 value->v.integer = bif->age_time; 863 break; 864 } 865 return (SNMP_ERR_NOERROR); 866 867 case SNMP_OP_GETNEXT: 868 abort(); 869 870 case SNMP_OP_SET: 871 if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) { 872 ctx->scratch->int1 = bif->age_time; 873 if (bridge_set_aging_time(bif, value->v.integer) < 0) 874 return (SNMP_ERR_GENERR); 875 } 876 return (SNMP_ERR_NOERROR); 877 878 case SNMP_OP_ROLLBACK: 879 if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) 880 bridge_set_aging_time(bif, ctx->scratch->int1); 881 return (SNMP_ERR_NOERROR); 882 883 case SNMP_OP_COMMIT: 884 return (SNMP_ERR_NOERROR); 885 } 886 887 return (SNMP_ERR_NOERROR); 888 } 889 890 /* 891 * Private BEGEMOT-BRIDGE-MIB specifics. 892 */ 893 894 /* 895 * Get the bridge name from an OID index. 896 */ 897 static char * 898 bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name) 899 { 900 uint i; 901 902 if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) 903 return (NULL); 904 905 for (i = 0; i < oid->subs[sub]; i++) 906 b_name[i] = oid->subs[sub + i + 1]; 907 b_name[i] = '\0'; 908 909 return (b_name); 910 } 911 912 static void 913 bridge_if_index_append(struct asn_oid *oid, uint sub, 914 const struct bridge_if *bif) 915 { 916 uint i; 917 918 oid->len = sub + strlen(bif->bif_name) + 1; 919 oid->subs[sub] = strlen(bif->bif_name); 920 921 for (i = 1; i <= strlen(bif->bif_name); i++) 922 oid->subs[sub + i] = bif->bif_name[i - 1]; 923 } 924 925 static struct bridge_if * 926 bridge_if_index_get(const struct asn_oid *oid, uint sub) 927 { 928 uint i; 929 char bif_name[IFNAMSIZ]; 930 931 if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) 932 return (NULL); 933 934 for (i = 0; i < oid->subs[sub]; i++) 935 bif_name[i] = oid->subs[sub + i + 1]; 936 bif_name[i] = '\0'; 937 938 return (bridge_if_find_ifname(bif_name)); 939 } 940 941 static struct bridge_if * 942 bridge_if_index_getnext(const struct asn_oid *oid, uint sub) 943 { 944 uint i; 945 char bif_name[IFNAMSIZ]; 946 struct bridge_if *bif; 947 948 if (oid->len - sub == 0) 949 return (bridge_first_bif()); 950 951 if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) 952 return (NULL); 953 954 for (i = 0; i < oid->subs[sub]; i++) 955 bif_name[i] = oid->subs[sub + i + 1]; 956 bif_name[i] = '\0'; 957 958 if ((bif = bridge_if_find_ifname(bif_name)) == NULL) 959 return (NULL); 960 961 return (bridge_next_bif(bif)); 962 } 963 964 static int 965 bridge_set_if_status(struct snmp_context *ctx, 966 struct snmp_value *val, uint sub) 967 { 968 struct bridge_if *bif; 969 char bif_name[IFNAMSIZ]; 970 971 bif = bridge_if_index_get(&val->var, sub); 972 973 switch (val->v.integer) { 974 case RowStatus_active: 975 if (bif == NULL) 976 return (SNMP_ERR_INCONS_VALUE); 977 978 ctx->scratch->int1 = bif->if_status; 979 980 switch (bif->if_status) { 981 case RowStatus_active: 982 return (SNMP_ERR_NOERROR); 983 case RowStatus_notInService: 984 if (bridge_set_if_up(bif->bif_name, 1) < 0) 985 return (SNMP_ERR_GENERR); 986 return (SNMP_ERR_NOERROR); 987 default: 988 break; 989 } 990 return (SNMP_ERR_INCONS_VALUE); 991 992 case RowStatus_notInService: 993 if (bif == NULL) 994 return (SNMP_ERR_INCONS_VALUE); 995 996 ctx->scratch->int1 = bif->if_status; 997 998 switch (bif->if_status) { 999 case RowStatus_active: 1000 if (bridge_set_if_up(bif->bif_name, 1) < 0) 1001 return (SNMP_ERR_GENERR); 1002 return (SNMP_ERR_NOERROR); 1003 case RowStatus_notInService: 1004 return (SNMP_ERR_NOERROR); 1005 default: 1006 break; 1007 } 1008 return (SNMP_ERR_INCONS_VALUE); 1009 1010 case RowStatus_notReady: 1011 return (SNMP_ERR_INCONS_VALUE); 1012 1013 case RowStatus_createAndGo: 1014 if (bif != NULL) 1015 return (SNMP_ERR_INCONS_VALUE); 1016 1017 ctx->scratch->int1 = RowStatus_destroy; 1018 1019 if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) 1020 return (SNMP_ERR_BADVALUE); 1021 if (bridge_if_create(bif_name, 1) < 0) 1022 return (SNMP_ERR_GENERR); 1023 return (SNMP_ERR_NOERROR); 1024 1025 case RowStatus_createAndWait: 1026 if (bif != NULL) 1027 return (SNMP_ERR_INCONS_VALUE); 1028 1029 if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) 1030 return (SNMP_ERR_BADVALUE); 1031 1032 ctx->scratch->int1 = RowStatus_destroy; 1033 1034 if (bridge_if_create(bif_name, 0) < 0) 1035 return (SNMP_ERR_GENERR); 1036 return (SNMP_ERR_NOERROR); 1037 1038 case RowStatus_destroy: 1039 if (bif == NULL) 1040 return (SNMP_ERR_NOSUCHNAME); 1041 1042 ctx->scratch->int1 = bif->if_status; 1043 bif->if_status = RowStatus_destroy; 1044 } 1045 1046 return (SNMP_ERR_NOERROR); 1047 } 1048 1049 static int 1050 bridge_rollback_if_status(struct snmp_context *ctx, 1051 struct snmp_value *val, uint sub) 1052 { 1053 struct bridge_if *bif; 1054 1055 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1056 return (SNMP_ERR_GENERR); 1057 1058 switch (ctx->scratch->int1) { 1059 case RowStatus_destroy: 1060 bridge_if_destroy(bif); 1061 return (SNMP_ERR_NOERROR); 1062 1063 case RowStatus_notInService: 1064 if (bif->if_status != ctx->scratch->int1) 1065 bridge_set_if_up(bif->bif_name, 0); 1066 bif->if_status = RowStatus_notInService; 1067 return (SNMP_ERR_NOERROR); 1068 1069 case RowStatus_active: 1070 if (bif->if_status != ctx->scratch->int1) 1071 bridge_set_if_up(bif->bif_name, 1); 1072 bif->if_status = RowStatus_active; 1073 return (SNMP_ERR_NOERROR); 1074 } 1075 1076 abort(); 1077 } 1078 1079 static int 1080 bridge_commit_if_status(struct snmp_value *val, uint sub) 1081 { 1082 struct bridge_if *bif; 1083 1084 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1085 return (SNMP_ERR_GENERR); 1086 1087 if (bif->if_status == RowStatus_destroy && 1088 bridge_if_destroy(bif) < 0) 1089 return (SNMP_ERR_COMMIT_FAILED); 1090 1091 return (SNMP_ERR_NOERROR); 1092 } 1093 1094 int 1095 op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val, 1096 uint sub, uint iidx __unused, enum snmp_op op) 1097 { 1098 int ret; 1099 struct bridge_if *bif; 1100 1101 if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) 1102 bridge_update_all_ifs(); 1103 1104 switch (op) { 1105 case SNMP_OP_GET: 1106 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1107 return (SNMP_ERR_NOSUCHNAME); 1108 break; 1109 1110 case SNMP_OP_GETNEXT: 1111 if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) 1112 return (SNMP_ERR_NOSUCHNAME); 1113 bridge_if_index_append(&val->var, sub, bif); 1114 break; 1115 1116 case SNMP_OP_SET: 1117 switch (val->var.subs[sub - 1]) { 1118 case LEAF_begemotBridgeBaseStatus: 1119 return (bridge_set_if_status(ctx, val, sub)); 1120 case LEAF_begemotBridgeBaseName: 1121 case LEAF_begemotBridgeBaseAddress: 1122 case LEAF_begemotBridgeBaseNumPorts: 1123 case LEAF_begemotBridgeBaseType: 1124 return (SNMP_ERR_NOT_WRITEABLE); 1125 } 1126 abort(); 1127 1128 case SNMP_OP_ROLLBACK: 1129 return (bridge_rollback_if_status(ctx, val, sub)); 1130 1131 case SNMP_OP_COMMIT: 1132 return (bridge_commit_if_status(val, sub)); 1133 1134 default: 1135 abort(); 1136 } 1137 1138 ret = SNMP_ERR_NOERROR; 1139 switch (val->var.subs[sub - 1]) { 1140 case LEAF_begemotBridgeBaseName: 1141 ret = string_get(val, bif->bif_name, -1); 1142 break; 1143 1144 case LEAF_begemotBridgeBaseAddress: 1145 ret = string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN); 1146 break; 1147 1148 case LEAF_begemotBridgeBaseNumPorts: 1149 val->v.integer = bif->num_ports; 1150 break; 1151 1152 case LEAF_begemotBridgeBaseType: 1153 val->v.integer = bif->br_type; 1154 break; 1155 1156 case LEAF_begemotBridgeBaseStatus: 1157 val->v.integer = bif->if_status; 1158 break; 1159 } 1160 1161 return (ret); 1162 } 1163 1164 int 1165 op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val, 1166 uint sub, uint iidx __unused, enum snmp_op op) 1167 { 1168 int ret; 1169 struct bridge_if *bif; 1170 1171 if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) 1172 bridge_update_all_ifs(); 1173 1174 switch (op) { 1175 case SNMP_OP_GET: 1176 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1177 return (SNMP_ERR_NOSUCHNAME); 1178 break; 1179 1180 case SNMP_OP_GETNEXT: 1181 if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) 1182 return (SNMP_ERR_NOSUCHNAME); 1183 bridge_if_index_append(&val->var, sub, bif); 1184 break; 1185 1186 case SNMP_OP_SET: 1187 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1188 return (SNMP_ERR_NOSUCHNAME); 1189 1190 switch (val->var.subs[sub - 1]) { 1191 case LEAF_begemotBridgeStpPriority: 1192 ctx->scratch->int1 = bif->priority; 1193 ret = bridge_set_priority(bif, val->v.integer); 1194 break; 1195 1196 case LEAF_begemotBridgeStpBridgeMaxAge: 1197 ctx->scratch->int1 = bif->bridge_max_age; 1198 ret = bridge_set_maxage(bif, val->v.integer); 1199 break; 1200 1201 case LEAF_begemotBridgeStpBridgeHelloTime: 1202 ctx->scratch->int1 = bif->bridge_hello_time; 1203 ret = bridge_set_hello_time(bif, val->v.integer); 1204 break; 1205 1206 case LEAF_begemotBridgeStpBridgeForwardDelay: 1207 ctx->scratch->int1 = bif->bridge_fwd_delay; 1208 ret = bridge_set_forward_delay(bif, val->v.integer); 1209 break; 1210 1211 case LEAF_begemotBridgeStpVersion: 1212 ctx->scratch->int1 = bif->stp_version; 1213 ret = bridge_set_stp_version(bif, val->v.integer); 1214 break; 1215 1216 case LEAF_begemotBridgeStpTxHoldCount: 1217 ctx->scratch->int1 = bif->tx_hold_count; 1218 ret = bridge_set_tx_hold_count(bif, val->v.integer); 1219 break; 1220 1221 case LEAF_begemotBridgeStpProtocolSpecification: 1222 case LEAF_begemotBridgeStpTimeSinceTopologyChange: 1223 case LEAF_begemotBridgeStpTopChanges: 1224 case LEAF_begemotBridgeStpDesignatedRoot: 1225 case LEAF_begemotBridgeStpRootCost: 1226 case LEAF_begemotBridgeStpRootPort: 1227 case LEAF_begemotBridgeStpMaxAge: 1228 case LEAF_begemotBridgeStpHelloTime: 1229 case LEAF_begemotBridgeStpHoldTime: 1230 case LEAF_begemotBridgeStpForwardDelay: 1231 return (SNMP_ERR_NOT_WRITEABLE); 1232 1233 default: 1234 return (SNMP_ERR_NOSUCHNAME); 1235 } 1236 1237 if (ret == 0) 1238 return (SNMP_ERR_NOERROR); 1239 else if (ret == -2) 1240 return (SNMP_ERR_WRONG_VALUE); 1241 return (SNMP_ERR_GENERR); 1242 1243 case SNMP_OP_ROLLBACK: 1244 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1245 return (SNMP_ERR_NOSUCHNAME); 1246 1247 switch (val->var.subs[sub - 1]) { 1248 case LEAF_begemotBridgeStpPriority: 1249 bridge_set_priority(bif, ctx->scratch->int1); 1250 break; 1251 1252 case LEAF_begemotBridgeStpBridgeMaxAge: 1253 bridge_set_maxage(bif, ctx->scratch->int1); 1254 break; 1255 1256 case LEAF_begemotBridgeStpBridgeHelloTime: 1257 bridge_set_hello_time(bif, ctx->scratch->int1); 1258 break; 1259 1260 case LEAF_begemotBridgeStpBridgeForwardDelay: 1261 bridge_set_forward_delay(bif, ctx->scratch->int1); 1262 break; 1263 1264 case LEAF_begemotBridgeStpVersion: 1265 bridge_set_stp_version(bif, ctx->scratch->int1); 1266 break; 1267 1268 case LEAF_begemotBridgeStpTxHoldCount: 1269 bridge_set_tx_hold_count(bif, ctx->scratch->int1); 1270 break; 1271 } 1272 return (SNMP_ERR_NOERROR); 1273 1274 case SNMP_OP_COMMIT: 1275 return (SNMP_ERR_NOERROR); 1276 1277 default: 1278 abort(); 1279 } 1280 1281 ret = SNMP_ERR_NOERROR; 1282 1283 switch (val->var.subs[sub - 1]) { 1284 case LEAF_begemotBridgeStpProtocolSpecification: 1285 val->v.integer = bif->prot_spec; 1286 break; 1287 1288 case LEAF_begemotBridgeStpPriority: 1289 val->v.integer = bif->priority; 1290 break; 1291 1292 case LEAF_begemotBridgeStpTimeSinceTopologyChange: 1293 if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) 1294 return (SNMP_ERR_GENERR); 1295 break; 1296 1297 case LEAF_begemotBridgeStpTopChanges: 1298 val->v.uint32 = bif->top_changes; 1299 break; 1300 1301 case LEAF_begemotBridgeStpDesignatedRoot: 1302 ret = string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN); 1303 break; 1304 1305 case LEAF_begemotBridgeStpRootCost: 1306 val->v.integer = bif->root_cost; 1307 break; 1308 1309 case LEAF_begemotBridgeStpRootPort: 1310 val->v.integer = bif->root_port; 1311 break; 1312 1313 case LEAF_begemotBridgeStpMaxAge: 1314 val->v.integer = bif->max_age; 1315 break; 1316 1317 case LEAF_begemotBridgeStpHelloTime: 1318 val->v.integer = bif->hello_time; 1319 break; 1320 1321 case LEAF_begemotBridgeStpHoldTime: 1322 val->v.integer = bif->hold_time; 1323 break; 1324 1325 case LEAF_begemotBridgeStpForwardDelay: 1326 val->v.integer = bif->fwd_delay; 1327 break; 1328 1329 case LEAF_begemotBridgeStpBridgeMaxAge: 1330 val->v.integer = bif->bridge_max_age; 1331 break; 1332 1333 case LEAF_begemotBridgeStpBridgeHelloTime: 1334 val->v.integer = bif->bridge_hello_time; 1335 break; 1336 1337 case LEAF_begemotBridgeStpBridgeForwardDelay: 1338 val->v.integer = bif->bridge_fwd_delay; 1339 break; 1340 1341 case LEAF_begemotBridgeStpVersion: 1342 val->v.integer = bif->stp_version; 1343 break; 1344 1345 case LEAF_begemotBridgeStpTxHoldCount: 1346 val->v.integer = bif->tx_hold_count; 1347 break; 1348 } 1349 1350 return (ret); 1351 } 1352 1353 int 1354 op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val, 1355 uint sub, uint iidx __unused, enum snmp_op op) 1356 { 1357 struct bridge_if *bif; 1358 1359 if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) 1360 bridge_update_all_ifs(); 1361 1362 switch (op) { 1363 case SNMP_OP_GET: 1364 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1365 return (SNMP_ERR_NOSUCHNAME); 1366 break; 1367 1368 case SNMP_OP_GETNEXT: 1369 if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) 1370 return (SNMP_ERR_NOSUCHNAME); 1371 bridge_if_index_append(&val->var, sub, bif); 1372 break; 1373 1374 case SNMP_OP_SET: 1375 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1376 return (SNMP_ERR_NOSUCHNAME); 1377 1378 switch (val->var.subs[sub - 1]) { 1379 case LEAF_begemotBridgeTpAgingTime: 1380 ctx->scratch->int1 = bif->age_time; 1381 if (bridge_set_aging_time(bif, val->v.integer) < 0) 1382 return (SNMP_ERR_GENERR); 1383 return (SNMP_ERR_NOERROR); 1384 1385 case LEAF_begemotBridgeTpMaxAddresses: 1386 ctx->scratch->int1 = bif->max_addrs; 1387 if (bridge_set_max_cache(bif, val->v.integer) < 0) 1388 return (SNMP_ERR_GENERR); 1389 return (SNMP_ERR_NOERROR); 1390 1391 case LEAF_begemotBridgeTpLearnedEntryDiscards: 1392 return (SNMP_ERR_NOT_WRITEABLE); 1393 } 1394 abort(); 1395 1396 case SNMP_OP_ROLLBACK: 1397 if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) 1398 return (SNMP_ERR_GENERR); 1399 1400 switch (val->var.subs[sub - 1]) { 1401 case LEAF_begemotBridgeTpAgingTime: 1402 bridge_set_aging_time(bif, ctx->scratch->int1); 1403 break; 1404 1405 case LEAF_begemotBridgeTpMaxAddresses: 1406 bridge_set_max_cache(bif, ctx->scratch->int1); 1407 break; 1408 } 1409 return (SNMP_ERR_NOERROR); 1410 1411 case SNMP_OP_COMMIT: 1412 return (SNMP_ERR_NOERROR); 1413 1414 default: 1415 abort(); 1416 } 1417 1418 switch (val->var.subs[sub - 1]) { 1419 case LEAF_begemotBridgeTpLearnedEntryDiscards: 1420 val->v.uint32 = bif->lrnt_drops; 1421 break; 1422 1423 case LEAF_begemotBridgeTpAgingTime: 1424 val->v.integer = bif->age_time; 1425 break; 1426 1427 case LEAF_begemotBridgeTpMaxAddresses: 1428 val->v.integer = bif->max_addrs; 1429 break; 1430 } 1431 1432 return (SNMP_ERR_NOERROR); 1433 } 1434