blob: 5229c94c9ef1a4a5b46a384b8215aa4846deec6f [file] [log] [blame]
#include "rtnetlink.hpp"
#include "netlink.hpp"
#include "util.hpp"
#include <linux/rtnetlink.h>
namespace phosphor::network::netlink
{
using std::literals::string_view_literals::operator""sv;
static void parseVlanInfo(InterfaceInfo& info, std::string_view msg)
{
if (msg.data() == nullptr)
{
throw std::runtime_error("Missing VLAN data");
}
while (!msg.empty())
{
auto [hdr, data] = netlink::extractRtAttr(msg);
switch (hdr.rta_type)
{
case IFLA_VLAN_ID:
info.vlan_id.emplace(stdplus::raw::copyFrom<uint16_t>(data));
break;
}
}
}
static void parseLinkInfo(InterfaceInfo& info, std::string_view msg)
{
std::string_view submsg;
while (!msg.empty())
{
auto [hdr, data] = netlink::extractRtAttr(msg);
switch (hdr.rta_type)
{
case IFLA_INFO_KIND:
data.remove_suffix(1);
info.kind.emplace(data);
break;
case IFLA_INFO_DATA:
submsg = data;
break;
}
}
if (info.kind == "vlan"sv)
{
parseVlanInfo(info, submsg);
}
}
InterfaceInfo intfFromRtm(std::string_view msg)
{
const auto& ifinfo = netlink::extractRtData<ifinfomsg>(msg);
InterfaceInfo ret;
ret.type = ifinfo.ifi_type;
ret.idx = ifinfo.ifi_index;
ret.flags = ifinfo.ifi_flags;
while (!msg.empty())
{
auto [hdr, data] = netlink::extractRtAttr(msg);
switch (hdr.rta_type)
{
case IFLA_IFNAME:
ret.name.emplace(data.begin(), data.end() - 1);
break;
case IFLA_ADDRESS:
if (data.size() == sizeof(stdplus::EtherAddr))
{
ret.mac.emplace(
stdplus::raw::copyFrom<stdplus::EtherAddr>(data));
}
break;
case IFLA_MTU:
ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
break;
case IFLA_LINK:
ret.parent_idx.emplace(stdplus::raw::copyFrom<unsigned>(data));
break;
case IFLA_LINKINFO:
parseLinkInfo(ret, data);
break;
}
}
return ret;
}
template <typename Addr>
static std::optional<std::tuple<unsigned, stdplus::InAnyAddr>>
parse(std::string_view msg)
{
std::optional<unsigned> ifIdx;
std::optional<stdplus::InAnyAddr> gw;
while (!msg.empty())
{
auto [hdr, data] = extractRtAttr(msg);
switch (hdr.rta_type)
{
case RTA_OIF:
ifIdx.emplace(stdplus::raw::copyFromStrict<int>(data));
break;
case RTA_GATEWAY:
gw.emplace(stdplus::raw::copyFromStrict<Addr>(data));
break;
}
}
if (ifIdx && gw)
{
return std::make_tuple(*ifIdx, *gw);
}
return std::nullopt;
}
std::optional<std::tuple<unsigned, stdplus::InAnyAddr>>
gatewayFromRtm(std::string_view msg)
{
const auto& rtm = extractRtData<rtmsg>(msg);
if (rtm.rtm_table != RT_TABLE_MAIN || rtm.rtm_dst_len != 0)
{
return std::nullopt;
}
switch (rtm.rtm_family)
{
case AF_INET:
return parse<stdplus::In4Addr>(msg);
case AF_INET6:
return parse<stdplus::In6Addr>(msg);
}
return std::nullopt;
}
AddressInfo addrFromRtm(std::string_view msg)
{
const auto& ifa = extractRtData<ifaddrmsg>(msg);
uint32_t flags = ifa.ifa_flags;
std::optional<stdplus::InAnyAddr> addr;
while (!msg.empty())
{
auto [hdr, data] = extractRtAttr(msg);
if (hdr.rta_type == IFA_ADDRESS)
{
addr.emplace(addrFromBuf(ifa.ifa_family, data));
}
else if (hdr.rta_type == IFA_FLAGS)
{
flags = stdplus::raw::copyFromStrict<uint32_t>(data);
}
}
if (!addr)
{
throw std::runtime_error("Missing address");
}
return AddressInfo{.ifidx = ifa.ifa_index,
.ifaddr = stdplus::SubnetAny{*addr, ifa.ifa_prefixlen},
.scope = ifa.ifa_scope,
.flags = flags};
}
NeighborInfo neighFromRtm(std::string_view msg)
{
const auto& ndm = netlink::extractRtData<ndmsg>(msg);
NeighborInfo ret;
ret.ifidx = ndm.ndm_ifindex;
ret.state = ndm.ndm_state;
while (!msg.empty())
{
auto [hdr, data] = netlink::extractRtAttr(msg);
if (hdr.rta_type == NDA_LLADDR)
{
ret.mac = stdplus::raw::copyFrom<stdplus::EtherAddr>(data);
}
else if (hdr.rta_type == NDA_DST)
{
ret.addr = addrFromBuf(ndm.ndm_family, data);
}
}
return ret;
}
} // namespace phosphor::network::netlink