mac_util.c revision fd0939ef389f48c901faf4bf0b60b82d4bc58b64
/*
* 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
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* MAC Services Module - misc utilities
*/
#include <sys/mac_impl.h>
#include <sys/mac_client_priv.h>
#include <sys/mac_client_impl.h>
#include <sys/mac_soft_ring.h>
#include <sys/pci_tools.h>
#include <inet/ipsec_impl.h>
#include <inet/ipsecesp.h>
/*
* Copy an mblk, preserving its hardware checksum flags.
*/
static mblk_t *
{
return (NULL);
flags, KM_NOSLEEP);
return (mp1);
}
/*
* Copy an mblk chain, presenting the hardware checksum flags of the
* individual mblks.
*/
mblk_t *
{
return (NULL);
}
}
return (nmp);
}
/*
* Process the specified mblk chain for proper handling of hardware
* checksum offload. This routine is invoked for loopback traffic
* between MAC clients.
* The function handles a NULL mblk chain passed as argument.
*/
mblk_t *
{
struct ether_header *ehp;
&flags);
if (flags == 0)
continue;
/*
* Since the processing of checksum offload for loopback
* traffic requires modification of the packet contents,
* ensure sure that we are always modifying our own copy.
*/
continue;
else
}
/*
* Ethernet, and optionally VLAN header.
*/
/* LINTED: improper alignment cast */
struct ether_vlan_header *evhp;
/* LINTED: improper alignment cast */
offset = sizeof (struct ether_vlan_header);
} else {
offset = sizeof (struct ether_header);
}
/* corrupted packet, skip it */
else
continue;
}
}
/*
* In order to compute the full and header
* checksums, we need to find and parse
*/
/*
* IP header.
*/
if (sap != ETHERTYPE_IP)
continue;
/* LINTED: improper alignment cast */
if (flags & HCK_FULLCKSUM) {
/*
* Pointer to checksum field in ULP header.
*/
switch (proto) {
case IPPROTO_TCP:
/* LINTED: improper alignment cast */
break;
case IPPROTO_UDP:
/* LINTED: improper alignment cast */
break;
default:
"unexpected protocol: %d", proto);
continue;
}
/*
* Pseudo-header checksum.
*/
/*
* The checksum value stored in the packet needs
* to be correct. Compute it here.
*/
*up = 0;
value = 0xffff;
}
if (flags & HCK_IPV4_HDRCKSUM) {
}
}
if (flags & HCK_PARTIALCKSUM) {
continue;
else
}
/* LINTED: cast may result in improper alignment */
*up = 0;
/*
* Since we already computed the whole checksum,
* indicate to the stack that it has already
* been verified by the hardware.
*/
flags &= ~HCK_PARTIALCKSUM;
value = 0xffff;
}
}
return (new_chain);
}
/*
* Add VLAN tag to the specified mblk.
*/
mblk_t *
{
struct ether_vlan_header *evhp;
struct ether_header *ehp;
/*
* Allocate an mblk for the new tagged ethernet header,
* and copy the MAC addresses and ethertype from the
* original header.
*/
return (NULL);
}
/*
* Free the original message if it's now empty. Link the
* rest of messages to the header message.
*/
} else {
}
/*
* Initialize the new TCI (Tag Control Information).
*/
return (hmp);
}
/*
* Adds a VLAN tag with the specified VID and priority to each mblk of
* the specified chain.
*/
mblk_t *
{
break;
}
}
return (mp_chain);
}
/*
* Strip VLAN tag
*/
mblk_t *
{
struct ether_vlan_header *evhp;
return (NULL);
}
}
return (mp);
}
/*
* Strip VLAN tag from each mblk of the chain.
*/
mblk_t *
{
break;
}
}
return (mp_chain);
}
/*
* Default callback function. Used when the datapath is not yet initialized.
*/
/* ARGSUSED */
void
{
}
}
/*
* Determines the IPv6 header length accounting for all the optional IPv6
* headers (hop-by-hop, destination, routing and fragment). The header length
* and next header value (a transport header) is captured.
*
* Returns B_FALSE if all the IP headers are not in the same mblk otherwise
* returns B_TRUE.
*/
{
return (B_FALSE);
if (ip_fragmented != NULL)
*ip_fragmented = B_FALSE;
/* Is there enough left for len + nexthdr? */
break;
switch (*nexthdrp) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
/* Assumes the headers are identical for hbh and dst */
return (B_FALSE);
break;
case IPPROTO_ROUTING:
return (B_FALSE);
break;
case IPPROTO_FRAGMENT:
ehdrlen = sizeof (ip6_frag_t);
return (B_FALSE);
if (ip_fragmented != NULL)
*ip_fragmented = B_TRUE;
if (ip_frag_ident != NULL)
break;
case IPPROTO_NONE:
/* No next header means we're finished */
default:
*hdr_length = length;
return (B_TRUE);
}
*hdr_length = length;
}
switch (*nexthdrp) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
case IPPROTO_FRAGMENT:
/*
* If any know extension headers are still to be processed,
* the packet's malformed (or at least all the IP header(s) are
* not in the same mblk - and that should never happen.
*/
return (B_FALSE);
default:
/*
* If we get here, we know that all of the IP headers were in
* the same mblk, even if the ULP header is in the next mblk.
*/
*hdr_length = length;
return (B_TRUE);
}
}
typedef struct mac_dladm_intr {
int ino;
int cpu_id;
char driver_path[MAXPATHLEN];
char nexus_path[MAXPATHLEN];
/* Bind the interrupt to cpu_num */
static int
{
int err;
return (err);
}
/*
* Search interrupt information. iget is filled in with the info to search
*/
static boolean_t
{
int i;
/* Match the device path for the device path */
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Get information about ino, i.e. if this is the interrupt for our
* device and where it is bound etc.
*/
static boolean_t
{
int ipsz;
int nipsz;
int err;
/*
* Check if SLEEP is OK, i.e if could come here in response to
* changing the fanout due to some callback from the driver, say
* link speed changes.
*/
ipsz = PCITOOL_IGET_SIZE(0);
iget_p->num_devs_ret = 0;
if (err != 0) {
return (B_FALSE);
}
return (B_FALSE);
}
/* Reallocate */
if (err != 0) {
return (B_FALSE);
}
/* defensive */
return (B_FALSE);
}
}
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Get the interrupts and check each one to see if it is for our device.
*/
static int
{
int err;
int ino;
if (err != 0)
return (-1);
return (0);
return (1);
}
}
return (-1);
}
/*
* Obtain the nexus parent node info. for mdip.
*/
static dev_info_t *
{
struct ddi_minor_data *minordata;
int circ;
char pathname[MAXPATHLEN];
/*
* The netboot code could call this function while walking the
* device tree so we need to use ndi_devi_tryenter() here to
* avoid deadlock.
*/
break;
strlen(DDI_NT_INTRCTL)) == 0) {
"/devices%s:intr", pathname);
return (pdip);
}
}
}
return (NULL);
}
/*
* For a primary MAC client, if the user has set a list or CPUs or
* we have obtained it implicitly, we try to retarget the interrupt
* for that device on one of the CPUs in the list.
* We assign the interrupt to the same CPU as the poll thread.
*/
static boolean_t
{
int err;
int ret;
struct ddi_minor_data *minordata;
break;
}
return (B_FALSE);
/* defensive */
return (B_FALSE);
if (err != 0)
return (B_FALSE);
if (err != 0)
return (B_FALSE);
if (ret < 0) {
return (B_FALSE);
}
/* cmn_note? */
if (ret != 0)
return (B_FALSE);
}
return (B_TRUE);
}
void
{
return;
}
{
/*
* Check if we need to retarget the interrupt. We do this only
* for the primary MAC client. We do this if we have the only
* exclusive ring in the group.
*/
return (-1);
}
return (-1);
}
void *
{
}
#define PKT_HASH_2BYTES(x) ((x)[0] ^ (x)[1])
{
struct ether_header *ehp;
/*
* We may want to have one of these per MAC type plugin in the
* future. For now supports only ethernet.
*/
return (0L);
/* for now we support only outbound packets */
/* compute L2 hash */
if ((policy & MAC_PKT_HASH_L2) != 0) {
policy &= ~MAC_PKT_HASH_L2;
}
if (policy == 0)
goto done;
/* skip ethernet header */
if (sap == ETHERTYPE_VLAN) {
struct ether_vlan_header *evhp;
skip_len = sizeof (struct ether_vlan_header);
/* the vlan tag is the payload, pull up first */
goto done;
}
} else {
}
} else {
skip_len = sizeof (struct ether_header);
}
/* if ethernet header is in its own mblk, skip it */
goto done;
}
switch (sap) {
case ETHERTYPE_IP: {
/*
* If the header is not aligned or the header doesn't fit
* in the mblk, bail now. Note that this may cause packets
* reordering.
*/
goto done;
/* Check if the packet is fragmented. */
/*
* For fragmented packets, use addresses in addition to
* the frag_id to generate the hash inorder to get
* better distribution.
*/
policy &= ~MAC_PKT_HASH_L3;
}
if (ip_fragmented) {
goto done;
}
break;
}
case ETHERTYPE_IPV6: {
/*
* If the header is not aligned or the header doesn't fit
* in the mblk, bail now. Note that this may cause packets
* reordering.
*/
goto done;
goto done;
skip_len += hdr_length;
/*
* For fragmented packets, use addresses in addition to
* the frag_id to generate the hash inorder to get
* better distribution.
*/
policy &= ~MAC_PKT_HASH_L3;
}
if (ip_fragmented) {
goto done;
}
break;
}
default:
goto done;
}
if (policy == 0)
goto done;
/* if ip header is in its own mblk, skip it */
goto done;
}
/* parse ULP header */
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_ESP:
case IPPROTO_SCTP:
/*
* These Internet Protocols are intentionally designed
* for hashing from the git-go. Port numbers are in the first
* word for transports, SPI is first for ESP.
*/
goto done;
break;
case IPPROTO_AH: {
goto done;
/* if AH header is in its own mblk, skip it */
goto done;
}
goto again;
}
}
done:
return (hash);
}