/*
* 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.
*/
/*
* IEEE 802.3ad Link Aggregation - Receive
*
* Implements the collector function.
* Manages the RX resources exposed by a link aggregation group.
*/
#include <sys/sysmacros.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/strsun.h>
#include <sys/strsubr.h>
#include <sys/byteorder.h>
#include <sys/aggr.h>
#include <sys/aggr_impl.h>
static void
aggr_mac_rx(mac_handle_t lg_mh, mac_resource_handle_t mrh, mblk_t *mp)
{
if (mrh == NULL) {
mac_rx(lg_mh, mrh, mp);
} else {
aggr_pseudo_rx_ring_t *ring = (aggr_pseudo_rx_ring_t *)mrh;
mac_rx_ring(lg_mh, ring->arr_rh, mp, ring->arr_gen);
}
}
void
aggr_recv_lacp(aggr_port_t *port, mac_resource_handle_t mrh, 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)
aggr_mac_rx(grp->lg_mh, mrh, nmp);
}
aggr_lacp_rx_enqueue(port, mp);
}
/*
* Callback function invoked by MAC service module when packets are
* made available by a MAC port.
*/
/* ARGSUSED */
void
aggr_recv_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
boolean_t loopback)
{
aggr_port_t *port = (aggr_port_t *)arg;
aggr_grp_t *grp = port->lp_grp;
if (grp->lg_lacp_mode == AGGR_LACP_OFF) {
aggr_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) {
aggr_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, mrh, cmp);
cmp = head;
} else {
/* previously accumulated packets */
ASSERT(last != NULL);
/* send up non-LACP packets */
last->b_next = NULL;
if (port->lp_collector_enabled) {
aggr_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, mrh, cmp);
cmp = head;
last = NULL;
}
} else {
last = cmp;
cmp = cmp->b_next;
}
}
if (head != NULL) {
if (port->lp_collector_enabled)
aggr_mac_rx(grp->lg_mh, mrh, head);
else
freemsgchain(head);
}
}
}