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