1 /******************************************************************************
4 * Project: GEnesis, PCI Gigabit Ethernet Adapter
5 * Version: $Revision: 1.10 $
6 * Date: $Date: 2002/04/11 10:02:04 $
7 * Purpose: Store/verify Internet checksum in send/receive packets.
9 ******************************************************************************/
11 /******************************************************************************
13 * (C)Copyright 1998-2001 SysKonnect GmbH.
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * The information in this file is provided "AS IS" without warranty.
22 ******************************************************************************/
24 /******************************************************************************
29 * Revision 1.10 2002/04/11 10:02:04 rwahl
30 * Fix in SkCsGetSendInfo():
31 * - function did not return ProtocolFlags in every case.
32 * - pseudo header csum calculated wrong for big endian.
34 * Revision 1.9 2001/06/13 07:42:08 gklug
35 * fix: NetNumber was wrong in CLEAR_STAT event
36 * add: check for good NetNumber in Clear STAT
38 * Revision 1.8 2001/02/06 11:15:36 rassmann
39 * Supporting two nets on dual-port adapters.
41 * Revision 1.7 2000/06/29 13:17:05 rassmann
42 * Corrected reception of a packet with UDP checksum == 0 (which means there
43 * is no UDP checksum).
45 * Revision 1.6 2000/02/21 12:35:10 cgoos
46 * Fixed license header comment.
48 * Revision 1.5 2000/02/21 11:05:19 cgoos
49 * Merged changes back to common source.
50 * Fixed rx path for BIG ENDIAN architecture.
52 * Revision 1.1 1999/07/26 15:28:12 mkarl
53 * added return SKCS_STATUS_IP_CSUM_ERROR_UDP and
54 * SKCS_STATUS_IP_CSUM_ERROR_TCP to pass the NidsTester
55 * changed from common source to windows specific source
56 * therefore restarting with v1.0
58 * Revision 1.3 1999/05/10 08:39:33 mkarl
59 * prevent overflows in SKCS_HTON16
60 * fixed a bug in pseudo header checksum calculation
63 * Revision 1.2 1998/10/22 11:53:28 swolf
64 * Now using SK_DBG_MSG.
66 * Revision 1.1 1998/09/01 15:35:41 swolf
69 * 13-May-1998 sw Created.
71 ******************************************************************************/
73 #ifdef SK_USE_CSUM /* Check if CSUM is to be used. */
76 static const char SysKonnectFileId[] = "@(#)"
77 "$Id: skcsum.c,v 1.10 2002/04/11 10:02:04 rwahl Exp $"
81 /******************************************************************************
85 * This is the "GEnesis" common module "CSUM".
87 * This module contains the code necessary to calculate, store, and verify the
88 * Internet Checksum of IP, TCP, and UDP frames.
90 * "GEnesis" is an abbreviation of "Gigabit Ethernet Network System in Silicon"
91 * and is the code name of this SysKonnect project.
93 * Compilation Options:
95 * SK_USE_CSUM - Define if CSUM is to be used. Otherwise, CSUM will be an
98 * SKCS_OVERWRITE_PROTO - Define to overwrite the default protocol id
99 * definitions. In this case, all SKCS_PROTO_xxx definitions must be made
102 * SKCS_OVERWRITE_STATUS - Define to overwrite the default return status
103 * definitions. In this case, all SKCS_STATUS_xxx definitions must be made
106 * Include File Hierarchy:
114 ******************************************************************************/
116 #include "h/skdrv1st.h"
117 #include "h/skcsum.h"
118 #include "h/skdrv2nd.h"
120 /* defines ********************************************************************/
122 /* The size of an Ethernet MAC header. */
123 #define SKCS_ETHERNET_MAC_HEADER_SIZE (6+6+2)
125 /* The size of the used topology's MAC header. */
126 #define SKCS_MAC_HEADER_SIZE SKCS_ETHERNET_MAC_HEADER_SIZE
128 /* The size of the IP header without any option fields. */
129 #define SKCS_IP_HEADER_SIZE 20
132 * Field offsets within the IP header.
135 /* "Internet Header Version" and "Length". */
136 #define SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH 0
138 /* "Total Length". */
139 #define SKCS_OFS_IP_TOTAL_LENGTH 2
141 /* "Flags" "Fragment Offset". */
142 #define SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET 6
144 /* "Next Level Protocol" identifier. */
145 #define SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL 9
147 /* Source IP address. */
148 #define SKCS_OFS_IP_SOURCE_ADDRESS 12
150 /* Destination IP address. */
151 #define SKCS_OFS_IP_DESTINATION_ADDRESS 16
155 * Field offsets within the UDP header.
159 #define SKCS_OFS_UDP_CHECKSUM 6
161 /* IP "Next Level Protocol" identifiers (see RFC 790). */
162 #define SKCS_PROTO_ID_TCP 6 /* Transport Control Protocol */
163 #define SKCS_PROTO_ID_UDP 17 /* User Datagram Protocol */
165 /* IP "Don't Fragment" bit. */
166 #define SKCS_IP_DONT_FRAGMENT SKCS_HTON16(0x4000)
168 /* Add a byte offset to a pointer. */
169 #define SKCS_IDX(pPtr, Ofs) ((void *) ((char *) (pPtr) + (Ofs)))
172 * Macros that convert host to network representation and vice versa, i.e.
173 * little/big endian conversion on little endian machines only.
175 #ifdef SK_LITTLE_ENDIAN
176 #define SKCS_HTON16(Val16) (((unsigned) (Val16) >> 8) | (((Val16) & 0xFF) << 8))
177 #endif /* SK_LITTLE_ENDIAN */
179 #define SKCS_HTON16(Val16) (Val16)
180 #endif /* SK_BIG_ENDIAN */
181 #define SKCS_NTOH16(Val16) SKCS_HTON16(Val16)
183 /* typedefs *******************************************************************/
185 /* function prototypes ********************************************************/
187 /******************************************************************************
189 * SkCsGetSendInfo - get checksum information for a send packet
192 * Get all checksum information necessary to send a TCP or UDP packet. The
193 * function checks the IP header passed to it. If the high-level protocol
194 * is either TCP or UDP the pseudo header checksum is calculated and
197 * The function returns the total length of the IP header (including any
198 * IP option fields), which is the same as the start offset of the IP data
199 * which in turn is the start offset of the TCP or UDP header.
201 * The function also returns the TCP or UDP pseudo header checksum, which
202 * should be used as the start value for the hardware checksum calculation.
203 * (Note that any actual pseudo header checksum can never calculate to
207 * There is a bug in the ASIC which may lead to wrong checksums.
210 * pAc - A pointer to the adapter context struct.
212 * pIpHeader - Pointer to IP header. Must be at least the IP header *not*
213 * including any option fields, i.e. at least 20 bytes.
215 * Note: This pointer will be used to address 8-, 16-, and 32-bit
216 * variables with the respective alignment offsets relative to the pointer.
217 * Thus, the pointer should point to a 32-bit aligned address. If the
218 * target system cannot address 32-bit variables on non 32-bit aligned
219 * addresses, then the pointer *must* point to a 32-bit aligned address.
221 * pPacketInfo - A pointer to the packet information structure for this
222 * packet. Before calling this SkCsGetSendInfo(), the following field must
225 * ProtocolFlags - Initialize with any combination of
226 * SKCS_PROTO_XXX bit flags. SkCsGetSendInfo() will only work on
227 * the protocols specified here. Any protocol(s) not specified
228 * here will be ignored.
230 * Note: Only one checksum can be calculated in hardware. Thus, if
231 * SKCS_PROTO_IP is specified in the 'ProtocolFlags',
232 * SkCsGetSendInfo() must calculate the IP header checksum in
233 * software. It might be a better idea to have the calling
234 * protocol stack calculate the IP header checksum.
237 * On return, the following fields in 'pPacketInfo' may or may not have
238 * been filled with information, depending on the protocol(s) found in the
241 * ProtocolFlags - Returns the SKCS_PROTO_XXX bit flags of the protocol(s)
242 * that were both requested by the caller and actually found in the packet.
243 * Protocol(s) not specified by the caller and/or not found in the packet
244 * will have their respective SKCS_PROTO_XXX bit flags reset.
246 * Note: For IP fragments, TCP and UDP packet information is ignored.
248 * IpHeaderLength - The total length in bytes of the complete IP header
249 * including any option fields is returned here. This is the start offset
250 * of the IP data, i.e. the TCP or UDP header if present.
252 * IpHeaderChecksum - If IP has been specified in the 'ProtocolFlags', the
253 * 16-bit Internet Checksum of the IP header is returned here. This value
254 * is to be stored into the packet's 'IP Header Checksum' field.
256 * PseudoHeaderChecksum - If this is a TCP or UDP packet and if TCP or UDP
257 * has been specified in the 'ProtocolFlags', the 16-bit Internet Checksum
258 * of the TCP or UDP pseudo header is returned here.
261 void SkCsGetSendInfo(
262 SK_AC *pAc, /* Adapter context struct. */
263 void *pIpHeader, /* IP header. */
264 SKCS_PACKET_INFO *pPacketInfo, /* Packet information struct. */
265 int NetNumber) /* Net number */
267 /* Internet Header Version found in IP header. */
268 unsigned InternetHeaderVersion;
270 /* Length of the IP header as found in IP header. */
271 unsigned IpHeaderLength;
273 /* Bit field specifiying the desired/found protocols. */
274 unsigned ProtocolFlags;
276 /* Next level protocol identifier found in IP header. */
277 unsigned NextLevelProtocol;
279 /* Length of IP data portion. */
280 unsigned IpDataLength;
282 /* TCP/UDP pseudo header checksum. */
283 unsigned long PseudoHeaderChecksum;
285 /* Pointer to next level protocol statistics structure. */
286 SKCS_PROTO_STATS *NextLevelProtoStats;
288 /* Temporary variable. */
292 SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH);
294 /* Get the Internet Header Version (IHV). */
295 /* Note: The IHV is stored in the upper four bits. */
297 InternetHeaderVersion = Tmp >> 4;
299 /* Check the Internet Header Version. */
300 /* Note: We currently only support IP version 4. */
302 if (InternetHeaderVersion != 4) { /* IPv4? */
303 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX,
304 ("Tx: Unknown Internet Header Version %u.\n",
305 InternetHeaderVersion));
306 pPacketInfo->ProtocolFlags = 0;
307 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++;
311 /* Get the IP header length (IHL). */
313 * Note: The IHL is stored in the lower four bits as the number of
317 IpHeaderLength = (Tmp & 0xf) * 4;
318 pPacketInfo->IpHeaderLength = IpHeaderLength;
320 /* Check the IP header length. */
322 /* 04-Aug-1998 sw - Really check the IHL? Necessary? */
324 if (IpHeaderLength < 5*4) {
325 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX,
326 ("Tx: Invalid IP Header Length %u.\n", IpHeaderLength));
327 pPacketInfo->ProtocolFlags = 0;
328 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++;
332 /* This is an IPv4 frame with a header of valid length. */
334 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxOkCts++;
336 /* Check if we should calculate the IP header checksum. */
338 ProtocolFlags = pPacketInfo->ProtocolFlags;
340 if (ProtocolFlags & SKCS_PROTO_IP) {
341 pPacketInfo->IpHeaderChecksum =
342 SkCsCalculateChecksum(pIpHeader, IpHeaderLength);
345 /* Get the next level protocol identifier. */
348 *(SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL);
351 * Check if this is a TCP or UDP frame and if we should calculate the
352 * TCP/UDP pseudo header checksum.
354 * Also clear all protocol bit flags of protocols not present in the
358 if ((ProtocolFlags & SKCS_PROTO_TCP) != 0 &&
359 NextLevelProtocol == SKCS_PROTO_ID_TCP) {
361 ProtocolFlags &= SKCS_PROTO_TCP | SKCS_PROTO_IP;
362 NextLevelProtoStats =
363 &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP];
365 else if ((ProtocolFlags & SKCS_PROTO_UDP) != 0 &&
366 NextLevelProtocol == SKCS_PROTO_ID_UDP) {
368 ProtocolFlags &= SKCS_PROTO_UDP | SKCS_PROTO_IP;
369 NextLevelProtoStats =
370 &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP];
374 * Either not a TCP or UDP frame and/or TCP/UDP processing not
377 pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP;
381 /* Check if this is an IP fragment. */
384 * Note: An IP fragment has a non-zero "Fragment Offset" field and/or
385 * the "More Fragments" bit set. Thus, if both the "Fragment Offset"
386 * and the "More Fragments" are zero, it is *not* a fragment. We can
387 * easily check both at the same time since they are in the same 16-bit
392 SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) &
393 ~SKCS_IP_DONT_FRAGMENT) != 0) {
394 /* IP fragment; ignore all other protocols. */
395 pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP;
396 NextLevelProtoStats->TxUnableCts++;
401 * Calculate the TCP/UDP pseudo header checksum.
404 /* Get total length of IP header and data. */
407 *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH);
409 /* Get length of IP data portion. */
411 IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength;
413 /* Calculate the sum of all pseudo header fields (16-bit). */
415 PseudoHeaderChecksum =
416 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
417 SKCS_OFS_IP_SOURCE_ADDRESS + 0) +
418 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
419 SKCS_OFS_IP_SOURCE_ADDRESS + 2) +
420 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
421 SKCS_OFS_IP_DESTINATION_ADDRESS + 0) +
422 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
423 SKCS_OFS_IP_DESTINATION_ADDRESS + 2) +
424 (unsigned long) SKCS_HTON16(NextLevelProtocol) +
425 (unsigned long) SKCS_HTON16(IpDataLength);
427 /* Add-in any carries. */
429 SKCS_OC_ADD(PseudoHeaderChecksum, PseudoHeaderChecksum, 0);
431 /* Add-in any new carry. */
433 SKCS_OC_ADD(pPacketInfo->PseudoHeaderChecksum, PseudoHeaderChecksum, 0);
435 pPacketInfo->ProtocolFlags = ProtocolFlags;
436 NextLevelProtoStats->TxOkCts++; /* Success. */
437 } /* SkCsGetSendInfo */
440 /******************************************************************************
442 * SkCsGetReceiveInfo - verify checksum information for a received packet
445 * Verify a received frame's checksum. The function returns a status code
446 * reflecting the result of the verification.
449 * Before calling this function you have to verify that the frame is
450 * not padded and Checksum1 and Checksum2 are bigger than 1.
453 * pAc - Pointer to adapter context struct.
455 * pIpHeader - Pointer to IP header. Must be at least the length in bytes
456 * of the received IP header including any option fields. For UDP packets,
457 * 8 additional bytes are needed to access the UDP checksum.
459 * Note: The actual length of the IP header is stored in the lower four
460 * bits of the first octet of the IP header as the number of 4-byte words,
461 * so it must be multiplied by four to get the length in bytes. Thus, the
462 * maximum IP header length is 15 * 4 = 60 bytes.
464 * Checksum1 - The first 16-bit Internet Checksum calculated by the
465 * hardware starting at the offset returned by SkCsSetReceiveFlags().
467 * Checksum2 - The second 16-bit Internet Checksum calculated by the
468 * hardware starting at the offset returned by SkCsSetReceiveFlags().
471 * SKCS_STATUS_UNKNOWN_IP_VERSION - Not an IP v4 frame.
472 * SKCS_STATUS_IP_CSUM_ERROR - IP checksum error.
473 * SKCS_STATUS_IP_CSUM_ERROR_TCP - IP checksum error in TCP frame.
474 * SKCS_STATUS_IP_CSUM_ERROR_UDP - IP checksum error in UDP frame
475 * SKCS_STATUS_IP_FRAGMENT - IP fragment (IP checksum ok).
476 * SKCS_STATUS_IP_CSUM_OK - IP checksum ok (not a TCP or UDP frame).
477 * SKCS_STATUS_TCP_CSUM_ERROR - TCP checksum error (IP checksum ok).
478 * SKCS_STATUS_UDP_CSUM_ERROR - UDP checksum error (IP checksum ok).
479 * SKCS_STATUS_TCP_CSUM_OK - IP and TCP checksum ok.
480 * SKCS_STATUS_UDP_CSUM_OK - IP and UDP checksum ok.
481 * SKCS_STATUS_IP_CSUM_OK_NO_UDP - IP checksum OK and no UDP checksum.
483 * Note: If SKCS_OVERWRITE_STATUS is defined, the SKCS_STATUS_XXX values
484 * returned here can be defined in some header file by the module using CSUM.
485 * In this way, the calling module can assign return values for its own needs,
486 * e.g. by assigning bit flags to the individual protocols.
488 SKCS_STATUS SkCsGetReceiveInfo(
489 SK_AC *pAc, /* Adapter context struct. */
490 void *pIpHeader, /* IP header. */
491 unsigned Checksum1, /* Hardware checksum 1. */
492 unsigned Checksum2, /* Hardware checksum 2. */
493 int NetNumber) /* Net number */
495 /* Internet Header Version found in IP header. */
496 unsigned InternetHeaderVersion;
498 /* Length of the IP header as found in IP header. */
499 unsigned IpHeaderLength;
501 /* Length of IP data portion. */
502 unsigned IpDataLength;
504 /* IP header checksum. */
505 unsigned IpHeaderChecksum;
507 /* IP header options checksum, if any. */
508 unsigned IpOptionsChecksum;
510 /* IP data checksum, i.e. TCP/UDP checksum. */
511 unsigned IpDataChecksum;
513 /* Next level protocol identifier found in IP header. */
514 unsigned NextLevelProtocol;
516 /* The checksum of the "next level protocol", i.e. TCP or UDP. */
517 unsigned long NextLevelProtocolChecksum;
519 /* Pointer to next level protocol statistics structure. */
520 SKCS_PROTO_STATS *NextLevelProtoStats;
522 /* Temporary variable. */
526 SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH);
528 /* Get the Internet Header Version (IHV). */
529 /* Note: The IHV is stored in the upper four bits. */
531 InternetHeaderVersion = Tmp >> 4;
533 /* Check the Internet Header Version. */
534 /* Note: We currently only support IP version 4. */
536 if (InternetHeaderVersion != 4) { /* IPv4? */
537 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX,
538 ("Rx: Unknown Internet Header Version %u.\n",
539 InternetHeaderVersion));
540 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++;
541 return (SKCS_STATUS_UNKNOWN_IP_VERSION);
544 /* Get the IP header length (IHL). */
546 * Note: The IHL is stored in the lower four bits as the number of
550 IpHeaderLength = (Tmp & 0xf) * 4;
552 /* Check the IP header length. */
554 /* 04-Aug-1998 sw - Really check the IHL? Necessary? */
556 if (IpHeaderLength < 5*4) {
557 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX,
558 ("Rx: Invalid IP Header Length %u.\n", IpHeaderLength));
559 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++;
560 return (SKCS_STATUS_IP_CSUM_ERROR);
563 /* This is an IPv4 frame with a header of valid length. */
565 /* Get the IP header and data checksum. */
567 IpDataChecksum = Checksum2;
570 * The IP header checksum is calculated as follows:
572 * IpHeaderChecksum = Checksum1 - Checksum2
575 SKCS_OC_SUB(IpHeaderChecksum, Checksum1, Checksum2);
577 /* Check if any IP header options. */
579 if (IpHeaderLength > SKCS_IP_HEADER_SIZE) {
581 /* Get the IP options checksum. */
583 IpOptionsChecksum = SkCsCalculateChecksum(
584 SKCS_IDX(pIpHeader, SKCS_IP_HEADER_SIZE),
585 IpHeaderLength - SKCS_IP_HEADER_SIZE);
587 /* Adjust the IP header and IP data checksums. */
589 SKCS_OC_ADD(IpHeaderChecksum, IpHeaderChecksum, IpOptionsChecksum);
591 SKCS_OC_SUB(IpDataChecksum, IpDataChecksum, IpOptionsChecksum);
595 * Check if the IP header checksum is ok.
597 * NOTE: We must check the IP header checksum even if the caller just wants
598 * us to check upper-layer checksums, because we cannot do any further
599 * processing of the packet without a valid IP checksum.
602 /* Get the next level protocol identifier. */
604 NextLevelProtocol = *(SK_U8 *)
605 SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL);
607 if (IpHeaderChecksum != 0xFFFF) {
608 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++;
609 /* the NDIS tester wants to know the upper level protocol too */
610 if (NextLevelProtocol == SKCS_PROTO_ID_TCP) {
611 return(SKCS_STATUS_IP_CSUM_ERROR_TCP);
613 else if (NextLevelProtocol == SKCS_PROTO_ID_UDP) {
614 return(SKCS_STATUS_IP_CSUM_ERROR_UDP);
616 return (SKCS_STATUS_IP_CSUM_ERROR);
620 * Check if this is a TCP or UDP frame and if we should calculate the
621 * TCP/UDP pseudo header checksum.
623 * Also clear all protocol bit flags of protocols not present in the
627 if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_TCP) != 0 &&
628 NextLevelProtocol == SKCS_PROTO_ID_TCP) {
630 NextLevelProtoStats =
631 &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP];
633 else if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_UDP) != 0 &&
634 NextLevelProtocol == SKCS_PROTO_ID_UDP) {
636 NextLevelProtoStats =
637 &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP];
641 * Either not a TCP or UDP frame and/or TCP/UDP processing not
644 return (SKCS_STATUS_IP_CSUM_OK);
647 /* Check if this is an IP fragment. */
650 * Note: An IP fragment has a non-zero "Fragment Offset" field and/or
651 * the "More Fragments" bit set. Thus, if both the "Fragment Offset"
652 * and the "More Fragments" are zero, it is *not* a fragment. We can
653 * easily check both at the same time since they are in the same 16-bit
658 SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) &
659 ~SKCS_IP_DONT_FRAGMENT) != 0) {
660 /* IP fragment; ignore all other protocols. */
661 NextLevelProtoStats->RxUnableCts++;
662 return (SKCS_STATUS_IP_FRAGMENT);
669 * If the computed checksum is zero, it is transmitted as all ones (the
670 * equivalent in one's complement arithmetic). An all zero transmitted
671 * checksum value means that the transmitter generated no checksum (for
672 * debugging or for higher level protocols that don't care).
675 if (NextLevelProtocol == SKCS_PROTO_ID_UDP &&
676 *(SK_U16*)SKCS_IDX(pIpHeader, IpHeaderLength + 6) == 0x0000) {
678 NextLevelProtoStats->RxOkCts++;
680 return (SKCS_STATUS_IP_CSUM_OK_NO_UDP);
684 * Calculate the TCP/UDP checksum.
687 /* Get total length of IP header and data. */
690 *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH);
692 /* Get length of IP data portion. */
694 IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength;
696 NextLevelProtocolChecksum =
698 /* Calculate the pseudo header checksum. */
700 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
701 SKCS_OFS_IP_SOURCE_ADDRESS + 0) +
702 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
703 SKCS_OFS_IP_SOURCE_ADDRESS + 2) +
704 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
705 SKCS_OFS_IP_DESTINATION_ADDRESS + 0) +
706 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
707 SKCS_OFS_IP_DESTINATION_ADDRESS + 2) +
708 (unsigned long) SKCS_HTON16(NextLevelProtocol) +
709 (unsigned long) SKCS_HTON16(IpDataLength) +
711 /* Add the TCP/UDP header checksum. */
713 (unsigned long) IpDataChecksum;
715 /* Add-in any carries. */
717 SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0);
719 /* Add-in any new carry. */
721 SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0);
723 /* Check if the TCP/UDP checksum is ok. */
725 if ((unsigned) NextLevelProtocolChecksum == 0xFFFF) {
727 /* TCP/UDP checksum ok. */
729 NextLevelProtoStats->RxOkCts++;
731 return (NextLevelProtocol == SKCS_PROTO_ID_TCP ?
732 SKCS_STATUS_TCP_CSUM_OK : SKCS_STATUS_UDP_CSUM_OK);
735 /* TCP/UDP checksum error. */
737 NextLevelProtoStats->RxErrCts++;
739 return (NextLevelProtocol == SKCS_PROTO_ID_TCP ?
740 SKCS_STATUS_TCP_CSUM_ERROR : SKCS_STATUS_UDP_CSUM_ERROR);
741 } /* SkCsGetReceiveInfo */
745 /******************************************************************************
747 * SkCsSetReceiveFlags - set checksum receive flags
750 * Use this function to set the various receive flags. According to the
751 * protocol flags set by the caller, the start offsets within received
752 * packets of the two hardware checksums are returned. These offsets must
753 * be stored in all receive descriptors.
756 * pAc - Pointer to adapter context struct.
758 * ReceiveFlags - Any combination of SK_PROTO_XXX flags of the protocols
759 * for which the caller wants checksum information on received frames.
761 * pChecksum1Offset - The start offset of the first receive descriptor
762 * hardware checksum to be calculated for received frames is returned
765 * pChecksum2Offset - The start offset of the second receive descriptor
766 * hardware checksum to be calculated for received frames is returned
770 * Returns the two hardware checksum start offsets.
772 void SkCsSetReceiveFlags(
773 SK_AC *pAc, /* Adapter context struct. */
774 unsigned ReceiveFlags, /* New receive flags. */
775 unsigned *pChecksum1Offset, /* Offset for hardware checksum 1. */
776 unsigned *pChecksum2Offset, /* Offset for hardware checksum 2. */
779 /* Save the receive flags. */
781 pAc->Csum.ReceiveFlags[NetNumber] = ReceiveFlags;
783 /* First checksum start offset is the IP header. */
784 *pChecksum1Offset = SKCS_MAC_HEADER_SIZE;
787 * Second checksum start offset is the IP data. Note that this may vary
788 * if there are any IP header options in the actual packet.
790 *pChecksum2Offset = SKCS_MAC_HEADER_SIZE + SKCS_IP_HEADER_SIZE;
791 } /* SkCsSetReceiveFlags */
793 #ifndef SkCsCalculateChecksum
795 /******************************************************************************
797 * SkCsCalculateChecksum - calculate checksum for specified data
800 * Calculate and return the 16-bit Internet Checksum for the specified
804 * pData - Pointer to data for which the checksum shall be calculated.
805 * Note: The pointer should be aligned on a 16-bit boundary.
807 * Length - Length in bytes of data to checksum.
810 * The 16-bit Internet Checksum for the specified data.
812 * Note: The checksum is calculated in the machine's natural byte order,
813 * i.e. little vs. big endian. Thus, the resulting checksum is different
814 * for the same input data on little and big endian machines.
816 * However, when written back to the network packet, the byte order is
817 * always in correct network order.
819 unsigned SkCsCalculateChecksum(
820 void *pData, /* Data to checksum. */
821 unsigned Length) /* Length of data. */
823 SK_U16 *pU16; /* Pointer to the data as 16-bit words. */
824 unsigned long Checksum; /* Checksum; must be at least 32 bits. */
826 /* Sum up all 16-bit words. */
828 pU16 = (SK_U16 *) pData;
829 for (Checksum = 0; Length > 1; Length -= 2) {
833 /* If this is an odd number of bytes, add-in the last byte. */
837 /* Add the last byte as the high byte. */
838 Checksum += ((unsigned) *(SK_U8 *) pU16) << 8;
839 #else /* !SK_BIG_ENDIAN */
840 /* Add the last byte as the low byte. */
841 Checksum += *(SK_U8 *) pU16;
842 #endif /* !SK_BIG_ENDIAN */
845 /* Add-in any carries. */
847 SKCS_OC_ADD(Checksum, Checksum, 0);
849 /* Add-in any new carry. */
851 SKCS_OC_ADD(Checksum, Checksum, 0);
853 /* Note: All bits beyond the 16-bit limit are now zero. */
855 return ((unsigned) Checksum);
856 } /* SkCsCalculateChecksum */
858 #endif /* SkCsCalculateChecksum */
860 /******************************************************************************
862 * SkCsEvent - the CSUM event dispatcher
865 * This is the event handler for the CSUM module.
868 * pAc - Pointer to adapter context.
874 * Param - Event dependent parameter.
877 * The 16-bit Internet Checksum for the specified data.
879 * Note: The checksum is calculated in the machine's natural byte order,
880 * i.e. little vs. big endian. Thus, the resulting checksum is different
881 * for the same input data on little and big endian machines.
883 * However, when written back to the network packet, the byte order is
884 * always in correct network order.
887 SK_AC *pAc, /* Pointer to adapter context. */
888 SK_IOC Ioc, /* I/O context. */
889 SK_U32 Event, /* Event id. */
890 SK_EVPARA Param) /* Event dependent parameter. */
897 * Clear protocol statistics.
899 * Param - Protocol index, or -1 for all protocols.
902 case SK_CSUM_EVENT_CLEAR_PROTO_STATS:
904 ProtoIndex = (int)Param.Para32[1];
905 NetNumber = (int)Param.Para32[0];
906 if (ProtoIndex < 0) { /* Clear for all protocols. */
907 if (NetNumber >= 0) {
908 memset(&pAc->Csum.ProtoStats[NetNumber][0], 0,
909 sizeof(pAc->Csum.ProtoStats[NetNumber]));
912 else { /* Clear for individual protocol. */
913 memset(&pAc->Csum.ProtoStats[NetNumber][ProtoIndex], 0,
914 sizeof(pAc->Csum.ProtoStats[NetNumber][ProtoIndex]));
920 return (0); /* Success. */
923 #endif /* SK_USE_CSUM */