|  | // Copyright (c) 2019 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "quic/qbone/platform/rtnetlink_message.h" | 
|  |  | 
|  | #include <net/if_arp.h> | 
|  |  | 
|  | #include "quic/platform/api/quic_ip_address.h" | 
|  | #include "quic/platform/api/quic_test.h" | 
|  |  | 
|  | namespace quic { | 
|  | namespace { | 
|  |  | 
|  | using ::testing::StrEq; | 
|  |  | 
|  | TEST(RtnetlinkMessageTest, LinkMessageCanBeCreatedForGetOperation) { | 
|  | uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; | 
|  | uint32_t seq = 42; | 
|  | uint32_t pid = 7; | 
|  | auto message = LinkMessage::New(RtnetlinkMessage::Operation::GET, flags, seq, | 
|  | pid, nullptr); | 
|  |  | 
|  | // No rtattr appended. | 
|  | EXPECT_EQ(1, message.IoVecSize()); | 
|  |  | 
|  | // nlmsghdr is built properly. | 
|  | auto iov = message.BuildIoVec(); | 
|  | EXPECT_EQ(NLMSG_SPACE(sizeof(struct rtgenmsg)), iov[0].iov_len); | 
|  | auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); | 
|  | EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); | 
|  | EXPECT_EQ(RTM_GETLINK, netlink_message->nlmsg_type); | 
|  | EXPECT_EQ(flags, netlink_message->nlmsg_flags); | 
|  | EXPECT_EQ(seq, netlink_message->nlmsg_seq); | 
|  | EXPECT_EQ(pid, netlink_message->nlmsg_pid); | 
|  |  | 
|  | // We actually included rtgenmsg instead of the passed in ifinfomsg since this | 
|  | // is a GET operation. | 
|  | EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); | 
|  | } | 
|  |  | 
|  | TEST(RtnetlinkMessageTest, LinkMessageCanBeCreatedForNewOperation) { | 
|  | struct ifinfomsg interface_info_header = {AF_INET, /* pad */ 0, ARPHRD_TUNNEL, | 
|  | 3,       0,           0xffffffff}; | 
|  | uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; | 
|  | uint32_t seq = 42; | 
|  | uint32_t pid = 7; | 
|  | auto message = LinkMessage::New(RtnetlinkMessage::Operation::NEW, flags, seq, | 
|  | pid, &interface_info_header); | 
|  |  | 
|  | std::string device_name = "device0"; | 
|  | message.AppendAttribute(IFLA_IFNAME, device_name.c_str(), device_name.size()); | 
|  |  | 
|  | // One rtattr appended. | 
|  | EXPECT_EQ(2, message.IoVecSize()); | 
|  |  | 
|  | // nlmsghdr is built properly. | 
|  | auto iov = message.BuildIoVec(); | 
|  | EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifinfomsg))), | 
|  | iov[0].iov_len); | 
|  | auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); | 
|  | EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifinfomsg))) + | 
|  | RTA_LENGTH(device_name.size()), | 
|  | netlink_message->nlmsg_len); | 
|  | EXPECT_EQ(RTM_NEWLINK, netlink_message->nlmsg_type); | 
|  | EXPECT_EQ(flags, netlink_message->nlmsg_flags); | 
|  | EXPECT_EQ(seq, netlink_message->nlmsg_seq); | 
|  | EXPECT_EQ(pid, netlink_message->nlmsg_pid); | 
|  |  | 
|  | // ifinfomsg is included properly. | 
|  | auto* parsed_header = | 
|  | reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(netlink_message)); | 
|  | EXPECT_EQ(interface_info_header.ifi_family, parsed_header->ifi_family); | 
|  | EXPECT_EQ(interface_info_header.ifi_type, parsed_header->ifi_type); | 
|  | EXPECT_EQ(interface_info_header.ifi_index, parsed_header->ifi_index); | 
|  | EXPECT_EQ(interface_info_header.ifi_flags, parsed_header->ifi_flags); | 
|  | EXPECT_EQ(interface_info_header.ifi_change, parsed_header->ifi_change); | 
|  |  | 
|  | // rtattr is handled properly. | 
|  | EXPECT_EQ(RTA_SPACE(device_name.size()), iov[1].iov_len); | 
|  | auto* rta = reinterpret_cast<struct rtattr*>(iov[1].iov_base); | 
|  | EXPECT_EQ(IFLA_IFNAME, rta->rta_type); | 
|  | EXPECT_EQ(RTA_LENGTH(device_name.size()), rta->rta_len); | 
|  | EXPECT_THAT(device_name, | 
|  | StrEq(std::string(reinterpret_cast<char*>(RTA_DATA(rta)), | 
|  | RTA_PAYLOAD(rta)))); | 
|  | } | 
|  |  | 
|  | TEST(RtnetlinkMessageTest, AddressMessageCanBeCreatedForGetOperation) { | 
|  | uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; | 
|  | uint32_t seq = 42; | 
|  | uint32_t pid = 7; | 
|  | auto message = AddressMessage::New(RtnetlinkMessage::Operation::GET, flags, | 
|  | seq, pid, nullptr); | 
|  |  | 
|  | // No rtattr appended. | 
|  | EXPECT_EQ(1, message.IoVecSize()); | 
|  |  | 
|  | // nlmsghdr is built properly. | 
|  | auto iov = message.BuildIoVec(); | 
|  | EXPECT_EQ(NLMSG_SPACE(sizeof(struct rtgenmsg)), iov[0].iov_len); | 
|  | auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); | 
|  | EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); | 
|  | EXPECT_EQ(RTM_GETADDR, netlink_message->nlmsg_type); | 
|  | EXPECT_EQ(flags, netlink_message->nlmsg_flags); | 
|  | EXPECT_EQ(seq, netlink_message->nlmsg_seq); | 
|  | EXPECT_EQ(pid, netlink_message->nlmsg_pid); | 
|  |  | 
|  | // We actually included rtgenmsg instead of the passed in ifinfomsg since this | 
|  | // is a GET operation. | 
|  | EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); | 
|  | } | 
|  |  | 
|  | TEST(RtnetlinkMessageTest, AddressMessageCanBeCreatedForNewOperation) { | 
|  | struct ifaddrmsg interface_address_header = {AF_INET, | 
|  | /* prefixlen */ 24, | 
|  | /* flags */ 0, | 
|  | /* scope */ RT_SCOPE_LINK, | 
|  | /* index */ 4}; | 
|  | uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; | 
|  | uint32_t seq = 42; | 
|  | uint32_t pid = 7; | 
|  | auto message = AddressMessage::New(RtnetlinkMessage::Operation::NEW, flags, | 
|  | seq, pid, &interface_address_header); | 
|  |  | 
|  | QuicIpAddress ip; | 
|  | QUICHE_CHECK(ip.FromString("10.0.100.3")); | 
|  | message.AppendAttribute(IFA_ADDRESS, ip.ToPackedString().c_str(), | 
|  | ip.ToPackedString().size()); | 
|  |  | 
|  | // One rtattr is appended. | 
|  | EXPECT_EQ(2, message.IoVecSize()); | 
|  |  | 
|  | // nlmsghdr is built properly. | 
|  | auto iov = message.BuildIoVec(); | 
|  | EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifaddrmsg))), | 
|  | iov[0].iov_len); | 
|  | auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); | 
|  | EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifaddrmsg))) + | 
|  | RTA_LENGTH(ip.ToPackedString().size()), | 
|  | netlink_message->nlmsg_len); | 
|  | EXPECT_EQ(RTM_NEWADDR, netlink_message->nlmsg_type); | 
|  | EXPECT_EQ(flags, netlink_message->nlmsg_flags); | 
|  | EXPECT_EQ(seq, netlink_message->nlmsg_seq); | 
|  | EXPECT_EQ(pid, netlink_message->nlmsg_pid); | 
|  |  | 
|  | // ifaddrmsg is included properly. | 
|  | auto* parsed_header = | 
|  | reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message)); | 
|  | EXPECT_EQ(interface_address_header.ifa_family, parsed_header->ifa_family); | 
|  | EXPECT_EQ(interface_address_header.ifa_prefixlen, | 
|  | parsed_header->ifa_prefixlen); | 
|  | EXPECT_EQ(interface_address_header.ifa_flags, parsed_header->ifa_flags); | 
|  | EXPECT_EQ(interface_address_header.ifa_scope, parsed_header->ifa_scope); | 
|  | EXPECT_EQ(interface_address_header.ifa_index, parsed_header->ifa_index); | 
|  |  | 
|  | // rtattr is handled properly. | 
|  | EXPECT_EQ(RTA_SPACE(ip.ToPackedString().size()), iov[1].iov_len); | 
|  | auto* rta = reinterpret_cast<struct rtattr*>(iov[1].iov_base); | 
|  | EXPECT_EQ(IFA_ADDRESS, rta->rta_type); | 
|  | EXPECT_EQ(RTA_LENGTH(ip.ToPackedString().size()), rta->rta_len); | 
|  | EXPECT_THAT(ip.ToPackedString(), | 
|  | StrEq(std::string(reinterpret_cast<char*>(RTA_DATA(rta)), | 
|  | RTA_PAYLOAD(rta)))); | 
|  | } | 
|  |  | 
|  | TEST(RtnetlinkMessageTest, RouteMessageCanBeCreatedFromNewOperation) { | 
|  | struct rtmsg route_message_header = {AF_INET6, | 
|  | /* rtm_dst_len */ 48, | 
|  | /* rtm_src_len */ 0, | 
|  | /* rtm_tos */ 0, | 
|  | /* rtm_table */ RT_TABLE_MAIN, | 
|  | /* rtm_protocol */ RTPROT_STATIC, | 
|  | /* rtm_scope */ RT_SCOPE_LINK, | 
|  | /* rtm_type */ RTN_LOCAL, | 
|  | /* rtm_flags */ 0}; | 
|  | uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; | 
|  | uint32_t seq = 42; | 
|  | uint32_t pid = 7; | 
|  | auto message = RouteMessage::New(RtnetlinkMessage::Operation::NEW, flags, seq, | 
|  | pid, &route_message_header); | 
|  |  | 
|  | QuicIpAddress preferred_source; | 
|  | QUICHE_CHECK(preferred_source.FromString("ff80::1")); | 
|  | message.AppendAttribute(RTA_PREFSRC, | 
|  | preferred_source.ToPackedString().c_str(), | 
|  | preferred_source.ToPackedString().size()); | 
|  |  | 
|  | // One rtattr is appended. | 
|  | EXPECT_EQ(2, message.IoVecSize()); | 
|  |  | 
|  | // nlmsghdr is built properly | 
|  | auto iov = message.BuildIoVec(); | 
|  | EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct rtmsg))), iov[0].iov_len); | 
|  | auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); | 
|  | EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct rtmsg))) + | 
|  | RTA_LENGTH(preferred_source.ToPackedString().size()), | 
|  | netlink_message->nlmsg_len); | 
|  | EXPECT_EQ(RTM_NEWROUTE, netlink_message->nlmsg_type); | 
|  | EXPECT_EQ(flags, netlink_message->nlmsg_flags); | 
|  | EXPECT_EQ(seq, netlink_message->nlmsg_seq); | 
|  | EXPECT_EQ(pid, netlink_message->nlmsg_pid); | 
|  |  | 
|  | // rtmsg is included properly. | 
|  | auto* parsed_header = | 
|  | reinterpret_cast<struct rtmsg*>(NLMSG_DATA(netlink_message)); | 
|  | EXPECT_EQ(route_message_header.rtm_family, parsed_header->rtm_family); | 
|  | EXPECT_EQ(route_message_header.rtm_dst_len, parsed_header->rtm_dst_len); | 
|  | EXPECT_EQ(route_message_header.rtm_src_len, parsed_header->rtm_src_len); | 
|  | EXPECT_EQ(route_message_header.rtm_tos, parsed_header->rtm_tos); | 
|  | EXPECT_EQ(route_message_header.rtm_table, parsed_header->rtm_table); | 
|  | EXPECT_EQ(route_message_header.rtm_protocol, parsed_header->rtm_protocol); | 
|  | EXPECT_EQ(route_message_header.rtm_scope, parsed_header->rtm_scope); | 
|  | EXPECT_EQ(route_message_header.rtm_type, parsed_header->rtm_type); | 
|  | EXPECT_EQ(route_message_header.rtm_flags, parsed_header->rtm_flags); | 
|  |  | 
|  | // rtattr is handled properly. | 
|  | EXPECT_EQ(RTA_SPACE(preferred_source.ToPackedString().size()), | 
|  | iov[1].iov_len); | 
|  | auto* rta = reinterpret_cast<struct rtattr*>(iov[1].iov_base); | 
|  | EXPECT_EQ(RTA_PREFSRC, rta->rta_type); | 
|  | EXPECT_EQ(RTA_LENGTH(preferred_source.ToPackedString().size()), rta->rta_len); | 
|  | EXPECT_THAT(preferred_source.ToPackedString(), | 
|  | StrEq(std::string(reinterpret_cast<char*>(RTA_DATA(rta)), | 
|  | RTA_PAYLOAD(rta)))); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace quic |