From 030ec034f1db93c29b56c111793a00bea71f4a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Victor?= Date: Thu, 1 Oct 2020 09:10:54 +0200 Subject: [PATCH] Support for Locally Administered Addresses for ethernet interfaces Support for Locally Administered Addresses for ethernet interfaces to avoid collisions (and manual config) when running more than one lam process per network. See https://www.why-did-it.fail/blog/old-04-mac-address-space/ and IEEE documents linked from there. --- doc/OLDCONF.md | 2 + examples/lam.yml | 8 ++++ src/3com.c | 21 ++++----- src/kernel.c | 108 +++++++++++++++++++++++++++++++++++++++++++---- src/ld.h | 3 ++ 5 files changed, 122 insertions(+), 20 deletions(-) diff --git a/doc/OLDCONF.md b/doc/OLDCONF.md index 7521ba3..59bf6c6 100644 --- a/doc/OLDCONF.md +++ b/doc/OLDCONF.md @@ -23,6 +23,8 @@ LAN, so do not make it the same as the host! It is written in the canonical colon-separated hex format. The default is 00:02:9C:55:89:C6. + You can also specify LAA (case-insensitive) to generate a Locally Administered Address, + which is saved persistently per interface in the file $IFNAME.LAA. disk N FNAME diff --git a/examples/lam.yml b/examples/lam.yml index 45700ae..5a77862 100644 --- a/examples/lam.yml +++ b/examples/lam.yml @@ -94,6 +94,14 @@ network: interface: ldtap # The Lambda's MAC address address: 00:02:9C:55:89:C6 + # Alternatively, to avoid collisions (and manual configuration) + # if you run more than one lam process on your network, + # you can specify LAA to generate a random + # Locally Administered Address, which is saved persistently + # in the file $interface.LAA. + # (This uses the interface name defined when parsing the address setting, + # so have the "interface:" before "address: LAA") + address: LAA # The Lambda's guest IP if you are using UTUN (otherwise undefined key) guest-ip: aaa.bbb.ccc.ddd diff --git a/src/3com.c b/src/3com.c index de5839f..e2b5c66 100644 --- a/src/3com.c +++ b/src/3com.c @@ -991,18 +991,15 @@ int yaml_network_mapping_loop(yaml_parser_t *parser){ } #endif if(strcmp(key,"address") == 0){ - int x = 0; - char *tok; - char *str = value; - while(x < 6){ - long int val = 0; - tok = strtok(str," :\t\r\n"); - if(str != NULL){ str = NULL; } // Clobber - if(tok != NULL){ - val = strtol(tok,NULL,16); - } - ether_addr[x] = val; - x++; + if (strcasecmp(value,"laa") == 0) { + make_locally_administered_address_for_interface(ether_iface, ether_addr); + } else if (sscanf(value,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + ðer_addr[0],ðer_addr[1],ðer_addr[2],ðer_addr[3],ðer_addr[4],ðer_addr[5]) + // try to parse an explicit address + != 6) { + // fail, so complain + logmsgf(LT_3COM,0,"network address: could not parse value '%s'\n", value); + return -1; } logmsgf(LT_3COM,0,"Using 3Com Ethernet address %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n", ether_addr[0],ether_addr[1],ether_addr[2],ether_addr[3],ether_addr[4],ether_addr[5]); diff --git a/src/kernel.c b/src/kernel.c index f8989c7..2a8f26c 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -37,6 +37,7 @@ #include #include #include +#include // TCP socket #include @@ -2997,16 +2998,20 @@ void parse_config_line(char *line){ if(tok[0] == '#' || tok[0] == ';'){ return; } // Comment if(strcasecmp(tok,"ether_addr") == 0){ // 3Com Ethernet address - int x = 0; extern unsigned char ether_addr[6]; - while(x < 6){ - long int val = 0; - tok = strtok(NULL," :\t\r\n"); - if(tok != NULL){ - val = strtol(tok,NULL,16); + extern char ether_iface[30]; + tok = strtok(NULL," \t\r\n"); + if (tok != NULL) { + if (strcasecmp(tok,"laa") == 0) { + make_locally_administered_address_for_interface(ether_iface, ether_addr); + } else if (sscanf(tok,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + ðer_addr[0],ðer_addr[1],ðer_addr[2],ðer_addr[3],ðer_addr[4],ðer_addr[5]) + // try to parse an explicit address + != 6) { + // fail, so complain + logmsgf(LT_3COM,0,"network address: could not parse value '%s'\n", tok); + return; } - ether_addr[x] = val; - x++; } printf("Using 3Com Ethernet address %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n", ether_addr[0],ether_addr[1],ether_addr[2],ether_addr[3],ether_addr[4],ether_addr[5]); @@ -4471,6 +4476,86 @@ void nubus_cycle(int sdu){ icount++; // Main cycle } +// Support for Locally Administered Addresses for ethernet interfaces, +// to avoid collisions (and manual config) when running more than one +// lam process per network. +// See https://www.why-did-it.fail/blog/old-04-mac-address-space/ and IEEE documents linked from there. +static int +get_laa_for_ifname(char *ifname, u_char *ea) +{ + FILE *f; + char fname[PATH_MAX]; + sprintf(fname,"%s.LAA", ifname); + if ((f = fopen(fname,"r")) != NULL) { + if (fscanf(f, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &ea[0],&ea[1],&ea[2],&ea[3],&ea[4],&ea[5]) == 6) { + logmsgf(LT_3COM, 1, "network: read persistent LAA from %s\n", fname); + fclose(f); + return 1; + } else { + logmsgf(LT_3COM,0,"network: file %s corrupt, deleting and ignoring it\n", fname); + fclose(f); + if (unlink(fname) < 0) { + logmsgf(LT_3COM,0,"network: failed to unlink %s: %s\n", fname, strerror(errno)); + } + return 0; + } + } else { + logmsgf(LT_3COM, 1, "network: no persistent LAA file found for %s\n", ifname); + return 0; + } +} + +static int +save_laa_for_ifname(char *ifname, u_char *ea) +{ + FILE *f; + char fname[PATH_MAX]; + sprintf(fname,"%s.LAA", ifname); + if ((f = fopen(fname,"w")) != NULL) { + if (fprintf(f, "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX\n", ea[0],ea[1],ea[2],ea[3],ea[4],ea[5]) > 0) { + logmsgf(LT_3COM, 1, "network: wrote persistent LAA to %s\n", fname); + fclose(f); + return 1; + } else { + logmsgf(LT_3COM, 0, "network: failed to write persistent LAA to %s: %s\n", fname, strerror(errno)); + fclose(f); + return 0; + } + } else { + logmsgf(LT_3COM, 1, "network: cannot save persistent LAA to file %s: %s\n", fname, strerror(errno)); + return 0; + } +} + +void +make_locally_administered_address_for_interface(char *iface, u_char *ea) +{ + int x; + u_char *oea = ea; + + // first check if we have a persistent address already for ether_iface + if (get_laa_for_ifname(iface, ea) == 0) { + // We have no persistent address for that interface, so generate one. + + // generate a Locally Administered Address (LAA, see https://www.why-did-it.fail/blog/old-04-mac-address-space/) + // These are z2::, z6::, zA::, zE:: + // but some of them are actually allocated historically, so pick some free ones. + // The probabilities of getting a duplicate on a network are approx 1/2^44. + unsigned long long a = (u_long)random() | ((u_long)random()<<32); + *ea = a & 0xf0; + // generate a zE:: address except if we got z==2, then make a z6:: address + // because one address in 2E:: is taken, and one in 56:: + if (*ea == 0x20) *ea |= 6; + else *ea |= 0xe; + for (x = 1; x < 6; x++) { + *++ea = (a >> x*8) & 0xff; + } + + // then save it persistently, for ether_iface + save_laa_for_ifname(iface, oea); + } +} + // Main int main(int argc, char *argv[]){ #ifndef HAVE_YAML_H @@ -4498,6 +4583,13 @@ int main(int argc, char *argv[]){ init_sdl_to_keysym_map(); #endif + // Initialize randomness +#if __APPLE__ + srandomdev(); +#else + srandom(time(NULL)); +#endif + #ifdef SDL2 init_sdl_to_scancode_map(); #endif diff --git a/src/ld.h b/src/ld.h index 560cab8..7fe4aa6 100644 --- a/src/ld.h +++ b/src/ld.h @@ -55,6 +55,9 @@ void warp_mouse_callback(int cp); // Logging stuff int logmsgf(int type, int level, const char *format, ...); +// persistent storage for LAA address, per interface +void make_locally_administered_address_for_interface(char *iface, unsigned char *ea); + // Type numbers // Make sure these stay in sync with the array logtype_name in kernel.c // I can't initialize that here because gcc whines about it (sigh)