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