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