/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * IEEE 802.3ad Link Aggregation - Receive * * Implements the collector function. * Manages the RX resources exposed by a link aggregation group. */ #include #include #include #include #include #include #include #include static void aggr_recv_lacp(aggr_port_t *port, mblk_t *mp) { aggr_grp_t *grp = port->lp_grp; /* in promiscuous mode, send copy of packet up */ if (grp->lg_promisc) { mblk_t *nmp = copymsg(mp); if (nmp != NULL) mac_rx(grp->lg_mh, NULL, nmp); } aggr_lacp_rx(port, mp); } /* * Callback function invoked by MAC service module when packets are * made available by a MAC port. */ void aggr_recv_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp) { aggr_port_t *port = (aggr_port_t *)arg; aggr_grp_t *grp = port->lp_grp; /* * If this message is looped back from the legacy devices, drop * it as the Nemo framework will be responsible for looping it * back by the mac_txloop() function. */ if (mp->b_flag & MSGNOLOOP) { ASSERT(mp->b_next == NULL); freemsg(mp); return; } if (grp->lg_lacp_mode == AGGR_LACP_OFF) { mac_rx(grp->lg_mh, mrh, mp); } else { mblk_t *cmp, *last, *head; struct ether_header *ehp; uint16_t sap; /* filter out slow protocol packets (LACP & Marker) */ last = NULL; head = cmp = mp; while (cmp != NULL) { if (MBLKL(cmp) < sizeof (struct ether_header)) { /* packet too short */ if (head == cmp) { /* no packets accumulated */ head = cmp->b_next; cmp->b_next = NULL; freemsg(cmp); cmp = head; } else { /* send up accumulated packets */ last->b_next = NULL; if (port->lp_collector_enabled) mac_rx(grp->lg_mh, mrh, head); else freemsgchain(head); head = cmp->b_next; cmp->b_next = NULL; freemsg(cmp); cmp = head; last = NULL; } continue; } ehp = (struct ether_header *)cmp->b_rptr; sap = ntohs(ehp->ether_type); if (sap == ETHERTYPE_SLOW) { /* * LACP or Marker packet. Send up pending * chain, and send LACP/Marker packet * to LACP subsystem. */ if (head == cmp) { /* first packet of chain */ ASSERT(last == NULL); head = cmp->b_next; cmp->b_next = NULL; aggr_recv_lacp(port, cmp); cmp = head; } else { /* previously accumulated packets */ ASSERT(last != NULL); /* send up non-LACP packets */ last->b_next = NULL; if (port->lp_collector_enabled) mac_rx(grp->lg_mh, mrh, head); else freemsgchain(head); /* unlink and pass up LACP packets */ head = cmp->b_next; cmp->b_next = NULL; aggr_recv_lacp(port, cmp); cmp = head; last = NULL; } } else { last = cmp; cmp = cmp->b_next; } } if (head != NULL) { if (port->lp_collector_enabled) mac_rx(grp->lg_mh, mrh, head); else freemsgchain(head); } } }