### Local Variables: ***
### mode:perl ***
### comment-column:0 ***
### comment-start: "### " ***
### comment-end: "***" ***
### End: ***
#
# ****************DO NOT MOVE OR CHANGE LINES ABOVE THIS*********************
#
# The first set of lines runs perl from any shell. The second set of lines
# identifies the rest of the file as PERL for EMACS autoformatting.
# See end of copyright for more information.
#
# 
# -------------------------------------------------------------------
#                                   X-BONE
#
#                          http://www.isi.edu/xbone
#                USC Information Sciences Institute (USC/ISI)
#                   Marina del Rey, California 90292, USA
#                          Copyright (c) 1998-2005
# 
# -------------------------------------------------------------------
#
# Copyright (c) 1998-2005 by the University of Southern California.
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and
# its documentation in source and binary forms for non-commercial
# purposes and without fee is hereby granted, provided that the above
# copyright notice appear in all copies and that both the copyright
# notice and this permission notice appear in supporting
# documentation, and that any documentation, advertising materials,
# and other materials related to such distribution and use acknowledge
# that the software was developed by the University of Southern
# California, Information Sciences Institute.  The name of the
# University may not be used to endorse or promote products derived
# from this software without specific prior written permission.
# 
# THE UNIVERSITY OF SOUTHERN CALIFORNIA MAKES NO REPRESENTATIONS ABOUT
# THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
# PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# Other copyrights might apply to parts of this software and are so
# noted when applicable.
#
# -------------------------------------------------------------------
#
# Effort partly sponsored by the Defense Advanced Research Projects
# Agency (DARPA) and Air Force Research Laboratory, Air Force Materiel
# Command, USAF, under agreement numbers F30602-98-1-0200 (X-Bone) and
# F30602-01-2-0529 (DynaBone). The views and conclusions contained
# herein are those of the authors and should not be interpreted as
# necessarily representing the official policies or endorsements,
# either expressed or implied, of the Defense Advanced Research
# Projects Agency (DARPA), the Air Force Research Laboratory, or the
# U.S. Government.
#
# This work was partly supported by the NSF STI-XTEND (ANI-0230789)
# and NETFS (ANI-0129689) projects. Any opinions, findings, and
# conclusions or recommendations expressed in this material are those
# of the authors and do not necessarily reflect the views of the
# National Science Foundation.
#
# -------------------------------------------------------------------
#  $RCSfile: XB_Route.pm,v $
#
# $Revision: 1.71 $
#   $Author: pingali $
#     $Date: 2005/03/31 07:03:56 $
#    $State: Exp $
# ----------------------------------------------------------------------------
#
# Primary Author: Amy Hughes

# This script is used in place of the unix 'route' command.
# It takes the same input as 'route'. 
#
# usage:
# XXX - This needs to be fixed. 
# add_route( [-net|-host], destinationIP, [-netmask mask], 
#                                   (gateway | -interface name))
# delete_route ([-net|-host], destinationIP, [-netmask mask])
# get_route ([-net|-host], destinationIP, [-netmask mask])
# Supported OS:
# FreeBSD (Default OS)
# Linux (modifications are marked)

package XB_Route;

require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw();
@EXPORT_OK = qw(add_route delete_route get_route init); 

use strict;
use sigtrap;
use Fcntl;
use POSIX qw(:errno_h);

use File::Copy;
use Socket;
use IPC::Open3;
use Net::Netmask;
use Net::IP; 
use XB_Log;
use XB_Params;
use IO::Select;
use XB_CiscoSSH;

my $log = 0;
my $module = "XB_Route::"; 

###########################################################################
#
#          GENERAL CODE
#
###########################################################################

