Emulator: kernel BPF RX filter for the pcap NIC + NE2000 accept/drop diagnostics
Real-NE2000 semantics: accept only own-MAC unicast + broadcast (multicast
excluded -- the guest stack is unicast+ARP only), and drop npcap's loopback
of our own injected frames. Without it, a live-LAN host download floods the
emulated NIC at wire rate (65k+ frames/min) and wedges netnub before boot
("discarding..."). Filter failure is non-fatal: logs and runs unfiltered.
Also log the first 12 unicast accepts/drops with the live PAR registers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
#include "dosbox.h"
|
#include "dosbox.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "support.h" /* strcasecmp */
|
#include "support.h" /* strcasecmp */
|
||||||
|
#include "control.h" /* [ne2000] macaddr for the RX filter */
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
extern std::string niclist;
|
extern std::string niclist;
|
||||||
@@ -40,6 +41,9 @@ extern std::string niclist;
|
|||||||
#define pcap_next_ex(A,B,C) PacketNextEx(A,B,C)
|
#define pcap_next_ex(A,B,C) PacketNextEx(A,B,C)
|
||||||
#define pcap_findalldevs_ex(A,B,C,D) PacketFindALlDevsEx(A,B,C,D)
|
#define pcap_findalldevs_ex(A,B,C,D) PacketFindALlDevsEx(A,B,C,D)
|
||||||
#define pcap_geterr(A) PacketGetError(A)
|
#define pcap_geterr(A) PacketGetError(A)
|
||||||
|
#define pcap_compile(A,B,C,D,E) PacketCompile(A,B,C,D,E)
|
||||||
|
#define pcap_setfilter(A,B) PacketSetFilter(A,B)
|
||||||
|
#define pcap_freecode(A) PacketFreecode(A)
|
||||||
|
|
||||||
int (*PacketSendPacket)(pcap_t *, const u_char *, int) = 0;
|
int (*PacketSendPacket)(pcap_t *, const u_char *, int) = 0;
|
||||||
void (*PacketClose)(pcap_t *) = 0;
|
void (*PacketClose)(pcap_t *) = 0;
|
||||||
@@ -48,6 +52,9 @@ pcap_t* (*PacketOpen)(char const *,int,int,int,struct pcap_rmtauth *,char *) = 0
|
|||||||
int (*PacketNextEx)(pcap_t *, struct pcap_pkthdr **, const u_char **) = 0;
|
int (*PacketNextEx)(pcap_t *, struct pcap_pkthdr **, const u_char **) = 0;
|
||||||
int (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *) = 0;
|
int (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *) = 0;
|
||||||
char* (*PacketGetError)(pcap_t *) = nullptr;
|
char* (*PacketGetError)(pcap_t *) = nullptr;
|
||||||
|
int (*PacketCompile)(pcap_t *, struct bpf_program *, const char *, int, bpf_u_int32) = 0;
|
||||||
|
int (*PacketSetFilter)(pcap_t *, struct bpf_program *) = 0;
|
||||||
|
void (*PacketFreecode)(struct bpf_program *) = 0;
|
||||||
|
|
||||||
char pcap_src_if_string[] = PCAP_SRC_IF_STRING;
|
char pcap_src_if_string[] = PCAP_SRC_IF_STRING;
|
||||||
|
|
||||||
@@ -105,6 +112,19 @@ bool LoadPcapLibrary() {
|
|||||||
if(!PacketGetError) PacketGetError =
|
if(!PacketGetError) PacketGetError =
|
||||||
(char* (__cdecl *)(pcap_t *)) psp;
|
(char* (__cdecl *)(pcap_t *)) psp;
|
||||||
|
|
||||||
|
/* RX-filter trio: optional -- if absent we just run unfiltered */
|
||||||
|
psp = GetProcAddress(pcapinst,"pcap_compile");
|
||||||
|
if(!PacketCompile) PacketCompile =
|
||||||
|
(int (__cdecl *)(pcap_t *, struct bpf_program *, const char *, int, bpf_u_int32)) psp;
|
||||||
|
|
||||||
|
psp = GetProcAddress(pcapinst,"pcap_setfilter");
|
||||||
|
if(!PacketSetFilter) PacketSetFilter =
|
||||||
|
(int (__cdecl *)(pcap_t *, struct bpf_program *)) psp;
|
||||||
|
|
||||||
|
psp = GetProcAddress(pcapinst,"pcap_freecode");
|
||||||
|
if(!PacketFreecode) PacketFreecode =
|
||||||
|
(void (__cdecl *)(struct bpf_program *)) psp;
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
@@ -252,6 +272,48 @@ bool PcapEthernetConnection::Initialize(Section* config)
|
|||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
pcap_setnonblock(adhandle,1,errbuf);
|
pcap_setnonblock(adhandle,1,errbuf);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// A real NE2000 in non-promiscuous mode hears only its own MAC plus
|
||||||
|
// broadcast; this promiscuous tap otherwise hands the guest EVERY frame
|
||||||
|
// on the host NIC. On a live LAN the host's own bulk traffic wedges the
|
||||||
|
// DOS-era stack before it boots (netnub sits at "discarding..."), so
|
||||||
|
// filter kernel-side and the flood never reaches the emulation. The
|
||||||
|
// guest stack is unicast+ARP only: multicast is deliberately excluded,
|
||||||
|
// and "not ether src" drops npcap's loopback of our own injected frames
|
||||||
|
// (a real NIC never hears its own TX). Any failure = run unfiltered,
|
||||||
|
// i.e. the old behavior.
|
||||||
|
#ifdef WIN32
|
||||||
|
const bool have_filter_api = (PacketCompile != 0 && PacketSetFilter != 0);
|
||||||
|
#else
|
||||||
|
const bool have_filter_api = true;
|
||||||
|
#endif
|
||||||
|
const char *macstr = NULL;
|
||||||
|
Section_prop *ne2ksec = control ? static_cast<Section_prop*>(control->GetSection("ne2000")) : NULL;
|
||||||
|
if (ne2ksec) macstr = ne2ksec->Get_string("macaddr");
|
||||||
|
unsigned int m[6];
|
||||||
|
if (have_filter_api && macstr &&
|
||||||
|
sscanf(macstr, "%x:%x:%x:%x:%x:%x", &m[0],&m[1],&m[2],&m[3],&m[4],&m[5]) == 6) {
|
||||||
|
char fexp[160];
|
||||||
|
snprintf(fexp, sizeof(fexp),
|
||||||
|
"(ether dst %02x:%02x:%02x:%02x:%02x:%02x or ether broadcast)"
|
||||||
|
" and not ether src %02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
m[0],m[1],m[2],m[3],m[4],m[5], m[0],m[1],m[2],m[3],m[4],m[5]);
|
||||||
|
struct bpf_program fprog;
|
||||||
|
if (pcap_compile(adhandle, &fprog, fexp, 1, (bpf_u_int32)0xffffffffu) == 0) {
|
||||||
|
if (pcap_setfilter(adhandle, &fprog) == 0)
|
||||||
|
LOG_MSG("PCAP RX filter active: %s", fexp);
|
||||||
|
else
|
||||||
|
LOG_MSG("PCAP setfilter failed (%s) -- running unfiltered", pcap_geterr(adhandle));
|
||||||
|
#ifdef WIN32
|
||||||
|
if (PacketFreecode)
|
||||||
|
#endif
|
||||||
|
pcap_freecode(&fprog);
|
||||||
|
} else
|
||||||
|
LOG_MSG("PCAP filter compile failed (%s) -- running unfiltered", pcap_geterr(adhandle));
|
||||||
|
} else
|
||||||
|
LOG_MSG("PCAP RX filter NOT installed (%s) -- promiscuous, expect LAN noise",
|
||||||
|
have_filter_api ? "no ne2000 macaddr" : "wpcap lacks pcap_compile");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1412,8 +1412,22 @@ bx_ne2k_c::rx_frame(const void *buf, unsigned io_len)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (0 != memcmp(buf, BX_NE2K_THIS s.physaddr, 6)) {
|
} else if (0 != memcmp(buf, BX_NE2K_THIS s.physaddr, 6)) {
|
||||||
|
static unsigned drop_n = 0;
|
||||||
|
if (drop_n < 12) { drop_n++;
|
||||||
|
LOG_MSG("NE2000 rx DROP unicast dst=%02x:%02x:%02x:%02x:%02x:%02x != PAR=%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
pktbuf[0],pktbuf[1],pktbuf[2],pktbuf[3],pktbuf[4],pktbuf[5],
|
||||||
|
BX_NE2K_THIS s.physaddr[0],BX_NE2K_THIS s.physaddr[1],BX_NE2K_THIS s.physaddr[2],
|
||||||
|
BX_NE2K_THIS s.physaddr[3],BX_NE2K_THIS s.physaddr[4],BX_NE2K_THIS s.physaddr[5]);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
static unsigned uni_n = 0;
|
||||||
|
if (uni_n < 12) { uni_n++;
|
||||||
|
LOG_MSG("NE2000 rx ACCEPT unicast len=%d dst=%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
(int)io_len, pktbuf[0],pktbuf[1],pktbuf[2],pktbuf[3],pktbuf[4],pktbuf[5]);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
BX_DEBUG(("rx_frame promiscuous receive"));
|
BX_DEBUG(("rx_frame promiscuous receive"));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user