diff options
Diffstat (limited to 'datapath-windows/ovsext/User.c')
-rw-r--r-- | datapath-windows/ovsext/User.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/datapath-windows/ovsext/User.c b/datapath-windows/ovsext/User.c new file mode 100644 index 000000000..612a4bd57 --- /dev/null +++ b/datapath-windows/ovsext/User.c @@ -0,0 +1,867 @@ +/* + * Copyright (c) 2014 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * OvsUser.c + * Manage packet queue for packet miss for userAction. + */ + + +#include "precomp.h" + +#include "Datapath.h" +#include "Switch.h" +#include "Vport.h" +#include "Event.h" +#include "User.h" +#include "PacketIO.h" +#include "Checksum.h" +#include "NetProto.h" +#include "Flow.h" +#include "TunnelIntf.h" + +#ifdef OVS_DBG_MOD +#undef OVS_DBG_MOD +#endif +#define OVS_DBG_MOD OVS_DBG_USER +#include "Debug.h" + +OVS_USER_PACKET_QUEUE ovsPacketQueues[OVS_MAX_NUM_PACKET_QUEUES]; + +POVS_PACKET_QUEUE_ELEM OvsGetNextPacket(POVS_OPEN_INSTANCE instance); +extern PNDIS_SPIN_LOCK gOvsCtrlLock; +extern POVS_SWITCH_CONTEXT gOvsSwitchContext; +OVS_USER_STATS ovsUserStats; + + +NTSTATUS +OvsUserInit() +{ + UINT32 i; + POVS_USER_PACKET_QUEUE queue; + for (i = 0; i < OVS_MAX_NUM_PACKET_QUEUES; i++) { + queue = &ovsPacketQueues[i]; + RtlZeroMemory(queue, sizeof (*queue)); + InitializeListHead(&queue->packetList); + NdisAllocateSpinLock(&queue->queueLock); + } + return STATUS_SUCCESS; +} + +VOID +OvsUserCleanup() +{ + UINT32 i; + POVS_USER_PACKET_QUEUE queue; + for (i = 0; i < OVS_MAX_NUM_PACKET_QUEUES; i++) { + queue = &ovsPacketQueues[i]; + ASSERT(IsListEmpty(&queue->packetList)); + ASSERT(queue->instance == NULL); + ASSERT(queue->pendingIrp == NULL); + NdisFreeSpinLock(&queue->queueLock); + } +} + +static VOID +OvsPurgePacketQueue(POVS_USER_PACKET_QUEUE queue, + POVS_OPEN_INSTANCE instance) +{ + PLIST_ENTRY link, next; + LIST_ENTRY tmp; + POVS_PACKET_QUEUE_ELEM elem; + + InitializeListHead(&tmp); + NdisAcquireSpinLock(&queue->queueLock); + if (queue->instance != instance) { + NdisReleaseSpinLock(&queue->queueLock); + return; + } + + if (queue->numPackets) { + OvsAppendList(&tmp, &queue->packetList); + queue->numPackets = 0; + } + NdisReleaseSpinLock(&queue->queueLock); + LIST_FORALL_SAFE(&tmp, link, next) { + RemoveEntryList(link); + elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); + OvsFreeMemory(elem); + } +} + + +VOID +OvsCleanupPacketQueue(POVS_OPEN_INSTANCE instance) +{ + POVS_USER_PACKET_QUEUE queue; + POVS_PACKET_QUEUE_ELEM elem; + PLIST_ENTRY link, next; + LIST_ENTRY tmp; + PIRP irp = NULL; + + InitializeListHead(&tmp); + queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; + if (queue) { + PDRIVER_CANCEL cancelRoutine; + NdisAcquireSpinLock(&queue->queueLock); + if (queue->instance != instance) { + NdisReleaseSpinLock(&queue->queueLock); + return; + } + + if (queue->numPackets) { + OvsAppendList(&tmp, &queue->packetList); + queue->numPackets = 0; + } + queue->instance = NULL; + queue->queueId = OVS_MAX_NUM_PACKET_QUEUES; + instance->packetQueue = NULL; + irp = queue->pendingIrp; + queue->pendingIrp = NULL; + if (irp) { + cancelRoutine = IoSetCancelRoutine(irp, NULL); + if (cancelRoutine == NULL) { + irp = NULL; + } + } + NdisReleaseSpinLock(&queue->queueLock); + } + LIST_FORALL_SAFE(&tmp, link, next) { + RemoveEntryList(link); + elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); + OvsFreeMemory(elem); + } + if (irp) { + OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); + } +} + +NTSTATUS +OvsSubscribeDpIoctl(PFILE_OBJECT fileObject, + PVOID inputBuffer, + UINT32 inputLength) +{ + POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; + UINT32 queueId; + POVS_USER_PACKET_QUEUE queue; + if (inputLength < sizeof (UINT32)) { + return STATUS_INVALID_PARAMETER; + } + queueId = *(UINT32 *)inputBuffer; + if (instance->packetQueue && queueId >= OVS_MAX_NUM_PACKET_QUEUES) { + /* + * unsubscribe + */ + OvsCleanupPacketQueue(instance); + } else if (instance->packetQueue == NULL && + queueId < OVS_MAX_NUM_PACKET_QUEUES) { + queue = &ovsPacketQueues[queueId]; + NdisAcquireSpinLock(&queue->queueLock); + if (ovsPacketQueues[queueId].instance) { + if (ovsPacketQueues[queueId].instance != instance) { + NdisReleaseSpinLock(&queue->queueLock); + return STATUS_INSUFFICIENT_RESOURCES; + } else { + NdisReleaseSpinLock(&queue->queueLock); + return STATUS_SUCCESS; + } + } + queue->queueId = queueId; + queue->instance = instance; + instance->packetQueue = queue; + ASSERT(IsListEmpty(&queue->packetList)); + NdisReleaseSpinLock(&queue->queueLock); + } else { + return STATUS_INVALID_PARAMETER; + } + return STATUS_SUCCESS; +} + + +NTSTATUS +OvsReadDpIoctl(PFILE_OBJECT fileObject, + PVOID outputBuffer, + UINT32 outputLength, + UINT32 *replyLen) +{ + POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; + POVS_PACKET_QUEUE_ELEM elem; + UINT32 len; + +#define TCP_CSUM_OFFSET 16 +#define UDP_CSUM_OFFSET 6 + ASSERT(instance); + + if (instance->packetQueue == NULL) { + return STATUS_INVALID_PARAMETER; + } + if (outputLength < (sizeof (OVS_PACKET_INFO) + OVS_MIN_PACKET_SIZE)) { + return STATUS_BUFFER_TOO_SMALL; + } + + elem = OvsGetNextPacket(instance); + if (elem) { + /* + * XXX revisit this later + */ + len = elem->packet.totalLen > outputLength ? outputLength : + elem->packet.totalLen; + + if ((elem->hdrInfo.tcpCsumNeeded || elem->hdrInfo.udpCsumNeeded) && + len == elem->packet.totalLen) { + UINT16 sum, *ptr; + UINT16 size = (UINT16)(elem->packet.userDataLen + + elem->hdrInfo.l4Offset + + (UINT16)sizeof (OVS_PACKET_INFO)); + RtlCopyMemory(outputBuffer, &elem->packet, size); + ASSERT(len - size >= elem->hdrInfo.l4PayLoad); + sum = CopyAndCalculateChecksum((UINT8 *)outputBuffer + size, + (UINT8 *)&elem->packet + size, + elem->hdrInfo.l4PayLoad, 0); + ptr =(UINT16 *)((UINT8 *)outputBuffer + size + + (elem->hdrInfo.tcpCsumNeeded ? + TCP_CSUM_OFFSET : UDP_CSUM_OFFSET)); + *ptr = sum; + ovsUserStats.l4Csum++; + } else { + RtlCopyMemory(outputBuffer, &elem->packet, len); + } + + *replyLen = len; + OvsFreeMemory(elem); + } + return STATUS_SUCCESS; +} + +/* Helper function to allocate a Forwarding Context for an NBL */ +NTSTATUS +OvsAllocateForwardingContextForNBL(POVS_SWITCH_CONTEXT switchContext, + PNET_BUFFER_LIST nbl) +{ + return switchContext->NdisSwitchHandlers. + AllocateNetBufferListForwardingContext( + switchContext->NdisSwitchContext, nbl); +} + +/* + * -------------------------------------------------------------------------- + * This function allocates all the stuff necessary for creating an NBL from the + * input buffer of specified length, namely, a nonpaged data buffer of size + * length, an MDL from it, and a NB and NBL from it. It does not allocate an NBL + * context yet. It also copies data from the specified buffer to the NBL. + * -------------------------------------------------------------------------- + */ +PNET_BUFFER_LIST +OvsAllocateNBLForUserBuffer(POVS_SWITCH_CONTEXT switchContext, + PVOID userBuffer, + ULONG length) +{ + UINT8 *data = NULL; + PNET_BUFFER_LIST nbl = NULL; + PNET_BUFFER nb; + PMDL mdl; + + if (length > OVS_DEFAULT_DATA_SIZE) { + nbl = OvsAllocateVariableSizeNBL(switchContext, length, + OVS_DEFAULT_HEADROOM_SIZE); + + } else { + nbl = OvsAllocateFixSizeNBL(switchContext, length, + OVS_DEFAULT_HEADROOM_SIZE); + } + if (nbl == NULL) { + return NULL; + } + + nb = NET_BUFFER_LIST_FIRST_NB(nbl); + mdl = NET_BUFFER_CURRENT_MDL(nb); + data = (PUINT8)MmGetSystemAddressForMdlSafe(mdl, LowPagePriority) + + NET_BUFFER_CURRENT_MDL_OFFSET(nb); + if (!data) { + OvsCompleteNBL(switchContext, nbl, TRUE); + return NULL; + } + + NdisMoveMemory(data, userBuffer, length); + + return nbl; +} + +NTSTATUS +OvsExecuteDpIoctl(PVOID inputBuffer, + UINT32 inputLength, + UINT32 outputLength) +{ + NTSTATUS status = STATUS_SUCCESS; + NTSTATUS ndisStatus; + OvsPacketExecute *execute; + LOCK_STATE_EX lockState; + PNET_BUFFER_LIST pNbl; + PNL_ATTR actions; + PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; + OvsFlowKey key; + OVS_PACKET_HDR_INFO layers; + POVS_VPORT_ENTRY vport; + + if (inputLength < sizeof(*execute) || outputLength != 0) { + return STATUS_INFO_LENGTH_MISMATCH; + } + + NdisAcquireSpinLock(gOvsCtrlLock); + if (gOvsSwitchContext == NULL) { + status = STATUS_INVALID_PARAMETER; + goto unlock; + } + + execute = (struct OvsPacketExecute *) inputBuffer; + + if (execute->packetLen == 0) { + status = STATUS_INVALID_PARAMETER; + goto unlock; + } + + if (inputLength != sizeof (*execute) + + execute->actionsLen + execute->packetLen) { + status = STATUS_INFO_LENGTH_MISMATCH; + goto unlock; + } + actions = (PNL_ATTR)((PCHAR)&execute->actions + execute->packetLen); + + /* + * Allocate the NBL, copy the data from the userspace buffer. Allocate + * also, the forwarding context for the packet. + */ + pNbl = OvsAllocateNBLForUserBuffer(gOvsSwitchContext, &execute->packetBuf, + execute->packetLen); + if (pNbl == NULL) { + status = STATUS_NO_MEMORY; + goto unlock; + } + + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl); + vport = OvsFindVportByPortNo(gOvsSwitchContext, execute->inPort); + if (vport) { + fwdDetail->SourcePortId = vport->portId; + fwdDetail->SourceNicIndex = vport->nicIndex; + } else { + fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID; + fwdDetail->SourceNicIndex = 0; + } + // XXX: Figure out if any of the other members of fwdDetail need to be set. + + ndisStatus = OvsExtractFlow(pNbl, fwdDetail->SourcePortId, &key, &layers, + NULL); + if (ndisStatus == NDIS_STATUS_SUCCESS) { + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, + NDIS_RWL_AT_DISPATCH_LEVEL); + ndisStatus = OvsActionsExecute(gOvsSwitchContext, NULL, pNbl, + vport ? vport->portNo : + OVS_DEFAULT_PORT_NO, + NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP, + &key, NULL, &layers, actions, + execute->actionsLen); + pNbl = NULL; + NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); + } + if (ndisStatus != NDIS_STATUS_SUCCESS) { + status = STATUS_UNSUCCESSFUL; + } + + if (pNbl) { + OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE); + } +unlock: + NdisReleaseSpinLock(gOvsCtrlLock); + return status; +} + + +NTSTATUS +OvsPurgeDpIoctl(PFILE_OBJECT fileObject) +{ + POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; + POVS_USER_PACKET_QUEUE queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; + + if (queue == NULL) { + return STATUS_INVALID_PARAMETER; + } + OvsPurgePacketQueue(queue, instance); + return STATUS_SUCCESS; +} + +VOID +OvsCancelIrpDatapath(PDEVICE_OBJECT deviceObject, + PIRP irp) +{ + PIO_STACK_LOCATION irpSp; + PFILE_OBJECT fileObject; + POVS_OPEN_INSTANCE instance; + POVS_USER_PACKET_QUEUE queue = NULL; + + UNREFERENCED_PARAMETER(deviceObject); + + IoReleaseCancelSpinLock(irp->CancelIrql); + irpSp = IoGetCurrentIrpStackLocation(irp); + fileObject = irpSp->FileObject; + + if (fileObject == NULL) { + goto done; + } + NdisAcquireSpinLock(gOvsCtrlLock); + instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; + if (instance) { + queue = instance->packetQueue; + } + if (instance == NULL || queue == NULL) { + NdisReleaseSpinLock(gOvsCtrlLock); + goto done; + } + NdisReleaseSpinLock(gOvsCtrlLock); + NdisAcquireSpinLock(&queue->queueLock); + if (queue->pendingIrp == irp) { + queue->pendingIrp = NULL; + } + NdisReleaseSpinLock(&queue->queueLock); +done: + OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); +} + + +NTSTATUS +OvsWaitDpIoctl(PIRP irp, PFILE_OBJECT fileObject) +{ + POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; + POVS_USER_PACKET_QUEUE queue = + (POVS_USER_PACKET_QUEUE)instance->packetQueue; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN cancelled = FALSE; + + if (queue == NULL) { + return STATUS_INVALID_PARAMETER; + } + NdisAcquireSpinLock(&queue->queueLock); + if (queue->instance != instance) { + NdisReleaseSpinLock(&queue->queueLock); + return STATUS_INVALID_PARAMETER; + } + if (queue->pendingIrp) { + NdisReleaseSpinLock(&queue->queueLock); + return STATUS_DEVICE_BUSY; + } + if (queue->numPackets == 0) { + PDRIVER_CANCEL cancelRoutine; + IoMarkIrpPending(irp); + IoSetCancelRoutine(irp, OvsCancelIrpDatapath); + if (irp->Cancel) { + cancelRoutine = IoSetCancelRoutine(irp, NULL); + if (cancelRoutine) { + cancelled = TRUE; + } + } else { + queue->pendingIrp = irp; + } + status = STATUS_PENDING; + } + NdisReleaseSpinLock(&queue->queueLock); + if (cancelled) { + OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); + OVS_LOG_INFO("Datapath IRP cancelled: %p", irp); + } + return status; +} + + +POVS_PACKET_QUEUE_ELEM +OvsGetNextPacket(POVS_OPEN_INSTANCE instance) +{ + POVS_USER_PACKET_QUEUE queue; + PLIST_ENTRY link; + queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; + if (queue == NULL) { + return NULL; + } + NdisAcquireSpinLock(&queue->queueLock); + if (queue->instance != instance || queue->numPackets == 0) { + NdisReleaseSpinLock(&queue->queueLock); + return NULL; + } + link = RemoveHeadList(&queue->packetList); + queue->numPackets--; + NdisReleaseSpinLock(&queue->queueLock); + return CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); +} + + +POVS_USER_PACKET_QUEUE +OvsGetQueue(UINT32 queueId) +{ + POVS_USER_PACKET_QUEUE queue; + if (queueId >= OVS_MAX_NUM_PACKET_QUEUES) { + return NULL; + } + queue = &ovsPacketQueues[queueId]; + return queue->instance != NULL ? queue : NULL; +} + +/* + *---------------------------------------------------------------------------- + * OvsCreateQueuePacket -- + * + * Create a packet which will be forwarded to user space. + * + * InputParameter: + * queueId Identify the queue the packet to be inserted + * This will be used when multiple queues is supported + * in userspace + * userData: when cmd is user action, this field contain + * user action data. + * userDataLen: as name indicated + * cmd: either miss or user action + * inPort: datapath port id from which the packet is received. + * tunnelKey: tunnelKey for tunneled packet + * nbl: the NET_BUFFER_LIST which contain the packet + * nb: the packet + * isRecv: This is used to decide how to interprete the csum info + * hdrInfo: include hdr info initialized during flow extraction. + * + * Results: + * NULL if fail to create the packet + * The packet element otherwise + *---------------------------------------------------------------------------- + */ +POVS_PACKET_QUEUE_ELEM +OvsCreateQueuePacket(UINT32 queueId, + PVOID userData, + UINT32 userDataLen, + UINT32 cmd, + UINT32 inPort, + OvsIPv4TunnelKey *tunnelKey, + PNET_BUFFER_LIST nbl, + PNET_BUFFER nb, + BOOLEAN isRecv, + POVS_PACKET_HDR_INFO hdrInfo) +{ +#define VLAN_TAG_SIZE 4 + UINT32 allocLen, dataLen, extraLen = 0; + POVS_PACKET_QUEUE_ELEM elem; + PMDL mdl; + UINT8 *src, *dst; + NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; + NDIS_NET_BUFFER_LIST_8021Q_INFO vlanInfo; + + csumInfo.Value = NET_BUFFER_LIST_INFO(nbl, TcpIpChecksumNetBufferListInfo); + + if (isRecv && (csumInfo.Receive.TcpChecksumFailed || + (csumInfo.Receive.UdpChecksumFailed && + !hdrInfo->udpCsumZero) || + csumInfo.Receive.IpChecksumFailed)) { + OVS_LOG_INFO("Packet dropped due to checksum failure."); + ovsUserStats.dropDuetoChecksum++; + return NULL; + } + + vlanInfo.Value = NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo); + if (vlanInfo.TagHeader.VlanId) { + /* + * We may also need to check priority XXX + */ + extraLen = VLAN_TAG_SIZE; + } + + dataLen = NET_BUFFER_DATA_LENGTH(nb); + allocLen = sizeof (OVS_PACKET_QUEUE_ELEM) + userDataLen + dataLen + + extraLen; + + elem = (POVS_PACKET_QUEUE_ELEM)OvsAllocateMemory(allocLen); + if (elem == NULL) { + ovsUserStats.dropDuetoResource++; + return NULL; + } + elem->hdrInfo.value = hdrInfo->value; + elem->packet.totalLen = sizeof (OVS_PACKET_INFO) + userDataLen + dataLen + + extraLen; + elem->packet.queue = queueId; + elem->packet.userDataLen = userDataLen; + elem->packet.inPort = inPort; + elem->packet.cmd = cmd; + if (cmd == (UINT32)OVS_PACKET_CMD_MISS) { + ovsUserStats.miss++; + } else { + ovsUserStats.action++; + } + elem->packet.packetLen = dataLen + extraLen; + if (tunnelKey) { + RtlCopyMemory(&elem->packet.tunnelKey, tunnelKey, + sizeof (*tunnelKey)); + } else { + RtlZeroMemory(&elem->packet.tunnelKey, + sizeof (elem->packet.tunnelKey)); + } + + dst = elem->packet.data; + if (userDataLen) { + RtlCopyMemory(dst, userData, userDataLen); + dst = dst + userDataLen; + } + dst += extraLen; + + mdl = NET_BUFFER_CURRENT_MDL(nb); + src = NdisGetDataBuffer(nb, dataLen, dst, 1, 0); + if (src == NULL) { + OvsFreeMemory(elem); + ovsUserStats.dropDuetoResource++; + return NULL; + } else if (src != dst) { + /* Copy the data from the NDIS buffer to dst. */ + RtlCopyMemory(dst, src, dataLen); + } + + dst = elem->packet.data + userDataLen + extraLen; + /* + * Fix IP hdr if necessary + */ + if ((isRecv && csumInfo.Receive.IpChecksumValueInvalid) || + (!isRecv && csumInfo.Transmit.IsIPv4 && + csumInfo.Transmit.IpHeaderChecksum)) { + PIPV4_HEADER ipHdr = (PIPV4_HEADER)(dst + hdrInfo->l3Offset); + ASSERT(elem->hdrInfo.isIPv4); + ASSERT(ipHdr->Version == 4); + ipHdr->HeaderChecksum = IPChecksum((UINT8 *)ipHdr, + ipHdr->HeaderLength << 2, + (UINT16)~ipHdr->HeaderChecksum); + ovsUserStats.ipCsum++; + } + ASSERT(elem->hdrInfo.tcpCsumNeeded == 0 && + elem->hdrInfo.udpCsumNeeded == 0); + /* + * Fow now, we will not do verification + * There is no correctness issue here. + * XXX + */ + /* + * calculate TCP/UDP pseudo checksum + */ + if (isRecv && csumInfo.Receive.TcpChecksumValueInvalid) { + /* + * Only this case, we need to reclaculate pseudo checksum + * all other cases, it is assumed the pseudo checksum is + * filled already. + * + */ + PTCP_HDR tcpHdr = (PTCP_HDR)(dst + hdrInfo->l4Offset); + if (hdrInfo->isIPv4) { + PIPV4_HEADER ipHdr = (PIPV4_HEADER)(dst + hdrInfo->l3Offset); + elem->hdrInfo.l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) - + (ipHdr->HeaderLength << 2)); + tcpHdr->th_sum = + IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress, + (UINT32 *)&ipHdr->DestinationAddress, + IPPROTO_TCP, elem->hdrInfo.l4PayLoad); + } else { + PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(dst + hdrInfo->l3Offset); + elem->hdrInfo.l4PayLoad = + (UINT16)(ntohs(ipv6Hdr->PayloadLength) + + hdrInfo->l3Offset + sizeof(IPV6_HEADER) - + hdrInfo->l4Offset); + ASSERT(hdrInfo->isIPv6); + tcpHdr->th_sum = + IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress, + (UINT32 *)&ipv6Hdr->DestinationAddress, + IPPROTO_TCP, elem->hdrInfo.l4PayLoad); + } + elem->hdrInfo.tcpCsumNeeded = 1; + ovsUserStats.recalTcpCsum++; + } else if (!isRecv) { + if (csumInfo.Transmit.TcpChecksum) { + elem->hdrInfo.tcpCsumNeeded = 1; + } else if (csumInfo.Transmit.UdpChecksum) { + elem->hdrInfo.udpCsumNeeded = 1; + } + if (elem->hdrInfo.tcpCsumNeeded || elem->hdrInfo.udpCsumNeeded) { +#ifdef DBG + UINT16 sum, *ptr; + UINT8 proto = + elem->hdrInfo.tcpCsumNeeded ? IPPROTO_TCP : IPPROTO_UDP; +#endif + if (hdrInfo->isIPv4) { + PIPV4_HEADER ipHdr = (PIPV4_HEADER)(dst + hdrInfo->l3Offset); + elem->hdrInfo.l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) - + (ipHdr->HeaderLength << 2)); +#ifdef DBG + sum = IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress, + (UINT32 *)&ipHdr->DestinationAddress, + proto, elem->hdrInfo.l4PayLoad); +#endif + } else { + PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(dst + + hdrInfo->l3Offset); + elem->hdrInfo.l4PayLoad = + (UINT16)(ntohs(ipv6Hdr->PayloadLength) + + hdrInfo->l3Offset + sizeof(IPV6_HEADER) - + hdrInfo->l4Offset); + ASSERT(hdrInfo->isIPv6); +#ifdef DBG + sum = IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress, + (UINT32 *)&ipv6Hdr->DestinationAddress, + proto, elem->hdrInfo.l4PayLoad); +#endif + } +#ifdef DBG + ptr = (UINT16 *)(dst + hdrInfo->l4Offset + + (elem->hdrInfo.tcpCsumNeeded ? + TCP_CSUM_OFFSET : UDP_CSUM_OFFSET)); + ASSERT(*ptr == sum); +#endif + } + } + /* + * Finally insert VLAN tag + */ + if (extraLen) { + dst = elem->packet.data + userDataLen; + src = dst + extraLen; + ((UINT32 *)dst)[0] = ((UINT32 *)src)[0]; + ((UINT32 *)dst)[1] = ((UINT32 *)src)[1]; + ((UINT32 *)dst)[2] = ((UINT32 *)src)[2]; + dst += 12; + ((UINT16 *)dst)[0] = htons(0x8100); + ((UINT16 *)dst)[1] = htons(vlanInfo.TagHeader.VlanId | + (vlanInfo.TagHeader.UserPriority << 13)); + elem->hdrInfo.l3Offset += VLAN_TAG_SIZE; + elem->hdrInfo.l4Offset += VLAN_TAG_SIZE; + ovsUserStats.vlanInsert++; + } + + return elem; +} + + +VOID +OvsQueuePackets(UINT32 queueId, + PLIST_ENTRY packetList, + UINT32 numElems) +{ + POVS_USER_PACKET_QUEUE queue = OvsGetQueue(queueId); + POVS_PACKET_QUEUE_ELEM elem; + PIRP irp = NULL; + PLIST_ENTRY link; + UINT32 num = 0; + + OVS_LOG_LOUD("Enter: queueId %u, numELems: %u", + queueId, numElems); + if (queue == NULL) { + goto cleanup; + } + + NdisAcquireSpinLock(&queue->queueLock); + if (queue->instance == NULL) { + NdisReleaseSpinLock(&queue->queueLock); + goto cleanup; + } else { + OvsAppendList(&queue->packetList, packetList); + queue->numPackets += numElems; + } + if (queue->pendingIrp) { + PDRIVER_CANCEL cancelRoutine; + irp = queue->pendingIrp; + queue->pendingIrp = NULL; + cancelRoutine = IoSetCancelRoutine(irp, NULL); + if (cancelRoutine == NULL) { + irp = NULL; + } + } + NdisReleaseSpinLock(&queue->queueLock); + if (irp) { + OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); + } + +cleanup: + while (!IsListEmpty(packetList)) { + link = RemoveHeadList(packetList); + elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); + OvsFreeMemory(elem); + num++; + } + OVS_LOG_LOUD("Exit: drop %u packets", num); +} + + +/* + *---------------------------------------------------------------------------- + * OvsCreateAndAddPackets -- + * + * Create a packet and forwarded to user space. + * + * This function would fragment packet if needed, and queue + * each segment to user space. + *---------------------------------------------------------------------------- + */ +NTSTATUS +OvsCreateAndAddPackets(UINT32 queueId, + PVOID userData, + UINT32 userDataLen, + UINT32 cmd, + UINT32 inPort, + OvsIPv4TunnelKey *tunnelKey, + PNET_BUFFER_LIST nbl, + BOOLEAN isRecv, + POVS_PACKET_HDR_INFO hdrInfo, + POVS_SWITCH_CONTEXT switchContext, + LIST_ENTRY *list, + UINT32 *num) +{ + POVS_PACKET_QUEUE_ELEM elem; + PNET_BUFFER_LIST newNbl = NULL; + PNET_BUFFER nb; + + if (hdrInfo->isTcp) { + NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo; + UINT32 packetLength; + + tsoInfo.Value = NET_BUFFER_LIST_INFO(nbl, TcpLargeSendNetBufferListInfo); + nb = NET_BUFFER_LIST_FIRST_NB(nbl); + packetLength = NET_BUFFER_DATA_LENGTH(nb); + + OVS_LOG_TRACE("MSS %u packet len %u", + tsoInfo.LsoV1Transmit.MSS, packetLength); + if (tsoInfo.LsoV1Transmit.MSS) { + OVS_LOG_TRACE("l4Offset %d", hdrInfo->l4Offset); + newNbl = OvsTcpSegmentNBL(switchContext, nbl, hdrInfo, + tsoInfo.LsoV1Transmit.MSS , 0); + if (newNbl == NULL) { + return NDIS_STATUS_FAILURE; + } + nbl = newNbl; + } + } + + nb = NET_BUFFER_LIST_FIRST_NB(nbl); + while (nb) { + elem = OvsCreateQueuePacket(queueId, userData, userDataLen, + cmd, inPort, tunnelKey, nbl, nb, + isRecv, hdrInfo); + if (elem) { + InsertTailList(list, &elem->link); + (*num)++; + } + nb = NET_BUFFER_NEXT_NB(nb); + } + if (newNbl) { + OvsCompleteNBL(switchContext, newNbl, TRUE); + } + return NDIS_STATUS_SUCCESS; +} |