split in several files
This commit is contained in:
parent
00a630fd34
commit
2cf04c331a
14
src/autodetect_environment.cr
Normal file
14
src/autodetect_environment.cr
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
class Autodetect
|
||||||
|
class_property print_autodetect : Bool = false
|
||||||
|
|
||||||
|
def self.which(cmd : String)
|
||||||
|
if Process.run("which", [ cmd ]).success?
|
||||||
|
puts "#{cmd} installed" if print_autodetect
|
||||||
|
true
|
||||||
|
else
|
||||||
|
puts "#{cmd} not installed" if print_autodetect
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
170
src/cli.cr
Normal file
170
src/cli.cr
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
|
||||||
|
file_option : String? = nil
|
||||||
|
|
||||||
|
OptionParser.parse! do |parser|
|
||||||
|
parser.on "-s", "--simulation", "Export the network configuration." do
|
||||||
|
Context.simulation = true
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-a", "--print-autodetect", "Print autodetection of the installed programs." do
|
||||||
|
Context.print_autodetect = true
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-w wireless-configuration-program", "--wireless wireless-configuration-program", "iw" do |prog|
|
||||||
|
Context.prefered_wireless_configuration_program = prog
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-n network-configuration-program", "--net-conf network-configuration-program", "ifconfig | ip" do |prog|
|
||||||
|
Context.prefered_network_configuration_program = prog
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-d dhcp-client-program", "--dhcp-client dhcp-client-program", "udhcpc | dhclient" do |prog|
|
||||||
|
Context.prefered_dhcp_client = prog
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-r root", "--root root", "Root where to search for <root>/etc/hostname.* files." do |optsn|
|
||||||
|
Context.root = optsn
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-f file", "--file file", "Parse a configuration file." do |optsn|
|
||||||
|
file_option = optsn
|
||||||
|
end
|
||||||
|
|
||||||
|
# 0: nothing is printed, 1: only events, 2: events and messages
|
||||||
|
parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0-2). Default: 1" do |optsn|
|
||||||
|
Context.verbosity = optsn.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.missing_option do |opt|
|
||||||
|
STDERR.puts "You missed the argument for option #{opt}"
|
||||||
|
# TODO: explain the different arguments
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.invalid_option do |flag|
|
||||||
|
STDERR.puts "Error: #{flag} not a valid option"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.unknown_args do |arg|
|
||||||
|
Context.command = arg.shift
|
||||||
|
Context.args = arg
|
||||||
|
|
||||||
|
case Context.command
|
||||||
|
when /^(list)/
|
||||||
|
when /^(up)/
|
||||||
|
when /^(down)/
|
||||||
|
when /^(scan)/
|
||||||
|
else
|
||||||
|
STDERR.puts "Command #{Context.command} not understood"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# unless arg.empty?
|
||||||
|
# STDERR.puts "unknown arg: #{arg}"
|
||||||
|
# exit 1
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-h", "--help", "Show this help" do
|
||||||
|
puts parser
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Do.simulation = Context.simulation
|
||||||
|
Autodetect.print_autodetect = Context.print_autodetect
|
||||||
|
|
||||||
|
#
|
||||||
|
# discover available configuration commands
|
||||||
|
#
|
||||||
|
|
||||||
|
# ifconfig = *bsd and some linux
|
||||||
|
# ip = linux
|
||||||
|
possible_network_configuration_cmds = {
|
||||||
|
"ifconfig" => NetworkCommands::IfconfigCommand,
|
||||||
|
"ip" => NetworkCommands::IPCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
# udhcpc = busybox dhcp client
|
||||||
|
possible_dhcp_clients = {
|
||||||
|
"udhcpc" => NetworkCommands::UDHCPCCommand,
|
||||||
|
"dhclient" => NetworkCommands::DHClientCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
# iw = linux
|
||||||
|
possible_wireless_configuration_cmds = {
|
||||||
|
"iw" => NetworkCommands::IWCommand,
|
||||||
|
"ifconfig" => NetworkCommands::IfconfigCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
key = Context.prefered_network_configuration_program
|
||||||
|
key = possible_network_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil?
|
||||||
|
# should crash if there is no network command installed
|
||||||
|
NetworkCommands.cmd_network_configuration = possible_network_configuration_cmds[key.not_nil!]
|
||||||
|
|
||||||
|
key = Context.prefered_dhcp_client
|
||||||
|
key = possible_dhcp_clients.keys.find { |key| Autodetect.which(key) } if key.nil?
|
||||||
|
# should not crash if there is no
|
||||||
|
NetworkCommands.cmd_dhcp_client = possible_dhcp_clients[key] unless key.nil?
|
||||||
|
|
||||||
|
key = Context.prefered_wireless_configuration_program
|
||||||
|
key = possible_wireless_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil?
|
||||||
|
# should crash if there is no wireless command installed
|
||||||
|
NetworkCommands.cmd_wireless_configuration = possible_wireless_configuration_cmds[key.not_nil!]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
files = Array(String).new
|
||||||
|
Dir.children("#{Context.root}/etc/").each do |f|
|
||||||
|
if /^hostname\./.match(f)
|
||||||
|
files << f
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
interface_files = Array(String).new
|
||||||
|
|
||||||
|
if ! file_option.nil?
|
||||||
|
# file passed via the '-f' option
|
||||||
|
# TODO: why having to force "not_nil!" ? Seems like a compiler bug
|
||||||
|
interface_files << file_option.not_nil!
|
||||||
|
elsif Context.args.empty?
|
||||||
|
# every configured interface
|
||||||
|
files.each do |f|
|
||||||
|
interface_files << "#{Context.root}/etc/#{f}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# only interfaces in arguments
|
||||||
|
Context.args.each do |interface|
|
||||||
|
interface_files << "#{Context.root}/etc/hostname.#{interface}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
begin
|
||||||
|
case Context.command
|
||||||
|
when "list"
|
||||||
|
interface_files.each do |f|
|
||||||
|
puts NetworkConfigurationParser.parse_file(f)
|
||||||
|
end
|
||||||
|
when "up"
|
||||||
|
interface_files.each do |f|
|
||||||
|
network_configuration = NetworkConfigurationParser.parse_file(f)
|
||||||
|
network_configuration.execute
|
||||||
|
end
|
||||||
|
when "down"
|
||||||
|
interface_files.each do |f|
|
||||||
|
network_configuration = NetworkConfigurationParser.parse_file(f)
|
||||||
|
network_configuration.down
|
||||||
|
end
|
||||||
|
when "scan"
|
||||||
|
interface_files.each do |f|
|
||||||
|
network_configuration = NetworkConfigurationParser.parse_file(f)
|
||||||
|
network_configuration.scan
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue e
|
||||||
|
STDERR.puts "#{CRED}Exception: #{CRESET}#{e}"
|
||||||
|
end
|
292
src/configuration.cr
Normal file
292
src/configuration.cr
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
|
||||||
|
class NotSetup
|
||||||
|
def to_s(io : IO)
|
||||||
|
io << "not setup"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Autoconfiguration
|
||||||
|
def to_s(io : IO)
|
||||||
|
io << "autoconfiguration"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class DHCP
|
||||||
|
def to_s(io : IO)
|
||||||
|
io << "dhcp"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessAPSetup
|
||||||
|
property ssid : String
|
||||||
|
|
||||||
|
# This is a list of parameters that should be unique to each AP
|
||||||
|
property up : Bool
|
||||||
|
property description : String?
|
||||||
|
property mtu : Int32?
|
||||||
|
property main_ip_v4 : IPAddress | DHCP | NotSetup
|
||||||
|
property main_ip_v6 : IPAddress | DHCP | Autoconfiguration | NotSetup
|
||||||
|
property aliasses_v4 : Array(IPAddress)
|
||||||
|
property aliasses_v6 : Array(IPAddress)
|
||||||
|
property dns : Array(IPAddress)
|
||||||
|
|
||||||
|
# we currently only support WPA2-PSK wireless security mechanism
|
||||||
|
property security : WPA
|
||||||
|
|
||||||
|
class WPA
|
||||||
|
property key : String
|
||||||
|
def initialize(@key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(@ssid, @security)
|
||||||
|
@main_ip_v4 = NotSetup.new
|
||||||
|
@main_ip_v6 = NotSetup.new
|
||||||
|
@aliasses_v4 = Array(IPAddress).new
|
||||||
|
@aliasses_v6 = Array(IPAddress).new
|
||||||
|
@up = true
|
||||||
|
@dns = Array(IPAddress).new
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def to_s(io : IO)
|
||||||
|
io << to_string
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_string
|
||||||
|
String.build do |str|
|
||||||
|
str << "\t#{CBLUE}#{ssid}#{CRESET}\n"
|
||||||
|
|
||||||
|
str << "\t\t#description #{description.not_nil!}\n" unless description.nil?
|
||||||
|
str << "\t\t#{@up? "up" : "down"}\n"
|
||||||
|
str << "\t\tmtu #{mtu}\n" unless mtu.nil?
|
||||||
|
|
||||||
|
# ipv4
|
||||||
|
unless main_ip_v4.is_a?(NotSetup)
|
||||||
|
str << "\t\tinet #{main_ip_v4}\n"
|
||||||
|
|
||||||
|
aliasses_v4.each do |a|
|
||||||
|
str << "\t\talias #{a}\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ipv6
|
||||||
|
unless main_ip_v6.is_a?(NotSetup)
|
||||||
|
str << "\t\tinet6 #{main_ip_v6}\n"
|
||||||
|
|
||||||
|
@aliasses_v6.each do |a|
|
||||||
|
str << "\t\talias6 #{a}\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
dns.each do |ip|
|
||||||
|
str << "\t\tdns: #{ip}\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
# to improve readability
|
||||||
|
str << "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# interface configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
class InterfaceConfiguration
|
||||||
|
|
||||||
|
property name : String
|
||||||
|
property up : Bool
|
||||||
|
property description : String?
|
||||||
|
property mtu : Int32?
|
||||||
|
property wireless : Bool
|
||||||
|
property main_ip_v4 : IPAddress | DHCP | NotSetup
|
||||||
|
property main_ip_v6 : IPAddress | Autoconfiguration | DHCP | NotSetup
|
||||||
|
property aliasses_v4 : Array(IPAddress)
|
||||||
|
property aliasses_v6 : Array(IPAddress)
|
||||||
|
property wireless_networks : Hash(String, WirelessAPSetup)
|
||||||
|
property dns : Array(IPAddress)
|
||||||
|
|
||||||
|
def initialize (@name, @up,
|
||||||
|
@description,
|
||||||
|
@mtu,
|
||||||
|
@main_ip_v4, @main_ip_v6, aliasses,
|
||||||
|
@wireless, @wireless_networks,
|
||||||
|
@dns = Array(IPAddress).new
|
||||||
|
)
|
||||||
|
|
||||||
|
@aliasses_v4 = Array(IPAddress).new
|
||||||
|
@aliasses_v6 = Array(IPAddress).new
|
||||||
|
|
||||||
|
aliasses.each do |ip|
|
||||||
|
if ip.ipv4?
|
||||||
|
aliasses_v4 << ip
|
||||||
|
else
|
||||||
|
aliasses_v6 << ip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s(io : IO)
|
||||||
|
io << to_string
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_string
|
||||||
|
String.build do |str|
|
||||||
|
if NetworkCommands.interface_exists(@name)
|
||||||
|
str << "#{CGREEN}#{@name}#{CRESET}\n"
|
||||||
|
else
|
||||||
|
str << "#{CRED}#{@name}#{CRESET}\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
str << "\tdescription: '#{description.not_nil!}'\n" unless description.nil?
|
||||||
|
|
||||||
|
str << "\t#{@up? "up" : "down"}\n"
|
||||||
|
|
||||||
|
unless mtu.nil?
|
||||||
|
str << "\tmtu #{mtu}\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
# ipv4
|
||||||
|
unless main_ip_v4.is_a?(NotSetup)
|
||||||
|
str << "\tinet #{main_ip_v4}\n"
|
||||||
|
|
||||||
|
aliasses_v4.each do |a|
|
||||||
|
str << "\talias #{a}\n"
|
||||||
|
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
|
||||||
|
unless main_ip_v6.is_a?(NotSetup)
|
||||||
|
str << "\tinet6 #{main_ip_v6}\n"
|
||||||
|
|
||||||
|
aliasses_v6.each do |a|
|
||||||
|
str << "\talias6 #{a}\n"
|
||||||
|
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?
|
||||||
|
dns.each do |ip|
|
||||||
|
str << "\tdns: #{ip}\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless wireless_networks.empty?
|
||||||
|
# to improve readability
|
||||||
|
str << "\n"
|
||||||
|
wireless_networks.each do |k,v|
|
||||||
|
str << v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# configure the interface
|
||||||
|
def execute
|
||||||
|
unless NetworkCommands.interface_exists(@name)
|
||||||
|
raise "The interface #{@name} doesn't exists, yet."
|
||||||
|
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
|
||||||
|
NetworkCommands.up name
|
||||||
|
else
|
||||||
|
puts "not marked as 'up' -- ending here"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
unless mtu.nil?
|
||||||
|
NetworkCommands.mtu name, mtu
|
||||||
|
end
|
||||||
|
|
||||||
|
unless description.nil?
|
||||||
|
NetworkCommands.description name, description.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
# ipv4 configuration
|
||||||
|
main_ip_v4.tap do |ip|
|
||||||
|
case ip
|
||||||
|
when IPAddress
|
||||||
|
NetworkCommands.set_ip name, ip
|
||||||
|
when DHCP
|
||||||
|
NetworkCommands.dhcp name
|
||||||
|
when NotSetup
|
||||||
|
# do nothing
|
||||||
|
else
|
||||||
|
raise "ipv4 configuration: neither static nor dynamic"
|
||||||
|
end
|
||||||
|
|
||||||
|
# We wont setup aliasses unless there is an actual IP address
|
||||||
|
if ip != NotSetup
|
||||||
|
aliasses_v4.each do |ip_alias|
|
||||||
|
NetworkCommands.set_alias name, ip_alias
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ipv6 configuration
|
||||||
|
main_ip_v6.tap do |ip|
|
||||||
|
case ip
|
||||||
|
when IPAddress
|
||||||
|
NetworkCommands.set_ip name, ip
|
||||||
|
when Autoconfiguration
|
||||||
|
NetworkCommands.autoconfiguration name
|
||||||
|
when DHCP
|
||||||
|
NetworkCommands.dhcp6 name
|
||||||
|
when NotSetup
|
||||||
|
# do nothing
|
||||||
|
else
|
||||||
|
raise "ipv6 configuration: neither static nor dynamic"
|
||||||
|
end
|
||||||
|
|
||||||
|
# We wont setup aliasses unless there is an actual IP address
|
||||||
|
if ip != NotSetup
|
||||||
|
aliasses_v6.each do |ip_alias|
|
||||||
|
NetworkCommands.set_alias name, ip_alias
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
unless NetworkCommands.interface_exists(@name)
|
||||||
|
raise "The interface #{@name} doesn't exists or is already down."
|
||||||
|
end
|
||||||
|
|
||||||
|
NetworkCommands.flush name
|
||||||
|
NetworkCommands.down name
|
||||||
|
end
|
||||||
|
|
||||||
|
def scan
|
||||||
|
unless NetworkCommands.interface_exists(@name)
|
||||||
|
raise "The interface #{@name} doesn't exists or is already down."
|
||||||
|
end
|
||||||
|
|
||||||
|
ssid_list = NetworkCommands.scan name
|
||||||
|
pp! ssid_list
|
||||||
|
end
|
||||||
|
end
|
17
src/context.cr
Normal file
17
src/context.cr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
class Context
|
||||||
|
class_property root : String = "/"
|
||||||
|
|
||||||
|
class_property simulation = false
|
||||||
|
class_property verbosity = 1
|
||||||
|
|
||||||
|
class_property prefered_network_configuration_program : String? = nil
|
||||||
|
class_property prefered_wireless_configuration_program : String? = nil
|
||||||
|
class_property prefered_dhcp_client : String? = nil
|
||||||
|
|
||||||
|
class_property root = "/"
|
||||||
|
class_property print_autodetect = false
|
||||||
|
|
||||||
|
class_property command = "list"
|
||||||
|
class_property args = Array(String).new
|
||||||
|
end
|
22
src/do.cr
Normal file
22
src/do.cr
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
class Do < Process
|
||||||
|
class_property simulation = false
|
||||||
|
|
||||||
|
def self.run(cmd : String, params : Array(String) = nil)
|
||||||
|
if @@simulation
|
||||||
|
puts "simulation, do: #{cmd} #{params.join(" ")}"
|
||||||
|
Process::Status.new 0
|
||||||
|
else
|
||||||
|
Process.run cmd, params
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.run(cmd : String, params : Array(String) = nil, &block : Process -> _)
|
||||||
|
if @@simulation
|
||||||
|
puts "simulation, do: #{cmd} #{params.join(" ")}"
|
||||||
|
Process::Status.new 0
|
||||||
|
else
|
||||||
|
Process.run cmd, params, &block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
1001
src/main.cr
1001
src/main.cr
File diff suppressed because it is too large
Load Diff
280
src/network_commands.cr
Normal file
280
src/network_commands.cr
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
|
||||||
|
# TODO: OpenBSD: no '-w' parameter for sysctl
|
||||||
|
# TODO: OpenBSD: ifconfig scan
|
||||||
|
# TODO: Linux: description with ip-* command family
|
||||||
|
|
||||||
|
class NetworkCommands
|
||||||
|
class_property cmd_network_configuration : IfconfigCommand.class | IPCommand.class = IfconfigCommand
|
||||||
|
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 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
|
||||||
|
# get the available SSID
|
||||||
|
def self.scan(ifname : String) : Array(String)
|
||||||
|
ssids = Array(String).new
|
||||||
|
|
||||||
|
Do.run("iw", [ ifname, "scan" ]) do |p|
|
||||||
|
p.output.each_line do |line|
|
||||||
|
ssid = /SSID: (?<ssid>[a-zA-Z0-9_-]+)/.match(line).try &.["ssid"]
|
||||||
|
|
||||||
|
unless ssid.nil?
|
||||||
|
ssids << ssid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ssids.empty?
|
||||||
|
raise "(iw) cannot get ssid list from #{ifname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
ssids
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class UDHCPCCommand
|
||||||
|
def self.run(ifname : String)
|
||||||
|
unless Do.run("udhcpc", [ ifname ]).success?
|
||||||
|
raise "(udhcpc) dhcp failed on #{ifname}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.dhcp6(ifname : String)
|
||||||
|
# we suppose udhcpc6 installed along with udhcpc
|
||||||
|
unless Do.run("udhcpc6", [ ifname ]).success?
|
||||||
|
raise "(udhcpc6) dhcp failed on #{ifname}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class DHClientCommand
|
||||||
|
def self.run(ifname : String)
|
||||||
|
unless Do.run("dhclient", [ ifname ]).success?
|
||||||
|
raise "(dhclient) dhcp failed on #{ifname}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.dhcp6(ifname : String)
|
||||||
|
unless Do.run("dhclient", [ "-6", ifname ]).success?
|
||||||
|
raise "(dhclient) dhcp6 failed on #{ifname}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class IfconfigCommand
|
||||||
|
def self.interface_exists(name : String)
|
||||||
|
Do.run("ifconfig", [ name ]).success?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.up_or_down(name : String, updown : String)
|
||||||
|
unless Do.run("ifconfig", [ name, updown ]).success?
|
||||||
|
raise "(ifconfig) Cannot set #{updown} link name #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def self.up(name : String)
|
||||||
|
self.up_or_down name, "up"
|
||||||
|
end
|
||||||
|
def self.down(name : String)
|
||||||
|
self.up_or_down name, "down"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_ip(name : String, ip : IPAddress)
|
||||||
|
unless Do.run("ifconfig", [ name, ip.to_string ]).success?
|
||||||
|
raise "(ifconfig) Cannot set ip address #{ip.to_string} for #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# currently, aliasses with ifconfig: ifconfig add ip/mask
|
||||||
|
# same command as the ip setup
|
||||||
|
def self.set_alias(name : String, ip : IPAddress)
|
||||||
|
unless Do.run("ifconfig", [ name, "add", ip.to_s ]).success?
|
||||||
|
raise "(ifconfig) Cannot set ip address alias #{ip.to_s} for #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mtu(name : String, mtu : Int32?)
|
||||||
|
unless Do.run("ifconfig", [ name, "mtu", mtu.to_s ]).success?
|
||||||
|
raise "(ifconfig) Cannot set mtu #{mtu} for #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.description(name : String, description : String)
|
||||||
|
unless Do.run("ifconfig", [ name, "description", "\"#{description}\"" ]).success?
|
||||||
|
raise "(ifconfig) Cannot set description #{description} for #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.flush(name : String)
|
||||||
|
unless Do.run("ifconfig", [ name, "0.0.0.0" ]).success?
|
||||||
|
raise "(ifconfig) Cannot flush #{name} ip addresses"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down(name : String)
|
||||||
|
unless Do.run("ifconfig", [ name, "down" ]).success?
|
||||||
|
raise "(ifconfig) Cannot set down #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ifconfig also performs wireless configuration on some OSs
|
||||||
|
def self.scan(name : String)
|
||||||
|
puts "TODO: (ifconfig) ifconfig ifname scan | grep SSID"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class IPCommand
|
||||||
|
def self.interface_exists(name : String)
|
||||||
|
Do.run("ip", [ "link", "show", "dev", name ]).success?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.up_or_down(name : String, updown : String)
|
||||||
|
unless Do.run("ip", [ "link", "set", updown, "dev", name ]).success?
|
||||||
|
raise "(ip) Cannot set #{updown} link name #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def self.up(name : String)
|
||||||
|
self.up_or_down name, "up"
|
||||||
|
end
|
||||||
|
def self.down(name : String)
|
||||||
|
self.up_or_down name, "down"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_ip(name : String, ip : IPAddress)
|
||||||
|
unless Do.run("ip", [ "address", "add", ip.to_string, "dev", name ]).success?
|
||||||
|
raise "(ip) Cannot add ip address #{ip.to_string} to #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_alias(name : String, ip : IPAddress)
|
||||||
|
unless Do.run("ip", [ "address", "add", ip.to_string, "dev", name ]).success?
|
||||||
|
raise "(ip) Cannot add ip address alias #{ip.to_string} to #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mtu(name : String, mtu : Int32?)
|
||||||
|
unless Do.run("ip", [ "link", "set", "mtu", mtu.to_s, "dev", name ]).success?
|
||||||
|
raise "(ip) Cannot set mtu #{mtu} to #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.flush(name : String)
|
||||||
|
unless Do.run("ip", [ "address", "flush", "dev", name ]).success?
|
||||||
|
raise "(ip) Cannot flush #{name} ip addresses"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down(name : String)
|
||||||
|
unless Do.run("ip", [ "link", "set", "down", "dev", name ]).success?
|
||||||
|
raise "(ip) Cannot set down #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.description(name : String, description : String)
|
||||||
|
puts "TODO: (ip) setup description '#{description}' to interface #{name}"
|
||||||
|
# unless Do.run("ip", [ "link", "set", "description", description, "dev", name ]).success?
|
||||||
|
# raise "(ip) Cannot set description #{description} to #{name}"
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.interface_exists(name : String)
|
||||||
|
@@cmd_network_configuration.interface_exists(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.up(ifname : String)
|
||||||
|
@@cmd_network_configuration.up(ifname)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down(ifname : String)
|
||||||
|
@@cmd_network_configuration.up(ifname)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_ip(name : String, ip : IPAddress)
|
||||||
|
@@cmd_network_configuration.set_ip name, ip
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.dhcp(name : String)
|
||||||
|
cmd = @@cmd_dhcp_client
|
||||||
|
case cmd
|
||||||
|
when NotSetup.class
|
||||||
|
raise "no dhcp client: cannot perform dhcp on #{name}"
|
||||||
|
else
|
||||||
|
cmd.run name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_alias(name : String, ip : IPAddress)
|
||||||
|
@@cmd_network_configuration.set_alias name, ip
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mtu(name : String, mtu : Int32?)
|
||||||
|
@@cmd_network_configuration.mtu name, mtu
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.description(name : String, description : String)
|
||||||
|
@@cmd_network_configuration.description name, description
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.flush(name : String)
|
||||||
|
@@cmd_network_configuration.flush name
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down(name : String)
|
||||||
|
@@cmd_network_configuration.down name
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.scan(ifname : String)
|
||||||
|
cmd = @@cmd_wireless_configuration
|
||||||
|
case cmd
|
||||||
|
when NotSetup.class
|
||||||
|
raise "no wireless configuration program: cannot list ssid"
|
||||||
|
else
|
||||||
|
cmd.scan ifname
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.autoconfiguration(ifname : String)
|
||||||
|
# TODO: no '-w' option on openbsd
|
||||||
|
unless Do.run("sysctl", [ "-w", "net.ipv6.conf.#{ifname}.autoconf=1" ]).success?
|
||||||
|
raise "(sysctl) cannot set 'net.ipv6.conf.#{ifname}.autoconf=1'"
|
||||||
|
end
|
||||||
|
|
||||||
|
unless Do.run("sysctl", [ "-w", "net.ipv6.conf.#{ifname}.accept_ra=1" ]).success?
|
||||||
|
raise "(sysctl) cannot set 'net.ipv6.conf.#{ifname}.accept_ra=1'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.dhcp6(ifname : String)
|
||||||
|
cmd = @@cmd_dhcp_client
|
||||||
|
case cmd
|
||||||
|
when NotSetup.class
|
||||||
|
raise "no dhcpv6 program"
|
||||||
|
else
|
||||||
|
cmd.dhcp6 ifname
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.wireless_connect_wpa_psk(ifname : String, ssid : String, passwd : String)
|
||||||
|
cmd = @@cmd_wireless_configuration
|
||||||
|
case cmd
|
||||||
|
when NotSetup.class
|
||||||
|
puts "no wireless configuration program: cannot connect to ssid #{ssid}"
|
||||||
|
else
|
||||||
|
cmd.list_ssid ifname
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
210
src/network_configuration_parser.cr
Normal file
210
src/network_configuration_parser.cr
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
|
||||||
|
class NetworkConfigurationParser
|
||||||
|
def self.parse_file(file_name : String) : InterfaceConfiguration
|
||||||
|
content = File.read(file_name)
|
||||||
|
content = content.rchop
|
||||||
|
ifname = /.([a-zA-Z0-9]+)$/.match(file_name).try &.[1]
|
||||||
|
if ifname.nil?
|
||||||
|
raise "The interface name is not known from the filename: '#{file_name}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
wireless = false
|
||||||
|
wireless = true unless /^wl[0-9]+$/.match(ifname)
|
||||||
|
self.parse(ifname.not_nil!, content, wireless)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse (ifname : String, data : String, wireless = false) : InterfaceConfiguration
|
||||||
|
up = false
|
||||||
|
description = nil
|
||||||
|
mtu = nil
|
||||||
|
main_ip_v4 = NotSetup.new
|
||||||
|
main_ip_v6 = NotSetup.new
|
||||||
|
|
||||||
|
dns = Array(IPAddress).new
|
||||||
|
|
||||||
|
aliasses = [] of IPAddress
|
||||||
|
|
||||||
|
wireless_networks = {} of String => WirelessAPSetup
|
||||||
|
|
||||||
|
data.split("\n").each do |line|
|
||||||
|
case line
|
||||||
|
when /^up/
|
||||||
|
up = true
|
||||||
|
when /^description/
|
||||||
|
description = /^description (.+)/.match(line).try &.[1]
|
||||||
|
when /^inet6? alias .*/
|
||||||
|
ipstr = /^inet6? alias ([a-f0-9:.\/]+)/.match(line).try &.[1]
|
||||||
|
if ipstr.nil?
|
||||||
|
puts "wrong IP address alias, line #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
aliasses.push IPAddress.parse(ipstr)
|
||||||
|
when /^inet6? dhcp/
|
||||||
|
# IP address is DHCP
|
||||||
|
if /^inet /.match(line)
|
||||||
|
main_ip_v4 = DHCP.new
|
||||||
|
else
|
||||||
|
main_ip_v6 = DHCP.new
|
||||||
|
end
|
||||||
|
when /^inet6 autoconf/
|
||||||
|
# IP address is autoconfigured
|
||||||
|
main_ip_v6 = Autoconfiguration.new
|
||||||
|
|
||||||
|
when /^inet6? .*/
|
||||||
|
ipstr = /^inet6? ([a-f0-9:.\/]+)/.match(line).try &.[1]
|
||||||
|
if ipstr.nil?
|
||||||
|
puts "wrong IP address, line #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
if /^inet /.match(line)
|
||||||
|
main_ip_v4 = IPAddress.parse ipstr
|
||||||
|
else
|
||||||
|
main_ip_v6 = IPAddress.parse ipstr
|
||||||
|
end
|
||||||
|
when /^join [^ \t]+ wpakey .*/
|
||||||
|
# WPA2-PSK, other security mechanisms are not supported, yet
|
||||||
|
ssid = /^join ([^ \t]+)/.match(line).try &.[1]
|
||||||
|
wpakeystr = /^join [^ \t]+ wpakey ([^ \t]+)/.match(line).try &.[1]
|
||||||
|
|
||||||
|
if ssid.nil?
|
||||||
|
puts "wrong SSID in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if wpakeystr.nil?
|
||||||
|
puts "wrong wpa key in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
new_ap = WirelessAPSetup.new ssid, WirelessAPSetup::WPA.new(wpakeystr)
|
||||||
|
wireless_networks[ssid] = new_ap
|
||||||
|
|
||||||
|
|
||||||
|
when /^network [^ \t]+ inet6 autoconf/
|
||||||
|
puts "TODO: network SSID inet6 autoconf"
|
||||||
|
|
||||||
|
ssid = /^network ([^ \t]+)/.match(line).try &.[1]
|
||||||
|
ipstr = /^network [^ \t]+ inet6? ([^ \t]+)/.match(line).try &.[1]
|
||||||
|
|
||||||
|
if ssid.nil?
|
||||||
|
puts "wrong SSID in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if ipstr.nil?
|
||||||
|
puts "wrong ip address in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
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? .*/
|
||||||
|
ssid = nil
|
||||||
|
ipstr = nil
|
||||||
|
|
||||||
|
/^network (?<ssid>[^ \t]+) inet6? (?<ip>[^ \t]+)/.match(line).try do |m|
|
||||||
|
ssid = m["ssid"]
|
||||||
|
ipstr = m["ip"]
|
||||||
|
end
|
||||||
|
|
||||||
|
if ssid.nil?
|
||||||
|
puts "wrong SSID in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if ipstr.nil?
|
||||||
|
puts "wrong ip address in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
ipaddr = IPAddress.parse ipstr
|
||||||
|
access_point = wireless_networks[ssid].not_nil!
|
||||||
|
|
||||||
|
if ipaddr.ipv4?
|
||||||
|
access_point.main_ip_v4 = ipaddr
|
||||||
|
elsif ipaddr.ipv6?
|
||||||
|
access_point.main_ip_v6 = ipaddr
|
||||||
|
else
|
||||||
|
puts "wrong ip address in line: #{line} (neither ipv4 or ipv6)"
|
||||||
|
end
|
||||||
|
|
||||||
|
when /^network [^ ]+ dhcp6?/
|
||||||
|
ssid = /^network (?<ssid>[^ \t]+)/.match(line).try &.["ssid"]
|
||||||
|
|
||||||
|
if ssid.nil?
|
||||||
|
puts "wrong SSID in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
access_point = wireless_networks[ssid].not_nil!
|
||||||
|
|
||||||
|
if /dhcp6/.match(line)
|
||||||
|
access_point.main_ip_v6 = DHCP.new
|
||||||
|
elsif /dhcp/.match(line)
|
||||||
|
access_point.main_ip_v4 = DHCP.new
|
||||||
|
else
|
||||||
|
puts "wrong dhcp instruction in line: #{line}"
|
||||||
|
end
|
||||||
|
|
||||||
|
when /^network [^ ]+ dns .*/
|
||||||
|
ssid = nil
|
||||||
|
ipstr = nil
|
||||||
|
|
||||||
|
/^network (?<ssid>[^ \t]+) dns (?<ip>[^ \t]+)/.match(line).try do |m|
|
||||||
|
ssid = m["ssid"]
|
||||||
|
ipstr = m["ip"]
|
||||||
|
end
|
||||||
|
|
||||||
|
if ssid.nil?
|
||||||
|
puts "wrong SSID in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if ipstr.nil?
|
||||||
|
puts "wrong ip address in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
access_point = wireless_networks[ssid].not_nil!
|
||||||
|
ipaddr = IPAddress.parse ipstr
|
||||||
|
access_point.dns << ipaddr
|
||||||
|
|
||||||
|
when /^mtu [0-9]+/
|
||||||
|
mtu = /^mtu ([0-9]+)/.match(line).try &.[1].to_i
|
||||||
|
|
||||||
|
when /^dns [^ \t]+/
|
||||||
|
ipstr = nil
|
||||||
|
|
||||||
|
/^dns (?<ip>[^ \t]+)/.match(line).try do |m|
|
||||||
|
ipstr = m["ip"]
|
||||||
|
end
|
||||||
|
|
||||||
|
if ipstr.nil?
|
||||||
|
puts "wrong ip address in line: #{line}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
ipaddr = IPAddress.parse ipstr
|
||||||
|
dns << ipaddr
|
||||||
|
|
||||||
|
when /^#.*$/
|
||||||
|
# simple comment
|
||||||
|
when /^[ \t]*$/
|
||||||
|
# empty line
|
||||||
|
else
|
||||||
|
raise "Cannot parse: #{line}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
InterfaceConfiguration.new(ifname, up,
|
||||||
|
description,
|
||||||
|
mtu,
|
||||||
|
main_ip_v4, main_ip_v6,
|
||||||
|
aliasses,
|
||||||
|
wireless, wireless_networks,
|
||||||
|
dns)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user