| /****************************************************************************** |
| * |
| * Name: skcsum.c |
| * Project: GEnesis, PCI Gigabit Ethernet Adapter |
| * Version: $Revision: 2.4 $ |
| * Date: $Date: 2007/03/14 16:20:40 $ |
| * Purpose: Store/verify Internet checksum in send/receive packets. |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * |
| * LICENSE: |
| * (C)Copyright 1998-2003 SysKonnect GmbH. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * The information in this file is provided "AS IS" without warranty. |
| * /LICENSE |
| * |
| ******************************************************************************/ |
| |
| #ifdef SK_USE_CSUM /* Check if CSUM is to be used. */ |
| |
| #ifndef lint |
| static const char SysKonnectFileId[] = |
| "@(#) $Id: skcsum.c,v 2.4 2007/03/14 16:20:40 rassmann Exp $ (C) SysKonnect."; |
| #endif /* !lint */ |
| |
| /****************************************************************************** |
| * |
| * Description: |
| * |
| * This is the "GEnesis" common module "CSUM". |
| * |
| * This module contains the code necessary to calculate, store, and verify the |
| * Internet Checksum of IP, TCP, and UDP frames. |
| * |
| * "GEnesis" is an abbreviation of "Gigabit Ethernet Network System in Silicon" |
| * and is the code name of this SysKonnect project. |
| * |
| * Compilation Options: |
| * |
| * SK_USE_CSUM - Define if CSUM is to be used. Otherwise, CSUM will be an |
| * empty module. |
| * |
| * SKCS_OVERWRITE_PROTO - Define to overwrite the default protocol id |
| * definitions. In this case, all SKCS_PROTO_xxx definitions must be made |
| * external. |
| * |
| * SKCS_OVERWRITE_STATUS - Define to overwrite the default return status |
| * definitions. In this case, all SKCS_STATUS_xxx definitions must be made |
| * external. |
| * |
| * Include File Hierarchy: |
| * |
| * "h/skdrv1st.h" |
| * "h/skcsum.h" |
| * "h/sktypes.h" |
| * "h/skqueue.h" |
| * "h/skdrv2nd.h" |
| * |
| ******************************************************************************/ |
| |
| #include "h/skdrv1st.h" |
| #include "h/skcsum.h" |
| #include "h/skdrv2nd.h" |
| |
| /* defines ********************************************************************/ |
| |
| /* The size of an Ethernet MAC header. */ |
| #define SKCS_ETHERNET_MAC_HEADER_SIZE (6+6+2) |
| |
| /* The size of the used topology's MAC header. */ |
| #define SKCS_MAC_HEADER_SIZE SKCS_ETHERNET_MAC_HEADER_SIZE |
| |
| /* |
| * Field offsets within the IPv4 header. |
| */ |
| |
| /* "Internet Header Version" and "Length". */ |
| #define SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH 0 |
| |
| /* "Total Length". */ |
| #define SKCS_OFS_IP_TOTAL_LENGTH 2 |
| |
| /* "Flags" "Fragment Offset". */ |
| #define SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET 6 |
| |
| /* "Next Level Protocol" identifier. */ |
| #define SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL 9 |
| |
| /* Source IP address. */ |
| #define SKCS_OFS_IP_SOURCE_ADDRESS 12 |
| |
| /* Destination IP address. */ |
| #define SKCS_OFS_IP_DESTINATION_ADDRESS 16 |
| |
| #ifdef SK_IPV6_SUPPORT |
| /* |
| * Field offsets in the IPv6 header |
| */ |
| |
| /* Payload length field (size without IP header) */ |
| #define SKCS_OFS_IP6_PAYLOAD_LENGTH 4 |
| |
| /* Next header identifier */ |
| #define SKCS_OFS_IP6_NEXT_HEADER 6 |
| |
| /* Source IP adress (16 byte) */ |
| #define SKCS_OFS_IP6_SOURCE_ADDRESS 8 |
| |
| /* Destination IP address (16 byte) */ |
| #define SKCS_OFS_IP6_DESTINATION_ADDRESS 32 |
| |
| #endif /* SK_IPV6_SUPPORT */ |
| |
| /* |
| * Field offsets within the UDP header. |
| */ |
| |
| /* UDP checksum. */ |
| #define SKCS_OFS_UDP_CHECKSUM 6 |
| |
| /* IP "Next Level Protocol" identifiers (see RFC 790). */ |
| #define SKCS_PROTO_ID_TCP 6 /* Transport Control Protocol */ |
| #define SKCS_PROTO_ID_UDP 17 /* User Datagram Protocol */ |
| #ifdef SK_IPV6_SUPPORT |
| #define SKCS_PROTO_ID_ICMPV6 58 /* ICMPv6 */ |
| #define SKCS_PROTO_ID_NONE 59 /* No next header (used with IPv6) */ |
| #endif /* SK_IPV6_SUPPORT */ |
| |
| /* IP "Don't Fragment" bit. */ |
| #define SKCS_IP_DONT_FRAGMENT SKCS_HTON16(0x4000) |
| |
| /* Add a byte offset to a pointer. */ |
| #define SKCS_IDX(pPtr, Ofs) ((void *) ((char *) (pPtr) + (Ofs))) |
| |
| /* Check whether offset is within buffer. */ |
| #define SKCS_IDX_CHECK(Le, Ofs) ((Le) <= (Ofs)) |
| |
| /* |
| * Macros that convert host to network representation and vice versa, i.e. |
| * little/big endian conversion on little endian machines only. |
| */ |
| #ifdef SK_LITTLE_ENDIAN |
| #define SKCS_HTON16(Val16) (((unsigned) (Val16) >> 8) | (((Val16) & 0xff) << 8)) |
| #endif /* SK_LITTLE_ENDIAN */ |
| #ifdef SK_BIG_ENDIAN |
| #define SKCS_HTON16(Val16) (Val16) |
| #endif /* SK_BIG_ENDIAN */ |
| #define SKCS_NTOH16(Val16) SKCS_HTON16(Val16) |
| |
| /* typedefs *******************************************************************/ |
| |
| /* function prototypes ********************************************************/ |
| |
| /****************************************************************************** |
| * |
| * SkCsGetSendInfo - get checksum information for a send packet |
| * |
| * Description: |
| * Get all checksum information necessary to send a TCP or UDP packet. The |
| * function checks the IP header passed to it. If the high-level protocol |
| * is either TCP or UDP the pseudo header checksum is calculated and |
| * returned. |
| * |
| * The function returns the total length of the IP header (including any |
| * IP option fields), which is the same as the start offset of the IP data |
| * which in turn is the start offset of the TCP or UDP header. |
| * |
| * The function also returns the TCP or UDP pseudo header checksum, which |
| * should be used as the start value for the hardware checksum calculation. |
| * (Note that any actual pseudo header checksum can never calculate to |
| * zero.) |
| * |
| * Note: |
| * There is a bug in the GENESIS ASIC which may lead to wrong checksums. |
| * |
| * Arguments: |
| * pAc - A pointer to the adapter context struct. |
| * |
| * pIpHeader - Pointer to IP header. Must be at least the IP header *not* |
| * including any option fields, i.e. at least 20 bytes. |
| * |
| * Note: This pointer will be used to address 8-, 16-, and 32-bit |
| * variables with the respective alignment offsets relative to the pointer. |
| * Thus, the pointer should point to a 32-bit aligned address. If the |
| * target system cannot address 32-bit variables on non 32-bit aligned |
| * addresses, then the pointer *must* point to a 32-bit aligned address. |
| * |
| * pPacketInfo - A pointer to the packet information structure for this |
| * packet. Before calling this SkCsGetSendInfo(), the following field must |
| * be initialized: |
| * |
| * ProtocolFlags - Initialize with any combination of |
| * SKCS_PROTO_XXX bit flags. SkCsGetSendInfo() will only work on |
| * the protocols specified here. Any protocol(s) not specified |
| * here will be ignored. |
| * |
| * Note: Only one checksum can be calculated in hardware. Thus, if |
| * SKCS_PROTO_IP is specified in the 'ProtocolFlags', |
| * SkCsGetSendInfo() must calculate the IP header checksum in |
| * software. It might be a better idea to have the calling |
| * protocol stack calculate the IP header checksum. |
| * |
| * Returns: N/A |
| * On return, the following fields in 'pPacketInfo' may or may not have |
| * been filled with information, depending on the protocol(s) found in the |
| * packet: |
| * |
| * ProtocolFlags - Returns the SKCS_PROTO_XXX bit flags of the protocol(s) |
| * that were both requested by the caller and actually found in the packet. |
| * Protocol(s) not specified by the caller and/or not found in the packet |
| * will have their respective SKCS_PROTO_XXX bit flags reset. |
| * |
| * Note: For IP fragments, TCP and UDP packet information is ignored. |
| * |
| * IpHeaderLength - The total length in bytes of the complete IP header |
| * including any option fields is returned here. This is the start offset |
| * of the IP data, i.e. the TCP or UDP header if present. |
| * |
| * IpHeaderChecksum - If IP has been specified in the 'ProtocolFlags', the |
| * 16-bit Internet Checksum of the IP header is returned here. This value |
| * is to be stored into the packet's 'IP Header Checksum' field. |
| * |
| * PseudoHeaderChecksum - If this is a TCP or UDP packet and if TCP or UDP |
| * has been specified in the 'ProtocolFlags', the 16-bit Internet Checksum |
| * of the TCP or UDP pseudo header is returned here. |
| */ |
| void SkCsGetSendInfo( |
| SK_AC *pAc, /* Adapter context struct. */ |
| void *pIpHeader, /* IP header. */ |
| SKCS_PACKET_INFO *pPacketInfo, /* Packet information struct. */ |
| int NetNumber) /* Net number */ |
| { |
| /* Internet Header Version found in IP header. */ |
| unsigned InternetHeaderVersion; |
| |
| /* Length of the IP header as found in IP header. */ |
| unsigned IpHeaderLength; |
| |
| /* Bit field specifiying the desired/found protocols. */ |
| unsigned ProtocolFlags; |
| |
| /* Next level protocol identifier found in IP header. */ |
| unsigned NextLevelProtocol; |
| |
| /* Length of IP data portion. */ |
| unsigned IpDataLength; |
| |
| /* TCP/UDP pseudo header checksum. */ |
| unsigned long PseudoHeaderChecksum; |
| |
| /* Pointer to next level protocol statistics structure. */ |
| SKCS_PROTO_STATS *NextLevelProtoStats; |
| |
| /* Temporary variable. */ |
| unsigned Tmp; |
| |
| Tmp = *(SK_U8 *) |
| SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH); |
| |
| /* Get the Internet Header Version (IHV). */ |
| /* Note: The IHV is stored in the upper four bits. */ |
| |
| InternetHeaderVersion = Tmp >> 4; |
| |
| /* Check the Internet Header Version. */ |
| /* Note: We currently only support IP version 4. */ |
| |
| if (InternetHeaderVersion != 4) { /* IPv4? */ |
| SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX, |
| ("Tx: Unknown Internet Header Version %u.\n", |
| InternetHeaderVersion)); |
| pPacketInfo->ProtocolFlags = 0; |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++; |
| return; |
| } |
| |
| /* Get the IP header length (IHL). */ |
| /* |
| * Note: The IHL is stored in the lower four bits as the number of |
| * 4-byte words. |
| */ |
| |
| IpHeaderLength = (Tmp & 0xf) * 4; |
| pPacketInfo->IpHeaderLength = IpHeaderLength; |
| |
| /* Check the IP header length. */ |
| |
| /* 04-Aug-1998 sw - Really check the IHL? Necessary? */ |
| |
| if (IpHeaderLength < 5*4) { |
| SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX, |
| ("Tx: Invalid IP Header Length %u.\n", IpHeaderLength)); |
| pPacketInfo->ProtocolFlags = 0; |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++; |
| return; |
| } |
| |
| /* This is an IPv4 frame with a header of valid length. */ |
| |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxOkCts++; |
| |
| /* Check if we should calculate the IP header checksum. */ |
| |
| ProtocolFlags = pPacketInfo->ProtocolFlags; |
| |
| if (ProtocolFlags & SKCS_PROTO_IP) { |
| pPacketInfo->IpHeaderChecksum = |
| SkCsCalculateChecksum(pIpHeader, IpHeaderLength); |
| } |
| |
| /* Get the next level protocol identifier. */ |
| |
| NextLevelProtocol = |
| *(SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL); |
| |
| /* |
| * Check if this is a TCP or UDP frame and if we should calculate the |
| * TCP/UDP pseudo header checksum. |
| * |
| * Also clear all protocol bit flags of protocols not present in the |
| * frame. |
| */ |
| |
| if ((ProtocolFlags & SKCS_PROTO_TCP) != 0 && |
| NextLevelProtocol == SKCS_PROTO_ID_TCP) { |
| /* TCP/IP frame. */ |
| ProtocolFlags &= SKCS_PROTO_TCP | SKCS_PROTO_IP; |
| NextLevelProtoStats = |
| &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP]; |
| } |
| else if ((ProtocolFlags & SKCS_PROTO_UDP) != 0 && |
| NextLevelProtocol == SKCS_PROTO_ID_UDP) { |
| /* UDP/IP frame. */ |
| ProtocolFlags &= SKCS_PROTO_UDP | SKCS_PROTO_IP; |
| NextLevelProtoStats = |
| &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP]; |
| } |
| else { |
| /* |
| * Either not a TCP or UDP frame and/or TCP/UDP processing not |
| * specified. |
| */ |
| pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP; |
| return; |
| } |
| |
| /* Check if this is an IP fragment. */ |
| |
| /* |
| * Note: An IP fragment has a non-zero "Fragment Offset" field and/or |
| * the "More Fragments" bit set. Thus, if both the "Fragment Offset" |
| * and the "More Fragments" are zero, it is *not* a fragment. We can |
| * easily check both at the same time since they are in the same 16-bit |
| * word. |
| */ |
| |
| if ((*(SK_U16 *) |
| SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) & |
| ~SKCS_IP_DONT_FRAGMENT) != 0) { |
| /* IP fragment; ignore all other protocols. */ |
| pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP; |
| NextLevelProtoStats->TxUnableCts++; |
| return; |
| } |
| |
| /* |
| * Calculate the TCP/UDP pseudo header checksum. |
| */ |
| |
| /* Get total length of IP header and data. */ |
| |
| IpDataLength = |
| *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH); |
| |
| /* Get length of IP data portion. */ |
| |
| IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength; |
| |
| /* Calculate the sum of all pseudo header fields (16-bit). */ |
| |
| PseudoHeaderChecksum = |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_SOURCE_ADDRESS + 0) + |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_SOURCE_ADDRESS + 2) + |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_DESTINATION_ADDRESS + 0) + |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_DESTINATION_ADDRESS + 2) + |
| (unsigned long) SKCS_HTON16(NextLevelProtocol) + |
| (unsigned long) SKCS_HTON16(IpDataLength); |
| |
| /* Add-in any carries. */ |
| |
| SKCS_OC_ADD(PseudoHeaderChecksum, PseudoHeaderChecksum, 0); |
| |
| /* Add-in any new carry. */ |
| |
| SKCS_OC_ADD(pPacketInfo->PseudoHeaderChecksum, PseudoHeaderChecksum, 0); |
| |
| pPacketInfo->ProtocolFlags = ProtocolFlags; |
| NextLevelProtoStats->TxOkCts++; /* Success. */ |
| } /* SkCsGetSendInfo */ |
| |
| |
| /****************************************************************************** |
| * |
| * SkCsGetReceiveInfo - verify checksum information for a received packet |
| * |
| * Description: |
| * Verify a received frame's checksum. The function returns a status code |
| * reflecting the result of the verification. |
| * |
| * Note: |
| * Before calling this function you have to verify that the frame is |
| * not padded and Checksum1 and Checksum2 are bigger than 1. |
| * |
| * Arguments: |
| * pAc - Pointer to adapter context struct. |
| * |
| * pIpHeader - Pointer to IP header. Must be at least the length in bytes |
| * of the received IP header including any option fields. For UDP packets, |
| * 8 additional bytes are needed to access the UDP checksum. |
| * |
| * Note: The actual length of the IP header is stored in the lower four |
| * bits of the first octet of the IP header as the number of 4-byte words, |
| * so it must be multiplied by four to get the length in bytes. Thus, the |
| * maximum IP header length is 15 * 4 = 60 bytes. |
| * |
| * Checksum1 - The first 16-bit Internet Checksum calculated by the |
| * hardware starting at the offset returned by SkCsSetReceiveFlags(). |
| * |
| * Checksum2 - The second 16-bit Internet Checksum calculated by the |
| * hardware starting at the offset returned by SkCsSetReceiveFlags(). |
| * |
| * NetNumber - The net number. |
| * |
| * Len - The packet length (without MAC header) - never access memory |
| * behind pIpHeader[Len]. |
| * |
| * Returns: |
| * SKCS_STATUS_UNKNOWN_IP_VERSION - Not an IP v4 frame. |
| * SKCS_STATUS_IP_CSUM_ERROR - IP checksum error. |
| * SKCS_STATUS_IP_CSUM_ERROR_TCP - IP checksum error in TCP frame. |
| * SKCS_STATUS_IP_CSUM_ERROR_UDP - IP checksum error in UDP frame |
| * SKCS_STATUS_IP_FRAGMENT - IP fragment (IP checksum ok). |
| * SKCS_STATUS_IP_CSUM_OK - IP checksum ok (not a TCP or UDP frame). |
| * SKCS_STATUS_TCP_CSUM_ERROR - TCP checksum error (IP checksum ok). |
| * SKCS_STATUS_UDP_CSUM_ERROR - UDP checksum error (IP checksum ok). |
| * SKCS_STATUS_TCP_CSUM_OK - IP and TCP checksum ok. |
| * SKCS_STATUS_UDP_CSUM_OK - IP and UDP checksum ok. |
| * SKCS_STATUS_IP_CSUM_OK_NO_UDP - IP checksum OK and no UDP checksum. |
| * SKCS_STATUS_NO_CSUM_POSSIBLE - Checksum could not be built (various reasons). |
| * |
| * Note: If SKCS_OVERWRITE_STATUS is defined, the SKCS_STATUS_XXX values |
| * returned here can be defined in some header file by the module using CSUM. |
| * In this way, the calling module can assign return values for its own needs, |
| * e.g. by assigning bit flags to the individual protocols. |
| */ |
| SKCS_STATUS SkCsGetReceiveInfo( |
| SK_AC *pAc, /* Adapter context struct. */ |
| void *pIpHeader, /* IP header. */ |
| unsigned Checksum1, /* Hardware checksum 1. */ |
| unsigned Checksum2, /* Hardware checksum 2. */ |
| int NetNumber, /* Net number. */ |
| unsigned Len) /* Packet length (without MAC header). */ |
| { |
| /* Internet Header Version found in IP header. */ |
| unsigned InternetHeaderVersion; |
| |
| /* Length of the IP header as found in IP header. */ |
| unsigned IpHeaderLength; |
| |
| /* Length of IP data portion. */ |
| unsigned IpDataLength; |
| |
| /* IP header checksum. */ |
| unsigned IpHeaderChecksum; |
| |
| /* IP header options checksum, if any. */ |
| unsigned IpOptionsChecksum; |
| |
| /* IP data checksum, i.e. TCP/UDP checksum. */ |
| unsigned IpDataChecksum; |
| |
| /* Next level protocol identifier found in IP header. */ |
| unsigned NextLevelProtocol; |
| |
| /* The checksum of the "next level protocol", i.e. TCP or UDP. */ |
| unsigned long NextLevelProtocolChecksum; |
| |
| /* Pointer to next level protocol statistics structure. */ |
| SKCS_PROTO_STATS *NextLevelProtoStats; |
| |
| /* Temporary variable. */ |
| unsigned Tmp; |
| |
| #ifdef SK_IPV6_SUPPORT |
| /* For parsing IPv6 packets */ |
| SK_U8 *pDst; |
| SK_U32 ProtCsum; |
| SK_U32 Pseudo; |
| SK_U32 Pseudo2; |
| SK_U16 Offset; |
| SK_U16 NextHeaderOffset; |
| SK_U16 OptSize; |
| SK_U8 NextHeader; |
| #endif |
| |
| if (SKCS_IDX_CHECK(Len, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH)) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| |
| Tmp = *(SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH); |
| |
| /* Get the Internet Header Version (IHV). */ |
| /* Note: The IHV is stored in the upper four bits. */ |
| |
| InternetHeaderVersion = Tmp >> 4; |
| |
| /* Check the Internet Header Version. */ |
| |
| #ifdef SK_IPV6_SUPPORT |
| if (InternetHeaderVersion == 6) { /* IPv6? */ |
| |
| if ((pAc->Csum.ReceiveFlags[NetNumber] & |
| (SKCS_PROTO_TCPV6 | SKCS_PROTO_UDPV6)) == 0) { |
| |
| /* The OS/stack told us not to verify IPv6 checksum. */ |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| |
| /* Get the payload length. the payload starts immediately after the |
| * IPv6 header. This value includes all additional headers! |
| */ |
| /* first test if the frame has at least the length of the IPv6 header */ |
| if (Len < SKCS_IP6_HEADER_SIZE) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| IpDataLength = *(SK_U16 *) |
| SKCS_IDX(pIpHeader, SKCS_OFS_IP6_PAYLOAD_LENGTH); |
| IpDataLength = SKCS_NTOH16(IpDataLength); |
| |
| /* Before we can proceed we need to find the protocol frame that |
| * contains the checksum. We support TCP and UDP. |
| */ |
| |
| Offset = SKCS_IP6_HEADER_SIZE; |
| NextHeaderOffset = SKCS_OFS_IP6_NEXT_HEADER; |
| /* this access has been secured by checking frame len against SKCS_IP6_HEADER_SIZE */ |
| NextHeader = *(SK_U8 *) SKCS_IDX(pIpHeader, NextHeaderOffset); |
| |
| pDst = (SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP6_DESTINATION_ADDRESS); |
| |
| do { |
| if ((NextHeader == SKCS_PROTO_ID_TCP) || |
| (NextHeader == SKCS_PROTO_ID_UDP)) { |
| |
| /* We found the TCP/UDP header */ |
| break; |
| } |
| |
| /* Check type of next header to find "Next Header" field */ |
| switch (NextHeader) { |
| |
| case 0: /* hop-by-hop options header */ |
| case 60: /* Destination options header */ |
| NextHeaderOffset = Offset; |
| |
| /* |
| * Calculate the size of the option header. The size is in |
| * units of 8-octets and does not contain the first 8 octets. |
| */ |
| if (SKCS_IDX_CHECK(Len, (unsigned) (NextHeaderOffset + 1))) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| OptSize = *(SK_U8 *) SKCS_IDX(pIpHeader, NextHeaderOffset + 1); |
| OptSize *= 8; |
| Offset += OptSize + 8; |
| break; |
| |
| case 43: /* routing header */ |
| NextHeaderOffset = Offset; |
| |
| /* |
| * Calculate the size of the option header. The size is in |
| * units of 8-octets and does not contain the first 8 octets. |
| */ |
| if (SKCS_IDX_CHECK(Len, (unsigned) (NextHeaderOffset + 1))) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| OptSize = *(SK_U8 *) SKCS_IDX(pIpHeader, NextHeaderOffset + 1); |
| OptSize *= 8; |
| Offset += OptSize + 8; |
| |
| /* |
| * If SegLeft != 0, use the last address in the routing header |
| * as IP dst in the pseudo header (for both, type 0 and type 2). |
| */ |
| if (*(SK_U8 *) SKCS_IDX(pIpHeader, NextHeaderOffset + 3) != 0) { |
| SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, |
| ("Rx: IPv6 Routing Header with SegLeft != 0.\n")); |
| pDst = (SK_U8 *) SKCS_IDX(pIpHeader, Offset - 16); |
| } |
| break; |
| |
| case 44: /* fragment header */ |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_IP_FRAGMENT); |
| |
| case 46: /* resource ReSerVation protocol */ |
| case 41: /* encapsulated IPv6 header */ |
| case 59: /* No next header */ |
| SK_DBG_MSG( |
| pAc, |
| SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, |
| ("Rx: Header in IPv6 packet can not be processed: %u.\n", |
| NextHeader)); |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| |
| case 51: /* authentication header, would have to use OptSize *= 4! */ |
| case 50: /* encapsulating security payload */ |
| case 58: /* ICMP v6 */ |
| default: |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| |
| /* Continue with the next header */ |
| if (SKCS_IDX_CHECK(Len, NextHeaderOffset)) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| NextHeader = *(SK_U8 *) SKCS_IDX(pIpHeader, NextHeaderOffset); |
| } while (Offset < (IpDataLength + SKCS_IP6_HEADER_SIZE)); |
| |
| if (Offset >= (IpDataLength + SKCS_IP6_HEADER_SIZE)) { |
| /* We passed the end of the packet without finding the payload. */ |
| SK_DBG_MSG( |
| pAc, |
| SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, |
| ("Rx: malformed IPv6 packet.\n")); |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| |
| /* Before starting checksum calculating, check if we really need to. */ |
| if ((NextHeader == SKCS_PROTO_ID_TCP) && |
| (((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_TCPV6) == 0))) { |
| |
| /* Header is TCP but we shall not verify TCP checksum. */ |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| if ((NextHeader == SKCS_PROTO_ID_UDP) && |
| (((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_UDPV6) == 0))) { |
| |
| /* Header is UDP but we shall not verify UDP checksum. */ |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| |
| if (IpDataLength + SKCS_IP6_HEADER_SIZE > Len) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| ProtCsum = SkCsCalculateChecksum( |
| SKCS_IDX(pIpHeader, Offset), |
| IpDataLength + SKCS_IP6_HEADER_SIZE -Offset); |
| |
| /* |
| * Calculate the pseudo header checksum. |
| */ |
| |
| /* Start with src and dest address */ |
| /* this access has been secured by checking frame len against SKCS_IP6_HEADER_SIZE */ |
| Pseudo = SkCsCalculateChecksum( |
| SKCS_IDX(pIpHeader, SKCS_OFS_IP6_SOURCE_ADDRESS), 16); |
| |
| Pseudo2 = SkCsCalculateChecksum(pDst, 16); |
| SKCS_OC_ADD(Pseudo, Pseudo, Pseudo2); |
| SKCS_OC_ADD(Pseudo, Pseudo, 0); /* Add-in any carries. */ |
| |
| SKCS_OC_ADD(Pseudo, Pseudo, |
| SKCS_HTON16(IpDataLength + SKCS_IP6_HEADER_SIZE -Offset)); |
| SKCS_OC_ADD(Pseudo, Pseudo, SKCS_HTON16(NextHeader)); |
| SKCS_OC_ADD(Pseudo, Pseudo, 0); /* Add-in any carries. */ |
| |
| /* Add to protocol part's checksum */ |
| SKCS_OC_ADD(ProtCsum, ProtCsum, Pseudo); |
| SKCS_OC_ADD(ProtCsum, ProtCsum, 0); /* Add-in any carries. */ |
| |
| /* |
| * Check result. We can only process TCP or UDP. |
| */ |
| switch (NextHeader) { |
| case SKCS_PROTO_ID_TCP: |
| if (ProtCsum != 0xFFFF) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP].RxErrCts++; |
| return (SKCS_STATUS_TCP_CSUM_ERROR); |
| } |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP].RxOkCts++; |
| return (SKCS_STATUS_TCP_CSUM_OK); |
| |
| case SKCS_PROTO_ID_UDP: |
| |
| if (ProtCsum != 0xFFFF) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP].RxErrCts++; |
| return (SKCS_STATUS_UDP_CSUM_ERROR); |
| } |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP].RxOkCts++; |
| return (SKCS_STATUS_UDP_CSUM_OK); |
| |
| default: |
| /* This case should already be eliminated because we only compute |
| * the checksum for TCP, UDP or ICMPv6 |
| */ |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| } |
| #endif /* SK_IPV6_SUPPORT */ |
| |
| if (InternetHeaderVersion != 4) { /* IPv4? */ |
| SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, |
| ("Rx: Unknown Internet Header Version %u.\n", |
| InternetHeaderVersion)); |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_UNKNOWN_IP_VERSION); |
| } |
| |
| /* Get the IP header length (IHL). */ |
| /* |
| * Note: The IHL is stored in the lower four bits as the number of |
| * 4-byte words. |
| */ |
| |
| IpHeaderLength = (Tmp & 0xf) * 4; |
| |
| /* Check the IP header length. */ |
| |
| /* 04-Aug-1998 sw - Really check the IHL? Necessary? */ |
| |
| if (IpHeaderLength < 5*4) { |
| SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, |
| ("Rx: Invalid IP Header Length %u.\n", IpHeaderLength)); |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++; |
| return (SKCS_STATUS_IP_CSUM_ERROR); |
| } |
| |
| /* This is an IPv4 frame with a header of valid length. */ |
| |
| /* First test if the frame has at least the length of the IP header. */ |
| if (Len < IpHeaderLength) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| |
| /* Get the IP header and data checksum. */ |
| IpDataChecksum = Checksum2; |
| |
| /* Get the next level protocol identifier. */ |
| /* this access has been secured by checking frame len against IpHeaderLength */ |
| NextLevelProtocol = *(SK_U8 *) |
| SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL); |
| |
| if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_IP) != 0) { |
| |
| /* IPv4 checksum verification is specified */ |
| |
| /* |
| * The IP header checksum is calculated as follows: |
| * |
| * IpHeaderChecksum = Checksum1 - Checksum2 |
| */ |
| |
| SKCS_OC_SUB(IpHeaderChecksum, Checksum1, Checksum2); |
| |
| /* Check if any IP header options. */ |
| |
| if (IpHeaderLength > SKCS_IP_HEADER_SIZE) { |
| |
| /* |
| * Get the IP options checksum. |
| * |
| * This access has been secured by checking the frame length |
| * against IpHeaderLength. |
| */ |
| IpOptionsChecksum = SkCsCalculateChecksum( |
| SKCS_IDX(pIpHeader, SKCS_IP_HEADER_SIZE), |
| IpHeaderLength - SKCS_IP_HEADER_SIZE); |
| |
| /* Adjust the IP header and IP data checksums. */ |
| |
| SKCS_OC_ADD(IpHeaderChecksum, IpHeaderChecksum, IpOptionsChecksum); |
| |
| SKCS_OC_SUB(IpDataChecksum, IpDataChecksum, IpOptionsChecksum); |
| } |
| |
| /* |
| * Check if the IP header checksum is ok. |
| * |
| * NOTE: We must check the IP header checksum even if the caller |
| * just wants us to check upper-layer checksums, because we cannot do |
| * any further processing of the packet without a valid IP checksum. |
| */ |
| |
| if (IpHeaderChecksum != 0xffff) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++; |
| /* The NDIS tester wants to know the upper level protocol, too. */ |
| if (NextLevelProtocol == SKCS_PROTO_ID_TCP) { |
| return(SKCS_STATUS_IP_CSUM_ERROR_TCP); |
| } |
| else if (NextLevelProtocol == SKCS_PROTO_ID_UDP) { |
| return(SKCS_STATUS_IP_CSUM_ERROR_UDP); |
| } |
| return (SKCS_STATUS_IP_CSUM_ERROR); |
| } |
| } |
| |
| /* |
| * Check if this is a TCP or UDP frame and if we should calculate the |
| * TCP/UDP pseudo header checksum. |
| * |
| * Also clear all protocol bit flags of protocols not present in the |
| * frame. |
| */ |
| |
| if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_TCP) != 0 && |
| NextLevelProtocol == SKCS_PROTO_ID_TCP) { |
| /* TCP/IP frame. */ |
| NextLevelProtoStats = |
| &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP]; |
| } |
| else if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_UDP) != 0 && |
| NextLevelProtocol == SKCS_PROTO_ID_UDP) { |
| /* UDP/IP frame. */ |
| NextLevelProtoStats = |
| &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP]; |
| } |
| else { |
| /* |
| * Either not a TCP or UDP frame and/or TCP/UDP processing not |
| * specified. |
| */ |
| return (SKCS_STATUS_IP_CSUM_OK); |
| } |
| |
| /* Check if this is an IP fragment. */ |
| |
| /* |
| * Note: An IP fragment has a non-zero "Fragment Offset" field and/or |
| * the "More Fragments" bit set. Thus, if both the "Fragment Offset" |
| * and the "More Fragments" are zero, it is *not* a fragment. We can |
| * easily check both at the same time since they are in the same 16-bit |
| * word. |
| * |
| * This access has been secured by checking the frame length |
| * against IpHeaderLength. |
| */ |
| |
| if ((*(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) & |
| ~SKCS_IP_DONT_FRAGMENT) != 0) { |
| /* IP fragment; ignore all other protocols. */ |
| NextLevelProtoStats->RxUnableCts++; |
| return (SKCS_STATUS_IP_FRAGMENT); |
| } |
| |
| if (SKCS_IDX_CHECK(Len, (IpHeaderLength + 6 + 1))) { |
| pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| return (SKCS_STATUS_NO_CSUM_POSSIBLE); |
| } |
| |
| /* |
| * A checksum value of 0 in UDP over IPv4 frames means that no checksum |
| * was inserted by the sender (RFC 768). |
| */ |
| |
| if (NextLevelProtocol == SKCS_PROTO_ID_UDP && |
| *(SK_U16 *) SKCS_IDX(pIpHeader, IpHeaderLength + 6) == 0x0000) { |
| |
| NextLevelProtoStats->RxOkCts++; |
| |
| return (SKCS_STATUS_IP_CSUM_OK_NO_UDP); |
| } |
| |
| /* |
| * Calculate the TCP/UDP checksum. |
| */ |
| |
| /* |
| * Get total length of IP header and data. |
| * |
| * This access has been secured by checking the frame length |
| * against IpHeaderLength. |
| */ |
| |
| IpDataLength = |
| *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH); |
| |
| /* Get length of IP data portion. */ |
| |
| IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength; |
| |
| /* |
| * This access has been secured by checking the frame length |
| * against IpHeaderLength. |
| */ |
| |
| NextLevelProtocolChecksum = |
| |
| /* Calculate the pseudo header checksum. */ |
| |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_SOURCE_ADDRESS + 0) + |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_SOURCE_ADDRESS + 2) + |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_DESTINATION_ADDRESS + 0) + |
| (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| SKCS_OFS_IP_DESTINATION_ADDRESS + 2) + |
| (unsigned long) SKCS_HTON16(NextLevelProtocol) + |
| (unsigned long) SKCS_HTON16(IpDataLength) + |
| |
| /* Add the TCP/UDP header checksum. */ |
| |
| (unsigned long) IpDataChecksum; |
| |
| /* Add-in any carries. */ |
| |
| SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0); |
| |
| /* Add-in any new carry. */ |
| |
| SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0); |
| |
| /* Check if the TCP/UDP checksum is ok. */ |
| |
| if ((unsigned) NextLevelProtocolChecksum == 0xffff) { |
| |
| /* TCP/UDP checksum ok. */ |
| |
| NextLevelProtoStats->RxOkCts++; |
| |
| return (NextLevelProtocol == SKCS_PROTO_ID_TCP ? |
| SKCS_STATUS_TCP_CSUM_OK : SKCS_STATUS_UDP_CSUM_OK); |
| } |
| |
| /* TCP/UDP checksum error. */ |
| |
| NextLevelProtoStats->RxErrCts++; |
| |
| return (NextLevelProtocol == SKCS_PROTO_ID_TCP ? |
| SKCS_STATUS_TCP_CSUM_ERROR : SKCS_STATUS_UDP_CSUM_ERROR); |
| } /* SkCsGetReceiveInfo */ |
| |
| |
| /****************************************************************************** |
| * |
| * SkCsSetReceiveFlags - set checksum receive flags |
| * |
| * Description: |
| * Use this function to set the various receive flags. According to the |
| * protocol flags set by the caller, the start offsets within received |
| * packets of the two hardware checksums are returned. These offsets must |
| * be stored in all receive descriptors. |
| * |
| * Arguments: |
| * pAc - Pointer to adapter context struct. |
| * |
| * ReceiveFlags - Any combination of SK_PROTO_XXX flags of the protocols |
| * for which the caller wants checksum information on received frames. |
| * |
| * pChecksum1Offset - The start offset of the first receive descriptor |
| * hardware checksum to be calculated for received frames is returned |
| * here. |
| * |
| * pChecksum2Offset - The start offset of the second receive descriptor |
| * hardware checksum to be calculated for received frames is returned |
| * here. |
| * |
| * Returns: N/A |
| * Returns the two hardware checksum start offsets. |
| */ |
| void SkCsSetReceiveFlags( |
| SK_AC *pAc, /* Adapter context struct. */ |
| unsigned ReceiveFlags, /* New receive flags. */ |
| unsigned *pChecksum1Offset, /* Offset for hardware checksum 1. */ |
| unsigned *pChecksum2Offset, /* Offset for hardware checksum 2. */ |
| int NetNumber) |
| { |
| /* Save the receive flags. */ |
| |
| pAc->Csum.ReceiveFlags[NetNumber] = ReceiveFlags; |
| |
| /* First checksum start offset is the IP header. */ |
| *pChecksum1Offset = SKCS_MAC_HEADER_SIZE; |
| |
| /* |
| * Second checksum start offset is the IP data. Note that this may vary |
| * if there are any IP header options in the actual packet. |
| */ |
| *pChecksum2Offset = SKCS_MAC_HEADER_SIZE + SKCS_IP_HEADER_SIZE; |
| } /* SkCsSetReceiveFlags */ |
| |
| #ifndef SK_CS_CALCULATE_CHECKSUM |
| |
| /****************************************************************************** |
| * |
| * SkCsCalculateChecksum - calculate checksum for specified data |
| * |
| * Description: |
| * Calculate and return the 16-bit Internet Checksum for the specified |
| * data. |
| * |
| * Arguments: |
| * pData - Pointer to data for which the checksum shall be calculated. |
| * Note: The pointer should be aligned on a 16-bit boundary. |
| * |
| * Length - Length in bytes of data to checksum. |
| * |
| * Returns: |
| * The 16-bit Internet Checksum for the specified data. |
| * |
| * Note: The checksum is calculated in the machine's natural byte order, |
| * i.e. little vs. big endian. Thus, the resulting checksum is different |
| * for the same input data on little and big endian machines. |
| * |
| * However, when written back to the network packet, the byte order is |
| * always in correct network order. |
| */ |
| unsigned SkCsCalculateChecksum( |
| void *pData, /* Data to checksum. */ |
| unsigned Length) /* Length of data. */ |
| { |
| SK_U16 *pU16; /* Pointer to the data as 16-bit words. */ |
| unsigned long Checksum; /* Checksum; must be at least 32 bits. */ |
| |
| /* Sum up all 16-bit words. */ |
| |
| pU16 = (SK_U16 *) pData; |
| for (Checksum = 0; Length > 1; Length -= 2) { |
| Checksum += *pU16++; |
| } |
| |
| /* If this is an odd number of bytes, add-in the last byte. */ |
| |
| if (Length > 0) { |
| #ifdef SK_BIG_ENDIAN |
| /* Add the last byte as the high byte. */ |
| Checksum += ((unsigned) *(SK_U8 *) pU16) << 8; |
| #else /* !SK_BIG_ENDIAN */ |
| /* Add the last byte as the low byte. */ |
| Checksum += *(SK_U8 *) pU16; |
| #endif /* !SK_BIG_ENDIAN */ |
| } |
| |
| /* Add-in any carries. */ |
| |
| SKCS_OC_ADD(Checksum, Checksum, 0); |
| |
| /* Add-in any new carry. */ |
| |
| SKCS_OC_ADD(Checksum, Checksum, 0); |
| |
| /* Note: All bits beyond the 16-bit limit are now zero. */ |
| |
| return ((unsigned) Checksum); |
| } /* SkCsCalculateChecksum */ |
| |
| #endif /* SK_CS_CALCULATE_CHECKSUM */ |
| |
| /****************************************************************************** |
| * |
| * SkCsEvent - the CSUM event dispatcher |
| * |
| * Description: |
| * This is the event handler for the CSUM module. |
| * |
| * Arguments: |
| * pAc - Pointer to adapter context. |
| * |
| * Ioc - I/O context. |
| * |
| * Event - Event id. |
| * |
| * Param - Event dependent parameter. |
| * |
| * Returns: |
| * 0 (Success) |
| */ |
| int SkCsEvent( |
| SK_AC *pAc, /* Pointer to adapter context. */ |
| SK_IOC Ioc, /* I/O context. */ |
| SK_U32 Event, /* Event id. */ |
| SK_EVPARA Param) /* Event dependent parameter. */ |
| { |
| int ProtoIndex; |
| int NetNumber; |
| |
| switch (Event) { |
| /* |
| * Clear protocol statistics. |
| * |
| * Param - Protocol index, or -1 for all protocols. |
| * - Net number. |
| */ |
| case SK_CSUM_EVENT_CLEAR_PROTO_STATS: |
| ProtoIndex = (int)Param.Para32[1]; |
| NetNumber = (int)Param.Para32[0]; |
| if (ProtoIndex < 0) { /* Clear for all protocols. */ |
| if (NetNumber >= 0) { |
| SK_MEMSET(&pAc->Csum.ProtoStats[NetNumber][0], 0, |
| sizeof(pAc->Csum.ProtoStats[NetNumber])); |
| } |
| } |
| else { /* Clear for individual protocol. */ |
| SK_MEMSET(&pAc->Csum.ProtoStats[NetNumber][ProtoIndex], 0, |
| sizeof(pAc->Csum.ProtoStats[NetNumber][ProtoIndex])); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| return (0); /* Success. */ |
| } /* SkCsEvent */ |
| |
| #endif /* SK_USE_CSUM */ |