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