1 #include "rtnetlink.hpp"
2 
3 #include "netlink.hpp"
4 #include "util.hpp"
5 
6 #include <linux/rtnetlink.h>
7 
8 namespace phosphor::network::netlink
9 {
10 
11 using std::literals::string_view_literals::operator""sv;
12 
parseVlanInfo(InterfaceInfo & info,std::string_view msg)13 static void parseVlanInfo(InterfaceInfo& info, std::string_view msg)
14 {
15     if (msg.data() == nullptr)
16     {
17         throw std::runtime_error("Missing VLAN data");
18     }
19     while (!msg.empty())
20     {
21         auto [hdr, data] = netlink::extractRtAttr(msg);
22         switch (hdr.rta_type)
23         {
24             case IFLA_VLAN_ID:
25                 info.vlan_id.emplace(stdplus::raw::copyFrom<uint16_t>(data));
26                 break;
27         }
28     }
29 }
30 
parseLinkInfo(InterfaceInfo & info,std::string_view msg)31 static void parseLinkInfo(InterfaceInfo& info, std::string_view msg)
32 {
33     std::string_view submsg;
34     while (!msg.empty())
35     {
36         auto [hdr, data] = netlink::extractRtAttr(msg);
37         switch (hdr.rta_type)
38         {
39             case IFLA_INFO_KIND:
40                 data.remove_suffix(1);
41                 info.kind.emplace(data);
42                 break;
43             case IFLA_INFO_DATA:
44                 submsg = data;
45                 break;
46         }
47     }
48     if (info.kind == "vlan"sv)
49     {
50         parseVlanInfo(info, submsg);
51     }
52 }
53 
intfFromRtm(std::string_view msg)54 InterfaceInfo intfFromRtm(std::string_view msg)
55 {
56     const auto& ifinfo = netlink::extractRtData<ifinfomsg>(msg);
57     InterfaceInfo ret;
58     ret.type = ifinfo.ifi_type;
59     ret.idx = ifinfo.ifi_index;
60     ret.flags = ifinfo.ifi_flags;
61     while (!msg.empty())
62     {
63         auto [hdr, data] = netlink::extractRtAttr(msg);
64         switch (hdr.rta_type)
65         {
66             case IFLA_IFNAME:
67                 ret.name.emplace(data.begin(), data.end() - 1);
68                 break;
69             case IFLA_ADDRESS:
70                 if (data.size() == sizeof(stdplus::EtherAddr))
71                 {
72                     ret.mac.emplace(
73                         stdplus::raw::copyFrom<stdplus::EtherAddr>(data));
74                 }
75                 break;
76             case IFLA_MTU:
77                 ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
78                 break;
79             case IFLA_LINK:
80                 ret.parent_idx.emplace(stdplus::raw::copyFrom<unsigned>(data));
81                 break;
82             case IFLA_LINKINFO:
83                 parseLinkInfo(ret, data);
84                 break;
85         }
86     }
87     return ret;
88 }
89 
90 template <typename Addr>
91 static std::optional<std::tuple<unsigned, stdplus::InAnyAddr>>
parse(std::string_view msg)92     parse(std::string_view msg)
93 {
94     std::optional<unsigned> ifIdx;
95     std::optional<stdplus::InAnyAddr> gw;
96     while (!msg.empty())
97     {
98         auto [hdr, data] = extractRtAttr(msg);
99         switch (hdr.rta_type)
100         {
101             case RTA_OIF:
102                 ifIdx.emplace(stdplus::raw::copyFromStrict<int>(data));
103                 break;
104             case RTA_GATEWAY:
105                 gw.emplace(stdplus::raw::copyFromStrict<Addr>(data));
106                 break;
107         }
108     }
109     if (ifIdx && gw)
110     {
111         return std::make_tuple(*ifIdx, *gw);
112     }
113     return std::nullopt;
114 }
115 
116 std::optional<std::tuple<unsigned, stdplus::InAnyAddr>>
gatewayFromRtm(std::string_view msg)117     gatewayFromRtm(std::string_view msg)
118 {
119     const auto& rtm = extractRtData<rtmsg>(msg);
120     if (rtm.rtm_table != RT_TABLE_MAIN || rtm.rtm_dst_len != 0)
121     {
122         return std::nullopt;
123     }
124     switch (rtm.rtm_family)
125     {
126         case AF_INET:
127             return parse<stdplus::In4Addr>(msg);
128         case AF_INET6:
129             return parse<stdplus::In6Addr>(msg);
130     }
131     return std::nullopt;
132 }
133 
addrFromRtm(std::string_view msg)134 AddressInfo addrFromRtm(std::string_view msg)
135 {
136     const auto& ifa = extractRtData<ifaddrmsg>(msg);
137 
138     uint32_t flags = ifa.ifa_flags;
139     std::optional<stdplus::InAnyAddr> addr;
140     while (!msg.empty())
141     {
142         auto [hdr, data] = extractRtAttr(msg);
143         if (hdr.rta_type == IFA_ADDRESS)
144         {
145             addr.emplace(addrFromBuf(ifa.ifa_family, data));
146         }
147         else if (hdr.rta_type == IFA_FLAGS)
148         {
149             flags = stdplus::raw::copyFromStrict<uint32_t>(data);
150         }
151     }
152     if (!addr)
153     {
154         throw std::runtime_error("Missing address");
155     }
156     return AddressInfo{.ifidx = ifa.ifa_index,
157                        .ifaddr = stdplus::SubnetAny{*addr, ifa.ifa_prefixlen},
158                        .scope = ifa.ifa_scope,
159                        .flags = flags};
160 }
161 
neighFromRtm(std::string_view msg)162 NeighborInfo neighFromRtm(std::string_view msg)
163 {
164     const auto& ndm = netlink::extractRtData<ndmsg>(msg);
165 
166     NeighborInfo ret;
167     ret.ifidx = ndm.ndm_ifindex;
168     ret.state = ndm.ndm_state;
169     while (!msg.empty())
170     {
171         auto [hdr, data] = netlink::extractRtAttr(msg);
172         if (hdr.rta_type == NDA_LLADDR)
173         {
174             ret.mac = stdplus::raw::copyFrom<stdplus::EtherAddr>(data);
175         }
176         else if (hdr.rta_type == NDA_DST)
177         {
178             ret.addr = addrFromBuf(ndm.ndm_family, data);
179         }
180     }
181     return ret;
182 }
183 
184 } // namespace phosphor::network::netlink
185