// // Copyright (C) 2012 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 "shill/net/rtnl_message.h" #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <netinet/in.h> #include <sys/socket.h> #include <base/logging.h> #include "shill/net/ndisc.h" namespace shill { struct RTNLHeader { RTNLHeader() { memset(this, 0, sizeof(*this)); } struct nlmsghdr hdr; union { struct ifinfomsg ifi; struct ifaddrmsg ifa; struct rtmsg rtm; struct rtgenmsg gen; struct nduseroptmsg nd_user_opt; struct ndmsg ndm; }; }; RTNLMessage::RTNLMessage() : type_(kTypeUnknown), mode_(kModeUnknown), flags_(0), seq_(0), pid_(0), interface_index_(0), family_(IPAddress::kFamilyUnknown) {} RTNLMessage::RTNLMessage(Type type, Mode mode, unsigned int flags, uint32_t seq, uint32_t pid, int interface_index, IPAddress::Family family) : type_(type), mode_(mode), flags_(flags), seq_(seq), pid_(pid), interface_index_(interface_index), family_(family) {} bool RTNLMessage::Decode(const ByteString& msg) { bool ret = DecodeInternal(msg); if (!ret) { Reset(); } return ret; } bool RTNLMessage::DecodeInternal(const ByteString& msg) { const RTNLHeader* hdr = reinterpret_cast<const RTNLHeader*>(msg.GetConstData()); if (msg.GetLength() < sizeof(hdr->hdr) || msg.GetLength() < hdr->hdr.nlmsg_len) return false; Mode mode = kModeUnknown; switch (hdr->hdr.nlmsg_type) { case RTM_NEWLINK: case RTM_NEWADDR: case RTM_NEWROUTE: case RTM_NEWNDUSEROPT: case RTM_NEWNEIGH: mode = kModeAdd; break; case RTM_DELLINK: case RTM_DELADDR: case RTM_DELROUTE: case RTM_DELNEIGH: mode = kModeDelete; break; default: return false; } rtattr* attr_data = nullptr; int attr_length = 0; switch (hdr->hdr.nlmsg_type) { case RTM_NEWLINK: case RTM_DELLINK: if (!DecodeLink(hdr, mode, &attr_data, &attr_length)) return false; break; case RTM_NEWADDR: case RTM_DELADDR: if (!DecodeAddress(hdr, mode, &attr_data, &attr_length)) return false; break; case RTM_NEWROUTE: case RTM_DELROUTE: if (!DecodeRoute(hdr, mode, &attr_data, &attr_length)) return false; break; case RTM_NEWNDUSEROPT: if (!DecodeNdUserOption(hdr, mode, &attr_data, &attr_length)) return false; break; case RTM_NEWNEIGH: case RTM_DELNEIGH: if (!DecodeNeighbor(hdr, mode, &attr_data, &attr_length)) return false; break; default: NOTREACHED(); } flags_ = hdr->hdr.nlmsg_flags; seq_ = hdr->hdr.nlmsg_seq; pid_ = hdr->hdr.nlmsg_pid; while (attr_data && RTA_OK(attr_data, attr_length)) { SetAttribute( attr_data->rta_type, ByteString(reinterpret_cast<unsigned char*>(RTA_DATA(attr_data)), RTA_PAYLOAD(attr_data))); attr_data = RTA_NEXT(attr_data, attr_length); } if (attr_length) { // We hit a parse error while going through the attributes attributes_.clear(); return false; } return true; } bool RTNLMessage::DecodeLink(const RTNLHeader* hdr, Mode mode, rtattr** attr_data, int* attr_length) { if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) { return false; } mode_ = mode; *attr_data = IFLA_RTA(NLMSG_DATA(&hdr->hdr)); *attr_length = IFLA_PAYLOAD(&hdr->hdr); type_ = kTypeLink; family_ = hdr->ifi.ifi_family; interface_index_ = hdr->ifi.ifi_index; set_link_status(LinkStatus(hdr->ifi.ifi_type, hdr->ifi.ifi_flags, hdr->ifi.ifi_change)); return true; } bool RTNLMessage::DecodeAddress(const RTNLHeader* hdr, Mode mode, rtattr** attr_data, int* attr_length) { if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) { return false; } mode_ = mode; *attr_data = IFA_RTA(NLMSG_DATA(&hdr->hdr)); *attr_length = IFA_PAYLOAD(&hdr->hdr); type_ = kTypeAddress; family_ = hdr->ifa.ifa_family; interface_index_ = hdr->ifa.ifa_index; set_address_status(AddressStatus(hdr->ifa.ifa_prefixlen, hdr->ifa.ifa_flags, hdr->ifa.ifa_scope)); return true; } bool RTNLMessage::DecodeRoute(const RTNLHeader* hdr, Mode mode, rtattr** attr_data, int* attr_length) { if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) { return false; } mode_ = mode; *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr)); *attr_length = RTM_PAYLOAD(&hdr->hdr); type_ = kTypeRoute; family_ = hdr->rtm.rtm_family; set_route_status(RouteStatus(hdr->rtm.rtm_dst_len, hdr->rtm.rtm_src_len, hdr->rtm.rtm_table, hdr->rtm.rtm_protocol, hdr->rtm.rtm_scope, hdr->rtm.rtm_type, hdr->rtm.rtm_flags)); return true; } bool RTNLMessage::DecodeNdUserOption(const RTNLHeader* hdr, Mode mode, rtattr** attr_data, int* attr_length) { if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->nd_user_opt))) { return false; } mode_ = mode; interface_index_ = hdr->nd_user_opt.nduseropt_ifindex; family_ = hdr->nd_user_opt.nduseropt_family; // Verify IP family. if (family_ != IPAddress::kFamilyIPv6) { return false; } // Verify message must at-least contain the option header. if (hdr->nd_user_opt.nduseropt_opts_len < sizeof(NDUserOptionHeader)) { return false; } // Parse the option header. const NDUserOptionHeader* nd_user_option_header = reinterpret_cast<const NDUserOptionHeader*>( reinterpret_cast<const uint8_t*>(&hdr->nd_user_opt) + sizeof(struct nduseroptmsg)); uint32_t lifetime = ntohl(nd_user_option_header->lifetime); // Verify option length. // The length field in the header is in units of 8 octets. int opt_len = static_cast<int>(nd_user_option_header->length) * 8; if (opt_len != hdr->nd_user_opt.nduseropt_opts_len) { return false; } // Determine option data pointer and data length. const uint8_t* option_data = reinterpret_cast<const uint8_t*>(nd_user_option_header + 1); int data_len = opt_len - sizeof(NDUserOptionHeader); if (nd_user_option_header->type == ND_OPT_DNSSL) { // TODO(zqiu): Parse DNSSL (DNS Search List) option. type_ = kTypeDnssl; return true; } else if (nd_user_option_header->type == ND_OPT_RDNSS) { // Parse RNDSS (Recursive DNS Server) option. type_ = kTypeRdnss; return ParseRdnssOption(option_data, data_len, lifetime); } return false; } bool RTNLMessage::ParseRdnssOption(const uint8_t* data, int length, uint32_t lifetime) { const int addr_length = IPAddress::GetAddressLength(IPAddress::kFamilyIPv6); // Verify data size are multiple of individual address size. if (length % addr_length != 0) { return false; } // Parse the DNS server addresses. std::vector<IPAddress> dns_server_addresses; while (length > 0) { dns_server_addresses.push_back( IPAddress(IPAddress::kFamilyIPv6, ByteString(data, addr_length))); length -= addr_length; data += addr_length; } set_rdnss_option(RdnssOption(lifetime, dns_server_addresses)); return true; } bool RTNLMessage::DecodeNeighbor(const RTNLHeader* hdr, Mode mode, rtattr** attr_data, int* attr_length) { if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ndm))) { return false; } mode_ = mode; interface_index_ = hdr->ndm.ndm_ifindex; family_ = hdr->ndm.ndm_family; type_ = kTypeNeighbor; *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr)); *attr_length = RTM_PAYLOAD(&hdr->hdr); set_neighbor_status(NeighborStatus(hdr->ndm.ndm_state, hdr->ndm.ndm_flags, hdr->ndm.ndm_type)); return true; } ByteString RTNLMessage::Encode() const { if (type_ != kTypeLink && type_ != kTypeAddress && type_ != kTypeRoute && type_ != kTypeNeighbor) { return ByteString(); } RTNLHeader hdr; hdr.hdr.nlmsg_flags = flags_; hdr.hdr.nlmsg_seq = seq_; hdr.hdr.nlmsg_pid = pid_; if (mode_ == kModeGet) { if (type_ == kTypeLink) { hdr.hdr.nlmsg_type = RTM_GETLINK; } else if (type_ == kTypeAddress) { hdr.hdr.nlmsg_type = RTM_GETADDR; } else if (type_ == kTypeRoute) { hdr.hdr.nlmsg_type = RTM_GETROUTE; } else if (type_ == kTypeNeighbor) { hdr.hdr.nlmsg_type = RTM_GETNEIGH; } else { NOTIMPLEMENTED(); return ByteString(); } hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen)); hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; hdr.gen.rtgen_family = family_; } else { switch (type_) { case kTypeLink: if (!EncodeLink(&hdr)) { return ByteString(); } break; case kTypeAddress: if (!EncodeAddress(&hdr)) { return ByteString(); } break; case kTypeRoute: if (!EncodeRoute(&hdr)) { return ByteString(); } break; case kTypeNeighbor: if (!EncodeNeighbor(&hdr)) { return ByteString(); } break; default: NOTREACHED(); } } size_t header_length = hdr.hdr.nlmsg_len; ByteString attributes; for (auto attr = attributes_.begin(); attr != attributes_.end(); ++attr) { size_t len = RTA_LENGTH(attr->second.GetLength()); hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len); struct rtattr rt_attr = { static_cast<unsigned short>(len), // NOLINT(runtime/int) attr->first }; ByteString attr_header(reinterpret_cast<unsigned char*>(&rt_attr), sizeof(rt_attr)); attr_header.Resize(RTA_ALIGN(attr_header.GetLength())); attributes.Append(attr_header); ByteString attr_data(attr->second); attr_data.Resize(RTA_ALIGN(attr_data.GetLength())); attributes.Append(attr_data); } ByteString packet(reinterpret_cast<unsigned char*>(&hdr), header_length); packet.Append(attributes); return packet; } bool RTNLMessage::EncodeLink(RTNLHeader* hdr) const { switch (mode_) { case kModeAdd: hdr->hdr.nlmsg_type = RTM_NEWLINK; break; case kModeDelete: hdr->hdr.nlmsg_type = RTM_DELLINK; break; case kModeQuery: hdr->hdr.nlmsg_type = RTM_GETLINK; break; default: NOTIMPLEMENTED(); return false; } hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi)); hdr->ifi.ifi_family = family_; hdr->ifi.ifi_index = interface_index_; hdr->ifi.ifi_type = link_status_.type; hdr->ifi.ifi_flags = link_status_.flags; hdr->ifi.ifi_change = link_status_.change; return true; } bool RTNLMessage::EncodeAddress(RTNLHeader* hdr) const { switch (mode_) { case kModeAdd: hdr->hdr.nlmsg_type = RTM_NEWADDR; break; case kModeDelete: hdr->hdr.nlmsg_type = RTM_DELADDR; break; case kModeQuery: hdr->hdr.nlmsg_type = RTM_GETADDR; break; default: NOTIMPLEMENTED(); return false; } hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa)); hdr->ifa.ifa_family = family_; hdr->ifa.ifa_prefixlen = address_status_.prefix_len; hdr->ifa.ifa_flags = address_status_.flags; hdr->ifa.ifa_scope = address_status_.scope; hdr->ifa.ifa_index = interface_index_; return true; } bool RTNLMessage::EncodeRoute(RTNLHeader* hdr) const { switch (mode_) { case kModeAdd: hdr->hdr.nlmsg_type = RTM_NEWROUTE; break; case kModeDelete: hdr->hdr.nlmsg_type = RTM_DELROUTE; break; case kModeQuery: hdr->hdr.nlmsg_type = RTM_GETROUTE; break; default: NOTIMPLEMENTED(); return false; } hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm)); hdr->rtm.rtm_family = family_; hdr->rtm.rtm_dst_len = route_status_.dst_prefix; hdr->rtm.rtm_src_len = route_status_.src_prefix; hdr->rtm.rtm_table = route_status_.table; hdr->rtm.rtm_protocol = route_status_.protocol; hdr->rtm.rtm_scope = route_status_.scope; hdr->rtm.rtm_type = route_status_.type; hdr->rtm.rtm_flags = route_status_.flags; return true; } bool RTNLMessage::EncodeNeighbor(RTNLHeader* hdr) const { switch (mode_) { case kModeAdd: hdr->hdr.nlmsg_type = RTM_NEWNEIGH; break; case kModeDelete: hdr->hdr.nlmsg_type = RTM_DELNEIGH; break; case kModeQuery: hdr->hdr.nlmsg_type = RTM_GETNEIGH; break; default: NOTIMPLEMENTED(); return false; } hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ndm)); hdr->ndm.ndm_family = family_; hdr->ndm.ndm_ifindex = interface_index_; hdr->ndm.ndm_state = neighbor_status_.state; hdr->ndm.ndm_flags = neighbor_status_.flags; hdr->ndm.ndm_type = neighbor_status_.type; return true; } void RTNLMessage::Reset() { mode_ = kModeUnknown; type_ = kTypeUnknown; flags_ = 0; seq_ = 0; pid_ = 0; interface_index_ = 0; family_ = IPAddress::kFamilyUnknown; link_status_ = LinkStatus(); address_status_ = AddressStatus(); route_status_ = RouteStatus(); attributes_.clear(); } } // namespace shill