1 #include "system_queries.hpp"
2 
3 #include "netlink.hpp"
4 
5 #include <fmt/format.h>
6 #include <linux/ethtool.h>
7 #include <linux/rtnetlink.h>
8 #include <linux/sockios.h>
9 #include <net/if.h>
10 
11 #include <phosphor-logging/lg2.hpp>
12 #include <stdplus/fd/create.hpp>
13 #include <stdplus/util/cexec.hpp>
14 
15 #include <algorithm>
16 #include <optional>
17 #include <stdexcept>
18 #include <string_view>
19 
20 namespace phosphor::network::system
21 {
22 
23 using std::literals::string_view_literals::operator""sv;
24 
25 static stdplus::Fd& getIFSock()
26 {
27     using namespace stdplus::fd;
28     static auto fd = socket(SocketDomain::INet, SocketType::Datagram,
29                             SocketProto::IP);
30     return fd;
31 }
32 
33 static ifreq makeIFReq(std::string_view ifname)
34 {
35     ifreq ifr = {};
36     const auto copied = std::min<std::size_t>(ifname.size(), IFNAMSIZ - 1);
37     std::copy_n(ifname.begin(), copied, ifr.ifr_name);
38     return ifr;
39 }
40 
41 static ifreq executeIFReq(std::string_view ifname, unsigned long cmd,
42                           void* data = nullptr)
43 {
44     ifreq ifr = makeIFReq(ifname);
45     ifr.ifr_data = reinterpret_cast<char*>(data);
46     getIFSock().ioctl(cmd, &ifr);
47     return ifr;
48 }
49 
50 inline auto optionalIFReq(stdplus::zstring_view ifname, unsigned long long cmd,
51                           std::string_view cmdname, auto&& complete,
52                           void* data = nullptr)
53 {
54     ifreq ifr;
55     std::optional<decltype(complete(ifr))> ret;
56     auto ukey = std::make_tuple(std::string(ifname), cmd);
57     static std::unordered_set<std::tuple<std::string, unsigned long long>>
58         unsupported;
59     try
60     {
61         ifr = executeIFReq(ifname, cmd, data);
62     }
63     catch (const std::system_error& e)
64     {
65         if (e.code() == std::errc::operation_not_supported)
66         {
67             if (unsupported.find(ukey) == unsupported.end())
68             {
69                 unsupported.emplace(std::move(ukey));
70                 lg2::info("{NET_IFREQ} not supported on {NET_INTF}",
71                           "NET_IFREQ", cmdname, "NET_INTF", ifname);
72             }
73             return ret;
74         }
75         throw;
76     }
77     unsupported.erase(ukey);
78     ret.emplace(complete(ifr));
79     return ret;
80 }
81 
82 EthInfo getEthInfo(stdplus::zstring_view ifname)
83 {
84     ethtool_cmd edata = {};
85     edata.cmd = ETHTOOL_GSET;
86     return optionalIFReq(
87                ifname, SIOCETHTOOL, "ETHTOOL"sv,
88                [&](const ifreq&) {
89         return EthInfo{.autoneg = edata.autoneg != 0, .speed = edata.speed};
90                },
91                &edata)
92         .value_or(EthInfo{});
93 }
94 
95 void setMTU(std::string_view ifname, unsigned mtu)
96 {
97     auto ifr = makeIFReq(ifname);
98     ifr.ifr_mtu = mtu;
99     getIFSock().ioctl(SIOCSIFMTU, &ifr);
100 }
101 
102 void setNICUp(std::string_view ifname, bool up)
103 {
104     ifreq ifr = executeIFReq(ifname, SIOCGIFFLAGS);
105     ifr.ifr_flags &= ~IFF_UP;
106     ifr.ifr_flags |= up ? IFF_UP : 0;
107     getIFSock().ioctl(SIOCSIFFLAGS, &ifr);
108 }
109 
110 void deleteIntf(unsigned idx)
111 {
112     if (idx == 0)
113     {
114         return;
115     }
116     ifinfomsg msg = {};
117     msg.ifi_family = AF_UNSPEC;
118     msg.ifi_index = idx;
119     netlink::performRequest(NETLINK_ROUTE, RTM_DELLINK, NLM_F_REPLACE, msg,
120                             [&](const nlmsghdr& hdr, std::string_view data) {
121         int err = 0;
122         if (hdr.nlmsg_type == NLMSG_ERROR)
123         {
124             err = netlink::extractRtData<nlmsgerr>(data).error;
125         }
126         throw std::runtime_error(
127             fmt::format("Failed to delete `{}`: {}", idx, strerror(err)));
128     });
129 }
130 
131 } // namespace phosphor::network::system
132