gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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/fib_rules.h> 18 #include <linux/if.h> 19 #include <linux/netlink.h> 20 #include <linux/rtnetlink.h> 21 22 #include <cerrno> 23 #include <cstdint> 24 #include <cstring> 25 26 #include "test/syscalls/linux/socket_netlink_util.h" 27 #include "test/util/file_descriptor.h" 28 #include "test/util/posix_error.h" 29 30 namespace gvisor { 31 namespace testing { 32 namespace { 33 34 constexpr uint32_t kSeq = 12345; 35 constexpr uint32_t kMetric = 999; 36 37 // Types of modifications that may be performed to a Netlink resource. 38 enum class NetlinkModification { 39 kAdd, 40 kAddExclusive, 41 kReplace, 42 kDelete, 43 }; 44 45 // Populates |hdr| with appropriate values for the modification type. 46 PosixError PopulateLinkAddrNlmsghdr(NetlinkModification modification, 47 struct nlmsghdr* hdr) { 48 switch (modification) { 49 case NetlinkModification::kAdd: 50 hdr->nlmsg_type = RTM_NEWADDR; 51 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 52 return NoError(); 53 case NetlinkModification::kAddExclusive: 54 hdr->nlmsg_type = RTM_NEWADDR; 55 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; 56 return NoError(); 57 case NetlinkModification::kReplace: 58 hdr->nlmsg_type = RTM_NEWADDR; 59 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; 60 return NoError(); 61 case NetlinkModification::kDelete: 62 hdr->nlmsg_type = RTM_DELADDR; 63 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 64 return NoError(); 65 } 66 67 return PosixError(EINVAL); 68 } 69 70 // Populates |hdr| with appropriate values for the modification type. 71 PosixError PopulateRouteNlmsghdr(NetlinkModification modification, 72 struct nlmsghdr* hdr) { 73 switch (modification) { 74 case NetlinkModification::kAdd: 75 hdr->nlmsg_type = RTM_NEWROUTE; 76 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE; 77 return NoError(); 78 case NetlinkModification::kAddExclusive: 79 hdr->nlmsg_type = RTM_NEWROUTE; 80 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; 81 return NoError(); 82 case NetlinkModification::kReplace: 83 hdr->nlmsg_type = RTM_NEWROUTE; 84 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; 85 return NoError(); 86 case NetlinkModification::kDelete: 87 hdr->nlmsg_type = RTM_DELROUTE; 88 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 89 return NoError(); 90 } 91 92 return PosixError(EINVAL); 93 } 94 95 // Populates |hdr| with appropriate values for the modification type. 96 PosixError PopulateRuleNlmsghdr(NetlinkModification modification, 97 struct nlmsghdr* hdr) { 98 switch (modification) { 99 case NetlinkModification::kAdd: 100 hdr->nlmsg_type = RTM_NEWRULE; 101 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 102 return NoError(); 103 case NetlinkModification::kAddExclusive: 104 hdr->nlmsg_type = RTM_NEWRULE; 105 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; 106 return NoError(); 107 case NetlinkModification::kReplace: 108 hdr->nlmsg_type = RTM_NEWRULE; 109 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; 110 return NoError(); 111 case NetlinkModification::kDelete: 112 hdr->nlmsg_type = RTM_DELRULE; 113 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 114 return NoError(); 115 } 116 117 return PosixError(EINVAL); 118 } 119 120 // Adds or removes the specified address from the specified interface. 121 PosixError LinkModifyLocalAddr(int index, int family, int prefixlen, 122 const void* addr, int addrlen, 123 NetlinkModification modification) { 124 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 125 126 struct request { 127 struct nlmsghdr hdr; 128 struct ifaddrmsg ifaddr; 129 char attrbuf[512]; 130 }; 131 132 struct request req = {}; 133 PosixError err = PopulateLinkAddrNlmsghdr(modification, &req.hdr); 134 if (!err.ok()) { 135 return err; 136 } 137 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifaddr)); 138 req.hdr.nlmsg_seq = kSeq; 139 req.ifaddr.ifa_index = index; 140 req.ifaddr.ifa_family = family; 141 req.ifaddr.ifa_prefixlen = prefixlen; 142 143 struct rtattr* rta = reinterpret_cast<struct rtattr*>( 144 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 145 rta->rta_type = IFA_LOCAL; 146 rta->rta_len = RTA_LENGTH(addrlen); 147 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen); 148 memcpy(RTA_DATA(rta), addr, addrlen); 149 150 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 151 } 152 153 // Adds or removes the specified route. 154 PosixError ModifyUnicastRoute(int interface, int family, int prefixlen, 155 const void* dst, int dstlen, 156 NetlinkModification modification) { 157 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 158 159 struct request { 160 struct nlmsghdr hdr; 161 struct rtmsg route; 162 char attrbuf[512]; 163 }; 164 165 struct request req = {}; 166 PosixError err = PopulateRouteNlmsghdr(modification, &req.hdr); 167 if (!err.ok()) { 168 return err; 169 } 170 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.route)); 171 req.hdr.nlmsg_seq = kSeq; 172 req.route.rtm_dst_len = prefixlen; 173 req.route.rtm_family = family; 174 req.route.rtm_type = RTN_UNICAST; 175 176 struct rtattr* rta_oif = reinterpret_cast<struct rtattr*>( 177 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 178 rta_oif->rta_type = RTA_OIF; 179 rta_oif->rta_len = RTA_LENGTH(sizeof(interface)); 180 req.hdr.nlmsg_len = 181 NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(interface)); 182 memcpy(RTA_DATA(rta_oif), &interface, sizeof(interface)); 183 184 struct rtattr* rta_priority = reinterpret_cast<struct rtattr*>( 185 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 186 rta_priority->rta_type = RTA_PRIORITY; 187 rta_priority->rta_len = RTA_LENGTH(sizeof(kMetric)); 188 req.hdr.nlmsg_len = 189 NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(kMetric)); 190 memcpy(RTA_DATA(rta_priority), &kMetric, sizeof(kMetric)); 191 192 struct rtattr* rta_dst = reinterpret_cast<struct rtattr*>( 193 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 194 rta_dst->rta_type = RTA_DST; 195 rta_dst->rta_len = RTA_LENGTH(dstlen); 196 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(dstlen); 197 memcpy(RTA_DATA(rta_dst), dst, dstlen); 198 199 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 200 } 201 202 // Adds or removes the specified route. 203 PosixError ModifyLookupInTableRule(int family, int table, int priority, 204 int prefixlen, const void* dst, int dstlen, 205 NetlinkModification modification) { 206 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 207 208 struct request { 209 struct nlmsghdr hdr; 210 struct fib_rule_hdr rule; 211 char attrbuf[512]; 212 }; 213 214 struct request req = {}; 215 PosixError err = PopulateRuleNlmsghdr(modification, &req.hdr); 216 if (!err.ok()) { 217 return err; 218 } 219 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.rule)); 220 req.hdr.nlmsg_seq = kSeq; 221 req.rule.family = family; 222 req.rule.table = table; 223 req.rule.action = FR_ACT_TO_TBL; 224 req.rule.dst_len = prefixlen; 225 226 struct rtattr* fra_priority = reinterpret_cast<struct rtattr*>( 227 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 228 fra_priority->rta_type = FRA_PRIORITY; 229 fra_priority->rta_len = RTA_LENGTH(sizeof(priority)); 230 req.hdr.nlmsg_len = 231 NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(priority)); 232 memcpy(RTA_DATA(fra_priority), &priority, sizeof(priority)); 233 234 struct rtattr* fra_dst = reinterpret_cast<struct rtattr*>( 235 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 236 fra_dst->rta_type = FRA_DST; 237 fra_dst->rta_len = RTA_LENGTH(dstlen); 238 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(dstlen); 239 memcpy(RTA_DATA(fra_dst), dst, dstlen); 240 241 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 242 } 243 244 } // namespace 245 246 PosixError DumpLinks( 247 const FileDescriptor& fd, uint32_t seq, 248 const std::function<void(const struct nlmsghdr* hdr)>& fn) { 249 struct request { 250 struct nlmsghdr hdr; 251 struct ifinfomsg ifm; 252 }; 253 254 struct request req = {}; 255 req.hdr.nlmsg_len = sizeof(req); 256 req.hdr.nlmsg_type = RTM_GETLINK; 257 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 258 req.hdr.nlmsg_seq = seq; 259 req.ifm.ifi_family = AF_UNSPEC; 260 261 return NetlinkRequestResponse(fd, &req, sizeof(req), fn, false); 262 } 263 264 PosixErrorOr<std::vector<Link>> DumpLinks() { 265 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 266 267 std::vector<Link> links; 268 RETURN_IF_ERRNO(DumpLinks(fd, kSeq, [&](const struct nlmsghdr* hdr) { 269 if (hdr->nlmsg_type != RTM_NEWLINK || 270 hdr->nlmsg_len < NLMSG_SPACE(sizeof(struct ifinfomsg))) { 271 return; 272 } 273 const struct ifinfomsg* msg = 274 reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr)); 275 const auto* rta = FindRtAttr(hdr, msg, IFLA_IFNAME); 276 if (rta == nullptr) { 277 // Ignore links that do not have a name. 278 return; 279 } 280 281 links.emplace_back(); 282 links.back().index = msg->ifi_index; 283 links.back().type = msg->ifi_type; 284 links.back().name = 285 std::string(reinterpret_cast<const char*>(RTA_DATA(rta))); 286 })); 287 return links; 288 } 289 290 PosixErrorOr<Link> LoopbackLink() { 291 ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks()); 292 for (const auto& link : links) { 293 if (link.type == ARPHRD_LOOPBACK) { 294 return link; 295 } 296 } 297 return PosixError(ENOENT, "loopback link not found"); 298 } 299 300 PosixError LinkAddLocalAddr(int index, int family, int prefixlen, 301 const void* addr, int addrlen) { 302 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 303 NetlinkModification::kAdd); 304 } 305 306 PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, 307 const void* addr, int addrlen) { 308 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 309 NetlinkModification::kAddExclusive); 310 } 311 312 PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, 313 const void* addr, int addrlen) { 314 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 315 NetlinkModification::kReplace); 316 } 317 318 PosixError LinkDelLocalAddr(int index, int family, int prefixlen, 319 const void* addr, int addrlen) { 320 return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, 321 NetlinkModification::kDelete); 322 } 323 324 PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change) { 325 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 326 327 struct request { 328 struct nlmsghdr hdr; 329 struct ifinfomsg ifinfo; 330 char pad[NLMSG_ALIGNTO]; 331 }; 332 333 struct request req = {}; 334 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo)); 335 req.hdr.nlmsg_type = RTM_NEWLINK; 336 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 337 req.hdr.nlmsg_seq = kSeq; 338 req.ifinfo.ifi_index = index; 339 req.ifinfo.ifi_flags = flags; 340 req.ifinfo.ifi_change = change; 341 342 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 343 } 344 345 PosixError LinkSetMacAddr(int index, const void* addr, int addrlen) { 346 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); 347 348 struct request { 349 struct nlmsghdr hdr; 350 struct ifinfomsg ifinfo; 351 char attrbuf[512]; 352 }; 353 354 struct request req = {}; 355 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo)); 356 req.hdr.nlmsg_type = RTM_NEWLINK; 357 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 358 req.hdr.nlmsg_seq = kSeq; 359 req.ifinfo.ifi_index = index; 360 361 struct rtattr* rta = reinterpret_cast<struct rtattr*>( 362 reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); 363 rta->rta_type = IFLA_ADDRESS; 364 rta->rta_len = RTA_LENGTH(addrlen); 365 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen); 366 memcpy(RTA_DATA(rta), addr, addrlen); 367 368 return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); 369 } 370 371 PosixError AddUnicastRoute(int interface, int family, int prefixlen, 372 const void* dst, int dstlen) { 373 return ModifyUnicastRoute(interface, family, prefixlen, dst, dstlen, 374 NetlinkModification::kAdd); 375 } 376 377 PosixError DelUnicastRoute(int interface, int family, int prefixlen, 378 const void* dst, int dstlen) { 379 return ModifyUnicastRoute(interface, family, prefixlen, dst, dstlen, 380 NetlinkModification::kDelete); 381 } 382 383 PosixError AddExclusiveLookupInTableRule(int family, int table, int priority, 384 int prefixlen, const void* dst, 385 int dstlen) { 386 return ModifyLookupInTableRule(family, table, priority, prefixlen, dst, 387 dstlen, NetlinkModification::kAddExclusive); 388 } 389 390 PosixError DelLookupInTableRule(int family, int table, int priority, 391 int prefixlen, const void* dst, int dstlen) { 392 return ModifyLookupInTableRule(family, table, priority, prefixlen, dst, 393 dstlen, NetlinkModification::kDelete); 394 } 395 396 } // namespace testing 397 } // namespace gvisor