wdenk | 7152b1d | 2003-09-05 23:19:14 +0000 | [diff] [blame] | 1 | /****************************************************************************** |
| 2 | * |
| 3 | * Name: skcsum.c |
| 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. |
| 8 | * |
| 9 | ******************************************************************************/ |
| 10 | |
| 11 | /****************************************************************************** |
| 12 | * |
| 13 | * (C)Copyright 1998-2001 SysKonnect GmbH. |
| 14 | * |
| 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. |
| 19 | * |
| 20 | * The information in this file is provided "AS IS" without warranty. |
| 21 | * |
| 22 | ******************************************************************************/ |
| 23 | |
| 24 | /****************************************************************************** |
| 25 | * |
| 26 | * History: |
| 27 | * |
| 28 | * $Log: skcsum.c,v $ |
| 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. |
| 33 | * |
| 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 |
| 37 | * |
| 38 | * Revision 1.8 2001/02/06 11:15:36 rassmann |
| 39 | * Supporting two nets on dual-port adapters. |
| 40 | * |
| 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). |
| 44 | * |
| 45 | * Revision 1.6 2000/02/21 12:35:10 cgoos |
| 46 | * Fixed license header comment. |
| 47 | * |
| 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. |
| 51 | * |
| 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 |
| 57 | * |
| 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 |
| 61 | * added some comments |
| 62 | * |
| 63 | * Revision 1.2 1998/10/22 11:53:28 swolf |
| 64 | * Now using SK_DBG_MSG. |
| 65 | * |
| 66 | * Revision 1.1 1998/09/01 15:35:41 swolf |
| 67 | * initial revision |
| 68 | * |
| 69 | * 13-May-1998 sw Created. |
| 70 | * |
| 71 | ******************************************************************************/ |
| 72 | |
wdenk | 149dded | 2003-09-10 18:20:28 +0000 | [diff] [blame] | 73 | #include <config.h> |
| 74 | |
| 75 | #ifdef CONFIG_SK98 |
| 76 | |
wdenk | 7152b1d | 2003-09-05 23:19:14 +0000 | [diff] [blame] | 77 | #ifdef SK_USE_CSUM /* Check if CSUM is to be used. */ |
| 78 | |
| 79 | #ifndef lint |
| 80 | static const char SysKonnectFileId[] = "@(#)" |
| 81 | "$Id: skcsum.c,v 1.10 2002/04/11 10:02:04 rwahl Exp $" |
| 82 | " (C) SysKonnect."; |
| 83 | #endif /* !lint */ |
| 84 | |
| 85 | /****************************************************************************** |
| 86 | * |
| 87 | * Description: |
| 88 | * |
| 89 | * This is the "GEnesis" common module "CSUM". |
| 90 | * |
| 91 | * This module contains the code necessary to calculate, store, and verify the |
| 92 | * Internet Checksum of IP, TCP, and UDP frames. |
| 93 | * |
| 94 | * "GEnesis" is an abbreviation of "Gigabit Ethernet Network System in Silicon" |
| 95 | * and is the code name of this SysKonnect project. |
| 96 | * |
| 97 | * Compilation Options: |
| 98 | * |
| 99 | * SK_USE_CSUM - Define if CSUM is to be used. Otherwise, CSUM will be an |
| 100 | * empty module. |
| 101 | * |
| 102 | * SKCS_OVERWRITE_PROTO - Define to overwrite the default protocol id |
| 103 | * definitions. In this case, all SKCS_PROTO_xxx definitions must be made |
| 104 | * external. |
| 105 | * |
| 106 | * SKCS_OVERWRITE_STATUS - Define to overwrite the default return status |
| 107 | * definitions. In this case, all SKCS_STATUS_xxx definitions must be made |
| 108 | * external. |
| 109 | * |
| 110 | * Include File Hierarchy: |
| 111 | * |
| 112 | * "h/skdrv1st.h" |
| 113 | * "h/skcsum.h" |
| 114 | * "h/sktypes.h" |
| 115 | * "h/skqueue.h" |
| 116 | * "h/skdrv2nd.h" |
| 117 | * |
| 118 | ******************************************************************************/ |
| 119 | |
| 120 | #include "h/skdrv1st.h" |
| 121 | #include "h/skcsum.h" |
| 122 | #include "h/skdrv2nd.h" |
| 123 | |
| 124 | /* defines ********************************************************************/ |
| 125 | |
| 126 | /* The size of an Ethernet MAC header. */ |
| 127 | #define SKCS_ETHERNET_MAC_HEADER_SIZE (6+6+2) |
| 128 | |
| 129 | /* The size of the used topology's MAC header. */ |
| 130 | #define SKCS_MAC_HEADER_SIZE SKCS_ETHERNET_MAC_HEADER_SIZE |
| 131 | |
| 132 | /* The size of the IP header without any option fields. */ |
| 133 | #define SKCS_IP_HEADER_SIZE 20 |
| 134 | |
| 135 | /* |
| 136 | * Field offsets within the IP header. |
| 137 | */ |
| 138 | |
| 139 | /* "Internet Header Version" and "Length". */ |
| 140 | #define SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH 0 |
| 141 | |
| 142 | /* "Total Length". */ |
| 143 | #define SKCS_OFS_IP_TOTAL_LENGTH 2 |
| 144 | |
| 145 | /* "Flags" "Fragment Offset". */ |
| 146 | #define SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET 6 |
| 147 | |
| 148 | /* "Next Level Protocol" identifier. */ |
| 149 | #define SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL 9 |
| 150 | |
| 151 | /* Source IP address. */ |
| 152 | #define SKCS_OFS_IP_SOURCE_ADDRESS 12 |
| 153 | |
| 154 | /* Destination IP address. */ |
| 155 | #define SKCS_OFS_IP_DESTINATION_ADDRESS 16 |
| 156 | |
| 157 | |
| 158 | /* |
| 159 | * Field offsets within the UDP header. |
| 160 | */ |
| 161 | |
| 162 | /* UDP checksum. */ |
| 163 | #define SKCS_OFS_UDP_CHECKSUM 6 |
| 164 | |
| 165 | /* IP "Next Level Protocol" identifiers (see RFC 790). */ |
| 166 | #define SKCS_PROTO_ID_TCP 6 /* Transport Control Protocol */ |
| 167 | #define SKCS_PROTO_ID_UDP 17 /* User Datagram Protocol */ |
| 168 | |
| 169 | /* IP "Don't Fragment" bit. */ |
| 170 | #define SKCS_IP_DONT_FRAGMENT SKCS_HTON16(0x4000) |
| 171 | |
| 172 | /* Add a byte offset to a pointer. */ |
| 173 | #define SKCS_IDX(pPtr, Ofs) ((void *) ((char *) (pPtr) + (Ofs))) |
| 174 | |
| 175 | /* |
| 176 | * Macros that convert host to network representation and vice versa, i.e. |
| 177 | * little/big endian conversion on little endian machines only. |
| 178 | */ |
| 179 | #ifdef SK_LITTLE_ENDIAN |
| 180 | #define SKCS_HTON16(Val16) (((unsigned) (Val16) >> 8) | (((Val16) & 0xFF) << 8)) |
| 181 | #endif /* SK_LITTLE_ENDIAN */ |
| 182 | #ifdef SK_BIG_ENDIAN |
| 183 | #define SKCS_HTON16(Val16) (Val16) |
| 184 | #endif /* SK_BIG_ENDIAN */ |
| 185 | #define SKCS_NTOH16(Val16) SKCS_HTON16(Val16) |
| 186 | |
| 187 | /* typedefs *******************************************************************/ |
| 188 | |
| 189 | /* function prototypes ********************************************************/ |
| 190 | |
| 191 | /****************************************************************************** |
| 192 | * |
| 193 | * SkCsGetSendInfo - get checksum information for a send packet |
| 194 | * |
| 195 | * Description: |
| 196 | * Get all checksum information necessary to send a TCP or UDP packet. The |
| 197 | * function checks the IP header passed to it. If the high-level protocol |
| 198 | * is either TCP or UDP the pseudo header checksum is calculated and |
| 199 | * returned. |
| 200 | * |
| 201 | * The function returns the total length of the IP header (including any |
| 202 | * IP option fields), which is the same as the start offset of the IP data |
| 203 | * which in turn is the start offset of the TCP or UDP header. |
| 204 | * |
| 205 | * The function also returns the TCP or UDP pseudo header checksum, which |
| 206 | * should be used as the start value for the hardware checksum calculation. |
| 207 | * (Note that any actual pseudo header checksum can never calculate to |
| 208 | * zero.) |
| 209 | * |
| 210 | * Note: |
| 211 | * There is a bug in the ASIC which may lead to wrong checksums. |
| 212 | * |
| 213 | * Arguments: |
| 214 | * pAc - A pointer to the adapter context struct. |
| 215 | * |
| 216 | * pIpHeader - Pointer to IP header. Must be at least the IP header *not* |
| 217 | * including any option fields, i.e. at least 20 bytes. |
| 218 | * |
| 219 | * Note: This pointer will be used to address 8-, 16-, and 32-bit |
| 220 | * variables with the respective alignment offsets relative to the pointer. |
| 221 | * Thus, the pointer should point to a 32-bit aligned address. If the |
| 222 | * target system cannot address 32-bit variables on non 32-bit aligned |
| 223 | * addresses, then the pointer *must* point to a 32-bit aligned address. |
| 224 | * |
| 225 | * pPacketInfo - A pointer to the packet information structure for this |
| 226 | * packet. Before calling this SkCsGetSendInfo(), the following field must |
| 227 | * be initialized: |
| 228 | * |
| 229 | * ProtocolFlags - Initialize with any combination of |
| 230 | * SKCS_PROTO_XXX bit flags. SkCsGetSendInfo() will only work on |
| 231 | * the protocols specified here. Any protocol(s) not specified |
| 232 | * here will be ignored. |
| 233 | * |
| 234 | * Note: Only one checksum can be calculated in hardware. Thus, if |
| 235 | * SKCS_PROTO_IP is specified in the 'ProtocolFlags', |
| 236 | * SkCsGetSendInfo() must calculate the IP header checksum in |
| 237 | * software. It might be a better idea to have the calling |
| 238 | * protocol stack calculate the IP header checksum. |
| 239 | * |
| 240 | * Returns: N/A |
| 241 | * On return, the following fields in 'pPacketInfo' may or may not have |
| 242 | * been filled with information, depending on the protocol(s) found in the |
| 243 | * packet: |
| 244 | * |
| 245 | * ProtocolFlags - Returns the SKCS_PROTO_XXX bit flags of the protocol(s) |
| 246 | * that were both requested by the caller and actually found in the packet. |
| 247 | * Protocol(s) not specified by the caller and/or not found in the packet |
| 248 | * will have their respective SKCS_PROTO_XXX bit flags reset. |
| 249 | * |
| 250 | * Note: For IP fragments, TCP and UDP packet information is ignored. |
| 251 | * |
| 252 | * IpHeaderLength - The total length in bytes of the complete IP header |
| 253 | * including any option fields is returned here. This is the start offset |
| 254 | * of the IP data, i.e. the TCP or UDP header if present. |
| 255 | * |
| 256 | * IpHeaderChecksum - If IP has been specified in the 'ProtocolFlags', the |
| 257 | * 16-bit Internet Checksum of the IP header is returned here. This value |
| 258 | * is to be stored into the packet's 'IP Header Checksum' field. |
| 259 | * |
| 260 | * PseudoHeaderChecksum - If this is a TCP or UDP packet and if TCP or UDP |
| 261 | * has been specified in the 'ProtocolFlags', the 16-bit Internet Checksum |
| 262 | * of the TCP or UDP pseudo header is returned here. |
| 263 | */ |
| 264 | #if 0 |
| 265 | void SkCsGetSendInfo( |
| 266 | SK_AC *pAc, /* Adapter context struct. */ |
| 267 | void *pIpHeader, /* IP header. */ |
| 268 | SKCS_PACKET_INFO *pPacketInfo, /* Packet information struct. */ |
| 269 | int NetNumber) /* Net number */ |
| 270 | { |
| 271 | /* Internet Header Version found in IP header. */ |
| 272 | unsigned InternetHeaderVersion; |
| 273 | |
| 274 | /* Length of the IP header as found in IP header. */ |
| 275 | unsigned IpHeaderLength; |
| 276 | |
| 277 | /* Bit field specifiying the desired/found protocols. */ |
| 278 | unsigned ProtocolFlags; |
| 279 | |
| 280 | /* Next level protocol identifier found in IP header. */ |
| 281 | unsigned NextLevelProtocol; |
| 282 | |
| 283 | /* Length of IP data portion. */ |
| 284 | unsigned IpDataLength; |
| 285 | |
| 286 | /* TCP/UDP pseudo header checksum. */ |
| 287 | unsigned long PseudoHeaderChecksum; |
| 288 | |
| 289 | /* Pointer to next level protocol statistics structure. */ |
| 290 | SKCS_PROTO_STATS *NextLevelProtoStats; |
| 291 | |
| 292 | /* Temporary variable. */ |
| 293 | unsigned Tmp; |
| 294 | |
| 295 | Tmp = *(SK_U8 *) |
| 296 | SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH); |
| 297 | |
| 298 | /* Get the Internet Header Version (IHV). */ |
| 299 | /* Note: The IHV is stored in the upper four bits. */ |
| 300 | |
| 301 | InternetHeaderVersion = Tmp >> 4; |
| 302 | |
| 303 | /* Check the Internet Header Version. */ |
| 304 | /* Note: We currently only support IP version 4. */ |
| 305 | |
| 306 | if (InternetHeaderVersion != 4) { /* IPv4? */ |
| 307 | SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX, |
| 308 | ("Tx: Unknown Internet Header Version %u.\n", |
| 309 | InternetHeaderVersion)); |
| 310 | pPacketInfo->ProtocolFlags = 0; |
| 311 | pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++; |
| 312 | return; |
| 313 | } |
| 314 | |
| 315 | /* Get the IP header length (IHL). */ |
| 316 | /* |
| 317 | * Note: The IHL is stored in the lower four bits as the number of |
| 318 | * 4-byte words. |
| 319 | */ |
| 320 | |
| 321 | IpHeaderLength = (Tmp & 0xf) * 4; |
| 322 | pPacketInfo->IpHeaderLength = IpHeaderLength; |
| 323 | |
| 324 | /* Check the IP header length. */ |
| 325 | |
| 326 | /* 04-Aug-1998 sw - Really check the IHL? Necessary? */ |
| 327 | |
| 328 | if (IpHeaderLength < 5*4) { |
| 329 | SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX, |
| 330 | ("Tx: Invalid IP Header Length %u.\n", IpHeaderLength)); |
| 331 | pPacketInfo->ProtocolFlags = 0; |
| 332 | pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++; |
| 333 | return; |
| 334 | } |
| 335 | |
| 336 | /* This is an IPv4 frame with a header of valid length. */ |
| 337 | |
| 338 | pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxOkCts++; |
| 339 | |
| 340 | /* Check if we should calculate the IP header checksum. */ |
| 341 | |
| 342 | ProtocolFlags = pPacketInfo->ProtocolFlags; |
| 343 | |
| 344 | if (ProtocolFlags & SKCS_PROTO_IP) { |
| 345 | pPacketInfo->IpHeaderChecksum = |
| 346 | SkCsCalculateChecksum(pIpHeader, IpHeaderLength); |
| 347 | } |
| 348 | |
| 349 | /* Get the next level protocol identifier. */ |
| 350 | |
| 351 | NextLevelProtocol = |
| 352 | *(SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL); |
| 353 | |
| 354 | /* |
| 355 | * Check if this is a TCP or UDP frame and if we should calculate the |
| 356 | * TCP/UDP pseudo header checksum. |
| 357 | * |
| 358 | * Also clear all protocol bit flags of protocols not present in the |
| 359 | * frame. |
| 360 | */ |
| 361 | |
| 362 | if ((ProtocolFlags & SKCS_PROTO_TCP) != 0 && |
| 363 | NextLevelProtocol == SKCS_PROTO_ID_TCP) { |
| 364 | /* TCP/IP frame. */ |
| 365 | ProtocolFlags &= SKCS_PROTO_TCP | SKCS_PROTO_IP; |
| 366 | NextLevelProtoStats = |
| 367 | &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP]; |
| 368 | } |
| 369 | else if ((ProtocolFlags & SKCS_PROTO_UDP) != 0 && |
| 370 | NextLevelProtocol == SKCS_PROTO_ID_UDP) { |
| 371 | /* UDP/IP frame. */ |
| 372 | ProtocolFlags &= SKCS_PROTO_UDP | SKCS_PROTO_IP; |
| 373 | NextLevelProtoStats = |
| 374 | &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP]; |
| 375 | } |
| 376 | else { |
| 377 | /* |
| 378 | * Either not a TCP or UDP frame and/or TCP/UDP processing not |
| 379 | * specified. |
| 380 | */ |
| 381 | pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP; |
| 382 | return; |
| 383 | } |
| 384 | |
| 385 | /* Check if this is an IP fragment. */ |
| 386 | |
| 387 | /* |
| 388 | * Note: An IP fragment has a non-zero "Fragment Offset" field and/or |
| 389 | * the "More Fragments" bit set. Thus, if both the "Fragment Offset" |
| 390 | * and the "More Fragments" are zero, it is *not* a fragment. We can |
| 391 | * easily check both at the same time since they are in the same 16-bit |
| 392 | * word. |
| 393 | */ |
| 394 | |
| 395 | if ((*(SK_U16 *) |
| 396 | SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) & |
| 397 | ~SKCS_IP_DONT_FRAGMENT) != 0) { |
| 398 | /* IP fragment; ignore all other protocols. */ |
| 399 | pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP; |
| 400 | NextLevelProtoStats->TxUnableCts++; |
| 401 | return; |
| 402 | } |
| 403 | |
| 404 | /* |
| 405 | * Calculate the TCP/UDP pseudo header checksum. |
| 406 | */ |
| 407 | |
| 408 | /* Get total length of IP header and data. */ |
| 409 | |
| 410 | IpDataLength = |
| 411 | *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH); |
| 412 | |
| 413 | /* Get length of IP data portion. */ |
| 414 | |
| 415 | IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength; |
| 416 | |
| 417 | /* Calculate the sum of all pseudo header fields (16-bit). */ |
| 418 | |
| 419 | PseudoHeaderChecksum = |
| 420 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 421 | SKCS_OFS_IP_SOURCE_ADDRESS + 0) + |
| 422 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 423 | SKCS_OFS_IP_SOURCE_ADDRESS + 2) + |
| 424 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 425 | SKCS_OFS_IP_DESTINATION_ADDRESS + 0) + |
| 426 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 427 | SKCS_OFS_IP_DESTINATION_ADDRESS + 2) + |
| 428 | (unsigned long) SKCS_HTON16(NextLevelProtocol) + |
| 429 | (unsigned long) SKCS_HTON16(IpDataLength); |
| 430 | |
| 431 | /* Add-in any carries. */ |
| 432 | |
| 433 | SKCS_OC_ADD(PseudoHeaderChecksum, PseudoHeaderChecksum, 0); |
| 434 | |
| 435 | /* Add-in any new carry. */ |
| 436 | |
| 437 | SKCS_OC_ADD(pPacketInfo->PseudoHeaderChecksum, PseudoHeaderChecksum, 0); |
| 438 | |
| 439 | pPacketInfo->ProtocolFlags = ProtocolFlags; |
| 440 | NextLevelProtoStats->TxOkCts++; /* Success. */ |
| 441 | } /* SkCsGetSendInfo */ |
| 442 | |
| 443 | |
| 444 | /****************************************************************************** |
| 445 | * |
| 446 | * SkCsGetReceiveInfo - verify checksum information for a received packet |
| 447 | * |
| 448 | * Description: |
| 449 | * Verify a received frame's checksum. The function returns a status code |
| 450 | * reflecting the result of the verification. |
| 451 | * |
| 452 | * Note: |
| 453 | * Before calling this function you have to verify that the frame is |
| 454 | * not padded and Checksum1 and Checksum2 are bigger than 1. |
| 455 | * |
| 456 | * Arguments: |
| 457 | * pAc - Pointer to adapter context struct. |
| 458 | * |
| 459 | * pIpHeader - Pointer to IP header. Must be at least the length in bytes |
| 460 | * of the received IP header including any option fields. For UDP packets, |
| 461 | * 8 additional bytes are needed to access the UDP checksum. |
| 462 | * |
| 463 | * Note: The actual length of the IP header is stored in the lower four |
| 464 | * bits of the first octet of the IP header as the number of 4-byte words, |
| 465 | * so it must be multiplied by four to get the length in bytes. Thus, the |
| 466 | * maximum IP header length is 15 * 4 = 60 bytes. |
| 467 | * |
| 468 | * Checksum1 - The first 16-bit Internet Checksum calculated by the |
| 469 | * hardware starting at the offset returned by SkCsSetReceiveFlags(). |
| 470 | * |
| 471 | * Checksum2 - The second 16-bit Internet Checksum calculated by the |
| 472 | * hardware starting at the offset returned by SkCsSetReceiveFlags(). |
| 473 | * |
| 474 | * Returns: |
| 475 | * SKCS_STATUS_UNKNOWN_IP_VERSION - Not an IP v4 frame. |
| 476 | * SKCS_STATUS_IP_CSUM_ERROR - IP checksum error. |
| 477 | * SKCS_STATUS_IP_CSUM_ERROR_TCP - IP checksum error in TCP frame. |
| 478 | * SKCS_STATUS_IP_CSUM_ERROR_UDP - IP checksum error in UDP frame |
| 479 | * SKCS_STATUS_IP_FRAGMENT - IP fragment (IP checksum ok). |
| 480 | * SKCS_STATUS_IP_CSUM_OK - IP checksum ok (not a TCP or UDP frame). |
| 481 | * SKCS_STATUS_TCP_CSUM_ERROR - TCP checksum error (IP checksum ok). |
| 482 | * SKCS_STATUS_UDP_CSUM_ERROR - UDP checksum error (IP checksum ok). |
| 483 | * SKCS_STATUS_TCP_CSUM_OK - IP and TCP checksum ok. |
| 484 | * SKCS_STATUS_UDP_CSUM_OK - IP and UDP checksum ok. |
| 485 | * SKCS_STATUS_IP_CSUM_OK_NO_UDP - IP checksum OK and no UDP checksum. |
| 486 | * |
| 487 | * Note: If SKCS_OVERWRITE_STATUS is defined, the SKCS_STATUS_XXX values |
| 488 | * returned here can be defined in some header file by the module using CSUM. |
| 489 | * In this way, the calling module can assign return values for its own needs, |
| 490 | * e.g. by assigning bit flags to the individual protocols. |
| 491 | */ |
| 492 | SKCS_STATUS SkCsGetReceiveInfo( |
| 493 | SK_AC *pAc, /* Adapter context struct. */ |
| 494 | void *pIpHeader, /* IP header. */ |
| 495 | unsigned Checksum1, /* Hardware checksum 1. */ |
| 496 | unsigned Checksum2, /* Hardware checksum 2. */ |
| 497 | int NetNumber) /* Net number */ |
| 498 | { |
| 499 | /* Internet Header Version found in IP header. */ |
| 500 | unsigned InternetHeaderVersion; |
| 501 | |
| 502 | /* Length of the IP header as found in IP header. */ |
| 503 | unsigned IpHeaderLength; |
| 504 | |
| 505 | /* Length of IP data portion. */ |
| 506 | unsigned IpDataLength; |
| 507 | |
| 508 | /* IP header checksum. */ |
| 509 | unsigned IpHeaderChecksum; |
| 510 | |
| 511 | /* IP header options checksum, if any. */ |
| 512 | unsigned IpOptionsChecksum; |
| 513 | |
| 514 | /* IP data checksum, i.e. TCP/UDP checksum. */ |
| 515 | unsigned IpDataChecksum; |
| 516 | |
| 517 | /* Next level protocol identifier found in IP header. */ |
| 518 | unsigned NextLevelProtocol; |
| 519 | |
| 520 | /* The checksum of the "next level protocol", i.e. TCP or UDP. */ |
| 521 | unsigned long NextLevelProtocolChecksum; |
| 522 | |
| 523 | /* Pointer to next level protocol statistics structure. */ |
| 524 | SKCS_PROTO_STATS *NextLevelProtoStats; |
| 525 | |
| 526 | /* Temporary variable. */ |
| 527 | unsigned Tmp; |
| 528 | |
| 529 | Tmp = *(SK_U8 *) |
| 530 | SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH); |
| 531 | |
| 532 | /* Get the Internet Header Version (IHV). */ |
| 533 | /* Note: The IHV is stored in the upper four bits. */ |
| 534 | |
| 535 | InternetHeaderVersion = Tmp >> 4; |
| 536 | |
| 537 | /* Check the Internet Header Version. */ |
| 538 | /* Note: We currently only support IP version 4. */ |
| 539 | |
| 540 | if (InternetHeaderVersion != 4) { /* IPv4? */ |
| 541 | SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, |
| 542 | ("Rx: Unknown Internet Header Version %u.\n", |
| 543 | InternetHeaderVersion)); |
| 544 | pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++; |
| 545 | return (SKCS_STATUS_UNKNOWN_IP_VERSION); |
| 546 | } |
| 547 | |
| 548 | /* Get the IP header length (IHL). */ |
| 549 | /* |
| 550 | * Note: The IHL is stored in the lower four bits as the number of |
| 551 | * 4-byte words. |
| 552 | */ |
| 553 | |
| 554 | IpHeaderLength = (Tmp & 0xf) * 4; |
| 555 | |
| 556 | /* Check the IP header length. */ |
| 557 | |
| 558 | /* 04-Aug-1998 sw - Really check the IHL? Necessary? */ |
| 559 | |
| 560 | if (IpHeaderLength < 5*4) { |
| 561 | SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, |
| 562 | ("Rx: Invalid IP Header Length %u.\n", IpHeaderLength)); |
| 563 | pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++; |
| 564 | return (SKCS_STATUS_IP_CSUM_ERROR); |
| 565 | } |
| 566 | |
| 567 | /* This is an IPv4 frame with a header of valid length. */ |
| 568 | |
| 569 | /* Get the IP header and data checksum. */ |
| 570 | |
| 571 | IpDataChecksum = Checksum2; |
| 572 | |
| 573 | /* |
| 574 | * The IP header checksum is calculated as follows: |
| 575 | * |
| 576 | * IpHeaderChecksum = Checksum1 - Checksum2 |
| 577 | */ |
| 578 | |
| 579 | SKCS_OC_SUB(IpHeaderChecksum, Checksum1, Checksum2); |
| 580 | |
| 581 | /* Check if any IP header options. */ |
| 582 | |
| 583 | if (IpHeaderLength > SKCS_IP_HEADER_SIZE) { |
| 584 | |
| 585 | /* Get the IP options checksum. */ |
| 586 | |
| 587 | IpOptionsChecksum = SkCsCalculateChecksum( |
| 588 | SKCS_IDX(pIpHeader, SKCS_IP_HEADER_SIZE), |
| 589 | IpHeaderLength - SKCS_IP_HEADER_SIZE); |
| 590 | |
| 591 | /* Adjust the IP header and IP data checksums. */ |
| 592 | |
| 593 | SKCS_OC_ADD(IpHeaderChecksum, IpHeaderChecksum, IpOptionsChecksum); |
| 594 | |
| 595 | SKCS_OC_SUB(IpDataChecksum, IpDataChecksum, IpOptionsChecksum); |
| 596 | } |
| 597 | |
| 598 | /* |
| 599 | * Check if the IP header checksum is ok. |
| 600 | * |
| 601 | * NOTE: We must check the IP header checksum even if the caller just wants |
| 602 | * us to check upper-layer checksums, because we cannot do any further |
| 603 | * processing of the packet without a valid IP checksum. |
| 604 | */ |
| 605 | |
| 606 | /* Get the next level protocol identifier. */ |
| 607 | |
| 608 | NextLevelProtocol = *(SK_U8 *) |
| 609 | SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL); |
| 610 | |
| 611 | if (IpHeaderChecksum != 0xFFFF) { |
| 612 | pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++; |
| 613 | /* the NDIS tester wants to know the upper level protocol too */ |
| 614 | if (NextLevelProtocol == SKCS_PROTO_ID_TCP) { |
| 615 | return(SKCS_STATUS_IP_CSUM_ERROR_TCP); |
| 616 | } |
| 617 | else if (NextLevelProtocol == SKCS_PROTO_ID_UDP) { |
| 618 | return(SKCS_STATUS_IP_CSUM_ERROR_UDP); |
| 619 | } |
| 620 | return (SKCS_STATUS_IP_CSUM_ERROR); |
| 621 | } |
| 622 | |
| 623 | /* |
| 624 | * Check if this is a TCP or UDP frame and if we should calculate the |
| 625 | * TCP/UDP pseudo header checksum. |
| 626 | * |
| 627 | * Also clear all protocol bit flags of protocols not present in the |
| 628 | * frame. |
| 629 | */ |
| 630 | |
| 631 | if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_TCP) != 0 && |
| 632 | NextLevelProtocol == SKCS_PROTO_ID_TCP) { |
| 633 | /* TCP/IP frame. */ |
| 634 | NextLevelProtoStats = |
| 635 | &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP]; |
| 636 | } |
| 637 | else if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_UDP) != 0 && |
| 638 | NextLevelProtocol == SKCS_PROTO_ID_UDP) { |
| 639 | /* UDP/IP frame. */ |
| 640 | NextLevelProtoStats = |
| 641 | &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP]; |
| 642 | } |
| 643 | else { |
| 644 | /* |
| 645 | * Either not a TCP or UDP frame and/or TCP/UDP processing not |
| 646 | * specified. |
| 647 | */ |
| 648 | return (SKCS_STATUS_IP_CSUM_OK); |
| 649 | } |
| 650 | |
| 651 | /* Check if this is an IP fragment. */ |
| 652 | |
| 653 | /* |
| 654 | * Note: An IP fragment has a non-zero "Fragment Offset" field and/or |
| 655 | * the "More Fragments" bit set. Thus, if both the "Fragment Offset" |
| 656 | * and the "More Fragments" are zero, it is *not* a fragment. We can |
| 657 | * easily check both at the same time since they are in the same 16-bit |
| 658 | * word. |
| 659 | */ |
| 660 | |
| 661 | if ((*(SK_U16 *) |
| 662 | SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) & |
| 663 | ~SKCS_IP_DONT_FRAGMENT) != 0) { |
| 664 | /* IP fragment; ignore all other protocols. */ |
| 665 | NextLevelProtoStats->RxUnableCts++; |
| 666 | return (SKCS_STATUS_IP_FRAGMENT); |
| 667 | } |
| 668 | |
| 669 | /* |
| 670 | * 08-May-2000 ra |
| 671 | * |
| 672 | * From RFC 768 (UDP) |
| 673 | * If the computed checksum is zero, it is transmitted as all ones (the |
| 674 | * equivalent in one's complement arithmetic). An all zero transmitted |
| 675 | * checksum value means that the transmitter generated no checksum (for |
| 676 | * debugging or for higher level protocols that don't care). |
| 677 | */ |
| 678 | |
| 679 | if (NextLevelProtocol == SKCS_PROTO_ID_UDP && |
| 680 | *(SK_U16*)SKCS_IDX(pIpHeader, IpHeaderLength + 6) == 0x0000) { |
| 681 | |
| 682 | NextLevelProtoStats->RxOkCts++; |
| 683 | |
| 684 | return (SKCS_STATUS_IP_CSUM_OK_NO_UDP); |
| 685 | } |
| 686 | |
| 687 | /* |
| 688 | * Calculate the TCP/UDP checksum. |
| 689 | */ |
| 690 | |
| 691 | /* Get total length of IP header and data. */ |
| 692 | |
| 693 | IpDataLength = |
| 694 | *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH); |
| 695 | |
| 696 | /* Get length of IP data portion. */ |
| 697 | |
| 698 | IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength; |
| 699 | |
| 700 | NextLevelProtocolChecksum = |
| 701 | |
| 702 | /* Calculate the pseudo header checksum. */ |
| 703 | |
| 704 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 705 | SKCS_OFS_IP_SOURCE_ADDRESS + 0) + |
| 706 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 707 | SKCS_OFS_IP_SOURCE_ADDRESS + 2) + |
| 708 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 709 | SKCS_OFS_IP_DESTINATION_ADDRESS + 0) + |
| 710 | (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, |
| 711 | SKCS_OFS_IP_DESTINATION_ADDRESS + 2) + |
| 712 | (unsigned long) SKCS_HTON16(NextLevelProtocol) + |
| 713 | (unsigned long) SKCS_HTON16(IpDataLength) + |
| 714 | |
| 715 | /* Add the TCP/UDP header checksum. */ |
| 716 | |
| 717 | (unsigned long) IpDataChecksum; |
| 718 | |
| 719 | /* Add-in any carries. */ |
| 720 | |
| 721 | SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0); |
| 722 | |
| 723 | /* Add-in any new carry. */ |
| 724 | |
| 725 | SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0); |
| 726 | |
| 727 | /* Check if the TCP/UDP checksum is ok. */ |
| 728 | |
| 729 | if ((unsigned) NextLevelProtocolChecksum == 0xFFFF) { |
| 730 | |
| 731 | /* TCP/UDP checksum ok. */ |
| 732 | |
| 733 | NextLevelProtoStats->RxOkCts++; |
| 734 | |
| 735 | return (NextLevelProtocol == SKCS_PROTO_ID_TCP ? |
| 736 | SKCS_STATUS_TCP_CSUM_OK : SKCS_STATUS_UDP_CSUM_OK); |
| 737 | } |
| 738 | |
| 739 | /* TCP/UDP checksum error. */ |
| 740 | |
| 741 | NextLevelProtoStats->RxErrCts++; |
| 742 | |
| 743 | return (NextLevelProtocol == SKCS_PROTO_ID_TCP ? |
| 744 | SKCS_STATUS_TCP_CSUM_ERROR : SKCS_STATUS_UDP_CSUM_ERROR); |
| 745 | } /* SkCsGetReceiveInfo */ |
| 746 | #endif |
| 747 | |
| 748 | |
| 749 | /****************************************************************************** |
| 750 | * |
| 751 | * SkCsSetReceiveFlags - set checksum receive flags |
| 752 | * |
| 753 | * Description: |
| 754 | * Use this function to set the various receive flags. According to the |
| 755 | * protocol flags set by the caller, the start offsets within received |
| 756 | * packets of the two hardware checksums are returned. These offsets must |
| 757 | * be stored in all receive descriptors. |
| 758 | * |
| 759 | * Arguments: |
| 760 | * pAc - Pointer to adapter context struct. |
| 761 | * |
| 762 | * ReceiveFlags - Any combination of SK_PROTO_XXX flags of the protocols |
| 763 | * for which the caller wants checksum information on received frames. |
| 764 | * |
| 765 | * pChecksum1Offset - The start offset of the first receive descriptor |
| 766 | * hardware checksum to be calculated for received frames is returned |
| 767 | * here. |
| 768 | * |
| 769 | * pChecksum2Offset - The start offset of the second receive descriptor |
| 770 | * hardware checksum to be calculated for received frames is returned |
| 771 | * here. |
| 772 | * |
| 773 | * Returns: N/A |
| 774 | * Returns the two hardware checksum start offsets. |
| 775 | */ |
| 776 | void SkCsSetReceiveFlags( |
| 777 | SK_AC *pAc, /* Adapter context struct. */ |
| 778 | unsigned ReceiveFlags, /* New receive flags. */ |
| 779 | unsigned *pChecksum1Offset, /* Offset for hardware checksum 1. */ |
| 780 | unsigned *pChecksum2Offset, /* Offset for hardware checksum 2. */ |
| 781 | int NetNumber) |
| 782 | { |
| 783 | /* Save the receive flags. */ |
| 784 | |
| 785 | pAc->Csum.ReceiveFlags[NetNumber] = ReceiveFlags; |
| 786 | |
| 787 | /* First checksum start offset is the IP header. */ |
| 788 | *pChecksum1Offset = SKCS_MAC_HEADER_SIZE; |
| 789 | |
| 790 | /* |
| 791 | * Second checksum start offset is the IP data. Note that this may vary |
| 792 | * if there are any IP header options in the actual packet. |
| 793 | */ |
| 794 | *pChecksum2Offset = SKCS_MAC_HEADER_SIZE + SKCS_IP_HEADER_SIZE; |
| 795 | } /* SkCsSetReceiveFlags */ |
| 796 | |
| 797 | #ifndef SkCsCalculateChecksum |
| 798 | |
| 799 | /****************************************************************************** |
| 800 | * |
| 801 | * SkCsCalculateChecksum - calculate checksum for specified data |
| 802 | * |
| 803 | * Description: |
| 804 | * Calculate and return the 16-bit Internet Checksum for the specified |
| 805 | * data. |
| 806 | * |
| 807 | * Arguments: |
| 808 | * pData - Pointer to data for which the checksum shall be calculated. |
| 809 | * Note: The pointer should be aligned on a 16-bit boundary. |
| 810 | * |
| 811 | * Length - Length in bytes of data to checksum. |
| 812 | * |
| 813 | * Returns: |
| 814 | * The 16-bit Internet Checksum for the specified data. |
| 815 | * |
| 816 | * Note: The checksum is calculated in the machine's natural byte order, |
| 817 | * i.e. little vs. big endian. Thus, the resulting checksum is different |
| 818 | * for the same input data on little and big endian machines. |
| 819 | * |
| 820 | * However, when written back to the network packet, the byte order is |
| 821 | * always in correct network order. |
| 822 | */ |
| 823 | unsigned SkCsCalculateChecksum( |
| 824 | void *pData, /* Data to checksum. */ |
| 825 | unsigned Length) /* Length of data. */ |
| 826 | { |
| 827 | SK_U16 *pU16; /* Pointer to the data as 16-bit words. */ |
| 828 | unsigned long Checksum; /* Checksum; must be at least 32 bits. */ |
| 829 | |
| 830 | /* Sum up all 16-bit words. */ |
| 831 | |
| 832 | pU16 = (SK_U16 *) pData; |
| 833 | for (Checksum = 0; Length > 1; Length -= 2) { |
| 834 | Checksum += *pU16++; |
| 835 | } |
| 836 | |
| 837 | /* If this is an odd number of bytes, add-in the last byte. */ |
| 838 | |
| 839 | if (Length > 0) { |
| 840 | #ifdef SK_BIG_ENDIAN |
| 841 | /* Add the last byte as the high byte. */ |
| 842 | Checksum += ((unsigned) *(SK_U8 *) pU16) << 8; |
| 843 | #else /* !SK_BIG_ENDIAN */ |
| 844 | /* Add the last byte as the low byte. */ |
| 845 | Checksum += *(SK_U8 *) pU16; |
| 846 | #endif /* !SK_BIG_ENDIAN */ |
| 847 | } |
| 848 | |
| 849 | /* Add-in any carries. */ |
| 850 | |
| 851 | SKCS_OC_ADD(Checksum, Checksum, 0); |
| 852 | |
| 853 | /* Add-in any new carry. */ |
| 854 | |
| 855 | SKCS_OC_ADD(Checksum, Checksum, 0); |
| 856 | |
| 857 | /* Note: All bits beyond the 16-bit limit are now zero. */ |
| 858 | |
| 859 | return ((unsigned) Checksum); |
| 860 | } /* SkCsCalculateChecksum */ |
| 861 | |
| 862 | #endif /* SkCsCalculateChecksum */ |
| 863 | |
| 864 | /****************************************************************************** |
| 865 | * |
| 866 | * SkCsEvent - the CSUM event dispatcher |
| 867 | * |
| 868 | * Description: |
| 869 | * This is the event handler for the CSUM module. |
| 870 | * |
| 871 | * Arguments: |
| 872 | * pAc - Pointer to adapter context. |
| 873 | * |
| 874 | * Ioc - I/O context. |
| 875 | * |
| 876 | * Event - Event id. |
| 877 | * |
| 878 | * Param - Event dependent parameter. |
| 879 | * |
| 880 | * Returns: |
| 881 | * The 16-bit Internet Checksum for the specified data. |
| 882 | * |
| 883 | * Note: The checksum is calculated in the machine's natural byte order, |
| 884 | * i.e. little vs. big endian. Thus, the resulting checksum is different |
| 885 | * for the same input data on little and big endian machines. |
| 886 | * |
| 887 | * However, when written back to the network packet, the byte order is |
| 888 | * always in correct network order. |
| 889 | */ |
| 890 | int SkCsEvent( |
| 891 | SK_AC *pAc, /* Pointer to adapter context. */ |
| 892 | SK_IOC Ioc, /* I/O context. */ |
| 893 | SK_U32 Event, /* Event id. */ |
| 894 | SK_EVPARA Param) /* Event dependent parameter. */ |
| 895 | { |
| 896 | int ProtoIndex; |
| 897 | int NetNumber; |
| 898 | |
| 899 | switch (Event) { |
| 900 | /* |
| 901 | * Clear protocol statistics. |
| 902 | * |
| 903 | * Param - Protocol index, or -1 for all protocols. |
| 904 | * - Net number. |
| 905 | */ |
| 906 | case SK_CSUM_EVENT_CLEAR_PROTO_STATS: |
| 907 | |
| 908 | ProtoIndex = (int)Param.Para32[1]; |
| 909 | NetNumber = (int)Param.Para32[0]; |
| 910 | if (ProtoIndex < 0) { /* Clear for all protocols. */ |
| 911 | if (NetNumber >= 0) { |
| 912 | memset(&pAc->Csum.ProtoStats[NetNumber][0], 0, |
| 913 | sizeof(pAc->Csum.ProtoStats[NetNumber])); |
| 914 | } |
| 915 | } |
| 916 | else { /* Clear for individual protocol. */ |
| 917 | memset(&pAc->Csum.ProtoStats[NetNumber][ProtoIndex], 0, |
| 918 | sizeof(pAc->Csum.ProtoStats[NetNumber][ProtoIndex])); |
| 919 | } |
| 920 | break; |
| 921 | default: |
| 922 | break; |
| 923 | } |
| 924 | return (0); /* Success. */ |
| 925 | } /* SkCsEvent */ |
| 926 | |
| 927 | #endif /* SK_USE_CSUM */ |
wdenk | 149dded | 2003-09-10 18:20:28 +0000 | [diff] [blame] | 928 | |
| 929 | #endif /* CONFIG_SK98 */ |