Skip to content

Conversation

@klemensn
Copy link
Contributor

This cleans up the mess to configure an IP address on a tun(4) device.

Handrolling a hardcoded ioctl(2) request is far from perfect, but Go (golang.org/sys/unix) is to blame here.

Tested on OpenBSD 7.6 -current where yggdrasil now drives the interface would use of ifconfig or other helpers.

The address ioctl code is just plain broken.

I fixed it for OpenBSD, but have no resources or time for FreeBSD,
so there the code will stay as-is.

FreeBSD also supports stuff like netlink, so perhaps a future rewrite
will look different from OpenBSD's ioctl code (the only approach) anyway.
Start by deleting stuff we will not need or want in the end.
This also seems to be the only code doing execve(2), which is best
to be prevented from ever being called via pledge(2), but that is another
story (I am already running yggdrasil with pledge(2) and unveil(2)).
Otherwise you get "panic: inappropriate ioctl for device",
even for a perfectly fine ioctl(2) call.
See <netinet6/in6_var.h> and <netinet6/in6.h>, respectively.
SIOCSIFADDR_IN6 simply does not exist, on no system out there.

SIOCSIFADDR exists, but long ago was deprecated by
SIOCAIFADDR which is IPv4-only, see netintro(4).

SIOCAIFADDR_IN6 is correct, but does uses a different struct,
so bring that in, also.

NB:  The reason we have to handroll ioctl(2) ourselves is because
golang.org/sys/unix does ship IPv6 ioctl and/or struct definitions.

That should really be fixed upstream.
The net package has all we need and internally repesents addresses
as byte arrays.

<netinet6/in6.h> `struct in6_addr` is just a union over differnt sized
byte arrays, so chosing u_int8_t[16] lets us access addresses byte-wise
and thus no longer need to fiddle with multi-byte values and host vs.
network byte order.
An ioctl request to set an address without gateway address or prefix mask
is rather unproductive.

Reuse code my moving it into a helper function;  again the net package
does all the parsing for us.
At this point, the ioctl success without any error, but no address
appears on the interface.

Turns out telling NDP to not expire helps.

Congrats, yggdrasil is now operational on OpenBSD.
Direct syscalls are discouraged and usually do not work.

(Not entirely sure why they do with Golang, but that is another story.)
@klemensn
Copy link
Contributor Author

Rebased onto develop as of now.

Copy link
Member

@neilalexander neilalexander left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

@neilalexander neilalexander merged commit ccda107 into yggdrasil-network:develop Sep 30, 2024
@klemensn
Copy link
Contributor Author

klemensn commented Oct 1, 2024

Sadly, I just noticed you squashed all commits, thereby omitting all their messages, i.e. insights into what was wrong, from the commit history.

klemensn added a commit to klemensn/yggdrasil-go that referenced this pull request Oct 22, 2024
After yggdrasil-network#1175 removed ioctl(2) fallback code shelling out to ifconfig(8),
there is no code left (compiled on OpenBSD) that would fork(2) or execve(2).

Drop the ability to run any executable file to double down on this, thus
reducing the attack surface of this this experimental, internet facing
daemon running as root.

pledge(2) is doable, but needs more polish.
unveil(2), however, is as simple as it gets.

On other systems, this code is a NOOP, but can still help to implement
similar safety belts.
klemensn added a commit to klemensn/yggdrasil-go that referenced this pull request Oct 22, 2024
After yggdrasil-network#1175 removed ioctl(2) fallback code shelling out to ifconfig(8),
there is no code left (compiled on OpenBSD) that would fork(2) or execve(2).

Drop the ability to run any executable file to double down on this, thus
reducing the attack surface of this this experimental, internet facing
daemon running as root.

pledge(2) is doable, but needs more polish.
unveil(2), however, is as simple as it gets.

On other systems, this code is a NOOP, but can still help to implement
similar safety belts.
klemensn added a commit to klemensn/yggdrasil-go that referenced this pull request Nov 3, 2024
After yggdrasil-network#1175 removed ioctl(2) fallback code shelling out to ifconfig(8),
there is no code left (compiled on OpenBSD) that would fork(2) or execve(2).

Drop the ability to run any executable file to double down on this, thus
reducing the attack surface of this this experimental, internet facing
daemon running as root.

pledge(2) is doable, but needs more polish.
unveil(2), however, is as simple as it gets.

On other systems, this code is a NOOP, but can still help to implement
similar safety belts.
neilalexander pushed a commit that referenced this pull request Dec 12, 2024
After #1175 removed ioctl(2) fallback code shelling out to ifconfig(8),
there is no code left (compiled on OpenBSD) that would fork(2) or
execve(2).

Drop the ability to run any executable file to double down on this, thus
reducing the attack surface of this this experimental, internet facing
daemon running as root.

pledge(2) is doable, but needs more polish.
unveil(2), however, is as simple as it gets.

On other systems, this code is a NOOP, but can still help to implement
similar safety belts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants