xref: /openbmc/phosphor-networkd/src/rtnetlink.cpp (revision 60903ee7fc85d87a51bd5bc1f08d4c86234b322e)
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 
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 
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 
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(ether_addr))
71                 {
72                     ret.mac.emplace(stdplus::raw::copyFrom<ether_addr>(data));
73                 }
74                 break;
75             case IFLA_MTU:
76                 ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
77                 break;
78             case IFLA_LINK:
79                 ret.parent_idx.emplace(stdplus::raw::copyFrom<unsigned>(data));
80                 break;
81             case IFLA_LINKINFO:
82                 parseLinkInfo(ret, data);
83                 break;
84         }
85     }
86     return ret;
87 }
88 
89 template <typename Addr>
90 static std::optional<std::tuple<unsigned, InAddrAny>>
91     parse(std::string_view msg)
92 {
93     std::optional<unsigned> ifIdx;
94     std::optional<InAddrAny> gw;
95     while (!msg.empty())
96     {
97         auto [hdr, data] = extractRtAttr(msg);
98         switch (hdr.rta_type)
99         {
100             case RTA_OIF:
101                 ifIdx.emplace(stdplus::raw::copyFromStrict<int>(data));
102                 break;
103             case RTA_GATEWAY:
104                 gw.emplace(stdplus::raw::copyFromStrict<Addr>(data));
105                 break;
106         }
107     }
108     if (ifIdx && gw)
109     {
110         return std::make_tuple(*ifIdx, *gw);
111     }
112     return std::nullopt;
113 }
114 
115 std::optional<std::tuple<unsigned, InAddrAny>>
116     gatewayFromRtm(std::string_view msg)
117 {
118     const auto& rtm = extractRtData<rtmsg>(msg);
119     if (rtm.rtm_table != RT_TABLE_MAIN || rtm.rtm_dst_len != 0)
120     {
121         return std::nullopt;
122     }
123     switch (rtm.rtm_family)
124     {
125         case AF_INET:
126             return parse<in_addr>(msg);
127         case AF_INET6:
128             return parse<in6_addr>(msg);
129     }
130     return std::nullopt;
131 }
132 
133 AddressInfo addrFromRtm(std::string_view msg)
134 {
135     const auto& ifa = extractRtData<ifaddrmsg>(msg);
136 
137     AddressInfo ret;
138     ret.ifidx = ifa.ifa_index;
139     ret.flags = ifa.ifa_flags;
140     ret.scope = ifa.ifa_scope;
141     std::optional<InAddrAny> addr;
142     while (!msg.empty())
143     {
144         auto [hdr, data] = extractRtAttr(msg);
145         if (hdr.rta_type == IFA_ADDRESS)
146         {
147             addr.emplace(addrFromBuf(ifa.ifa_family, data));
148         }
149         else if (hdr.rta_type == IFA_FLAGS)
150         {
151             ret.flags = stdplus::raw::copyFromStrict<uint32_t>(data);
152         }
153     }
154     if (!addr)
155     {
156         throw std::runtime_error("Missing address");
157     }
158     ret.ifaddr = {*addr, ifa.ifa_prefixlen};
159     return ret;
160 }
161 
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<ether_addr>(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