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