dns, WIP autoconfiguration, context...

dev
Philippe PITTOLI 2019-10-13 01:21:40 +02:00
parent 3b6073a949
commit 90e1aaa20d
1 changed files with 108 additions and 63 deletions

View File

@ -3,51 +3,57 @@ require "option_parser"
require "ipaddress" require "ipaddress"
require "./colors" require "./colors"
simulation = false class Context
file = nil class_property root : String = "/"
class_property simulation = false
class_property verbosity = 1
prefered_network_configuration_program = nil class_property prefered_network_configuration_program : String? = nil
prefered_wireless_configuration_program = nil class_property prefered_wireless_configuration_program : String? = nil
prefered_dhcp_client = nil class_property prefered_dhcp_client : String? = nil
root = "/" class_property root = "/"
print_autodetect = false class_property print_autodetect = false
command = "list" class_property command = "list"
args = Array(String).new class_property args = Array(String).new
end
file_option : String? = nil
OptionParser.parse! do |parser| OptionParser.parse! do |parser|
parser.on "-s", "--simulation", "Export the network configuration." do parser.on "-s", "--simulation", "Export the network configuration." do
simulation = true Context.simulation = true
end end
parser.on "-a", "--print-autodetect", "Print autodetection of the installed programs." do parser.on "-a", "--print-autodetect", "Print autodetection of the installed programs." do
print_autodetect = true Context.print_autodetect = true
end end
parser.on "-w wireless-configuration-program", "--wireless wireless-configuration-program", "iw" do |prog| parser.on "-w wireless-configuration-program", "--wireless wireless-configuration-program", "iw" do |prog|
prefered_wireless_configuration_program = prog Context.prefered_wireless_configuration_program = prog
end end
parser.on "-n network-configuration-program", "--net-conf network-configuration-program", "ifconfig | ip" do |prog| parser.on "-n network-configuration-program", "--net-conf network-configuration-program", "ifconfig | ip" do |prog|
prefered_network_configuration_program = prog Context.prefered_network_configuration_program = prog
end end
parser.on "-d dhcp-client-program", "--dhcp-client dhcp-client-program", "udhcpc | dhclient" do |prog| parser.on "-d dhcp-client-program", "--dhcp-client dhcp-client-program", "udhcpc | dhclient" do |prog|
prefered_dhcp_client = prog Context.prefered_dhcp_client = prog
end end
parser.on "-r root", "--root root", "Root where to search for <root>/etc/hostname.* files." do |optsn| parser.on "-r root", "--root root", "Root where to search for <root>/etc/hostname.* files." do |optsn|
root = optsn Context.root = optsn
end end
parser.on "-f file", "--file file", "Parse a configuration file." do |optsn| parser.on "-f file", "--file file", "Parse a configuration file." do |optsn|
file = optsn file_option = optsn
end end
# 0: nothing is printed, 1: only events, 2: events and messages # 0: nothing is printed, 1: only events, 2: events and messages
parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0-2). Default: 1" do |optsn| parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0-2). Default: 1" do |optsn|
verbosity = optsn.to_i Context.verbosity = optsn.to_i
end end
parser.missing_option do |opt| parser.missing_option do |opt|
@ -62,16 +68,16 @@ OptionParser.parse! do |parser|
end end
parser.unknown_args do |arg| parser.unknown_args do |arg|
command = arg.shift Context.command = arg.shift
args = arg Context.args = arg
case command case Context.command
when /^(list)/ when /^(list)/
when /^(up)/ when /^(up)/
when /^(down)/ when /^(down)/
when /^(scan)/ when /^(scan)/
else else
STDERR.puts "Command #{command} not understood" STDERR.puts "Command #{Context.command} not understood"
exit 1 exit 1
end end
@ -115,6 +121,12 @@ class NotSetup
end end
end end
class Autoconfiguration
def to_s(io : IO)
io << "autoconfiguration"
end
end
class DHCP class DHCP
def to_s(io : IO) def to_s(io : IO)
io << "dhcp" io << "dhcp"
@ -127,6 +139,21 @@ class NetworkCommands
class_property cmd_wireless_configuration : IfconfigCommand.class | IWCommand.class | NotSetup.class = NotSetup class_property cmd_wireless_configuration : IfconfigCommand.class | IWCommand.class | NotSetup.class = NotSetup
class_property cmd_dhcp_client : UDHCPCCommand.class | DHClientCommand.class | NotSetup.class = NotSetup class_property cmd_dhcp_client : UDHCPCCommand.class | DHClientCommand.class | NotSetup.class = NotSetup
class DNS
def initialize(@addresses : Array(String), @search : Array(String))
end
def execute
File.open("#{Context.root}/etc/resolv.conf", "w") do |file|
@addresses.each do |address|
file.puts "nameserver #{address}\n"
end
file.puts "search #{@search.join(" ")}"
end
end
end
class IWCommand class IWCommand
# get the available SSID # get the available SSID
def self.scan(ifname : String) : Array(String) def self.scan(ifname : String) : Array(String)
@ -338,6 +365,10 @@ class NetworkCommands
end end
end end
def self.autoconfiguration(ifname : String)
puts "TODO: IPv6 autoconfiguration setup"
end
def self.wireless_connect_wpa_psk(ifname : String, ssid : String, passwd : String) def self.wireless_connect_wpa_psk(ifname : String, ssid : String, passwd : String)
cmd = @@cmd_wireless_configuration cmd = @@cmd_wireless_configuration
case cmd case cmd
@ -358,7 +389,7 @@ class WirelessAPSetup
property description : String? property description : String?
property mtu : Int32? property mtu : Int32?
property main_ip_v4 : IPAddress | DHCP | NotSetup property main_ip_v4 : IPAddress | DHCP | NotSetup
property main_ip_v6 : IPAddress | DHCP | NotSetup property main_ip_v6 : IPAddress | DHCP | Autoconfiguration | NotSetup
property aliasses_v4 : Array(IPAddress) property aliasses_v4 : Array(IPAddress)
property aliasses_v6 : Array(IPAddress) property aliasses_v6 : Array(IPAddress)
property dns : Array(IPAddress) property dns : Array(IPAddress)
@ -397,29 +428,23 @@ class WirelessAPSetup
# ipv4 # ipv4
unless main_ip_v4.is_a?(NotSetup) unless main_ip_v4.is_a?(NotSetup)
str << "\t\tinet #{main_ip_v4}\n" str << "\t\tinet #{main_ip_v4}\n"
end
aliasses_v4.each do |a| aliasses_v4.each do |a|
str << "\t\talias #{a}\n" str << "\t\talias #{a}\n"
end
end end
# ipv6 # ipv6
unless main_ip_v6.is_a?(NotSetup) unless main_ip_v6.is_a?(NotSetup)
str << "\t\tinet6 #{main_ip_v6}\n" str << "\t\tinet6 #{main_ip_v6}\n"
end
unless @aliasses_v6.empty?
@aliasses_v6.each do |a| @aliasses_v6.each do |a|
str << "\t\talias6 #{a}\n" str << "\t\talias6 #{a}\n"
end end
end end
if dns.empty? dns.each do |ip|
str << "\t\tno dns configured\n" str << "\t\tdns: #{ip}\n"
else
dns.each do |ip|
str << "\t\tdns: #{ip}\n"
end
end end
# to improve readability # to improve readability
@ -441,7 +466,7 @@ class InterfaceConfiguration
property mtu : Int32? property mtu : Int32?
property wireless : Bool property wireless : Bool
property main_ip_v4 : IPAddress | DHCP | NotSetup property main_ip_v4 : IPAddress | DHCP | NotSetup
property main_ip_v6 : IPAddress | DHCP | NotSetup property main_ip_v6 : IPAddress | Autoconfiguration | DHCP | NotSetup
property aliasses_v4 : Array(IPAddress) property aliasses_v4 : Array(IPAddress)
property aliasses_v6 : Array(IPAddress) property aliasses_v6 : Array(IPAddress)
property wireless_networks : Hash(String, WirelessAPSetup) property wireless_networks : Hash(String, WirelessAPSetup)
@ -490,25 +515,33 @@ class InterfaceConfiguration
# ipv4 # ipv4
unless main_ip_v4.is_a?(NotSetup) unless main_ip_v4.is_a?(NotSetup)
str << "\tinet #{main_ip_v4}\n" str << "\tinet #{main_ip_v4}\n"
end
unless aliasses_v4.empty?
aliasses_v4.each do |a| aliasses_v4.each do |a|
str << "\talias #{a}\n" str << "\talias #{a}\n"
end end
end end
# warning: alias but no main ip address
if main_ip_v4.is_a?(NotSetup) && ! aliasses_v4.empty?
str << "\t#{CRED}alias configured but no main ipv4 configuration.#{CRESET}\n"
str << "\t#{CRED}Should main ipv4 be obtained from DHCP? Static configuration?#{CRESET}\n"
end
# ipv6 # ipv6
unless main_ip_v6.is_a?(NotSetup) unless main_ip_v6.is_a?(NotSetup)
str << "\tinet6 #{main_ip_v6}\n" str << "\tinet6 #{main_ip_v6}\n"
end
unless aliasses_v6.empty?
aliasses_v6.each do |a| aliasses_v6.each do |a|
str << "\talias6 #{a}\n" str << "\talias6 #{a}\n"
end end
end end
# warning: alias but no main ip address
if main_ip_v6.is_a?(NotSetup) && ! aliasses_v6.empty?
str << "\t#{CRED}alias6 configured but no main ipv6 configuration.#{CRESET}\n"
str << "\t#{CRED}Should main ipv6 be obtained from autoconfiguration? DHCP? Static configuration?#{CRESET}\n"
end
unless dns.empty? unless dns.empty?
dns.each do |ip| dns.each do |ip|
str << "\tdns: #{ip}\n" str << "\tdns: #{ip}\n"
@ -531,6 +564,17 @@ class InterfaceConfiguration
raise "The interface #{@name} doesn't exists, yet." raise "The interface #{@name} doesn't exists, yet."
end end
# TODO: treat differently wireless and non-wireless interfaces
if @wireless
puts "interface #{name} is wireless"
puts "TODO:"
puts "1. scan for SSID"
puts "2. select configured SSID then try to connect"
puts "3. connectivity check with the gateway"
else
puts "interface #{name} is not wireless"
end
if up if up
NetworkCommands.up name NetworkCommands.up name
else else
@ -569,19 +613,22 @@ class InterfaceConfiguration
# ipv6 configuration # ipv6 configuration
main_ip_v6.tap do |ip| main_ip_v6.tap do |ip|
puts "CONFIGURATION IPV6"
case ip case ip
when IPAddress when IPAddress
NetworkCommands.set_ip name, ip NetworkCommands.set_ip name, ip
# TODO # TODO
#when Autoconfiguration when Autoconfiguration
# NetworkCommands.autoconfiguration name puts "CONFIGURATION IPV6 : autoconfiguration !!!"
NetworkCommands.autoconfiguration name
#when DHCP #when DHCP
# NetworkCommands.dhcp6 name # NetworkCommands.dhcp6 name
when NotSetup when NotSetup
# do nothing # do nothing
puts "CONFIGURATION IPV6 : not setup !!!"
else else
raise "ipv4 configuration: neither static nor dynamic" raise "ipv6 configuration: neither static nor dynamic"
end end
# We wont setup aliasses unless there is an actual IP address # We wont setup aliasses unless there is an actual IP address
@ -661,7 +708,7 @@ class NetworkConfigurationParser
end end
when /^inet6 autoconf/ when /^inet6 autoconf/
# IP address is autoconfigured # IP address is autoconfigured
puts "TODO: IPv6 autoconfiguration" main_ip_v6 = Autoconfiguration.new
when /^inet6? .*/ when /^inet6? .*/
ipstr = /^inet6? ([a-f0-9:.\/]+)/.match(line).try &.[1] ipstr = /^inet6? ([a-f0-9:.\/]+)/.match(line).try &.[1]
@ -710,8 +757,9 @@ class NetworkConfigurationParser
next next
end end
# TODO
access_point = wireless_networks[ssid].not_nil! access_point = wireless_networks[ssid].not_nil!
access_point.main_ip_v6 = Autoconfiguration.new
puts "for SSID: #{ssid} ipv6 configuration = autoconf"
when /^network [^ \t]+ inet6? .*/ when /^network [^ \t]+ inet6? .*/
ssid = nil ssid = nil
@ -841,8 +889,8 @@ class Autodetect
end end
Do.simulation = simulation Do.simulation = Context.simulation
Autodetect.print_autodetect = print_autodetect Autodetect.print_autodetect = Context.print_autodetect
# #
# discover available configuration commands # discover available configuration commands
@ -867,17 +915,17 @@ possible_wireless_configuration_cmds = {
"ifconfig" => NetworkCommands::IfconfigCommand "ifconfig" => NetworkCommands::IfconfigCommand
} }
key = prefered_network_configuration_program key = Context.prefered_network_configuration_program
key = possible_network_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil? key = possible_network_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil?
# should crash if there is no network command installed # should crash if there is no network command installed
NetworkCommands.cmd_network_configuration = possible_network_configuration_cmds[key.not_nil!] NetworkCommands.cmd_network_configuration = possible_network_configuration_cmds[key.not_nil!]
key = prefered_dhcp_client key = Context.prefered_dhcp_client
key = possible_dhcp_clients.keys.find { |key| Autodetect.which(key) } if key.nil? key = possible_dhcp_clients.keys.find { |key| Autodetect.which(key) } if key.nil?
# should not crash if there is no # should not crash if there is no
NetworkCommands.cmd_dhcp_client = possible_dhcp_clients[key] unless key.nil? NetworkCommands.cmd_dhcp_client = possible_dhcp_clients[key] unless key.nil?
key = prefered_wireless_configuration_program key = Context.prefered_wireless_configuration_program
key = possible_wireless_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil? key = possible_wireless_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil?
# should crash if there is no wireless command installed # should crash if there is no wireless command installed
NetworkCommands.cmd_wireless_configuration = possible_wireless_configuration_cmds[key.not_nil!] NetworkCommands.cmd_wireless_configuration = possible_wireless_configuration_cmds[key.not_nil!]
@ -885,7 +933,7 @@ NetworkCommands.cmd_wireless_configuration = possible_wireless_configuration_cmd
files = Array(String).new files = Array(String).new
Dir.children("#{root}/etc/").each do |f| Dir.children("#{Context.root}/etc/").each do |f|
if /^hostname\./.match(f) if /^hostname\./.match(f)
files << f files << f
end end
@ -893,46 +941,43 @@ end
interface_files = Array(String).new interface_files = Array(String).new
if ! file.nil? if ! file_option.nil?
# file passed via the '-f' option # file passed via the '-f' option
# TODO: why having to force "not_nil!" ? Seems like a compiler bug # TODO: why having to force "not_nil!" ? Seems like a compiler bug
interface_files << file.not_nil! interface_files << file_option.not_nil!
elsif args.empty? elsif Context.args.empty?
# every configured interface # every configured interface
files.each do |f| files.each do |f|
interface_files << "#{root}/etc/#{f}" interface_files << "#{Context.root}/etc/#{f}"
end end
else else
# only interfaces in arguments # only interfaces in arguments
args.each do |interface| Context.args.each do |interface|
interface_files << "#{root}/etc/hostname.#{interface}" interface_files << "#{Context.root}/etc/hostname.#{interface}"
end end
end end
begin begin
case command case Context.command
when "list" when "list"
interface_files.each do |f| interface_files.each do |f|
puts NetworkConfigurationParser.parse_file(f.not_nil!) puts NetworkConfigurationParser.parse_file(f)
end end
when "up" when "up"
# TODO: why having to force "not_nil!" ? Seems like a compiler bug
interface_files.each do |f| interface_files.each do |f|
network_configuration = NetworkConfigurationParser.parse_file(f.not_nil!) network_configuration = NetworkConfigurationParser.parse_file(f)
network_configuration.execute network_configuration.execute
end end
when "down" when "down"
# TODO: why having to force "not_nil!" ? Seems like a compiler bug
interface_files.each do |f| interface_files.each do |f|
network_configuration = NetworkConfigurationParser.parse_file(f.not_nil!) network_configuration = NetworkConfigurationParser.parse_file(f)
network_configuration.down network_configuration.down
end end
when "scan" when "scan"
# TODO: why having to force "not_nil!" ? Seems like a compiler bug
interface_files.each do |f| interface_files.each do |f|
network_configuration = NetworkConfigurationParser.parse_file(f.not_nil!) network_configuration = NetworkConfigurationParser.parse_file(f)
network_configuration.scan network_configuration.scan
end end
end end