#!/usr/bin/perl
#
# ident "@(#)sc_ip.pl 1.3     04/05/24 SMI"
#
# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.

#
# sc_ip.pl
#
# Collection of perl subroutines to manage adapters, ip adresses, netmasks, 
# and other network configurations.
#

my $lifreq;
########################################################################
# These ioctls use struct lifreq.   For 32 bit addresses, this looks
# something like this in Solaris:
#
#	struct lifreq {
#		char	lifr_name[32];		/* a32 */
#		int	lifr_addrlen;		/* integer */
#		u_int	lifr_movetoindex;	/* unsigned int */	
#		struct sockaddr_in {
#			short sin_family;	/* short */
#			u_short sin_port;	/* unsigned short */
#			u_long sin_addr;	/* 32-bit address */
#		} lifr_addr;
#	}
#
my $lifreq_t = "a32iIsSL";			# Binary structure of lifreq
$sizeof{'struct lifreq'} = 376;		# In reality, a total of 376 bytes

#######################################################################
#######################################################################
##
## Solaris ioctl command definitions.
##
## The following subroutine definitions are derived from the Solaris
## header files sys/ioccom.h and sys/sockio.h.   The h2ph(1) Perl utility
## was used to generate icccom.ph and sockio.ph files from the Solaris
## headers.   The important definitions for the ioctls(2) which we
## are using were then copied directly into this script.
##
#######################################################################
#######################################################################
eval 'sub IOCPARM_MASK () {0xff;}' unless defined(&IOCPARM_MASK);
eval 'sub IOC_VOID () {0x20000000;}' unless defined(&IOC_VOID);
eval 'sub IOC_OUT () {0x40000000;}' unless defined(&IOC_OUT);
eval 'sub IOC_IN () {0x80000000;}' unless defined(&IOC_IN);
eval 'sub IOC_INOUT () {( &IOC_IN| &IOC_OUT);}' unless defined(&IOC_INOUT);

eval 'sub _IOWR {
	local($x, $y, $t) = @_;
	eval q(((( &IOC_INOUT|((($sizeof{$t}) &IOCPARM_MASK)<<16)| ($x<<8)|$y))));          
}' unless defined(&_IOWR);

