2c955e1e683f4ffabef7b8a2292789fdc7e7f428
[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     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
179 VOID
180 OvsParseTcp(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
194 VOID
195 OvsParseUdp(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
212 NDIS_STATUS
213 OvsParseIcmpV6(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
297 invalid:
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 }