#
# $Id: FreeBSD.pm,v efe6729c89ce 2015/01/31 17:37:12 gomor $
#
package Net::Routing::FreeBSD;
use strict;
use warnings;

our $VERSION = '0.20';

use base qw(Net::Routing::Linux);

use Net::IPv4Addr;
use Net::IPv6Addr;
use Net::Routing qw($Error NR_DEFAULT_ROUTE4 NR_DEFAULT_ROUTE6 NR_LOCAL_ROUTE4 NR_LOCAL_ROUTE6);

sub get {
   my $self = shift;

   my $bin = '';
   for my $path (@{$self->path}) {
      if (-f "$path/netstat") {
         $bin = "$path/netstat";
         last;
      }
   }
   if (! length($bin)) {
      $Error = "unable to find netstat command from current PATH";
      return;
   }

   my $cmd4 = [ $bin, '-rn4' ];
   my $cmd6 = [ $bin, '-rn6' ];

   return $self->SUPER::get($cmd4, $cmd6);
}

sub _get_inet {
   my $self = shift;
   my ($lines) = @_;

   my @routes = ();

   for my $line (@$lines) {
      my @toks = split(/\s+/, $line);
      my $route = $toks[0];
      my $gateway = $toks[1];
      my $flags = $toks[2];
      my $interface = $toks[3];
      my $expire = $toks[4];

      if (defined($route) && defined($gateway) && defined($interface)) {
         #print STDERR "*** DEBUG $route $gateway $interface\n";

         # Convert FreeBSD strings to universal IP addresses
         if ($route eq 'default') {
            $route = '0.0.0.0';
         }
         if ($gateway =~ /^link/) {
            $gateway = '0.0.0.0';
         }

         # A first sanity check to help Net::IPv4Addr
         if ($route !~ /^[0-9\.]+$/ || $gateway !~ /^[0-9\.]+$/) {
            next;
         }

         eval {
            my ($ip1, $cidr1) = Net::IPv4Addr::ipv4_parse($route);
            my ($ip2, $cidr2) = Net::IPv4Addr::ipv4_parse($gateway);
         };
         if ($@) {
            #chomp($@);
            #print "*** DEBUG[$@]\n";
            next; # Not a valid line for us.
         }

         # Ok, proceed.
         my %route = (
            route => $route,
            gateway => $gateway,
            interface => $interface,
         );

         # Default route
         if ($route eq '0.0.0.0') {
            $route{default} = 1;
            $route{route} = NR_DEFAULT_ROUTE4();
         }

         # Local subnet
         if ($gateway eq '0.0.0.0') {
            $route{local} = 1;
            $route{gateway} = NR_LOCAL_ROUTE4();
         }

         if ($route{route} !~ /\/\d+$/) {
            $route{route} .= '/32';
         }

         push @routes, \%route;
      }
   }

   return \@routes;
}

sub _get_inet6 {
   my $self = shift;
   my ($lines) = @_;

   my @routes = ();

   for my $line (@$lines) {
      my @toks = split(/\s+/, $line);
      my $route = $toks[0];
      my $gateway = $toks[1];
      my $flag = $toks[2];
      my $interface = $toks[3];
      my $expire = $toks[4];

      if (defined($route) && defined($gateway) && defined($interface)) {
         # Convert FreeBSD strings to universal IP addresses
         if ($gateway =~ /^link/ || $gateway eq '::1') {
            $gateway = '::';
         }

         # Special case: an entry with a MAC address means a default gateway
         if ($gateway =~ /^[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}$/) {
            push @routes, {
               route => NR_DEFAULT_ROUTE6(),
               gateway => $route,
               interface => $interface,
            };
         }

         # A first sanity check to help Net::IPv6Addr
         if ($route !~ /^[0-9a-f:\/]+$/i || $gateway !~ /^[0-9a-f:\/]+$/i) {
            next;
         }

         #print STDERR "*** DEBUG $route $gateway $interface\n";

         eval {
            #print "*** DEBUG $route $gateway\n";
            my $ip1 = Net::IPv6Addr::ipv6_parse($route);
            my $ip2 = Net::IPv6Addr::ipv6_parse($gateway);
         };
         if ($@) {
            #chomp($@);
            #print "*** DEBUG[$@]\n";
            next; # Not a valid line for us.
         }

         # Ok, proceed.
         my %route = (
            route => $route,
            gateway => $gateway,
            interface => $interface,
         );

         # Default route
         if ($route eq '::') {
            $route{default} = 1;
            $route{route} = NR_DEFAULT_ROUTE6();
         }

         # Local subnet
         if ($gateway eq '::') {
            $route{local} = 1;
            $route{gateway} = NR_LOCAL_ROUTE6();
         }

         if ($route{route} !~ /\/\d+$/) {
            $route{route} .= '/128';
         }

         push @routes, \%route;
      }
   }

   return \@routes;
}

1;

__END__

=head1 NAME

Net::Routing::FreeBSD - manage route entries on FreeBSD

=head1 SYNOPSIS

=head1 AUTHOR

Patrice E<lt>GomoRE<gt> Auffret

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2015, Patrice E<lt>GomoRE<gt> Auffret

You may distribute this module under the terms of the Artistic license.
See LICENSE.Artistic file in the source distribution archive.

=cut
