1/* $OpenBSD: if_etherbridge.c,v 1.7 2021/07/05 04:17:41 dlg Exp $ */ 2 3/* 4 * Copyright (c) 2018, 2021 David Gwynne <dlg@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include "bpfilter.h" 20 21#include <sys/param.h> 22#include <sys/systm.h> 23#include <sys/kernel.h> 24#include <sys/mbuf.h> 25#include <sys/socket.h> 26#include <sys/ioctl.h> 27#include <sys/timeout.h> 28#include <sys/pool.h> 29#include <sys/tree.h> 30 31#include <net/if.h> 32#include <net/if_var.h> 33#include <net/if_dl.h> 34#include <net/if_media.h> 35#include <net/if_types.h> 36#include <net/rtable.h> 37#include <net/toeplitz.h> 38 39#include <netinet/in.h> 40#include <netinet/if_ether.h> 41 42/* for bridge stuff */ 43#include <net/if_bridge.h> 44 45#include <net/if_etherbridge.h> 46 47static inline void ebe_rele(struct eb_entry *); 48static void ebe_free(void *); 49 50static void etherbridge_age(void *); 51 52RBT_PROTOTYPE(eb_tree, eb_entry, ebe_tentry, ebt_cmp); 53 54static struct pool eb_entry_pool; 55 56static inline int 57eb_port_eq(struct etherbridge *eb, void *a, void *b) 58{ 59 return ((*eb->eb_ops->eb_op_port_eq)(eb->eb_cookie, a, b)); 60} 61 62static inline void * 63eb_port_take(struct etherbridge *eb, void *port) 64{ 65 return ((*eb->eb_ops->eb_op_port_take)(eb->eb_cookie, port)); 66} 67 68static inline void 69eb_port_rele(struct etherbridge *eb, void *port) 70{ 71 return ((*eb->eb_ops->eb_op_port_rele)(eb->eb_cookie, port)); 72} 73 74static inline size_t 75eb_port_ifname(struct etherbridge *eb, char *dst, size_t len, void *port) 76{ 77 return ((*eb->eb_ops->eb_op_port_ifname)(eb->eb_cookie, dst, len, 78 port)); 79} 80 81static inline void 82eb_port_sa(struct etherbridge *eb, struct sockaddr_storage *ss, void *port) 83{ 84 (*eb->eb_ops->eb_op_port_sa)(eb->eb_cookie, ss, port); 85} 86 87int 88etherbridge_init(struct etherbridge *eb, const char *name, 89 const struct etherbridge_ops *ops, void *cookie) 90{ 91 size_t i; 92 93 if (eb_entry_pool.pr_size == 0) { 94 pool_init(&eb_entry_pool, sizeof(struct eb_entry), 95 0, IPL_SOFTNET, 0, "ebepl", NULL); 96 } 97 98 eb->eb_table = mallocarray(ETHERBRIDGE_TABLE_SIZE, 99 sizeof(*eb->eb_table), M_DEVBUF, M_WAITOK|M_CANFAIL); 100 if (eb->eb_table == NULL) 101 return (ENOMEM); 102 103 eb->eb_name = name; 104 eb->eb_ops = ops; 105 eb->eb_cookie = cookie; 106 107 mtx_init(&eb->eb_lock, IPL_SOFTNET); 108 RBT_INIT(eb_tree, &eb->eb_tree); 109 110 eb->eb_num = 0; 111 eb->eb_max = 100; 112 eb->eb_max_age = 240; 113 timeout_set(&eb->eb_tmo_age, etherbridge_age, eb); 114 115 for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) { 116 struct eb_list *ebl = &eb->eb_table[i]; 117 SMR_TAILQ_INIT(ebl); 118 } 119 120 return (0); 121} 122 123int 124etherbridge_up(struct etherbridge *eb) 125{ 126 etherbridge_age(eb); 127 128 return (0); 129} 130 131int 132etherbridge_down(struct etherbridge *eb) 133{ 134 smr_barrier(); 135 136 return (0); 137} 138 139void 140etherbridge_destroy(struct etherbridge *eb) 141{ 142 struct eb_entry *ebe, *nebe; 143 144 /* XXX assume that nothing will calling etherbridge_map now */ 145 146 timeout_del_barrier(&eb->eb_tmo_age); 147 148 free(eb->eb_table, M_DEVBUF, 149 ETHERBRIDGE_TABLE_SIZE * sizeof(*eb->eb_table)); 150 151 RBT_FOREACH_SAFE(ebe, eb_tree, &eb->eb_tree, nebe) { 152 RBT_REMOVE(eb_tree, &eb->eb_tree, ebe); 153 ebe_free(ebe); 154 } 155} 156 157static struct eb_list * 158etherbridge_list(struct etherbridge *eb, uint64_t eba) 159{ 160 uint16_t hash; 161 162 hash = stoeplitz_h64(eba) & ETHERBRIDGE_TABLE_MASK; 163 164 return (&eb->eb_table[hash]); 165} 166 167static struct eb_entry * 168ebl_find(struct eb_list *ebl, uint64_t eba) 169{ 170 struct eb_entry *ebe; 171 172 SMR_TAILQ_FOREACH(ebe, ebl, ebe_lentry) { 173 if (ebe->ebe_addr == eba) 174 return (ebe); 175 } 176 177 return (NULL); 178} 179 180static inline void 181ebl_insert(struct eb_list *ebl, struct eb_entry *ebe) 182{ 183 SMR_TAILQ_INSERT_TAIL_LOCKED(ebl, ebe, ebe_lentry); 184} 185 186static inline void 187ebl_remove(struct eb_list *ebl, struct eb_entry *ebe) 188{ 189 SMR_TAILQ_REMOVE_LOCKED(ebl, ebe, ebe_lentry); 190} 191 192static inline int 193ebt_cmp(const struct eb_entry *aebe, const struct eb_entry *bebe) 194{ 195 if (aebe->ebe_addr > bebe->ebe_addr) 196 return (1); 197 if (aebe->ebe_addr < bebe->ebe_addr) 198 return (-1); 199 return (0); 200} 201 202RBT_GENERATE(eb_tree, eb_entry, ebe_tentry, ebt_cmp); 203 204static inline struct eb_entry * 205ebt_insert(struct etherbridge *eb, struct eb_entry *ebe) 206{ 207 return (RBT_INSERT(eb_tree, &eb->eb_tree, ebe)); 208} 209 210static inline struct eb_entry * 211ebt_find(struct etherbridge *eb, const struct eb_entry *ebe) 212{ 213 return (RBT_FIND(eb_tree, &eb->eb_tree, ebe)); 214} 215 216static inline void 217ebt_replace(struct etherbridge *eb, struct eb_entry *oebe, 218 struct eb_entry *nebe) 219{ 220 struct eb_entry *rvebe; 221 222 RBT_REMOVE(eb_tree, &eb->eb_tree, oebe); 223 rvebe = RBT_INSERT(eb_tree, &eb->eb_tree, nebe); 224 KASSERTMSG(rvebe == NULL, "ebt_replace eb %p nebe %p rvebe %p", 225 eb, nebe, rvebe); 226} 227 228static inline void 229ebt_remove(struct etherbridge *eb, struct eb_entry *ebe) 230{ 231 RBT_REMOVE(eb_tree, &eb->eb_tree, ebe); 232} 233 234static inline void 235ebe_rele(struct eb_entry *ebe) 236{ 237 smr_call(&ebe->ebe_smr_entry, ebe_free, ebe); 238} 239 240static void 241ebe_free(void *arg) 242{ 243 struct eb_entry *ebe = arg; 244 struct etherbridge *eb = ebe->ebe_etherbridge; 245 246 eb_port_rele(eb, ebe->ebe_port); 247 pool_put(&eb_entry_pool, ebe); 248} 249 250void * 251etherbridge_resolve_ea(struct etherbridge *eb, 252 const struct ether_addr *ea) 253{ 254 return (etherbridge_resolve(eb, ether_addr_to_e64(ea))); 255} 256 257void * 258etherbridge_resolve(struct etherbridge *eb, uint64_t eba) 259{ 260 struct eb_list *ebl = etherbridge_list(eb, eba); 261 struct eb_entry *ebe; 262 263 SMR_ASSERT_CRITICAL(); 264 265 ebe = ebl_find(ebl, eba); 266 if (ebe != NULL) { 267 if (ebe->ebe_type == EBE_DYNAMIC) { 268 int diff = getuptime() - ebe->ebe_age; 269 if (diff > eb->eb_max_age) 270 return (NULL); 271 } 272 273 return (ebe->ebe_port); 274 } 275 276 return (NULL); 277} 278 279void 280etherbridge_map_ea(struct etherbridge *eb, void *port, 281 const struct ether_addr *ea) 282{ 283 etherbridge_map(eb, port, ether_addr_to_e64(ea)); 284} 285 286void 287etherbridge_map(struct etherbridge *eb, void *port, uint64_t eba) 288{ 289 struct eb_list *ebl; 290 struct eb_entry *oebe, *nebe; 291 unsigned int num; 292 void *nport; 293 int new = 0; 294 time_t now; 295 296 if (ETH64_IS_MULTICAST(eba) || ETH64_IS_ANYADDR(eba)) 297 return; 298 299 now = getuptime(); 300 ebl = etherbridge_list(eb, eba); 301 302 smr_read_enter(); 303 oebe = ebl_find(ebl, eba); 304 if (oebe == NULL) { 305 /* 306 * peek at the space to see if it's worth trying 307 * to make a new entry. 308 */ 309 if (eb->eb_num < eb->eb_max) 310 new = 1; 311 } else { 312 if (oebe->ebe_age != now) 313 oebe->ebe_age = now; 314 315 /* does this entry need to be replaced? */ 316 if (oebe->ebe_type == EBE_DYNAMIC && 317 !eb_port_eq(eb, oebe->ebe_port, port)) 318 new = 1; 319 } 320 smr_read_leave(); 321 322 if (!new) 323 return; 324 325 nport = eb_port_take(eb, port); 326 if (nport == NULL) { 327 /* XXX should we remove the old one and flood? */ 328 return; 329 } 330 331 nebe = pool_get(&eb_entry_pool, PR_NOWAIT); 332 if (nebe == NULL) { 333 /* XXX should we remove the old one and flood? */ 334 eb_port_rele(eb, nport); 335 return; 336 } 337 338 smr_init(&nebe->ebe_smr_entry); 339 nebe->ebe_etherbridge = eb; 340 341 nebe->ebe_addr = eba; 342 nebe->ebe_port = nport; 343 nebe->ebe_type = EBE_DYNAMIC; 344 nebe->ebe_age = now; 345 346 mtx_enter(&eb->eb_lock); 347 oebe = ebt_find(eb, nebe); 348 if (oebe == NULL) { 349 num = eb->eb_num + 1; 350 if (num <= eb->eb_max) { 351 ebl_insert(ebl, nebe); 352 353 oebe = ebt_insert(eb, nebe); 354 if (oebe != NULL) { 355 panic("etherbridge %p changed while locked", 356 eb); 357 } 358 359 /* great success */ 360 eb->eb_num = num; 361 nebe = NULL; /* give ref to table */ 362 } 363 } else if (oebe->ebe_type == EBE_DYNAMIC) { 364 /* do the update */ 365 ebl_insert(ebl, nebe); 366 367 ebl_remove(ebl, oebe); 368 ebt_replace(eb, oebe, nebe); 369 370 nebe = NULL; /* give ref to table */ 371 } else { 372 /* 373 * oebe is not a dynamic entry, so don't replace it. 374 */ 375 oebe = NULL; 376 } 377 mtx_leave(&eb->eb_lock); 378 379 if (nebe != NULL) { 380 /* 381 * the new entry didn't make it into the 382 * table so it can be freed directly. 383 */ 384 ebe_free(nebe); 385 } 386 387 if (oebe != NULL) { 388 /* 389 * we replaced this entry, it needs to be released. 390 */ 391 ebe_rele(oebe); 392 } 393} 394 395int 396etherbridge_add_addr(struct etherbridge *eb, void *port, 397 const struct ether_addr *ea, unsigned int type) 398{ 399 uint64_t eba = ether_addr_to_e64(ea); 400 struct eb_list *ebl; 401 struct eb_entry *nebe; 402 unsigned int num; 403 void *nport; 404 int error = 0; 405 406 if (ETH64_IS_MULTICAST(eba) || ETH64_IS_ANYADDR(eba)) 407 return (EADDRNOTAVAIL); 408 409 nport = eb_port_take(eb, port); 410 if (nport == NULL) 411 return (ENOMEM); 412 413 nebe = pool_get(&eb_entry_pool, PR_NOWAIT); 414 if (nebe == NULL) { 415 eb_port_rele(eb, nport); 416 return (ENOMEM); 417 } 418 419 smr_init(&nebe->ebe_smr_entry); 420 nebe->ebe_etherbridge = eb; 421 422 nebe->ebe_addr = eba; 423 nebe->ebe_port = nport; 424 nebe->ebe_type = type; 425 nebe->ebe_age = getuptime(); 426 427 ebl = etherbridge_list(eb, eba); 428 429 mtx_enter(&eb->eb_lock); 430 num = eb->eb_num + 1; 431 if (num >= eb->eb_max) 432 error = ENOSPC; 433 else if (ebt_insert(eb, nebe) != NULL) 434 error = EADDRINUSE; 435 else { 436 /* we win, do the insert */ 437 ebl_insert(ebl, nebe); /* give the ref to etherbridge */ 438 eb->eb_num = num; 439 } 440 mtx_leave(&eb->eb_lock); 441 442 if (error != 0) { 443 /* 444 * the new entry didn't make it into the 445 * table, so it can be freed directly. 446 */ 447 ebe_free(nebe); 448 } 449 450 return (error); 451} 452int 453etherbridge_del_addr(struct etherbridge *eb, const struct ether_addr *ea) 454{ 455 uint64_t eba = ether_addr_to_e64(ea); 456 struct eb_list *ebl; 457 struct eb_entry *oebe; 458 const struct eb_entry key = { 459 .ebe_addr = eba, 460 }; 461 int error = 0; 462 463 ebl = etherbridge_list(eb, eba); 464 465 mtx_enter(&eb->eb_lock); 466 oebe = ebt_find(eb, &key); 467 if (oebe == NULL) 468 error = ESRCH; 469 else { 470 KASSERT(eb->eb_num > 0); 471 eb->eb_num--; 472 473 ebl_remove(ebl, oebe); /* it's our ref now */ 474 ebt_remove(eb, oebe); 475 } 476 mtx_leave(&eb->eb_lock); 477 478 if (oebe != NULL) 479 ebe_rele(oebe); 480 481 return (error); 482} 483 484static void 485etherbridge_age(void *arg) 486{ 487 struct etherbridge *eb = arg; 488 struct eb_entry *ebe, *nebe; 489 struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq); 490 int diff; 491 unsigned int now = getuptime(); 492 size_t i; 493 494 timeout_add_sec(&eb->eb_tmo_age, 100); 495 496 for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) { 497 struct eb_list *ebl = &eb->eb_table[i]; 498#if 0 499 if (SMR_TAILQ_EMPTY(ebl)); 500 continue; 501#endif 502 503 mtx_enter(&eb->eb_lock); /* don't block map too much */ 504 SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) { 505 if (ebe->ebe_type != EBE_DYNAMIC) 506 continue; 507 508 diff = now - ebe->ebe_age; 509 if (diff < eb->eb_max_age) 510 continue; 511 512 ebl_remove(ebl, ebe); 513 ebt_remove(eb, ebe); 514 eb->eb_num--; 515 516 /* we own the tables ref now */ 517 518 TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry); 519 } 520 mtx_leave(&eb->eb_lock); 521 } 522 523 TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) { 524 TAILQ_REMOVE(&ebq, ebe, ebe_qentry); 525 ebe_rele(ebe); 526 } 527} 528 529void 530etherbridge_detach_port(struct etherbridge *eb, void *port) 531{ 532 struct eb_entry *ebe, *nebe; 533 struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq); 534 size_t i; 535 536 for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) { 537 struct eb_list *ebl = &eb->eb_table[i]; 538 539 mtx_enter(&eb->eb_lock); /* don't block map too much */ 540 SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) { 541 if (!eb_port_eq(eb, ebe->ebe_port, port)) 542 continue; 543 544 ebl_remove(ebl, ebe); 545 ebt_remove(eb, ebe); 546 eb->eb_num--; 547 548 /* we own the tables ref now */ 549 550 TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry); 551 } 552 mtx_leave(&eb->eb_lock); 553 } 554 555 if (TAILQ_EMPTY(&ebq)) 556 return; 557 558 /* 559 * do one smr barrier for all the entries rather than an 560 * smr_call each. 561 */ 562 smr_barrier(); 563 564 TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) { 565 TAILQ_REMOVE(&ebq, ebe, ebe_qentry); 566 ebe_free(ebe); 567 } 568} 569 570void 571etherbridge_flush(struct etherbridge *eb, uint32_t flags) 572{ 573 struct eb_entry *ebe, *nebe; 574 struct eb_queue ebq = TAILQ_HEAD_INITIALIZER(ebq); 575 size_t i; 576 577 for (i = 0; i < ETHERBRIDGE_TABLE_SIZE; i++) { 578 struct eb_list *ebl = &eb->eb_table[i]; 579 580 mtx_enter(&eb->eb_lock); /* don't block map too much */ 581 SMR_TAILQ_FOREACH_SAFE_LOCKED(ebe, ebl, ebe_lentry, nebe) { 582 if (flags == IFBF_FLUSHDYN && 583 ebe->ebe_type != EBE_DYNAMIC) 584 continue; 585 586 ebl_remove(ebl, ebe); 587 ebt_remove(eb, ebe); 588 eb->eb_num--; 589 590 /* we own the tables ref now */ 591 592 TAILQ_INSERT_TAIL(&ebq, ebe, ebe_qentry); 593 } 594 mtx_leave(&eb->eb_lock); 595 } 596 597 if (TAILQ_EMPTY(&ebq)) 598 return; 599 600 /* 601 * do one smr barrier for all the entries rather than an 602 * smr_call each. 603 */ 604 smr_barrier(); 605 606 TAILQ_FOREACH_SAFE(ebe, &ebq, ebe_qentry, nebe) { 607 TAILQ_REMOVE(&ebq, ebe, ebe_qentry); 608 ebe_free(ebe); 609 } 610} 611 612int 613etherbridge_rtfind(struct etherbridge *eb, struct ifbaconf *baconf) 614{ 615 struct eb_entry *ebe; 616 struct ifbareq bareq; 617 caddr_t buf; 618 size_t len, nlen; 619 time_t age, now = getuptime(); 620 int error; 621 622 if (baconf->ifbac_len == 0) { 623 /* single read is atomic */ 624 baconf->ifbac_len = eb->eb_num * sizeof(bareq); 625 return (0); 626 } 627 628 buf = malloc(baconf->ifbac_len, M_TEMP, M_WAITOK|M_CANFAIL); 629 if (buf == NULL) 630 return (ENOMEM); 631 len = 0; 632 633 mtx_enter(&eb->eb_lock); 634 RBT_FOREACH(ebe, eb_tree, &eb->eb_tree) { 635 nlen = len + sizeof(bareq); 636 if (nlen > baconf->ifbac_len) 637 break; 638 639 strlcpy(bareq.ifba_name, eb->eb_name, 640 sizeof(bareq.ifba_name)); 641 eb_port_ifname(eb, 642 bareq.ifba_ifsname, sizeof(bareq.ifba_ifsname), 643 ebe->ebe_port); 644 ether_e64_to_addr(&bareq.ifba_dst, ebe->ebe_addr); 645 646 memset(&bareq.ifba_dstsa, 0, sizeof(bareq.ifba_dstsa)); 647 eb_port_sa(eb, &bareq.ifba_dstsa, ebe->ebe_port); 648 649 switch (ebe->ebe_type) { 650 case EBE_DYNAMIC: 651 age = now - ebe->ebe_age; 652 bareq.ifba_age = MIN(age, 0xff); 653 bareq.ifba_flags = IFBAF_DYNAMIC; 654 break; 655 case EBE_STATIC: 656 bareq.ifba_age = 0; 657 bareq.ifba_flags = IFBAF_STATIC; 658 break; 659 } 660 661 memcpy(buf + len, &bareq, sizeof(bareq)); 662 len = nlen; 663 } 664 nlen = baconf->ifbac_len; 665 baconf->ifbac_len = eb->eb_num * sizeof(bareq); 666 mtx_leave(&eb->eb_lock); 667 668 error = copyout(buf, baconf->ifbac_buf, len); 669 free(buf, M_TEMP, nlen); 670 671 return (error); 672} 673 674int 675etherbridge_set_max(struct etherbridge *eb, struct ifbrparam *bparam) 676{ 677 if (bparam->ifbrp_csize < 1 || 678 bparam->ifbrp_csize > 4096) /* XXX */ 679 return (EINVAL); 680 681 /* commit */ 682 eb->eb_max = bparam->ifbrp_csize; 683 684 return (0); 685} 686 687int 688etherbridge_get_max(struct etherbridge *eb, struct ifbrparam *bparam) 689{ 690 bparam->ifbrp_csize = eb->eb_max; 691 692 return (0); 693} 694 695int 696etherbridge_set_tmo(struct etherbridge *eb, struct ifbrparam *bparam) 697{ 698 if (bparam->ifbrp_ctime < 8 || 699 bparam->ifbrp_ctime > 3600) 700 return (EINVAL); 701 702 /* commit */ 703 eb->eb_max_age = bparam->ifbrp_ctime; 704 705 return (0); 706} 707 708int 709etherbridge_get_tmo(struct etherbridge *eb, struct ifbrparam *bparam) 710{ 711 bparam->ifbrp_ctime = eb->eb_max_age; 712 713 return (0); 714} 715