]> git.proxmox.com Git - mirror_ovs.git/blame - datapath-windows/ovsext/PacketParser.c
datapath-windows: Improved offloading on STT tunnel
[mirror_ovs.git] / datapath-windows / ovsext / PacketParser.c
CommitLineData
c803536e
SS
1/*
2 * Copyright (c) 2014 VMware, Inc.
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
fa1324c9 17#include "PacketParser.h"
c803536e
SS
18
19//XXX consider moving to NdisGetDataBuffer.
20const VOID *
21OvsGetPacketBytes(const NET_BUFFER_LIST *nbl,
22 UINT32 len,
23 UINT32 srcOffset,
24 VOID *storage)
25{
26 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
27 PNET_BUFFER netBuffer = NET_BUFFER_LIST_FIRST_NB(nbl);
28 PMDL currentMdl;
29 BOOLEAN firstMDL = TRUE;
30 ULONG destOffset = 0;
31 VOID *dest = storage;
32 const UINT32 copyLen = len;
33 ULONG packetLen;
34
35 packetLen = NET_BUFFER_DATA_LENGTH(netBuffer);
36 // Start copy from current MDL
37 currentMdl = NET_BUFFER_CURRENT_MDL(netBuffer);
38
39 // Data on current MDL may be offset from start of MDL
40 while (destOffset < copyLen && currentMdl) {
41 PUCHAR srcMemory = MmGetSystemAddressForMdlSafe(currentMdl,
42 LowPagePriority);
43 ULONG length = MmGetMdlByteCount(currentMdl);
44 if (!srcMemory) {
45 status = NDIS_STATUS_RESOURCES;
46 break;
47 }
48
49 if (firstMDL) {
50 ULONG mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(netBuffer);
51 srcMemory += mdlOffset;
52 length -= mdlOffset;
53 firstMDL = FALSE;
54 }
55 length = MIN(length, packetLen);
56 packetLen -= length;
57 ASSERT((INT)packetLen >= 0);
58
59 if (srcOffset >= length) {
60 currentMdl = NDIS_MDL_LINKAGE(currentMdl);
61 srcOffset -= length;
62 continue;
63 } else {
64 srcMemory += srcOffset;
65 length -= srcOffset;
66 srcOffset = 0;
67 }
68
69 length = min(length, copyLen-destOffset);
70
71 NdisMoveMemory((PUCHAR)dest+destOffset, srcMemory, length);
72 destOffset += length;
73
74 currentMdl = NDIS_MDL_LINKAGE(currentMdl);
75 }
76
77 if (destOffset == copyLen) {
78 ASSERT(status == NDIS_STATUS_SUCCESS);
79 return storage;
80 }
81
82 return NULL;
83}
84
85NDIS_STATUS
86OvsParseIPv6(const NET_BUFFER_LIST *packet,
c3e85147 87 Ipv6Key *ipv6Key,
5874d571 88 POVS_PACKET_HDR_INFO layers)
c803536e
SS
89{
90 UINT16 ofs = layers->l3Offset;
91 IPv6Hdr ipv6HdrStorage;
92 const IPv6Hdr *nh;
93 UINT32 nextHdr;
c803536e 94
c803536e
SS
95 nh = OvsGetPacketBytes(packet, sizeof *nh, ofs, &ipv6HdrStorage);
96 if (!nh) {
97 return NDIS_STATUS_FAILURE;
98 }
99
100 nextHdr = nh->nexthdr;
c3e85147
PB
101 RtlCopyMemory(&ipv6Key->ipv6Src, nh->saddr.s6_addr, 16);
102 RtlCopyMemory(&ipv6Key->ipv6Dst, nh->daddr.s6_addr, 16);
c803536e 103
c3e85147
PB
104 ipv6Key->nwTos = ((nh->flow_lbl[0] & 0xF0) >> 4) | (nh->priority << 4);
105 ipv6Key->ipv6Label =
c803536e 106 ((nh->flow_lbl[0] & 0x0F) << 16) | (nh->flow_lbl[1] << 8) | nh->flow_lbl[2];
c3e85147
PB
107 ipv6Key->nwTtl = nh->hop_limit;
108 ipv6Key->nwProto = SOCKET_IPPROTO_NONE;
109 ipv6Key->nwFrag = OVS_FRAG_TYPE_NONE;
c803536e
SS
110
111 // Parse extended headers and compute L4 offset
112 ofs += sizeof(IPv6Hdr);
113 for (;;) {
114 if ((nextHdr != SOCKET_IPPROTO_HOPOPTS)
115 && (nextHdr != SOCKET_IPPROTO_ROUTING)
116 && (nextHdr != SOCKET_IPPROTO_DSTOPTS)
117 && (nextHdr != SOCKET_IPPROTO_AH)
118 && (nextHdr != SOCKET_IPPROTO_FRAGMENT)) {
119 /*
120 * It's either a terminal header (e.g., TCP, UDP) or one we
121 * don't understand. In either case, we're done with the
122 * packet, so use it to fill in 'nw_proto'.
123 */
124 break;
125 }
126
127 if (nextHdr == SOCKET_IPPROTO_HOPOPTS
128 || nextHdr == SOCKET_IPPROTO_ROUTING
129 || nextHdr == SOCKET_IPPROTO_DSTOPTS
130 || nextHdr == SOCKET_IPPROTO_AH) {
131 IPv6ExtHdr extHdrStorage;
132 const IPv6ExtHdr *extHdr;
133 UINT8 len;
134
135 extHdr = OvsGetPacketBytes(packet, sizeof *extHdr, ofs, &extHdrStorage);
136 if (!extHdr) {
137 return NDIS_STATUS_FAILURE;
138 }
139
140 len = extHdr->hdrExtLen;
141 ofs += nextHdr == SOCKET_IPPROTO_AH ? (len + 2) * 4 : (len + 1) * 8;
142 nextHdr = extHdr->nextHeader;
143 if (OvsPacketLenNBL(packet) < ofs) {
144 return NDIS_STATUS_FAILURE;
145 }
146 } else if (nextHdr == SOCKET_IPPROTO_FRAGMENT) {
147 IPv6FragHdr fragHdrStorage;
148 const IPv6FragHdr *fragHdr;
149
150 fragHdr = OvsGetPacketBytes(packet, sizeof *fragHdr, ofs,
151 &fragHdrStorage);
152 if (!fragHdr) {
153 return NDIS_STATUS_FAILURE;
154 }
155
156 nextHdr = fragHdr->nextHeader;
157 ofs += sizeof *fragHdr;
158
159 /* We only process the first fragment. */
160 if (fragHdr->offlg != htons(0)) {
161 if ((fragHdr->offlg & IP6F_OFF_HOST_ORDER_MASK) == htons(0)) {
c3e85147 162 ipv6Key->nwFrag = OVS_FRAG_TYPE_FIRST;
c803536e 163 } else {
c3e85147 164 ipv6Key->nwFrag = OVS_FRAG_TYPE_LATER;
c803536e
SS
165 nextHdr = SOCKET_IPPROTO_FRAGMENT;
166 break;
167 }
168 }
169 }
170 }
171
c3e85147 172 ipv6Key->nwProto = (UINT8)nextHdr;
c803536e
SS
173 layers->l4Offset = ofs;
174 return NDIS_STATUS_SUCCESS;
175}
176
177VOID
178OvsParseTcp(const NET_BUFFER_LIST *packet,
5874d571
SV
179 L4Key *flow,
180 POVS_PACKET_HDR_INFO layers)
c803536e
SS
181{
182 TCPHdr tcpStorage;
183 const TCPHdr *tcp = OvsGetTcp(packet, layers->l4Offset, &tcpStorage);
184 if (tcp) {
c3e85147
PB
185 if (flow) {
186 flow->tpSrc = tcp->source;
187 flow->tpDst = tcp->dest;
188 }
189 if (layers) {
190 layers->isTcp = 1;
191 layers->l7Offset = layers->l4Offset + 4 * tcp->doff;
192 }
c803536e
SS
193 }
194}
195
efee3309
SV
196VOID
197OvsParseSctp(const NET_BUFFER_LIST *packet,
198 L4Key *flow,
199 POVS_PACKET_HDR_INFO layers)
200{
201 SCTPHdr sctpStorage;
202 const SCTPHdr *sctp = OvsGetSctp(packet, layers->l4Offset, &sctpStorage);
203 if (sctp) {
c3e85147
PB
204 if (flow) {
205 flow->tpSrc = sctp->source;
206 flow->tpDst = sctp->dest;
207 }
208 if (layers) {
209 layers->isSctp = 1;
210 layers->l7Offset = layers->l4Offset + sizeof *sctp;
211 }
efee3309
SV
212 }
213}
214
c803536e
SS
215VOID
216OvsParseUdp(const NET_BUFFER_LIST *packet,
5874d571
SV
217 L4Key *flow,
218 POVS_PACKET_HDR_INFO layers)
c803536e
SS
219{
220 UDPHdr udpStorage;
221 const UDPHdr *udp = OvsGetUdp(packet, layers->l4Offset, &udpStorage);
222 if (udp) {
c3e85147
PB
223 if (flow) {
224 flow->tpSrc = udp->source;
225 flow->tpDst = udp->dest;
226 }
227 if (layers) {
228 layers->isUdp = 1;
229 if (udp->check == 0) {
230 layers->udpCsumZero = 1;
231 }
232 layers->l7Offset = layers->l4Offset + sizeof *udp;
c803536e 233 }
c803536e
SS
234 }
235}
236
237NDIS_STATUS
238OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
c3e85147
PB
239 Ipv6Key *ipv6Key,
240 Icmp6Key *icmp6Key,
241 POVS_PACKET_HDR_INFO layers)
c803536e
SS
242{
243 UINT16 ofs = layers->l4Offset;
244 ICMPHdr icmpStorage;
245 const ICMPHdr *icmp;
c803536e 246
c3e85147
PB
247 memset(&icmp6Key->ndTarget, 0, sizeof(icmp6Key->ndTarget));
248 memset(icmp6Key->arpSha, 0, sizeof(icmp6Key->arpSha));
249 memset(icmp6Key->arpTha, 0, sizeof(icmp6Key->arpTha));
c803536e
SS
250
251 icmp = OvsGetIcmp(packet, ofs, &icmpStorage);
252 if (!icmp) {
253 return NDIS_STATUS_FAILURE;
254 }
255 ofs += sizeof *icmp;
256
257 /*
258 * The ICMPv6 type and code fields use the 16-bit transport port
259 * fields, so we need to store them in 16-bit network byte order.
260 */
c3e85147
PB
261 if (ipv6Key) {
262 ipv6Key->l4.tpSrc = htons(icmp->type);
263 ipv6Key->l4.tpDst = htons(icmp->code);
264 }
c803536e
SS
265
266 if (icmp->code == 0 &&
267 (icmp->type == ND_NEIGHBOR_SOLICIT ||
268 icmp->type == ND_NEIGHBOR_ADVERT)) {
269 struct in6_addr ndTargetStorage;
270 const struct in6_addr *ndTarget;
271
272 ndTarget = OvsGetPacketBytes(packet, sizeof *ndTarget, ofs,
273 &ndTargetStorage);
274 if (!ndTarget) {
275 return NDIS_STATUS_FAILURE;
276 }
c3e85147 277 icmp6Key->ndTarget = *ndTarget;
c803536e
SS
278
279 while ((UINT32)(ofs + 8) <= OvsPacketLenNBL(packet)) {
280 /*
281 * The minimum size of an option is 8 bytes, which also is
282 * the size of Ethernet link-layer options.
283 */
284 IPv6NdOptHdr ndOptStorage;
285 const IPv6NdOptHdr *ndOpt;
286 UINT16 optLen;
287
288 ndOpt = OvsGetPacketBytes(packet, sizeof *ndOpt, ofs, &ndOptStorage);
289 if (!ndOpt) {
290 return NDIS_STATUS_FAILURE;
291 }
292
293 optLen = ndOpt->len * 8;
294 if (!optLen || (UINT32)(ofs + optLen) > OvsPacketLenNBL(packet)) {
295 goto invalid;
296 }
297
298 /*
299 * Store the link layer address if the appropriate option is
300 * provided. It is considered an error if the same link
301 * layer option is specified twice.
302 */
303 if (ndOpt->type == ND_OPT_SOURCE_LINKADDR && optLen == 8) {
c3e85147
PB
304 if (Eth_IsNullAddr(icmp6Key->arpSha)) {
305 memcpy(icmp6Key->arpSha, ndOpt + 1, ETH_ADDR_LENGTH);
c803536e
SS
306 } else {
307 goto invalid;
308 }
309 } else if (ndOpt->type == ND_OPT_TARGET_LINKADDR && optLen == 8) {
c3e85147
PB
310 if (Eth_IsNullAddr(icmp6Key->arpTha)) {
311 memcpy(icmp6Key->arpTha, ndOpt + 1, ETH_ADDR_LENGTH);
c803536e
SS
312 } else {
313 goto invalid;
314 }
315 }
316
317 ofs += optLen;
318 }
319 }
320
321 layers->l7Offset = ofs;
322 return NDIS_STATUS_SUCCESS;
323
324invalid:
c3e85147
PB
325 RtlZeroMemory(&icmp6Key->ndTarget, sizeof(icmp6Key->ndTarget));
326 RtlZeroMemory(icmp6Key->arpSha, sizeof(icmp6Key->arpSha));
327 RtlZeroMemory(icmp6Key->arpTha, sizeof(icmp6Key->arpTha));
c803536e
SS
328
329 return NDIS_STATUS_FAILURE;
330}