Getting IP in a script

Kevin Buettner kev@primenet.com
Tue, 13 Mar 2001 14:10:11 -0700


On Mar 13,  1:39pm, Deepak Saxena wrote:

> strace is your friend. :)  Below is the strace for an 'ifconfig eth0'. 
> Looks like they open a couple of sockets and do some ioctls on them.  
> SIOIFADDR is probably what you want.  Not sure what the return parameter 
> is.  Probably a sock_addr or something of that sort.

Actually, I think it's the SIOCGIFCONF ioctl() that's needed in this
context.  Unfortunately, this appears to be non-trivial to invoke this
ioctl directly from Perl.  (But it's doable with an XS module.) The
reason it's difficult (impossible?) is because struct ifconf has a
member which is an address of a buffer.  It's not difficult to
allocate a buffer in Perl, but it is difficult to divine the address
so that it can be packed into the necessary struct.  (I think it would
really screw up Perl's GC if this were possible.)

Here's what struct ifconf from /usr/include/net/if.h looks like:

/* Structure used in SIOCGIFCONF request.  Used to retrieve interface
   configuration for machine (useful for programs which must know all
   networks accessible).  */

struct ifconf
  {
    int	ifc_len;			/* Size of buffer.  */
    union
      {
	__caddr_t ifcu_buf;
	struct ifreq *ifcu_req;
      } ifc_ifcu;
  };

The ifcu_req member is the one which (I think) prevents us from using
the SIOCGIFCONF ioctl.  You need to pass the address of a struct ifconf
with ifcu_req initialized to a buffer to hold the results.

I think the best approach would be to invoke ifconfig and look at the
output as Deepak suggested earlier today.  This will certainly be more
portable than calling ioctl() from your Perl code.  Here's one way
to do it:

--- ifaddr ---
#!/usr/bin/perl -w

my %ifs;

foreach my $ifpara (split /\n\n/, `/sbin/ifconfig -a`) {
    my ($if,$ipaddr,$bcast,$mask) =
	$ifpara =~ /\A
	            (\w+)			# interface name
		    \s+Link\ encap:.*
		    inet\ addr:(\S+)		# ip address
		    (?: \s+Bcast:(\S+) )?	# broadcast address
		    (?: \s+Mask:(\S+) )?	# netmask
		   /xs;

    $ifs{$if}{IPADDR}    = $ipaddr	if defined $ipaddr;
    $ifs{$if}{BROADCAST} = $bcast	if defined $bcast;
    $ifs{$if}{NETMASK}   = $mask	if defined $mask;
}

foreach my $arg (@ARGV) {
    if (defined $ifs{$arg} && defined $ifs{$arg}{IPADDR}) {
	print "$arg: $ifs{$arg}{IPADDR}\n";
    }
}
--- end ifaddr ---

The above code uses the -a switch to load certain information about all
the interfaces into a hash table.  If you only care about one interface,
the above code could be simplified quite a bit.

Kevin