2 * Copyright (c) 2015 VMware, Inc.
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.
28 #include "PacketParser.h"
35 #define OVS_DBG_MOD OVS_DBG_STT
39 OvsDoEncapStt(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 STT tunnel module.
50 * --------------------------------------------------------------------------
53 OvsInitSttTunnel(POVS_VPORT_ENTRY vport,
56 POVS_STT_VPORT sttPort;
58 sttPort = (POVS_STT_VPORT) OvsAllocateMemoryWithTag(sizeof(*sttPort),
61 OVS_LOG_ERROR("Insufficient memory, can't allocate STT_VPORT");
62 return STATUS_INSUFFICIENT_RESOURCES;
65 RtlZeroMemory(sttPort, sizeof(*sttPort));
66 sttPort->dstPort = tcpDestPort;
67 vport->priv = (PVOID) sttPort;
68 return STATUS_SUCCESS;
72 * --------------------------------------------------------------------------
73 * OvsCleanupSttTunnel --
74 * Cleanup STT Tunnel module.
75 * --------------------------------------------------------------------------
78 OvsCleanupSttTunnel(POVS_VPORT_ENTRY vport)
80 if (vport->ovsType != OVS_VPORT_TYPE_STT ||
81 vport->priv == NULL) {
85 OvsFreeMemoryWithTag(vport->priv, OVS_STT_POOL_TAG);
90 * --------------------------------------------------------------------------
92 * Encapsulates a packet with an STT header.
93 * --------------------------------------------------------------------------
96 OvsEncapStt(POVS_VPORT_ENTRY vport,
97 PNET_BUFFER_LIST curNbl,
98 OvsIPv4TunnelKey *tunKey,
99 POVS_SWITCH_CONTEXT switchContext,
100 POVS_PACKET_HDR_INFO layers,
101 PNET_BUFFER_LIST *newNbl)
103 OVS_FWD_INFO fwdInfo;
106 UNREFERENCED_PARAMETER(switchContext);
107 status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
108 if (status != STATUS_SUCCESS) {
109 OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
111 * XXX This case where the ARP table is not populated is
112 * currently not handled
114 return NDIS_STATUS_FAILURE;
117 status = OvsDoEncapStt(vport, curNbl, tunKey, &fwdInfo, layers, switchContext,
123 * --------------------------------------------------------------------------
125 * Internal utility function which actually does the STT encap.
126 * --------------------------------------------------------------------------
129 OvsDoEncapStt(POVS_VPORT_ENTRY vport,
130 PNET_BUFFER_LIST curNbl,
131 const OvsIPv4TunnelKey *tunKey,
132 const POVS_FWD_INFO fwdInfo,
133 POVS_PACKET_HDR_INFO layers,
134 POVS_SWITCH_CONTEXT switchContext,
135 PNET_BUFFER_LIST *newNbl)
137 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
145 UINT32 innerFrameLen, ipTotalLen;
146 POVS_STT_VPORT vportStt;
147 UINT32 headRoom = OvsGetSttTunHdrSize();
150 UNREFERENCED_PARAMETER(layers);
152 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
154 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO lsoInfo;
156 lsoInfo.Value = NET_BUFFER_LIST_INFO(curNbl,
157 TcpLargeSendNetBufferListInfo);
158 if (lsoInfo.LsoV1Transmit.MSS) {
159 /* XXX We don't handle LSO yet */
160 OVS_LOG_ERROR("LSO on STT is not supported");
161 return NDIS_STATUS_FAILURE;
165 vportStt = (POVS_STT_VPORT) GetOvsVportPriv(vport);
168 *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom,
169 FALSE /*copy NblInfo*/);
170 if (*newNbl == NULL) {
171 OVS_LOG_ERROR("Unable to copy NBL");
172 return NDIS_STATUS_FAILURE;
176 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
177 /* NB Chain should be split before */
178 ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL);
180 innerFrameLen = NET_BUFFER_DATA_LENGTH(curNb);
182 * External port can't be removed as we hold the dispatch lock
183 * We also check if the external port was removed beforecalling
184 * port encapsulation functions
186 if (innerFrameLen > OvsGetExternalMtu(switchContext) - headRoom) {
187 OVS_LOG_ERROR("Packet too large (size %d, mtu %d). Can't encapsulate",
188 innerFrameLen, OvsGetExternalMtu(switchContext));
189 status = NDIS_STATUS_FAILURE;
193 status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL);
194 if (status != NDIS_STATUS_SUCCESS) {
195 ASSERT(!"Unable to NdisRetreatNetBufferDataStart(headroom)");
196 OVS_LOG_ERROR("Unable to NdisRetreatNetBufferDataStart(headroom)");
201 * Make sure that the headroom for the tunnel header is continguous in
204 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
205 ASSERT((int) (MmGetMdlByteCount(curMdl) - NET_BUFFER_CURRENT_MDL_OFFSET(curNb))
208 buf = (PUINT8) MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority);
210 ASSERT(!"MmGetSystemAddressForMdlSafe failed");
211 OVS_LOG_ERROR("MmGetSystemAddressForMdlSafe failed");
212 status = NDIS_STATUS_RESOURCES;
216 buf += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
217 outerEthHdr = (EthHdr *)buf;
218 outerIpHdr = (IPHdr *) (outerEthHdr + 1);
219 outerTcpHdr = (TCPHdr *) (outerIpHdr + 1);
220 sttHdr = (SttHdr *) (outerTcpHdr + 1);
223 ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) ==
224 (PCHAR)&fwdInfo->srcMacAddr);
225 NdisMoveMemory(outerEthHdr->Destination, fwdInfo->dstMacAddr,
226 sizeof outerEthHdr->Destination + sizeof outerEthHdr->Source);
227 outerEthHdr->Type = htons(ETH_TYPE_IPV4);
230 outerIpHdr->ihl = sizeof(IPHdr) >> 2;
231 outerIpHdr->version = IPPROTO_IPV4;
232 outerIpHdr->tos = tunKey->tos;
234 ipTotalLen = sizeof(IPHdr) + sizeof(TCPHdr) + STT_HDR_LEN + innerFrameLen;
235 outerIpHdr->tot_len = htons(ipTotalLen);
236 ASSERT(ipTotalLen < 65536);
238 outerIpHdr->id = (uint16) atomic_add64(&vportStt->ipId, innerFrameLen);
239 outerIpHdr->frag_off = (tunKey->flags & OVS_TNL_F_DONT_FRAGMENT) ?
241 outerIpHdr->ttl = tunKey->ttl? tunKey->ttl : 64;
242 outerIpHdr->protocol = IPPROTO_TCP;
243 outerIpHdr->check = 0;
244 outerIpHdr->saddr = fwdInfo->srcIpAddr;
245 outerIpHdr->daddr = tunKey->dst;
246 outerIpHdr->check = IPChecksum((uint8 *)outerIpHdr, sizeof *outerIpHdr, 0);
249 RtlZeroMemory(outerTcpHdr, sizeof *outerTcpHdr);
250 outerTcpHdr->source = htons(tunKey->flow_hash | 32768);
251 outerTcpHdr->dest = htons(vportStt->dstPort);
252 outerTcpHdr->seq = htonl((STT_HDR_LEN + innerFrameLen) <<
254 outerTcpHdr->ack_seq = htonl(atomic_inc64(&vportStt->ackNo));
255 outerTcpHdr->doff = sizeof(TCPHdr) >> 2;
256 outerTcpHdr->psh = 1;
257 outerTcpHdr->ack = 1;
258 outerTcpHdr->window = (uint16) ~0;
260 /* Calculate pseudo header chksum */
261 tcpChksumLen = sizeof(TCPHdr) + STT_HDR_LEN + innerFrameLen;
262 ASSERT(tcpChksumLen < 65535);
263 outerTcpHdr->check = IPPseudoChecksum(&fwdInfo->srcIpAddr,(uint32 *) &tunKey->dst,
264 IPPROTO_TCP, (uint16) tcpChksumLen);
267 /* XXX need to peek into the inner packet, hard code for now */
268 sttHdr->flags = STT_PROTO_IPV4;
269 sttHdr->l4Offset = 0;
271 sttHdr->reserved = 0;
272 /* XXX Used for large TCP packets.Not sure how it is used, clarify */
275 sttHdr->key = tunKey->tunnelId;
276 /* Zero out stt padding */
277 *(uint16 *)(sttHdr + 1) = 0;
279 /* Calculate software tcp checksum */
280 outerTcpHdr->check = CalculateChecksumNB(curNb, (uint16) tcpChksumLen,
281 sizeof(EthHdr) + sizeof(IPHdr));
282 if (outerTcpHdr->check == 0) {
283 status = NDIS_STATUS_FAILURE;
287 return STATUS_SUCCESS;
290 OvsCompleteNBL(switchContext, *newNbl, TRUE);
296 * --------------------------------------------------------------------------
298 * Decapsulates an STT packet.
299 * --------------------------------------------------------------------------
302 OvsDecapStt(POVS_SWITCH_CONTEXT switchContext,
303 PNET_BUFFER_LIST curNbl,
304 OvsIPv4TunnelKey *tunKey,
305 PNET_BUFFER_LIST *newNbl)
307 NDIS_STATUS status = NDIS_STATUS_FAILURE;
310 char *ipBuf[sizeof(IPHdr)];
312 char *sttBuf[STT_HDR_LEN];
313 UINT32 advanceCnt, hdrLen;
315 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
316 ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL);
318 if (NET_BUFFER_DATA_LENGTH(curNb) < OvsGetSttTunHdrSize()) {
319 OVS_LOG_ERROR("Packet length received is less than the tunnel header:"
320 " %d<%d\n", NET_BUFFER_DATA_LENGTH(curNb), OvsGetSttTunHdrSize());
321 return NDIS_STATUS_INVALID_LENGTH;
324 /* Skip Eth header */
325 hdrLen = sizeof(EthHdr);
326 NdisAdvanceNetBufferDataStart(curNb, hdrLen, FALSE, NULL);
329 ipHdr = NdisGetDataBuffer(curNb, sizeof *ipHdr, (PVOID) &ipBuf,
333 /* Skip IP & TCP headers */
334 hdrLen = sizeof(IPHdr) + sizeof(TCPHdr),
335 NdisAdvanceNetBufferDataStart(curNb, hdrLen, FALSE, NULL);
336 advanceCnt += hdrLen;
339 sttHdr = NdisGetDataBuffer(curNb, sizeof *sttHdr, (PVOID) &sttBuf,
343 /* Initialize the tunnel key */
344 tunKey->dst = ipHdr->daddr;
345 tunKey->src = ipHdr->saddr;
346 tunKey->tunnelId = sttHdr->key;
347 tunKey->flags = (OVS_TNL_F_CSUM | OVS_TNL_F_KEY);
348 tunKey->tos = ipHdr->tos;
349 tunKey->ttl = ipHdr->ttl;
352 /* Skip stt header, DataOffset points to inner pkt now. */
353 hdrLen = STT_HDR_LEN;
354 NdisAdvanceNetBufferDataStart(curNb, hdrLen, FALSE, NULL);
355 advanceCnt += hdrLen;
357 *newNbl = OvsPartialCopyNBL(switchContext, curNbl, OVS_DEFAULT_COPY_SIZE,
358 0, FALSE /*copy NBL info*/);
360 ASSERT(advanceCnt == OvsGetSttTunHdrSize());
361 status = NdisRetreatNetBufferDataStart(curNb, advanceCnt, 0, NULL);
363 if (*newNbl == NULL) {
364 OVS_LOG_ERROR("OvsDecapStt: Unable to allocate a new cloned NBL");
365 status = NDIS_STATUS_RESOURCES;