]>
Commit | Line | Data |
---|---|---|
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 | |
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, | |
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 | */ | |
124 | NDIS_STATUS | |
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) | |
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 | ||
301 | ret_error: | |
302 | OvsCompleteNBL(switchContext, *newNbl, TRUE); | |
303 | *newNbl = NULL; | |
304 | return status; | |
305 | } | |
306 | ||
307 | NDIS_STATUS | |
308 | OvsDecapGre(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 |
441 | end: |
442 | if (tempBuf) { | |
443 | OvsFreeMemoryWithTag(tempBuf, OVS_GRE_POOL_TAG); | |
444 | tempBuf = NULL; | |
445 | } | |
85571a3d AS |
446 | return status; |
447 | } |