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
;