From 9b2899cfef5cdc14d45d8da0e2713fd2726f3c0f Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 16 May 2022 03:10:18 -0700 Subject: [PATCH] ETHER, CARD, SOCK: Include shared components from V4 codebase --- sim_card.c | 4 +- sim_card.h | 2 +- sim_ether.c | 202 +++++++++++++++++++++++++++++++++++----------------- sim_ether.h | 20 ++++-- sim_sock.c | 194 ++++++++++++++++++++++++++++++++++++++++++++----- sim_sock.h | 8 ++- 6 files changed, 335 insertions(+), 95 deletions(-) diff --git a/sim_card.c b/sim_card.c index 16a31bff..c0fcfabb 100644 --- a/sim_card.c +++ b/sim_card.c @@ -1470,7 +1470,7 @@ return SCPE_OK; #include -t_stat sim_card_test (DEVICE *dptr) +t_stat sim_card_test (DEVICE *dptr, const char *cptr) { t_stat stat = SCPE_OK; #if defined(USE_SIM_CARD) && defined(SIM_CARD_API) && (SIM_MAJOR > 3) @@ -1536,7 +1536,7 @@ t_stat sim_card_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, cons return SCPE_OK; } -t_stat sim_card_test (DEVICE *dptr) +t_stat sim_card_test (DEVICE *dptr, const char *cptr) { return SCPE_OK; } diff --git a/sim_card.h b/sim_card.h index 6f1b3e6c..1a388fb4 100644 --- a/sim_card.h +++ b/sim_card.h @@ -145,7 +145,7 @@ extern const char sim_ascii_to_six[128]; /* Map 7 bit ASCII to BCD */ extern const uint8 sim_parity_table[64]; /* 64 entry odd parity table */ /* Unit test routine */ -extern t_stat sim_card_test (DEVICE *dptr); +extern t_stat sim_card_test (DEVICE *dptr, const char *cptr); #ifdef __cplusplus } diff --git a/sim_ether.c b/sim_ether.c index 081ad931..16d383db 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -383,6 +383,9 @@ /* Internal routine - forward declaration */ static int _eth_get_system_id (char *buf, size_t buf_size); +static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname, int set_on); + +static const unsigned char framer_oui[3] = { 0xaa, 0x00, 0x03 }; /*============================================================================*/ /* OS-independant ethernet routines */ @@ -780,13 +783,11 @@ return eth_show (st, uptr, val, NULL); } #if defined (USE_NETWORK) || defined (USE_SHARED) -/* Internal routine - forward declaration */ -static int _eth_devices (int max, ETH_LIST* dev); /* get ethernet devices on host */ static const char* _eth_getname(int number, char* name, char *desc) { ETH_LIST list[ETH_MAX_DEVICE]; - int count = _eth_devices(ETH_MAX_DEVICE, list); + int count = eth_devices(ETH_MAX_DEVICE, list, FALSE); if ((number < 0) || (count <= number)) return NULL; @@ -803,7 +804,7 @@ static const char* _eth_getname(int number, char* name, char *desc) const char* eth_getname_bydesc(const char* desc, char* name, char *ndesc) { ETH_LIST list[ETH_MAX_DEVICE]; - int count = _eth_devices(ETH_MAX_DEVICE, list); + int count = eth_devices(ETH_MAX_DEVICE, list, FALSE); int i; size_t j=strlen(desc); @@ -829,7 +830,7 @@ const char* eth_getname_bydesc(const char* desc, char* name, char *ndesc) char* eth_getname_byname(const char* name, char* temp, char *desc) { ETH_LIST list[ETH_MAX_DEVICE]; - int count = _eth_devices(ETH_MAX_DEVICE, list); + int count = eth_devices(ETH_MAX_DEVICE, list, FALSE); size_t n; int i, found; @@ -849,7 +850,7 @@ char* eth_getname_byname(const char* name, char* temp, char *desc) char* eth_getdesc_byname(char* name, char* temp) { ETH_LIST list[ETH_MAX_DEVICE]; - int count = _eth_devices(ETH_MAX_DEVICE, list); + int count = eth_devices(ETH_MAX_DEVICE, list, FALSE); size_t n; int i, found; @@ -868,7 +869,7 @@ char* eth_getdesc_byname(char* name, char* temp) static ETH_DEV **eth_open_devices = NULL; static int eth_open_device_count = 0; -static char* (*p_pcap_lib_version) (void); +static char *(*p_pcap_lib_version) (void); static void _eth_add_to_open_list (ETH_DEV* dev) { @@ -894,7 +895,7 @@ t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc) ETH_LIST list[ETH_MAX_DEVICE]; int number; - number = _eth_devices(ETH_MAX_DEVICE, list); + number = eth_devices(ETH_MAX_DEVICE, list, FALSE); fprintf(st, "ETH devices:\n"); if (number == -1) fprintf(st, " network support not available in simulator\n"); @@ -965,6 +966,8 @@ t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, {return SCPE_NOFNC;} const char *eth_version (void) {return NULL;} +int eth_devices(int max, ETH_LIST* list, ETH_BOOL framers) + {return 0;} void eth_show_dev (FILE* st, ETH_DEV* dev) {} t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc) @@ -975,7 +978,7 @@ t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc) } static int _eth_get_system_id (char *buf, size_t buf_size) {memset (buf, 0, buf_size); return 0;} -t_stat sim_ether_test (DEVICE *dptr) +t_stat sim_ether_test (DEVICE *dptr, const char *cptr) {return SCPE_OK;} #else /* endif unimplemented */ @@ -1046,12 +1049,13 @@ typedef void * pcap_t; /* Pseudo Type to avoid compiler errors */ */ static int eth_host_pcap_devices(int used, int max, ETH_LIST* list) { -pcap_t* conn = NULL; -int i, j, datalink = 0; +int i; for (i=0; inext, ++used) { + for (used=0, dev=alldevs; dev && (used < max); dev=dev->next) { + edev.eth_api = ETH_API_PCAP; + eth_get_nic_hw_addr (&edev, dev->name, 0); + if ((memcmp (edev.host_nic_phy_hw_addr, framer_oui, 3) == 0) != framers) + continue; if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; strlcpy(list[used].name, dev->name, sizeof(list[used].name)); @@ -1136,6 +1145,7 @@ else { strlcpy(list[used].desc, dev->description, sizeof(list[used].desc)); else strlcpy(list[used].desc, "No description available", sizeof(list[used].desc)); + ++used; } /* free device list */ @@ -1151,6 +1161,9 @@ if ((used == 0) && (errbuf[0])) { sim_printf ("Eth: pcap_findalldevs warning: %s\n", errbuf); } +if (framers) + return used; /* don't add pseudo-ethernet devices */ + #ifdef HAVE_TAP_NETWORK if (used < max) { #if defined(__OpenBSD__) @@ -1224,11 +1237,11 @@ extern "C" { #include #endif -#ifdef HAVE_DLOPEN +#ifdef SIM_HAVE_DLOPEN #include #endif -#if defined(USE_SHARED) && (defined(_WIN32) || defined(HAVE_DLOPEN)) +#if defined(USE_SHARED) && (defined(_WIN32) || defined(SIM_HAVE_DLOPEN)) /* Dynamic DLL loading technique and modified source comes from Etherial/WireShark capture_pcap.c */ @@ -1248,7 +1261,7 @@ static const char* lib_name = #elif defined(__APPLE__) "/usr/lib/libpcap.A.dylib"; #else - "libpcap." __STR(HAVE_DLOPEN); + "libpcap." __STR(SIM_HAVE_DLOPEN); #endif static char no_pcap[PCAP_ERRBUF_SIZE] = @@ -1257,7 +1270,7 @@ static char no_pcap[PCAP_ERRBUF_SIZE] = #elif defined(__APPLE__) "/usr/lib/libpcap.A.dylib failed to load, install libpcap to use pcap networking"; #else - "libpcap." __STR(HAVE_DLOPEN) " failed to load, install libpcap to use pcap networking"; + "libpcap." __STR(SIM_HAVE_DLOPEN) " failed to load, install libpcap to use pcap networking"; #endif #undef __STR #undef __STR_QUOTE @@ -1528,7 +1541,7 @@ int pcap_setnonblock(pcap_t* a, int nonblock, char *errbuf) { return 0; } } -#endif /* defined(USE_SHARED) && (defined(_WIN32) || defined(HAVE_DLOPEN)) */ +#endif /* defined(USE_SHARED) && (defined(_WIN32) || defined(SIM_HAVE_DLOPEN)) */ /* Some platforms have always had pcap_sendpacket */ #if defined(_WIN32) || defined(__VMS) @@ -1735,7 +1748,14 @@ static int pcap_mac_if_vms(const char *AdapterName, unsigned char MACAddress[6]) } #endif /* defined (__VMS) && !defined(__VAX) */ -static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname) +#if SIM_MAJOR != 4 +static const char *sim_get_tool_path (const char *tool) +{ +return tool; +} +#endif + +static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname, int set_on) { memset(&dev->host_nic_phy_hw_addr, 0, sizeof(dev->host_nic_phy_hw_addr)); dev->have_host_nic_phy_addr = 0; @@ -1752,47 +1772,63 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname) char command[1024]; FILE *f; int i; + char tool[CBUFSIZE]; + const char *turnon[] = { + "ip link set dev %.*s up 2>/dev/null", + "ifconfig %.*s up 2>/dev/null", + NULL}; const char *patterns[] = { - "grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]", - "egrep [0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]", + "ip link show %.*s 2>/dev/null | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]", + "ip link show %.*s 2>/dev/null | egrep [0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]", + "ifconfig %.*s 2>/dev/null | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]", + "ifconfig %.*s 2>/dev/null | egrep [0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]", NULL}; memset(command, 0, sizeof(command)); - /* try to force an otherwise unused interface to be turned on */ - snprintf(command, sizeof(command)-1, "ifconfig %.*s up", (int)(sizeof(command) - 14), devname); - if (system(command)) {}; - for (i=0; patterns[i] && (0 == dev->have_host_nic_phy_addr); ++i) { - snprintf(command, sizeof(command)-1, "ifconfig %.*s | %s >NIC.hwaddr", (int)(sizeof(command) - (26 + strlen(patterns[i]))), devname, patterns[i]); - if (system(command)) {}; - if (NULL != (f = fopen("NIC.hwaddr", "r"))) { - while (0 == dev->have_host_nic_phy_addr) { - if (fgets(command, sizeof(command)-1, f)) { - char *p1, *p2; - - p1 = strchr(command, ':'); - while (p1) { - p2 = strchr(p1+1, ':'); - if (p2 <= p1+3) { - unsigned int mac_bytes[6]; - if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { - dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; - dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; - dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; - dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; - dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; - dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; - dev->have_host_nic_phy_addr = 1; - } - break; - } - p1 = p2; - } - } - else - break; + if (set_on) { + /* try to force an otherwise unused interface to be turned on */ + for (i=0; turnon[i]; ++i) { + snprintf(command, sizeof(command), turnon[i], (int)(sizeof(command) - (2 + strlen(patterns[i]))), devname); + get_glyph_nc (command, tool, 0); + if (sim_get_tool_path (tool)[0]) { + if (NULL != (f = popen(command, "r"))) + pclose(f); + } + } + } + for (i=0; patterns[i] && (0 == dev->have_host_nic_phy_addr); ++i) { + snprintf(command, sizeof(command), patterns[i], (int)(sizeof(command) - (2 + strlen(patterns[i]))), devname); + get_glyph_nc (command, tool, 0); + if (sim_get_tool_path (tool)[0]) { + if (NULL != (f = popen(command, "r"))) { + while (0 == dev->have_host_nic_phy_addr) { + if (fgets(command, sizeof(command)-1, f)) { + char *p1, *p2; + + p1 = strchr(command, ':'); + while (p1) { + p2 = strchr(p1+1, ':'); + if (p2 <= p1+3) { + unsigned int mac_bytes[6]; + if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { + dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; + dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; + dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; + dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; + dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; + dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; + dev->have_host_nic_phy_addr = 1; + } + break; + } + p1 = p2; + } + } + else + break; + } + pclose(f); } - fclose(f); - (void)remove("NIC.hwaddr"); } } } @@ -2379,8 +2415,7 @@ else { /* !tap: */ char command[1024]; /* try to force an otherwise unused interface to be turned on */ - memset(command, 0, sizeof(command)); - snprintf(command, sizeof(command)-1, "ifconfig %s up", savname); + snprintf(command, sizeof(command), (sim_get_tool_path ("ifconfig")[0] != '\0') ? "ifconfig %s up" : "ip link set dev %s up", savname); if (system(command)) {}; errbuf[0] = '\0'; *handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); @@ -2486,12 +2521,13 @@ if (bufsz < ETH_MAX_JUMBO_FRAME) /* initialize device */ eth_zero(dev); -/* translate name of type "ethX" to real device name */ -if ((strlen(name) == 4) +/* translate name of type "eth" to real device name */ +if ((strlen(name) == 4 || strlen(name) == 5) && (tolower(name[0]) == 'e') && (tolower(name[1]) == 't') && (tolower(name[2]) == 'h') && isdigit(name[3]) + && (strlen(name) == 4 || isdigit(name[4])) ) { num = atoi(&name[3]); savname = _eth_getname(num, temp, desc); @@ -2531,7 +2567,7 @@ if (!strcmp (desc, "No description available")) sim_messagef (SCPE_OK, "Eth: opened OS device %s%s%s\n", savname, desc[0] ? " - " : "", desc); /* get the NIC's hardware MAC address */ -eth_get_nic_hw_addr(dev, savname); +eth_get_nic_hw_addr(dev, savname, 1); /* save name of device */ dev->name = (char *)malloc(strlen(savname)+1); @@ -4000,15 +4036,24 @@ return SCPE_OK; t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) { -return eth_filter_hash(dev, addr_count, addresses, - all_multicast, promiscuous, - NULL); +return eth_filter_hash_ex(dev, addr_count, addresses, + all_multicast, promiscuous, FALSE, + NULL); } t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash) { +return eth_filter_hash_ex(dev, addr_count, addresses, + all_multicast, promiscuous, TRUE, + hash); +} + +t_stat eth_filter_hash_ex(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, + ETH_BOOL all_multicast, ETH_BOOL promiscuous, + ETH_BOOL match_broadcast, ETH_MULTIHASH* const hash) +{ int i; char buf[116+66*ETH_FILTER_MAX]; char mac[20]; @@ -4021,7 +4066,7 @@ struct bpf_program bpf; if (!dev) return SCPE_UNATT; /* filter count OK? */ -if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) +if ((addr_count < 0) || ((addr_count + (match_broadcast ? 1 : 0)) > ETH_FILTER_MAX)) return SCPE_ARG; else if (!addresses && (addr_count != 0)) @@ -4036,6 +4081,11 @@ if (dev->reflections == -1) for (i = 0; i < addr_count; i++) memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); dev->addr_count = addr_count; +if (match_broadcast) { + memset(&dev->filter_address[addr_count], 0xFF, sizeof(ETH_MAC)); + ++addr_count; + } +dev->addr_count = addr_count; /* store other flags */ dev->all_multicast = all_multicast; @@ -4205,6 +4255,26 @@ fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); #endif +if (dev->error_needs_reset) + fprintf(st, " In Error Needs Reset: True\n"); +if (dev->error_reopen_count) + fprintf(st, " Error Reopen Count: %d\n", (int)dev->error_reopen_count); +if (1) { + int i, count = 0; + ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; + char buffer[20]; + + for (i = 0; i < ETH_FILTER_MAX; i++) { + if (memcmp(zeros, &dev->filter_address[i], sizeof(ETH_MAC))) { + eth_mac_fmt(&dev->filter_address[i], buffer); + fprintf(st, " MAC Filter[%2d]: %s\n", count++, buffer); + } + } + } +if (dev->all_multicast) + fprintf(st, " All Multicast mode: Enabled\n"); +if (dev->promiscuous) + fprintf(st, " Promiscuous mode: Enabled\n"); if (dev->bpf_filter) fprintf(st, " BPF Filter: %s\n", dev->bpf_filter); #if defined(HAVE_SLIRP_NETWORK) @@ -4320,7 +4390,7 @@ int bpf_compile_skip_count = 0; memset (ð_tst, 0, sizeof(eth_tst)); -eth_device_count = _eth_devices(ETH_MAX_DEVICE, eth_list); +eth_device_count = eth_devices(ETH_MAX_DEVICE, eth_list, FALSE); eth_opened = 0; for (eth_num=0; eth_num -t_stat sim_ether_test (DEVICE *dptr) +t_stat sim_ether_test (DEVICE *dptr, const char *cptr) { t_stat stat = SCPE_OK; SIM_TEST_INIT; diff --git a/sim_ether.h b/sim_ether.h index f3edab8d..04e41d9c 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -124,8 +124,8 @@ extern "C" { #if defined(USE_NETWORK) && defined(USE_SHARED) #undef USE_SHARED #endif -/* USE_SHARED only works on Windows or if HAVE_DLOPEN */ -#if defined(USE_SHARED) && !defined(_WIN32) && !defined(HAVE_DLOPEN) +/* USE_SHARED only works on Windows or if SIM_HAVE_DLOPEN */ +#if defined(USE_SHARED) && !defined(_WIN32) && !defined(SIM_HAVE_DLOPEN) #undef USE_SHARED #endif @@ -232,6 +232,8 @@ struct eth_queue { struct eth_item* item; }; +typedef unsigned char ETH_MAC[6]; + struct eth_list { char name[ETH_DEV_NAME_MAX]; char desc[ETH_DEV_DESC_MAX]; @@ -239,7 +241,6 @@ struct eth_list { }; typedef int ETH_BOOL; -typedef unsigned char ETH_MAC[6]; typedef unsigned char ETH_MULTIHASH[8]; typedef struct eth_packet ETH_PACK; typedef void (*ETH_PCALLBACK)(int status); @@ -342,11 +343,17 @@ t_stat eth_filter (ETH_DEV* dev, int addr_count, /* set filter on incomin ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous); -t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, /* set filter on incoming packets with AUTODIN II based hash */ +t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, /* set filter on incoming packets with hash */ ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous, - ETH_MULTIHASH* const hash); + ETH_MULTIHASH* const hash); /* AUTODIN II based 8 byte imperfect hash */ +t_stat eth_filter_hash_ex (ETH_DEV* dev, int addr_count,/* set filter on incoming packets with hash */ + ETH_MAC* const addresses, + ETH_BOOL all_multicast, + ETH_BOOL promiscuous, + ETH_BOOL match_broadcast, + ETH_MULTIHASH* const hash); /* AUTODIN II based 8 byte imperfect hash */ t_stat eth_check_address_conflict (ETH_DEV* dev, ETH_MAC* const address); const char *eth_version (void); /* Version of dynamically loaded library (pcap) */ @@ -362,6 +369,7 @@ t_stat eth_show (FILE* st, UNIT* uptr, /* show ethernet devices int32 val, CONST void* desc); t_stat eth_show_devices (FILE* st, DEVICE *dptr, /* show ethernet devices */ UNIT* uptr, int32 val, CONST char* desc); +int eth_devices (int max, ETH_LIST* dev, ETH_BOOL framers); /* get ethernet devices on host */ void eth_show_dev (FILE*st, ETH_DEV* dev); /* show ethernet device state */ void eth_mac_fmt (ETH_MAC* const add, char* buffer); /* format ethernet mac address */ @@ -379,7 +387,7 @@ void ethq_insert_data(ETH_QUE* que, int32 type, /* insert item into FIFO size_t crc_len, const uint8 *crc_data, int32 status); t_stat ethq_destroy(ETH_QUE* que); /* release FIFO queue */ const char *eth_capabilities(void); -t_stat sim_ether_test (DEVICE *dptr); /* unit test routine */ +t_stat sim_ether_test (DEVICE *dptr, const char *cptr); /* unit test routine */ #if !defined(SIM_TEST_INIT) /* Need stubs for test APIs */ #define SIM_TEST_INIT diff --git a/sim_sock.c b/sim_sock.c index 6f357cc3..f528bafe 100644 --- a/sim_sock.c +++ b/sim_sock.c @@ -55,7 +55,7 @@ extern "C" { #include #endif -#ifdef HAVE_DLOPEN +#ifdef SIM_HAVE_DLOPEN #include #endif @@ -501,27 +501,38 @@ int load_ws2(void) { /* OS independent routines - sim_parse_addr parse a hostname/ipaddress from port and apply defaults and - optionally validate an address match + sim_parse_addr parse a hostname/ipaddress from port and apply defaults + and optionally validate an address match + sim_addr_acl_check parse a hostname/ipaddress (possibly in CIDR form) and + test against an acl */ /* sim_parse_addr host:port - Presumption is that the input, if it doesn't contain a ':' character is a port specifier. - If the host field contains one or more colon characters (i.e. it is an IPv6 address), - the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format) + Presumption is that the cptr input, if it doesn't contain a ':' character + is a port specifier. If the host field contains one or more colon characters + (i.e. it is an IPv6 address), the IPv6 address MUST be enclosed in square + bracket characters (i.e. Domain Literal format) Inputs: cptr = pointer to input string - default_host - = optional pointer to default host if none specified + host = optional pointer to host buffer host_len = length of host buffer - default_port - = optional pointer to default port if none specified + default_host = optional pointer to default host if none specified + in cptr + port = optional pointer to port buffer port_len = length of port buffer + default_port = optional pointer to default port if none specified + in cptr validate_addr = optional name/addr which is checked to be equivalent to the host result of parsing the other input. This address would usually be returned by sim_accept_conn. + The validate_addr can also be a CIDR address specifier + which will match against the provided host. + If the validate_addr is provided with cptr as NULL, + the validate_addr is parsed for reasonableness and + the result returned with 0 indicating a reasonable + value and -1 indicating a parsing error. Outputs: host = pointer to buffer for IP address (may be NULL), 0 = none port = pointer to buffer for IP port (may be NULL), 0 = none @@ -532,7 +543,9 @@ int load_ws2(void) { doesn't match the parsed host) */ -int sim_parse_addr (const char *cptr, char *host, size_t host_len, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr) +int sim_parse_addr (const char *cptr, char *host, size_t host_len, const char *default_host, + char *port, size_t port_len, const char *default_port, + const char *validate_addr) { char gbuf[CBUFSIZE], default_pbuf[CBUFSIZE]; const char *hostp; @@ -545,7 +558,8 @@ if ((host != NULL) && (host_len != 0)) if ((port != NULL) && (port_len != 0)) memset (port, 0, port_len); if ((cptr == NULL) || (*cptr == 0)) { - if (((default_host == NULL) || (*default_host == 0)) || ((default_port == NULL) || (*default_port == 0))) + if (((default_host == NULL) || (*default_host == 0)) || + ((default_port == NULL) || (*default_port == 0))) return -1; if ((host == NULL) || (port == NULL)) return -1; /* no place */ @@ -572,7 +586,7 @@ else { /* No colon in input */ portp = gbuf; /* Input is the port specifier */ hostp = (const char *)default_host; /* host is defaulted if provided */ } -if (portp != NULL) { +if ((portp != NULL) && (*portp != '\0')) { portval = strtoul(portp, &endc, 10); if ((*endc == '\0') && ((portval == 0) || (portval > 65535))) return -1; /* numeric value too big */ @@ -590,7 +604,7 @@ if (port) /* port wanted? */ else strcpy (port, portp); } -if (hostp != NULL) { +if ((hostp != NULL) && (*hostp != '\0')) { if (']' == hostp[strlen(hostp)-1]) { if ('[' != hostp[0]) return -1; /* invalid domain literal */ @@ -626,16 +640,15 @@ if (validate_addr) { struct addrinfo *ai_host, *ai_validate, *ai, *aiv; int status; - if (hostp == NULL) - return -1; - if (p_getaddrinfo(hostp, NULL, NULL, &ai_host)) + if ((hostp == NULL) || + (0 != p_getaddrinfo(hostp, NULL, NULL, &ai_host))) return -1; if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) { p_freeaddrinfo (ai_host); return -1; } status = -1; - for (ai = ai_host; ai != NULL; ai = ai->ai_next) { + for (ai = ai_host; (ai != NULL) && (status == -1); ai = ai->ai_next) { for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) { if ((ai->ai_addrlen == aiv->ai_addrlen) && (ai->ai_family == aiv->ai_family) && @@ -660,6 +673,151 @@ if (validate_addr) { return 0; } +/* sim_addr_acl_check host:port,acl + + parse a hostname/ipaddress (possibly in CIDR form) and + test against an acl + + Inputs: + validate_addr = This address would usually be returned by + sim_accept_conn. The validate_addr can also be a + CIDR address specifier and in that mode, acl should + be NULL so that we're just validating the syntax + of what will likely become an entry in an acl list. + If the validate_addr is provided with cptr as NULL, + the validate_addr is parsed for reasonableness and + the result returned with 0 indicating a reasonable + value and -1 indicating a parsing error. + acl = pointer to acl string which is comprised of comma + separated entries each which may have a + or - + prefix that indicated a permit or deny status when + the entry matches. Each entry may specify a CIDR + form match criteria. + Outputs: + result = status (0 on complete success or -1 if + parsing can't happen due to bad syntax, a value is + out of range or the validate_addr matches a reject + entry in the acl or it is not mentioned at all in + the acl. +*/ + +int sim_addr_acl_check (const char *validate_addr, const char *acl) +{ +int status = -1; +int done = 0; +struct addrinfo *ai_validate; +unsigned long bits = 0; +const char *c; +char *c1, v_cpy[256]; + +if (validate_addr == NULL) + return status; + +c = strchr (validate_addr, '/'); +if (c != NULL) { + bits = strtoul (c + 1, &c1, 10); + if ((bits == 0) || (bits > 128) || (*c1 != '\0')) + return status; + if ((size_t)(c - validate_addr) > sizeof (v_cpy) - 1) + return status; + memcpy (v_cpy, validate_addr, c - validate_addr); /* Copy everything before the / */ + v_cpy[1 + c - validate_addr] = '\0'; /* NUL terminate the result */ + validate_addr = v_cpy; /* Use the original string minus the prefix specifier */ + } +if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) + return status; +if (acl == NULL) { /* Just checking validate_addr syntax? */ + status = 0; + if ((ai_validate->ai_family == AF_INET) && (bits > 32)) + status = -1; + p_freeaddrinfo (ai_validate); + return status; + } +status = -1; +while ((*acl != '\0') && !done) { + struct addrinfo *ai_rule, *ai, *aiv; + int permit; + unsigned long bits = 0; + const char *cc; + char *c,*c1, rule[260]; + + permit = (*acl == '+'); + cc = strchr (acl, ','); + if (cc != NULL) { + if ((size_t)(cc - acl) > sizeof (rule)) + break; /* Too big - error */ + memcpy (rule, acl + 1, cc - (acl + 1)); + rule[cc - (acl + 1)] = '\0'; + } + else { + if (strlen (acl) > sizeof (rule)) + break; /* Too big - error */ + strcpy (rule, acl + 1); + } + acl += strlen (rule) + 1 + (cc != NULL); + c = strchr (rule, '/'); + if (c != NULL) { + bits = strtoul (c + 1, &c1, 10); + if ((bits == 0) || (bits > 128) || (*c1 != '\0')) + break; + *c = '\0'; + } + + if (p_getaddrinfo(rule, NULL, NULL, &ai_rule)) + break; + + for (ai = ai_rule; (ai != NULL) && (done == 0); ai = ai->ai_next) { + for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) { + if ((ai->ai_addrlen == aiv->ai_addrlen) && + (ai->ai_family == aiv->ai_family)) { + unsigned int bit, addr_bits; + unsigned char *da, *dav; + + if (ai->ai_family == AF_INET) { + da = (unsigned char *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr; + dav = (unsigned char *)&((struct sockaddr_in *)aiv->ai_addr)->sin_addr; + addr_bits = 32; + } + #if !defined(AF_INET6) + else { + done = 1; + break; + } + #else + else { + if (ai->ai_family == AF_INET6) { + da = (unsigned char *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; + dav = (unsigned char *)&((struct sockaddr_in6 *)aiv->ai_addr)->sin6_addr; + addr_bits = 128; + } + else { + done = 1; + break; + } + } + #endif + if (bits == 0) /* Bits not specified? */ + bits = addr_bits; /* Use them all */ + for (bit=0; (bit < bits) && (bit < addr_bits); bit++) { + unsigned int bitmask = 1 << (7 - (bit & 7)); + + if ((da[bit>>3] & bitmask) != (dav[bit>>3] & bitmask)) + break; + } + if (bit == bits) { /* All desired bits matched? */ + done = 1; + status = permit ? 0 : -1; + break; + } + } + } + } + p_freeaddrinfo (ai_rule); + } +p_freeaddrinfo (ai_validate); +return status; +} + /* sim_parse_addr_ex localport:host:port Presumption is that the input, if it doesn't contain a ':' character is a port specifier. diff --git a/sim_sock.h b/sim_sock.h index f42e8f16..1a0ee450 100644 --- a/sim_sock.h +++ b/sim_sock.h @@ -111,8 +111,12 @@ extern "C" { #define sim_printf printf #endif -int sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr); -int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port); +int sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host, + char *port, size_t port_len, const char *default_port, + const char *validate_addr); +int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, + char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port); +int sim_addr_acl_check (const char *validate_addr, const char *acl); #define SIM_SOCK_OPT_REUSEADDR 0x0001 #define SIM_SOCK_OPT_DATAGRAM 0x0002 #define SIM_SOCK_OPT_NODELAY 0x0004