ethsock.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. /**
  2. * nmrpflash - Netgear Unbrick Utility
  3. * Copyright (C) 2016-2020 Joseph Lehner <joseph.c.lehner@gmail.com>
  4. *
  5. * nmrpflash is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * nmrpflash is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with nmrpflash. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. #if 0
  20. #include <netlink/route/addr.h>
  21. #endif
  22. #include <sys/socket.h>
  23. #include <sys/ioctl.h>
  24. #include <net/if.h>
  25. #include <fstream>
  26. #include <cstdarg>
  27. #include "ethsock.h"
  28. #if BOOST_OS_LINUX
  29. #include <linux/if_packet.h>
  30. #endif
  31. using namespace std;
  32. namespace nmrpflash {
  33. namespace {
  34. struct eth_header
  35. {
  36. uint8_t dest[6];
  37. uint8_t src[6];
  38. uint16_t proto;
  39. } __attribute__((packed));
  40. class pcap_devices
  41. {
  42. pcap_if_t* m_devs = nullptr;
  43. public:
  44. pcap_devices()
  45. {
  46. char errbuf[PCAP_ERRBUF_SIZE];
  47. if (pcap_findalldevs(&m_devs, errbuf) != 0) {
  48. throw runtime_error(errbuf);
  49. }
  50. }
  51. ~pcap_devices()
  52. {
  53. pcap_freealldevs(m_devs);
  54. }
  55. list<const pcap_if_t*> get() const
  56. {
  57. list<const pcap_if_t*> ret;
  58. for (pcap_if_t* dev = m_devs; dev; dev = dev->next) {
  59. ret.push_back(dev);
  60. }
  61. return ret;
  62. }
  63. const pcap_if_t* get(const string& intf)
  64. {
  65. for (pcap_if_t* dev = m_devs; dev; dev = dev->next) {
  66. if (dev->name == intf) {
  67. return dev;
  68. }
  69. }
  70. throw invalid_argument("No such interface: " + intf);
  71. }
  72. };
  73. void check_mac_addr(const mac_addr& mac)
  74. {
  75. if (mac == mac_addr::none || mac == mac_addr::broadcast) {
  76. throw invalid_argument("Invalid MAC address: " + stringify(mac));
  77. }
  78. }
  79. void check_ip_addr(const ip_addr& ip)
  80. {
  81. if (!ip || ip == ip_addr(255, 255, 255, 255)) {
  82. throw invalid_argument("Invalid IP address: " + stringify(ip));
  83. }
  84. }
  85. #if BOOST_OS_WINDOWS
  86. GUID guid_from_str(const string& str)
  87. {
  88. GUID guid;
  89. int n;
  90. sscanf(str.c_str(),
  91. "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X%n",
  92. &guid.Data1, &guid.Data2, &guid.Data3,
  93. &guid.Data4[0], &guid.Data4[1], &guid.Data4[2],
  94. &guid.Data4[3], &guid.Data4[4], &guid.Data4[5],
  95. &guid.Data4[6], &guid.Data4[7], &n);
  96. if (n != 36) {
  97. throw invalid_argument("Not a valid GUID: " + str);
  98. }
  99. return ret;
  100. }
  101. string guid_to_str(const GUID& guid)
  102. {
  103. string ret('\0', 37);
  104. snprintf(&ret[0], ret.size(),
  105. "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  106. guid.Data1, guid.Data2, guid.Data3,
  107. guid.Data4[0], guid.Data4[1], guid.Data4[2],
  108. guid.Data4[3], guid.Data4[4], guid.Data4[5],
  109. guid.Data4[6], guid.Data4[7]);
  110. return ret;
  111. }
  112. void get_if_entry(MIB_IF_ROW2* row, NET_IFINDEX index)
  113. {
  114. memset(row, 0, sizeof(*row));
  115. row->InterfaceIndex = index;
  116. DWORD err = GetIfEntry2(row);
  117. if (err) {
  118. throw winapi_error("GetIfEntry2", err);
  119. }
  120. }
  121. #elif BOOST_OS_LINUX
  122. bool linux_sys_read_b(const string& intf, const string& file, bool defval)
  123. {
  124. ifstream in((boost::format("/sys/class/net/%s/%s") % intf % file).str().c_str());
  125. return in ? (in.get() == '1') : defval;
  126. }
  127. #endif
  128. #if 0
  129. #if defined(NMRPFLASH_LINUX) && false
  130. auto create_nl_route_socket()
  131. {
  132. auto sock = wrap_unique(nl_socket_alloc(), &nl_socket_free);
  133. if (sock) {
  134. if (nl_connect(sock.get(), NETLINK_ROUTE) != 0) {
  135. throw libnl_error("nl_connect");
  136. }
  137. return sock;
  138. } else {
  139. throw libnl_error("nl_socket_alloc");
  140. }
  141. }
  142. auto build_nl_ip(const ip_addr& ip)
  143. {
  144. uint32_t raw = ip.to_uint();
  145. auto ret = wrap_unique(nl_addr_build(AF_INET, &raw, 4), &nl_addr_put);
  146. if (!ret) {
  147. throw libnl_error("nl_addr_build");
  148. }
  149. if (ip.pfxlen()) {
  150. nl_addr_set_prefixlen(ret.get(), ip.pfxlen());
  151. }
  152. return ret;
  153. }
  154. #endif
  155. static int systemf(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
  156. static int systemf(const char* fmt, ...)
  157. {
  158. va_list va;
  159. va_start(va, fmt);
  160. char cmd[1024];
  161. int n = vsnprintf(cmd, sizeof(cmd), fmt, va);
  162. if (n >= sizeof(cmd)) {
  163. return -1;
  164. }
  165. int ret = system(cmd);
  166. va_end(va);
  167. return ret;
  168. }
  169. #endif
  170. #if !BOOST_OS_WINDOWS && !BOOST_OS_LINUX
  171. static void bsd_set_intf_up(const string& intf, bool up, int fd = -1)
  172. {
  173. scoped_fd sfd;
  174. if (fd == -1) {
  175. sfd.reset(xsocket(AF_INET, SOCK_DGRAM, 0));
  176. fd = *sfd;
  177. }
  178. struct ifreq ifr;
  179. strncpy(ifr.ifr_name, intf.c_str(), IFNAMSIZ);
  180. if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
  181. throw errno_error("ioctl(SIOCGIFFLAGS)");
  182. }
  183. if (!up) {
  184. ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
  185. } else {
  186. ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
  187. }
  188. if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
  189. throw errno_error("ioctl(SIOCSIFFLAGS)");
  190. }
  191. }
  192. #endif
  193. }
  194. eth_interface::eth_interface(const string& intf)
  195. {
  196. init_from_name(intf);
  197. pcap_devices devs;
  198. init_from_pcap(devs.get(m_name));
  199. }
  200. eth_interface::eth_interface(const pcap_if_t* dev)
  201. {
  202. init_from_name(dev->name);
  203. init_from_pcap(dev);
  204. }
  205. eth_interface::~eth_interface()
  206. {
  207. try {
  208. for (auto ip : m_undo_ip) {
  209. del_ip(ip);
  210. }
  211. for (auto arp : m_undo_arp) {
  212. del_arp(arp.first);
  213. }
  214. } catch (const exception& e) {
  215. // yum!
  216. }
  217. }
  218. void eth_interface::init_index(const string& intf)
  219. {
  220. m_index = if_nametoindex(intf.c_str());
  221. if (!m_index) {
  222. throw invalid_argument("Interface does not exist: " + intf);
  223. }
  224. }
  225. void eth_interface::init_from_name(const string& intf)
  226. {
  227. // On Windows, `intf` might be either the pcap name, which is
  228. // `\Device\NPF_{GUID}`, or the ANSI interface name!
  229. #if !BOOST_OS_WINDOWS
  230. m_name = intf;
  231. init_index(intf);
  232. #else
  233. if (intf.find("\\Device\\NPF_{") == string::npos) {
  234. if (intf.find("net") == 0 && intf.find('_') == string::npos) {
  235. m_index = stoi(intf.substr(3));
  236. } else {
  237. init_index(intf);
  238. }
  239. DWORD err = ConvertInterfaceIndexToLuid(m_index, &m_luid);
  240. if (err) {
  241. throw winapi_error("ConvertInterfaceIndexToLuid", err);
  242. }
  243. err = ConvertInterfaceLuidToGuid(index, &m_guid);
  244. if (err) {
  245. throw winapi_error("ConvertInterfaceLuidToGuid", err);
  246. }
  247. m_name = "\\Device\\NPF_{" + guid_to_str(m_guid) + "}";
  248. } else {
  249. m_name = intf;
  250. m_guid = guid_from_str(intf.substr(13, 36));
  251. DWORD err = ConvertInterfaceGuidToLuid(&guid, &m_luid);
  252. if (err) {
  253. throw winapi_error("ConvertInterfaceGuidToLuid", err);
  254. }
  255. err = ConvertInterfaceLuidToIndex(&m_luid, &m_index);
  256. if (err) {
  257. throw winapi_error("ConvertInterfaceGuidToLuid", err);
  258. }
  259. }
  260. #endif
  261. }
  262. void eth_interface::init_from_pcap(const pcap_if_t* dev)
  263. {
  264. #if !BOOST_OS_WINDOWS
  265. for (pcap_addr_t* addr = dev->addresses; addr; addr = addr->next) {
  266. if (addr->addr->sa_family == AF_PACKET) {
  267. void* src;
  268. #if BOOST_OS_LINUX
  269. src = reinterpret_cast<sockaddr_ll*>(addr->addr)->sll_addr;
  270. #else
  271. src = LLADDR(reinterpret_cast<sockaddr_dl*>(addr->addr));
  272. #endif
  273. m_mac = mac_addr(src);
  274. return;
  275. }
  276. }
  277. throw runtime_error("Failed to get hwaddr of interface");
  278. #else
  279. MIB_IF_ROW2 row;
  280. get_if_entry(&row, m_index);
  281. memcpy(m_mac, row.PhysicalAddress, sizeof(m_mac));
  282. char ansi[IF_NAMESIZE];
  283. m_ansi = if_indextoname(m_index, ansi);
  284. m_alias = row.Alias;
  285. #endif
  286. }
  287. bool eth_interface::is_unplugged() const
  288. {
  289. #if BOOST_OS_WINDOWS
  290. MIB_IF_ROW2 row;
  291. get_if_entry(&row, m_index);
  292. return row.InterfaceAndOperStatusFlags.NotMediaConnected;
  293. #elif BOOST_OS_LINUX
  294. return !linux_sys_read_b(m_name, "carrier", true);
  295. #else
  296. return false;
  297. #endif
  298. }
  299. void eth_interface::add_del_ip(const ip_addr& ip, bool add)
  300. {
  301. check_ip_addr(ip);
  302. cmdfmt cmd;
  303. #if BOOST_OS_WINDOWS
  304. if (add) {
  305. cmd = cmdfmt(L"netsh interface add address %s addr=%s mask=%s gateway=0.0.0.0")
  306. % quote(m_alias) % stringify(ip.address()) % stringify(ip.netmask());
  307. } else {
  308. cmd = cmdfmt(L"netsh interface delete address %s addr=%s")
  309. % quote(m_alias) % stringify(ip.address());
  310. }
  311. #elif BOOST_OS_LINUX
  312. cmd = cmdfmt("ip address %s %s dev %s") % (add ? "add" : "del")
  313. % stringify(ip) % quote(m_name);
  314. #else
  315. cmd = cmdfmt("ifconfig %s inet %s netmask %s %s") % quote(m_name)
  316. % stringify(ip.address()) % stringify(ip.netmask()) % (add ? "add" : "delete");
  317. #endif
  318. run(cmd, add);
  319. #if 0
  320. #if defined(NMRPFLASH_WINDOWS)
  321. MIB_UNICASTIPADDRESS_ROW row;
  322. memset(&row, 0, sizeof(row));
  323. row.InterfaceIndex = m_index;
  324. row.PrefixOrigin = IpPrefixOriginManual;
  325. row.SuffixOrigin = IpPrefixOriginManual;
  326. row.OnLinkPrefixLength = pfix;
  327. row.SkipAsSource = false;
  328. row.PreferredLifetime = 0xffffffff;
  329. row.ValidLifetime = 0xffffffff;
  330. ip.apply_to(row.Address.Ipv4);
  331. if (add) {
  332. DWORD err = CreateUnicastIpAddressEntry(&row);
  333. if (err && err != ERROR_OBJECT_ALREADY_EXISTS) {
  334. throw winapi_error("CreateUnicastIpAddressEntry", err);
  335. }
  336. } else {
  337. DWORD err = DeleteUnicastIpAddressEntry(&row);
  338. if (err) {
  339. log::d("DeleteUnicastIpAddressEntry: %d", err);
  340. }
  341. }
  342. #elif defined(NMRPFLASH_LINUX)
  343. auto ra = wrap_unique(rtnl_addr_alloc(), rtnl_addr_put);
  344. if (!ra) {
  345. throw libnl_error("rtnl_addr_alloc");
  346. }
  347. rtnl_addr_set_ifindex(ra.get(), m_index);
  348. rtnl_addr_set_local(ra.get(), build_nl_ip(ip).get());
  349. rtnl_addr_set_broadcast(ra.get(), build_nl_ip(ip.broadcast()).get());
  350. auto sk = create_nl_route_socket();
  351. int err = add ? rtnl_addr_add(*sk, *ra, 0) : rtnl_addr_delete(*sk, *ra, 0);
  352. if (add) {
  353. if (err && err != -NLE_EXIST) {
  354. throw libnl_error("rtnl_addr_add");
  355. }
  356. } else if (err) {
  357. log::d("rtnl_addr_delete: %d", err);
  358. }
  359. #else // NMRPFLASH_OSX (or any other BSD)
  360. struct ifaliasreq ifra;
  361. memset(&ifra, 0, sizeof(ifra));
  362. strncpy(ifra.ifra_name, m_name.c_str(), IFNAMSIZ);
  363. ip.apply_to(ifra.ifra_addr);
  364. ip.netmask().apply_to(ifra.ifra_mask);
  365. scoped_fd fd(xsocket(AF_INET, SOCK_DGRAM, 0);
  366. if (ioctl(*fd, add ? SIOCAIFADDR : SIOCDIFADDR, &ifra) != 0) {
  367. if (add) {
  368. throw errno_error("ioctl(SIOCAIFADDR");
  369. } else {
  370. log::d("ioctl(SIOCDIFADDR): %d", errno);
  371. }
  372. }
  373. bsd_set_intf_up(m_name, true, *fd);
  374. #endif
  375. #endif
  376. if (add) {
  377. m_undo_ip.insert(ip);
  378. } else {
  379. m_undo_ip.erase(ip);
  380. }
  381. }
  382. void eth_interface::add_del_arp(const mac_addr& mac, const ip_addr& ip, bool add)
  383. {
  384. #if 0
  385. #if defined(NMRPFLASH_WINDOWS)
  386. MIB_IPNETROW arp = {
  387. .dwIndex = m_index,
  388. .dwPhysAddrLen = sizeof(mac),
  389. .dwAddr = ip,
  390. .dwType = MIB_IPNET_TYPE_STATIC,
  391. };
  392. mac.apply_to(&arp.bPhysAddr);
  393. DWORD err = add ? CreateIpNetEntry(&arp) : DeleteIpNetEntry(&arp);
  394. if (err)
  395. throw winapi_error(add ? "CreateIpNetEntry" : "DelteIpNetEntry", err);
  396. }
  397. #elif defined(NMRPFLASH_LINUX)
  398. arpreq arp;
  399. memset(&arp, 0, sizeof(arp));
  400. arp.arp_ha.sa_family = ARPHRD_ETHER;
  401. arp.arp_flags = ATF_PERM | ATF_COM;
  402. mac.apply_to(&arp.arp_ha.sa_data);
  403. ip.apply_to(&arp.arp_pa);
  404. int fd = socket(AF_INET, SOCK_DGRAM, 0);
  405. if (fd < 0) {
  406. throw errno_error("socket");
  407. }
  408. bool err = (ioctl(fd, add ? SIOCSARP : SIOCDARP, &req) < 0);
  409. close(fd);
  410. if (err) {
  411. throw errno_error(add ? "ioctl(SIOCSARP)" : "ioctl(SIOCDARP)");
  412. }
  413. #else
  414. #endif
  415. #endif
  416. cmdfmt cmd;
  417. if (add) {
  418. #if BOOST_OS_WINDOWS
  419. cmd = cmdfmt(L"netsh add neighbors interface=%s address=%s neighbor=%s store=active"
  420. % quote(m_alias) % stringify(ip.address()) % mac.to_string('-');
  421. #else
  422. cmd = cmdfmt("arp -s %s %s") % stringify(ip.address()) % stringify(mac);
  423. #endif
  424. } else {
  425. #if BOOST_OS_WINDOWS
  426. cmd = cmdfmt(L"netsh delete neighbors interface=%s address=%s")
  427. % quote(m_alias) % stringify(ip.address()));
  428. #else
  429. cmd = cmdfmt("arp -d %s") % stringify(ip.address());
  430. #endif
  431. }
  432. run(cmd, add);
  433. if (add) {
  434. m_undo_arp[ip] = mac;
  435. } else {
  436. m_undo_arp.erase(ip);
  437. }
  438. }
  439. list<eth_interface> eth_interface::all()
  440. {
  441. list<eth_interface> ret;
  442. pcap_devices devs;
  443. for (auto dev : devs.get()) {
  444. if (if_nametoindex(dev->name)) {
  445. ret.emplace_back(dev);
  446. }
  447. }
  448. return ret;
  449. }
  450. eth_sock::eth_sock(eth_interface& iface, uint16_t proto)
  451. : m_iface(iface), m_proto(proto), m_timeout(0), m_stp_enabled(iface.is_stp_enabled())
  452. {
  453. char errbuf[PCAP_ERRBUF_SIZE];
  454. m_pcap = pcap_open_live(iface.name().c_str(), BUFSIZ, 1, 1, errbuf);
  455. if (!m_pcap) {
  456. throw runtime_error(errbuf);
  457. } else if (*errbuf) {
  458. log::w(errbuf);
  459. }
  460. try {
  461. init();
  462. } catch (const exception& e) {
  463. shutdown();
  464. throw e;
  465. }
  466. }
  467. void eth_sock::send(const string& buf, const mac_addr& dest)
  468. {
  469. send(buf.data(), buf.size(), dest);
  470. }
  471. void eth_sock::send(const void* data, size_t size, const mac_addr& dest)
  472. {
  473. size += sizeof(eth_header);
  474. auto pkt = make_unique<char[]>(size);
  475. auto hdr = reinterpret_cast<eth_header*>(pkt.get());
  476. m_iface.hwaddr().apply_to(hdr->src);
  477. hdr->proto = htons(m_proto);
  478. if (m_peer == mac_addr::none) {
  479. dest.apply_to(hdr->dest);
  480. } else {
  481. m_peer.apply_to(hdr->dest);
  482. }
  483. #if BOOST_OS_WINDOWS
  484. if (pcap_sendpacket(m_pcap, pkt.get(), size) != 0) {
  485. throw pcap_error("pcap_sendpacket", m_pcap);
  486. }
  487. #else
  488. if (pcap_inject(m_pcap, pkt.get(), size) != size) {
  489. throw pcap_error("pcap_inject", m_pcap);
  490. }
  491. #endif
  492. }
  493. std::string eth_sock::recv(unsigned timeout, mac_addr* src)
  494. {
  495. if (!timeout) {
  496. timeout = m_timeout;
  497. }
  498. if (timeout) {
  499. #if BOOST_OS_WINDOWS
  500. DWORD ret = WaitForSingleObject(m_handle, timeout);
  501. if (ret == WAIT_TIMEOUT) {
  502. return pkt::empty();
  503. } else if (ret != WAIT_OBJECT_0) {
  504. throw winapi_error("WaitForSingleObject", ret);
  505. }
  506. #else
  507. if (!select_readfd(m_fd, timeout)) {
  508. return {};
  509. }
  510. #endif
  511. }
  512. pcap_pkthdr* hdr;
  513. const u_char* buf;
  514. int status = pcap_next_ex(m_pcap, &hdr, &buf);
  515. if (!status) {
  516. return {};
  517. } else if (status == PCAP_ERROR) {
  518. throw pcap_error("pcap_next_ex", m_pcap);
  519. } else if (status != 1) {
  520. throw runtime_error("pcap_next_ex: error " + to_string(status));
  521. } else if (hdr->caplen < sizeof(eth_header)) {
  522. throw runtime_error("Received short packet: " + to_string(hdr->caplen) + "b");
  523. }
  524. if (src) {
  525. *src = mac_addr(&buf[6]);
  526. }
  527. return string(reinterpret_cast<const char*>(buf + 14), hdr->caplen - 14);
  528. }
  529. void eth_sock::init()
  530. {
  531. if (pcap_datalink(m_pcap) != DLT_EN10MB) {
  532. throw invalid_argument("Not an Ethernet interface: " + m_iface.name());
  533. }
  534. #if BOOST_OS_WINDOWS
  535. m_handle = pcap_getevent(m_pcap);
  536. if (!m_handle) {
  537. throw pcap_error("pcap_getevent", m_pcap);
  538. }
  539. if (pcap_setmintocopy(m_pcap, 1) != 0) {
  540. throw pcap_error("pcap_setmintocopy", m_pcap);
  541. }
  542. #else
  543. m_fd = pcap_get_selectable_fd(m_pcap);
  544. if (m_fd == -1) {
  545. throw pcap_error("pcap_get_selectable_fd", m_pcap);
  546. }
  547. #endif
  548. bpf_program bpf;
  549. string filter = (boost::format("ether proto 0x%04x and not ether src %s")
  550. % m_proto % stringify(m_iface.hwaddr())).str();
  551. if (pcap_compile(m_pcap, &bpf, filter.c_str(), 0, 0) != 0) {
  552. throw pcap_error("pcap_compile", m_pcap);
  553. }
  554. int err = pcap_setfilter(m_pcap, &bpf);
  555. pcap_freecode(&bpf);
  556. if (err) {
  557. throw pcap_error("pcap_setfilter", m_pcap);
  558. }
  559. m_iface.enable_stp(false);
  560. }
  561. void eth_sock::shutdown()
  562. {
  563. if (m_pcap) {
  564. pcap_close(m_pcap);
  565. m_pcap = nullptr;
  566. m_iface.enable_stp(m_stp_enabled);
  567. }
  568. }
  569. }