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, stdplus::InAnyAddr>> 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>> 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 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 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