1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * Bridge MIB implementation for SNMPd. 29 * Bridge addresses. 30 */ 31 32 #include <sys/queue.h> 33 #include <sys/socket.h> 34 #include <sys/types.h> 35 36 #include <net/ethernet.h> 37 #include <net/if.h> 38 #include <net/if_mib.h> 39 40 #include <assert.h> 41 #include <errno.h> 42 #include <stdarg.h> 43 #include <string.h> 44 #include <stdlib.h> 45 #include <syslog.h> 46 47 #include <bsnmp/snmpmod.h> 48 #include <bsnmp/snmp_mibII.h> 49 50 #define SNMPTREE_TYPES 51 #include "bridge_tree.h" 52 #include "bridge_snmp.h" 53 54 TAILQ_HEAD(tp_entries, tp_entry); 55 56 /* 57 * Free the bridge address list. 58 */ 59 static void 60 bridge_tpe_free(struct tp_entries *headp) 61 { 62 struct tp_entry *t; 63 64 while ((t = TAILQ_FIRST(headp)) != NULL) { 65 TAILQ_REMOVE(headp, t, tp_e); 66 free(t); 67 } 68 } 69 70 /* 71 * Free the bridge address entries from the address list, 72 * for the specified bridge interface only. 73 */ 74 static void 75 bridge_tpe_bif_free(struct tp_entries *headp, 76 struct bridge_if *bif) 77 { 78 struct tp_entry *tp; 79 80 while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) { 81 tp = TAILQ_NEXT(bif->f_tpa, tp_e); 82 TAILQ_REMOVE(headp, bif->f_tpa, tp_e); 83 free(bif->f_tpa); 84 bif->f_tpa = tp; 85 } 86 } 87 88 /* 89 * Compare two mac addresses. 90 * m1 < m2 : -1 91 * m1 > m2 : +1 92 * m1 = m2 : 0 93 */ 94 static int 95 bridge_compare_macs(const uint8_t *m1, const uint8_t *m2) 96 { 97 int i; 98 99 for (i = 0; i < ETHER_ADDR_LEN; i++) { 100 if (m1[i] < m2[i]) 101 return (-1); 102 if (m1[i] > m2[i]) 103 return (1); 104 } 105 106 return (0); 107 } 108 109 /* 110 * Insert an address entry in the bridge address TAILQ starting to search 111 * for its place from the position of the first bridge address for the bridge 112 * interface. Update the first bridge address if necessary. 113 */ 114 static void 115 bridge_addrs_insert_at(struct tp_entries *headp, 116 struct tp_entry *ta, struct tp_entry **f_tpa) 117 { 118 struct tp_entry *t1; 119 120 assert(f_tpa != NULL); 121 122 for (t1 = *f_tpa; 123 t1 != NULL && ta->sysindex == t1->sysindex; 124 t1 = TAILQ_NEXT(t1, tp_e)) { 125 if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) { 126 TAILQ_INSERT_BEFORE(t1, ta, tp_e); 127 if (*f_tpa == t1) 128 (*f_tpa) = ta; 129 return; 130 } 131 } 132 133 if (t1 == NULL) 134 TAILQ_INSERT_TAIL(headp, ta, tp_e); 135 else 136 TAILQ_INSERT_BEFORE(t1, ta, tp_e); 137 } 138 139 /* 140 * Find an address entry's position in the address list 141 * according to bridge interface name. 142 */ 143 static struct tp_entry * 144 bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx) 145 { 146 uint32_t t_idx; 147 struct tp_entry *t1; 148 149 if ((t1 = TAILQ_FIRST(headp)) == NULL || 150 bridge_compare_sysidx(b_idx, t1->sysindex) < 0) 151 return (NULL); 152 153 t_idx = t1->sysindex; 154 155 for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) { 156 157 if (t1->sysindex != t_idx) { 158 if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) 159 return (TAILQ_PREV(t1, tp_entries, tp_e)); 160 else 161 t_idx = t1->sysindex; 162 } 163 } 164 165 if (t1 == NULL) 166 t1 = TAILQ_LAST(headp, tp_entries); 167 168 return (t1); 169 } 170 171 /* 172 * Insert a bridge address in the bridge addresses list. 173 */ 174 static void 175 bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te, 176 struct tp_entry **f_tpa) 177 { 178 struct tp_entry *temp; 179 180 if (*f_tpa != NULL) 181 bridge_addrs_insert_at(headp, te, f_tpa); 182 else { 183 temp = bridge_addrs_find_pos(headp, te->sysindex); 184 185 if (temp == NULL) 186 TAILQ_INSERT_HEAD(headp, te, tp_e); 187 else 188 TAILQ_INSERT_AFTER(headp, temp, te, tp_e); 189 *f_tpa = te; 190 } 191 } 192 193 static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries); 194 static time_t address_list_age; 195 196 void 197 bridge_addrs_update_listage(void) 198 { 199 address_list_age = time(NULL); 200 } 201 202 void 203 bridge_addrs_fini(void) 204 { 205 bridge_tpe_free(&tp_entries); 206 } 207 208 void 209 bridge_addrs_free(struct bridge_if *bif) 210 { 211 bridge_tpe_bif_free(&tp_entries, bif); 212 } 213 214 /* 215 * Find the first address in the list. 216 */ 217 static struct tp_entry * 218 bridge_addrs_first(void) 219 { 220 return (TAILQ_FIRST(&tp_entries)); 221 } 222 223 /* 224 * Find the next address in the list. 225 */ 226 static struct tp_entry * 227 bridge_addrs_next(struct tp_entry *te) 228 { 229 return (TAILQ_NEXT(te, tp_e)); 230 } 231 232 /* 233 * Find the first address, learnt by the specified bridge interface. 234 */ 235 struct tp_entry * 236 bridge_addrs_bif_first(struct bridge_if *bif) 237 { 238 return (bif->f_tpa); 239 } 240 241 /* 242 * Find the next address, learnt by the specified bridge interface. 243 */ 244 struct tp_entry * 245 bridge_addrs_bif_next(struct tp_entry *te) 246 { 247 struct tp_entry *te_next; 248 249 if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL || 250 te_next->sysindex != te->sysindex) 251 return (NULL); 252 253 return (te_next); 254 } 255 256 /* 257 * Remove a bridge address from the list. 258 */ 259 void 260 bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif) 261 { 262 if (bif->f_tpa == te) 263 bif->f_tpa = bridge_addrs_bif_next(te); 264 265 TAILQ_REMOVE(&tp_entries, te, tp_e); 266 free(te); 267 } 268 269 /* 270 * Allocate memory for a new bridge address and insert it in the list. 271 */ 272 struct tp_entry * 273 bridge_new_addrs(uint8_t *mac, struct bridge_if *bif) 274 { 275 struct tp_entry *te; 276 277 if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) { 278 syslog(LOG_ERR, "bridge new address: failed: %s", 279 strerror(errno)); 280 return (NULL); 281 } 282 283 bzero(te, sizeof(*te)); 284 285 te->sysindex = bif->sysindex; 286 bcopy(mac, te->tp_addr, ETHER_ADDR_LEN); 287 bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa)); 288 289 return (te); 290 } 291 292 /* 293 * Given a mac address, learnt on a bridge, 294 * find the corrsponding TP entry for it. 295 */ 296 struct tp_entry * 297 bridge_addrs_find(uint8_t *mac, struct bridge_if *bif) 298 { 299 struct tp_entry *te; 300 301 for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) { 302 if (te->sysindex != bif->sysindex) { 303 te = NULL; 304 break; 305 } 306 307 if (bridge_compare_macs(te->tp_addr, mac) == 0) 308 break; 309 } 310 311 return (te); 312 } 313 314 void 315 bridge_addrs_dump(struct bridge_if *bif) 316 { 317 struct tp_entry *te; 318 319 syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs); 320 for (te = bridge_addrs_bif_first(bif); te != NULL; 321 te = bridge_addrs_bif_next(te)) { 322 syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d", 323 te->tp_addr[0], te->tp_addr[1], te->tp_addr[2], 324 te->tp_addr[3], te->tp_addr[4], te->tp_addr[5], 325 te->sysindex, te->port_no); 326 } 327 } 328 329 /* 330 * RFC4188 specifics. 331 */ 332 333 /* 334 * Construct the SNMP index from the address DST Mac. 335 */ 336 static void 337 bridge_addrs_index_append(struct asn_oid *oid, uint sub, 338 const struct tp_entry *te) 339 { 340 int i; 341 342 oid->len = sub + ETHER_ADDR_LEN + 1; 343 oid->subs[sub] = ETHER_ADDR_LEN; 344 345 for (i = 1; i <= ETHER_ADDR_LEN; i++) 346 oid->subs[sub + i] = te->tp_addr[i - 1]; 347 } 348 349 /* 350 * Find the address entry for the SNMP index from the default bridge only. 351 */ 352 static struct tp_entry * 353 bridge_addrs_get(const struct asn_oid *oid, uint sub, 354 struct bridge_if *bif) 355 { 356 int i; 357 uint8_t tp_addr[ETHER_ADDR_LEN]; 358 359 if (oid->len - sub != ETHER_ADDR_LEN + 1 || 360 oid->subs[sub] != ETHER_ADDR_LEN) 361 return (NULL); 362 363 for (i = 0; i < ETHER_ADDR_LEN; i++) 364 tp_addr[i] = oid->subs[sub + i + 1]; 365 366 return (bridge_addrs_find(tp_addr, bif)); 367 } 368 369 /* 370 * Find the next address entry for the SNMP index 371 * from the default bridge only. 372 */ 373 static struct tp_entry * 374 bridge_addrs_getnext(const struct asn_oid *oid, uint sub, 375 struct bridge_if *bif) 376 { 377 int i; 378 uint8_t tp_addr[ETHER_ADDR_LEN]; 379 static struct tp_entry *te; 380 381 if (oid->len - sub == 0) 382 return (bridge_addrs_bif_first(bif)); 383 384 if (oid->len - sub != ETHER_ADDR_LEN + 1 || 385 oid->subs[sub] != ETHER_ADDR_LEN) 386 return (NULL); 387 388 for (i = 0; i < ETHER_ADDR_LEN; i++) 389 tp_addr[i] = oid->subs[sub + i + 1]; 390 391 if ((te = bridge_addrs_find(tp_addr, bif)) == NULL) 392 return (NULL); 393 394 return (bridge_addrs_bif_next(te)); 395 } 396 397 int 398 op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, 399 uint sub, uint iidx __unused, enum snmp_op op) 400 { 401 struct bridge_if *bif; 402 struct tp_entry *te; 403 404 if ((bif = bridge_get_default()) == NULL) 405 return (SNMP_ERR_NOSUCHNAME); 406 407 if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() && 408 bridge_update_addrs(bif) <= 0) 409 return (SNMP_ERR_NOSUCHNAME); 410 411 switch (op) { 412 case SNMP_OP_GET: 413 if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL) 414 return (SNMP_ERR_NOSUCHNAME); 415 goto get; 416 417 case SNMP_OP_GETNEXT: 418 if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL) 419 return (SNMP_ERR_NOSUCHNAME); 420 bridge_addrs_index_append(&val->var, sub, te); 421 goto get; 422 423 case SNMP_OP_SET: 424 return (SNMP_ERR_NOT_WRITEABLE); 425 426 case SNMP_OP_ROLLBACK: 427 case SNMP_OP_COMMIT: 428 break; 429 } 430 abort(); 431 432 get: 433 switch (val->var.subs[sub - 1]) { 434 case LEAF_dot1dTpFdbAddress: 435 return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); 436 case LEAF_dot1dTpFdbPort : 437 val->v.integer = te->port_no; 438 return (SNMP_ERR_NOERROR); 439 case LEAF_dot1dTpFdbStatus: 440 val->v.integer = te->status; 441 return (SNMP_ERR_NOERROR); 442 } 443 444 abort(); 445 } 446 447 /* 448 * Private BEGEMOT-BRIDGE-MIB specifics. 449 */ 450 451 /* 452 * Construct the SNMP index from the bridge interface name 453 * and the address DST Mac. 454 */ 455 static int 456 bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub, 457 const struct tp_entry *te) 458 { 459 uint i, n_len; 460 const char *b_name; 461 462 if ((b_name = bridge_if_find_name(te->sysindex)) == NULL) 463 return (-1); 464 465 n_len = strlen(b_name); 466 oid->len = sub++; 467 oid->subs[oid->len++] = n_len; 468 469 for (i = 1; i <= n_len; i++) 470 oid->subs[oid->len++] = b_name[i - 1]; 471 472 oid->subs[oid->len++] = ETHER_ADDR_LEN; 473 for (i = 1 ; i <= ETHER_ADDR_LEN; i++) 474 oid->subs[oid->len++] = te->tp_addr[i - 1]; 475 476 return (0); 477 } 478 479 /* 480 * Find a bridge address entry by the bridge interface name 481 * and the address DST Mac. 482 */ 483 static struct tp_entry * 484 bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub) 485 { 486 uint i, n_len; 487 uint8_t tp_addr[ETHER_ADDR_LEN]; 488 char bif_name[IFNAMSIZ]; 489 struct bridge_if *bif; 490 491 n_len = oid->subs[sub]; 492 if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 || 493 n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) 494 return (NULL); 495 496 for (i = 0; i < n_len; i++) 497 bif_name[i] = oid->subs[n_len + i + 1]; 498 bif_name[i] = '\0'; 499 500 for (i = 1; i <= ETHER_ADDR_LEN; i++) 501 tp_addr[i - 1] = oid->subs[n_len + i + 1]; 502 503 if ((bif = bridge_if_find_ifname(bif_name)) == NULL) 504 return (NULL); 505 506 return (bridge_addrs_find(tp_addr, bif)); 507 } 508 509 /* 510 * Find the next bridge address entry by the bridge interface name 511 * and the address DST Mac. 512 */ 513 static struct tp_entry * 514 bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub) 515 { 516 uint i, n_len; 517 uint8_t tp_addr[ETHER_ADDR_LEN]; 518 char bif_name[IFNAMSIZ]; 519 struct bridge_if *bif; 520 struct tp_entry *tp; 521 522 if (oid->len - sub == 0) 523 return (bridge_addrs_first()); 524 525 n_len = oid->subs[sub]; 526 if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 || 527 n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) 528 return (NULL); 529 530 for (i = 1; i <= n_len; i++) 531 bif_name[i - 1] = oid->subs[sub + i]; 532 533 bif_name[i - 1] = '\0'; 534 535 for (i = 1; i <= ETHER_ADDR_LEN; i++) 536 tp_addr[i - 1] = oid->subs[sub + n_len + i + 1]; 537 538 if ((bif = bridge_if_find_ifname(bif_name)) == NULL || 539 (tp = bridge_addrs_find(tp_addr, bif)) == NULL) 540 return (NULL); 541 542 return (bridge_addrs_next(tp)); 543 } 544 545 int 546 op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, 547 uint sub, uint iidx __unused, enum snmp_op op) 548 { 549 struct tp_entry *te; 550 551 if (time(NULL) - address_list_age > bridge_get_data_maxage()) 552 bridge_update_all_addrs(); 553 554 switch (op) { 555 case SNMP_OP_GET: 556 if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL) 557 return (SNMP_ERR_NOSUCHNAME); 558 goto get; 559 560 case SNMP_OP_GETNEXT: 561 if ((te = bridge_addrs_begemot_getnext(&val->var, 562 sub)) == NULL || 563 bridge_addrs_begemot_index_append(&val->var, 564 sub, te) < 0) 565 return (SNMP_ERR_NOSUCHNAME); 566 goto get; 567 568 case SNMP_OP_SET: 569 return (SNMP_ERR_NOT_WRITEABLE); 570 571 case SNMP_OP_ROLLBACK: 572 case SNMP_OP_COMMIT: 573 break; 574 } 575 abort(); 576 577 get: 578 switch (val->var.subs[sub - 1]) { 579 case LEAF_begemotBridgeTpFdbAddress: 580 return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); 581 case LEAF_begemotBridgeTpFdbPort: 582 val->v.integer = te->port_no; 583 return (SNMP_ERR_NOERROR); 584 case LEAF_begemotBridgeTpFdbStatus: 585 val->v.integer = te->status; 586 return (SNMP_ERR_NOERROR); 587 } 588 589 abort(); 590 } 591