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.
19 * WFP Classified callback function and Action code for injecting a packet to the vswitch
25 #pragma warning(disable:4201) // unnamed struct/union
29 #pragma warning( push )
30 #pragma warning( disable:4127 )
44 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
47 OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl,
48 OVS_TUNNEL_PENDED_PACKET *packet);
50 VOID OvsAcquireDatapathRead(OVS_DATAPATH *datapath,
51 LOCK_STATE_EX *lockState,
53 VOID OvsAcquireDatapathWrite(OVS_DATAPATH *datapath,
54 LOCK_STATE_EX *lockState,
56 VOID OvsReleaseDatapath(OVS_DATAPATH *datapath,
57 LOCK_STATE_EX *lockState);
61 OvsTunnelNotify(FWPS_CALLOUT_NOTIFY_TYPE notifyType,
62 const GUID *filterKey,
63 const FWPS_FILTER *filter)
65 UNREFERENCED_PARAMETER(notifyType);
66 UNREFERENCED_PARAMETER(filterKey);
67 UNREFERENCED_PARAMETER(filter);
69 return STATUS_SUCCESS;
73 OvsTunnelAnalyzePacket(OVS_TUNNEL_PENDED_PACKET *packet)
75 NTSTATUS status = STATUS_SUCCESS;
76 UINT32 packetLength = 0;
77 ULONG bytesCopied = 0;
78 NET_BUFFER_LIST *copiedNBL = NULL;
79 NET_BUFFER *netBuffer;
80 NDIS_STATUS ndisStatus;
83 * For inbound net buffer list, we can assume it contains only one
84 * net buffer (unless it was an re-assembeled fragments). in both cases
85 * the first net buffer should include all headers, we assert if the retreat fails
87 netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList);
89 /* Drop the packet from the host stack */
90 packet->classifyOut->actionType = FWP_ACTION_BLOCK;
91 packet->classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
93 /* Adjust the net buffer list offset to the start of the IP header */
94 ndisStatus = NdisRetreatNetBufferDataStart(netBuffer,
95 packet->ipHeaderSize +
96 packet->transportHeaderSize,
98 ASSERT(ndisStatus == NDIS_STATUS_SUCCESS);
100 /* Single NBL element for WFP */
101 ASSERT(packet->netBufferList->Next == NULL);
103 /* Note that the copy will inherit the original net buffer list's offset */
104 packetLength = NET_BUFFER_DATA_LENGTH(netBuffer);
105 copiedNBL = OvsAllocateVariableSizeNBL(gOvsSwitchContext, packetLength,
106 OVS_DEFAULT_HEADROOM_SIZE);
108 if (copiedNBL == NULL) {
112 status = NdisCopyFromNetBufferToNetBuffer(NET_BUFFER_LIST_FIRST_NB(copiedNBL),
114 netBuffer, 0, &bytesCopied);
115 if (status != NDIS_STATUS_SUCCESS || packetLength != bytesCopied) {
119 status = OvsInjectPacketThroughActions(copiedNBL,
123 /* Undo the adjustment on the original net buffer list */
125 OvsCompleteNBL(gOvsSwitchContext, copiedNBL, TRUE);
127 NdisAdvanceNetBufferDataStart(netBuffer,
128 packet->transportHeaderSize + packet->ipHeaderSize,
136 * --------------------------------------------------------------------------
137 * This is the classifyFn function of the datagram-data callout. It
138 * allocates a packet structure to store the classify and meta data and
139 * it references the net buffer list for out-of-band modification and
140 * re-injection. The packet structure will be queued to the global packet
141 * queue. The worker thread will then be signaled, if idle, to process
143 * --------------------------------------------------------------------------
146 OvsTunnelClassify(const FWPS_INCOMING_VALUES *inFixedValues,
147 const FWPS_INCOMING_METADATA_VALUES *inMetaValues,
149 const VOID *classifyContext,
150 const FWPS_FILTER *filter,
152 FWPS_CLASSIFY_OUT *classifyOut)
154 OVS_TUNNEL_PENDED_PACKET packetStorage;
155 OVS_TUNNEL_PENDED_PACKET *packet = &packetStorage;
156 FWP_DIRECTION direction;
158 UNREFERENCED_PARAMETER(classifyContext);
159 UNREFERENCED_PARAMETER(filter);
160 UNREFERENCED_PARAMETER(flowContext);
162 ASSERT(layerData != NULL);
164 /* We don't have the necessary right to alter the packet flow */
165 if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) {
166 /* XXX TBD revisit protect against other filters owning this packet */
171 RtlZeroMemory(packet, sizeof(OVS_TUNNEL_PENDED_PACKET));
173 /* classifyOut cannot be accessed from a different thread context */
174 packet->classifyOut = classifyOut;
176 if (inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4) {
178 inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].\
182 ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6);
184 inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].\
188 packet->netBufferList = layerData;
190 ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues,
191 FWPS_METADATA_FIELD_COMPARTMENT_ID));
193 ASSERT(direction == FWP_DIRECTION_INBOUND);
195 ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
197 FWPS_METADATA_FIELD_IP_HEADER_SIZE));
198 ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
200 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE));
202 packet->ipHeaderSize = inMetaValues->ipHeaderSize;
203 packet->transportHeaderSize = inMetaValues->transportHeaderSize;
205 ASSERT(inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8 == IPPROTO_UDP );
206 OvsTunnelAnalyzePacket(packet);
214 OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl,
215 OVS_TUNNEL_PENDED_PACKET *packet)
218 OvsIPv4TunnelKey tunnelKey;
220 ULONG sendCompleteFlags = 0;
222 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
223 LOCK_STATE_EX lockState, dpLockState;
224 LIST_ENTRY missedPackets;
225 OvsCompletionList completionList;
227 ULONG SendFlags = NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP;
228 OVS_DATAPATH *datapath = &gOvsSwitchContext->datapath;;
230 ASSERT(gOvsSwitchContext);
232 /* Fill the tunnel key */
233 status = OvsSlowPathDecapVxlan(pNbl, &tunnelKey);
235 if(!NT_SUCCESS(status)) {
239 pNb = NET_BUFFER_LIST_FIRST_NB(pNbl);
241 NdisAdvanceNetBufferDataStart(pNb,
242 packet->transportHeaderSize + packet->ipHeaderSize +
247 /* Most likely (always) dispatch irql */
248 irql = KeGetCurrentIrql();
250 /* dispatch is used for datapath lock as well */
251 dispatch = (irql == DISPATCH_LEVEL) ? NDIS_RWL_AT_DISPATCH_LEVEL : 0;
253 sendCompleteFlags |= NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL;
256 InitializeListHead(&missedPackets);
257 OvsInitCompletionList(&completionList, gOvsSwitchContext,
261 POVS_VPORT_ENTRY vport = NULL;
263 OVS_PACKET_HDR_INFO layers = { 0 };
264 OvsFlowKey key = { 0 };
266 PNET_BUFFER curNb = NULL;
267 OvsFlow *flow = NULL;
269 fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl);
272 * XXX WFP packets contain a single NBL structure.
273 * Reassembeled packet "may" have multiple NBs, however, a simple test shows
274 * that the packet still has a single NB (after reassemble)
275 * We still need to check if the Ethernet header of the innet packet is in a single MD
278 curNb = NET_BUFFER_LIST_FIRST_NB(pNbl);
279 ASSERT(curNb->Next == NULL);
281 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, dispatch);
283 /* Lock the flowtable for the duration of accessing the flow */
284 OvsAcquireDatapathRead(datapath, &dpLockState, NDIS_RWL_AT_DISPATCH_LEVEL);
286 SendFlags |= NDIS_SEND_FLAGS_DISPATCH_LEVEL;
288 vport = OvsFindTunnelVportByDstPort(gOvsSwitchContext,
289 htons(tunnelKey.dst_port),
290 OVS_VPORT_TYPE_VXLAN);
293 status = STATUS_UNSUCCESSFUL;
297 ASSERT(vport->ovsType == OVS_VPORT_TYPE_VXLAN);
299 portNo = vport->portNo;
301 status = OvsExtractFlow(pNbl, portNo, &key, &layers, &tunnelKey);
302 if (status != NDIS_STATUS_SUCCESS) {
306 flow = OvsLookupFlow(datapath, &key, &hash, FALSE);
308 OvsFlowUsed(flow, pNbl, &layers);
311 OvsActionsExecute(gOvsSwitchContext, &completionList, pNbl,
312 portNo, SendFlags, &key, &hash, &layers,
313 flow->actions, flow->actionsLen);
315 OvsReleaseDatapath(datapath, &dpLockState);
317 POVS_PACKET_QUEUE_ELEM elem;
320 elem = OvsCreateQueueNlPacket(NULL, 0, OVS_PACKET_CMD_MISS,
321 vport, &key, pNbl, curNb,
324 /* Complete the packet since it was copied to user buffer. */
325 InsertTailList(&missedPackets, &elem->link);
326 OvsQueuePackets(&missedPackets, 1);
328 status = STATUS_INSUFFICIENT_RESOURCES;
333 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
340 OvsReleaseDatapath(datapath, &dpLockState);
341 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
343 pNbl = OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE);
344 ASSERT(pNbl == NULL);