From 2d907980f1dbc421a491f4bf34cc940e927c35e3 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 23 May 2016 16:56:06 -0700 Subject: [PATCH] ETHER: Add support for generated interface MAC addresses sim> SET XQ MAC=aa:bb:cc:dd:ee:ff{/bits}{>filespec} where: - all of the aa:bb:cc:dd:ee:ff values must be hex digits - bits is the number of bits which are to be taken from the supplied MAC aa:bb:cc:dd:ee:ff with legal values from 16 to 48 and a default of 48 bits. - filespec specifies a file which contains the MAC address to be used and if it doesn't exist an appropriate generated address will be stored in this file and a subsequent SET MAC invocation specifying the same file will use the value stored in the file rather than generating a new MAC. As discussed in #317 --- PDP11/pdp11_xq.c | 19 ++++- PDP11/pdp11_xu.c | 2 +- sim_ether.c | 179 ++++++++++++++++++++++++++++++++++++++++++----- sim_ether.h | 2 + 4 files changed, 184 insertions(+), 18 deletions(-) diff --git a/PDP11/pdp11_xq.c b/PDP11/pdp11_xq.c index 3b655086..475e9ae5 100644 --- a/PDP11/pdp11_xq.c +++ b/PDP11/pdp11_xq.c @@ -685,7 +685,7 @@ t_stat xq_setmac (UNIT* uptr, int32 val, CONST char* cptr, void* desc) if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; - status = eth_mac_scan(&xq->var->mac, cptr); + status = eth_mac_scan_ex(&xq->var->mac, cptr, uptr); if (status != SCPE_OK) return status; @@ -3176,6 +3176,23 @@ const char helpString[] = "\n" " The SET MAC command must be done before the %D device is attached to a\n" " network.\n" + "4 Generated MAC\n" + " Support exists to provide a way to dynamically generate relatively\n" + " unique MAC addresses and to provide a way to save generated addresses\n" + " for subsequent reuse in later simulator invocations.\n" + "\n" + "+sim> SET XQ MAC=AA:BB:CC:DD:EE:FF{/bits}{>filespec}\n" + "\n" + " where:\n" + "+1. All of the AA:BB:CC:DD:EE:FF values must be hex digits\n" + "+2. bits is the number of bits which are to be taken from the\n" + "++ supplied MAC aa:bb:cc:dd:ee:ff with legal values from 16\n" + "++ to 48 and a default of 48 bits.\n" + "+3. filespec specifies a file which contains the MAC address\n" + "++ to be used and if it doesn't exist an appropriate generated\n" + "++ address will be stored in this file and a subsequent SET MAC\n" + "++ invocation specifying the same file will use the value stored\n" + "++ in the file rather than generating a new MAC.\n" "3 Type\n" " The type of device being emulated can be changed with the following\n" " command:\n" diff --git a/PDP11/pdp11_xu.c b/PDP11/pdp11_xu.c index db6a191e..ccabef98 100644 --- a/PDP11/pdp11_xu.c +++ b/PDP11/pdp11_xu.c @@ -380,7 +380,7 @@ t_stat xu_setmac (UNIT* uptr, int32 val, CONST char* cptr, void* desc) if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; - status = eth_mac_scan(&xu->var->mac, cptr); + status = eth_mac_scan_ex(&xu->var->mac, cptr, uptr); return status; } diff --git a/sim_ether.c b/sim_ether.c index 549300ad..97c20470 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -369,6 +369,14 @@ #include "sim_ether.h" #include "sim_sock.h" #include "sim_timer.h" +#if defined(_WIN32) +#include +#else +#include +#endif + +/* Internal routines - forward declarations */ +static int _eth_get_system_id (char *buf, size_t buf_size); /*============================================================================*/ /* OS-independant ethernet routines */ @@ -376,23 +384,80 @@ t_stat eth_mac_scan (ETH_MAC* mac, const char* strmac) { - unsigned int a0, a1, a2, a3, a4, a5; - const ETH_MAC zeros = {0,0,0,0,0,0}; - const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - ETH_MAC newmac; +return eth_mac_scan_ex (mac, strmac, NULL); +} - if ((6 != sscanf(strmac, "%x:%x:%x:%x:%x:%x", &a0, &a1, &a2, &a3, &a4, &a5)) && - (6 != sscanf(strmac, "%x.%x.%x.%x.%x.%x", &a0, &a1, &a2, &a3, &a4, &a5)) && - (6 != sscanf(strmac, "%x-%x-%x-%x-%x-%x", &a0, &a1, &a2, &a3, &a4, &a5))) +t_stat eth_mac_scan_ex (ETH_MAC* mac, const char* strmac, UNIT *uptr) +{ + unsigned int a[6], g[6]; + FILE *f; + char filebuf[64] = ""; + uint32 i; + static const ETH_MAC zeros = {0,0,0,0,0,0}; + static const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + ETH_MAC newmac; + struct { + uint32 bits; + char system_id[37]; + char cwd[PATH_MAX]; + char file[PATH_MAX]; + ETH_MAC base_mac; + char uname[64]; + char sim[128]; + } state; + CONST char *cptr, *tptr; + uint32 data; + + /* Allow generated MAC address */ + /* XX:XX:XX:XX:XX:XX{/bits{>file}} */ + /* bits (if specified) must be <= 32 */ + + memset (&state, 0, sizeof(state)); + _eth_get_system_id (state.system_id, sizeof(state.system_id)); + strncpy (state.sim, sim_name, sizeof(state.sim)); + getcwd (state.cwd, sizeof(state.cwd)); + if (uptr) + strncpy (state.uname, sim_uname (uptr), sizeof(state.uname)); + cptr = strchr (strmac, '>'); + if (cptr) { + strncpy (state.file, cptr + 1, sizeof(state.file)); + if ((f = fopen (state.file, "r"))) { + filebuf[sizeof(filebuf)-1] = '\0'; + fgets (filebuf, sizeof(filebuf)-1, f); + strmac = filebuf; + fclose (f); + strcpy (state.file, ""); /* avoid saving */ + } + } + cptr = strchr (strmac, '/'); + if (cptr) { + state.bits = (uint32)strtotv (cptr + 1, &tptr, 10); + if ((state.bits < 16) || (state.bits > 48)) + return SCPE_ARG; + } + else + state.bits = 48; + data = eth_crc32 (0, (void *)&state, sizeof(state)); + for (i=g[0]=g[1]=0; i<4; i++) + g[i+2] = (data >> (i << 3)) & 0xFF; + if ((6 != sscanf(strmac, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) && + (6 != sscanf(strmac, "%x.%x.%x.%x.%x.%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) && + (6 != sscanf(strmac, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]))) return SCPE_ARG; - if ((a0 > 0xFF) || (a1 > 0xFF) || (a2 > 0xFF) || (a3 > 0xFF) || (a4 > 0xFF) || (a5 > 0xFF)) - return SCPE_ARG; - newmac[0] = (unsigned char)a0; - newmac[1] = (unsigned char)a1; - newmac[2] = (unsigned char)a2; - newmac[3] = (unsigned char)a3; - newmac[4] = (unsigned char)a4; - newmac[5] = (unsigned char)a5; + for (i=0; i<6; i++) + if (a[i] > 0xFF) + return SCPE_ARG; + else { + uint32 mask, shift; + + state.base_mac[i] = a[i]; + if (((i + 1) << 3) < state.bits) + shift = 0; + else + shift = ((i + 1) << 3) - state.bits; + mask = 0xFF << shift; + newmac[i] = (unsigned char)((a[i] & mask) | (g[i] & ~mask)); + } /* final check - mac cannot be broadcast or multicast address */ if (!memcmp(newmac, zeros, sizeof(ETH_MAC)) || /* broadcast */ @@ -401,7 +466,26 @@ t_stat eth_mac_scan (ETH_MAC* mac, const char* strmac) ) return SCPE_ARG; - /* new mac is OK, copy into passed mac */ + /* new mac is OK */ + /* optionally save */ + if (state.file[0]) { /* Save File specified? */ + f = fopen (state.file, "w"); + if (f == NULL) + return SCPE_ARG; + eth_mac_fmt (&newmac, filebuf); + fprintf (f, "%s/48\n", filebuf); + fprintf (f, "system-id: %s\n", state.system_id); + fprintf (f, "directory: %s\n", state.cwd); + fprintf (f, "simulator: %s\n", state.sim); + fprintf (f, "device: %s\n", state.uname); + fprintf (f, "file: %s\n", state.file); + eth_mac_fmt (&state.base_mac, filebuf); + fprintf (f, "base-mac: %s\n", filebuf); + fprintf (f, "specified: %d bits\n", state.bits); + fprintf (f, "generated: %d bits\n", 48-state.bits); + fclose (f); + } + /* copy into passed mac */ memcpy (*mac, newmac, sizeof(ETH_MAC)); return SCPE_OK; } @@ -894,6 +978,8 @@ int eth_devices (int max, ETH_LIST* dev) {return -1;} void eth_show_dev (FILE* st, ETH_DEV* dev) {} +static int _eth_get_system_id (char *buf, size_t buf_size) + {memset (buf, 0, buf_size); return 0;} #else /* endif unimplemented */ const char *eth_capabilities(void) @@ -1360,6 +1446,29 @@ static int pcap_mac_if_win32(const char *AdapterName, unsigned char MACAddress[6 #endif return ReturnValue; } + +static int _eth_get_system_id (char *buf, size_t buf_size) +{ + LONG status; + DWORD reglen, regtype; + HKEY reghnd; + + memset (buf, 0, buf_size); + if ((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_QUERY_VALUE|KEY_WOW64_64KEY, ®hnd)) != ERROR_SUCCESS) + return -1; + reglen = buf_size; + if ((status = RegQueryValueExA (reghnd, "MachineGuid", NULL, ®type, buf, ®len)) != ERROR_SUCCESS) { + RegCloseKey (reghnd); + return -1; + } + RegCloseKey (reghnd ); + /* make sure value is the right type, bail if not acceptable */ + if ((regtype != REG_SZ) || (reglen > buf_size)) + return -1; + /* registry value seems OK */ + return 0; +} + #endif /* defined(_WIN32) || defined(__CYGWIN__) */ #if defined (__VMS) && !defined(__VAX) @@ -1511,6 +1620,44 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname) #endif } +#if defined(__APPLE__) +#include +#include +static int _eth_get_system_id (char *buf, size_t buf_size) +{ +static struct timespec wait = {5, 0}; /* 5 seconds */ +static uuid_t uuid; + +memset (buf, 0, buf_size); +if (buf_size < 37) + return -1; +if (gethostuuid (uuid, &wait)) + memset (uuid, 0, sizeof(uuid)); +uuid_unparse_lower(uuid, buf); +return 0; +} +#elif !defined(_WIN32) +static int _eth_get_system_id (char *buf, size_t buf_size) +{ +FILE *f; + +memset (buf, 0, buf_size); +if ((f = fopen ("/etc/machine-id", "r"))) { + fread (buf, 1, buf_size, f); + fclose (f); + } +else { + if ((f = popen ("hostname", "r"))) { + fread (buf, 1, buf_size, f); + pclose (f); + } + } +while ((strlen (buf) > 0) && sim_isspace(buf[strlen (buf) - 1])) + buf[strlen (buf) - 1] = '\0'; +return 0; +} +#endif + /* Forward declarations */ static void _eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data); diff --git a/sim_ether.h b/sim_ether.h index 6670b52a..68891f7c 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -359,6 +359,8 @@ void eth_show_dev (FILE*st, ETH_DEV* dev); /* show ethernet device void eth_mac_fmt (ETH_MAC* const add, char* buffer); /* format ethernet mac address */ t_stat eth_mac_scan (ETH_MAC* mac, const char* strmac); /* scan string for mac, put in mac */ +t_stat eth_mac_scan_ex (ETH_MAC* mac, /* scan string for mac, put in mac */ + const char* strmac, UNIT *uptr);/* for specified unit */ t_stat ethq_init (ETH_QUE* que, int max); /* initialize FIFO queue */ void ethq_clear (ETH_QUE* que); /* clear FIFO queue */