1/* $NetBSD: ieee8023ad_lacp.c,v 1.13 2021/11/30 01:17:02 yamaguchi Exp $ */ 2 3/*- 4 * Copyright (c)2005 YAMAMOTO Takashi, 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 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: ieee8023ad_lacp.c,v 1.13 2021/11/30 01:17:02 yamaguchi Exp $"); 31 32#include <sys/param.h> 33#include <sys/callout.h> 34#include <sys/mbuf.h> 35#include <sys/systm.h> 36#include <sys/malloc.h> 37#include <sys/kernel.h> /* hz */ 38 39#include <net/if.h> 40#include <net/if_dl.h> 41#include <net/if_ether.h> 42#include <net/if_media.h> 43#include <net/ether_slowprotocols.h> 44 45#include <net/agr/if_agrvar_impl.h> 46#include <net/agr/if_agrsubr.h> 47#include <net/agr/ieee8023_tlv.h> 48#include <net/agr/ieee8023ad.h> 49#include <net/agr/ieee8023ad_lacp.h> 50#include <net/agr/ieee8023ad_lacp_impl.h> 51#include <net/agr/ieee8023ad_impl.h> 52#include <net/agr/ieee8023ad_lacp_sm.h> 53#include <net/agr/ieee8023ad_lacp_debug.h> 54 55static void lacp_fill_actorinfo(struct agr_port *, struct lacp_peerinfo *); 56 57static uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 58static void lacp_suppress_distributing(struct lacp_softc *, 59 struct lacp_aggregator *); 60static void lacp_transit_expire(void *); 61static void lacp_select_active_aggregator(struct lacp_softc *); 62static uint16_t lacp_compose_key(struct lacp_port *); 63 64/* 65 * actor system priority and port priority. 66 * XXX should be configurable. 67 */ 68 69#define LACP_SYSTEM_PRIO 0x8000 70#define LACP_PORT_PRIO 0x8000 71 72static const struct tlv_template lacp_info_tlv_template[] = { 73 { LACP_TYPE_ACTORINFO, 74 sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 75 { LACP_TYPE_PARTNERINFO, 76 sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 77 { LACP_TYPE_COLLECTORINFO, 78 sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 79 { 0, 0 }, 80}; 81 82/* 83 * ieee8023ad_lacp_input: process lacpdu 84 * 85 * => called from ether_input. (ie. at IPL_NET) 86 * 87 * XXX is it better to defer processing to lower IPL? 88 * XXX anyway input rate should be very low... 89 */ 90 91int 92ieee8023ad_lacp_input(struct ifnet *ifp, struct mbuf *m) 93{ 94 struct lacpdu *du; 95 struct agr_softc *sc; 96 struct agr_port *port; 97 struct lacp_port *lp; 98 int error = 0; 99 100 port = ifp->if_lagg; /* XXX race with agr_remport. */ 101 if (__predict_false(port->port_flags & AGRPORT_DETACHING)) { 102 goto bad; 103 } 104 105 sc = AGR_SC_FROM_PORT(port); 106 KASSERT(port); 107 108 /* running static config? */ 109 if (AGR_STATIC(sc)) { 110 /* static config, no lacp */ 111 goto bad; 112 } 113 114 115 if (m->m_pkthdr.len != sizeof(*du)) { 116 goto bad; 117 } 118 119 if ((m->m_flags & M_MCAST) == 0) { 120 goto bad; 121 } 122 123 if (m->m_len < sizeof(*du)) { 124 m = m_pullup(m, sizeof(*du)); 125 if (m == NULL) { 126 return ENOMEM; 127 } 128 } 129 130 du = mtod(m, struct lacpdu *); 131 132 if (memcmp(&du->ldu_eh.ether_dhost, 133 ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 134 goto bad; 135 } 136 137 KASSERT(du->ldu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_LACP); 138 139 /* 140 * ignore the version for compatibility with 141 * the future protocol revisions. 142 */ 143 144#if 0 145 if (du->ldu_sph.sph_version != 1) { 146 goto bad; 147 } 148#endif 149 150 /* 151 * ignore tlv types for compatibility with 152 * the future protocol revisions. 153 */ 154 155 if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 156 lacp_info_tlv_template, false)) { 157 goto bad; 158 } 159 160 AGR_LOCK(sc); 161 lp = LACP_PORT(port); 162 163#if defined(LACP_DEBUG) 164 if (lacpdebug) { 165 LACP_DPRINTF((lp, "lacpdu receive\n")); 166 lacp_dump_lacpdu(du); 167 } 168#endif /* defined(LACP_DEBUG) */ 169 lacp_sm_rx(lp, du); 170 171 AGR_UNLOCK(sc); 172 173 m_freem(m); 174 175 return error; 176 177bad: 178 m_freem(m); 179 return EINVAL; 180} 181 182static void 183lacp_fill_actorinfo(struct agr_port *port, struct lacp_peerinfo *info) 184{ 185 struct lacp_port *lp = LACP_PORT(port); 186 187 info->lip_systemid.lsi_prio = htobe16(LACP_SYSTEM_PRIO); 188 memcpy(&info->lip_systemid.lsi_mac, 189 CLLADDR(port->port_ifp->if_sadl), ETHER_ADDR_LEN); 190 info->lip_portid.lpi_prio = htobe16(LACP_PORT_PRIO); 191 info->lip_portid.lpi_portno = htobe16(port->port_ifp->if_index); 192 info->lip_state = lp->lp_state; 193} 194 195int 196lacp_xmit_lacpdu(struct lacp_port *lp) 197{ 198 struct agr_port *port = lp->lp_agrport; 199 struct mbuf *m; 200 struct lacpdu *du; 201 int error; 202 203 /* running static config? */ 204 if (AGR_STATIC(AGR_SC_FROM_PORT(port))) { 205 /* static config, no lacp transmit */ 206 return 0; 207 } 208 209 KDASSERT(MHLEN >= sizeof(*du)); 210 211 m = m_gethdr(M_DONTWAIT, MT_DATA); 212 if (m == NULL) { 213 return ENOMEM; 214 } 215 m->m_len = m->m_pkthdr.len = sizeof(*du); 216 217 du = mtod(m, struct lacpdu *); 218 memset(du, 0, sizeof(*du)); 219 220 memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 221 ETHER_ADDR_LEN); 222 memcpy(&du->ldu_eh.ether_shost, &port->port_origlladdr, ETHER_ADDR_LEN); 223 du->ldu_eh.ether_type = htobe16(ETHERTYPE_SLOWPROTOCOLS); 224 225 du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 226 du->ldu_sph.sph_version = 1; 227 228 TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 229 du->ldu_actor = lp->lp_actor; 230 231 TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 232 sizeof(du->ldu_partner)); 233 du->ldu_partner = lp->lp_partner; 234 235 TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 236 sizeof(du->ldu_collector)); 237 du->ldu_collector.lci_maxdelay = 0; 238 239#if defined(LACP_DEBUG) 240 if (lacpdebug) { 241 LACP_DPRINTF((lp, "lacpdu transmit\n")); 242 lacp_dump_lacpdu(du); 243 } 244#endif /* defined(LACP_DEBUG) */ 245 246 m->m_flags |= M_MCAST; 247 248 /* 249 * XXX should use higher priority queue. 250 * otherwise network congestion can break aggregation. 251 */ 252 253 error = agr_xmit_frame(port->port_ifp, m); 254 return error; 255} 256 257void 258ieee8023ad_lacp_portstate(struct agr_port *port) 259{ 260 struct lacp_port *lp = LACP_PORT(port); 261 u_int media = port->port_media; 262 uint8_t old_state; 263 uint16_t old_key; 264 265 AGR_ASSERT_LOCKED(AGR_SC_FROM_PORT(port)); 266 267 LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x\n", lp->lp_media, media)); 268 269 old_state = lp->lp_state; 270 old_key = lp->lp_key; 271 272 lp->lp_media = media; 273 if ((media & IFM_HDX) != 0) { 274 lp->lp_state &= ~LACP_STATE_AGGREGATION; 275 } else { 276 lp->lp_state |= LACP_STATE_AGGREGATION; 277 } 278 lp->lp_key = lacp_compose_key(lp); 279 280 if (old_state != lp->lp_state || old_key != lp->lp_key) { 281 LACP_DPRINTF((lp, "-> UNSELECTED\n")); 282 lp->lp_selected = LACP_UNSELECTED; 283 } 284} 285 286void 287ieee8023ad_lacp_porttick(struct agr_softc *sc, struct agr_port *port) 288{ 289 struct lacp_port *lp = LACP_PORT(port); 290 291 AGR_ASSERT_LOCKED(sc); 292 293 lacp_run_timers(lp); 294 295 lacp_select(lp); 296 lacp_sm_mux(lp); 297 lacp_sm_tx(lp); 298 lacp_sm_ptx_tx_schedule(lp); 299} 300 301void 302lacp_portinit(struct agr_port *port) 303{ 304 struct lacp_port *lp = LACP_PORT(port); 305 bool active = true; /* XXX should be configurable */ 306 bool fast = false; /* XXX should be configurable */ 307 308 lp->lp_agrport = port; 309 lacp_fill_actorinfo(port, &lp->lp_actor); 310 lp->lp_state = 311 (active ? LACP_STATE_ACTIVITY : 0) | 312 (fast ? LACP_STATE_TIMEOUT : 0); 313 lp->lp_aggregator = NULL; 314 lp->lp_media = port->port_media; /* XXX */ 315 lp->lp_key = lacp_compose_key(lp); 316 lacp_sm_rx_set_expired(lp); 317} 318 319void 320lacp_portfini(struct agr_port *port) 321{ 322 struct lacp_port *lp = LACP_PORT(port); 323 struct lacp_aggregator *la = lp->lp_aggregator; 324 int i; 325 326 LACP_DPRINTF((lp, "portfini\n")); 327 328 for (i = 0; i < LACP_NTIMER; i++) { 329 LACP_TIMER_DISARM(lp, i); 330 } 331 332 if (la == NULL) { 333 return; 334 } 335 336 lacp_disable_distributing(lp); 337 lacp_unselect(lp); 338} 339 340/* -------------------- */ 341void 342lacp_disable_collecting(struct lacp_port *lp) 343{ 344 struct agr_port *port = lp->lp_agrport; 345 346 lp->lp_state &= ~LACP_STATE_COLLECTING; 347 port->port_flags &= ~AGRPORT_COLLECTING; 348} 349 350void 351lacp_enable_collecting(struct lacp_port *lp) 352{ 353 struct agr_port *port = lp->lp_agrport; 354 355 lp->lp_state |= LACP_STATE_COLLECTING; 356 port->port_flags |= AGRPORT_COLLECTING; 357} 358 359void 360lacp_disable_distributing(struct lacp_port *lp) 361{ 362 struct agr_port *port = lp->lp_agrport; 363 struct lacp_aggregator *la = lp->lp_aggregator; 364 struct lacp_softc *lsc = LACP_SOFTC(AGR_SC_FROM_PORT(port)); 365#if defined(LACP_DEBUG) 366 char buf[LACP_LAGIDSTR_MAX+1]; 367#endif /* defined(LACP_DEBUG) */ 368 369 if ((lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 370 return; 371 } 372 373 KASSERT(la); 374 KASSERT(!TAILQ_EMPTY(&la->la_ports)); 375 KASSERT(la->la_nports > 0); 376 KASSERT(la->la_refcnt >= la->la_nports); 377 378 LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 379 "nports %d -> %d\n", 380 lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 381 la->la_nports, la->la_nports - 1)); 382 383 TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 384 la->la_nports--; 385 386 lacp_suppress_distributing(lsc, la); 387 388 lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 389 port->port_flags &= ~AGRPORT_DISTRIBUTING; 390 391 if (lsc->lsc_active_aggregator == la) { 392 lacp_select_active_aggregator(lsc); 393 } 394} 395 396void 397lacp_enable_distributing(struct lacp_port *lp) 398{ 399 struct agr_port *port = lp->lp_agrport; 400 struct lacp_aggregator *la = lp->lp_aggregator; 401 struct lacp_softc *lsc = LACP_SOFTC(AGR_SC_FROM_PORT(port)); 402#if defined(LACP_DEBUG) 403 char buf[LACP_LAGIDSTR_MAX+1]; 404#endif /* defined(LACP_DEBUG) */ 405 406 if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 407 return; 408 } 409 410 KASSERT(la); 411 412 LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 413 "nports %d -> %d\n", 414 lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 415 la->la_nports, la->la_nports + 1)); 416 417 KASSERT(la->la_refcnt > la->la_nports); 418 TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 419 la->la_nports++; 420 421 lacp_suppress_distributing(lsc, la); 422 423 lp->lp_state |= LACP_STATE_DISTRIBUTING; 424 port->port_flags |= AGRPORT_DISTRIBUTING; 425 426 if (lsc->lsc_active_aggregator != la) { 427 lacp_select_active_aggregator(lsc); 428 } 429} 430 431static void 432lacp_transit_expire(void *vp) 433{ 434 struct agr_softc *sc = vp; 435 struct lacp_softc *lsc = LACP_SOFTC(sc); 436 437 AGR_LOCK(sc); 438 LACP_DPRINTF((NULL, "%s\n", __func__)); 439 lsc->lsc_suppress_distributing = false; 440 AGR_UNLOCK(sc); 441} 442 443/* -------------------- */ 444/* XXX */ 445void 446ieee8023ad_portinit(struct agr_port *port) 447{ 448 struct ieee8023ad_port *iport = IEEE8023AD_PORT(port); 449 450 memset(iport, 0, sizeof(*iport)); 451 452 lacp_portinit(port); 453} 454 455void 456ieee8023ad_portfini(struct agr_port *port) 457{ 458 struct agr_softc *sc = AGR_SC_FROM_PORT(port); 459 460 AGR_LOCK(sc); 461 462 lacp_portfini(port); 463 464 AGR_UNLOCK(sc); 465} 466 467void 468ieee8023ad_ctor(struct agr_softc *sc) 469{ 470 struct ieee8023ad_softc *isc = IEEE8023AD_SOFTC(sc); 471 struct lacp_softc *lsc = &isc->isc_lacpsc; 472 473 lsc->lsc_active_aggregator = NULL; 474 TAILQ_INIT(&lsc->lsc_aggregators); 475 callout_init(&lsc->lsc_transit_callout, 0); 476 callout_setfunc(&lsc->lsc_transit_callout, lacp_transit_expire, sc); 477} 478 479void 480ieee8023ad_dtor(struct agr_softc *sc) 481{ 482 struct ieee8023ad_softc *isc = IEEE8023AD_SOFTC(sc); 483 struct lacp_softc *lsc = &isc->isc_lacpsc; 484 485 LACP_DPRINTF((NULL, "%s\n", __func__)); 486 487 callout_stop(&lsc->lsc_transit_callout); 488 KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators)); 489 KASSERT(lsc->lsc_active_aggregator == NULL); 490} 491 492/* -------------------- */ 493 494struct agr_port * 495ieee8023ad_select_tx_port(struct agr_softc *sc, struct mbuf *m) 496{ 497 const struct lacp_softc *lsc = LACP_SOFTC(sc); 498 const struct lacp_aggregator *la; 499 const struct lacp_port *lp; 500 uint32_t hash; 501 int nports; 502 503 if (__predict_false(lsc->lsc_suppress_distributing && 504 !AGR_ROUNDROBIN(sc))) { 505 LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 506 if_statinc(&sc->sc_if, if_collisions); /* XXX abuse */ 507 return NULL; 508 } 509 510 la = lsc->lsc_active_aggregator; 511 if (__predict_false(la == NULL)) { 512 LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 513 return NULL; 514 } 515 516 nports = la->la_nports; 517 KASSERT(nports > 0); 518 519 if (AGR_ROUNDROBIN(sc)) { 520 /* packet ordering rule violation */ 521 hash = sc->sc_rr_counter++; 522 } else { 523 hash = (*sc->sc_iftop->iftop_hashmbuf)(sc, m); 524 } 525 hash %= nports; 526 lp = TAILQ_FIRST(&la->la_ports); 527 KASSERT(lp != NULL); 528 while (hash--) { 529 lp = TAILQ_NEXT(lp, lp_dist_q); 530 KASSERT(lp != NULL); 531 } 532 533 KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0); 534 535 return lp->lp_agrport; 536} 537 538/* 539 * lacp_suppress_distributing: drop transmit packets for a while 540 * to preserve packet ordering. 541 */ 542 543static void 544lacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 545{ 546 547 if (lsc->lsc_active_aggregator != la) { 548 return; 549 } 550 551 LACP_DPRINTF((NULL, "%s\n", __func__)); 552 lsc->lsc_suppress_distributing = true; 553 /* XXX should consider collector max delay */ 554 callout_schedule(&lsc->lsc_transit_callout, 555 LACP_TRANSIT_DELAY * hz / 1000); 556} 557 558/* -------------------- */ 559 560int 561lacp_compare_peerinfo(const struct lacp_peerinfo *a, 562 const struct lacp_peerinfo *b) 563{ 564 565 return memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state)); 566} 567 568int 569lacp_compare_systemid(const struct lacp_systemid *a, 570 const struct lacp_systemid *b) 571{ 572 573 return memcmp(a, b, sizeof(*a)); 574} 575 576int 577lacp_compare_portid(const struct lacp_portid *a, 578 const struct lacp_portid *b) 579{ 580 581 return memcmp(a, b, sizeof(*a)); 582} 583 584/* -------------------- */ 585 586static uint64_t 587lacp_aggregator_bandwidth(struct lacp_aggregator *la) 588{ 589 struct lacp_port *lp; 590 uint64_t speed; 591 592 lp = TAILQ_FIRST(&la->la_ports); 593 if (lp == NULL) { 594 return 0; 595 } 596 597 speed = ifmedia_baudrate(lp->lp_media); 598 speed *= la->la_nports; 599 if (speed == 0) { 600 LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 601 lp->lp_media, la->la_nports)); 602 } 603 604 return speed; 605} 606 607/* 608 * lacp_select_active_aggregator: select an aggregator to be used to transmit 609 * packets from agr(4) interface. 610 */ 611 612static void 613lacp_select_active_aggregator(struct lacp_softc *lsc) 614{ 615 struct lacp_aggregator *la; 616 struct lacp_aggregator *best_la = NULL; 617 uint64_t best_speed = 0; 618#if defined(LACP_DEBUG) 619 char buf[LACP_LAGIDSTR_MAX+1]; 620#endif /* defined(LACP_DEBUG) */ 621 622 LACP_DPRINTF((NULL, "%s:\n", __func__)); 623 624 TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 625 uint64_t speed; 626 627 if (la->la_nports == 0) { 628 continue; 629 } 630 631 speed = lacp_aggregator_bandwidth(la); 632 LACP_DPRINTF((NULL, "%s, speed=%" PRIu64 ", nports=%d\n", 633 lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 634 speed, la->la_nports)); 635 if (speed > best_speed || 636 (speed == best_speed && 637 la == lsc->lsc_active_aggregator)) { 638 best_la = la; 639 best_speed = speed; 640 } 641 } 642 643 KASSERT(best_la == NULL || best_la->la_nports > 0); 644 KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports)); 645 646#if defined(LACP_DEBUG) 647 if (lsc->lsc_active_aggregator != best_la) { 648 LACP_DPRINTF((NULL, "active aggregator changed\n")); 649 LACP_DPRINTF((NULL, "old %s\n", 650 lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 651 buf, sizeof(buf)))); 652 } else { 653 LACP_DPRINTF((NULL, "active aggregator not changed\n")); 654 } 655 LACP_DPRINTF((NULL, "new %s\n", 656 lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 657#endif /* defined(LACP_DEBUG) */ 658 659 if (lsc->lsc_active_aggregator != best_la) { 660 lsc->lsc_active_aggregator = best_la; 661 if (best_la) { 662 lacp_suppress_distributing(lsc, best_la); 663 } 664 } 665} 666 667uint16_t 668lacp_compose_key(struct lacp_port *lp) 669{ 670 u_int media = lp->lp_media; 671 uint16_t key; 672 673 KASSERT(IFM_TYPE(media) == IFM_ETHER); 674 675 if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 676 677 /* 678 * non-aggregatable links should have unique keys. 679 * 680 * XXX this isn't really unique as if_index is 16 bit. 681 */ 682 683 /* bit 0..14: (some bits of) if_index of this port */ 684 key = lp->lp_agrport->port_ifp->if_index; 685 /* bit 15: 1 */ 686 key |= 0x8000; 687 } else { 688 u_int subtype = IFM_SUBTYPE(media); 689 690 KASSERT((media & IFM_HDX) == 0); /* should be handled above */ 691 KASSERT((subtype & 0x1f) == subtype); 692 693 /* bit 0..4: IFM_SUBTYPE */ 694 key = subtype; 695 /* bit 5..14: (some bits of) if_index of agr device */ 696 key |= 0x7fe0 & ((lp->lp_agrport->port_agrifp->if_index) << 5); 697 /* bit 15: 0 */ 698 } 699 700 return htobe16(key); 701} 702