Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Locally Administered Addresses for ethernet interfaces #40

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/OLDCONF.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions examples/lam.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 9 additions & 12 deletions src/3com.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
&ether_addr[0],&ether_addr[1],&ether_addr[2],&ether_addr[3],&ether_addr[4],&ether_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]);
Expand Down
108 changes: 100 additions & 8 deletions src/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <errno.h>
#include <pwd.h>
#include <termios.h>
#include <limits.h>

// TCP socket
#include <netinet/in.h>
Expand Down Expand Up @@ -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",
&ether_addr[0],&ether_addr[1],&ether_addr[2],&ether_addr[3],&ether_addr[4],&ether_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]);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/ld.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down