NET: full networked mission runs end-to-end; fix blank VDB heads (NIC/VDB port clash)
Console queues a mission -> egg over the wire -> pod loads + runs it -> all
cockpit heads render -> mission ends on the console timer. Four fixes:
1. ne2000.cpp: don't BX_PANIC on the TCR inhibit-CRC / auto-tx-disable bits.
A full production boot runs the packet driver's internal loopback
self-test, which sets those bits; Bochs' NE2000 aborted the whole
emulator. Record them in TCR state instead (harmless for an emulated NIC;
pcap/host does framing+CRC). Also fixes a latent coll_prio bit-4 vs bit-3
round-trip bug. Committed copy in vpx-device/.
2. Confirmed the mission-load page fault (00FF:219D) was RIO-OFF; booting
with the RIO live (net_full.conf) loads and runs the mission cleanly.
3. net_loop.conf + net-boot/loop.bat: GO.BAT-style loop so the pod stays
connected to the console and picks up missions as they're queued (the
real pod relaunches netnub after each mission/idle disconnect).
4. HEADLINE: blank MFD/radar heads were an I/O port collision. The VDB
video splitter is hardwired at 0x300-0x31A (palettes 0x300/0x308/0x310);
the NE2000 at nicbase=300 swallowed the game's VDB palette writes ->
vdb_pal stayed zero -> pal_draw decoded every head to black. Same VDB
spam corrupted NIC RX, dropping the console EndMission (mission overran
its timer). Fix: move the NIC to 0x340 (DOSBox nicbase=340 + DOS NET.CFG
PORT 340; must agree). VDB keeps 0x300. Config-only, no rebuild.
Adds: net_full.conf, net_loop.conf, net-boot/ (ODI drivers, NET.CFG@340,
loop.bat, README), vpx-device/{ne2000,ethernet_pcap}.cpp; updates NET-NOTES.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The DOSBox Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if C_PCAP
|
||||
|
||||
#include "ethernet_pcap.h"
|
||||
#include "dosbox.h"
|
||||
#include "logging.h"
|
||||
#include "support.h" /* strcasecmp */
|
||||
#include <cstring>
|
||||
|
||||
extern std::string niclist;
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// DLL loading
|
||||
#define pcap_sendpacket(A,B,C) PacketSendPacket(A,B,C)
|
||||
#define pcap_close(A) PacketClose(A)
|
||||
#define pcap_freealldevs(A) PacketFreealldevs(A)
|
||||
#define pcap_open(A,B,C,D,E,F) PacketOpen(A,B,C,D,E,F)
|
||||
#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_geterr(A) PacketGetError(A)
|
||||
|
||||
int (*PacketSendPacket)(pcap_t *, const u_char *, int) = 0;
|
||||
void (*PacketClose)(pcap_t *) = 0;
|
||||
void (*PacketFreealldevs)(pcap_if_t *) = 0;
|
||||
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 (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *) = 0;
|
||||
char* (*PacketGetError)(pcap_t *) = nullptr;
|
||||
|
||||
char pcap_src_if_string[] = PCAP_SRC_IF_STRING;
|
||||
|
||||
bool LoadPcapLibrary() {
|
||||
// remember if we've already initialized the library
|
||||
static HINSTANCE pcapinst = (HINSTANCE)-1;
|
||||
if(pcapinst!=(HINSTANCE)-1) {
|
||||
return (pcapinst!=NULL);
|
||||
}
|
||||
|
||||
// init the library
|
||||
pcapinst = LoadLibrary("WPCAP.DLL");
|
||||
if(pcapinst==NULL) {
|
||||
niclist = "WinPcap has to be installed for the NE2000 to work.";
|
||||
LOG_MSG(niclist.c_str());
|
||||
return false;
|
||||
}
|
||||
FARPROC psp;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// C++ defines function and data pointers as separate types to reflect
|
||||
// Harvard architecture machines (like the Arduino). As such, casting
|
||||
// between them isn't portable and GCC will helpfully warn us about it.
|
||||
// We're only running this code on Windows which explicitly allows this
|
||||
// behaviour, so silence the warning to avoid confusion.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
#endif
|
||||
|
||||
psp = GetProcAddress(pcapinst,"pcap_sendpacket");
|
||||
if(!PacketSendPacket) PacketSendPacket =
|
||||
(int (__cdecl *)(pcap_t *,const u_char *,int))psp;
|
||||
|
||||
psp = GetProcAddress(pcapinst,"pcap_close");
|
||||
if(!PacketClose) PacketClose =
|
||||
(void (__cdecl *)(pcap_t *)) psp;
|
||||
|
||||
psp = GetProcAddress(pcapinst,"pcap_freealldevs");
|
||||
if(!PacketFreealldevs) PacketFreealldevs =
|
||||
(void (__cdecl *)(pcap_if_t *)) psp;
|
||||
|
||||
psp = GetProcAddress(pcapinst,"pcap_open");
|
||||
if(!PacketOpen) PacketOpen =
|
||||
(pcap_t* (__cdecl *)(char const *,int,int,int,struct pcap_rmtauth *,char *)) psp;
|
||||
|
||||
psp = GetProcAddress(pcapinst,"pcap_next_ex");
|
||||
if(!PacketNextEx) PacketNextEx =
|
||||
(int (__cdecl *)(pcap_t *, struct pcap_pkthdr **, const u_char **)) psp;
|
||||
|
||||
psp = GetProcAddress(pcapinst,"pcap_findalldevs_ex");
|
||||
if(!PacketFindALlDevsEx) PacketFindALlDevsEx =
|
||||
(int (__cdecl *)(char *, struct pcap_rmtauth *, pcap_if_t **, char *)) psp;
|
||||
|
||||
psp = GetProcAddress(pcapinst,"pcap_geterr");
|
||||
if(!PacketGetError) PacketGetError =
|
||||
(char* (__cdecl *)(pcap_t *)) psp;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
if(PacketFindALlDevsEx==0 || PacketNextEx==0 || PacketOpen==0 ||
|
||||
PacketFreealldevs==0 || PacketClose==0 || PacketSendPacket==0 ||
|
||||
PacketGetError==0) {
|
||||
niclist = "Incorrect or non-functional WinPcap version.";
|
||||
LOG_MSG(niclist.c_str());
|
||||
pcapinst = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PcapEthernetConnection::PcapEthernetConnection()
|
||||
: EthernetConnection()
|
||||
{
|
||||
}
|
||||
|
||||
PcapEthernetConnection::~PcapEthernetConnection()
|
||||
{
|
||||
if(adhandle) pcap_close(adhandle);
|
||||
}
|
||||
|
||||
bool PcapEthernetConnection::Initialize(Section* config)
|
||||
{
|
||||
Section_prop *section = static_cast<Section_prop*>(config);
|
||||
const char* realnicstring = section->Get_string("realnic");
|
||||
|
||||
#ifdef WIN32
|
||||
if(!LoadPcapLibrary()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// find out which pcap device to use
|
||||
pcap_if_t *alldevs;
|
||||
pcap_if_t *currentdev = NULL;
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
unsigned int userdev;
|
||||
#ifdef WIN32
|
||||
if (pcap_findalldevs_ex(pcap_src_if_string, NULL, &alldevs, errbuf) == -1)
|
||||
#else
|
||||
if (pcap_findalldevs(&alldevs, errbuf) == -1)
|
||||
#endif
|
||||
{
|
||||
niclist = "Cannot enumerate network interfaces: "+std::string(errbuf);
|
||||
LOG_MSG("%s", niclist.c_str());
|
||||
return false;
|
||||
}
|
||||
{
|
||||
Bitu i = 0;
|
||||
niclist = "Network Interface List\n-------------------------------------------------------------\n";
|
||||
for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) {
|
||||
const char* desc = "no description";
|
||||
if(currentdev->description) desc=currentdev->description;
|
||||
i++;
|
||||
niclist+=(i<10?" ":"")+std::to_string(i)+" "+currentdev->name+"\n ("+desc+")\n";
|
||||
}
|
||||
}
|
||||
if (!strcasecmp(realnicstring,"list")) {
|
||||
// print list and quit
|
||||
std::istringstream in(("\n"+niclist+"\n").c_str());
|
||||
if (in) for (std::string line; std::getline(in, line); )
|
||||
LOG_MSG("%s", line.c_str());
|
||||
LOG_MSG("NOTE: No interface was chosen because the realnic setting is set to 'list', set realnic to the number of the interface you want to use.");
|
||||
pcap_freealldevs(alldevs);
|
||||
return false;
|
||||
} else if(1==sscanf(realnicstring,"%u",&userdev)) {
|
||||
// user passed us a number
|
||||
Bitu i = 0;
|
||||
currentdev=alldevs;
|
||||
while(currentdev!=NULL) {
|
||||
i++;
|
||||
if(i==userdev) break;
|
||||
else currentdev=currentdev->next;
|
||||
}
|
||||
} else {
|
||||
// user might have passed a piece of name
|
||||
for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) {
|
||||
if(strstr(currentdev->name,realnicstring)) {
|
||||
break;
|
||||
}else if(currentdev->description!=NULL &&
|
||||
strstr(currentdev->description,realnicstring)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(currentdev==NULL) {
|
||||
LOG_MSG("Unable to find network interface - check realnic parameter\n");
|
||||
pcap_freealldevs(alldevs);
|
||||
return false;
|
||||
}
|
||||
// print out which interface we are going to use
|
||||
const char* desc = "no description";
|
||||
if(currentdev->description) desc=currentdev->description;
|
||||
LOG_MSG("Using Network interface:\n%s\n(%s)\n",currentdev->name,desc);
|
||||
|
||||
const char *timeoutstr = section->Get_string("timeout");
|
||||
char *end;
|
||||
int timeout = -1;
|
||||
if (!strlen(timeoutstr)||(timeoutstr[0]!='-'&&!isdigit(timeoutstr[0]))) { // Default timeout values
|
||||
#ifdef MACOSX
|
||||
timeout = 3000; // For macOS, use 3000ms as it does not appear to support -1
|
||||
#else
|
||||
timeout = -1; // For other platforms, use -1 which should mean "non-blocking mode"
|
||||
#endif
|
||||
} else
|
||||
timeout = strtoul(timeoutstr,&end,10);
|
||||
// attempt to open it
|
||||
#ifdef WIN32
|
||||
if ( (adhandle= pcap_open(
|
||||
currentdev->name, // name of the device
|
||||
65536, // portion of the packet to capture
|
||||
// 65536 = whole packet
|
||||
PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
|
||||
timeout, // read timeout
|
||||
NULL, // authentication on the remote machine
|
||||
errbuf // error buffer
|
||||
) ) == NULL)
|
||||
#else
|
||||
/*pcap_t *pcap_open_live(const char *device, int snaplen,
|
||||
int promisc, int to_ms, char *errbuf)*/
|
||||
if ( (adhandle= pcap_open_live(
|
||||
currentdev->name, // name of the device
|
||||
65536, // portion of the packet to capture
|
||||
// 65536 = whole packet
|
||||
true, // promiscuous mode
|
||||
timeout, // read timeout
|
||||
errbuf // error buffer
|
||||
) ) == NULL)
|
||||
|
||||
#endif
|
||||
{
|
||||
LOG_MSG("\nUnable to open the interface: %s.", errbuf);
|
||||
pcap_freealldevs(alldevs);
|
||||
return false;
|
||||
}
|
||||
pcap_freealldevs(alldevs);
|
||||
#ifndef WIN32
|
||||
pcap_setnonblock(adhandle,1,errbuf);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void PcapEthernetConnection::SendPacket(const uint8_t* packet, int len)
|
||||
{
|
||||
static unsigned long txn = 0;
|
||||
if (((++txn) & (txn - 1)) == 0) /* powers of 2, avoid spam */
|
||||
LOG_MSG("PCAP TX #%lu len=%d dst=%02x:%02x:%02x src=%02x:%02x:%02x",
|
||||
txn, len, packet[0],packet[1],packet[2], packet[6],packet[7],packet[8]);
|
||||
int ret = pcap_sendpacket(adhandle, packet, len);
|
||||
if(ret) LOG_MSG("PCAP error: %s", pcap_geterr(adhandle));
|
||||
}
|
||||
|
||||
void PcapEthernetConnection::GetPackets(std::function<void(const uint8_t*, int)> callback)
|
||||
{
|
||||
int res;
|
||||
struct pcap_pkthdr *header;
|
||||
u_char *pkt_data;
|
||||
static unsigned long rxn = 0;
|
||||
//#if 0
|
||||
while((res = pcap_next_ex( adhandle, &header, (const u_char **)&pkt_data)) > 0) {
|
||||
if (((++rxn) & (rxn - 1)) == 0)
|
||||
LOG_MSG("PCAP RX #%lu len=%d dst=%02x:%02x:%02x src=%02x:%02x:%02x",
|
||||
rxn, header->len, pkt_data[0],pkt_data[1],pkt_data[2],
|
||||
pkt_data[6],pkt_data[7],pkt_data[8]);
|
||||
callback(pkt_data, header->len);
|
||||
}
|
||||
//#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user