obsigna,
although the original intention behind the post was the thought of using the description field for easy association of the loopback interfaces to their particular jail, your examples hit the spot in some manner.
I have been thinking much about the same problem you are telling about.
Because, I need an auto-detection functionality.
My jail admin script should do the PF configuration thing automatically for the user.
However, I do not know an "official" way to tell what is the WAN port.
So my approach is to use the "defaultrouter" entry in
/etc/rc.conf.
And then look which network interfaces match the defaultrouter address with their IP/netmask.
If there is only one match, I guess it is safe to assume that this is the WAN network device.
However, I think there are exceptions like multiple WAN-connected IPs etc. But I have no experience with such, so I have no idea how to handle such things.
And, I don't know how big the percentage of users is who actually have such installations with more than one network connection to the internet.
I do not know, what is correct?
Using one of these addresses or all of them? As this potentially affects the design of the pf.conf templates much, I wish I knew more about that.
Anyway, if you want to take a look:
I have attached the test script for my WAN device autodetection Perl subroutine.
That subroutine gethostipnicgate()...
... returns the WAN network device, IP and netmask if it is unambiguous
... if there are more than one interface that match with their to the defaultrouter IP,
then the user gets a list and can choose one of these to use, which then gets returned
... or returns "fail" if that method didn't succeed
I have packed that subroutine together with a test stub, so it can be easily tried out interactively.
Here is the script:
Code:
#!/usr/bin/env perl
use strict;
use warnings;
use POSIX;
my $external_interface;
my $external_ip_bin;
my $external_netmask_bin;
my $file_etc_rc_conf = '/etc/rc.conf';
# string read_a_file( string filename)
sub read_a_file
{
my $fn = shift;
local $/ = undef;
open FILE, $fn or return undef;
my $text = <FILE>;
close FILE or return undef;
return $text;
}
# string sub getipstr( num)
sub getipstr
{
my $num = shift;
$num &= 0x00000000ffffffff; # if i do not mask them out, 255.255.255.0 appears as 511.255.255.0 ?!?
my $n1 = $num >> 24;
my $n2 = ($num >> 16) & 255;
my $n3 = ($num >> 8) & 255;
my $n4 = $num & 255;
return "$n1.$n2.$n3.$n4";
}
# num sub getstrip( string)
sub getstrip
{
my $str = shift;
(my $d1, my $d2, my $d3, my $d4) = $str =~ /(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})/;
# my $num = (int($d1) << 24) & (int($d2) << 16) & (int($d3) << 8) & int($d4); # why doesnt this work?
my $num = (($d1 * 256 + $d2) * 256 + $d3) * 256 + $d4;
return $num;
}
# string sub askvalue (string itemdesc, string defaultvalue, string sanityregexp, string repeatmsg)
sub askvalue
{
my $itemdesc = shift;
my $defvalue = shift;
my $sanityregexp = shift;
my $repeatmsg = shift;
my $prompt = (defined $defvalue)
? "$itemdesc \[$defvalue\] "
: "$itemdesc ";
print( $prompt);
my $ts;
for (my $sane = 0; !$sane ;) {
$ts = <STDIN>;
chomp $ts;
$sane = ( (defined $defvalue and $ts eq '') or ($ts =~ m/$sanityregexp/) );
(print( $repeatmsg)) if !$sane;
}
return ($ts =~ /^\S+$/) ? $ts : $defvalue;
}
# possibly useful info to find out the host ip/NIC:
# rc.conf:
# defaultrouter="xx.xx.xx.xx"
# ifconfig_wwwwddd="inet xx.xx.xx.xx netmask 0xxxxxxxxx"
# -> find interfaces that match defaultrouter with netmask:
# -> if there is one, there is no ambiguity, take it
# -> if there are more, ask user
# int sub gethostipnicgate()
sub gethostipnicgate
{
my $defr;
my $rc_conf = read_a_file( $file_etc_rc_conf );
my $r = ! defined $rc_conf;
if (!$r) {
$r = !defined (($defr) = $rc_conf =~ /^defaultrouter\s*?=\s*?\"(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\"\s*?$/ms);
}
if (!$r) {
# get the defaultrouter IP as number
my $defrip = getstrip( $defr);
# find the network interface(s) that match it with their netmask
# does it make sense to counter-check with rc.conf pattern ifconfig_ data?
my $ifc = qx/ifconfig/;
my @ifclines = split( /\n/, $ifc);
my $intfs = -1;
my @intf_name;
my @intf_ip;
my @intf_netmask;
my @intf_match;
foreach my $l ( @ifclines) {
if ( $l =~ /^[a-z]{2,}\d{1,3}.*$/ ) { # new interface section
++$intfs;
($intf_name[ $intfs]) = $l =~ /^([a-z]{2,}\d{1,3})\: .*$/;
} elsif ( $l =~ /^\s+?inet\s\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}\snetmask.*$/ ) {
(my $tip, my $tnm) = $l =~ /^\s+inet\s(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\snetmask\s([x0-9a-f]{10}).*$/;
$intf_ip[ $intfs] = getstrip( $tip);
$intf_netmask[ $intfs] = hex( $tnm);
} }
# now we should have three arrays
# walk through every entry and count the matches of their subnets with default gateway
print "Interfaces found:";
my $n = scalar @intf_name;
for ( my $i = 0; $i < $n; ++$i) {
print "\n $intf_name[ $i] ";
if (!defined $intf_ip[ $i]) {
print "has no IP";
next;
}
print 'has IP: ' . getipstr( $intf_ip[ $i]) . ' and netmask: ' . getipstr( $intf_netmask[ $i]);
if ( (int($defrip) & int($intf_netmask[ $i])) == (int($intf_ip[ $i]) & int($intf_netmask[ $i])) ) {
push @intf_match, $i; # match
print ', which matches';
} }
print "\n\n";
my $matchcnt = scalar @intf_match;
if ($matchcnt != 1) {
if ($matchcnt == 0 ) {
$r = 1;
print "I seem unable to find a WAN interface.\n";
} else {
print "Choose one of these interfaces, please.\n";
my $ilistre = '';
for ( my $i = 0; $i < $matchcnt; ++$i) {
my $iname = $intf_name[ $intf_match[ $i]];
$ilistre .= (length( $ilistre)) ? "|$iname" : $iname;
print " Interface: <$iname> " .
"IP: <" . getipstr( $intf_ip[ $intf_match[ $i]]) . "> " .
"netmask: <" . getipstr( $intf_netmask[ $intf_match[ $i]]) . ">\n";
}
my $sel = askvalue("Enter <$ilistre>", undef, $ilistre, "Please enter one of those <$ilistre>");
for ( my $i = 0; $i < $n; ++$i) {
if ($sel eq $intf_name[ $i]) {
$external_interface = $intf_name[ $i];
$external_ip_bin = $intf_ip[ $i];
$external_netmask_bin = $intf_netmask[ $i];
last;
} } } } else {
my $match = $intf_match[ 0];
$external_interface = $intf_name[ $match];
$external_ip_bin = $intf_ip[ $match];
$external_netmask_bin = $intf_netmask[ $match];
} }
return $r;
}
# main part
my $r = gethostipnicgate();
if (!$r) {
print "WAN interface: <$external_interface>\n";
print "External IP: <" . getipstr( $external_ip_bin) . ">\n";
print "Ext. netmask: <" . getipstr( $external_netmask_bin) . ">\n";
}
(Maybe it could be easy to modify that scripts' output so it can be used from shell scripts, like for your needs?)