Skip to content

Commit a83e1f9

Browse files
committed
Fix ifconfig parse logic failing on some tunnels
Tunnel interfaces that have a line similar to "tunnel inet 192.0.2.1 --> 192.0.2.2" would fail the network resolver because IPs were extracted through two separate IP + mask patterns. However, in the example above, there is no such thing as a netmask. This fix merges the patterns and extracts the IPs and netmasks together so that no mismatches can occur. It might also fix some cases where only the netmask would be nil and therefore produce an invalid binding that could in turn resolve to the wrong (outer) IP of the tunnel instead of the inner IP.
1 parent 61a17ff commit a83e1f9

File tree

2 files changed

+34
-20
lines changed

2 files changed

+34
-20
lines changed

lib/facter/resolvers/networking.rb

+20-13
Original file line numberDiff line numberDiff line change
@@ -80,28 +80,35 @@ def extract_dhcp(interface_name, raw_data, parsed_interface_data)
8080
end
8181

8282
def extract_ip_data(raw_data, parsed_interface_data)
83-
ip = extract_values(raw_data, /inet (\S+)/)
84-
mask = extract_values(raw_data, /netmask (\S+)/).map { |val| val.hex.to_s(2).count('1') }
83+
inets = extract_values(raw_data, /inet (\S+).+netmask (\S+)/, :extract_ip4_data)
84+
bindings = create_bindings(inets)
85+
parsed_interface_data[:bindings] = bindings unless bindings.empty?
8586

86-
ip6 = extract_values(raw_data, /inet6 (\S+)/).map { |val| val.gsub(/%.+/, '') }
87-
mask6 = extract_values(raw_data, /prefixlen (\S+)/)
88-
89-
parsed_interface_data[:bindings] = create_bindings(ip, mask) unless ip.empty?
90-
parsed_interface_data[:bindings6] = create_bindings(ip6, mask6) unless ip6.empty?
87+
inets = extract_values(raw_data, /inet6 (\S+).+prefixlen (\S+)/, :extract_ip6_data)
88+
bindings = create_bindings(inets)
89+
parsed_interface_data[:bindings6] = bindings unless bindings.empty?
9190
end
9291

93-
def extract_values(data, regex)
92+
def extract_values(data, regex, ip_func)
9493
results = []
95-
data.scan(regex).flatten.each do |val|
96-
results << val
94+
data.scan(regex).flatten.each_slice(2) do |val|
95+
results << method(ip_func).call(val)
9796
end
9897
results
9998
end
10099

101-
def create_bindings(ips, masks)
100+
def extract_ip4_data(inet)
101+
[inet[0], inet[1].hex.to_s(2).count('1')]
102+
end
103+
104+
def extract_ip6_data(inet)
105+
[inet[0].gsub(/%.+/, ''), inet[1]]
106+
end
107+
108+
def create_bindings(inets)
102109
bindings = []
103-
ips.zip(masks).each do |ip, mask|
104-
bindings << Facter::Util::Resolvers::Networking.build_binding(ip, mask)
110+
inets.each do |inet|
111+
bindings << Facter::Util::Resolvers::Networking.build_binding(inet[0], inet[1])
105112
end
106113
bindings
107114
end

spec/facter/resolvers/networking_spec.rb

+14-7
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55

66
let(:log_spy) { instance_spy(Facter::Log) }
77

8-
before do
9-
pending 'contains failing tests to reproduce a bug'
10-
end
11-
128
describe '#resolve' do
139
before do
1410
networking.instance_variable_set(:@log, log_spy)
@@ -44,7 +40,7 @@
4440
end
4541

4642
it 'detects all interfaces' do
47-
expected = %w[lo0 gif0 stf0 en0 en0.1 en1 en2 bridge0 p2p0 awdl0 llw0 utun0 utun1 utun2 utun3 ib0 ib1]
43+
expected = %w[lo0 gif0 stf0 en0 en0.1 en1 en2 bridge0 p2p0 awdl0 llw0 utun0 utun1 utun2 utun3 utun4 utun5 ib0 ib1]
4844
expect(networking.resolve(:interfaces).keys).to match_array(expected)
4945
end
5046

@@ -142,6 +138,19 @@
142138
expect(networking.resolve(:interfaces)['utun3']).to include(expected)
143139
end
144140

141+
it 'checks interface utun4' do
142+
expected = { bindings: [{ address: '192.0.2.100', netmask: '255.255.255.255', network: '192.0.2.100' }] }
143+
expect(networking.resolve(:interfaces)['utun4']).to include(expected)
144+
end
145+
146+
it 'checks interface utun5' do
147+
expected = { bindings6: [
148+
{ address: '2001:db8::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
149+
network: '2001:db8::1', scope6: 'global' }
150+
] }
151+
expect(networking.resolve(:interfaces)['utun5']).to include(expected)
152+
end
153+
145154
it 'checks interface ib0 has the expected mac' do
146155
expected = { mac: '80:00:02:08:FA:81:00:00:00:00:00:00:00:00:00:00:00:00:00:00' }
147156
expect(networking.resolve(:interfaces)['ib0']).to include(expected)
@@ -160,7 +169,6 @@
160169

161170
it 'returns dhcp server ip as nil' do
162171
expect(networking.resolve(:dhcp)).to be(nil)
163-
raise
164172
end
165173
end
166174

@@ -169,7 +177,6 @@
169177

170178
it 'returns interfaces as nil' do
171179
expect(networking.resolve(:interfaces)).to be(nil)
172-
raise
173180
end
174181
end
175182
end

0 commit comments

Comments
 (0)