1 /* 2 * Copyright (C) 2017 The Android Open Source Project 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 "packet.h" 18 19 #include "address.h" 20 21 Packet::Packet(Message& message) 22 : mMessage(message), 23 mType(Type::Other), 24 mIp(nullptr), 25 mIcmp(nullptr), 26 mFirstOpt(nullptr) { 27 if (message.size() < sizeof(ip6_hdr) + sizeof(icmp6_hdr)) { 28 mType = Type::Other; 29 return; 30 } 31 mIp = reinterpret_cast<const ip6_hdr*>(message.data()); 32 uint8_t version = (mIp->ip6_vfc & 0xF0) >> 4; 33 if (version != 6 || mIp->ip6_nxt != IPPROTO_ICMPV6) { 34 mType = Type::Other; 35 return; 36 } 37 38 size_t size = message.size() - sizeof(ip6_hdr); 39 char* data = message.data() + sizeof(ip6_hdr); 40 mIcmp = reinterpret_cast<const icmp6_hdr*>(data); 41 if (mIcmp->icmp6_code != 0) { 42 // All messages we care about have a code of zero 43 mType = Type::Other; 44 return; 45 } 46 47 size_t headerSize = 0; 48 switch (mIcmp->icmp6_type) { 49 case ND_ROUTER_SOLICIT: 50 headerSize = sizeof(nd_router_solicit); 51 mType = Type::RouterSolicitation; 52 break; 53 case ND_ROUTER_ADVERT: 54 headerSize = sizeof(nd_router_advert); 55 mType = Type::RouterAdvertisement; 56 break; 57 case ND_NEIGHBOR_SOLICIT: 58 headerSize = sizeof(nd_neighbor_solicit); 59 mType = Type::NeighborSolicitation; 60 break; 61 case ND_NEIGHBOR_ADVERT: 62 headerSize = sizeof(nd_neighbor_advert); 63 mType = Type::NeighborAdvertisement; 64 break; 65 default: 66 mType = Type::Other; 67 return; 68 } 69 if (size < headerSize) { 70 mType = Type::Other; 71 return; 72 } 73 74 // We might have options 75 char* options = data + headerSize; 76 if (options + sizeof(nd_opt_hdr) < data + size) { 77 nd_opt_hdr* option = reinterpret_cast<nd_opt_hdr*>(options); 78 // Option length is in units of 8 bytes, multiply by 8 to get bytes 79 if (options + option->nd_opt_len * 8u <= data + size) { 80 mFirstOpt = option; 81 } 82 } 83 } 84 85 std::string Packet::description() const { 86 char buffer[256]; 87 switch (mType) { 88 case Type::NeighborSolicitation: { 89 auto ns = reinterpret_cast<const nd_neighbor_solicit*>(icmp()); 90 snprintf(buffer, sizeof(buffer), "Neighbor Solicitation for %s", 91 addrToStr(ns->nd_ns_target).c_str()); 92 return buffer; 93 } 94 case Type::NeighborAdvertisement: { 95 auto na = reinterpret_cast<const nd_neighbor_advert*>(icmp()); 96 snprintf(buffer, sizeof(buffer), 97 "Neighbor Advertisement for %s", 98 addrToStr(na->nd_na_target).c_str()); 99 return buffer; 100 } 101 case Type::RouterSolicitation: 102 return "Router Solicitation"; 103 case Type::RouterAdvertisement: 104 return "Router Advertisement"; 105 default: 106 break; 107 } 108 return "[unknown]"; 109 } 110 111 nd_opt_hdr* Packet::firstOpt() { 112 return mFirstOpt; 113 } 114 115 nd_opt_hdr* Packet::nextOpt(nd_opt_hdr* currentHeader) { 116 char* end = mMessage.data() + mMessage.size(); 117 char* current = reinterpret_cast<char*>(currentHeader); 118 if (currentHeader < mFirstOpt || current >= end) { 119 // The provided header does not belong to this packet info. 120 return nullptr; 121 } 122 char* next = current + currentHeader->nd_opt_len * 8u; 123 if (next >= end) { 124 // The next header points passed the message data 125 return nullptr; 126 } 127 nd_opt_hdr* nextHeader = reinterpret_cast<nd_opt_hdr*>(next); 128 if (next + nextHeader->nd_opt_len * 8u > end) { 129 // The next option extends beyond the message data 130 return nullptr; 131 } 132 return nextHeader; 133 } 134 135