]> git.proxmox.com Git - mirror_ovs.git/blame - datapath-windows/ovsext/Gre.c
datapath-windows: Account for VLAN tag in tunnel Decap
[mirror_ovs.git] / datapath-windows / ovsext / Gre.c
CommitLineData
85571a3d 1/*
7b383a56 2 * Copyright (c) 2015, 2016 Cloudbase Solutions Srl
85571a3d
AS
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include "precomp.h"
18
19#include "Atomic.h"
7b383a56 20#include "Debug.h"
85571a3d
AS
21#include "Flow.h"
22#include "Gre.h"
23#include "IpHelper.h"
24#include "NetProto.h"
7b383a56 25#include "Offload.h"
85571a3d
AS
26#include "PacketIO.h"
27#include "PacketParser.h"
28#include "Switch.h"
29#include "User.h"
30#include "Util.h"
31#include "Vport.h"
32
33#ifdef OVS_DBG_MOD
34#undef OVS_DBG_MOD
35#endif
36#define OVS_DBG_MOD OVS_DBG_GRE
85571a3d
AS
37
38static NDIS_STATUS
39OvsDoEncapGre(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);
45
46/*
47 * --------------------------------------------------------------------------
48 * OvsInitGreTunnel --
49 * Initialize GRE tunnel module.
50 * --------------------------------------------------------------------------
51 */
52NTSTATUS
53OvsInitGreTunnel(POVS_VPORT_ENTRY vport)
54{
55 POVS_GRE_VPORT grePort;
56
57 grePort = (POVS_GRE_VPORT)OvsAllocateMemoryWithTag(sizeof(*grePort),
58 OVS_GRE_POOL_TAG);
59 if (!grePort) {
60 OVS_LOG_ERROR("Insufficient memory, can't allocate OVS_GRE_VPORT");
61 return STATUS_INSUFFICIENT_RESOURCES;
62 }
63
64 RtlZeroMemory(grePort, sizeof(*grePort));
65 vport->priv = (PVOID)grePort;
66 return STATUS_SUCCESS;
67}
68
69/*
70 * --------------------------------------------------------------------------
71 * OvsCleanupGreTunnel --
72 * Cleanup GRE Tunnel module.
73 * --------------------------------------------------------------------------
74 */
75void
76OvsCleanupGreTunnel(POVS_VPORT_ENTRY vport)
77{
78 if (vport->ovsType != OVS_VPORT_TYPE_GRE ||
79 vport->priv == NULL) {
80 return;
81 }
82
83 OvsFreeMemoryWithTag(vport->priv, OVS_GRE_POOL_TAG);
84 vport->priv = NULL;
85}
86
87/*
88 * --------------------------------------------------------------------------
89 * OvsEncapGre --
90 * Encapsulates a packet with an GRE header.
91 * --------------------------------------------------------------------------
92 */
93NDIS_STATUS
94OvsEncapGre(POVS_VPORT_ENTRY vport,
95 PNET_BUFFER_LIST curNbl,
96 OvsIPv4TunnelKey *tunKey,
97 POVS_SWITCH_CONTEXT switchContext,
98 POVS_PACKET_HDR_INFO layers,
cd30b346
AS
99 PNET_BUFFER_LIST *newNbl,
100 POVS_FWD_INFO switchFwdInfo)
85571a3d
AS
101{
102 OVS_FWD_INFO fwdInfo;
103 NDIS_STATUS status;
104
cd30b346 105 status = OvsLookupIPFwdInfo(tunKey->src, tunKey->dst, &fwdInfo);
85571a3d
AS
106 if (status != STATUS_SUCCESS) {
107 OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
108 return NDIS_STATUS_FAILURE;
109 }
110
cd30b346
AS
111 RtlCopyMemory(switchFwdInfo->value, fwdInfo.value, sizeof fwdInfo.value);
112
85571a3d
AS
113 status = OvsDoEncapGre(vport, curNbl, tunKey, &fwdInfo, layers,
114 switchContext, newNbl);
115 return status;
116}
117
118/*
119 * --------------------------------------------------------------------------
120 * OvsDoEncapGre --
121 * Internal utility function which actually does the GRE encap.
122 * --------------------------------------------------------------------------
123 */
124NDIS_STATUS
125OvsDoEncapGre(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)
132{
133 NDIS_STATUS status;
134 PNET_BUFFER curNb;
135 PMDL curMdl;
136 PUINT8 bufferStart;
137 EthHdr *ethHdr;
138 IPHdr *ipHdr;
139 PGREHdr greHdr;
140 POVS_GRE_VPORT vportGre;
9a5b6ddf
AS
141 PCHAR pChk = NULL;
142 UINT32 headRoom = GreTunHdrSize(OvsTunnelFlagsToGreFlags(tunKey->flags));
85571a3d
AS
143#if DBG
144 UINT32 counterHeadRoom;
145#endif
146 UINT32 packetLength;
147 ULONG mss = 0;
148 ASSERT(*newNbl == NULL);
149
150 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
151 packetLength = NET_BUFFER_DATA_LENGTH(curNb);
152
153 if (layers->isTcp) {
7b383a56 154 mss = OVSGetTcpMSS(curNbl);
85571a3d 155
85571a3d
AS
156 OVS_LOG_TRACE("MSS %u packet len %u", mss,
157 packetLength);
158 if (mss) {
159 OVS_LOG_TRACE("l4Offset %d", layers->l4Offset);
160 *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers,
ac8df9f6 161 mss, headRoom, FALSE);
85571a3d
AS
162 if (*newNbl == NULL) {
163 OVS_LOG_ERROR("Unable to segment NBL");
164 return NDIS_STATUS_FAILURE;
165 }
166 /* Clear out LSO flags after this point */
167 NET_BUFFER_LIST_INFO(*newNbl, TcpLargeSendNetBufferListInfo) = 0;
168 }
169 }
170
171 vportGre = (POVS_GRE_VPORT)GetOvsVportPriv(vport);
172 ASSERT(vportGre);
173
174 /* If we didn't split the packet above, make a copy now */
175 if (*newNbl == NULL) {
176 *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom,
177 FALSE /*NBL info*/);
178 if (*newNbl == NULL) {
179 OVS_LOG_ERROR("Unable to copy NBL");
180 return NDIS_STATUS_FAILURE;
181 }
85571a3d
AS
182
183 NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
184 csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl,
185 TcpIpChecksumNetBufferListInfo);
186
7b383a56
AS
187 status = OvsApplySWChecksumOnNB(layers, *newNbl, &csumInfo);
188 if (status != NDIS_STATUS_SUCCESS) {
189 goto ret_error;
85571a3d 190 }
85571a3d
AS
191 }
192
193 curNbl = *newNbl;
194 for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL;
195 curNb = curNb->Next) {
196#if DBG
197 counterHeadRoom = headRoom;
198#endif
199 status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL);
200 if (status != NDIS_STATUS_SUCCESS) {
201 goto ret_error;
202 }
203
204 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
205 bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl,
206 LowPagePriority);
207 if (!bufferStart) {
208 status = NDIS_STATUS_RESOURCES;
209 goto ret_error;
210 }
211
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));
217 }
218
219 /* L2 header */
220 ethHdr = (EthHdr *)bufferStart;
85571a3d 221 NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr,
542d8cfa
AS
222 sizeof ethHdr->Destination);
223 NdisMoveMemory(ethHdr->Source, fwdInfo->srcMacAddr,
224 sizeof ethHdr->Source);
85571a3d
AS
225 ethHdr->Type = htons(ETH_TYPE_IPV4);
226#if DBG
227 counterHeadRoom -= sizeof *ethHdr;
228#endif
229
230 /* IP header */
231 ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr);
232
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) ?
240 IP_DF_NBO : 0;
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;
247
248 ipHdr->check = 0;
249 ipHdr->check = IPChecksum((UINT8 *)ipHdr, sizeof *ipHdr, 0);
250#if DBG
251 counterHeadRoom -= sizeof *ipHdr;
252#endif
253
254 /* GRE header */
255 greHdr = (GREHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
256 greHdr->flags = OvsTunnelFlagsToGreFlags(tunKey->flags);
257 greHdr->protocolType = GRE_NET_TEB;
258#if DBG
259 counterHeadRoom -= sizeof *greHdr;
260#endif
261
262 PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr;
263
264 if (tunKey->flags & OVS_TNL_F_CSUM) {
265 RtlZeroMemory(currentOffset, 4);
9a5b6ddf 266 pChk = currentOffset;
85571a3d
AS
267 currentOffset += 4;
268#if DBG
269 counterHeadRoom -= 4;
270#endif
271 }
272
273 if (tunKey->flags & OVS_TNL_F_KEY) {
274 RtlZeroMemory(currentOffset, 4);
275 UINT32 key = (tunKey->tunnelId >> 32);
276 RtlCopyMemory(currentOffset, &key, sizeof key);
277 currentOffset += 4;
278#if DBG
279 counterHeadRoom -= 4;
280#endif
281 }
282
9a5b6ddf
AS
283 /* Checksum needs to be done after the GRE header has been set */
284 if (tunKey->flags & OVS_TNL_F_CSUM) {
285 ASSERT(pChk);
286 UINT16 chksum =
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);
292 }
293
85571a3d
AS
294#if DBG
295 ASSERT(counterHeadRoom == 0);
296#endif
297
298 }
299 return STATUS_SUCCESS;
300
301ret_error:
302 OvsCompleteNBL(switchContext, *newNbl, TRUE);
303 *newNbl = NULL;
304 return status;
305}
306
307NDIS_STATUS
308OvsDecapGre(POVS_SWITCH_CONTEXT switchContext,
309 PNET_BUFFER_LIST curNbl,
310 OvsIPv4TunnelKey *tunKey,
311 PNET_BUFFER_LIST *newNbl)
312{
313 PNET_BUFFER curNb;
314 PMDL curMdl;
315 EthHdr *ethHdr;
316 IPHdr *ipHdr;
317 GREHdr *greHdr;
9a5b6ddf 318 UINT32 tunnelSize, packetLength;
85571a3d 319 UINT32 headRoom = 0;
a9e69581 320 UINT32 maxGreLen;
85571a3d 321 PUINT8 bufferStart;
9a5b6ddf
AS
322 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
323 PCHAR tempBuf = NULL;
a9e69581 324 OVS_PACKET_HDR_INFO layers;
9a5b6ddf
AS
325
326 ASSERT(*newNbl == NULL);
327
328 *newNbl = NULL;
a9e69581
SR
329 status = OvsExtractLayers(curNbl, &layers);
330 if (status != NDIS_STATUS_SUCCESS) {
331 return status;
332 }
85571a3d
AS
333
334 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
335 packetLength = NET_BUFFER_DATA_LENGTH(curNb);
9a5b6ddf 336 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
a9e69581 337 tunnelSize = GreTunHdrSizeFromLayers(0, &layers);
85571a3d
AS
338 if (packetLength <= tunnelSize) {
339 return NDIS_STATUS_INVALID_LENGTH;
340 }
341
a9e69581 342 maxGreLen = GreMaxLengthFromLayers(&layers);
9a5b6ddf 343 /* Get a contiguous buffer for the maximum length of a GRE header */
a9e69581 344 bufferStart = NdisGetDataBuffer(curNb, maxGreLen, NULL, 1, 0);
85571a3d 345 if (!bufferStart) {
9a5b6ddf
AS
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 */
a9e69581 349 tempBuf = OvsAllocateMemoryWithTag(maxGreLen, OVS_GRE_POOL_TAG);
9a5b6ddf
AS
350 if (!tempBuf) {
351 status = NDIS_STATUS_RESOURCES;
352 goto end;
353 }
a9e69581
SR
354 RtlZeroMemory(tempBuf, maxGreLen);
355 bufferStart = NdisGetDataBuffer(curNb, maxGreLen, tempBuf,
9a5b6ddf
AS
356 1, 0);
357 if (!bufferStart) {
358 status = NDIS_STATUS_RESOURCES;
359 goto end;
360 }
85571a3d
AS
361 }
362
363 ethHdr = (EthHdr *)bufferStart;
a9e69581 364 headRoom += layers.l3Offset;
85571a3d 365
a9e69581 366 ipHdr = (IPHdr *)(bufferStart + layers.l3Offset);
85571a3d
AS
367 tunKey->src = ipHdr->saddr;
368 tunKey->dst = ipHdr->daddr;
369 tunKey->tos = ipHdr->tos;
370 tunKey->ttl = ipHdr->ttl;
371 tunKey->pad = 0;
372 headRoom += sizeof *ipHdr;
373
a9e69581 374 greHdr = (GREHdr *)(bufferStart + layers.l4Offset);
85571a3d
AS
375 headRoom += sizeof *greHdr;
376
a9e69581 377 tunnelSize = GreTunHdrSizeFromLayers(greHdr->flags, &layers);
9a5b6ddf
AS
378
379 /* Verify the packet length after looking at the GRE flags*/
380 if (packetLength <= tunnelSize) {
381 status = NDIS_STATUS_INVALID_LENGTH;
382 goto end;
383 }
384
85571a3d
AS
385 /* Validate if GRE header protocol type. */
386 if (greHdr->protocolType != GRE_NET_TEB) {
387 status = STATUS_NDIS_INVALID_PACKET;
9a5b6ddf 388 goto end;
85571a3d
AS
389 }
390
391 PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr;
392
393 if (greHdr->flags & GRE_CSUM) {
394 tunKey->flags |= OVS_TNL_F_CSUM;
9a5b6ddf
AS
395 UINT16 prevChksum = *((UINT16 *)currentOffset);
396 RtlZeroMemory(currentOffset, 2);
397 UINT16 chksum =
398 CalculateChecksumNB(curNb,
399 (UINT16)(NET_BUFFER_DATA_LENGTH(curNb) -
a9e69581
SR
400 layers.l4Offset),
401 layers.l4Offset);
9a5b6ddf
AS
402 if (prevChksum != chksum) {
403 status = STATUS_NDIS_INVALID_PACKET;
404 goto end;
405 }
406 RtlCopyMemory(currentOffset, &prevChksum, 2);
85571a3d
AS
407 currentOffset += 4;
408 headRoom += 4;
409 }
410
411 if (greHdr->flags & GRE_KEY) {
412 tunKey->flags |= OVS_TNL_F_KEY;
413 UINT32 key = 0;
414 RtlCopyMemory(&key, currentOffset, 4);
415 tunKey->tunnelId = (UINT64)key << 32;
416 currentOffset += 4;
417 headRoom += 4;
418 }
419
9a5b6ddf
AS
420 /*
421 * Create a copy of the NBL so that we have all the headers in one MDL.
422 */
423 *newNbl = OvsPartialCopyNBL(switchContext, curNbl,
424 tunnelSize, 0,
425 TRUE /*copy NBL info */);
426
427 if (*newNbl == NULL) {
428 status = NDIS_STATUS_RESOURCES;
429 goto end;
430 }
431
432 curNbl = *newNbl;
433 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
434
85571a3d
AS
435 /* Clear out the receive flag for the inner packet. */
436 NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = 0;
9a5b6ddf 437 NdisAdvanceNetBufferDataStart(curNb, GreTunHdrSize(greHdr->flags), FALSE,
85571a3d 438 NULL);
9a5b6ddf 439 ASSERT(headRoom == GreTunHdrSize(greHdr->flags));
85571a3d 440
9a5b6ddf
AS
441end:
442 if (tempBuf) {
443 OvsFreeMemoryWithTag(tempBuf, OVS_GRE_POOL_TAG);
444 tempBuf = NULL;
445 }
85571a3d
AS
446 return status;
447}