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(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, InAddrAny>>
92     parse(std::string_view msg)
93 {
94     std::optional<unsigned> ifIdx;
95     std::optional<InAddrAny> 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, InAddrAny>>
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<in_addr>(msg);
128         case AF_INET6:
129             return parse<in6_addr>(msg);
130     }
131     return std::nullopt;
132 }
133 
134 AddressInfo addrFromRtm(std::string_view msg)
135 {
136     const auto& ifa = extractRtData<ifaddrmsg>(msg);
137 
138     AddressInfo ret;
139     ret.ifidx = ifa.ifa_index;
140     ret.flags = ifa.ifa_flags;
141     ret.scope = ifa.ifa_scope;
142     std::optional<InAddrAny> addr;
143     while (!msg.empty())
144     {
145         auto [hdr, data] = extractRtAttr(msg);
146         if (hdr.rta_type == IFA_ADDRESS)
147         {
148             addr.emplace(addrFromBuf(ifa.ifa_family, data));
149         }
150         else if (hdr.rta_type == IFA_FLAGS)
151         {
152             ret.flags = stdplus::raw::copyFromStrict<uint32_t>(data);
153         }
154     }
155     if (!addr)
156     {
157         throw std::runtime_error("Missing address");
158     }
159     ret.ifaddr = {*addr, ifa.ifa_prefixlen};
160     return ret;
161 }
162 
163 NeighborInfo neighFromRtm(std::string_view msg)
164 {
165     const auto& ndm = netlink::extractRtData<ndmsg>(msg);
166 
167     NeighborInfo ret;
168     ret.ifidx = ndm.ndm_ifindex;
169     ret.state = ndm.ndm_state;
170     while (!msg.empty())
171     {
172         auto [hdr, data] = netlink::extractRtAttr(msg);
173         if (hdr.rta_type == NDA_LLADDR)
174         {
175             ret.mac = stdplus::raw::copyFrom<stdplus::EtherAddr>(data);
176         }
177         else if (hdr.rta_type == NDA_DST)
178         {
179             ret.addr = addrFromBuf(ndm.ndm_family, data);
180         }
181     }
182     return ret;
183 }
184 
185 } // namespace phosphor::network::netlink
186