summaryrefslogtreecommitdiff
path: root/lib/Vyatta/Interface.pm
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@vyatta.com>2012-08-14 10:28:59 -0700
committerStephen Hemminger <shemminger@vyatta.com>2012-08-14 10:28:59 -0700
commit58bff658021e833f2033c6bf366fe42a4c977976 (patch)
tree3142562475cbbec570b2e7e5516505269fb7513d /lib/Vyatta/Interface.pm
parent21dc791c91c27a07d9cb9820eb3057017ce14767 (diff)
downloadvyatta-cfg-58bff658021e833f2033c6bf366fe42a4c977976.tar.gz
vyatta-cfg-58bff658021e833f2033c6bf366fe42a4c977976.zip
Rewrite interface prefix matching
Bug 8279 Read network device name prefixes from file.
Diffstat (limited to 'lib/Vyatta/Interface.pm')
-rwxr-xr-xlib/Vyatta/Interface.pm550
1 files changed, 289 insertions, 261 deletions
diff --git a/lib/Vyatta/Interface.pm b/lib/Vyatta/Interface.pm
index c636d51..84b0057 100755
--- a/lib/Vyatta/Interface.pm
+++ b/lib/Vyatta/Interface.pm
@@ -21,11 +21,11 @@ package Vyatta::Interface;
use strict;
use warnings;
-use Vyatta::Config;
+
use Vyatta::Misc;
use Vyatta::ioctl;
+use Vyatta::Config;
use base 'Exporter';
-use Socket;
our @EXPORT = qw(IFF_UP IFF_BROADCAST IFF_DEBUG IFF_LOOPBACK
IFF_POINTOPOINT IFF_RUNNING IFF_NOARP
@@ -53,286 +53,89 @@ use constant {
IFF_ECHO => 0x40000, # echo sent packets
};
-#
-# Mapping from name to attributes
-# path: configuration level below interfaces
-# vif: places to look for vif (if any)
-my %net_prefix = (
- '^adsl\d+$' => { path => 'adsl',
- vif => 'vif', },
- '^bond\d+$' => { path => 'bonding',
- vif => 'vif', },
- '^bond\d+v\d+$' => { path => 'vrrp' },
- '^br\d+$' => { path => 'bridge',
- vif => 'vif' },
- '^eth\d+$' => { path => 'ethernet',
- vif => 'vif', },
- '^eth\d+v\d+$' => { path => 'vrrp' },
- '^eth\d+.\d+v\d+$' => { path => 'vrrp' },
- '^lo$' => { path => 'loopback' },
- '^ml\d+$' => { path => 'multilink',
- vif => 'vif', },
- '^vtun\d+$' => { path => 'openvpn' },
- '^wan\d+$' => { path => 'serial',
- vif => ( 'cisco-hdlc vif', 'ppp vif',
- 'frame-relay vif' ), },
- '^tun\d+$' => { path => 'tunnel' },
- '^vti\d+$' => { path => 'vti' },
- '^wlm\d+$' => { path => 'wireless-modem' },
- '^peth\d+$' => { path => 'pseudo-ethernet',
- vif => 'vif', },
- '^wlan\d+$' => { path => 'wireless', vif => 'vif' },
- '^ifb\d+$' => { path => 'input' },
- '^dp\d+' => { path => 'dataplane', vif => 'vif' },
- '^vsw\d+$' => { path => 'vswitch', vif => 'vif' },
-);
-
-sub get_net_prefix {
- return %net_prefix;
-}
-
-# get list of interface types (only used in usage function)
-sub interface_types {
- my @types = map { $net_prefix{$_}{path} } keys %net_prefix;
- return @types;
-}
-
-# get map of current addresses on the system
-# returns reference to hash of form:
-# ( "192.168.1.1" => { 'eth0', 'eth2' } )
-sub get_cfg_addresses {
- my $config = new Vyatta::Config;
- my @cfgifs = get_all_cfg_interfaces();
- my %ahash;
+# Build list of known interface types
+my $NETDEV = '/opt/vyatta/etc/netdevice';
- foreach my $intf ( @cfgifs ) {
- my $name = $intf->{'name'};
+# Hash of interface types
+# ex: $net_prefix{"eth"} = "ethernet"
+my %net_prefix;
- # workaround openvpn wart
- my @addrs;
- $config->setLevel($intf->{'path'});
- if ($name =~ /^vtun/) {
- @addrs = $config->listNodes('local-address');
- } else {
- @addrs = $config->returnValues('address');
- }
+sub parse_netdev_file {
+ my $filename = shift;
- foreach my $addr ( @addrs ){
- next if ($addr =~ /^dhcp/);
+ open (my $in, '<', $filename)
+ or return;
- # put interface into
- my $aif = $ahash{$addr};
- if ($aif) {
- push @{$aif}, $name;
- } else {
- $ahash{$addr} = [ $name ];
- }
- }
- }
+ while (<$in>) {
+ chomp;
- return \%ahash;
-}
+ # remove text after # as comment
+ s/#.*$//;
-# get all configured interfaces (in active or working configuration)
-# return a hash of:
-# 'name' => ethX
-# 'path' => "interfaces ethernet ethX"
-sub get_all_cfg_interfaces {
- my ($in_active) = @_;
- my $vfunc = ($in_active ? 'listOrigNodes' : 'listNodes');
+ my ($prefix, $type) = split;
- my $cfg = new Vyatta::Config;
- my @ret_ifs = ();
- for my $pfx (keys %net_prefix) {
- my ($type, $vif) = ($net_prefix{$pfx}->{path},
- $net_prefix{$pfx}->{vif});
- my @vifs = (defined($vif)
- ? ((ref($vif) eq 'ARRAY') ? @{$vif}
- : ($vif))
- : ());
- for my $tif ($cfg->$vfunc("interfaces $type")) {
- push @ret_ifs, { 'name' => $tif,
- 'path' => "interfaces $type $tif" };
- for my $vpath (@vifs) {
- for my $vnum ($cfg->$vfunc("interfaces $type $tif $vpath")) {
- push @ret_ifs, { 'name' => "$tif.$vnum",
- 'path' => "interfaces $type $tif $vpath $vnum" };
- }
- }
- }
- }
- # special case for vrrp
- for my $eth ($cfg->$vfunc('interfaces ethernet')) {
- for my $vrid ($cfg->$vfunc("interfaces ethernet $eth vrrp vrrp-group")) {
- push @ret_ifs, { 'name' => $eth."v".$vrid,
- 'path' => "interfaces ethernet $eth vrrp vrrp-group $vrid interface" };
- }
- for my $vif ($cfg->$vfunc("interfaces ethernet $eth vif")) {
- for my $vrid ($cfg->$vfunc("interfaces ethernet $eth vif $vif vrrp vrrp-group")) {
- push @ret_ifs, { 'name' => $eth.".".$vif."v".$vrid,
- 'path' => "interfaces ethernet $eth vif $vif vrrp vrrp-group $vrid interface" };
- }
- }
- }
- for my $bond ($cfg->$vfunc('interfaces bonding')) {
- for my $vrid ($cfg->$vfunc("interfaces bonding $bond vrrp vrrp-group")) {
- push @ret_ifs, { 'name' => $bond."v".$vrid,
- 'path' => "interfaces bonding $bond vrrp vrrp-group $vrid interface" };
- }
- for my $vif ($cfg->$vfunc("interfaces bonding $bond vif")) {
- for my $vrid ($cfg->$vfunc("interfaces bonding $bond vif $vif vrrp vrrp-group")) {
- push @ret_ifs, { 'name' => $bond.".".$vif."v".$vrid,
- 'path' => "interfaces bonding $bond vif $vif vrrp vrrp-group $vrid interface" };
- }
- }
- }
+ # ignore blank lines or missing patterns
+ next unless defined($prefix) && defined($type);
- # now special cases for pppo*/adsl
- for my $eth ($cfg->$vfunc('interfaces ethernet')) {
- for my $ep ($cfg->$vfunc("interfaces ethernet $eth pppoe")) {
- push @ret_ifs, { 'name' => "pppoe$ep",
- 'path' => "interfaces ethernet $eth pppoe $ep" };
- }
+ $net_prefix{$prefix} = $type;
}
- for my $a ($cfg->$vfunc('interfaces adsl')) {
- for my $p ($cfg->$vfunc("interfaces adsl $a pvc")) {
- for my $t ($cfg->$vfunc("interfaces adsl $a pvc $p")) {
- if ($t eq 'classical-ipoa' or $t eq 'bridged-ethernet') {
- # classical-ipoa or bridged-ethernet
- push @ret_ifs, { 'name' => $a,
- 'path' => "interfaces adsl $a pvc $p $t" };
- next;
- }
- # pppo[ea]
- for my $i ($cfg->$vfunc("interfaces adsl $a pvc $p $t")) {
- push @ret_ifs, { 'name' => "$t$i",
- 'path' => "interfaces adsl $a pvc $p $t $i" };
- }
- }
- }
- }
-
- return @ret_ifs;
+ close $in;
}
-# Read ppp config to fine associated interface for ppp device
-sub _ppp_intf {
- my $dev = shift;
- my $intf;
-
- open (my $ppp, '<', "/etc/ppp/peers/$dev")
- or return; # no such device
+# read /opt/vyatta/etc/netdevice
+parse_netdev_file($NETDEV);
- while (<$ppp>) {
- chomp;
- # looking for line like:
- # pty "/usr/sbin/pppoe -m 1412 -I eth1"
- next unless /^pty\s.*-I\s*(\w+)"/;
- $intf = $1;
- last;
+# look for optional package interfaces in /opt/vyatta/etc/netdevice.d
+my $dirname = $NETDEV . '.d';
+if (opendir(my $netd, $dirname)) {
+ foreach my $pkg (sort readdir $netd) {
+ parse_netdev_file($dirname . '/' . $pkg);
}
- close $ppp;
-
- return $intf;
+ closedir $netd;
}
-# Go path hunting to find ppp device
-sub ppp_path {
- my $self = shift;
-
- return unless ($self->{name} =~ /^(pppo[ae])(\d+)/);
- my $type = $1;
- my $id = $2;
-
- my $intf = _ppp_intf($self->{name});
- return unless $intf;
-
- my $config = new Vyatta::Config;
- if ($type eq 'pppoe') {
- my $path = "interfaces ethernet $intf pppoe $id";
- return $path if $config->exists($path);
- }
-
- my $adsl = "interfaces adsl $intf pvc";
- foreach my $pvc ($config->listNodes($adsl)) {
- my $path = "$adsl $pvc $type $id";
- return $path if $config->exists($path);
- }
- return;
+# get list of interface types (only used in usage function)
+sub interface_types {
+ return values %net_prefix;
}
# new interface description object
sub new {
my $that = shift;
- my $name = pop;
+ my $name = pop;
my $class = ref($that) || $that;
- my ($dev, $vif, $vrid);
-
- # need argument to constructor
- return unless $name;
-
- # Special case for ppp devices
- if ($name =~ /^(pppo[ae])(\d+)/) {
- my $type = $1;
- my $self = {
- name => $name,
- type => $type,
- dev => $name,
- };
- bless $self, $class;
- return $self;
- }
+ my ($vif, $vrid);
+ my $dev = $name;
- if ( $name =~ m/(\w+)\.(\d+)v(\d+)/ ){
- $dev = $1;
- $vif = $2;
- $vrid = $3;
- } elsif ( $name =~ m/(\w+)v(\d+)/ ) {
- $dev = $1;
- $vrid = $2;
- # Strip off vif from name
- } elsif ( $name =~ m/(\w+)\.(\d+)/ ) {
- $dev = $1;
- $vif = $2;
- } else {
- $dev = $name;
+ # remove VRRP id suffix
+ if ($dev =~ /^(.*)v(\d+)$/) {
+ $dev = $1;
+ $vrid = $2;
}
- foreach my $prefix (keys %net_prefix) {
- next unless $dev =~ /$prefix/;
- my $type = $net_prefix{$prefix}{path};
- my $vifpath = $net_prefix{$prefix}{vif};
-
- # Interface name has vif, but this type doesn't support vif!
- return if ( $vif && !$vifpath && !$vrid);
-
- # Check path if given
- return if ( $#_ >= 0 && join( ' ', @_ ) ne $type );
-
- my $path = "interfaces $type $dev";
- $path .= " $vifpath $vif" if $vif;
- # add the vif 1 to multilink paths since they don't have vif interfaces
- # denoted by <if>.<vif> and only allow 1 vif to be set
- $path .= " vif 1" if ($dev =~ m/^ml\d+$/);
- $path .= " vrrp vrrp-group $vrid interface" if $vrid;
- $type = 'vrrp' if $vrid;
-
- my $self = {
- name => $name,
- type => $type,
- path => $path,
- dev => $dev,
- vif => $vif,
- vrid => $vrid
- };
-
- bless $self, $class;
- return $self;
+ # remove VLAN suffix
+ if ($dev =~ /^(.*)\.(\d+)/) {
+ $dev = $1;
+ $vif = $2;
}
- return; # nothing
+ return unless ($dev =~ /^([a-z]+)/);
+
+ # convert from prefix 'eth' to type 'ethernet'
+ my $type = $net_prefix{$1};
+ return unless $type; # unknown network interface type
+
+ my $self = {
+ name => $name,
+ type => $type,
+ dev => $dev,
+ vif => $vif,
+ vrid => $vrid,
+ };
+ bless $self, $class;
+ return $self;
}
## Field accessors
@@ -343,12 +146,51 @@ sub name {
sub path {
my $self = shift;
- my $path = $self->{path};
+ my $config = new Vyatta::Config;
+
+ if ($self->{name} =~ /^(pppo[ae])(\d+)/) {
+ # For ppp need to look in config file to find where used
+ my $type = $1;
+ my $id = $2;
- return $path if defined($path);
+ my $intf = _ppp_intf($self->{name});
+ return unless $intf;
- # Go path hunting to find ppp device
- return ppp_path($self);
+ if ($type eq 'pppoe') {
+ return "interfaces ethernet $intf pppoe $id";
+ }
+
+ my $adsl = "interfaces adsl $intf pvc";
+ my $config = new Vyatta::Config;
+ foreach my $pvc ($config->listNodes($adsl)) {
+ my $path = "$adsl $pvc $type $id";
+ return $path if $config->exists($path);
+ }
+ }
+ elsif ($self->{name} =~ /^(wan\d+)\.(\d+)/) {
+ # guesswork for wan devices
+ my $dev = $1;
+ my $vif = $2;
+ foreach my $type (qw(cisco-hdlc ppp frame-relay)) {
+ my $path = "interfaces serial $dev $type vif $vif";
+ return $path if $config->exists($path);
+ }
+ }
+ else {
+ # normal device
+ my $path = "interfaces $self->{type} $self->{dev}";
+ $path .= " vrrp $self->{vrid}" if $self->{vrid};
+ $path .= " vif $self->{vif}" if $self->{vif};
+
+ return $path;
+ }
+
+ return; # undefined (not in config)
+}
+
+sub type {
+ my $self = shift;
+ return $self->{type};
}
sub vif {
@@ -366,9 +208,25 @@ sub physicalDevice {
return $self->{dev};
}
-sub type {
- my $self = shift;
- return $self->{type};
+# Read ppp config to fine associated interface for ppp device
+sub _ppp_intf {
+ my $dev = shift;
+ my $intf;
+
+ open (my $ppp, '<', "/etc/ppp/peers/$dev")
+ or return; # no such device
+
+ while (<$ppp>) {
+ chomp;
+ # looking for line like:
+ # pty "/usr/sbin/pppoe -m 1412 -I eth1"
+ next unless /^pty\s.*-I\s*(\w+)"/;
+ $intf = $1;
+ last;
+ }
+ close $ppp;
+
+ return $intf;
}
## Configuration checks
@@ -491,4 +349,174 @@ sub description {
return interface_description($self->{name});
}
+## Utility functions
+
+# enumerate vrrp slave devices
+sub get_vrrp_interfaces {
+ my ($cfg, $vfunc, $dev, $path) = @_;
+ my @ret_ifs;
+
+ foreach my $vrid ($cfg->$vfunc("$path vrrp vrrp-group")) {
+ my $vrdev = $dev."v".$vrid;
+ my $vrpath = "$path vrrp vrrp-group $vrid interface";
+
+ push @ret_ifs, { name => $vrdev,
+ type => 'vrrp',
+ path => $vrpath,
+ };
+ }
+
+ return @ret_ifs;
+}
+
+# enumerate vif devies
+sub get_vif_interfaces {
+ my ($cfg, $vfunc, $dev, $type, $path) = @_;
+ my @ret_ifs;
+
+ foreach my $vnum ($cfg->$vfunc("$path vif")) {
+ my $vifdev = "$dev.$vnum";
+ my $vifpath = "$path vif $vnum";
+ push @ret_ifs, { name => $vifdev,
+ type => $type,
+ path => $vifpath };
+ push @ret_ifs, get_vrrp_interfaces($cfg, $vfunc, $vifdev, $vifpath);
+ }
+
+ return @ret_ifs;
+}
+
+sub get_pppoe_interfaces {
+ my ($cfg, $vfunc, $dev, $path) = @_;
+ my @ret_ifs;
+
+ foreach my $ep ($cfg->$vfunc("$path pppoe")) {
+ my $pppdev = "pppoe$ep";
+ my $ppppath = "$path pppoe $ep";
+
+ push @ret_ifs, { name => $pppdev,
+ type => 'pppoe',
+ path => $ppppath };
+ }
+
+ return @ret_ifs;
+}
+
+# special cases for adsl
+sub get_adsl_interfaces {
+ my ($cfg, $vfunc) = @_;
+ my @ret_ifs;
+
+ for my $p ($cfg->$vfunc("interfaces adsl $a $a pvc")) {
+ for my $t ($cfg->$vfunc("interfaces adsl $a $a pvc $p")) {
+ if ($t eq 'classical-ipoa' or $t eq 'bridged-ethernet') {
+ # classical-ipoa or bridged-ethernet
+ push @ret_ifs, { name => $a,
+ type => 'adsl',
+ path => "interfaces adsl $a $a pvc $p $t" };
+ next;
+ }
+
+ # pppo[ea]
+ for my $i ($cfg->$vfunc("interfaces adsl $a $a pvc $p $t")) {
+ push @ret_ifs, { name => "$t$i",
+ type => 'adsl-pppo[ea]',
+ path => "interfaces adsl $a $a pvc $p $t $i" };
+ }
+ }
+ }
+ return @ret_ifs;
+}
+
+# get all configured interfaces from configuration
+# parameter is virtual function (see Config.pm)
+#
+# return a hash of:
+# name => ethX
+# type => "ethernet"
+# path => "interfaces ethernet ethX"
+#
+# Don't use this function directly, use wrappers below instead
+sub get_config_interfaces {
+ my $vfunc = shift;
+ my $cfg = new Vyatta::Config;
+ my @ret_ifs;
+
+ foreach my $type ($cfg->$vfunc("interfaces")) {
+ if ($type eq 'adsl') {
+ push @ret_ifs, get_adsl_interfaces($cfg, $vfunc);
+ next;
+ }
+
+ foreach my $dev ($cfg->$vfunc("interfaces $type")) {
+ my $path = "interfaces $type $dev";
+
+ push @ret_ifs, { name => $dev,
+ type => $type,
+ path => $path };
+ push @ret_ifs, get_vrrp_interfaces($cfg, $vfunc, $dev, $path);
+ push @ret_ifs, get_vif_interfaces($cfg, $vfunc, $dev, $type, $path);
+
+ push @ret_ifs, get_pppoe_interfaces($cfg, $vfunc, $dev, $path)
+ if ($type eq 'ethernet');
+ }
+
+ }
+
+ return @ret_ifs;
+}
+
+# get array of hash for interfaces in working config
+sub get_interfaces {
+ return get_config_interfaces('listNodes');
+}
+
+# get array of hash for interfaces in configuration
+# when used outside of config mode.
+sub get_effective_interfaces {
+ return get_config_interfaces('listEffectiveNodes');
+}
+
+# get array of hash for interfaces in original config
+# only makes sense in configuration mode
+sub get_original_interfaces {
+ return get_config_interfaces('listOrigNodes');
+}
+
+# get map of current addresses on the system
+# returns reference to hash of form:
+# ( "192.168.1.1" => { 'eth0', 'eth2' } )
+sub get_cfg_addresses {
+ my $config = new Vyatta::Config;
+ my @cfgifs = get_interfaces();
+ my %ahash;
+
+ foreach my $intf ( @cfgifs ) {
+ my $name = $intf->{'name'};
+
+ # workaround openvpn wart
+ my @addrs;
+ $config->setLevel($intf->{'path'});
+ if ($name =~ /^vtun/) {
+ @addrs = $config->listNodes('local-address');
+ } else {
+ @addrs = $config->returnValues('address');
+ }
+
+ foreach my $addr ( @addrs ){
+ next if ($addr =~ /^dhcp/);
+
+ # put interface into
+ my $aif = $ahash{$addr};
+ if ($aif) {
+ push @{$aif}, $name;
+ } else {
+ $ahash{$addr} = [ $name ];
+ }
+ }
+ }
+
+ return \%ahash;
+}
+
1;