2 * Copyright (c) 2015, 2016 Cloudbase Solutions Srl
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.
27 #include "PacketParser.h"
36 #define OVS_DBG_MOD OVS_DBG_GRE
39 OvsDoEncapGre(POVS_VPORT_ENTRY vport
, PNET_BUFFER_LIST curNbl
,
40 const OvsIPv4TunnelKey
*tunKey
,
41 const POVS_FWD_INFO fwdInfo
,
42 POVS_PACKET_HDR_INFO layers
,
43 POVS_SWITCH_CONTEXT switchContext
,
44 PNET_BUFFER_LIST
*newNbl
);
47 * --------------------------------------------------------------------------
49 * Initialize GRE tunnel module.
50 * --------------------------------------------------------------------------
53 OvsInitGreTunnel(POVS_VPORT_ENTRY vport
)
55 POVS_GRE_VPORT grePort
;
57 grePort
= (POVS_GRE_VPORT
)OvsAllocateMemoryWithTag(sizeof(*grePort
),
60 OVS_LOG_ERROR("Insufficient memory, can't allocate OVS_GRE_VPORT");
61 return STATUS_INSUFFICIENT_RESOURCES
;
64 RtlZeroMemory(grePort
, sizeof(*grePort
));
65 vport
->priv
= (PVOID
)grePort
;
66 return STATUS_SUCCESS
;
70 * --------------------------------------------------------------------------
71 * OvsCleanupGreTunnel --
72 * Cleanup GRE Tunnel module.
73 * --------------------------------------------------------------------------
76 OvsCleanupGreTunnel(POVS_VPORT_ENTRY vport
)
78 if (vport
->ovsType
!= OVS_VPORT_TYPE_GRE
||
79 vport
->priv
== NULL
) {
83 OvsFreeMemoryWithTag(vport
->priv
, OVS_GRE_POOL_TAG
);
88 * --------------------------------------------------------------------------
90 * Encapsulates a packet with an GRE header.
91 * --------------------------------------------------------------------------
94 OvsEncapGre(POVS_VPORT_ENTRY vport
,
95 PNET_BUFFER_LIST curNbl
,
96 OvsIPv4TunnelKey
*tunKey
,
97 POVS_SWITCH_CONTEXT switchContext
,
98 POVS_PACKET_HDR_INFO layers
,
99 PNET_BUFFER_LIST
*newNbl
,
100 POVS_FWD_INFO switchFwdInfo
)
102 OVS_FWD_INFO fwdInfo
;
105 status
= OvsLookupIPFwdInfo(tunKey
->src
, tunKey
->dst
, &fwdInfo
);
106 if (status
!= STATUS_SUCCESS
) {
107 OvsFwdIPHelperRequest(NULL
, 0, tunKey
, NULL
, NULL
, NULL
);
108 return NDIS_STATUS_FAILURE
;
111 RtlCopyMemory(switchFwdInfo
->value
, fwdInfo
.value
, sizeof fwdInfo
.value
);
113 status
= OvsDoEncapGre(vport
, curNbl
, tunKey
, &fwdInfo
, layers
,
114 switchContext
, newNbl
);
119 * --------------------------------------------------------------------------
121 * Internal utility function which actually does the GRE encap.
122 * --------------------------------------------------------------------------
125 OvsDoEncapGre(POVS_VPORT_ENTRY vport
,
126 PNET_BUFFER_LIST curNbl
,
127 const OvsIPv4TunnelKey
*tunKey
,
128 const POVS_FWD_INFO fwdInfo
,
129 POVS_PACKET_HDR_INFO layers
,
130 POVS_SWITCH_CONTEXT switchContext
,
131 PNET_BUFFER_LIST
*newNbl
)
140 POVS_GRE_VPORT vportGre
;
142 UINT32 headRoom
= GreTunHdrSize(OvsTunnelFlagsToGreFlags(tunKey
->flags
));
144 UINT32 counterHeadRoom
;
148 ASSERT(*newNbl
== NULL
);
150 curNb
= NET_BUFFER_LIST_FIRST_NB(curNbl
);
151 packetLength
= NET_BUFFER_DATA_LENGTH(curNb
);
154 mss
= OVSGetTcpMSS(curNbl
);
156 OVS_LOG_TRACE("MSS %u packet len %u", mss
,
159 OVS_LOG_TRACE("l4Offset %d", layers
->l4Offset
);
160 *newNbl
= OvsTcpSegmentNBL(switchContext
, curNbl
, layers
,
161 mss
, headRoom
, FALSE
);
162 if (*newNbl
== NULL
) {
163 OVS_LOG_ERROR("Unable to segment NBL");
164 return NDIS_STATUS_FAILURE
;
166 /* Clear out LSO flags after this point */
167 NET_BUFFER_LIST_INFO(*newNbl
, TcpLargeSendNetBufferListInfo
) = 0;
171 vportGre
= (POVS_GRE_VPORT
)GetOvsVportPriv(vport
);
174 /* If we didn't split the packet above, make a copy now */
175 if (*newNbl
== NULL
) {
176 *newNbl
= OvsPartialCopyNBL(switchContext
, curNbl
, 0, headRoom
,
178 if (*newNbl
== NULL
) {
179 OVS_LOG_ERROR("Unable to copy NBL");
180 return NDIS_STATUS_FAILURE
;
183 NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo
;
184 csumInfo
.Value
= NET_BUFFER_LIST_INFO(curNbl
,
185 TcpIpChecksumNetBufferListInfo
);
187 status
= OvsApplySWChecksumOnNB(layers
, *newNbl
, &csumInfo
);
188 if (status
!= NDIS_STATUS_SUCCESS
) {
194 for (curNb
= NET_BUFFER_LIST_FIRST_NB(curNbl
); curNb
!= NULL
;
195 curNb
= curNb
->Next
) {
197 counterHeadRoom
= headRoom
;
199 status
= NdisRetreatNetBufferDataStart(curNb
, headRoom
, 0, NULL
);
200 if (status
!= NDIS_STATUS_SUCCESS
) {
204 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
205 bufferStart
= (PUINT8
)MmGetSystemAddressForMdlSafe(curMdl
,
208 status
= NDIS_STATUS_RESOURCES
;
212 bufferStart
+= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
213 if (NET_BUFFER_NEXT_NB(curNb
)) {
214 OVS_LOG_TRACE("nb length %u next %u",
215 NET_BUFFER_DATA_LENGTH(curNb
),
216 NET_BUFFER_DATA_LENGTH(curNb
->Next
));
220 ethHdr
= (EthHdr
*)bufferStart
;
221 NdisMoveMemory(ethHdr
->Destination
, fwdInfo
->dstMacAddr
,
222 sizeof ethHdr
->Destination
);
223 NdisMoveMemory(ethHdr
->Source
, fwdInfo
->srcMacAddr
,
224 sizeof ethHdr
->Source
);
225 ethHdr
->Type
= htons(ETH_TYPE_IPV4
);
227 counterHeadRoom
-= sizeof *ethHdr
;
231 ipHdr
= (IPHdr
*)((PCHAR
)ethHdr
+ sizeof *ethHdr
);
233 ipHdr
->ihl
= sizeof *ipHdr
/ 4;
234 ipHdr
->version
= IPPROTO_IPV4
;
235 ipHdr
->tos
= tunKey
->tos
;
236 ipHdr
->tot_len
= htons(NET_BUFFER_DATA_LENGTH(curNb
) - sizeof *ethHdr
);
237 ipHdr
->id
= (uint16
)atomic_add64(&vportGre
->ipId
,
238 NET_BUFFER_DATA_LENGTH(curNb
));
239 ipHdr
->frag_off
= (tunKey
->flags
& OVS_TNL_F_DONT_FRAGMENT
) ?
241 ipHdr
->ttl
= tunKey
->ttl
? tunKey
->ttl
: 64;
242 ipHdr
->protocol
= IPPROTO_GRE
;
243 ASSERT(tunKey
->dst
== fwdInfo
->dstIpAddr
);
244 ASSERT(tunKey
->src
== fwdInfo
->srcIpAddr
|| tunKey
->src
== 0);
245 ipHdr
->saddr
= fwdInfo
->srcIpAddr
;
246 ipHdr
->daddr
= fwdInfo
->dstIpAddr
;
249 ipHdr
->check
= IPChecksum((UINT8
*)ipHdr
, sizeof *ipHdr
, 0);
251 counterHeadRoom
-= sizeof *ipHdr
;
255 greHdr
= (GREHdr
*)((PCHAR
)ipHdr
+ sizeof *ipHdr
);
256 greHdr
->flags
= OvsTunnelFlagsToGreFlags(tunKey
->flags
);
257 greHdr
->protocolType
= GRE_NET_TEB
;
259 counterHeadRoom
-= sizeof *greHdr
;
262 PCHAR currentOffset
= (PCHAR
)greHdr
+ sizeof *greHdr
;
264 if (tunKey
->flags
& OVS_TNL_F_CSUM
) {
265 RtlZeroMemory(currentOffset
, 4);
266 pChk
= currentOffset
;
269 counterHeadRoom
-= 4;
273 if (tunKey
->flags
& OVS_TNL_F_KEY
) {
274 RtlZeroMemory(currentOffset
, 4);
275 UINT32 key
= (tunKey
->tunnelId
>> 32);
276 RtlCopyMemory(currentOffset
, &key
, sizeof key
);
279 counterHeadRoom
-= 4;
283 /* Checksum needs to be done after the GRE header has been set */
284 if (tunKey
->flags
& OVS_TNL_F_CSUM
) {
287 CalculateChecksumNB(curNb
,
288 (UINT16
)(NET_BUFFER_DATA_LENGTH(curNb
) -
289 sizeof *ipHdr
- sizeof *ethHdr
),
290 sizeof *ipHdr
+ sizeof *ethHdr
);
291 RtlCopyMemory(pChk
, &chksum
, 2);
295 ASSERT(counterHeadRoom
== 0);
299 return STATUS_SUCCESS
;
302 OvsCompleteNBL(switchContext
, *newNbl
, TRUE
);
308 OvsDecapGre(POVS_SWITCH_CONTEXT switchContext
,
309 PNET_BUFFER_LIST curNbl
,
310 OvsIPv4TunnelKey
*tunKey
,
311 PNET_BUFFER_LIST
*newNbl
)
318 UINT32 tunnelSize
, packetLength
;
322 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
323 PCHAR tempBuf
= NULL
;
324 OVS_PACKET_HDR_INFO layers
;
326 ASSERT(*newNbl
== NULL
);
329 status
= OvsExtractLayers(curNbl
, &layers
);
330 if (status
!= NDIS_STATUS_SUCCESS
) {
334 curNb
= NET_BUFFER_LIST_FIRST_NB(curNbl
);
335 packetLength
= NET_BUFFER_DATA_LENGTH(curNb
);
336 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
337 tunnelSize
= GreTunHdrSizeFromLayers(0, &layers
);
338 if (packetLength
<= tunnelSize
) {
339 return NDIS_STATUS_INVALID_LENGTH
;
342 maxGreLen
= GreMaxLengthFromLayers(&layers
);
343 /* Get a contiguous buffer for the maximum length of a GRE header */
344 bufferStart
= NdisGetDataBuffer(curNb
, maxGreLen
, NULL
, 1, 0);
346 /* Documentation is unclear on where the packet can be fragmented.
347 * For the moment allocate the buffer needed to get the maximum length
348 * of a GRE header contiguous */
349 tempBuf
= OvsAllocateMemoryWithTag(maxGreLen
, OVS_GRE_POOL_TAG
);
351 status
= NDIS_STATUS_RESOURCES
;
354 RtlZeroMemory(tempBuf
, maxGreLen
);
355 bufferStart
= NdisGetDataBuffer(curNb
, maxGreLen
, tempBuf
,
358 status
= NDIS_STATUS_RESOURCES
;
363 ethHdr
= (EthHdr
*)bufferStart
;
364 headRoom
+= layers
.l3Offset
;
366 ipHdr
= (IPHdr
*)(bufferStart
+ layers
.l3Offset
);
367 tunKey
->src
= ipHdr
->saddr
;
368 tunKey
->dst
= ipHdr
->daddr
;
369 tunKey
->tos
= ipHdr
->tos
;
370 tunKey
->ttl
= ipHdr
->ttl
;
372 headRoom
+= sizeof *ipHdr
;
374 greHdr
= (GREHdr
*)(bufferStart
+ layers
.l4Offset
);
375 headRoom
+= sizeof *greHdr
;
377 tunnelSize
= GreTunHdrSizeFromLayers(greHdr
->flags
, &layers
);
379 /* Verify the packet length after looking at the GRE flags*/
380 if (packetLength
<= tunnelSize
) {
381 status
= NDIS_STATUS_INVALID_LENGTH
;
385 /* Validate if GRE header protocol type. */
386 if (greHdr
->protocolType
!= GRE_NET_TEB
) {
387 status
= STATUS_NDIS_INVALID_PACKET
;
391 PCHAR currentOffset
= (PCHAR
)greHdr
+ sizeof *greHdr
;
393 if (greHdr
->flags
& GRE_CSUM
) {
394 tunKey
->flags
|= OVS_TNL_F_CSUM
;
395 UINT16 prevChksum
= *((UINT16
*)currentOffset
);
396 RtlZeroMemory(currentOffset
, 2);
398 CalculateChecksumNB(curNb
,
399 (UINT16
)(NET_BUFFER_DATA_LENGTH(curNb
) -
402 if (prevChksum
!= chksum
) {
403 status
= STATUS_NDIS_INVALID_PACKET
;
406 RtlCopyMemory(currentOffset
, &prevChksum
, 2);
411 if (greHdr
->flags
& GRE_KEY
) {
412 tunKey
->flags
|= OVS_TNL_F_KEY
;
414 RtlCopyMemory(&key
, currentOffset
, 4);
415 tunKey
->tunnelId
= (UINT64
)key
<< 32;
421 * Create a copy of the NBL so that we have all the headers in one MDL.
423 *newNbl
= OvsPartialCopyNBL(switchContext
, curNbl
,
425 TRUE
/*copy NBL info */);
427 if (*newNbl
== NULL
) {
428 status
= NDIS_STATUS_RESOURCES
;
433 curNb
= NET_BUFFER_LIST_FIRST_NB(curNbl
);
435 /* Clear out the receive flag for the inner packet. */
436 NET_BUFFER_LIST_INFO(curNbl
, TcpIpChecksumNetBufferListInfo
) = 0;
437 NdisAdvanceNetBufferDataStart(curNb
, GreTunHdrSize(greHdr
->flags
), FALSE
,
439 ASSERT(headRoom
== GreTunHdrSize(greHdr
->flags
));
443 OvsFreeMemoryWithTag(tempBuf
, OVS_GRE_POOL_TAG
);