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