Skip to content

Commit

Permalink
Report an ipaddress in a Linux corner case observed on Cisco IOS XR
Browse files Browse the repository at this point in the history
- we have a default route to an interface
- the route has a configured src
- the src address is not configured on the default interface,
  but the default interface has no address at all

In this case we will choose the src as the ipaddress to report.
  • Loading branch information
Glenn Matthews committed Jan 19, 2016
1 parent e93ecd5 commit 6daaca8
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 11 deletions.
36 changes: 26 additions & 10 deletions lib/ohai/plugins/linux/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,15 @@ def check_routing_table(family, iface)

# a sanity check, especially for Linux-VServer, OpenVZ and LXC:
# don't report the route entry if the src address isn't set on the node
next if route_entry[:src] and not iface[route_int][:addresses].has_key? route_entry[:src]
# unless the interface has no addresses of this type at all
if route_entry[:src]
addr = iface[route_int][:addresses]
unless addr.nil? || addr.has_key?(route_entry[:src]) ||
addr.values.all? { |a| a['family'] != family[:name] }
Ohai::Log.debug("Skipping route entry whose src does not match the interface IP")
next
end
end

iface[route_int][:routes] = Array.new unless iface[route_int][:routes]
iface[route_int][:routes] << route_entry
Expand All @@ -126,6 +134,7 @@ def check_routing_table(family, iface)
# for information, default routes can be of this form :
# - default via 10.0.2.4 dev br0
# - default dev br0 scope link
# - default dev eth0 scope link src 1.1.1.1
# - default via 10.0.3.1 dev eth1 src 10.0.3.2 metric 10
# - default via 10.0.4.1 dev eth2 src 10.0.4.2 metric 20

Expand Down Expand Up @@ -310,7 +319,7 @@ def parse_ip_addr_inet6_line(cint, iface, line)

# returns the macaddress for interface from a hash of interfaces (iface elsewhere in this file)
def get_mac_for_interface(interfaces, interface)
interfaces[interface][:addresses].select{|k,v| v["family"]=="lladdr"}.first.first unless interfaces[interface][:flags].include? "NOARP"
interfaces[interface][:addresses].select{|k,v| v["family"]=="lladdr"}.first.first unless interfaces[interface][:addresses].nil? || interfaces[interface][:flags].include?("NOARP")
end

# returns the default route with the lowest metric (unspecified metric is 0)
Expand All @@ -332,15 +341,22 @@ def favored_default_route(routes, iface, default_route, family)
# using the source field when it's specified :
# 1) in the default route
# 2) in the route entry used to reach the default gateway
r[:src] and # it has a src field
iface[r[:dev]] and # the iface exists
iface[r[:dev]][:addresses].has_key? r[:src] and # the src ip is set on the node
iface[r[:dev]][:addresses][r[:src]][:scope].downcase != "link" and # this isn't a link level addresse
( r[:destination] == "default" or
( default_route[:via] and # the default route has a gateway
IPAddress(r[:destination]).include? IPAddress(default_route[:via]) # the route matches the gateway
)
r[:src] && # it has a src field
iface[r[:dev]] && # the iface exists
(
iface[r[:dev]][:addresses].nil? || # this int has no addresses
iface[r[:dev]][:addresses].values.all? { |addr| addr['family'] != family[:name] } || # this int has no ip
(
iface[r[:dev]][:addresses].has_key?(r[:src]) && # the src ip is set on the node
iface[r[:dev]][:addresses][r[:src]][:scope].downcase != "link" # this isn't a link level address
)
) && (
r[:destination] == "default" ||
(
default_route[:via] && # the default route has a gateway
IPAddress(r[:destination]).include?(IPAddress(default_route[:via])) # the route matches the gateway
)
)
elsif family[:name] == "inet6"
# selecting routes for ipv6
iface[r[:dev]] and # the iface exists
Expand Down
66 changes: 65 additions & 1 deletion spec/unit/plugins/linux/network_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:21515031 (20.5 MiB) TX bytes:2052 (2.0 KiB)
fwdintf Link encap:Ethernet HWaddr 00:00:00:00:00:0a
inet6 addr: fe80::200:ff:fe00:a/64 Scope:Link
UP RUNNING NOARP MULTICAST MTU:1496 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:1 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:140 (140.0 B)
'
# Note that ifconfig shows foo:veth0@eth0 but fails to show any address information.
# This was not a mistake collecting the output and Apparently ifconfig is broken in this regard.
Expand Down Expand Up @@ -225,6 +233,8 @@
link/ether e8:39:35:c5:c8:50 brd ff:ff:ff:ff:ff:ff
inet 192.168.13.34/24 brd 192.168.13.255 scope global xapi1
valid_lft forever preferred_lft forever
13: fwdintf: <MULTICAST,NOARP,UP,LOWER_UP> mtu 1496 qdisc pfifo_fast state UNKNOWN group default qlen 1000
link/ether 00:00:00:00:00:0a brd ff:ff:ff:ff:ff:ff
'
}

Expand Down Expand Up @@ -278,6 +288,12 @@
21468183 159866 0 0 0 0
TX: bytes packets errors dropped carrier collsns
2052 6 0 0 0 0
13: fwdintf: <MULTICAST,NOARP,UP,LOWER_UP> mtu 1496 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 00:00:00:00:00:0a brd ff:ff:ff:ff:ff:ff promiscuity 0
RX: bytes packets errors dropped overrun mcast
0 0 0 0 0 0
TX: bytes packets errors dropped carrier collsns
140 2 0 1 0 0
'
}

Expand Down Expand Up @@ -375,7 +391,7 @@
end

it "detects the interfaces" do
expect(plugin['network']['interfaces'].keys.sort).to eq(["eth0", "eth0.11", "eth0.151", "eth0.152", "eth0.153", "eth0:5", "eth3", "foo:veth0@eth0", "lo", "ovs-system", "tun0", "venet0", "venet0:0", "xapi1"])
expect(plugin['network']['interfaces'].keys.sort).to eq(["eth0", "eth0.11", "eth0.151", "eth0.152", "eth0.153", "eth0:5", "eth3", "foo:veth0@eth0", "fwdintf", "lo", "ovs-system", "tun0", "venet0", "venet0:0", "xapi1"])
end

it "detects the layer one details of an ethernet interface" do
Expand Down Expand Up @@ -898,6 +914,54 @@
end
end

describe "with a link level default route to an unaddressed int" do
let(:linux_ip_route) {
'default dev eth3 scope link
'
}

before(:each) do
plugin.run
end

it "completes the run" do
expect(Ohai::Log).not_to receive(:debug).with(/Plugin linux::network threw exception/)
expect(plugin['network']).not_to be_nil
end

it "sets default_interface" do
expect(plugin['network']['default_interface']).to eq('eth3')
end

it "doesn't set ipaddress" do
expect(plugin['ipaddress']).to be_nil
end
end

describe "with a link level default route with a source" do
let(:linux_ip_route) {
'default dev fwdintf scope link src 2.2.2.2
'
}

before(:each) do
plugin.run
end

it "completes the run" do
expect(Ohai::Log).not_to receive(:debug).with(/Plugin linux::network threw exception/)
expect(plugin['network']).not_to be_nil
end

it "sets default_interface" do
expect(plugin['network']['default_interface']).to eq('fwdintf')
end

it "sets ipaddress" do
expect(plugin['ipaddress']).to eq('2.2.2.2')
end
end

describe "when not having a global scope ipv6 address" do
let(:linux_ip_route_inet6) {
'fe80::/64 dev eth0 proto kernel metric 256
Expand Down

0 comments on commit 6daaca8

Please sign in to comment.