Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 */ |