/* * -------------------------------------------------------------------------- * This function allocated the switch context, and initializes its necessary * members. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, POVS_SWITCH_CONTEXT *switchContextOut) { NDIS_STATUS status; POVS_SWITCH_CONTEXT switchContext; NDIS_SWITCH_CONTEXT hostSwitchContext; NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler; OVS_LOG_TRACE("Enter: Create switch object"); switchContext = (POVS_SWITCH_CONTEXT) OvsAllocateMemoryWithTag( sizeof(OVS_SWITCH_CONTEXT), OVS_SWITCH_POOL_TAG); if (switchContext == NULL) { status = NDIS_STATUS_RESOURCES; goto create_switch_done; } RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT)); /* Initialize the switch. */ hostSwitchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS; hostSwitchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1; hostSwitchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1; status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle, &hostSwitchContext, &hostSwitchHandler); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_ERROR("OvsExtAttach: Extension is running in " "non-switch environment."); OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG); goto create_switch_done; } switchContext->NdisFilterHandle = ndisFilterHandle; switchContext->NdisSwitchContext = hostSwitchContext; RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler, sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS)); status = OvsInitSwitchContext(switchContext); if (status != NDIS_STATUS_SUCCESS) { OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG); switchContext = NULL; goto create_switch_done; } status = OvsInitTunnelFilter(gOvsExtDriverObject, gOvsDeviceObject); if (status != NDIS_STATUS_SUCCESS) { OvsUninitSwitchContext(switchContext); goto create_switch_done; } *switchContextOut = switchContext; create_switch_done: OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx", switchContext, status); return status; }
/* *---------------------------------------------------------------------------- * OvsCtHandleFtp * Extract the FTP control data from the packet and created a related * entry if it's a valid connection. This method doesn't support extended * FTP yet. Supports PORT and PASV commands. * Eg: * 'PORT 192,168,137,103,192,22\r\n' -> '192.168.137.103' and 49174 * '227 Entering Passive Mode (192,168,137,104,194,14)\r\n' gets extracted * to '192.168.137.104' and 49678 *---------------------------------------------------------------------------- */ NDIS_STATUS OvsCtHandleFtp(PNET_BUFFER_LIST curNbl, OvsFlowKey *key, OVS_PACKET_HDR_INFO *layers, UINT64 currentTime, POVS_CT_ENTRY entry, BOOLEAN request) { NDIS_STATUS status; FTP_TYPE ftpType = 0; const char *buf; char temp[256] = { 0 }; char ftpMsg[256] = { 0 }; TCPHdr tcpStorage; const TCPHdr *tcp; tcp = OvsGetTcp(curNbl, layers->l4Offset, &tcpStorage); if (!tcp) { return NDIS_STATUS_INVALID_PACKET; } UINT32 len = OvsGetTcpPayloadLength(curNbl); if (len > sizeof(temp)) { /* We only care up to 256 */ len = sizeof(temp); } buf = OvsGetPacketBytes(curNbl, len, layers->l4Offset + TCP_HDR_LEN(tcp), temp); if (buf == NULL) { return NDIS_STATUS_INVALID_PACKET; } OvsStrlcpy((char *)ftpMsg, (char *)buf, min(len, sizeof(ftpMsg))); char *req = NULL; if (request) { if ((len >= 5) && (OvsStrncmp("PORT", ftpMsg, 4) == 0)) { ftpType = FTP_TYPE_ACTIVE; req = ftpMsg + 4; } } else { if ((len >= 4) && (OvsStrncmp(FTP_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) { ftpType = FTP_TYPE_PASV; /* There are various formats for PASV command. We try to support * some of them. This has been addressed by RFC 2428 - EPSV. * Eg: * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2 * 227 Entering Passive Mode. h1,h2,h3,h4,p1,p2 * 227 =h1,h2,h3,h4,p1,p2 */ char *paren; paren = strchr(ftpMsg, '('); if (paren) { req = paren + 1; } else { /* PASV command without ( */ req = ftpMsg + 3; } } } if (req == NULL) { /* Not a PORT/PASV control packet */ return NDIS_STATUS_SUCCESS; } UINT32 arr[6] = {0}; status = OvsCtExtractNumbers(req, len, arr, 6, ','); if (status != NDIS_STATUS_SUCCESS) { return status; } UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]); UINT16 port = ntohs(((arr[4] << 8) | arr[5])); switch (ftpType) { case FTP_TYPE_PASV: /* Ensure that the command states Server's IP address */ ASSERT(ip == key->ipKey.nwSrc); OvsCtRelatedEntryCreate(key->ipKey.nwProto, key->l2.dlType, /* Server's IP */ ip, /* Use intended client's IP */ key->ipKey.nwDst, /* Dynamic port opened on server */ port, /* We don't know the client port */ 0, currentTime, entry); break; case FTP_TYPE_ACTIVE: OvsCtRelatedEntryCreate(key->ipKey.nwProto, key->l2.dlType, /* Server's default IP address */ key->ipKey.nwDst, /* Client's IP address */ ip, /* FTP Data Port is 20 */ ntohs(IPPORT_FTP_DATA), /* Port opened up on Client */ port, currentTime, entry); break; default: OVS_LOG_ERROR("invalid ftp type:%d", ftpType); status = NDIS_STATUS_INVALID_PARAMETER; break; } return status; }
/* * -------------------------------------------------------------------------- * Implements filter driver's FilterAttach function. * * This function allocates the switch context, and initializes its necessary * members. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsExtAttach(NDIS_HANDLE ndisFilterHandle, NDIS_HANDLE filterDriverContext, PNDIS_FILTER_ATTACH_PARAMETERS attachParameters) { NDIS_STATUS status = NDIS_STATUS_FAILURE; NDIS_FILTER_ATTRIBUTES ovsExtAttributes; POVS_SWITCH_CONTEXT switchContext = NULL; UNREFERENCED_PARAMETER(filterDriverContext); OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle); ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject); if (attachParameters->MiniportMediaType != NdisMedium802_3) { status = NDIS_STATUS_INVALID_PARAMETER; goto cleanup; } if (gOvsExtDriverHandle == NULL) { OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded."); ASSERT(FALSE); goto cleanup; } NdisAcquireSpinLock(gOvsCtrlLock); if (gOvsSwitchContext) { NdisReleaseSpinLock(gOvsCtrlLock); OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is" "supported, %p.", gOvsSwitchContext); goto cleanup; } if (gOvsInAttach) { NdisReleaseSpinLock(gOvsCtrlLock); /* Just fail the request. */ OVS_LOG_TRACE("Exit: Failed to create OVS Switch, since another attach" "instance is in attach process."); goto cleanup; } gOvsInAttach = TRUE; NdisReleaseSpinLock(gOvsCtrlLock); status = OvsInitIpHelper(ndisFilterHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Exit: Failed to initialize IP helper."); goto cleanup; } status = OvsCreateSwitch(ndisFilterHandle, &switchContext); if (status != NDIS_STATUS_SUCCESS) { OvsCleanupIpHelper(); goto cleanup; } ASSERT(switchContext); /* * Register the switch context with NDIS so NDIS can pass it back to the * Filterxxx callback functions as the 'FilterModuleContext' parameter. */ RtlZeroMemory(&ovsExtAttributes, sizeof(NDIS_FILTER_ATTRIBUTES)); ovsExtAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1; ovsExtAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES); ovsExtAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES; ovsExtAttributes.Flags = 0; NDIS_DECLARE_FILTER_MODULE_CONTEXT(OVS_SWITCH_CONTEXT); status = NdisFSetAttributes(ndisFilterHandle, switchContext, &ovsExtAttributes); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_ERROR("Failed to set attributes."); OvsCleanupIpHelper(); goto cleanup; } /* Setup the state machine. */ switchContext->controlFlowState = OvsSwitchAttached; switchContext->dataFlowState = OvsSwitchPaused; gOvsSwitchContext = switchContext; KeMemoryBarrier(); cleanup: gOvsInAttach = FALSE; if (status != NDIS_STATUS_SUCCESS) { if (switchContext != NULL) { OvsDeleteSwitch(switchContext); } } OVS_LOG_TRACE("Exit: status %x", status); return status; }
/* * -------------------------------------------------------------------------- * OvsFullCopyNBL -- * * Copy the NBL to a new NBL including data. * * Notes: * The NBL can have multiple NBs, but the final result is one NBL. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsFullCopyNBL(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 headRoom, BOOLEAN copyNblInfo) { POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; POVS_NBL_POOL ovsPool = &context->ovsPool; PNET_BUFFER_LIST newNbl; PNET_BUFFER nb, newNb, firstNb = NULL, prevNb = NULL; POVS_BUFFER_CONTEXT dstCtx, srcCtx; PMDL mdl; NDIS_STATUS status; UINT32 size, totalSize; ULONG copiedSize; UINT16 flags; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO dstInfo; srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) { OVS_LOG_INFO("src nbl must have ctx initialized"); ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC); return NULL; } nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (NET_BUFFER_NEXT_NB(nb) == NULL) { return OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); } newNbl = NdisAllocateNetBufferList(ovsPool->nblOnlyPool, (UINT16)sizeof (OVS_BUFFER_CONTEXT), (UINT16)OVS_DEFAULT_NBL_CONTEXT_FILL); if (newNbl == NULL) { return NULL; } while (nb) { size = NET_BUFFER_DATA_LENGTH(nb); totalSize = MEM_ALIGN_SIZE(size + headRoom); mdl = OvsAllocateMDLAndData(ovsPool->ndisHandle, totalSize); if (mdl == NULL) { goto nblcopy_error; } newNb = NdisAllocateNetBuffer(ovsPool->nbPool, mdl, totalSize, 0); if (newNb == NULL) { OvsFreeMDLAndData(mdl); goto nblcopy_error; } if (firstNb == NULL) { firstNb = newNb; } else { NET_BUFFER_NEXT_NB(prevNb) = newNb; } prevNb = newNb; #ifdef DBG InterlockedIncrement((LONG volatile *)&ovsPool->nbCount); #endif status = NdisRetreatNetBufferDataStart(newNb, size, 0, NULL); ASSERT(status == NDIS_STATUS_SUCCESS); status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, size, nb, 0, &copiedSize); if (status != NDIS_STATUS_SUCCESS || size != copiedSize) { goto nblcopy_error; } nb = NET_BUFFER_NEXT_NB(nb); } NET_BUFFER_LIST_FIRST_NB(newNbl) = firstNb; newNbl->SourceHandle = ovsPool->ndisHandle; status = context->NdisSwitchHandlers. AllocateNetBufferListForwardingContext(ovsPool->ndisContext, newNbl); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, 0, copyNblInfo); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } dstInfo = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl); dstInfo->IsPacketDataSafe = TRUE; dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl); flags = srcCtx->flags & (OVS_BUFFER_RECV_BUFFER | OVS_BUFFER_SEND_BUFFER); flags |= OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA | OVS_BUFFER_PRIVATE_NET_BUFFER | OVS_BUFFER_FROM_NBL_ONLY_POOL | OVS_BUFFER_PRIVATE_FORWARD_CONTEXT; OvsInitNBLContext(dstCtx, flags, NET_BUFFER_DATA_LENGTH(firstNb), OVS_DEFAULT_PORT_NO); #ifdef DBG OvsDumpNetBufferList(nbl); OvsDumpForwardingDetails(nbl); InterlockedIncrement((LONG volatile *)&ovsPool->nblOnlyCount); #endif OVS_LOG_LOUD("newNbl: %p", newNbl); return newNbl; nblcopy_error: while (firstNb) { #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->nbCount); #endif prevNb = firstNb; firstNb = NET_BUFFER_NEXT_NB(prevNb); mdl = NET_BUFFER_FIRST_MDL(prevNb); NET_BUFFER_FIRST_MDL(prevNb) = NULL; NdisFreeNetBuffer(prevNb); OvsFreeMDLAndData(mdl); } NdisFreeNetBufferList(newNbl); OVS_LOG_ERROR("OvsFullCopyNBL failed"); return NULL; }
/* *---------------------------------------------------------------------------- * OvsDoEncapVxlan * Encapsulates the packet. *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_FWD_INFO fwdInfo, POVS_PACKET_HDR_INFO layers, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *newNbl) { NDIS_STATUS status; PNET_BUFFER curNb; PMDL curMdl; PUINT8 bufferStart; EthHdr *ethHdr; IPHdr *ipHdr; UDPHdr *udpHdr; VXLANHdr *vxlanHdr; UINT32 headRoom = OvsGetVxlanTunHdrSize(); UINT32 packetLength; /* * XXX: the assumption currently is that the NBL is owned by OVS, and * headroom has already been allocated as part of allocating the NBL and * MDL. */ curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); packetLength = NET_BUFFER_DATA_LENGTH(curNb); if (layers->isTcp) { NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo; tsoInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpLargeSendNetBufferListInfo); OVS_LOG_TRACE("MSS %u packet len %u", tsoInfo.LsoV1Transmit.MSS, packetLength); if (tsoInfo.LsoV1Transmit.MSS) { OVS_LOG_TRACE("l4Offset %d", layers->l4Offset); *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers, tsoInfo.LsoV1Transmit.MSS, headRoom); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to segment NBL"); return NDIS_STATUS_FAILURE; } } } /* If we didn't split the packet above, make a copy now */ if (*newNbl == NULL) { *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom, FALSE /*NBL info*/); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to copy NBL"); return NDIS_STATUS_FAILURE; } } curNbl = *newNbl; for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL; curNb = curNb->Next) { status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL); if (status != NDIS_STATUS_SUCCESS) { goto ret_error; } curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto ret_error; } bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (NET_BUFFER_NEXT_NB(curNb)) { OVS_LOG_TRACE("nb length %u next %u", NET_BUFFER_DATA_LENGTH(curNb), NET_BUFFER_DATA_LENGTH(curNb->Next)); } /* L2 header */ ethHdr = (EthHdr *)bufferStart; NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr, sizeof ethHdr->Destination + sizeof ethHdr->Source); ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) == (PCHAR)&fwdInfo->srcMacAddr); ethHdr->Type = htons(ETH_TYPE_IPV4); // XXX: question: there are fields in the OvsIPv4TunnelKey for ttl and such, // should we use those values instead? or will they end up being // uninitialized; /* IP header */ ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr); ipHdr->ihl = sizeof *ipHdr / 4; ipHdr->version = IPV4; ipHdr->tos = 0; ipHdr->tot_len = htons(NET_BUFFER_DATA_LENGTH(curNb) - sizeof *ethHdr); ipHdr->id = 0; ipHdr->frag_off = IP_DF_NBO; ipHdr->ttl = tunKey->ttl ? tunKey->ttl : VXLAN_DEFAULT_TTL; ipHdr->protocol = IPPROTO_UDP; ASSERT(tunKey->dst == fwdInfo->dstIpAddr); ASSERT(tunKey->src == fwdInfo->srcIpAddr || tunKey->src == 0); ipHdr->saddr = fwdInfo->srcIpAddr; ipHdr->daddr = fwdInfo->dstIpAddr; ipHdr->check = 0; ipHdr->check = IPChecksum((UINT8 *)ipHdr, sizeof *ipHdr, 0); /* UDP header */ udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr); udpHdr->source = htons(tunKey->flow_hash | 32768); udpHdr->dest = VXLAN_UDP_PORT_NBO; udpHdr->len = htons(NET_BUFFER_DATA_LENGTH(curNb) - headRoom + sizeof *udpHdr + sizeof *vxlanHdr); udpHdr->check = 0; /* VXLAN header */ vxlanHdr = (VXLANHdr *)((PCHAR)udpHdr + sizeof *udpHdr); vxlanHdr->flags1 = 0; vxlanHdr->locallyReplicate = 0; vxlanHdr->flags2 = 0; vxlanHdr->reserved1 = 0; if (tunKey->flags | OVS_TNL_F_KEY) { vxlanHdr->vxlanID = VXLAN_TUNNELID_TO_VNI(tunKey->tunnelId); vxlanHdr->instanceID = 1; } vxlanHdr->reserved2 = 0; } return STATUS_SUCCESS; ret_error: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; }