# Function:
#      verify_input
# Used by: ALL
# Description:
#	verify the route command
# Arguments:
#       \%hash
#                      ( command     => (add|delete|get) 
#                        ovlname     => <name>
#		    routing_method   => (static|dynamic) 
#			 family      => (-inet|-inet6)
#			 dstarg      => (-net|-host)
#			 destination => <ip address> 
#			 netmask_opt => (-netmask|-prefixlen)
#			 netmask     => (<netmask>|<prefix length>)
#			 gateway     => <ip address> 
#		       );
# Returns:
#	1 on success
# Exceptions:
#	"XB_Route::add_route" on error, nothing to clean up by caller

sub verify_input($){

  my ($inputhash) = @_; 
  my $proc = "verify_input";   

  XB_Log::log "info" => "-> $module$proc $inputhash";

  eval {
    
    # overlay 
    if (not defined $inputhash->{ovlname}){ 
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined overlayname ";
      die "arg";
    }
    
    # command
    if (not defined $inputhash->{command}){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{command} "; 
      die "arg";
    }

    if (($inputhash->{command} ne "get") and 
	($inputhash->{command} ne "add") and
	($inputhash->{command} ne "delete")){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Wrong command $inputhash->{command}";
      die "arg";
    }

    # routing method 
    if (not defined $inputhash->{routing_method}){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{routing_method} ";
      die "arg";
    }

    if (($inputhash->{routing_method} ne "static") and 
	($inputhash->{routing_method} ne "dynamic")){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Wrong routing method $inputhash->{routing_method}"; 
      die "arg";
    }

    # family
    if (not defined $inputhash->{family}){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{family} ";
      die "arg";
    }

    if (($inputhash->{family} ne "-inet6") and
	($inputhash->{family} ne "-inet")){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Wrong family $inputhash->{family}";
      die "arg";
    }

    # dstarg
    if (not defined $inputhash->{dstarg}){ 
      XB_Log::log "err","Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{dstarg} ";
      die "arg";
    }

    if (($inputhash->{dstarg} ne "-host") and
	($inputhash->{dstarg} ne "-net")){
      XB_Log::log "err","Unable to execute route commands. Consistency check failed." . "Wrong arg $inputhash->{dstarg}";
      die "arg";
    }

    # destination
    if (not defined $inputhash->{destination}){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{destination} ";
      die "arg";
    }

    my $n = new Net::IP($inputhash->{destination});
    if (not defined $n){    
      XB_Log::log "err","Unable to execute route commands. Consistency check failed." . "Wrong $inputhash->{destination}";
      die "arg";
    }

    # netmask option
    if (not defined $inputhash->{netmask_opt}){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{netmask_opt} ";
      die "arg";
    }

    unless ((($inputhash->{family} eq "-inet6") and
	 ($inputhash->{netmask_opt} eq "-prefixlen")) or
	(($inputhash->{family} eq "-inet") and
	 ($inputhash->{netmask_opt} eq "-netmask"))){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Inappropriate network mask - $inputhash->{netmask_opt}";
      die "arg";
    }

    # netmask
    if (not defined $inputhash->{netmask}){
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{netmask} ";
      die "arg"; 
    }

    # dev_opt
    if (defined $inputhash->{dev_opt}) {

      if (($inputhash->{dev_opt} ne "-interface") and
	  ($inputhash->{dev_opt} ne "dev")){
	XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Wrong arg $inputhash->{dev_opt}";
	die "arg";
      }

      if (not defined $inputhash->{dev}){
	XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Undefined $inputhash->{dev} "; 
	die "arg";
      }

      if ($inputhash->{dev} =~ m/^(([a-zA-Z]+\d+)(:\d+)?)$/){
	$inputhash->{dev} = $1;
      } else {
	XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Wrong arg $inputhash->{dev}";
	die "arg";
      }
    } elsif (defined $inputhash->{gateway}){
      my $n = new Net::IP($inputhash->{gateway});
      if (not defined $n){ 
	XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "Incorrect gateway $inputhash->{gateway}";
	die "arg";
      }
    } else {
      XB_Log::log "err", "Unable to execute route commands. Consistency check failed." . "No gateway or interface";
    }

  }; #eval

  XB_Log::log "info" => "<- $module$proc $inputhash";
  
  return 1 unless ($@); 

  if ($@ !~ /(arg)/){
    XB_Log::log "err" => "Error: $@"; 
  }

  die "$module$proc";  

}

