/* * Copyright (C) 2017 The Android Open Source Project * * 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 "packet.h" #include "address.h" Packet::Packet(Message& message) : mMessage(message), mType(Type::Other), mIp(nullptr), mIcmp(nullptr), mFirstOpt(nullptr) { if (message.size() < sizeof(ip6_hdr) + sizeof(icmp6_hdr)) { mType = Type::Other; return; } mIp = reinterpret_cast(message.data()); uint8_t version = (mIp->ip6_vfc & 0xF0) >> 4; if (version != 6 || mIp->ip6_nxt != IPPROTO_ICMPV6) { mType = Type::Other; return; } size_t size = message.size() - sizeof(ip6_hdr); char* data = message.data() + sizeof(ip6_hdr); mIcmp = reinterpret_cast(data); if (mIcmp->icmp6_code != 0) { // All messages we care about have a code of zero mType = Type::Other; return; } size_t headerSize = 0; switch (mIcmp->icmp6_type) { case ND_ROUTER_SOLICIT: headerSize = sizeof(nd_router_solicit); mType = Type::RouterSolicitation; break; case ND_ROUTER_ADVERT: headerSize = sizeof(nd_router_advert); mType = Type::RouterAdvertisement; break; case ND_NEIGHBOR_SOLICIT: headerSize = sizeof(nd_neighbor_solicit); mType = Type::NeighborSolicitation; break; case ND_NEIGHBOR_ADVERT: headerSize = sizeof(nd_neighbor_advert); mType = Type::NeighborAdvertisement; break; default: mType = Type::Other; return; } if (size < headerSize) { mType = Type::Other; return; } // We might have options char* options = data + headerSize; if (options + sizeof(nd_opt_hdr) < data + size) { nd_opt_hdr* option = reinterpret_cast(options); // Option length is in units of 8 bytes, multiply by 8 to get bytes if (options + option->nd_opt_len * 8u <= data + size) { mFirstOpt = option; } } } std::string Packet::description() const { char buffer[256]; switch (mType) { case Type::NeighborSolicitation: { auto ns = reinterpret_cast(icmp()); snprintf(buffer, sizeof(buffer), "Neighbor Solicitation for %s", addrToStr(ns->nd_ns_target).c_str()); return buffer; } case Type::NeighborAdvertisement: { auto na = reinterpret_cast(icmp()); snprintf(buffer, sizeof(buffer), "Neighbor Advertisement for %s", addrToStr(na->nd_na_target).c_str()); return buffer; } case Type::RouterSolicitation: return "Router Solicitation"; case Type::RouterAdvertisement: return "Router Advertisement"; default: break; } return "[unknown]"; } nd_opt_hdr* Packet::firstOpt() { return mFirstOpt; } nd_opt_hdr* Packet::nextOpt(nd_opt_hdr* currentHeader) { char* end = mMessage.data() + mMessage.size(); char* current = reinterpret_cast(currentHeader); if (currentHeader < mFirstOpt || current >= end) { // The provided header does not belong to this packet info. return nullptr; } char* next = current + currentHeader->nd_opt_len * 8u; if (next >= end) { // The next header points passed the message data return nullptr; } nd_opt_hdr* nextHeader = reinterpret_cast(next); if (next + nextHeader->nd_opt_len * 8u > end) { // The next option extends beyond the message data return nullptr; } return nextHeader; }