netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / datapath-windows / ovsext / Gre.c
1 /*
2  * Copyright (c) 2015, 2016 Cloudbase Solutions Srl
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"
20 #include "Debug.h"
21 #include "Flow.h"
22 #include "Gre.h"
23 #include "IpHelper.h"
24 #include "NetProto.h"
25 #include "Offload.h"
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
37
38 static NDIS_STATUS
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);
45
46 /*
47  * --------------------------------------------------------------------------
48  * OvsInitGreTunnel --
49  *    Initialize GRE tunnel module.
50  * --------------------------------------------------------------------------
51  */
52 NTSTATUS
53 OvsInitGreTunnel(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  */
75 void
76 OvsCleanupGreTunnel(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  */
93 NDIS_STATUS
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 {
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  */
121 NDIS_STATUS
122 OvsDoEncapGre(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;
138     UINT32 headRoom = GreTunHdrSize(tunKey->flags);
139 #if DBG
140     UINT32 counterHeadRoom;
141 #endif
142     UINT32 packetLength;
143     ULONG mss = 0;
144     ASSERT(*newNbl == NULL);
145
146     curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
147     packetLength = NET_BUFFER_DATA_LENGTH(curNb);
148
149     if (layers->isTcp) {
150         mss = OVSGetTcpMSS(curNbl);
151
152         OVS_LOG_TRACE("MSS %u packet len %u", mss,
153                       packetLength);
154         if (mss) {
155             OVS_LOG_TRACE("l4Offset %d", layers->l4Offset);
156             *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers,
157                                        mss, headRoom);
158             if (*newNbl == NULL) {
159                 OVS_LOG_ERROR("Unable to segment NBL");
160                 return NDIS_STATUS_FAILURE;
161             }
162             /* Clear out LSO flags after this point */
163             NET_BUFFER_LIST_INFO(*newNbl, TcpLargeSendNetBufferListInfo) = 0;
164         }
165     }
166
167     vportGre = (POVS_GRE_VPORT)GetOvsVportPriv(vport);
168     ASSERT(vportGre);
169
170     /* If we didn't split the packet above, make a copy now */
171     if (*newNbl == NULL) {
172         *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom,
173                                     FALSE /*NBL info*/);
174         if (*newNbl == NULL) {
175             OVS_LOG_ERROR("Unable to copy NBL");
176             return NDIS_STATUS_FAILURE;
177         }
178
179         NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
180         csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl,
181                                               TcpIpChecksumNetBufferListInfo);
182
183         status = OvsApplySWChecksumOnNB(layers, *newNbl, &csumInfo);
184         if (status != NDIS_STATUS_SUCCESS) {
185             goto ret_error;
186         }
187     }
188
189     curNbl = *newNbl;
190     for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL;
191          curNb = curNb->Next) {
192 #if DBG
193         counterHeadRoom = headRoom;
194 #endif
195         status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL);
196         if (status != NDIS_STATUS_SUCCESS) {
197             goto ret_error;
198         }
199
200         curMdl = NET_BUFFER_CURRENT_MDL(curNb);
201         bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl,
202                                                            LowPagePriority);
203         if (!bufferStart) {
204             status = NDIS_STATUS_RESOURCES;
205             goto ret_error;
206         }
207
208         bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
209         if (NET_BUFFER_NEXT_NB(curNb)) {
210             OVS_LOG_TRACE("nb length %u next %u",
211                           NET_BUFFER_DATA_LENGTH(curNb),
212                           NET_BUFFER_DATA_LENGTH(curNb->Next));
213         }
214
215         /* L2 header */
216         ethHdr = (EthHdr *)bufferStart;
217         ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) ==
218                (PCHAR)&fwdInfo->srcMacAddr);
219         NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr,
220                        sizeof ethHdr->Destination + sizeof ethHdr->Source);
221         ethHdr->Type = htons(ETH_TYPE_IPV4);
222 #if DBG
223         counterHeadRoom -= sizeof *ethHdr;
224 #endif
225
226         /* IP header */
227         ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr);
228
229         ipHdr->ihl = sizeof *ipHdr / 4;
230         ipHdr->version = IPPROTO_IPV4;
231         ipHdr->tos = tunKey->tos;
232         ipHdr->tot_len = htons(NET_BUFFER_DATA_LENGTH(curNb) - sizeof *ethHdr);
233         ipHdr->id = (uint16)atomic_add64(&vportGre->ipId,
234                                          NET_BUFFER_DATA_LENGTH(curNb));
235         ipHdr->frag_off = (tunKey->flags & OVS_TNL_F_DONT_FRAGMENT) ?
236                           IP_DF_NBO : 0;
237         ipHdr->ttl = tunKey->ttl ? tunKey->ttl : 64;
238         ipHdr->protocol = IPPROTO_GRE;
239         ASSERT(tunKey->dst == fwdInfo->dstIpAddr);
240         ASSERT(tunKey->src == fwdInfo->srcIpAddr || tunKey->src == 0);
241         ipHdr->saddr = fwdInfo->srcIpAddr;
242         ipHdr->daddr = fwdInfo->dstIpAddr;
243
244         ipHdr->check = 0;
245         ipHdr->check = IPChecksum((UINT8 *)ipHdr, sizeof *ipHdr, 0);
246 #if DBG
247         counterHeadRoom -= sizeof *ipHdr;
248 #endif
249
250         /* GRE header */
251         greHdr = (GREHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
252         greHdr->flags = OvsTunnelFlagsToGreFlags(tunKey->flags);
253         greHdr->protocolType = GRE_NET_TEB;
254 #if DBG
255         counterHeadRoom -= sizeof *greHdr;
256 #endif
257
258         PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr;
259
260         if (tunKey->flags & OVS_TNL_F_CSUM) {
261             RtlZeroMemory(currentOffset, 4);
262             currentOffset += 4;
263 #if DBG
264             counterHeadRoom -= 4;
265 #endif
266         }
267
268         if (tunKey->flags & OVS_TNL_F_KEY) {
269             RtlZeroMemory(currentOffset, 4);
270             UINT32 key = (tunKey->tunnelId >> 32);
271             RtlCopyMemory(currentOffset, &key, sizeof key);
272             currentOffset += 4;
273 #if DBG
274             counterHeadRoom -= 4;
275 #endif
276         }
277
278 #if DBG
279         ASSERT(counterHeadRoom == 0);
280 #endif
281
282     }
283     return STATUS_SUCCESS;
284
285 ret_error:
286     OvsCompleteNBL(switchContext, *newNbl, TRUE);
287     *newNbl = NULL;
288     return status;
289 }
290
291 NDIS_STATUS
292 OvsDecapGre(POVS_SWITCH_CONTEXT switchContext,
293             PNET_BUFFER_LIST curNbl,
294             OvsIPv4TunnelKey *tunKey,
295             PNET_BUFFER_LIST *newNbl)
296 {
297     PNET_BUFFER curNb;
298     PMDL curMdl;
299     EthHdr *ethHdr;
300     IPHdr *ipHdr;
301     GREHdr *greHdr;
302     UINT32 tunnelSize = 0, packetLength = 0;
303     UINT32 headRoom = 0;
304     PUINT8 bufferStart;
305     NDIS_STATUS status;
306
307     curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
308     packetLength = NET_BUFFER_DATA_LENGTH(curNb);
309     tunnelSize = GreTunHdrSize(tunKey->flags);
310     if (packetLength <= tunnelSize) {
311         return NDIS_STATUS_INVALID_LENGTH;
312     }
313
314     /*
315      * Create a copy of the NBL so that we have all the headers in one MDL.
316      */
317     *newNbl = OvsPartialCopyNBL(switchContext, curNbl,
318                                 tunnelSize + OVS_DEFAULT_COPY_SIZE, 0,
319                                 TRUE /*copy NBL info */);
320
321     if (*newNbl == NULL) {
322         return NDIS_STATUS_RESOURCES;
323     }
324
325     curNbl = *newNbl;
326     curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
327     curMdl = NET_BUFFER_CURRENT_MDL(curNb);
328     bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority) +
329                   NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
330     if (!bufferStart) {
331         status = NDIS_STATUS_RESOURCES;
332         goto dropNbl;
333     }
334
335     ethHdr = (EthHdr *)bufferStart;
336     headRoom += sizeof *ethHdr;
337
338     ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr);
339     tunKey->src = ipHdr->saddr;
340     tunKey->dst = ipHdr->daddr;
341     tunKey->tos = ipHdr->tos;
342     tunKey->ttl = ipHdr->ttl;
343     tunKey->pad = 0;
344     headRoom += sizeof *ipHdr;
345
346     greHdr = (GREHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
347     headRoom += sizeof *greHdr;
348
349     /* Validate if GRE header protocol type. */
350     if (greHdr->protocolType != GRE_NET_TEB) {
351         status = STATUS_NDIS_INVALID_PACKET;
352         goto dropNbl;
353     }
354
355     PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr;
356
357     if (greHdr->flags & GRE_CSUM) {
358         tunKey->flags |= OVS_TNL_F_CSUM;
359         currentOffset += 4;
360         headRoom += 4;
361     }
362
363     if (greHdr->flags & GRE_KEY) {
364         tunKey->flags |= OVS_TNL_F_KEY;
365         UINT32 key = 0;
366         RtlCopyMemory(&key, currentOffset, 4);
367         tunKey->tunnelId = (UINT64)key << 32;
368         currentOffset += 4;
369         headRoom += 4;
370     }
371
372     /* Clear out the receive flag for the inner packet. */
373     NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = 0;
374     NdisAdvanceNetBufferDataStart(curNb, GreTunHdrSize(tunKey->flags), FALSE,
375                                   NULL);
376     ASSERT(headRoom == GreTunHdrSize(tunKey->flags));
377     return NDIS_STATUS_SUCCESS;
378
379 dropNbl:
380     OvsCompleteNBL(switchContext, *newNbl, TRUE);
381     *newNbl = NULL;
382     return status;
383 }