/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PacketParser.h" //XXX consider moving to NdisGetDataBuffer. const VOID * OvsGetPacketBytes(const NET_BUFFER_LIST *nbl, UINT32 len, UINT32 srcOffset, VOID *storage) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; PNET_BUFFER netBuffer = NET_BUFFER_LIST_FIRST_NB(nbl); PMDL currentMdl; BOOLEAN firstMDL = TRUE; ULONG destOffset = 0; VOID *dest = storage; const UINT32 copyLen = len; ULONG packetLen; packetLen = NET_BUFFER_DATA_LENGTH(netBuffer); // Start copy from current MDL currentMdl = NET_BUFFER_CURRENT_MDL(netBuffer); // Data on current MDL may be offset from start of MDL while (destOffset < copyLen && currentMdl) { PUCHAR srcMemory = OvsGetMdlWithLowPriority(currentMdl); ULONG length = MmGetMdlByteCount(currentMdl); if (!srcMemory) { status = NDIS_STATUS_RESOURCES; break; } if (firstMDL) { ULONG mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(netBuffer); srcMemory += mdlOffset; length -= mdlOffset; firstMDL = FALSE; } length = MIN(length, packetLen); packetLen -= length; ASSERT((INT)packetLen >= 0); if (srcOffset >= length) { currentMdl = NDIS_MDL_LINKAGE(currentMdl); srcOffset -= length; continue; } else { srcMemory += srcOffset; length -= srcOffset; srcOffset = 0; } length = min(length, copyLen-destOffset); NdisMoveMemory((PUCHAR)dest+destOffset, srcMemory, length); destOffset += length; currentMdl = NDIS_MDL_LINKAGE(currentMdl); } if (destOffset == copyLen) { ASSERT(status == NDIS_STATUS_SUCCESS); return storage; } return NULL; } NDIS_STATUS OvsParseIPv6(const NET_BUFFER_LIST *packet, Ipv6Key *ipv6Key, POVS_PACKET_HDR_INFO layers) { UINT16 ofs = layers->l3Offset; IPv6Hdr ipv6HdrStorage; const IPv6Hdr *nh; UINT32 nextHdr; nh = OvsGetPacketBytes(packet, sizeof *nh, ofs, &ipv6HdrStorage); if (!nh) { return NDIS_STATUS_FAILURE; } nextHdr = nh->nexthdr; RtlCopyMemory(&ipv6Key->ipv6Src, nh->saddr.s6_addr, 16); RtlCopyMemory(&ipv6Key->ipv6Dst, nh->daddr.s6_addr, 16); ipv6Key->nwTos = ((nh->flow_lbl[0] & 0xF0) >> 4) | (nh->priority << 4); ipv6Key->ipv6Label = ((nh->flow_lbl[0] & 0x0F) << 16) | (nh->flow_lbl[1] << 8) | nh->flow_lbl[2]; ipv6Key->nwTtl = nh->hop_limit; ipv6Key->nwProto = SOCKET_IPPROTO_NONE; ipv6Key->nwFrag = OVS_FRAG_TYPE_NONE; // Parse extended headers and compute L4 offset ofs += sizeof(IPv6Hdr); for (;;) { if ((nextHdr != SOCKET_IPPROTO_HOPOPTS) && (nextHdr != SOCKET_IPPROTO_ROUTING) && (nextHdr != SOCKET_IPPROTO_DSTOPTS) && (nextHdr != SOCKET_IPPROTO_AH) && (nextHdr != SOCKET_IPPROTO_FRAGMENT)) { /* * It's either a terminal header (e.g., TCP, UDP) or one we * don't understand. In either case, we're done with the * packet, so use it to fill in 'nw_proto'. */ break; } if (nextHdr == SOCKET_IPPROTO_HOPOPTS || nextHdr == SOCKET_IPPROTO_ROUTING || nextHdr == SOCKET_IPPROTO_DSTOPTS || nextHdr == SOCKET_IPPROTO_AH) { IPv6ExtHdr extHdrStorage; const IPv6ExtHdr *extHdr; UINT8 len; extHdr = OvsGetPacketBytes(packet, sizeof *extHdr, ofs, &extHdrStorage); if (!extHdr) { return NDIS_STATUS_FAILURE; } len = extHdr->hdrExtLen; ofs += nextHdr == SOCKET_IPPROTO_AH ? (len + 2) * 4 : (len + 1) * 8; nextHdr = extHdr->nextHeader; if (OvsPacketLenNBL(packet) < ofs) { return NDIS_STATUS_FAILURE; } } else if (nextHdr == SOCKET_IPPROTO_FRAGMENT) { IPv6FragHdr fragHdrStorage; const IPv6FragHdr *fragHdr; fragHdr = OvsGetPacketBytes(packet, sizeof *fragHdr, ofs, &fragHdrStorage); if (!fragHdr) { return NDIS_STATUS_FAILURE; } nextHdr = fragHdr->nextHeader; ofs += sizeof *fragHdr; /* We only process the first fragment. */ if (fragHdr->offlg != htons(0)) { if ((ntohs(fragHdr->offlg) & IP6F_OFF_HOST_ORDER_MASK) == htons(0)) { ipv6Key->nwFrag = OVS_FRAG_TYPE_FIRST; } else { ipv6Key->nwFrag = OVS_FRAG_TYPE_LATER; nextHdr = SOCKET_IPPROTO_FRAGMENT; break; } } else { ipv6Key->nwFrag = OVS_FRAG_TYPE_NONE; } } } ipv6Key->nwProto = (UINT8)nextHdr; layers->l4Offset = ofs; return NDIS_STATUS_SUCCESS; } VOID OvsParseTcp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers) { TCPHdr tcpStorage; const TCPHdr *tcp = OvsGetTcp(packet, layers->l4Offset, &tcpStorage); if (tcp) { if (flow) { flow->tpSrc = tcp->source; flow->tpDst = tcp->dest; } if (layers) { layers->isTcp = 1; layers->l7Offset = layers->l4Offset + 4 * tcp->doff; } } } VOID OvsParseSctp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers) { SCTPHdr sctpStorage; const SCTPHdr *sctp = OvsGetSctp(packet, layers->l4Offset, &sctpStorage); if (sctp) { if (flow) { flow->tpSrc = sctp->source; flow->tpDst = sctp->dest; } if (layers) { layers->isSctp = 1; layers->l7Offset = layers->l4Offset + sizeof *sctp; } } } VOID OvsParseUdp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers) { UDPHdr udpStorage; const UDPHdr *udp = OvsGetUdp(packet, layers->l4Offset, &udpStorage); if (udp) { if (flow) { flow->tpSrc = udp->source; flow->tpDst = udp->dest; } if (layers) { layers->isUdp = 1; if (udp->check == 0) { layers->udpCsumZero = 1; } layers->l7Offset = layers->l4Offset + sizeof *udp; } } } NDIS_STATUS OvsParseIcmpV6(const NET_BUFFER_LIST *packet, Ipv6Key *ipv6Key, Icmp6Key *icmp6Key, POVS_PACKET_HDR_INFO layers) { UINT16 ofs = layers->l4Offset; ICMPHdr icmpStorage; const ICMPHdr *icmp; memset(&icmp6Key->ndTarget, 0, sizeof(icmp6Key->ndTarget)); memset(icmp6Key->arpSha, 0, sizeof(icmp6Key->arpSha)); memset(icmp6Key->arpTha, 0, sizeof(icmp6Key->arpTha)); icmp = OvsGetIcmp(packet, ofs, &icmpStorage); if (!icmp) { return NDIS_STATUS_FAILURE; } ofs += sizeof *icmp; /* * The ICMPv6 type and code fields use the 16-bit transport port * fields, so we need to store them in 16-bit network byte order. */ if (ipv6Key) { ipv6Key->l4.tpSrc = htons(icmp->type); ipv6Key->l4.tpDst = htons(icmp->code); } if (icmp->code == 0 && (icmp->type == ND_NEIGHBOR_SOLICIT || icmp->type == ND_NEIGHBOR_ADVERT)) { struct in6_addr ndTargetStorage; const struct in6_addr *ndTarget; ndTarget = OvsGetPacketBytes(packet, sizeof *ndTarget, ofs, &ndTargetStorage); if (!ndTarget) { return NDIS_STATUS_FAILURE; } icmp6Key->ndTarget = *ndTarget; while ((UINT32)(ofs + 8) <= OvsPacketLenNBL(packet)) { /* * The minimum size of an option is 8 bytes, which also is * the size of Ethernet link-layer options. */ IPv6NdOptHdr ndOptStorage; const IPv6NdOptHdr *ndOpt; UINT16 optLen; ndOpt = OvsGetPacketBytes(packet, sizeof *ndOpt, ofs, &ndOptStorage); if (!ndOpt) { return NDIS_STATUS_FAILURE; } optLen = ndOpt->len * 8; if (!optLen || (UINT32)(ofs + optLen) > OvsPacketLenNBL(packet)) { goto invalid; } /* * Store the link layer address if the appropriate option is * provided. It is considered an error if the same link * layer option is specified twice. */ if (ndOpt->type == ND_OPT_SOURCE_LINKADDR && optLen == 8) { if (Eth_IsNullAddr(icmp6Key->arpSha)) { memcpy(icmp6Key->arpSha, ndOpt + 1, ETH_ADDR_LENGTH); } else { goto invalid; } } else if (ndOpt->type == ND_OPT_TARGET_LINKADDR && optLen == 8) { if (Eth_IsNullAddr(icmp6Key->arpTha)) { memcpy(icmp6Key->arpTha, ndOpt + 1, ETH_ADDR_LENGTH); } else { goto invalid; } } ofs += optLen; } } layers->l7Offset = ofs; layers->isIcmp = 1; return NDIS_STATUS_SUCCESS; invalid: RtlZeroMemory(&icmp6Key->ndTarget, sizeof(icmp6Key->ndTarget)); RtlZeroMemory(icmp6Key->arpSha, sizeof(icmp6Key->arpSha)); RtlZeroMemory(icmp6Key->arpTha, sizeof(icmp6Key->arpTha)); return NDIS_STATUS_FAILURE; }