###########################################################
# Exported API
###########################################################

# Function:
#      add_route
# Used by: ALL
# Description:
#	Add the given route
# Arguments:
#       hash
#                      ( ovlname     => <name>
#		    routing_method   => (static|dynamic)
#			 family      => (-inet|-inet6)
#			 dstarg      => (-net|-host)
#			 destination => <ip address>
#			 gateway     => <ip address>
#		       );
# Returns:
#	1 on success
# Exceptions:
#	"XB_Route::add_route" on error, nothing to clean up by caller

sub add_route($) {
  my($params) = @_;
  my $static = 0;  # corresponding lines
  my $line;
  my $proc = "add_route";

  XB_Log::log "info" => "-> $module$proc $params";

  eval {

    my $cpid;

    die ("Incorrect argument") if (not defined $params);

    # parse the destination and specify the command.
    my $dip = new Net::IP::XB_IP ($params->{destination});
    if (not defined $dip){
      XB_Log::log "err", "Unable to create routes. ".
	  "Received incorrect format: " . $params->{destination};
      die "arg";
    }
    
    if ($dip->version() == 4) {
      $params->{netmask_opt} = "-netmask";
      $params->{netmask}     = $dip->mask;
      $params->{destination} = $dip->addr;
      $params->{family}      = "-inet";
    } else {
      $params->{netmask_opt} = "-prefixlen";
      $params->{netmask}     = $dip->masklen;
      $params->{destination} = $dip->addr;	
      $params->{family}      = "-inet6";
    }
    $params->{command} = "add";
    if (not verify_input($params)){
      XB_Log::log "err" => "Unable to create routes. Input could not be parsed";
      die "parse";
    }
    if ($XB_Params::node_opts{os} =~ /cisco/i) {
      # Cisco IOS
      if ((defined($params->{gateway})) && ($params->{gateway} ne "")) {
        if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
          my @cmd = "ip route $params->{destination} $params->{netmask} $params->{gateway} \n exit \n";
          XB_Log::log "debug", "Route comand on Cisco: @cmd";
          $cpid = XB_CiscoSSH::cmd @cmd
          or XB_Log::log "err" => "$!: Route command failed" and die "route";
	}
      }
      elsif ((defined($params->{dev_opt})) && ($params->{dev_opt} ne "")) {
        if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
	  my @cmd = ("ip route $params->{destination} $params->{netmask} $params->{dev_opt} \n exit \n");
	  $cpid = XB_CiscoSSH::cmd @cmd
	     or XB_Log::log "err" => "$!: Route command failed" and die "route";        }
        else {
          # netmask not provided. using a dummy netmask : 255.255.255.255,
          # since CISCO IOS IP route command requires a netmask
          my @cmd = ("ip route $params->{destination} 255.255.255.255 $params->{gateway} \n exit \n");
	  $cpid = XB_CiscoSSH::cmd @cmd
	    or XB_Log::log "err" => "$!: Route command failed" and die "route";         }
      }
      else {
        XB_Log::log "err" => "No destination specified for route command" and
         die "route";
      }
    }
    elsif ($XB_Params::node_opts{os} =~ /linux/i) {	
      # Linux OS
      my @cmd = (\*WTR, \*RDR, \*ERR,
		 "route", "-n", "add",
		 $params->{dstarg},
		 $params->{destination});

      my @remaining = ();

      if ((defined($params->{gateway})) && ($params->{gateway} ne "")) {
	if (($params->{dstarg} !~ /host/) && (defined($params->{netmask})) && ($params->{netmask} ne "")) {
	  @remaining = ( "netmask", $params->{netmask},
			 "gw", $params->{gateway});
	  } else {
	    @remaining  = ( "gw", $params->{gateway} );
	  }
      }	elsif ((defined($params->{dev_opt})) && ($params->{dev_opt} ne "")) {
	if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
	    @remaining = ($params->{netmask_opt}, $params->{netmask},
			  $params->{dev_opt}, $params->{dev});
	  } else {
	    @remaining = ($params->{dev_opt}, $params->{dev});
	  }

	}else {
	  XB_Log::log "err" => "Unable to create routes. No destination specified for route command" and
	      die "route";
	}

      # collected all the parameters. now, execute it.
      $cpid = open3(@cmd, @remaining) or
	XB_Log::log "err" => "Unable to create routes. $!: Route command failed" and die "route";

    } else {		

	# Default OS (FreeBSD)
      my @remaining = ();
      my @cmd = ( \*WTR, \*RDR, \*ERR,
		  "route", "-n", "-q", "add",
		  $params->{family}, $params->{dstarg},
		  $params->{destination});


      if ((defined($params->{gateway})) && ($params->{gateway} ne "")) {
	if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
	  @remaining = ($params->{netmask_opt}, $params->{netmask},
			     $params->{gateway});
	}else {
	  @remaining = ($params->{gateway});
	}
      } elsif ((defined($params->{dev_opt})) && ($params->{dev_opt} ne "")) {
	if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
	  @remaining = ($params->{netmask_opt}, $params->{netmask},
			  $params->{dev_opt}, $params->{dev});
	} else {
	  @remaining = ($params->{netmask_opt}, $params->{netmask},
			  $params->{dev_opt}, $params->{dev});
	}

      } else {	
	XB_Log::log "err" => "Unable to create routes. No destination specified for route command" and  die "route";
      };

      #XB_Log::log "info" => "executing route command @cmd @remaining \n";

      # now execute the command.
      $cpid = open3(@cmd, @remaining) or
	XB_Log::log "err" => "Unable to create routes. $!: Route command failed" and die "route";

    }; # else
   
    if ($XB_Params::node_opts{os} !~ /cisco/i) {

      # check output from route and warn if not successful
      my @route = <ERR>;

      close WTR or XB_Log::log "err" => "Unable to cleanup after creating routes. Could not close: $!" and die "close_route";
      close RDR or XB_Log::log "err" => "Unable to cleanup after creating routes. Could not close: $!" and die "close_route";
      close ERR or XB_Log::log "err" => "Unable to cleanup after creating routes. Could not close: $!" and die "close_route";

      if (defined $cpid) {
        waitpid $cpid, 0 == $cpid or
	  XB_Log::log "err" => "Operating system resource error. ".
	      "Could not create a child process $cpid" and die "wait"; }

      if (scalar @route) {
        # error
        XB_Log::log "err" => "Unable to create routers. Route command returned an error: @route"
	    and die "route"; }
      else { return; } # no error
    }

  }; # eval block

  XB_Log::log "info" => "<- $module$proc $params";
  return 1 unless $@;
  if ($@ =~ /^(parse|close_route|route|wait|make_backup|route_exists|
    XB_CiscoSSH::cmd)/) {
    XB_Log::log "err" => "$module$proc: caught error: $@";
    # no cleanup
  } elsif ($@ =~ /^(gopen|gduplicate|gclose|checkconf)/) {
    XB_Log::log "err" => "$module$proc: caught error: $@";
  } elsif ($@ =~ /^(gmove|grestart)/) {
    XB_Log::log "err" => "$module$proc: caught error: $@";
  } else {
    XB_Log::log "warning" => "$module$proc caught unexpected exception $@";
  }
  die "$proc";
} # sub add_route($)


