diff options
Diffstat (limited to 'src/osd/modules/netdev/pcap.cpp')
-rw-r--r-- | src/osd/modules/netdev/pcap.cpp | 248 |
1 files changed, 132 insertions, 116 deletions
diff --git a/src/osd/modules/netdev/pcap.cpp b/src/osd/modules/netdev/pcap.cpp index 68e9512b14a..8ad62773d58 100644 --- a/src/osd/modules/netdev/pcap.cpp +++ b/src/osd/modules/netdev/pcap.cpp @@ -1,22 +1,31 @@ // license:BSD-3-Clause // copyright-holders:Carl -#if defined(OSD_NET_USE_PCAP) - -#include "emu.h" -#include "osdnet.h" #include "netdev_module.h" + #include "modules/osdmodule.h" + +#if defined(OSD_NET_USE_PCAP) + +#include "netdev_common.h" + #include "modules/lib/osdlib.h" +#include "osdcore.h" // osd_printf_* + +#include "util/strformat.h" // string_format + +#include <memory> +#include <vector> + #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) #include <windows.h> #undef interface #define LIB_NAME "wpcap.dll" #elif defined(SDLMAME_MACOSX) +#include <atomic> #include <pthread.h> -#include <libkern/OSAtomic.h> #define LIB_NAME "libpcap.dylib" #else @@ -25,6 +34,10 @@ #include <pcap.h> +namespace osd { + +namespace { + // Typedefs for dynamically loaded functions typedef int (*pcap_findalldevs_fn)(pcap_if_t **, char *); typedef pcap_t *(*pcap_open_live_fn)(const char *, int, int, int, char *); @@ -39,16 +52,16 @@ typedef int (*pcap_dispatch_fn)(pcap_t *, int, pcap_handler, u_char *); class pcap_module : public osd_module, public netdev_module { public: - pcap_module() - : osd_module(OSD_NETDEV_PROVIDER, "pcap"), netdev_module(), - pcap_findalldevs_dl(nullptr), pcap_open_live_dl(nullptr), pcap_next_ex_dl(nullptr), pcap_compile_dl(nullptr), - pcap_close_dl(nullptr), pcap_setfilter_dl(nullptr), pcap_sendpacket_dl(nullptr), pcap_set_datalink_dl(nullptr), pcap_dispatch_dl(nullptr) + pcap_module() : + osd_module(OSD_NETDEV_PROVIDER, "pcap"), netdev_module(), + pcap_findalldevs_dl(nullptr), pcap_open_live_dl(nullptr), pcap_next_ex_dl(nullptr), pcap_compile_dl(nullptr), + pcap_close_dl(nullptr), pcap_setfilter_dl(nullptr), pcap_sendpacket_dl(nullptr), pcap_set_datalink_dl(nullptr), pcap_dispatch_dl(nullptr) { } virtual ~pcap_module() { } - virtual int init(const osd_options &options) override; + virtual int init(osd_interface &osd, const osd_options &options) override; virtual void exit() override; virtual bool probe() override @@ -76,6 +89,20 @@ public: return true; } + virtual std::unique_ptr<network_device> open_device(int id, network_handler &handler) override; + virtual std::vector<network_device_info> list_devices() override; + +private: + struct device_info + { + std::string name; + std::string description; + }; + + class netdev_pcap; + + std::vector<device_info> m_devices; + osd::dynamic_module::ptr pcap_dll; pcap_findalldevs_fn pcap_findalldevs_dl; @@ -89,192 +116,161 @@ public: pcap_dispatch_fn pcap_dispatch_dl; }; -// FIXME: bridge between pcap_module and netdev_pcap -static pcap_module *module = nullptr; - -#ifdef SDLMAME_MACOSX -struct netdev_pcap_context { - uint8_t *pkt; - int len; - pcap_t *p; - - uint8_t packets[32][1600]; - int packetlens[32]; - int head; - int tail; -}; -#endif - -class netdev_pcap : public osd_netdev +class pcap_module::netdev_pcap : public network_device_base { public: - netdev_pcap(const char *name, class device_network_interface *ifdev, int rate); + netdev_pcap(pcap_module &module, const char *name, network_handler &handler); ~netdev_pcap(); - virtual int send(uint8_t *buf, int len) override; - virtual void set_mac(const char *mac) override; + virtual int send(void const *buf, int len) override; + protected: virtual int recv_dev(uint8_t **buf) override; + private: + pcap_module &m_module; pcap_t *m_p; #ifdef SDLMAME_MACOSX - struct netdev_pcap_context m_ctx; pthread_t m_thread; + uint8_t *pkt; + int len; + pcap_t *p; + uint8_t m_pktbuf[2048]; + + uint8_t m_packets[32][1600]; + int m_packetlens[32]; + std::atomic<int> m_head; + std::atomic<int> m_tail; + + static void pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes); + static void *pcap_blocker(void *arg); #endif }; #ifdef SDLMAME_MACOSX -static void netdev_pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { - struct netdev_pcap_context *ctx = (struct netdev_pcap_context*)user; +void pcap_module::netdev_pcap::pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) +{ + netdev_pcap *const ctx = reinterpret_cast<netdev_pcap *>(user); - if(!ctx->p) return; + if (!ctx->p) + return; - if(OSAtomicCompareAndSwapInt((ctx->head+1) & 0x1F, ctx->tail, &ctx->tail)) { + int const curr = ctx->m_head.load(std::memory_order_relaxed); + int const next = (curr + 1) & 0x1f; + if (ctx->m_tail.load(std::memory_order_acquire) == next) + { printf("buffer full, dropping packet\n"); return; } - memcpy(ctx->packets[ctx->head], bytes, h->len); - ctx->packetlens[ctx->head] = h->len; - OSAtomicCompareAndSwapInt(ctx->head, (ctx->head+1) & 0x1F, &ctx->head); + memcpy(ctx->m_packets[curr], bytes, h->len); + ctx->m_packetlens[curr] = h->len; + ctx->m_head.store(next, std::memory_order_release); } -static void *netdev_pcap_blocker(void *arg) { - struct netdev_pcap_context *ctx = (struct netdev_pcap_context*)arg; +void *pcap_module::netdev_pcap::pcap_blocker(void *arg) +{ + netdev_pcap *const ctx = reinterpret_cast<netdev_pcap *>(arg); - while(ctx && ctx->p) { - (*module->pcap_dispatch_dl)(ctx->p, 1, netdev_pcap_handler, (u_char*)ctx); - } + while (ctx && ctx->p) + (*ctx->m_module.pcap_dispatch_dl)(ctx->p, 1, &netdev_pcap::pcap_handler, reinterpret_cast<u_char *>(ctx)); - return 0; + return nullptr; } #endif -netdev_pcap::netdev_pcap(const char *name, class device_network_interface *ifdev, int rate) - : osd_netdev(ifdev, rate) +pcap_module::netdev_pcap::netdev_pcap(pcap_module &module, const char *name, network_handler &handler) : + network_device_base(handler), + m_module(module), + m_p(nullptr) { char errbuf[PCAP_ERRBUF_SIZE]; #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) - m_p = (*module->pcap_open_live_dl)(name, 65535, 1, -1, errbuf); + m_p = (*m_module.pcap_open_live_dl)(name, 65535, 1, -1, errbuf); #else - m_p = (*module->pcap_open_live_dl)(name, 65535, 1, 1, errbuf); + m_p = (*m_module.pcap_open_live_dl)(name, 65535, 1, 1, errbuf); #endif if(!m_p) { osd_printf_error("Unable to open %s: %s\n", name, errbuf); return; } - if ((*module->pcap_set_datalink_dl)(m_p, DLT_EN10MB) == -1) + if ((*m_module.pcap_set_datalink_dl)(m_p, DLT_EN10MB) == -1) { osd_printf_error("Unable to set %s to ethernet", name); - (*module->pcap_close_dl)(m_p); + (*m_module.pcap_close_dl)(m_p); m_p = nullptr; return; } - netdev_pcap::set_mac(get_mac()); #ifdef SDLMAME_MACOSX - m_ctx.head = 0; - m_ctx.tail = 0; - m_ctx.p = m_p; - pthread_create(&m_thread, nullptr, netdev_pcap_blocker, &m_ctx); + m_head = 0; + m_tail = 0; + p = m_p; + pthread_create(&m_thread, nullptr, &netdev_pcap::pcap_blocker, this); #endif } -void netdev_pcap::set_mac(const char *mac) +int pcap_module::netdev_pcap::send(void const *buf, int len) { - char filter[256]; - struct bpf_program fp; - if(!m_p) return; -#ifdef SDLMAME_MACOSX - sprintf(filter, "not ether src %.2X:%.2X:%.2X:%.2X:%.2X:%.2X and (ether dst %.2X:%.2X:%.2X:%.2X:%.2X:%.2X or ether multicast or ether broadcast or ether dst 09:00:07:ff:ff:ff)", (unsigned char)mac[0], (unsigned char)mac[1], (unsigned char)mac[2],(unsigned char)mac[3], (unsigned char)mac[4], (unsigned char)mac[5], (unsigned char)mac[0], (unsigned char)mac[1], (unsigned char)mac[2],(unsigned char)mac[3], (unsigned char)mac[4], (unsigned char)mac[5]); -#else - sprintf(filter, "ether dst %.2X:%.2X:%.2X:%.2X:%.2X:%.2X or ether multicast or ether broadcast", (unsigned char)mac[0], (unsigned char)mac[1], (unsigned char)mac[2],(unsigned char)mac[3], (unsigned char)mac[4], (unsigned char)mac[5]); -#endif - if ((*module->pcap_compile_dl)(m_p, &fp, filter, 1, 0) == -1) { - osd_printf_error("Error with pcap_compile\n"); - } - if ((*module->pcap_setfilter_dl)(m_p, &fp) == -1) { - osd_printf_error("Error with pcap_setfilter\n"); - } -} - -int netdev_pcap::send(uint8_t *buf, int len) -{ - int ret; - if(!m_p) { + if (!m_p) + { printf("send invoked, but no pcap context\n"); return 0; } - ret = (*module->pcap_sendpacket_dl)(m_p, buf, len); + int ret = (*m_module.pcap_sendpacket_dl)(m_p, reinterpret_cast<const u_char *>(buf), len); printf("sent packet length %d, returned %d\n", len, ret); return ret ? len : 0; - //return (!pcap_sendpacket_dl(m_p, buf, len))?len:0; + //return (!pcap_sendpacket_dl(m_p, reinterpret_cast<const u_char *>(buf), len)) ? len : 0; } -int netdev_pcap::recv_dev(uint8_t **buf) +int pcap_module::netdev_pcap::recv_dev(uint8_t **buf) { -#ifdef SDLMAME_MACOSX - uint8_t pktbuf[2048]; - int ret; - // no device open? - if(!m_p) return 0; + if (!m_p) + return 0; +#ifdef SDLMAME_MACOSX // Empty - if(OSAtomicCompareAndSwapInt(m_ctx.head, m_ctx.tail, &m_ctx.tail)) { + int const curr = m_tail.load(std::memory_order_relaxed); + if (m_head.load(std::memory_order_acquire) == curr) return 0; - } - memcpy(pktbuf, m_ctx.packets[m_ctx.tail], m_ctx.packetlens[m_ctx.tail]); - ret = m_ctx.packetlens[m_ctx.tail]; - OSAtomicCompareAndSwapInt(m_ctx.tail, (m_ctx.tail+1) & 0x1F, &m_ctx.tail); - *buf = pktbuf; + memcpy(m_pktbuf, m_packets[curr], m_packetlens[curr]); + int ret = m_packetlens[curr]; + m_tail.store((curr + 1) & 0x1f, std::memory_order_release); + *buf = m_pktbuf; return ret; #else struct pcap_pkthdr *header; - if(!m_p) return 0; - return ((*module->pcap_next_ex_dl)(m_p, &header, (const u_char **)buf) == 1)?header->len:0; + return ((*m_module.pcap_next_ex_dl)(m_p, &header, (const u_char **)buf) == 1)?header->len:0; #endif } -netdev_pcap::~netdev_pcap() +pcap_module::netdev_pcap::~netdev_pcap() { #ifdef SDLMAME_MACOSX - m_ctx.p = nullptr; + p = nullptr; pthread_cancel(m_thread); pthread_join(m_thread, nullptr); #endif - if(m_p) (*module->pcap_close_dl)(m_p); + if(m_p) (*m_module.pcap_close_dl)(m_p); m_p = nullptr; } -static CREATE_NETDEV(create_pcap) -{ - class netdev_pcap *dev = global_alloc(netdev_pcap(ifname, ifdev, rate)); - return dynamic_cast<osd_netdev *>(dev); -} - -int pcap_module::init(const osd_options &options) +int pcap_module::init(osd_interface &osd, const osd_options &options) { pcap_if_t *devs; char errbuf[PCAP_ERRBUF_SIZE]; - // FIXME: bridge between pcap_module and netdev_pcap - module = this; - if ((*pcap_findalldevs_dl)(&devs, errbuf) == -1) { osd_printf_error("Unable to get network devices: %s\n", errbuf); return 1; } - while(devs) + while (devs) { - if(devs->description) { - add_netdev(devs->name, devs->description, create_pcap); - } else { - add_netdev(devs->name, devs->name, create_pcap); - } + m_devices.emplace_back(device_info{ devs->name, devs->description ? devs->description : devs->name }); devs = devs->next; } return 0; @@ -282,15 +278,35 @@ int pcap_module::init(const osd_options &options) void pcap_module::exit() { - clear_netdev(); + m_devices.clear(); } +std::unique_ptr<network_device> pcap_module::open_device(int id, network_handler &handler) +{ + if ((0 > id) || (m_devices.size() <= id)) + return nullptr; + + return std::make_unique<netdev_pcap>(*this, m_devices[id].name.c_str(), handler); +} + +std::vector<network_device_info> pcap_module::list_devices() +{ + std::vector<network_device_info> result; + result.reserve(m_devices.size()); + for (int id = 0; m_devices.size() > id; ++id) + result.emplace_back(network_device_info{ id, m_devices[id].description }); + return result; +} + +} // anonymous namespace + +} // namespace osd + #else - #include "modules/osdmodule.h" - #include "netdev_module.h" - MODULE_NOT_SUPPORTED(pcap_module, OSD_NETDEV_PROVIDER, "pcap") +namespace osd { namespace { MODULE_NOT_SUPPORTED(pcap_module, OSD_NETDEV_PROVIDER, "pcap") } } + #endif -MODULE_DEFINITION(NETDEV_PCAP, pcap_module) +MODULE_DEFINITION(NETDEV_PCAP, osd::pcap_module) |