]>
Commit | Line | Data |
---|---|---|
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. | |
20 | const VOID * | |
21 | OvsGetPacketBytes(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 | ||
85 | NDIS_STATUS | |
86 | OvsParseIPv6(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 | ||
177 | VOID | |
178 | OvsParseTcp(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 |
196 | VOID |
197 | OvsParseSctp(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 |
215 | VOID |
216 | OvsParseUdp(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 | ||
237 | NDIS_STATUS | |
238 | OvsParseIcmpV6(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 | ||
324 | invalid: | |
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 | } |