Last active
December 22, 2021 22:10
-
-
Save afresh1/45cc671547c4d5dcddc33b3c07f7d167 to your computer and use it in GitHub Desktop.
A helper to look up the IP for and ssh into an OpenBSD vmd vm that uses the standard tap interface in a bridge setup. Lots of setups this won't work for, but the one where vmd provides the IP seems to work OK.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use v5.30; | |
use warnings; | |
use OpenBSD::Pledge; | |
use OpenBSD::Unveil; | |
# Copyright (c) 2021 Andrew Hewus Fresh <andrew@afresh1.com> | |
# | |
# Permission to use, copy, modify, and distribute this software for any | |
# purpose with or without fee is hereby granted, provided that the above | |
# copyright notice and this permission notice appear in all copies. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
my %cmd = ( | |
ifconfig => '/sbin/ifconfig', | |
ssh => '/usr/bin/ssh', | |
vmctl => '/usr/sbin/vmctl', | |
); | |
unveil( $_, 'x' ) for values %cmd; | |
pledge( qw< proc exec > ); | |
my ($host, @args) = @ARGV; | |
($host) = $host =~ /^(\w+)$/ if $host; # untaint, but also security | |
die "Usage $0 vm" unless $host; | |
my $vmid = do { | |
open my $fh, "-|", $cmd{vmctl}, show => $host | |
or die "Unable to spawn vmctl: $!"; | |
readline $fh; # header | |
$_ = readline $fh; | |
/^\s*(\d+)\s/ && $1 if $_; | |
}; | |
die "Can't find VM $host\n" unless defined $vmid; | |
# vmd/priv.c says: | |
# * 1. Set the address prefix and mask, 100.64.0.0/10 by default. | |
# * 2. Encode the VM ID as a per-VM subnet range N, 100.64.N.0/24. | |
# Can't seem to ask vmctl for the prefix, so we assume the default. | |
my $vmnet = ip2num("100.64.$vmid.0"); | |
my $vmmask = 0xff_ff_ff_00; | |
# * 3. Assign a /31 subnet M per VM interface, 100.64.N.M/31. | |
# * Each subnet contains exactly two IP addresses; skip the | |
# * first subnet to avoid a gateway address ending with .0. | |
# Which means we could calculate what the IP it should be | |
# directly from the above vmnet, but instead we look on | |
# the TAP interfaces to find out whether we have | |
# IP that matches this available. | |
my @tap_ips = | |
map { /\s(inet) (\S+) (netmask) (\S+)/ ? { @{^CAPTURE} } : () } | |
do { | |
open my $fh, "-|", $cmd{ifconfig}, 'tap' | |
or die "Unable to spawn ifconfig: $!"; | |
readline $fh; | |
}; | |
my $ip; | |
for (@tap_ips) { | |
my $gw = ip2num( $_->{inet} ); | |
# Look for the first interface in vmnet for this vm | |
next unless ( $gw & $vmmask ) == $vmnet; | |
# Again from vmd/priv.c | |
# * 4. Use the first address for the gateway, the second for the VM. | |
my $vm = $gw + 1; | |
my $nm = hex $_->{netmask}; | |
# Make sure the $vm ip is actually in the $gw network | |
next unless ( $vm & $nm ) == ( $gw & $nm ); | |
$ip = num2ip($vm); | |
last; | |
} | |
die "Unable to find IP for $host\n" unless $ip; | |
exec $cmd{ssh}, $ip, @args; | |
die "Unable to exec ssh $ip @args\n"; | |
sub ip2num { | |
my @o = split /\./, $_[0]; | |
return ( $o[0] << 24 ) | |
+ ( $o[1] << 16 ) | |
+ ( $o[2] << 8 ) | |
+ ( $o[3] << 0 ); | |
} | |
sub num2ip { join '.', map { 0xff & $_[0] >> $_ } 24, 16, 8, 0 } | |
sub netmask2prefix { | |
my $mask = shift; | |
my $prefix = 32; | |
my $full = 2 ** $prefix - 1; | |
$prefix-- while $prefix | |
&& $mask != ( $full & $full << ( 32 - $prefix ) ); | |
return $prefix; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment