2
* Copyright (c) 2014 VMware, Inc.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at:
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
24
#define OVS_DBG_MOD OVS_DBG_CHECKSUM
26
#include "PacketParser.h"
29
#define htons(_x) (((UINT16)(_x) >> 8) + (((UINT16)(_x) << 8) & 0xff00))
33
#define swap64(_x) ((((UINT64)(_x) >> 8) & 0x00ff00ff00ff00ff) + \
34
(((UINT64)(_x) << 8) & 0xff00ff00ff00ff00))
38
_x = ((_x) >> 32) + ((_x) & 0xffffffff); \
39
_x = (UINT32)(((_x) >> 32) + (_x)); \
40
_x = ((_x) >> 16) + ((_x) & 0xffff); \
41
_x = (UINT16)(((_x) >> 16) + (_x))
44
_x = ((_x) >> 16) + ((_x) & 0xffff); \
45
_x = (UINT16)(((_x) >> 16) + (_x))
49
*----------------------------------------------------------------------------
50
* CalculateOnesComplement --
52
* Given the start address and buffer length, calculate the 1's complement
53
* This routine can be used when multiple buffers are used for a packets.
55
* PLEASE NOTE, even though the last parameter is UINT64, but the assumption
56
* is it will not overflowed after adding the extra data.
57
* ------------------------------------------------
60
* As name indicate, the final data is not 1's complemnent
61
*----------------------------------------------------------------------------
64
CalculateOnesComplement(UINT8 *start,
70
UINT64 *src = (UINT64 *)start;
76
while (totalLength > 7) {
78
sum += (val >> 32) + (val & 0xffffffff);
82
if (totalLength > 3) {
83
sum += *(UINT32 *)src;
84
src = (UINT64 *)((UINT8 *)src + 4);
89
switch (totalLength) {
98
sum = (isEvenStart ? sum : swap64(sum)) + initial;
103
*----------------------------------------------------------------------------
104
* CalculateChecksum --
106
* Given the start point, and length, calculate the checksum
107
* as 1's complement of 1's comlement.
109
* This assume the checksum field is initailized properly.
112
* ptr: point to the data to be checksumed
113
* totalLength: total length of the data
114
* initial: inital value to remit the checksum. Please note this
115
* value should be network byte order value.
117
* The last parameter may be useful where you don't want to set
118
* checksum field to zero, in that case you can pass ~checksum,
119
* this is equivalent of set checksum field to zero.
122
* The result can be assigned to checksum field directly.
123
*----------------------------------------------------------------------------
126
CalculateChecksum(UINT8 *ptr,
130
UINT64 sum = CalculateOnesComplement(ptr, totalLength, initial, TRUE);
136
*----------------------------------------------------------------------------
137
* CopyAndCalculateOnesComplement --
139
* Given the start address and buffer length, calculate the 1's complement
140
* at same time, copt the data from src to dst.
142
* This routine can be used when multiple buffers are used for a packets.
144
* PLEASE NOTE, even though the last parameter is UINT64, but the assumption
145
* is it will not overflowed after adding the extra data.
146
* ------------------------------------------------
149
* As name indicate, the final data is not 1's complemnent
150
*----------------------------------------------------------------------------
153
CopyAndCalculateOnesComplement(UINT8 *dst,
160
UINT64 *src64, *dst64;
166
src64 = (UINT64 *)src;
167
dst64 = (UINT64 *)dst;
172
sum += (val >> 32) + (val & 0xffffffff);
179
val = *(UINT32 *)src64;
180
*(UINT32 *)dst64 = (UINT32)val;
182
dst64 = (UINT64 *)((UINT8 *)dst64 + 4);
183
src64 = (UINT64 *)((UINT8 *)src64 + 4);
186
src = (UINT8 *)src64;
187
dst = (UINT8 *)dst64;
201
sum = (isEvenStart ? sum : swap64(sum)) + initial;
206
*----------------------------------------------------------------------------
207
* CopyAndCalculateChecksum --
209
* This is similar to CalculateChecksum, except it will also copy data to
210
* destination address.
211
*----------------------------------------------------------------------------
214
CopyAndCalculateChecksum(UINT8 *dst,
220
UINT64 sum = CopyAndCalculateOnesComplement(dst, src, length, initial,
228
*----------------------------------------------------------------------------
231
* Give IP header, calculate the IP checksum.
232
* We assume IP checksum field is initialized properly
235
* ipHdr: IP header start point
236
* length: IP header length (potentially include IP options)
237
* initial: same as CalculateChecksum
240
* The result is already 1's complement, so can be assigned
241
* to checksum field directly
242
*----------------------------------------------------------------------------
245
IPChecksum(UINT8 *ipHdr,
249
UINT32 sum = initial;
250
UINT16 *ptr = (UINT16 *)ipHdr;
251
ASSERT((length & 0x3) == 0);
262
*----------------------------------------------------------------------------
263
* IPPseudoChecksum --
265
* Give src and dst IP address, protocol value and total
266
* upper layer length(not include IP header, but include
267
* upller layer protocol header, for example it include
268
* TCP header for TCP checksum), calculate the pseudo
269
* checksum, please note this checksum is just 1's complement
273
* src: please note it is in network byte order
275
* protocol: protocol value in IP header
276
* totalLength: total length of upper layer data including
281
* This value should be put in TCP checksum field before
282
* calculating TCP checksum using CalculateChecksum with
283
* initial value of 0.
284
*----------------------------------------------------------------------------
287
IPPseudoChecksum(UINT32 *src,
292
UINT32 sum = (UINT32)htons(totalLength) + htons(protocol);
293
sum += (*src >> 16) + (*src & 0xffff);
294
sum += (*dst >> 16) + (*dst & 0xffff);
300
*----------------------------------------------------------------------------
301
* IPv6PseudoChecksum --
303
* Given IPv6 src and dst address, upper layer protocol and total
304
* upper layer protocol data length including upper layer header
305
* part, calculate the pseudo checksum for upper layer protocol
308
* please note this checksum is just 1's complement addition.
311
* src: src IPv6 address in network byte order
312
* dst: dst IPv6 address.
313
* protocol: upper layer protocol
314
* totalLength: total length of upper layer data. Please note this is
315
* in host byte order.
319
* Place in upper layer checksum field before calculate upper layer
321
*----------------------------------------------------------------------------
324
IPv6PseudoChecksum(UINT32 *src,
329
UINT64 sum = (UINT32)htons(totalLength) + htons(protocol);
330
sum += (UINT64)src[0] + src[1] + src[2] + src[3];
331
sum += (UINT64)dst[0] + dst[1] + dst[2] + dst[3];
337
*----------------------------------------------------------------------------
338
* ChecksumUpdate32 --
340
* Given old checksum value (as it is in checksum field),
341
* prev value of the relevant field in network byte order
342
* new value of the relevant field in the network byte order
343
* calculate the new checksum.
344
* Please check relevant RFC for reference.
347
* oldSum: old checksum value in checksum field
348
* prev: previous value of relevant 32 bit feld in network
350
* new: new value of the relevant 32 bit field in network
354
* new checksum value to be placed in the checksum field.
355
*----------------------------------------------------------------------------
358
ChecksumUpdate32(UINT16 oldSum,
363
sum = (sum >> 16) + (sum & 0xffff);
364
sum += (newValue >> 16) + (newValue & 0xffff);
365
sum += (UINT16)~oldSum;
372
*----------------------------------------------------------------------------
373
* ChecksumUpdate16 --
375
* Given old checksum value (as it is in checksum field),
376
* prev value of the relevant field in network byte order
377
* new value of the relevant field in the network byte order
378
* calculate the new checksum.
379
* Please check relevant RFC for reference.
382
* oldSum: old checksum value in checksum field
383
* prev: previous value of relevant 32 bit feld in network
385
* new: new value of the relevant 32 bit field in network
389
* new checksum value to be placed in the checksum field.
390
*----------------------------------------------------------------------------
393
ChecksumUpdate16(UINT16 oldSum,
397
UINT32 sum = (UINT16)~oldSum;
398
sum += (UINT32)((UINT16)~prev) + newValue;
404
*----------------------------------------------------------------------------
405
* CalculateChecksumNB --
407
* Calculates checksum over a length of bytes contained in an NB.
409
* nb : NB which contains the packet bytes.
410
* csumDataLen : Length of bytes to be checksummed.
411
* offset : offset to the first bytes of the data stream to be
415
* return 0, if there is a failure.
416
*----------------------------------------------------------------------------
419
CalculateChecksumNB(const PNET_BUFFER nb,
429
/* Running count of bytes in remainder of the MDLs including current. */
432
if ((nb == NULL) || (csumDataLen == 0)
433
|| (offset >= NET_BUFFER_DATA_LENGTH(nb))
434
|| (offset + csumDataLen > NET_BUFFER_DATA_LENGTH(nb))) {
435
OVS_LOG_ERROR("Invalid parameters - csum length %u, offset %u,"
436
"pkt%s len %u", csumDataLen, offset, nb? "":"(null)",
437
nb? NET_BUFFER_DATA_LENGTH(nb) : 0);
441
currentMdl = NET_BUFFER_CURRENT_MDL(nb);
442
packetLen = NET_BUFFER_DATA_LENGTH(nb);
444
MmGetMdlByteCount(currentMdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb);
446
firstMdlLen = MIN(firstMdlLen, packetLen);
447
if (offset < firstMdlLen) {
448
src = (PUCHAR) MmGetSystemAddressForMdlSafe(currentMdl, LowPagePriority);
452
src += (NET_BUFFER_CURRENT_MDL_OFFSET(nb) + offset);
453
mdlLen = firstMdlLen - offset;
454
packetLen -= firstMdlLen;
455
ASSERT((INT)packetLen >= 0);
457
offset -= firstMdlLen;
458
packetLen -= firstMdlLen;
459
ASSERT((INT)packetLen >= 0);
460
currentMdl = NDIS_MDL_LINKAGE(currentMdl);
461
mdlLen = MmGetMdlByteCount(currentMdl);
462
mdlLen = MIN(mdlLen, packetLen);
464
while (offset >= mdlLen) {
467
ASSERT((INT)packetLen >= 0);
468
currentMdl = NDIS_MDL_LINKAGE(currentMdl);
469
mdlLen = MmGetMdlByteCount(currentMdl);
470
mdlLen = MIN(mdlLen, packetLen);
473
src = (PUCHAR)MmGetSystemAddressForMdlSafe(currentMdl, LowPagePriority);
482
while (csumDataLen && (currentMdl != NULL)) {
483
ASSERT(mdlLen < 65536);
484
csLen = MIN((UINT16) mdlLen, csumDataLen);
485
//XXX Not handling odd bytes yet.
486
ASSERT(((csLen & 0x1) == 0) || csumDataLen <= mdlLen);
488
csum = CalculateOnesComplement(src, csLen, csum, TRUE);
491
csumDataLen -= csLen;
492
currentMdl = NDIS_MDL_LINKAGE(currentMdl);
493
if (csumDataLen && currentMdl) {
494
src = MmGetSystemAddressForMdlSafe(currentMdl, LowPagePriority);
499
mdlLen = MmGetMdlByteCount(currentMdl);
500
mdlLen = MIN(mdlLen, packetLen);
501
/* packetLen does not include the current MDL from here on. */
503
ASSERT((INT)packetLen >= 0);
507
ASSERT(csumDataLen == 0);
508
ASSERT((csum & ~0xffff) == 0);
509
return (UINT16) ~csum;
513
* --------------------------------------------------------------------------
514
* OvsValidateIPChecksum
515
* --------------------------------------------------------------------------
518
OvsValidateIPChecksum(PNET_BUFFER_LIST curNbl,
519
POVS_PACKET_HDR_INFO hdrInfo)
521
NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
522
uint16_t checksum, hdrChecksum;
523
struct IPHdr ip_storage;
526
if (!hdrInfo->isIPv4) {
527
return NDIS_STATUS_SUCCESS;
530
/* First check if NIC has indicated checksum failure. */
531
csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl,
532
TcpIpChecksumNetBufferListInfo);
533
if (csumInfo.Receive.IpChecksumFailed) {
534
return NDIS_STATUS_FAILURE;
537
/* Next, check if the NIC did not validate the RX checksum. */
538
if (!csumInfo.Receive.IpChecksumSucceeded) {
539
ipHdr = OvsGetIp(curNbl, hdrInfo->l3Offset, &ip_storage);
542
hdrChecksum = ipHdr->check;
543
ip_storage.check = 0;
544
checksum = IPChecksum((uint8 *)&ip_storage, ipHdr->ihl * 4, 0);
545
if (checksum != hdrChecksum) {
546
return NDIS_STATUS_FAILURE;
550
return NDIS_STATUS_SUCCESS;
554
*----------------------------------------------------------------------------
555
* OvsValidateUDPChecksum
556
*----------------------------------------------------------------------------
559
OvsValidateUDPChecksum(PNET_BUFFER_LIST curNbl, BOOLEAN udpCsumZero)
561
NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
563
csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo);
566
/* Zero is valid checksum. */
567
csumInfo.Receive.UdpChecksumFailed = 0;
568
NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value;
569
return NDIS_STATUS_SUCCESS;
572
/* First check if NIC has indicated UDP checksum failure. */
573
if (csumInfo.Receive.UdpChecksumFailed) {
574
return NDIS_STATUS_INVALID_PACKET;
577
return NDIS_STATUS_SUCCESS;