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