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.flags = ifinfo.ifi_flags;
59     ret.idx = ifinfo.ifi_index;
60     while (!msg.empty())
61     {
62         auto [hdr, data] = netlink::extractRtAttr(msg);
63         switch (hdr.rta_type)
64         {
65             case IFLA_IFNAME:
66                 ret.name.emplace(data.begin(), data.end() - 1);
67                 break;
68             case IFLA_ADDRESS:
69                 if (data.size() == sizeof(ether_addr))
70                 {
71                     ret.mac.emplace(stdplus::raw::copyFrom<ether_addr>(data));
72                 }
73                 break;
74             case IFLA_MTU:
75                 ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
76                 break;
77             case IFLA_LINK:
78                 ret.parent_idx.emplace(stdplus::raw::copyFrom<unsigned>(data));
79                 break;
80             case IFLA_LINKINFO:
81                 parseLinkInfo(ret, data);
82                 break;
83         }
84     }
85     return ret;
86 }
87 
88 template <typename Addr>
89 static std::optional<std::tuple<unsigned, InAddrAny>>
90     parse(std::string_view msg)
91 {
92     std::optional<unsigned> ifIdx;
93     std::optional<InAddrAny> gw;
94     while (!msg.empty())
95     {
96         auto [hdr, data] = extractRtAttr(msg);
97         switch (hdr.rta_type)
98         {
99             case RTA_OIF:
100                 ifIdx.emplace(stdplus::raw::copyFromStrict<int>(data));
101                 break;
102             case RTA_GATEWAY:
103                 gw.emplace(stdplus::raw::copyFromStrict<Addr>(data));
104                 break;
105         }
106     }
107     if (ifIdx && gw)
108     {
109         return std::make_tuple(*ifIdx, *gw);
110     }
111     return std::nullopt;
112 }
113 
114 std::optional<std::tuple<unsigned, InAddrAny>>
115     gatewayFromRtm(std::string_view msg)
116 {
117     const auto& rtm = extractRtData<rtmsg>(msg);
118     if (rtm.rtm_table != RT_TABLE_MAIN || rtm.rtm_dst_len != 0)
119     {
120         return std::nullopt;
121     }
122     switch (rtm.rtm_family)
123     {
124         case AF_INET:
125             return parse<in_addr>(msg);
126         case AF_INET6:
127             return parse<in6_addr>(msg);
128     }
129     return std::nullopt;
130 }
131 
132 AddressInfo addrFromRtm(std::string_view msg)
133 {
134     const auto& ifa = extractRtData<ifaddrmsg>(msg);
135 
136     AddressInfo ret;
137     ret.ifidx = ifa.ifa_index;
138     ret.flags = ifa.ifa_flags;
139     ret.scope = ifa.ifa_scope;
140     std::optional<InAddrAny> addr;
141     while (!msg.empty())
142     {
143         auto [hdr, data] = extractRtAttr(msg);
144         if (hdr.rta_type == IFA_ADDRESS)
145         {
146             addr.emplace(addrFromBuf(ifa.ifa_family, data));
147         }
148         else if (hdr.rta_type == IFA_FLAGS)
149         {
150             ret.flags = stdplus::raw::copyFromStrict<uint32_t>(data);
151         }
152     }
153     if (!addr)
154     {
155         throw std::runtime_error("Missing address");
156     }
157     ret.ifaddr = {*addr, ifa.ifa_prefixlen};
158     return ret;
159 }
160 
161 NeighborInfo neighFromRtm(std::string_view msg)
162 {
163     const auto& ndm = netlink::extractRtData<ndmsg>(msg);
164 
165     NeighborInfo ret;
166     ret.ifidx = ndm.ndm_ifindex;
167     ret.state = ndm.ndm_state;
168     while (!msg.empty())
169     {
170         auto [hdr, data] = netlink::extractRtAttr(msg);
171         if (hdr.rta_type == NDA_LLADDR)
172         {
173             ret.mac = stdplus::raw::copyFrom<ether_addr>(data);
174         }
175         else if (hdr.rta_type == NDA_DST)
176         {
177             ret.addr = addrFromBuf(ndm.ndm_family, data);
178         }
179     }
180     return ret;
181 }
182 
183 } // namespace phosphor::network::netlink
184