]> git.proxmox.com Git - mirror_ovs.git/blame - datapath-windows/ovsext/PacketParser.c
datapath-windows: Support for IRP cancelling mechanism
[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,
87 OvsFlowKey *key,
88 POVS_PACKET_HDR_INFO layers)
89{
90 UINT16 ofs = layers->l3Offset;
91 IPv6Hdr ipv6HdrStorage;
92 const IPv6Hdr *nh;
93 UINT32 nextHdr;
94 Ipv6Key *flow= &key->ipv6Key;
95
96 ofs = layers->l3Offset;
97 nh = OvsGetPacketBytes(packet, sizeof *nh, ofs, &ipv6HdrStorage);
98 if (!nh) {
99 return NDIS_STATUS_FAILURE;
100 }
101
102 nextHdr = nh->nexthdr;
103 memcpy(&flow->ipv6Src, nh->saddr.s6_addr, 16);
104 memcpy(&flow->ipv6Dst, nh->daddr.s6_addr, 16);
105
106 flow->nwTos = ((nh->flow_lbl[0] & 0xF0) >> 4) | (nh->priority << 4);
107 flow->ipv6Label =
108 ((nh->flow_lbl[0] & 0x0F) << 16) | (nh->flow_lbl[1] << 8) | nh->flow_lbl[2];
109 flow->nwTtl = nh->hop_limit;
110 flow->nwProto = SOCKET_IPPROTO_NONE;
111 flow->nwFrag = 0;
112
113 // Parse extended headers and compute L4 offset
114 ofs += sizeof(IPv6Hdr);
115 for (;;) {
116 if ((nextHdr != SOCKET_IPPROTO_HOPOPTS)
117 && (nextHdr != SOCKET_IPPROTO_ROUTING)
118 && (nextHdr != SOCKET_IPPROTO_DSTOPTS)
119 && (nextHdr != SOCKET_IPPROTO_AH)
120 && (nextHdr != SOCKET_IPPROTO_FRAGMENT)) {
121 /*
122 * It's either a terminal header (e.g., TCP, UDP) or one we
123 * don't understand. In either case, we're done with the
124 * packet, so use it to fill in 'nw_proto'.
125 */
126 break;
127 }
128
129 if (nextHdr == SOCKET_IPPROTO_HOPOPTS
130 || nextHdr == SOCKET_IPPROTO_ROUTING
131 || nextHdr == SOCKET_IPPROTO_DSTOPTS
132 || nextHdr == SOCKET_IPPROTO_AH) {
133 IPv6ExtHdr extHdrStorage;
134 const IPv6ExtHdr *extHdr;
135 UINT8 len;
136
137 extHdr = OvsGetPacketBytes(packet, sizeof *extHdr, ofs, &extHdrStorage);
138 if (!extHdr) {
139 return NDIS_STATUS_FAILURE;
140 }
141
142 len = extHdr->hdrExtLen;
143 ofs += nextHdr == SOCKET_IPPROTO_AH ? (len + 2) * 4 : (len + 1) * 8;
144 nextHdr = extHdr->nextHeader;
145 if (OvsPacketLenNBL(packet) < ofs) {
146 return NDIS_STATUS_FAILURE;
147 }
148 } else if (nextHdr == SOCKET_IPPROTO_FRAGMENT) {
149 IPv6FragHdr fragHdrStorage;
150 const IPv6FragHdr *fragHdr;
151
152 fragHdr = OvsGetPacketBytes(packet, sizeof *fragHdr, ofs,
153 &fragHdrStorage);
154 if (!fragHdr) {
155 return NDIS_STATUS_FAILURE;
156 }
157
158 nextHdr = fragHdr->nextHeader;
159 ofs += sizeof *fragHdr;
160
161 /* We only process the first fragment. */
162 if (fragHdr->offlg != htons(0)) {
163 if ((fragHdr->offlg & IP6F_OFF_HOST_ORDER_MASK) == htons(0)) {
164 flow->nwFrag = OVSWIN_NW_FRAG_ANY;
165 } else {
166 flow->nwFrag |= OVSWIN_NW_FRAG_LATER;
167 nextHdr = SOCKET_IPPROTO_FRAGMENT;
168 break;
169 }
170 }
171 }
172 }
173
174 flow->nwProto = (UINT8)nextHdr;
175 layers->l4Offset = ofs;
176 return NDIS_STATUS_SUCCESS;
177}
178
179VOID
180OvsParseTcp(const NET_BUFFER_LIST *packet,
181 L4Key *flow,
182 POVS_PACKET_HDR_INFO layers)
183{
184 TCPHdr tcpStorage;
185 const TCPHdr *tcp = OvsGetTcp(packet, layers->l4Offset, &tcpStorage);
186 if (tcp) {
187 flow->tpSrc = tcp->source;
188 flow->tpDst = tcp->dest;
189 layers->isTcp = 1;
190 layers->l7Offset = layers->l4Offset + 4 * tcp->doff;
191 }
192}
193
194VOID
195OvsParseUdp(const NET_BUFFER_LIST *packet,
196 L4Key *flow,
197 POVS_PACKET_HDR_INFO layers)
198{
199 UDPHdr udpStorage;
200 const UDPHdr *udp = OvsGetUdp(packet, layers->l4Offset, &udpStorage);
201 if (udp) {
202 flow->tpSrc = udp->source;
203 flow->tpDst = udp->dest;
204 layers->isUdp = 1;
205 if (udp->check == 0) {
206 layers->udpCsumZero = 1;
207 }
208 layers->l7Offset = layers->l4Offset + sizeof *udp;
209 }
210}
211
212NDIS_STATUS
213OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
214 OvsFlowKey *key,
215 POVS_PACKET_HDR_INFO layers)
216{
217 UINT16 ofs = layers->l4Offset;
218 ICMPHdr icmpStorage;
219 const ICMPHdr *icmp;
220 Icmp6Key *flow = &key->icmp6Key;
221
222 memset(&flow->ndTarget, 0, sizeof(flow->ndTarget));
223 memset(flow->arpSha, 0, sizeof(flow->arpSha));
224 memset(flow->arpTha, 0, sizeof(flow->arpTha));
225
226 icmp = OvsGetIcmp(packet, ofs, &icmpStorage);
227 if (!icmp) {
228 return NDIS_STATUS_FAILURE;
229 }
230 ofs += sizeof *icmp;
231
232 /*
233 * The ICMPv6 type and code fields use the 16-bit transport port
234 * fields, so we need to store them in 16-bit network byte order.
235 */
236 key->ipv6Key.l4.tpSrc = htons(icmp->type);
237 key->ipv6Key.l4.tpDst = htons(icmp->code);
238
239 if (icmp->code == 0 &&
240 (icmp->type == ND_NEIGHBOR_SOLICIT ||
241 icmp->type == ND_NEIGHBOR_ADVERT)) {
242 struct in6_addr ndTargetStorage;
243 const struct in6_addr *ndTarget;
244
245 ndTarget = OvsGetPacketBytes(packet, sizeof *ndTarget, ofs,
246 &ndTargetStorage);
247 if (!ndTarget) {
248 return NDIS_STATUS_FAILURE;
249 }
250 flow->ndTarget = *ndTarget;
251
252 while ((UINT32)(ofs + 8) <= OvsPacketLenNBL(packet)) {
253 /*
254 * The minimum size of an option is 8 bytes, which also is
255 * the size of Ethernet link-layer options.
256 */
257 IPv6NdOptHdr ndOptStorage;
258 const IPv6NdOptHdr *ndOpt;
259 UINT16 optLen;
260
261 ndOpt = OvsGetPacketBytes(packet, sizeof *ndOpt, ofs, &ndOptStorage);
262 if (!ndOpt) {
263 return NDIS_STATUS_FAILURE;
264 }
265
266 optLen = ndOpt->len * 8;
267 if (!optLen || (UINT32)(ofs + optLen) > OvsPacketLenNBL(packet)) {
268 goto invalid;
269 }
270
271 /*
272 * Store the link layer address if the appropriate option is
273 * provided. It is considered an error if the same link
274 * layer option is specified twice.
275 */
276 if (ndOpt->type == ND_OPT_SOURCE_LINKADDR && optLen == 8) {
277 if (Eth_IsNullAddr(flow->arpSha)) {
278 memcpy(flow->arpSha, ndOpt + 1, ETH_ADDR_LENGTH);
279 } else {
280 goto invalid;
281 }
282 } else if (ndOpt->type == ND_OPT_TARGET_LINKADDR && optLen == 8) {
283 if (Eth_IsNullAddr(flow->arpTha)) {
284 memcpy(flow->arpTha, ndOpt + 1, ETH_ADDR_LENGTH);
285 } else {
286 goto invalid;
287 }
288 }
289
290 ofs += optLen;
291 }
292 }
293
294 layers->l7Offset = ofs;
295 return NDIS_STATUS_SUCCESS;
296
297invalid:
298 memset(&flow->ndTarget, 0, sizeof(flow->ndTarget));
299 memset(flow->arpSha, 0, sizeof(flow->arpSha));
300 memset(flow->arpTha, 0, sizeof(flow->arpTha));
301
302 return NDIS_STATUS_FAILURE;
303}