# Function:
#      delete_route
# Used by: ALL
# Description:
#	Deletes given route
# Arguments:
#       %hash
#                      ( ovlname     => <name>
#		    routing_method   => (static|dynamic)
#			 family      => (-inet|-inet6)
#			 dstarg      => (-net|-host)
#			 destination => <ip address>
#			 netmask_opt => (-netmask|-prefixlen)
#			 netmask     => (<netmask>|<prefix length>)
#			 gateway     => <ip address>
#		       );
# Returns:
#	1 on success
# Exceptions:
#	"XB_Route::delete_route" on error, nothing to clean up by caller
#
sub delete_route(@) {
  my $proc = "delete_route";
  my ($params) = @_;

  XB_Log::log "info" => "-> $module$proc $params";

  eval {

    #just a sanity check. have to check the type instead.
    if (not defined $params){
      XB_Log::log "err" => "Unable to delete routes. Incorrect arguments";
      die "parse";
    }

    # parse the destination and specify the command.
    my $dip = new Net::IP::XB_IP ($params->{destination});
    if (not defined $dip){
      XB_Log::log "err" => "Unable to delete routes. Incorrect format: " . 
	  $params->{destination};
      die "parse"; 
    }

    if ($dip->version() == 4) {
      $params->{netmask_opt} = "-netmask";
      $params->{netmask}     = $dip->mask;
      $params->{destination} = $dip->addr;
      $params->{family}      = "-inet";
    } else {
      $params->{netmask_opt} = "-prefixlen";
      $params->{netmask}     = $dip->masklen;
      $params->{destination} = $dip->addr;
      $params->{family}      = "-inet6";
    }
    $params->{command} = "delete";
    if (not verify_input($params) ){
      XB_Log::log "err" => "Input could not be parsed";
      die "parse";
    }

    # This is the ugly hack for the Linux problem!!!!
      my $cpid;
      if ($XB_Params::node_opts{ os} =~ /cisco/i) {
        # Cisco IOS
	if ((defined($params->{gateway})) && ($params->{gateway} ne "")) {
	  if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
  	    my @cmd = "no ip route $params->{destination} $params->{netmask} $params->{gateway} \n exit \n";
	    $cpid = XB_CiscoSSH::cmd @cmd or XB_Log::log "err" => 
	      "$!: Route command failed" and die "route";
	  }
        }
	elsif ((defined($params->{dev_opt})) && ($params->{dev_opt} ne "")) {
          if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
	    my @cmd = ("no ip route $params->{destination} $params->{netmask} $params->{dev_opt} \n exit \n");
	    $cpid = XB_CiscoSSH::cmd @cmd or XB_Log::log "err" 
	      => "$!: Route command failed" and die "route"; 
	  }
	  else {
	    # netmask not provided. using a dummy netmask : 255.255.255.255, since CISCO IOS IP route command requires a netmask
	    my @cmd = ("no ip route $params->{destination} 255.255.255.255 $params->{gateway} \n exit \n");
	    $cpid = XB_CiscoSSH::cmd @cmd or XB_Log::log "err" => 
	      "$!: Route command failed" and die "route";
	  }
        }
	else {
	  XB_Log::log "err" => "No destination specified for route command" and
	  die "route"; 
	}
      }	
      elsif ($XB_Params::node_opts{os} =~ /linux/i) {
	# Linux OS
	
	my @cmd = (\*WTR, \*RDR, \*ERR, "route", "-n", "del",
		   $params->{dstarg},
		   $params->{destination});
	
	my @remaining = ();
	if (($params->{dstarg} !~ /host/) && (defined($params->{netmask})) && ($params->{netmask} ne "")) {
	  @remaining = ( "netmask", $params->{netmask} );
	};
	
	$cpid = open3(@cmd, @remaining) or
	  XB_Log::log "err" => "Unable to delete routes. ".
	      "$!: Route command failed" and die "route";

      } else {	
	# Default OS (FreeBSD)		

	my @cmd = (\*WTR, \*RDR, \*ERR, "route", "-n", "delete",
		   $params->{family}, $params->{dstarg},
		   $params->{destination});
	
	my @remaining = (); 	
	if ((defined($params->{netmask})) && ($params->{netmask} ne "")){
	  @remaining = ( $params->{netmask_opt}, $params->{netmask} );
	};
	
	$cpid = open3(@cmd, @remaining) or
	  XB_Log::log "err" => "Unable to delete routes. ".
	      "$!: Route command failed" and die "route"; 	
      }

      if ($XB_Params::node_opts{os} !~ /cisco/i){
        # check output from route and warn if not successful
        my @route = <ERR>;

        close WTR or 
	  XB_Log::log "err" => 
	      "Unable to cleanup after deleting routes. Close failed: $!" 
	        and die "close_route";
        close RDR or 
	  XB_Log::log "err" => 
	      "Unable to cleanup after deleting routes. Close failed: $!" 
	        and die "close_route";
        close ERR or 
	  XB_Log::log "err" => 
	      "Unable to cleanup after deleting routes. Close failed: $!" 
	        and die "close_route";

        if (defined $cpid) {
	  waitpid $cpid, 0 == $cpid or
	    XB_Log::log "err" => "Operating system resource error. ".
	        "No child $cpid" and die "wait"; 
        }

        # NOTE: NO ERROR WHEN DELETING A NON-EXISTING ROUTE. NEED REVIEW!
        #if (defined(@route)) {
        ### This is an ugly hack to avoid giving an error if the route
        ### didn't exist.
        if (scalar @route and ($route[0] !~ m/No such process/i)) {
	  # error
	  XB_Log::log "err" => "route command returned an error: @route"
	      and die "route"; }
        else { return; } # no error
     }
    
  }; # eval block


  XB_Log::log "info" => "<- $module$proc $params";
  return 1 unless $@;
  if ($@ =~ /^(parse|close_route|route|wait|make_backup|checkconf|
    XB_Cisco::cmd)/) {
    XB_Log::log "err" => "$module$proc: caught error: $@";
    # no cleanup
  } elsif ($@ =~ /^(open|close)/) {
    XB_Log::log "err" => "$module$proc: caught error: $@";
  } elsif ($@ =~ /^(move|restart|checkconf)/) {
    XB_Log::log "err" => "$module$proc: caught error: $@";
  } else {
    XB_Log::log "warning" => "$module$proc caught unexpected exception $@";
  }
  die "$module$proc $@";
} # sub delete_route(@)

