File Coverage

blib/lib/Net/Routing/Linux.pm
Criterion Covered Total %
statement 123 140 87.8
branch 30 44 68.1
condition 22 37 59.4
subroutine 11 11 100.0
pod 2 2 100.0
total 188 234 80.3


line stmt bran cond sub pod time code
1             #
2             # $Id: Linux.pm,v 717225574cff 2015/11/12 08:46:57 gomor $
3             #
4             package Net::Routing::Linux;
5 6     6   18413 use strict;
  6         11  
  6         181  
6 6     6   33 use warnings;
  6         12  
  6         282  
7              
8             our $VERSION = '0.44';
9              
10 6     6   29 use base qw(Net::Routing);
  6         14  
  6         2996  
11              
12 6     6   5804 use IPC::Run3;
  6         225238  
  6         355  
13 6     6   53 use Net::IPv4Addr;
  6         13  
  6         257  
14 6     6   30 use Net::IPv6Addr;
  6         12  
  6         217  
15 6     6   31 use Net::Routing qw($Error :constants);
  6         12  
  6         9542  
16              
17             sub new {
18 4 100   4 1 581 my $self = shift->SUPER::new(
19             @_,
20             ) or return;
21              
22 1 50       10 if (! defined($self->path)) {
23 0         0 $Error = "you must give a `path' attribute";
24 0         0 return;
25             }
26              
27 1         22 my $family = $self->family;
28 1 50       18 if (! defined($family)) {
29 0         0 $Error = "you must give a `family' attribute";
30 0         0 return;
31             }
32             else {
33 1 50 33     9 if ($family ne NR_FAMILY_INET4() && $family ne NR_FAMILY_INET6()) {
34 0         0 $Error = "family not supported [$family]: use either NR_FAMILY_INET4() or NR_FAMILY_INET6()";
35 0         0 return;
36             }
37             }
38              
39 1         89 return $self;
40             }
41              
42             sub get {
43 4     4 1 8 my $self = shift;
44 4         10 my ($cmd4, $cmd6) = @_;
45              
46 4         17 my $path = $self->path;
47 4         52 my $family = $self->family;
48              
49 4         30 my $bin = '';
50             {
51 4         7 local $ENV{LC_ALL} = $self->lc_all;
  4         19  
52              
53 4         48 for my $path (@{$self->path}) {
  4         14  
54 4 50       159 if (-f "$path/netstat") {
55 4         19 $bin = "$path/netstat";
56 4         8 last;
57             }
58             }
59 4 50       24 if (! length($bin)) {
60 0         0 $Error = "unable to find netstat command from current PATH";
61 0         0 return;
62             }
63             };
64              
65 4   100     22 $cmd4 ||= [ $bin, '-rnA', 'inet' ];
66 4   100     22 $cmd6 ||= [ $bin, '-rnA', 'inet6' ];
67              
68 4         9 my $cmd = [];
69 4 50       16 if ($family eq NR_FAMILY_INET4()) {
70 4         9 $cmd = $cmd4;
71             }
72             # If not NR_FAMILY_INET4(), it must be NR_FAMILY_INET6() because we validated family at new()
73             else {
74 0         0 $cmd = $cmd6;
75             }
76              
77 4         14 my $out;
78             my $err;
79 4         7 eval {
80 4         33 run3($cmd, undef, \$out, \$err);
81             };
82             # Error in executing run3()
83 4 50       66631 if ($@) {
    100          
84 0         0 chomp($@);
85 0         0 $Error = "unable to execute command [".join(' ', @$cmd)."]: $@";
86 0         0 return;
87             }
88             # Error in command execution
89             elsif ($?) {
90 3         27 chomp($err);
91 3         43 $Error = "command execution failed [".join(' ', @$cmd)."]: $err";
92 3         486 return;
93             }
94              
95 1         8 my $routes = [];
96              
97 1         9 my @lines = split(/\n/, $out);
98 1 50       12 if ($family eq NR_FAMILY_INET4()) {
99 1         16 $routes = $self->_get_inet4(\@lines);
100             }
101             # If not NR_FAMILY_INET4(), it must be NR_FAMILY_INET6() because we validated family at new()
102             else {
103 0         0 $routes = $self->_get_inet6(\@lines);
104             }
105              
106 1         37 return $routes;
107             }
108              
109             sub _get_inet4 {
110 2     2   337 my $self = shift;
111 2         9 my ($lines) = @_;
112              
113 2         7 my @routes = ();
114 2         6 my %cache = ();
115              
116 2         9 for my $line (@$lines) {
117 8         84 my @toks = split(/\s+/, $line);
118 8         23 my $route = $toks[0];
119 8         16 my $gateway = $toks[1];
120 8         16 my $netmask = $toks[2];
121 8         15 my $flags = $toks[3];
122 8         14 my $mss = $toks[4];
123 8         19 my $window = $toks[5];
124 8         13 my $irtt = $toks[6];
125 8         21 my $interface = $toks[7];
126              
127 8 100 33     113 if (defined($route) && defined($gateway) && defined($interface)
      66        
      66        
128             && defined($netmask)) {
129             # A first sanity check to help Net::IPv4Addr
130 6 50 66     88 if ($route !~ /^[0-9\.]+$/ || $gateway !~ /^[0-9\.]+$/
      33        
131             || $netmask !~ /^[0-9\.]+$/) {
132 2         12 next;
133             }
134              
135 4         11 eval {
136 4         29 my ($ip1, $cidr1) = Net::IPv4Addr::ipv4_parse($route);
137 4         334 my ($ip2, $cidr2) = Net::IPv4Addr::ipv4_parse($gateway);
138 4         190 my ($ip3, $cidr3) = Net::IPv4Addr::ipv4_parse($netmask);
139             };
140 4 50       197 if ($@) {
141             #chomp($@);
142             #print "*** DEBUG[$@]\n";
143 0         0 next; # Not a valid line for us.
144             }
145              
146             # Ok, proceed.
147 4         38 my %route = (
148             route => $route,
149             gateway => $gateway,
150             interface => $interface,
151             );
152              
153             # Default route
154 4 100 66     46 if ($route eq '0.0.0.0' && $netmask eq '0.0.0.0') {
155 2         12 $route{default} = 1;
156 2         10 $route{route} = NR_DEFAULT_ROUTE4();
157             }
158             else {
159 2         17 my ($ip, $cidr) = Net::IPv4Addr::ipv4_parse("$route / $netmask");
160 2         344 $route{route} = "$ip/$cidr";
161             }
162              
163             # Local subnet
164 4 100       23 if ($gateway eq '0.0.0.0') {
165 2         7 $route{local} = 1;
166 2         9 $route{gateway} = NR_LOCAL_ROUTE4();
167             }
168              
169 4         82 my $id = $self->_to_psv(\%route);
170 4 50       25 if (! exists($cache{$id})) {
171 4         13 push @routes, \%route;
172 4         40 $cache{$id}++;
173             }
174             }
175             }
176              
177 2         15 return \@routes;
178             }
179              
180             sub _get_inet6 {
181 1     1   211 my $self = shift;
182 1         4 my ($lines) = @_;
183              
184 1         7 my @routes = ();
185 1         4 my %cache = ();
186              
187 1         8 for my $line (@$lines) {
188 8         95 my @toks = split(/\s+/, $line);
189 8         20 my $route = $toks[0];
190 8         19 my $gateway = $toks[1];
191 8         18 my $flag = $toks[2];
192 8         17 my $met = $toks[3];
193 8         96 my $ref = $toks[4];
194 8         18 my $use = $toks[5];
195 8         15 my $interface = $toks[6];
196              
197 8 100 33     111 if (defined($route) && defined($gateway) && defined($interface)) {
      66        
198             # A first sanity check to help Net::IPv6Addr
199 7 100 66     76 if ($route !~ /^[0-9a-f:\/]+$/i || $gateway !~ /^[0-9a-f:\/]+$/i) {
200 1         6 next;
201             }
202              
203 6         15 eval {
204             #print "*** DEBUG $route $gateway\n";
205 6         58 my $ip1 = Net::IPv6Addr::ipv6_parse($route);
206 6         465 my $ip2 = Net::IPv6Addr::ipv6_parse($gateway);
207             };
208 6 50       283 if ($@) {
209             #chomp($@);
210             #print "*** DEBUG[$@]\n";
211 0         0 next; # Not a valid line for us.
212             }
213              
214             # Ok, proceed.
215 6         41 my %route = (
216             route => $route,
217             gateway => $gateway,
218             interface => $interface,
219             );
220              
221             # Default route
222 6 50 66     42 if ($route eq '::/0' && $interface ne 'lo') {
223 0         0 $route{default} = 1;
224 0         0 $route{route} = NR_DEFAULT_ROUTE6();
225             }
226              
227             # Local subnet
228 6 50       22 if ($gateway eq '::') {
229 6         18 $route{local} = 1;
230 6         14 $route{gateway} = NR_LOCAL_ROUTE6();
231             }
232              
233 6         33 my $id = $self->_to_psv(\%route);
234 6 100       35 if (! exists($cache{$id})) {
235 5         15 push @routes, \%route;
236 5         38 $cache{$id}++;
237             }
238             }
239             }
240              
241 1         8 return \@routes;
242             }
243              
244             1;
245              
246             __END__