2 * Copyright (c) 2014, 2016 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.
21 #include "PacketParser.h"
26 #define OVS_DBG_MOD OVS_DBG_CHECKSUM
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
;
71 while (totalLength
> 7) {
81 if (totalLength
> 3) {
82 UINT32 val
= *(UINT32
*)start
;
89 if (totalLength
> 1) {
90 UINT16 val
= *(UINT16
*)start
;
97 if (totalLength
> 0) {
100 if (sum
< val
) sum
++;
104 ASSERT(totalLength
== 0);
107 sum
= _byteswap_uint64(sum
);
111 if (sum
< initial
) sum
++;
117 *----------------------------------------------------------------------------
118 * CalculateChecksum --
120 * Given the start point, and length, calculate the checksum
121 * as 1's complement of 1's comlement.
123 * This assume the checksum field is initailized properly.
126 * ptr: point to the data to be checksumed
127 * totalLength: total length of the data
128 * initial: inital value to remit the checksum. Please note this
129 * value should be network byte order value.
131 * The last parameter may be useful where you don't want to set
132 * checksum field to zero, in that case you can pass ~checksum,
133 * this is equivalent of set checksum field to zero.
136 * The result can be assigned to checksum field directly.
137 *----------------------------------------------------------------------------
140 CalculateChecksum(UINT8
*ptr
,
144 UINT64 sum
= CalculateOnesComplement(ptr
, totalLength
, initial
, TRUE
);
150 *----------------------------------------------------------------------------
151 * CopyAndCalculateOnesComplement --
153 * Given the start address and buffer length, calculate the 1's complement
154 * at same time, copt the data from src to dst.
156 * This routine can be used when multiple buffers are used for a packets.
158 * PLEASE NOTE, even though the last parameter is UINT64, but the assumption
159 * is it will not overflowed after adding the extra data.
160 * ------------------------------------------------
163 * As name indicate, the final data is not 1's complemnent
164 *----------------------------------------------------------------------------
167 CopyAndCalculateOnesComplement(UINT8
*dst
,
174 UINT64
*src64
, *dst64
;
180 src64
= (UINT64
*)src
;
181 dst64
= (UINT64
*)dst
;
186 sum
+= (val
>> 32) + (val
& 0xffffffff);
193 val
= *(UINT32
*)src64
;
194 *(UINT32
*)dst64
= (UINT32
)val
;
196 dst64
= (UINT64
*)((UINT8
*)dst64
+ 4);
197 src64
= (UINT64
*)((UINT8
*)src64
+ 4);
200 src
= (UINT8
*)src64
;
201 dst
= (UINT8
*)dst64
;
215 sum
= (isEvenStart
? sum
: swap64(sum
)) + initial
;
220 *----------------------------------------------------------------------------
221 * CopyAndCalculateChecksum --
223 * This is similar to CalculateChecksum, except it will also copy data to
224 * destination address.
225 *----------------------------------------------------------------------------
228 CopyAndCalculateChecksum(UINT8
*dst
,
234 UINT64 sum
= CopyAndCalculateOnesComplement(dst
, src
, length
, initial
,
242 *----------------------------------------------------------------------------
245 * Give IP header, calculate the IP checksum.
246 * We assume IP checksum field is initialized properly
249 * ipHdr: IP header start point
250 * length: IP header length (potentially include IP options)
251 * initial: same as CalculateChecksum
254 * The result is already 1's complement, so can be assigned
255 * to checksum field directly
256 *----------------------------------------------------------------------------
259 IPChecksum(UINT8
*ipHdr
,
263 UINT32 sum
= initial
;
264 UINT16
*ptr
= (UINT16
*)ipHdr
;
265 ASSERT((length
& 0x3) == 0);
276 *----------------------------------------------------------------------------
277 * IPPseudoChecksum --
279 * Give src and dst IP address, protocol value and total
280 * upper layer length(not include IP header, but include
281 * upller layer protocol header, for example it include
282 * TCP header for TCP checksum), calculate the pseudo
283 * checksum, please note this checksum is just 1's complement
287 * src: please note it is in network byte order
289 * protocol: protocol value in IP header
290 * totalLength: total length of upper layer data including
295 * This value should be put in TCP checksum field before
296 * calculating TCP checksum using CalculateChecksum with
297 * initial value of 0.
298 *----------------------------------------------------------------------------
301 IPPseudoChecksum(UINT32
*src
,
306 UINT32 sum
= (UINT32
)htons(totalLength
) + htons(protocol
);
307 sum
+= (*src
>> 16) + (*src
& 0xffff);
308 sum
+= (*dst
>> 16) + (*dst
& 0xffff);
314 *----------------------------------------------------------------------------
315 * IPv6PseudoChecksum --
317 * Given IPv6 src and dst address, upper layer protocol and total
318 * upper layer protocol data length including upper layer header
319 * part, calculate the pseudo checksum for upper layer protocol
322 * please note this checksum is just 1's complement addition.
325 * src: src IPv6 address in network byte order
326 * dst: dst IPv6 address.
327 * protocol: upper layer protocol
328 * totalLength: total length of upper layer data. Please note this is
329 * in host byte order.
333 * Place in upper layer checksum field before calculate upper layer
335 *----------------------------------------------------------------------------
338 IPv6PseudoChecksum(UINT32
*src
,
343 UINT64 sum
= (UINT32
)htons(totalLength
) + htons(protocol
);
344 sum
+= (UINT64
)src
[0] + src
[1] + src
[2] + src
[3];
345 sum
+= (UINT64
)dst
[0] + dst
[1] + dst
[2] + dst
[3];
351 *----------------------------------------------------------------------------
352 * ChecksumUpdate32 --
354 * Given old checksum value (as it is in checksum field),
355 * prev value of the relevant field in network byte order
356 * new value of the relevant field in the network byte order
357 * calculate the new checksum.
358 * Please check relevant RFC for reference.
361 * oldSum: old checksum value in checksum field
362 * prev: previous value of relevant 32 bit feld in network
364 * new: new value of the relevant 32 bit field in network
368 * new checksum value to be placed in the checksum field.
369 *----------------------------------------------------------------------------
372 ChecksumUpdate32(UINT16 oldSum
,
377 sum
= (sum
>> 16) + (sum
& 0xffff);
378 sum
+= (newValue
>> 16) + (newValue
& 0xffff);
379 sum
+= (UINT16
)~oldSum
;
386 *----------------------------------------------------------------------------
387 * ChecksumUpdate16 --
389 * Given old checksum value (as it is in checksum field),
390 * prev value of the relevant field in network byte order
391 * new value of the relevant field in the network byte order
392 * calculate the new checksum.
393 * Please check relevant RFC for reference.
396 * oldSum: old checksum value in checksum field
397 * prev: previous value of relevant 32 bit feld in network
399 * new: new value of the relevant 32 bit field in network
403 * new checksum value to be placed in the checksum field.
404 *----------------------------------------------------------------------------
407 ChecksumUpdate16(UINT16 oldSum
,
411 UINT32 sum
= (UINT16
)~oldSum
;
412 sum
+= (UINT32
)((UINT16
)~prev
) + newValue
;
418 *----------------------------------------------------------------------------
419 * CalculateChecksumNB --
421 * Calculates checksum over a length of bytes contained in an NB.
423 * nb : NB which contains the packet bytes.
424 * csumDataLen : Length of bytes to be checksummed.
425 * offset : offset to the first bytes of the data stream to be
429 * return 0, if there is a failure.
430 *----------------------------------------------------------------------------
433 CalculateChecksumNB(const PNET_BUFFER nb
,
443 /* Running count of bytes in remainder of the MDLs including current. */
445 BOOLEAN swapEnd
= 1 & csumDataLen
;
447 if ((nb
== NULL
) || (csumDataLen
== 0)
448 || (offset
>= NET_BUFFER_DATA_LENGTH(nb
))
449 || (offset
+ csumDataLen
> NET_BUFFER_DATA_LENGTH(nb
))) {
450 OVS_LOG_ERROR("Invalid parameters - csum length %u, offset %u,"
451 "pkt%s len %u", csumDataLen
, offset
, nb
? "":"(null)",
452 nb
? NET_BUFFER_DATA_LENGTH(nb
) : 0);
456 currentMdl
= NET_BUFFER_CURRENT_MDL(nb
);
457 packetLen
= NET_BUFFER_DATA_LENGTH(nb
);
459 MmGetMdlByteCount(currentMdl
) - NET_BUFFER_CURRENT_MDL_OFFSET(nb
);
461 firstMdlLen
= MIN(firstMdlLen
, packetLen
);
462 if (offset
< firstMdlLen
) {
463 src
= (PUCHAR
) MmGetSystemAddressForMdlSafe(currentMdl
, LowPagePriority
);
467 src
+= (NET_BUFFER_CURRENT_MDL_OFFSET(nb
) + offset
);
468 mdlLen
= firstMdlLen
- offset
;
469 packetLen
-= firstMdlLen
;
470 ASSERT((INT
)packetLen
>= 0);
472 offset
-= firstMdlLen
;
473 packetLen
-= firstMdlLen
;
474 ASSERT((INT
)packetLen
>= 0);
475 currentMdl
= NDIS_MDL_LINKAGE(currentMdl
);
476 mdlLen
= MmGetMdlByteCount(currentMdl
);
477 mdlLen
= MIN(mdlLen
, packetLen
);
479 while (offset
>= mdlLen
) {
482 ASSERT((INT
)packetLen
>= 0);
483 currentMdl
= NDIS_MDL_LINKAGE(currentMdl
);
484 mdlLen
= MmGetMdlByteCount(currentMdl
);
485 mdlLen
= MIN(mdlLen
, packetLen
);
488 src
= (PUCHAR
)MmGetSystemAddressForMdlSafe(currentMdl
, LowPagePriority
);
497 while (csumDataLen
&& (currentMdl
!= NULL
)) {
498 ASSERT(mdlLen
< 65536);
499 csLen
= MIN((UINT16
) mdlLen
, csumDataLen
);
501 csum
= CalculateOnesComplement(src
, csLen
, csum
, !(1 & csumDataLen
));
504 csumDataLen
-= csLen
;
505 currentMdl
= NDIS_MDL_LINKAGE(currentMdl
);
506 if (csumDataLen
&& currentMdl
) {
507 src
= MmGetSystemAddressForMdlSafe(currentMdl
, LowPagePriority
);
512 mdlLen
= MmGetMdlByteCount(currentMdl
);
513 mdlLen
= MIN(mdlLen
, packetLen
);
514 /* packetLen does not include the current MDL from here on. */
516 ASSERT((INT
)packetLen
>= 0);
521 ASSERT(csumDataLen
== 0);
522 ASSERT((csum
& ~0xffff) == 0);
523 csum
= (UINT16
)~csum
;
525 return _byteswap_ushort((UINT16
)csum
);
531 * --------------------------------------------------------------------------
532 * OvsValidateIPChecksum
533 * --------------------------------------------------------------------------
536 OvsValidateIPChecksum(PNET_BUFFER_LIST curNbl
,
537 POVS_PACKET_HDR_INFO hdrInfo
)
539 NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo
;
540 uint16_t checksum
, hdrChecksum
;
541 struct IPHdr ip_storage
;
544 if (!hdrInfo
->isIPv4
) {
545 return NDIS_STATUS_SUCCESS
;
548 /* First check if NIC has indicated checksum failure. */
549 csumInfo
.Value
= NET_BUFFER_LIST_INFO(curNbl
,
550 TcpIpChecksumNetBufferListInfo
);
551 if (csumInfo
.Receive
.IpChecksumFailed
) {
552 return NDIS_STATUS_FAILURE
;
555 /* Next, check if the NIC did not validate the RX checksum. */
556 if (!csumInfo
.Receive
.IpChecksumSucceeded
) {
557 ipHdr
= OvsGetIp(curNbl
, hdrInfo
->l3Offset
, &ip_storage
);
560 hdrChecksum
= ipHdr
->check
;
561 ip_storage
.check
= 0;
562 checksum
= IPChecksum((uint8
*)&ip_storage
, ipHdr
->ihl
* 4, 0);
563 if (checksum
!= hdrChecksum
) {
564 return NDIS_STATUS_FAILURE
;
568 return NDIS_STATUS_SUCCESS
;
572 *----------------------------------------------------------------------------
573 * OvsValidateUDPChecksum
574 *----------------------------------------------------------------------------
577 OvsValidateUDPChecksum(PNET_BUFFER_LIST curNbl
, BOOLEAN udpCsumZero
)
579 NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo
;
581 csumInfo
.Value
= NET_BUFFER_LIST_INFO(curNbl
,
582 TcpIpChecksumNetBufferListInfo
);
585 /* Zero is valid checksum. */
586 csumInfo
.Receive
.UdpChecksumFailed
= 0;
587 NET_BUFFER_LIST_INFO(curNbl
, TcpIpChecksumNetBufferListInfo
) =
589 return NDIS_STATUS_SUCCESS
;
592 /* First check if NIC has indicated UDP checksum failure. */
593 if (csumInfo
.Receive
.UdpChecksumFailed
) {
594 return NDIS_STATUS_INVALID_PACKET
;
597 return NDIS_STATUS_SUCCESS
;
602 *----------------------------------------------------------------------------
603 * OvsCalculateUDPChecksum
604 * Calculate UDP checksum
605 *----------------------------------------------------------------------------
608 OvsCalculateUDPChecksum(PNET_BUFFER_LIST curNbl
,
614 NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo
;
617 csumInfo
.Value
= NET_BUFFER_LIST_INFO(curNbl
, TcpIpChecksumNetBufferListInfo
);
619 /* Next check if UDP checksum has been calculated. */
620 if (!csumInfo
.Receive
.UdpChecksumSucceeded
) {
623 checkSum
= udpHdr
->check
;
625 l4Payload
= packetLength
- sizeof(EthHdr
) - ipHdr
->ihl
* 4;
628 IPPseudoChecksum((UINT32
*)&ipHdr
->saddr
,
629 (UINT32
*)&ipHdr
->daddr
,
630 IPPROTO_UDP
, (UINT16
)l4Payload
);
631 udpHdr
->check
= CalculateChecksumNB(curNb
, (UINT16
)l4Payload
,
632 sizeof(EthHdr
) + ipHdr
->ihl
* 4);
633 if (checkSum
!= udpHdr
->check
) {
634 OVS_LOG_TRACE("UDP checksum incorrect.");
635 return NDIS_STATUS_INVALID_PACKET
;
639 csumInfo
.Receive
.UdpChecksumSucceeded
= 1;
640 NET_BUFFER_LIST_INFO(curNbl
, TcpIpChecksumNetBufferListInfo
) = csumInfo
.Value
;
641 return NDIS_STATUS_SUCCESS
;
647 * OvsApplySWChecksumOnNB --
649 * This function calculates and sets the required software offloads given by
650 * csumInfo for a given NBL(nbl) with a single NB.
654 OvsApplySWChecksumOnNB(POVS_PACKET_HDR_INFO layers
,
655 PNET_BUFFER_LIST nbl
,
656 PNDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo
)
661 UINT32 packetLength
= 0;
664 curNb
= NET_BUFFER_LIST_FIRST_NB(nbl
);
665 ASSERT(curNb
->Next
== NULL
);
666 packetLength
= NET_BUFFER_DATA_LENGTH(curNb
);
667 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
668 bufferStart
= (PUINT8
)MmGetSystemAddressForMdlSafe(curMdl
,
671 return NDIS_STATUS_RESOURCES
;
674 bufferStart
+= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
676 if (layers
->isIPv4
) {
677 IPHdr
*ip
= (IPHdr
*)(bufferStart
+ layers
->l3Offset
);
679 if (csumInfo
->Transmit
.IpHeaderChecksum
) {
681 ip
->check
= IPChecksum((UINT8
*)ip
, 4 * ip
->ihl
, 0);
684 if (layers
->isTcp
&& csumInfo
->Transmit
.TcpChecksum
) {
685 UINT16 csumLength
= (UINT16
)(packetLength
- layers
->l4Offset
);
686 TCPHdr
*tcp
= (TCPHdr
*)(bufferStart
+ layers
->l4Offset
);
687 tcp
->check
= IPPseudoChecksum(&ip
->saddr
, &ip
->daddr
,
688 IPPROTO_TCP
, csumLength
);
689 tcp
->check
= CalculateChecksumNB(curNb
, csumLength
,
690 (UINT32
)(layers
->l4Offset
));
691 } else if (layers
->isUdp
&& csumInfo
->Transmit
.UdpChecksum
) {
692 UINT16 csumLength
= (UINT16
)(packetLength
- layers
->l4Offset
);
693 UDPHdr
*udp
= (UDPHdr
*)((PCHAR
)ip
+ sizeof *ip
);
694 udp
->check
= IPPseudoChecksum(&ip
->saddr
, &ip
->daddr
,
695 IPPROTO_UDP
, csumLength
);
696 udp
->check
= CalculateChecksumNB(curNb
, csumLength
,
697 (UINT32
)(layers
->l4Offset
));
699 } else if (layers
->isIPv6
) {
700 IPv6Hdr
*ip
= (IPv6Hdr
*)(bufferStart
+ layers
->l3Offset
);
702 if (layers
->isTcp
&& csumInfo
->Transmit
.TcpChecksum
) {
703 UINT16 csumLength
= (UINT16
)(packetLength
- layers
->l4Offset
);
704 TCPHdr
*tcp
= (TCPHdr
*)(bufferStart
+ layers
->l4Offset
);
705 tcp
->check
= IPv6PseudoChecksum((UINT32
*) &ip
->saddr
,
706 (UINT32
*) &ip
->daddr
,
707 IPPROTO_TCP
, csumLength
);
708 tcp
->check
= CalculateChecksumNB(curNb
, csumLength
,
709 (UINT32
)(layers
->l4Offset
));
710 } else if (layers
->isUdp
&& csumInfo
->Transmit
.UdpChecksum
) {
711 UINT16 csumLength
= (UINT16
)(packetLength
- layers
->l4Offset
);
712 UDPHdr
*udp
= (UDPHdr
*)((PCHAR
)ip
+ sizeof *ip
);
713 udp
->check
= IPv6PseudoChecksum((UINT32
*) &ip
->saddr
,
714 (UINT32
*) &ip
->daddr
,
715 IPPROTO_UDP
, csumLength
);
716 udp
->check
= CalculateChecksumNB(curNb
, csumLength
,
717 (UINT32
)(layers
->l4Offset
));
721 return NDIS_STATUS_SUCCESS
;
727 * This function returns the maximum segment size of the given NBL. It takes
728 * into consideration both LSO v1 and v2.
731 OVSGetTcpMSS(PNET_BUFFER_LIST nbl
)
733 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO lsoInfo
;
736 lsoInfo
.Value
= NET_BUFFER_LIST_INFO(nbl
,
737 TcpLargeSendNetBufferListInfo
);
738 switch (lsoInfo
.Transmit
.Type
) {
739 case NDIS_TCP_LARGE_SEND_OFFLOAD_V1_TYPE
:
740 return lsoInfo
.LsoV1Transmit
.MSS
;
742 case NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE
:
743 return lsoInfo
.LsoV2Transmit
.MSS
;
746 OVS_LOG_ERROR("Unknown LSO transmit type:%d",
747 lsoInfo
.Transmit
.Type
);