# Function:
#      get_route
# Used by: ALL
# Description:
#	Retrieve configuration for given route
# Arguments:
#       %hash =
#                      ( ovlname     => <name>
#		    routing_method   => (static|dynamic)
#			 family      => (-inet|-inet6)
#			 dstarg      => (-net|-host)
#			 destination => <ip address>
#			 netmask_opt => (-netmask|-prefixlen)
#			 netmask     => (<netmask>|<prefix length>)
#			 gateway     => <ip address>
#		       );
# Returns:
#	1 if route is configured
#	0 otherwise
# Exceptions:
#	"XB_Route::get_route" on error, nothing to clean up by caller
#
sub get_route(@) {
  my $proc = "get_route";
  my (@args) = @_;
  my $params;

  XB_Log::log "info" => "-> $module$proc @args";

  $params->{command} = "get";
  if (not verify_input($params)){
    XB_Log::log "err" => "Unable to obtain routes. Input could not be parsed";
    die "parse";
  }

  my $get_route = 0;
  $get_route = eval {
    if ($XB_Params::node_opts{os} =~ /cisco/i) {
      # Cisco IOS
      my @cpid = "";
      my $line;
      my @cmd;
      if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
        @cmd = ("show running-config | include ip route $params->{destination} $params->{netmask}");
        @cpid = XB_CiscoSSH::show_cmd @cmd or XB_Log::log "err" 
          => "$!: Route command failed" and die "route"; 
      }
      else {
       @cmd = ("show running-configuration | include ip route $params->{destination}");
       @cpid = XB_CiscoSSH::show_cmd @cmd
         or XB_Log::log "err" => "$!: Route command failed" and die "route"; 
      }
      @cpid = split "\n", $cpid[1];
      @cpid = split(" ", $cpid[0]);
      if ($cpid[0] ne "") {
        if ($cpid[2] eq $params->{destination}) {
	  if ((defined($params->{netmask})) && ($params->{netmask} ne "") && 
	    ($cpid[3] eq $params->{netmask}))
	  { return 1; }
	  else 
	  { return 1; }
        }
      }
      else { return 0; }
    }  
    elsif ($XB_Params::NODEOS =~ /linux|nist/i) {
      # Linux OS (No "route get")
      my $pipe = "route -n |";
      open ROUTE, $pipe or
	XB_Log::log "err" => "Unable to obtain routes. $!: Could not open pipe $pipe"
	    and die "route";

      my $line;
      while (defined($line = <ROUTE>)) {
	my @line = split(" ", $line);
	
	if ($line[0] eq $params->{destination}){
	  if (defined $params->{netmask}){
	    if ($line[2] eq $params->{netmask}){
	      return 1;
	    } else {
	      return 0;
	    }
	  } else {
	    return 1;
	  }
	}
      }

      close ROUTE;
      return 0;

    }  else {

      # Default OS (FreeBSD)
      my $cpid;

      my @cmd = (\*WTR, \*RDR, \*ERR, "route", "-n", "get",
		 $params->{dstarg}, $params->{destination});

      my @remaining = ();
      if ((defined($params->{netmask})) && ($params->{netmask} ne "")) {
	@remaining = ($params->{netmask_opt}, $params->{netmask});
      }

      open3(@cmd, @remaining) or
	XB_Log::log "err" => "Unable to obtain routes. ".
	    "$!: Route command failed" and die "route";

      if (defined(my $line = <ERR>)) {
	close WTR or 
	  XB_Log::log "err" => "Unable to cleanup after obtaining routes. Close failed: $!"
	  and die "close";
	close RDR or 
	  XB_Log::log "err" => "Unable to cleanup after obtaining routes. Close failed: $!"
	  and die "close";
	close ERR or 
	  XB_Log::log "err" => "Unable to cleanup after obtaining routes. Close failed: $!"
	  and die "close";
	
	if (defined $cpid) {
	  waitpid $cpid, 0 == $cpid or
	    XB_Log::log "err" => "Operating system resource error. No child $cpid" and die "wait";
	}
	
	return 0;

      } else {

	while (defined(my $line = <RDR>)) {
	  if ($line =~ m/route to:/) {
	    # get "route to:" line and extract IP
	    $line =~ s/\s$//g;
	    my @line = split(':', $line);
	    my $goal = $line[1];
	    $goal =~ s/^\s*//;
	
	    # get "destination:" line and extract IP
	    $line = <RDR>;
	    $line =~ s/\s$//g;
	    @line = split(':', $line);
	    my $dest = $line[1];
	    $dest =~ s/^\s*//;
	
	    close WTR;
	    close RDR;
	    close ERR;
	
	    if (defined $cpid) {
	      waitpid $cpid, 0 == $cpid or
		XB_Log::log "err" => "Operating system resource error. No child $cpid" and die "wait";
	    }
	    if ($goal eq $dest) { return 1; } # explicit route defined
	    else { return 0; } # uses default route
	
	    last;
	  }
	} #while (defined(my $line = <ROUTE>))
      }
    } # else
  }; # eval

  XB_Log::log "info" => "<- $module$proc @args";
  return $get_route unless $@;
  if ($@ =~ /^(open|route|wait|close|XB_CiscoSSH::show_cmd)/){
    XB_Log::log "err" => "$module$proc: caught error: $@";
  } else {
    XB_Log::log "warning" => "$module$proc caught unexpected exception $@";
  }
  die "$module$proc";

} # sub get_route(@)

# Description:
#	Initialize the routing module. Call this *once* before using
#	any function in here.
# Arguments:
#       -
# Returns:
#	-
# Exceptions:
#	"XB_Route::init" on error, nothing to clean up by caller
sub init () {
}


1;



syntax highlighted by Code2HTML, v. 0.9.1