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