github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/socket_netlink_route_util.cc (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "test/syscalls/linux/socket_netlink_route_util.h" 16 17 #include <linux/if.h> 18 #include <linux/netlink.h> 19 #include <linux/rtnetlink.h> 20 21 #include "test/syscalls/linux/socket_netlink_util.h" 22 23 namespace gvisor { 24 namespace testing { 25 namespace { 26 27 constexpr uint32_t kSeq = 12345; 28 29 // Types of address modifications that may be performed on an interface. 30 enum class LinkAddrModification { 31 kAdd, 32 kAddExclusive, 33 kReplace, 34 kDelete, 35 }; 36 37 // Populates |hdr| with appripriate values for the modification type. 38 PosixError PopulateNlmsghdr(LinkAddrModification modification, 39 struct nlmsghdr* hdr) { 40 switch (modification) { 41 case LinkAddrModification::kAdd: 42 hdr->nlmsg_type = RTM_NEWADDR; 43 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 44 return NoError(); 45 case LinkAddrModification::kAddExclusive: 46 hdr->nlmsg_type = RTM_NEWADDR; 47 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; 48 return NoError(); 49 case LinkAddrModification::kReplace: 50 hdr->nlmsg_type = RTM_NEWADDR; 51 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; 52 return NoError(); 53 case LinkAddrModification::kDelete: 54 hdr->nlmsg_type = RTM_DELADDR; 55 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 56 return NoError(); 57 } 58 59 return PosixError(EINVAL); 60 } 61 62 // Adds or removes the specified address from the specified interface. 63 PosixError LinkModifyLocalAddr(int index, int family, int prefixlen, 64 const void* addr, int addrlen, 65 LinkAddrModification modification) { 66 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 67 68 struct request { 69 struct nlmsghdr hdr; 70 struct ifaddrmsg ifaddr; 71 char attrbuf[512]; 72 }; 73 74 struct request req = {}; 75 PosixError err = PopulateNlmsghdr(modification, &req.hdr); 76 if (!err.ok()) { 77 return err; 78 } 79 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifaddr)); 80 req.hdr.nlmsg_seq = kSeq; 81 req.ifaddr.ifa_index = index; 82 req.ifaddr.ifa_family = family; 83 req.ifaddr.ifa_prefixlen = prefixlen; 84 85 struct rtattr* rta = reinterpret_cast<struct rtattr*>( 86 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 87 rta->rta_type = IFA_LOCAL; 88 rta->rta_len = RTA_LENGTH(addrlen); 89 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen); 90 memcpy(RTA_DATA(rta), addr, addrlen); 91 92 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 93 } 94 95 } // namespace 96 97 PosixError DumpLinks( 98 const FileDescriptor& fd, uint32_t seq, 99 const std::function<void(const struct nlmsghdr* hdr)>& fn) { 100 struct request { 101 struct nlmsghdr hdr; 102 struct ifinfomsg ifm; 103 }; 104 105 struct request req = {}; 106 req.hdr.nlmsg_len = sizeof(req); 107 req.hdr.nlmsg_type = RTM_GETLINK; 108 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 109 req.hdr.nlmsg_seq = seq; 110 req.ifm.ifi_family = AF_UNSPEC; 111 112 return NetlinkRequestResponse(fd, &req, sizeof(req), fn, false); 113 } 114 115 PosixErrorOr<std::vector<Link>> DumpLinks() { 116 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 117 118 std::vector<Link> links; 119 RETURN_IF_ERRNO(DumpLinks(fd, kSeq, [&](const struct nlmsghdr* hdr) { 120 if (hdr->nlmsg_type != RTM_NEWLINK || 121 hdr->nlmsg_len < NLMSG_SPACE(sizeof(struct ifinfomsg))) { 122 return; 123 } 124 const struct ifinfomsg* msg = 125 reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr)); 126 const auto* rta = FindRtAttr(hdr, msg, IFLA_IFNAME); 127 if (rta == nullptr) { 128 // Ignore links that do not have a name. 129 return; 130 } 131 132 links.emplace_back(); 133 links.back().index = msg->ifi_index; 134 links.back().type = msg->ifi_type; 135 links.back().name = 136 std::string(reinterpret_cast<const char*>(RTA_DATA(rta))); 137 })); 138 return links; 139 } 140 141 PosixErrorOr<Link> LoopbackLink() { 142 ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks()); 143 for (const auto& link : links) { 144 if (link.type == ARPHRD_LOOPBACK) { 145 return link; 146 } 147 } 148 return PosixError(ENOENT, "loopback link not found"); 149 } 150 151 PosixError LinkAddLocalAddr(int index, int family, int prefixlen, 152 const void* addr, int addrlen) { 153 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 154 LinkAddrModification::kAdd); 155 } 156 157 PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, 158 const void* addr, int addrlen) { 159 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 160 LinkAddrModification::kAddExclusive); 161 } 162 163 PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, 164 const void* addr, int addrlen) { 165 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 166 LinkAddrModification::kReplace); 167 } 168 169 PosixError LinkDelLocalAddr(int index, int family, int prefixlen, 170 const void* addr, int addrlen) { 171 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 172 LinkAddrModification::kDelete); 173 } 174 175 PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change) { 176 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 177 178 struct request { 179 struct nlmsghdr hdr; 180 struct ifinfomsg ifinfo; 181 char pad[NLMSG_ALIGNTO]; 182 }; 183 184 struct request req = {}; 185 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo)); 186 req.hdr.nlmsg_type = RTM_NEWLINK; 187 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 188 req.hdr.nlmsg_seq = kSeq; 189 req.ifinfo.ifi_index = index; 190 req.ifinfo.ifi_flags = flags; 191 req.ifinfo.ifi_change = change; 192 193 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 194 } 195 196 PosixError LinkSetMacAddr(int index, const void* addr, int addrlen) { 197 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 198 199 struct request { 200 struct nlmsghdr hdr; 201 struct ifinfomsg ifinfo; 202 char attrbuf[512]; 203 }; 204 205 struct request req = {}; 206 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo)); 207 req.hdr.nlmsg_type = RTM_NEWLINK; 208 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 209 req.hdr.nlmsg_seq = kSeq; 210 req.ifinfo.ifi_index = index; 211 212 struct rtattr* rta = reinterpret_cast<struct rtattr*>( 213 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 214 rta->rta_type = IFLA_ADDRESS; 215 rta->rta_len = RTA_LENGTH(addrlen); 216 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen); 217 memcpy(RTA_DATA(rta), addr, addrlen); 218 219 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 220 } 221 222 } // namespace testing 223 } // namespace gvisor