# frozen_string_literal: true module Facter module Resolvers class NetworkingLinux < BaseResolver @semaphore = Mutex.new class << self private def post_resolve(fact_name) retrieve_network_info(fact_name) if @fact_list.nil? @fact_list[fact_name] end def retrieve_network_info(fact_name) @fact_list ||= {} retrieve_default_interface_and_ip retrieve_interface_info retrieve_macaddress @fact_list[fact_name] end def retrieve_macaddress output, _status = Open3.capture2("ip link show #{@fact_list[:primary_interface]}") macaddress = */ether ([^ ]*) /.match(output) @fact_list[:macaddress] = macaddress[1] end def retrieve_interface_info output, _status = Open3.capture2('ip -o address') interfaces = {} output.each_line do |ip_line| ip_tokens = ip_line.split(' ') fill_ip_v4_info!(ip_tokens, interfaces) fill_io_v6_info!(ip_tokens, interfaces) end @fact_list[:interfaces] = interfaces end def fill_ip_v4_info!(ip_tokens, network_info) return unless ip_tokens[2].casecmp('inet').zero? interface_name = ip_tokens[1] ip4_info = ip_tokens[3].split('/') ip4_address = ip4_info[0] ip4_mask_length = ip4_info[1] binding = build_binding(ip4_address, ip4_mask_length) build_network_info_structure!(network_info, interface_name, 'bindings') network_info[interface_name]['bindings'] << binding end def fill_io_v6_info!(ip_tokens, network_info) return unless ip_tokens[2].casecmp('inet6').zero? interface_name = ip_tokens[1] ip6_info = ip_tokens[3].split('/') ip6_address = ip6_info[0] ip6_mask_length = ip6_info[1] binding = build_binding(ip6_address, ip6_mask_length) build_network_info_structure!(network_info, interface_name, 'bindings6') network_info[interface_name]['bindings6'] << binding end def retrieve_default_interface_and_ip output, _status = Open3.capture2('ip route get 1') ip_route_tokens = output.each_line.first.strip.split(' ') default_interface = ip_route_tokens[4] default_ip = ip_route_tokens[6] @fact_list[:ip] = default_ip @fact_list[:primary_interface] = default_interface end def build_binding(addr, mask_length) require 'ipaddr' ip = IPAddr.new(addr) mask_helper = ip.ipv6? ? 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' : '255.255.255.255' mask = IPAddr.new(mask_helper).mask(mask_length) { address: addr, netmask: mask.to_s, network: ip.mask(mask_length).to_s } end def build_network_info_structure!(network_info, interface_name, binding) network_info[interface_name] = {} unless network_info.dig(interface_name) network_info[interface_name][binding] = [] unless network_info.dig(interface_name, binding) end end end end end