eth_interface.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. #include <boost/algorithm/string.hpp>
  2. #include <boost/uuid/uuid.hpp>
  3. #include <boost/uuid/uuid_generators.hpp>
  4. #include <net/if.h>
  5. #if !BOOST_OS_WINDOWS
  6. #include <sys/types.h>
  7. #include <ifaddrs.h>
  8. #endif
  9. #include "eth_interface.h"
  10. using namespace std;
  11. namespace nmrpflash {
  12. namespace {
  13. eth_interface::index_type name_to_index(string name, string& pcap_name)
  14. {
  15. auto index = if_nametoindex(name.c_str());
  16. #if BOOST_OS_WINDOWS
  17. if (!index) {
  18. // it's not an ANSI interface name, but it could still
  19. // be a GUID, or pcap device name.
  20. const std::string pcap_prefix = "\\Device\\NPF_";
  21. if (boost::starts_with(name, pcap_prefix)) {
  22. name = name.substr(pcap_prefix.size());
  23. }
  24. GUID guid;
  25. if (CLSIDFromString(name.c_str(), &guid) != NOERROR) {
  26. return 0;
  27. }
  28. NET_LUID luid;
  29. auto err = ConvertInterfaceGuidToLuid(&guid, &luid);
  30. if (err) {
  31. return 0;
  32. }
  33. pcap_name = pcap_prefix + name;
  34. err = ConvertInterfaceLuidToIndex(&luid, &index);
  35. if (err) {
  36. return 0;
  37. }
  38. }
  39. #else
  40. pcap_name = name;
  41. #endif
  42. return index;
  43. }
  44. struct pcap_devlist
  45. {
  46. pcap_if_t* raw;
  47. pcap_devlist()
  48. {
  49. char errbuf[PCAP_ERRBUF_SIZE];
  50. if (pcap_findalldevs(&raw, errbuf) != 0) {
  51. throw runtime_error("pcap_findalldevs: "s + errbuf);
  52. }
  53. }
  54. static void visit(const eth_interface& intf, function<void(const pcap_if_t&)> f)
  55. {
  56. pcap_devlist list;
  57. for (auto dev = list.raw; dev; dev = dev->next) {
  58. if (dev->name == intf.get_pcap_name()) {
  59. f(*dev);
  60. return;
  61. }
  62. }
  63. throw runtime_error("no such pcap device: " + intf.get_pcap_name());
  64. }
  65. ~pcap_devlist() { pcap_freealldevs(raw); }
  66. };
  67. #if !BOOST_OS_WINDOWS
  68. struct if_addrs
  69. {
  70. #if BOOST_OS_LINUX
  71. typedef uint8_t mac_byte_type;
  72. #else
  73. typedef char mac_byte_type;
  74. #endif
  75. ifaddrs* raw;
  76. if_addrs()
  77. {
  78. if (getifaddrs(&raw) != 0) {
  79. throw runtime_error("getifaddrs: "s + strerror(errno));
  80. }
  81. }
  82. static mac_byte_type* get_mac_addr(ifaddrs* ifa)
  83. {
  84. if (!ifa || !ifa->ifa_addr) {
  85. return nullptr;
  86. }
  87. #if BOOST_OS_LINUX
  88. sockaddr_ll* sll = reinterpret_cast<sockaddr_ll*>(ifa->ifa_addr);
  89. if (sll->sll_family == AF_PACKET) {
  90. return sll->sll_addr;
  91. }
  92. #else
  93. sockaddr_dl* sdl = reinterpret_cast<sockaddr_dl*>(ifa->ifa_addr);
  94. if (sdl->sdl_family == AF_LINK && sdl->sdl_type == IFT_ETHER) {
  95. return LLADDR(sdl);
  96. }
  97. #endif
  98. return nullptr;
  99. }
  100. ~if_addrs()
  101. {
  102. freeifaddrs(raw);
  103. }
  104. };
  105. #else
  106. struct adapter_info
  107. {
  108. unique_ptr<IP_ADAPTER_ADDRESSES, void(*)(IP_ADAPTER_ADDRESSES*)> addrs;
  109. MIB_IF_ROW2 row;
  110. adapter_info(DWORD index)
  111. {
  112. memset(&row, 0, sizeof(row));
  113. row.InterfaceIndex = index;
  114. if (!GetIfEntry2(&row)) {
  115. throw winapi_error("GetIfEntry2");
  116. }
  117. ULONG size = 0;
  118. ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
  119. GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_INCLUDE_GATEWAYS;
  120. auto err = GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, &size);
  121. if (err != ERROR_BUFFER_OVERFLOW) {
  122. throw winapi_error("GetAdaptersAddresses");
  123. }
  124. // just to be on the safe side, in case things change in between the two calls.
  125. size += 1024;
  126. addrs = { reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(size)), [](IP_ADAPTER_ADDRESSES* p) {
  127. free(p);
  128. }};
  129. err = GetAdaptersAddresses(AF_UNSPEC, flags, addrs.get(), &size);
  130. if (err) {
  131. throw winapi_error("GetAdaptersAddresses");
  132. }
  133. }
  134. void visit(DWORD index, std::function<void(IP_ADAPTER_ADDRESSES*>)> f)
  135. {
  136. for (auto addrs = addrs.get(); addrs; addrs = addrs->Next) {
  137. if (addrs->Index == index) {
  138. f(addrs);
  139. return;
  140. }
  141. }
  142. throw invalid_argument("invalid adapter index " + to_string(index));
  143. }
  144. };
  145. #endif
  146. #if BOOST_OS_MACOS
  147. typedef cf_ref<CFDictionaryRef> cf_dict_ref;
  148. cf_dict_ref plist_open_as_dict(const string& filename)
  149. {
  150. auto url = make_cf_ref(CFURLCreateFromFileSystemRepresentation(
  151. kCFAllocatorDefault, reinterpret_cast<const UInt8*>(filename.c_str()),
  152. filename.size(), false));
  153. if (!url) {
  154. throw runtime_error("CFURLCreateFromFileSystemRepresentation: " + filename);
  155. }
  156. auto stream = make_cf_ref(CFReadStreamCreateWithFile(kCFAllocatorDefault, url.get()));
  157. if (!stream) {
  158. throw runtime_error("CFReadStreamCreateWithFile: " + filename);
  159. }
  160. if (!CFReadStreamOpen(stream.get())) {
  161. throw runtime_error("CFReadStreamOpen: " + filename);
  162. }
  163. auto plist = make_cf_ref(CFPropertyListCreateWithStream(kCFAllocatorDefault, stream.get(), 0,
  164. kCFPropertyListImmutable, NULL, NULL));
  165. CFReadStreamClose(stream.get());
  166. if (!plist) {
  167. throw runtime_error("CFPropertyListCreateWithStream: " + filename);
  168. }
  169. return plist.as<CFDictionaryRef>();
  170. }
  171. void cf_dict_for_each(const cf_ref<CFDictionaryRef>& dict, function<void(const string&, const cf_ref<CFTypeRef>&)> f)
  172. {
  173. typedef decltype(&f) F;
  174. CFDictionaryApplyFunction(dict.get(), [](const void* key, const void* value, void* applier) {
  175. reinterpret_cast<F>(applier)->operator()(
  176. from_cf_string(cf_cast<CFStringRef>(key)),
  177. make_cf_view(reinterpret_cast<CFTypeRef>(value)));
  178. }, &f);
  179. }
  180. template<typename T> cf_ref<T> cf_dict_get(const cf_ref<CFDictionaryRef>& dict, const string& key)
  181. {
  182. const void* value;
  183. if (!CFDictionaryGetValueIfPresent(dict.get(), to_cf_string(key).get(), &value) || !value) {
  184. throw out_of_range("no such key: " + key);
  185. }
  186. return make_cf_view(cf_cast<T>(value));
  187. }
  188. string get_macos_pretty_name(const string& device)
  189. {
  190. try {
  191. string pretty;
  192. auto prefs = plist_open_as_dict("/Library/Preferences/SystemConfiguration/preferences.plist");
  193. auto services = cf_dict_get<CFDictionaryRef>(prefs, "NetworkServices");
  194. // Structure of preferences.plist:
  195. //
  196. // NetworkServices (CFDictionary):
  197. // - <UUID> (CFDictionary)
  198. // - Interface (CFDictionary)
  199. // - DeviceName (CFString)
  200. // - UserDefinedName (CFString)
  201. cf_dict_for_each(services, [&device, &pretty](const string& key, const cf_ref<CFTypeRef>& value) {
  202. if (!pretty.empty()) {
  203. return;
  204. }
  205. auto interface = cf_dict_get<CFDictionaryRef>(value.as<CFDictionaryRef>(), "Interface");
  206. auto cf_device = from_cf_string(cf_dict_get<CFStringRef>(interface, "DeviceName"));
  207. if (device == cf_device) {
  208. pretty = from_cf_string(cf_dict_get<CFStringRef>(interface, "UserDefinedName"));
  209. }
  210. });
  211. return pretty;
  212. } catch (const exception& e) {
  213. return "";
  214. }
  215. }
  216. #endif
  217. }
  218. eth_interface::eth_interface(const string& name)
  219. {
  220. m_index = name_to_index(name.c_str(), m_pcap_name);
  221. if (!m_index) {
  222. throw invalid_argument("no such interface: " + name);
  223. }
  224. pcap_devlist::visit(*this, [&] (const pcap_if_t& dev) {
  225. if (dev.flags & PCAP_IF_LOOPBACK) {
  226. throw invalid_argument("loopback device: " + name);
  227. }
  228. });
  229. #if !BOOST_OS_WINDOWS
  230. if_addrs addrs;
  231. for (auto ifa = addrs.raw; ifa; ifa = ifa->ifa_next) {
  232. if (ifa->ifa_name == name) {
  233. auto* raw = if_addrs::get_mac_addr(ifa);
  234. if (raw) {
  235. m_mac_addr = mac_addr::from_raw(raw);
  236. #if BOOST_OS_LINUX
  237. m_is_bridge = (access(("/sys/class/net/" + name + "/bridge").c_str(), F_OK) == 0);
  238. m_pretty_name = nm_get_connection(name);
  239. #else
  240. m_is_bridge = (reinterpret_cast<if_data*>(ifa->ifa_data)->ifi_type == IFT_BRIDGE);
  241. #if BOOST_OS_MACOS
  242. m_pretty_name = get_macos_pretty_name(name);
  243. #endif
  244. #endif
  245. return;
  246. }
  247. }
  248. }
  249. throw invalid_argument("not an Ethernet interface: " + name);
  250. #else
  251. adapter_info info(m_index);
  252. if (!info.row.InterfaceAndOperStatusFlags.HardwareInterface) {
  253. throw invalid_argument("not a hardware interface: " + name);
  254. }
  255. info.visit(m_index, [&](IP_ADAPTER_ADDRESSES* a) {
  256. if (a->IfType != IF_TYPE_ETHERNET_CSMACD && a->IfType != IF_TYPE_IEEE80211) {
  257. throw invalid_argument("not an Ethernet interface: " + name);
  258. } else if (a->PhysicalAdressLength != mac_addr::length) {
  259. throw runtime_error("unexpected address size: " + to_string(a->PhysicalAdressLength));
  260. }
  261. m_mac_addr = mac_addr::from_raw(a->PhysicalAddress);
  262. m_pretty_name = a->FriendlyName;
  263. // TODO how to detect a bridge interface on Windows?
  264. });
  265. #endif
  266. }
  267. eth_interface::~eth_interface()
  268. {
  269. }
  270. string eth_interface::get_name() const
  271. {
  272. #if !BOOST_OS_WINDOWS
  273. return m_pcap_name;
  274. #else
  275. char buf[IF_NAMESIZE];
  276. return if_indextoname(m_index, buf);
  277. #endif
  278. }
  279. vector<ip_net> eth_interface::list_networks(bool ipv4_only) const
  280. {
  281. vector<ip_net> ret;
  282. pcap_devlist::visit(*this, [&] (const pcap_if_t& dev) {
  283. for (auto addr = dev.addresses; addr; addr = addr->next) {
  284. if (ipv4_only && addr->addr->sa_family != AF_INET) {
  285. continue;
  286. }
  287. try {
  288. ret.push_back({ ip_from_sockaddr(addr->addr), ip_from_sockaddr(addr->netmask) });
  289. } catch (const invalid_argument&) {
  290. // ignore
  291. }
  292. }
  293. });
  294. return ret;
  295. }
  296. vector<eth_interface> eth_interface::list()
  297. {
  298. vector<eth_interface> ret;
  299. pcap_devlist devs;
  300. for (auto dev = devs.raw; dev; dev = dev->next) {
  301. try {
  302. ret.push_back({ dev->name });
  303. } catch (const exception& e) {
  304. // ignore
  305. }
  306. }
  307. return ret;
  308. }
  309. }