eval 'sub SIOCGLIFADDR () {
	&_IOWR(ord(\'i\'), 113, \'struct lifreq\');
}' unless defined(&SIOCGLIFADDR);

eval 'sub SIOCGLIFNETMASK () {
	&_IOWR(ord(\'i\'), 125, \'struct lifreq\');
}' unless defined(&SIOCGLIFNETMASK);

eval 'sub SIOCGLIFSUBNET () {
	&_IOWR(ord(\'i\'), 138, \'struct lifreq\');
}' unless defined(&SIOCGLIFSUBNET);

#######################################################################
#######################################################################
##
## Main
##
#######################################################################
#######################################################################
use Socket;
use strict;


my $progname=$0;
my $COMMAND;
my $ARG1;
my $subnet;
my $ipaddress;
my $ipaddr;
my $ipnetmask;
my $auto_create;

# Open datagram socket.
if (!socket(SOCK, AF_INET, SOCK_DGRAM, 0)) {
	print STDERR "socket:  $!.\n";
	exit -1;
}

# For each adaptername on the command line, print IP address, netmask, etc...
$COMMAND = $ARGV[0];

foreach $_ ($COMMAND) {
	/calculate_subnet/ and do { 
				convert_to_ip($ARGV[1],$ARG1);
				calculate_subnet($ARG1,$ARGV[2],$subnet);
				printf ("%s\n", $subnet);
				last; 
			    };

	/get_ip_in_hosts/ and do {  
     				get_ip_in_hosts($ARGV[1],$ipaddress);
     				printf ("%s\n", $ipaddress);
                                last; 
                               };

	/convert_to_ip/ and do {  
     				convert_to_ip($ARGV[1],$ipaddress);
     				printf ("%s\n", $ipaddress);
                                last; 
                               };


	/get_adapter_ip/ and do {  
     				get_adapter_ip($ARGV[1], $ipaddr);
     				printf ("%s\n", to_dots($ipaddr));
                                last; 
                               };

	/get_adapter_netmask/ and do {  
     				get_adapter_netmask($ARGV[1], $ipnetmask);
     				printf ("0x%8.8X\n", $ipnetmask);
                                last; 
                               };

	/check_if_autocreate_supported/ and do {
                               check_if_autocreate_supported($auto_create);
                               printf("%d\n", $auto_create);
                               last;
                              };

	/-h(elp)?/ and do { 
				print_usage();
				last; 
			    };

	// and do { 
				print_error();
				last; 
			    };
	exit 0
	}



#######################################################################
#######################################################################

#######################################################################
#######################################################################
##
## Functions
##
#######################################################################
#######################################################################

#######################################################################
#
# get_adapter_ip($adaptername, $ipaddr)
#
#		$adaptername		- name of the adatper
#		$ipaddr			- IP address is returned here
#
#	Get the IP address.
#	Error messages are printed on STDERR.
#
#	Return 1 on success, zero on failure.
#
#######################################################################
sub get_adapter_ip ($$) {
	my $adaptername = $_[0];

	my @sin_array;
	my $sin_family;
	my $sin_addr;

	my $lifreq = pack $lifreq_t, $adaptername, 0, 0, 0, 0, 0;

	if (!ioctl(SOCK, &SIOCGLIFADDR, $lifreq)) {
		print STDERR "ioctl $adaptername SIOCGLIFADDR:  $!.\n";
		return 0;
	}

	@sin_array = unpack $lifreq_t, $lifreq;
	$sin_family = $sin_array[3];
	$sin_addr = $sin_array[5];

	if ($sin_family != AF_INET) {
		print STDERR "ioctl $adaptername SIOCGLIFADDR:  unexpected address family.\n";
		return 0;
	}

	$_[1] = $sin_addr;

	return 1;
}

#######################################################################
#
# get_adapter_netmask($adaptername, $ipnetmask)
#
#		$adaptername		- name of the adatper
#		$ipaddr			- IP netmask is returned here
#
#	Get the netmask.
#	Error messages are printed on STDERR.
#
#	Return 1 on success, zero on failure.
#
#######################################################################
sub get_adapter_netmask ($$) {
	my $adaptername = $_[0];

	my @sin_array;
	my $sin_family;
	my $sin_addr;

	my $lifreq = pack $lifreq_t, $adaptername, 0, 0, 0, 0, 0;

	if (!ioctl(SOCK, &SIOCGLIFNETMASK, $lifreq)) {
		print STDERR "ioctl $adaptername SIOCGLIFNETMASK:  $!.\n";
		return 0;
	}

	@sin_array = unpack $lifreq_t, $lifreq;
	$sin_family = $sin_array[3];
	$sin_addr = $sin_array[5];

	if ($sin_family != AF_INET) {
		print STDERR "ioctl $adaptername SIOCGLIFADDR:  unexpected address family.\n";
		return 0;
	}

	$_[1] = $sin_addr;

	return 1;
}

#######################################################################
#
# get_adapter_network($adaptername, $ipnetwork)
#
#		$adaptername		- name of the adatper
#		$ipaddr			- IP netmask is returned here
#
#	Get the network prefix.
#	Error messages are printed on STDERR.
#
#	Return 1 on success, zero on failure.
#
#######################################################################
sub get_adapter_network ($$) {
	my $adaptername = $_[0];

	my @sin_array;
	my $sin_family;
	my $sin_addr;

	my $lifreq = pack $lifreq_t, $adaptername, 0, 0, 0, 0, 0;

	if (!ioctl(SOCK, &SIOCGLIFSUBNET, $lifreq)) {
		print STDERR "ioctl $adaptername SIOCGLIFSUBNET:  $!.\n";
		return 0;
	}

	@sin_array = unpack $lifreq_t, $lifreq;
	$sin_family = $sin_array[3];
	$sin_addr = $sin_array[5];

	if ($sin_family != AF_INET) {
		print STDERR "ioctl $adaptername SIOCGLIFSUBNET:  unexpected address family.\n";
		return 0;
	}

	$_[1] = $sin_addr;

	return 1;
}

#######################################################################
#
# dot_notation($ipaddr)
#
#		$ipaddr			- 32 bit IP address
#
#	Return a dot notation string.
#
#######################################################################
sub to_dots($) {
	my $ipaddr = $_[0];
	my $dotnotation;

	$dotnotation = sprintf("%d.%d.%d.%d",
	    (($ipaddr >> 24) & hex("ff")),
	    (($ipaddr >> 16) & hex("ff")),
	    (($ipaddr >> 8) & hex("ff")),
	    ($ipaddr & hex("ff")));

	return $dotnotation;
}

#######################################################################
#
# convert_to_ip($ipname, $ipaddr)
#
#		$ipname			- hostname / IP address
#		$ipaddr			- IP address is returned here
#
#	Resolves the IP-address corresponding to the hostname.
#	Validates if ipname is a valid IP-address 
#
#	Error messages are printed on STDERR.
#
#	Return 1 on success, zero on failure.
#
#######################################################################
sub convert_to_ip ($$)
{        
        my $hostname = $_[0];
        my @addrs;
        my $address;
	my $error;
	my $number;
                if ( $hostname =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/ ) {
			$error=0;
			foreach $number (split /\./,$hostname){
				$error=1 unless ( $number > 0 and $number < 255 ) ;
								}
			if ( $error eq 1 )  { $_[1] = "" ; return 1 }
					else { $_[1] = $hostname; return 0; }
                }
                @addrs = gethostbyname($hostname) || return(-1);
                $address = join( "." , unpack("C4",$addrs[0])) ;
	        $_[1] = $address;
                return(0);
}

#######################################################################
#
# get_ip_in_hosts($ipname, $ipaddr)
#
#		$ipname			- hostname / IP address
#		$ipaddr			- IP address is returned here
#
#	Resolves the IP-address corresponding to the hostname in the
#       /etc/hosts file only.
#
#	Validates if ipname is a valid IP-address. If it is the same value
#	is returned. 
#
#	Error messages are printed on STDERR.
#
#	Return 1 on success, zero on failure.
#
#######################################################################
sub get_ip_in_hosts ($$)
{
        my $hostname = $_[0];
        my @addrs;
        my $address;
	my $error;
	my $number;
                if ( $hostname =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/ ) {
                        $error=0;
                        foreach $number (split /\./,$hostname){
                                $error=1 unless ( $number > 0 and $number < 255
) ;
                                                                }
                        if ( $error eq 1 )  { $_[1] = "" ; return 1 }
                                        else { $_[1] = $hostname; return 0; }
                }
		open(HOSTS,"</etc/hosts");
		while (<HOSTS>)
		{ if ( s/^\s*?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)[^#]*?\s(\b$hostname\b).*$/$1/ )
			{   chomp $_; $_[1] = $_; return(0);}
		}
		close(HOSTS);
                return(1);
}

#######################################################################
#
# calculate_subnet($ipaddr,$mask,$subnet)
#
#		$ipaddr			- Ip address
#		$mask			- NETMASK in HEX (eg. 0xFFFFFF00)
#		$subnet			- Integer value Subnet for comparison
#
#	Given an ipaddr and mask calculates the subnet integer for comparing
#	two addresses for subnet match.
#
#	Error messages are printed on STDERR.
#
#	Return 0 always.
#
#######################################################################
sub calculate_subnet ($$$)
{
      my $ip = @_[0];
      my $netmask = @_[1];
      my ($a,$b,$c,$d,$addr,$subn);
      ($a,$b,$c,$d) = split ( /\./,$ip );
      $addr = (($a*256+$b)*256+$c)*256+$d; 
      $subn = $addr & hex($netmask);
	$_[2] = $subn;
        return(0);
}

######################################################################
#
# check_if_autocreate_supported($auto_create)
#
#     Check if auto create is supported for IPMP groups
#
#             $auto_create  - either 0 or 1 is retured here
#                         0 => auto create is not supported
#                         1 => auto create is supported
#
#       Error messages are printed on STDERR
#
#       Return 1 on success, zero on failure.
#
#######################################################################
sub check_if_autocreate_supported($)
{
      my $IPPROTO_ICMP=1;
      my $IPPROTO_IP=0;
      my $myaddr =  inet_aton("127.0.0.1");
      my $IP_DONTFAILOVER_IF = 0x44;

      # Open socket
      if (!socket(sock, AF_INET, SOCK_RAW, $IPPROTO_ICMP)) {
              $_[0] = 0;
              return 0;
      }

      if (!setsockopt(sock, $IPPROTO_IP, $IP_DONTFAILOVER_IF, $myaddr)) {
              $_[0] = 0;
              close(sock);
              return 0;
      }

      $_[0] = 1;
      close(sock);
      return 1;
}

#######################################################################
#
# print_error()
#
#	Error messages are printed on STDERR.
#
#	Return 0 always.
#
#######################################################################
sub print_error ()
{
print STDERR <<ERROR;

error:
	Internal Error :: Bad call to $progname().
ERROR
return 0
}
sub print_usage ()
{
print STDERR <<USAGE;

usage:
	$progname convert_to_ip  <hostname>|<hostip>
	$progname get_ip_in_hosts  <hostname>|<hostip>
	$progname calculate_subnet  <hostname>|<hostip> <subnet>
	$progname get_adapter_ip  <adapter>
	$progname get_adapter_netmask  <adapter>
	$progname check_if_autocreate_supported

USAGE
return 0
}
