C++程序  |  135行  |  4.23 KB

/*
 * 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<const ip6_hdr*>(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<const icmp6_hdr*>(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<nd_opt_hdr*>(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<const nd_neighbor_solicit*>(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<const nd_neighbor_advert*>(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<char*>(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<nd_opt_hdr*>(next);
    if (next + nextHeader->nd_opt_len * 8u > end) {
        // The next option extends beyond the message data
        return nullptr;
    }
    return nextHeader;
}