blob: 5aba1d2802f992ddf585213e21c5dfadb2a07521 [file] [log] [blame]
William A. Kennington III2e09d272022-10-14 17:15:00 -07001#include "system_queries.hpp"
2
William A. Kennington IIIfd862be2022-10-09 18:40:55 -07003#include "netlink.hpp"
William A. Kennington III6a923632022-11-06 18:17:33 -08004#include "rtnetlink.hpp"
William A. Kennington III2e09d272022-10-14 17:15:00 -07005#include "util.hpp"
6
7#include <fmt/format.h>
William A. Kennington III2e09d272022-10-14 17:15:00 -07008#include <linux/ethtool.h>
9#include <linux/sockios.h>
10#include <net/if.h>
11
12#include <algorithm>
13#include <optional>
14#include <phosphor-logging/log.hpp>
15#include <stdexcept>
16#include <stdplus/fd/create.hpp>
17#include <stdplus/raw.hpp>
18#include <stdplus/util/cexec.hpp>
19#include <string_view>
20#include <system_error>
21
22namespace phosphor::network::system
23{
24
25using std::literals::string_view_literals::operator""sv;
26using phosphor::logging::entry;
27using phosphor::logging::level;
28using phosphor::logging::log;
29
30static stdplus::Fd& getIFSock()
31{
32 using namespace stdplus::fd;
33 static auto fd =
34 socket(SocketDomain::INet, SocketType::Datagram, SocketProto::IP);
35 return fd;
36}
37
38static ifreq makeIFReq(std::string_view ifname)
39{
40 ifreq ifr = {};
41 const auto copied = std::min<std::size_t>(ifname.size(), IFNAMSIZ - 1);
42 std::copy_n(ifname.begin(), copied, ifr.ifr_name);
43 return ifr;
44}
45
46static ifreq executeIFReq(std::string_view ifname, unsigned long cmd,
47 void* data = nullptr)
48{
49 ifreq ifr = makeIFReq(ifname);
50 ifr.ifr_data = reinterpret_cast<char*>(data);
51 getIFSock().ioctl(cmd, &ifr);
52 return ifr;
53}
54
55inline auto optionalIFReq(stdplus::zstring_view ifname, unsigned long long cmd,
56 std::string_view cmdname, auto&& complete,
57 void* data = nullptr)
58{
59 ifreq ifr;
60 std::optional<decltype(complete(ifr))> ret;
William A. Kennington III3e471c52022-10-27 19:46:07 -070061 auto ukey = std::make_tuple(std::string(ifname), cmd);
62 static std::unordered_set<std::tuple<std::string, unsigned long long>>
63 unsupported;
William A. Kennington III2e09d272022-10-14 17:15:00 -070064 try
65 {
66 ifr = executeIFReq(ifname, cmd, data);
67 }
68 catch (const std::system_error& e)
69 {
70 if (e.code() == std::errc::operation_not_supported)
71 {
William A. Kennington III3e471c52022-10-27 19:46:07 -070072 if (unsupported.find(ukey) == unsupported.end())
73 {
74 unsupported.emplace(std::move(ukey));
75 auto msg =
76 fmt::format("{} not supported on {}", cmdname, ifname);
77 log<level::INFO>(msg.c_str(),
78 entry("INTERFACE=%s", ifname.c_str()));
79 }
William A. Kennington III2e09d272022-10-14 17:15:00 -070080 return ret;
81 }
82 throw;
83 }
William A. Kennington III3e471c52022-10-27 19:46:07 -070084 unsupported.erase(ukey);
William A. Kennington III2e09d272022-10-14 17:15:00 -070085 ret.emplace(complete(ifr));
86 return ret;
87}
88
89EthInfo getEthInfo(stdplus::zstring_view ifname)
90{
91 ethtool_cmd edata = {};
92 edata.cmd = ETHTOOL_GSET;
93 return optionalIFReq(
94 ifname, SIOCETHTOOL, "ETHTOOL"sv,
95 [&](const ifreq&) {
96 return EthInfo{.autoneg = edata.autoneg != 0,
97 .speed = edata.speed};
98 },
99 &edata)
100 .value_or(EthInfo{});
101}
102
103bool intfIsRunning(std::string_view ifname)
104{
105 return executeIFReq(ifname, SIOCGIFFLAGS).ifr_flags & IFF_RUNNING;
106}
107
William A. Kennington III2e09d272022-10-14 17:15:00 -0700108std::optional<unsigned> getMTU(stdplus::zstring_view ifname)
109{
110 return optionalIFReq(ifname, SIOCGIFMTU, "GMTU",
111 [](const ifreq& ifr) { return ifr.ifr_mtu; });
112}
113
114void setMTU(std::string_view ifname, unsigned mtu)
115{
116 auto ifr = makeIFReq(ifname);
117 ifr.ifr_mtu = mtu;
118 getIFSock().ioctl(SIOCSIFMTU, &ifr);
119}
120
121void setNICUp(std::string_view ifname, bool up)
122{
123 ifreq ifr = executeIFReq(ifname, SIOCGIFFLAGS);
124 ifr.ifr_flags &= ~IFF_UP;
125 ifr.ifr_flags |= up ? IFF_UP : 0;
126 getIFSock().ioctl(SIOCSIFFLAGS, &ifr);
127}
128
William A. Kennington III09f3a4a2022-10-25 02:59:16 -0700129static void parseVlanInfo(InterfaceInfo& info, std::string_view msg)
130{
131 if (msg.data() == nullptr)
132 {
133 throw std::runtime_error("Missing VLAN data");
134 }
135 while (!msg.empty())
136 {
137 auto [hdr, data] = netlink::extractRtAttr(msg);
138 switch (hdr.rta_type)
139 {
140 case IFLA_VLAN_ID:
141 info.vlan_id.emplace(stdplus::raw::copyFrom<uint16_t>(data));
142 break;
143 }
144 }
145}
146
147static void parseLinkInfo(InterfaceInfo& info, std::string_view msg)
148{
149 std::string_view submsg;
150 while (!msg.empty())
151 {
152 auto [hdr, data] = netlink::extractRtAttr(msg);
153 switch (hdr.rta_type)
154 {
155 case IFLA_INFO_KIND:
156 data.remove_suffix(1);
157 info.kind.emplace(data);
158 break;
159 case IFLA_INFO_DATA:
160 submsg = data;
161 break;
162 }
163 }
164 if (info.kind == "vlan"sv)
165 {
166 parseVlanInfo(info, submsg);
167 }
168}
169
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700170InterfaceInfo detail::parseInterface(const nlmsghdr& hdr, std::string_view msg)
William A. Kennington III2e09d272022-10-14 17:15:00 -0700171{
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700172 if (hdr.nlmsg_type != RTM_NEWLINK)
William A. Kennington III2e09d272022-10-14 17:15:00 -0700173 {
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700174 throw std::runtime_error("Not an interface msg");
175 }
William A. Kennington IIIf1289a02022-10-24 18:30:36 -0700176 const auto& ifinfo = netlink::extractRtData<ifinfomsg>(msg);
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700177 InterfaceInfo ret;
178 ret.flags = ifinfo.ifi_flags;
179 ret.idx = ifinfo.ifi_index;
180 while (!msg.empty())
181 {
182 auto [hdr, data] = netlink::extractRtAttr(msg);
William A. Kennington III09f3a4a2022-10-25 02:59:16 -0700183 switch (hdr.rta_type)
William A. Kennington III2e09d272022-10-14 17:15:00 -0700184 {
William A. Kennington III09f3a4a2022-10-25 02:59:16 -0700185 case IFLA_IFNAME:
186 ret.name.emplace(data.begin(), data.end() - 1);
187 break;
188 case IFLA_ADDRESS:
189 if (data.size() != sizeof(ether_addr))
190 {
191 // Some interfaces have IP addresses for their LLADDR
192 break;
193 }
194 ret.mac.emplace(stdplus::raw::copyFrom<ether_addr>(data));
195 break;
196 case IFLA_MTU:
197 ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
198 break;
199 case IFLA_LINK:
200 ret.parent_idx.emplace(stdplus::raw::copyFrom<unsigned>(data));
201 break;
202 case IFLA_LINKINFO:
203 parseLinkInfo(ret, data);
204 break;
William A. Kennington III2e09d272022-10-14 17:15:00 -0700205 }
206 }
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700207 return ret;
208}
209
210bool detail::validateNewInterface(const InterfaceInfo& info)
211{
212 if (info.flags & IFF_LOOPBACK)
213 {
214 return false;
215 }
216 if (!info.name)
217 {
218 throw std::invalid_argument("Interface Dump missing name");
219 }
220 const auto& ignored = internal::getIgnoredInterfaces();
221 if (ignored.find(*info.name) != ignored.end())
222 {
223 return false;
224 }
225 return true;
226}
227
William A. Kennington III6a923632022-11-06 18:17:33 -0800228bool detail::validateNewAddr(const AddressInfo& info,
229 const AddressFilter& filter) noexcept
230{
231 if (filter.ifidx != 0 && filter.ifidx != info.ifidx)
232 {
233 return false;
234 }
235 return true;
236}
237
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700238std::vector<InterfaceInfo> getInterfaces()
239{
240 std::vector<InterfaceInfo> ret;
241 auto cb = [&](const nlmsghdr& hdr, std::string_view msg) {
242 auto info = detail::parseInterface(hdr, msg);
243 if (detail::validateNewInterface(info))
244 {
245 ret.emplace_back(std::move(info));
246 }
247 };
248 ifinfomsg msg{};
249 netlink::performRequest(NETLINK_ROUTE, RTM_GETLINK, NLM_F_DUMP, msg, cb);
William A. Kennington III2e09d272022-10-14 17:15:00 -0700250 return ret;
251}
252
William A. Kennington III6a923632022-11-06 18:17:33 -0800253std::vector<AddressInfo> getAddresses(const AddressFilter& filter)
254{
255 std::vector<AddressInfo> ret;
256 auto cb = [&](const nlmsghdr&, std::string_view msg) {
257 auto info = netlink::addrFromRtm(msg);
258 if (detail::validateNewAddr(info, filter))
259 {
260 ret.emplace_back(std::move(info));
261 }
262 };
263 ifaddrmsg msg{};
264 msg.ifa_index = filter.ifidx;
265 netlink::performRequest(NETLINK_ROUTE, RTM_GETADDR, NLM_F_DUMP, msg, cb);
266 return ret;
267}
268
William A. Kennington III2e09d272022-10-14 17:15:00 -0700269} // namespace phosphor::network::system