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, TcpIpChecksumNetBufferListInfo);
584 /* Zero is valid checksum. */
585 csumInfo.Receive.UdpChecksumFailed = 0;
586 NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value;
587 return NDIS_STATUS_SUCCESS;
590 /* First check if NIC has indicated UDP checksum failure. */
591 if (csumInfo.Receive.UdpChecksumFailed) {
592 return NDIS_STATUS_INVALID_PACKET;
595 return NDIS_STATUS_SUCCESS;
600 * OvsApplySWChecksumOnNB --
602 * This function calculates and sets the required sofware offloads given by
603 * csumInfo for a given NBL(nbl) with a single NB.
607 OvsApplySWChecksumOnNB(POVS_PACKET_HDR_INFO layers,
608 PNET_BUFFER_LIST nbl,
609 PNDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo)
614 UINT32 packetLength = 0;
617 curNb = NET_BUFFER_LIST_FIRST_NB(nbl);
618 ASSERT(curNb->Next == NULL);
619 packetLength = NET_BUFFER_DATA_LENGTH(curNb);
620 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
621 bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl,
624 return NDIS_STATUS_RESOURCES;
627 bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
629 if (layers->isIPv4) {
630 IPHdr *ip = (IPHdr *)(bufferStart + layers->l3Offset);
632 if (csumInfo->Transmit.IpHeaderChecksum) {
634 ip->check = IPChecksum((UINT8 *)ip, 4 * ip->ihl, 0);
637 if (layers->isTcp && csumInfo->Transmit.TcpChecksum) {
638 UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset);
639 TCPHdr *tcp = (TCPHdr *)(bufferStart + layers->l4Offset);
640 tcp->check = IPPseudoChecksum(&ip->saddr, &ip->daddr,
641 IPPROTO_TCP, csumLength);
642 tcp->check = CalculateChecksumNB(curNb, csumLength,
643 (UINT32)(layers->l4Offset));
644 } else if (layers->isUdp && csumInfo->Transmit.UdpChecksum) {
645 UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset);
646 UDPHdr *udp = (UDPHdr *)((PCHAR)ip + sizeof *ip);
647 udp->check = IPPseudoChecksum(&ip->saddr, &ip->daddr,
648 IPPROTO_UDP, csumLength);
649 udp->check = CalculateChecksumNB(curNb, csumLength,
650 (UINT32)(layers->l4Offset));
652 } else if (layers->isIPv6) {
653 IPv6Hdr *ip = (IPv6Hdr *)(bufferStart + layers->l3Offset);
655 if (layers->isTcp && csumInfo->Transmit.TcpChecksum) {
656 UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset);
657 TCPHdr *tcp = (TCPHdr *)(bufferStart + layers->l4Offset);
658 tcp->check = IPv6PseudoChecksum((UINT32 *) &ip->saddr,
659 (UINT32 *) &ip->daddr,
660 IPPROTO_TCP, csumLength);
661 tcp->check = CalculateChecksumNB(curNb, csumLength,
662 (UINT32)(layers->l4Offset));
663 } else if (layers->isUdp && csumInfo->Transmit.UdpChecksum) {
664 UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset);
665 UDPHdr *udp = (UDPHdr *)((PCHAR)ip + sizeof *ip);
666 udp->check = IPv6PseudoChecksum((UINT32 *) &ip->saddr,
667 (UINT32 *) &ip->daddr,
668 IPPROTO_UDP, csumLength);
669 udp->check = CalculateChecksumNB(curNb, csumLength,
670 (UINT32)(layers->l4Offset));
674 return NDIS_STATUS_SUCCESS;
680 * This function returns the maximum segment size of the given NBL. It takes
681 * into consideration both LSO v1 and v2.
684 OVSGetTcpMSS(PNET_BUFFER_LIST nbl)
686 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO lsoInfo;
689 lsoInfo.Value = NET_BUFFER_LIST_INFO(nbl,
690 TcpLargeSendNetBufferListInfo);
691 switch (lsoInfo.Transmit.Type) {
692 case NDIS_TCP_LARGE_SEND_OFFLOAD_V1_TYPE:
693 return lsoInfo.LsoV1Transmit.MSS;
695 case NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE:
696 return lsoInfo.LsoV2Transmit.MSS;
699 OVS_LOG_ERROR("Unknown LSO transmit type:%d",
700 lsoInfo.Transmit.Type);