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