]> git.proxmox.com Git - mirror_ovs.git/blame - datapath-windows/ovsext/Gre.c
datapath-windows: Add Geneve support
[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,
99 PNET_BUFFER_LIST *newNbl)
100{
101 OVS_FWD_INFO fwdInfo;
102 NDIS_STATUS status;
103
104 status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
105 if (status != STATUS_SUCCESS) {
106 OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
107 return NDIS_STATUS_FAILURE;
108 }
109
110 status = OvsDoEncapGre(vport, curNbl, tunKey, &fwdInfo, layers,
111 switchContext, newNbl);
112 return status;
113}
114
115/*
116 * --------------------------------------------------------------------------
117 * OvsDoEncapGre --
118 * Internal utility function which actually does the GRE encap.
119 * --------------------------------------------------------------------------
120 */
121NDIS_STATUS
122OvsDoEncapGre(POVS_VPORT_ENTRY vport,
123 PNET_BUFFER_LIST curNbl,
124 const OvsIPv4TunnelKey *tunKey,
125 const POVS_FWD_INFO fwdInfo,
126 POVS_PACKET_HDR_INFO layers,
127 POVS_SWITCH_CONTEXT switchContext,
128 PNET_BUFFER_LIST *newNbl)
129{
130 NDIS_STATUS status;
131 PNET_BUFFER curNb;
132 PMDL curMdl;
133 PUINT8 bufferStart;
134 EthHdr *ethHdr;
135 IPHdr *ipHdr;
136 PGREHdr greHdr;
137 POVS_GRE_VPORT vportGre;
9a5b6ddf
AS
138 PCHAR pChk = NULL;
139 UINT32 headRoom = GreTunHdrSize(OvsTunnelFlagsToGreFlags(tunKey->flags));
85571a3d
AS
140#if DBG
141 UINT32 counterHeadRoom;
142#endif
143 UINT32 packetLength;
144 ULONG mss = 0;
145 ASSERT(*newNbl == NULL);
146
147 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
148 packetLength = NET_BUFFER_DATA_LENGTH(curNb);
149
150 if (layers->isTcp) {
7b383a56 151 mss = OVSGetTcpMSS(curNbl);
85571a3d 152
85571a3d
AS
153 OVS_LOG_TRACE("MSS %u packet len %u", mss,
154 packetLength);
155 if (mss) {
156 OVS_LOG_TRACE("l4Offset %d", layers->l4Offset);
157 *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers,
158 mss, headRoom);
159 if (*newNbl == NULL) {
160 OVS_LOG_ERROR("Unable to segment NBL");
161 return NDIS_STATUS_FAILURE;
162 }
163 /* Clear out LSO flags after this point */
164 NET_BUFFER_LIST_INFO(*newNbl, TcpLargeSendNetBufferListInfo) = 0;
165 }
166 }
167
168 vportGre = (POVS_GRE_VPORT)GetOvsVportPriv(vport);
169 ASSERT(vportGre);
170
171 /* If we didn't split the packet above, make a copy now */
172 if (*newNbl == NULL) {
173 *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom,
174 FALSE /*NBL info*/);
175 if (*newNbl == NULL) {
176 OVS_LOG_ERROR("Unable to copy NBL");
177 return NDIS_STATUS_FAILURE;
178 }
85571a3d
AS
179
180 NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
181 csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl,
182 TcpIpChecksumNetBufferListInfo);
183
7b383a56
AS
184 status = OvsApplySWChecksumOnNB(layers, *newNbl, &csumInfo);
185 if (status != NDIS_STATUS_SUCCESS) {
186 goto ret_error;
85571a3d 187 }
85571a3d
AS
188 }
189
190 curNbl = *newNbl;
191 for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL;
192 curNb = curNb->Next) {
193#if DBG
194 counterHeadRoom = headRoom;
195#endif
196 status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL);
197 if (status != NDIS_STATUS_SUCCESS) {
198 goto ret_error;
199 }
200
201 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
202 bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl,
203 LowPagePriority);
204 if (!bufferStart) {
205 status = NDIS_STATUS_RESOURCES;
206 goto ret_error;
207 }
208
209 bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
210 if (NET_BUFFER_NEXT_NB(curNb)) {
211 OVS_LOG_TRACE("nb length %u next %u",
212 NET_BUFFER_DATA_LENGTH(curNb),
213 NET_BUFFER_DATA_LENGTH(curNb->Next));
214 }
215
216 /* L2 header */
217 ethHdr = (EthHdr *)bufferStart;
218 ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) ==
219 (PCHAR)&fwdInfo->srcMacAddr);
220 NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr,
221 sizeof ethHdr->Destination + sizeof ethHdr->Source);
222 ethHdr->Type = htons(ETH_TYPE_IPV4);
223#if DBG
224 counterHeadRoom -= sizeof *ethHdr;
225#endif
226
227 /* IP header */
228 ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr);
229
230 ipHdr->ihl = sizeof *ipHdr / 4;
231 ipHdr->version = IPPROTO_IPV4;
232 ipHdr->tos = tunKey->tos;
233 ipHdr->tot_len = htons(NET_BUFFER_DATA_LENGTH(curNb) - sizeof *ethHdr);
234 ipHdr->id = (uint16)atomic_add64(&vportGre->ipId,
235 NET_BUFFER_DATA_LENGTH(curNb));
236 ipHdr->frag_off = (tunKey->flags & OVS_TNL_F_DONT_FRAGMENT) ?
237 IP_DF_NBO : 0;
238 ipHdr->ttl = tunKey->ttl ? tunKey->ttl : 64;
239 ipHdr->protocol = IPPROTO_GRE;
240 ASSERT(tunKey->dst == fwdInfo->dstIpAddr);
241 ASSERT(tunKey->src == fwdInfo->srcIpAddr || tunKey->src == 0);
242 ipHdr->saddr = fwdInfo->srcIpAddr;
243 ipHdr->daddr = fwdInfo->dstIpAddr;
244
245 ipHdr->check = 0;
246 ipHdr->check = IPChecksum((UINT8 *)ipHdr, sizeof *ipHdr, 0);
247#if DBG
248 counterHeadRoom -= sizeof *ipHdr;
249#endif
250
251 /* GRE header */
252 greHdr = (GREHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
253 greHdr->flags = OvsTunnelFlagsToGreFlags(tunKey->flags);
254 greHdr->protocolType = GRE_NET_TEB;
255#if DBG
256 counterHeadRoom -= sizeof *greHdr;
257#endif
258
259 PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr;
260
261 if (tunKey->flags & OVS_TNL_F_CSUM) {
262 RtlZeroMemory(currentOffset, 4);
9a5b6ddf 263 pChk = currentOffset;
85571a3d
AS
264 currentOffset += 4;
265#if DBG
266 counterHeadRoom -= 4;
267#endif
268 }
269
270 if (tunKey->flags & OVS_TNL_F_KEY) {
271 RtlZeroMemory(currentOffset, 4);
272 UINT32 key = (tunKey->tunnelId >> 32);
273 RtlCopyMemory(currentOffset, &key, sizeof key);
274 currentOffset += 4;
275#if DBG
276 counterHeadRoom -= 4;
277#endif
278 }
279
9a5b6ddf
AS
280 /* Checksum needs to be done after the GRE header has been set */
281 if (tunKey->flags & OVS_TNL_F_CSUM) {
282 ASSERT(pChk);
283 UINT16 chksum =
284 CalculateChecksumNB(curNb,
285 (UINT16)(NET_BUFFER_DATA_LENGTH(curNb) -
286 sizeof *ipHdr - sizeof *ethHdr),
287 sizeof *ipHdr + sizeof *ethHdr);
288 RtlCopyMemory(pChk, &chksum, 2);
289 }
290
85571a3d
AS
291#if DBG
292 ASSERT(counterHeadRoom == 0);
293#endif
294
295 }
296 return STATUS_SUCCESS;
297
298ret_error:
299 OvsCompleteNBL(switchContext, *newNbl, TRUE);
300 *newNbl = NULL;
301 return status;
302}
303
304NDIS_STATUS
305OvsDecapGre(POVS_SWITCH_CONTEXT switchContext,
306 PNET_BUFFER_LIST curNbl,
307 OvsIPv4TunnelKey *tunKey,
308 PNET_BUFFER_LIST *newNbl)
309{
310 PNET_BUFFER curNb;
311 PMDL curMdl;
312 EthHdr *ethHdr;
313 IPHdr *ipHdr;
314 GREHdr *greHdr;
9a5b6ddf 315 UINT32 tunnelSize, packetLength;
85571a3d
AS
316 UINT32 headRoom = 0;
317 PUINT8 bufferStart;
9a5b6ddf
AS
318 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
319 PCHAR tempBuf = NULL;
320
321 ASSERT(*newNbl == NULL);
322
323 *newNbl = NULL;
85571a3d
AS
324
325 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
326 packetLength = NET_BUFFER_DATA_LENGTH(curNb);
9a5b6ddf
AS
327 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
328 tunnelSize = GreTunHdrSize(0);
85571a3d
AS
329 if (packetLength <= tunnelSize) {
330 return NDIS_STATUS_INVALID_LENGTH;
331 }
332
9a5b6ddf
AS
333 /* Get a contiguous buffer for the maximum length of a GRE header */
334 bufferStart = NdisGetDataBuffer(curNb, OVS_MAX_GRE_LGTH, NULL, 1, 0);
85571a3d 335 if (!bufferStart) {
9a5b6ddf
AS
336 /* Documentation is unclear on where the packet can be fragmented.
337 * For the moment allocate the buffer needed to get the maximum length
338 * of a GRE header contiguous */
339 tempBuf = OvsAllocateMemoryWithTag(OVS_MAX_GRE_LGTH, OVS_GRE_POOL_TAG);
340 if (!tempBuf) {
341 status = NDIS_STATUS_RESOURCES;
342 goto end;
343 }
344 RtlZeroMemory(tempBuf, OVS_MAX_GRE_LGTH);
345 bufferStart = NdisGetDataBuffer(curNb, OVS_MAX_GRE_LGTH, tempBuf,
346 1, 0);
347 if (!bufferStart) {
348 status = NDIS_STATUS_RESOURCES;
349 goto end;
350 }
85571a3d
AS
351 }
352
353 ethHdr = (EthHdr *)bufferStart;
354 headRoom += sizeof *ethHdr;
355
356 ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr);
357 tunKey->src = ipHdr->saddr;
358 tunKey->dst = ipHdr->daddr;
359 tunKey->tos = ipHdr->tos;
360 tunKey->ttl = ipHdr->ttl;
361 tunKey->pad = 0;
362 headRoom += sizeof *ipHdr;
363
364 greHdr = (GREHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
365 headRoom += sizeof *greHdr;
366
9a5b6ddf
AS
367 tunnelSize = GreTunHdrSize(greHdr->flags);
368
369 /* Verify the packet length after looking at the GRE flags*/
370 if (packetLength <= tunnelSize) {
371 status = NDIS_STATUS_INVALID_LENGTH;
372 goto end;
373 }
374
85571a3d
AS
375 /* Validate if GRE header protocol type. */
376 if (greHdr->protocolType != GRE_NET_TEB) {
377 status = STATUS_NDIS_INVALID_PACKET;
9a5b6ddf 378 goto end;
85571a3d
AS
379 }
380
381 PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr;
382
383 if (greHdr->flags & GRE_CSUM) {
384 tunKey->flags |= OVS_TNL_F_CSUM;
9a5b6ddf
AS
385 UINT16 prevChksum = *((UINT16 *)currentOffset);
386 RtlZeroMemory(currentOffset, 2);
387 UINT16 chksum =
388 CalculateChecksumNB(curNb,
389 (UINT16)(NET_BUFFER_DATA_LENGTH(curNb) -
390 (ipHdr->ihl * 4 + sizeof *ethHdr)),
391 ipHdr->ihl * 4 + sizeof *ethHdr);
392 if (prevChksum != chksum) {
393 status = STATUS_NDIS_INVALID_PACKET;
394 goto end;
395 }
396 RtlCopyMemory(currentOffset, &prevChksum, 2);
85571a3d
AS
397 currentOffset += 4;
398 headRoom += 4;
399 }
400
401 if (greHdr->flags & GRE_KEY) {
402 tunKey->flags |= OVS_TNL_F_KEY;
403 UINT32 key = 0;
404 RtlCopyMemory(&key, currentOffset, 4);
405 tunKey->tunnelId = (UINT64)key << 32;
406 currentOffset += 4;
407 headRoom += 4;
408 }
409
9a5b6ddf
AS
410 /*
411 * Create a copy of the NBL so that we have all the headers in one MDL.
412 */
413 *newNbl = OvsPartialCopyNBL(switchContext, curNbl,
414 tunnelSize, 0,
415 TRUE /*copy NBL info */);
416
417 if (*newNbl == NULL) {
418 status = NDIS_STATUS_RESOURCES;
419 goto end;
420 }
421
422 curNbl = *newNbl;
423 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
424
85571a3d
AS
425 /* Clear out the receive flag for the inner packet. */
426 NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = 0;
9a5b6ddf 427 NdisAdvanceNetBufferDataStart(curNb, GreTunHdrSize(greHdr->flags), FALSE,
85571a3d 428 NULL);
9a5b6ddf 429 ASSERT(headRoom == GreTunHdrSize(greHdr->flags));
85571a3d 430
9a5b6ddf
AS
431end:
432 if (tempBuf) {
433 OvsFreeMemoryWithTag(tempBuf, OVS_GRE_POOL_TAG);
434 tempBuf = NULL;
435 }
85571a3d
AS
436 return status;
437}