diff options
Diffstat (limited to 'src/osd/modules/netdev/pcap.cpp')
-rw-r--r-- | src/osd/modules/netdev/pcap.cpp | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/osd/modules/netdev/pcap.cpp b/src/osd/modules/netdev/pcap.cpp new file mode 100644 index 00000000000..3f3e20ef7f4 --- /dev/null +++ b/src/osd/modules/netdev/pcap.cpp @@ -0,0 +1,328 @@ +// license:BSD-3-Clause +// copyright-holders:Carl +#if defined(OSD_NET_USE_PCAP) + +#if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) +#ifdef UNICODE +#define LIB_NAME L"wpcap.dll" +#define LoadDynamicLibrary LoadLibraryW +#else +#define LIB_NAME "wpcap.dll" +#define LoadDynamicLibrary LoadLibraryA +#endif +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + +#include <pcap.h> + +#include "emu.h" +#include "osdnet.h" +#include "netdev_module.h" +#include "modules/osdmodule.h" + +#if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) + +#define LIB_ERROR_STR "Unable to load winpcap: %lx\n" +typedef DWORD except_type; + +#else + +#include <dlfcn.h> +#ifdef SDLMAME_MACOSX +#include <pthread.h> +#include <libkern/OSAtomic.h> +#endif + +#ifdef SDLMAME_MACOSX +#define LIB_NAME "libpcap.dylib" +#else +#define LIB_NAME "libpcap.so" +#endif +#define LIB_ERROR_STR "Unable to load pcap: %s\n" + +typedef void *HMODULE; +typedef const char *except_type; +#define FreeLibrary(x) dlclose(x) +#define GetLastError() dlerror() +#define GetProcAddress(x, y) dlsym(x, y) +#define LoadDynamicLibrary(x) dlopen(x, RTLD_LAZY) + +#endif + +class pcap_module : public osd_module, public netdev_module +{ +public: + + pcap_module() + : osd_module(OSD_NETDEV_PROVIDER, "pcap"), netdev_module(), handle(NULL) + { + } + virtual ~pcap_module() { } + + virtual int init(const osd_options &options); + virtual void exit(); + + virtual bool probe(); + + HMODULE handle; +}; + +static int (*pcap_compile_dl)(pcap_t *, struct bpf_program *, char *, int, bpf_u_int32) = NULL; +static int (*pcap_findalldevs_dl)(pcap_if_t **, char *) = NULL; +static pcap_t *(*pcap_open_live_dl)(const char *name, int, int, int, char *) = NULL; +static int (*pcap_next_ex_dl)(pcap_t *, struct pcap_pkthdr **, const u_char **) = NULL; +static void (*pcap_close_dl)(pcap_t *) = NULL; +static int (*pcap_setfilter_dl)(pcap_t *, struct bpf_program *) = NULL; +static int (*pcap_sendpacket_dl)(pcap_t *, u_char *, int) = NULL; +static int (*pcap_set_datalink_dl)(pcap_t *, int) = NULL; +static int (*pcap_dispatch_dl)(pcap_t *, int, pcap_handler callback, u_char *) = NULL; + + +#if 0 +#define pcap_compile_dl pcap_compile +#define pcap_findalldevs_dl pcap_findalldevs +#define pcap_open_live_dl pcap_open_live +#define pcap_next_ex_dl pcap_next_ex +#define pcap_close_dl pcap_close +#define pcap_setfilter_dl pcap_setfilter +#define pcap_sendpacket_dl pcap_sendpacket +#define pcap_set_datalink_dl pcap_set_datalink +#endif + +#ifdef SDLMAME_MACOSX +struct netdev_pcap_context { + UINT8 *pkt; + int len; + pcap_t *p; + + UINT8 packets[32][1600]; + int packetlens[32]; + int head; + int tail; +}; +#endif + +class netdev_pcap : public osd_netdev +{ +public: + netdev_pcap(const char *name, class device_network_interface *ifdev, int rate); + ~netdev_pcap(); + + int send(UINT8 *buf, int len); + void set_mac(const char *mac); +protected: + int recv_dev(UINT8 **buf); +private: + pcap_t *m_p; +#ifdef SDLMAME_MACOSX + struct netdev_pcap_context m_ctx; + pthread_t m_thread; +#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; + + if(!ctx->p) return; + + if(OSAtomicCompareAndSwapInt((ctx->head+1) & 0x1F, ctx->tail, &ctx->tail)) { + 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); +} + +static void *netdev_pcap_blocker(void *arg) { + struct netdev_pcap_context *ctx = (struct netdev_pcap_context*)arg; + + while(ctx && ctx->p) { + pcap_dispatch_dl(ctx->p, 1, netdev_pcap_handler, (u_char*)ctx); + } + + return 0; +} +#endif + +netdev_pcap::netdev_pcap(const char *name, class device_network_interface *ifdev, int rate) + : osd_netdev(ifdev, rate) +{ + char errbuf[PCAP_ERRBUF_SIZE]; +#if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) + m_p = pcap_open_live_dl(name, 65535, 1, -1, errbuf); +#else + m_p = 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(pcap_set_datalink_dl(m_p, DLT_EN10MB) == -1) + { + osd_printf_error("Unable to set %s to ethernet", name); + pcap_close_dl(m_p); + m_p = NULL; + return; + } + set_mac(get_mac()); + +#ifdef SDLMAME_MACOSX + m_ctx.head = 0; + m_ctx.tail = 0; + m_ctx.p = m_p; + pthread_create(&m_thread, NULL, netdev_pcap_blocker, &m_ctx); +#endif +} + +void netdev_pcap::set_mac(const char *mac) +{ + 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(pcap_compile_dl(m_p, &fp, filter, 1, 0) == -1) { + osd_printf_error("Error with pcap_compile\n"); + } + if(pcap_setfilter_dl(m_p, &fp) == -1) { + osd_printf_error("Error with pcap_setfilter\n"); + } +} + +int netdev_pcap::send(UINT8 *buf, int len) +{ + int ret; + if(!m_p) { + printf("send invoked, but no pcap context\n"); + return 0; + } + ret = pcap_sendpacket_dl(m_p, 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; +} + +int netdev_pcap::recv_dev(UINT8 **buf) +{ +#ifdef SDLMAME_MACOSX + UINT8 pktbuf[2048]; + int ret; + + // Empty + if(OSAtomicCompareAndSwapInt(m_ctx.head, m_ctx.tail, &m_ctx.tail)) { + 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; + return ret; +#else + struct pcap_pkthdr *header; + if(!m_p) return 0; + return (pcap_next_ex_dl(m_p, &header, (const u_char **)buf) == 1)?header->len:0; +#endif +} + +netdev_pcap::~netdev_pcap() +{ +#ifdef SDLMAME_MACOSX + m_ctx.p = NULL; + pthread_cancel(m_thread); + pthread_join(m_thread, NULL); +#endif + if(m_p) pcap_close_dl(m_p); + m_p = NULL; +} + +static CREATE_NETDEV(create_pcap) +{ + class netdev_pcap *dev = global_alloc(netdev_pcap(ifname, ifdev, rate)); + return dynamic_cast<osd_netdev *>(dev); +} + +bool pcap_module::probe() +{ + if (handle == NULL) + { + handle = LoadDynamicLibrary(LIB_NAME); + return (handle != NULL); + } + return true; +} + + +int pcap_module::init(const osd_options &options) +{ + pcap_if_t *devs; + char errbuf[PCAP_ERRBUF_SIZE]; + + try + { + if(!(pcap_findalldevs_dl = (int (*)(pcap_if_t **, char *))GetProcAddress(handle, "pcap_findalldevs"))) + throw GetLastError(); + if(!(pcap_open_live_dl = (pcap_t* (*)(const char *, int, int, int, char *))GetProcAddress(handle, "pcap_open_live"))) + throw GetLastError(); + if(!(pcap_next_ex_dl = (int (*)(pcap_t *, struct pcap_pkthdr **, const u_char **))GetProcAddress(handle, "pcap_next_ex"))) + throw GetLastError(); + if(!(pcap_compile_dl = (int (*)(pcap_t *, struct bpf_program *, char *, int, bpf_u_int32))GetProcAddress(handle, "pcap_compile"))) + throw GetLastError(); + if(!(pcap_close_dl = (void (*)(pcap_t *))GetProcAddress(handle, "pcap_close"))) + throw GetLastError(); + if(!(pcap_setfilter_dl = (int (*)(pcap_t *, struct bpf_program *))GetProcAddress(handle, "pcap_setfilter"))) + throw GetLastError(); + if(!(pcap_sendpacket_dl = (int (*)(pcap_t *, u_char *, int))GetProcAddress(handle, "pcap_sendpacket"))) + throw GetLastError(); + if(!(pcap_set_datalink_dl = (int (*)(pcap_t *, int))GetProcAddress(handle, "pcap_set_datalink"))) + throw GetLastError(); + if(!(pcap_dispatch_dl = (int (*)(pcap_t *, int, pcap_handler callback, u_char *))GetProcAddress(handle, "pcap_dispatch"))) + throw GetLastError(); + } + catch (except_type e) + { + FreeLibrary(handle); + osd_printf_error(LIB_ERROR_STR, e); + return 1; + } + if(pcap_findalldevs_dl(&devs, errbuf) == -1) + { + FreeLibrary(handle); + osd_printf_error("Unable to get network devices: %s\n", errbuf); + return 1; + } + + while(devs) + { + if(devs->description) { + add_netdev(devs->name, devs->description, create_pcap); + } else { + add_netdev(devs->name, devs->name, create_pcap); + } + devs = devs->next; + } + return 0; +} + +void pcap_module::exit() +{ + clear_netdev(); + FreeLibrary(handle); + handle = NULL; +} +#else + #include "modules/osdmodule.h" + #include "netdev_module.h" + + MODULE_NOT_SUPPORTED(pcap_module, OSD_NETDEV_PROVIDER, "pcap") +#endif + + +MODULE_DEFINITION(NETDEV_PCAP, pcap_module) |