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
= OvsFindTunnelVportByDstPortAndType(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
);