|
|
|
@ -0,0 +1,328 @@ |
|
|
|
|
|
|
|
|
|
require "option_parser" |
|
|
|
|
require "ipaddress" |
|
|
|
|
require "./colors" |
|
|
|
|
|
|
|
|
|
simulation = false |
|
|
|
|
file = nil |
|
|
|
|
|
|
|
|
|
OptionParser.parse! do |parser| |
|
|
|
|
parser.on "-s", "--simulation", "Export the network configuration." do |
|
|
|
|
simulation = true |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
parser.on "-f file", "--file file", "Parse a configuration file." do |optsn| |
|
|
|
|
file = 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| |
|
|
|
|
verbosity = optsn.to_i |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
parser.on "-h", "--help", "Show this help" do |
|
|
|
|
puts parser |
|
|
|
|
exit 0 |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NetworkCommands |
|
|
|
|
class DHCPCommands |
|
|
|
|
def self.dhcp(ifname : String) |
|
|
|
|
# TODO: verify which dhcp client is installed on the system |
|
|
|
|
cmd = "udhcpc" |
|
|
|
|
unless Do.run(cmd, [ name ]).success? |
|
|
|
|
raise "(#{cmd}) dhcp failed on #{ifname}" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.which(cmd : String) |
|
|
|
|
Do.run("which", [ cmd ]).success? |
|
|
|
|
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) |
|
|
|
|
puts "(ip) setup static IP address" |
|
|
|
|
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) |
|
|
|
|
puts "(ip) setup static IP address" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def self.choose_command : IfconfigCommand.class | IPCommand.class |
|
|
|
|
if self.which("ifconfig") |
|
|
|
|
IfconfigCommand |
|
|
|
|
elsif self.which("ip") |
|
|
|
|
IPCommand |
|
|
|
|
else |
|
|
|
|
raise "Neither ifconfig or ip commands exists on this system" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.interface_exists?(name : String) |
|
|
|
|
cmd = self.choose_command |
|
|
|
|
cmd.interface_exists?(name) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.up(ifname : String) |
|
|
|
|
cmd = self.choose_command |
|
|
|
|
cmd.up(ifname) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.down(ifname : String) |
|
|
|
|
cmd = self.choose_command |
|
|
|
|
cmd.up(ifname) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.set_ip(name : String, ip : IPAddress) |
|
|
|
|
puts "(ip) setup static IP address" |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.dhcp(name : String) |
|
|
|
|
puts "(ip) setup dynamic IP address" |
|
|
|
|
DHCPCommands.dhcp name |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# |
|
|
|
|
# interface configuration |
|
|
|
|
# |
|
|
|
|
|
|
|
|
|
class InterfaceConfiguration |
|
|
|
|
class NotSetup |
|
|
|
|
def to_s(io : IO) |
|
|
|
|
io << "not setup" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
class DHCP |
|
|
|
|
def to_s(io : IO) |
|
|
|
|
io << "dhcp" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
property name : String |
|
|
|
|
property up : Bool |
|
|
|
|
property main_ip_v4 : IPAddress | DHCP | NotSetup |
|
|
|
|
property main_ip_v6 : IPAddress | DHCP | NotSetup |
|
|
|
|
property aliasses_v4 : Array(IPAddress) |
|
|
|
|
property aliasses_v6 : Array(IPAddress) |
|
|
|
|
|
|
|
|
|
def initialize (@name, @up, @main_ip_v4, @main_ip_v6, aliasses) |
|
|
|
|
@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 << "\t#{@up? "up" : "down"}\n" |
|
|
|
|
str << "\tinet #{@main_ip_v4}\n" |
|
|
|
|
|
|
|
|
|
unless @aliasses_v4.empty? |
|
|
|
|
@aliasses_v4.each do |a| |
|
|
|
|
str << "\talias #{a}\n" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
str << "\tinet6 #{@main_ip_v6}\n" |
|
|
|
|
|
|
|
|
|
unless @aliasses_v6.empty? |
|
|
|
|
@aliasses_v6.each do |a| |
|
|
|
|
str << "\talias6 #{a}\n" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# configure the interface |
|
|
|
|
def execute |
|
|
|
|
unless NetworkCommands.interface_exists?(@name) |
|
|
|
|
raise "The interface #{@name} doesn't exists, yet." |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
puts "OK on va configurer ça" |
|
|
|
|
puts "#{self}" |
|
|
|
|
|
|
|
|
|
if @up |
|
|
|
|
NetworkCommands.up @name |
|
|
|
|
else |
|
|
|
|
puts "not marked as 'up' -- ending here" |
|
|
|
|
return |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case main_ip_v4 = @main_ip_v4 |
|
|
|
|
when IPAddress |
|
|
|
|
NetworkCommands.set_ip @name, main_ip_v4 |
|
|
|
|
when DHCP |
|
|
|
|
NetworkCommands.dhcp @name |
|
|
|
|
when NotSetup |
|
|
|
|
puts "no ipv4" |
|
|
|
|
else |
|
|
|
|
raise "ipv4 configuration: neither static nor dynamic" |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
case main_ip_v6 = @main_ip_v6 |
|
|
|
|
when IPAddress |
|
|
|
|
NetworkCommands.set_ip @name, main_ip_v6 |
|
|
|
|
# TODO |
|
|
|
|
#when Autoconfiguration |
|
|
|
|
# NetworkCommands.autoconfiguration @name |
|
|
|
|
#when DHCP |
|
|
|
|
# NetworkCommands.dhcp6 @name |
|
|
|
|
when NotSetup |
|
|
|
|
puts "no ipv4" |
|
|
|
|
else |
|
|
|
|
raise "ipv4 configuration: neither static nor dynamic" |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# str << "\tinet #{@main_ip_v4}\n" |
|
|
|
|
|
|
|
|
|
# str << "\t#{@up? "up" : "down"}\n" |
|
|
|
|
# str << "\tinet #{@main_ip_v4}\n" |
|
|
|
|
|
|
|
|
|
# unless @aliasses_v4.empty? |
|
|
|
|
# @aliasses_v4.each do |a| |
|
|
|
|
# str << "\talias #{a}\n" |
|
|
|
|
# end |
|
|
|
|
# end |
|
|
|
|
|
|
|
|
|
# str << "\tinet6 #{@main_ip_v6}\n" |
|
|
|
|
|
|
|
|
|
# @aliasses_v6.each do |a| |
|
|
|
|
# str << "\talias6 #{a}\n" |
|
|
|
|
# end |
|
|
|
|
# alias execution: only when the main ip address is setup |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
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] |
|
|
|
|
self.parse(ifname.not_nil!, content) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.parse (ifname : String, data : String) : InterfaceConfiguration |
|
|
|
|
up = false |
|
|
|
|
main_ip_v4 = InterfaceConfiguration::NotSetup.new |
|
|
|
|
main_ip_v6 = InterfaceConfiguration::NotSetup.new |
|
|
|
|
|
|
|
|
|
aliasses = [] of IPAddress |
|
|
|
|
|
|
|
|
|
data.split("\n").each do |line| |
|
|
|
|
case line |
|
|
|
|
when /^up/ |
|
|
|
|
up = true |
|
|
|
|
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/ |
|
|
|
|
# IPaddress is DHCP |
|
|
|
|
if /^inet /.match(line) |
|
|
|
|
main_ip_v4 = InterfaceConfiguration::DHCP.new |
|
|
|
|
else |
|
|
|
|
main_ip_v6 = InterfaceConfiguration::DHCP.new |
|
|
|
|
end |
|
|
|
|
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 |
|
|
|
|
else |
|
|
|
|
raise "Cannot parse: #{line}" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
InterfaceConfiguration.new(ifname, up, main_ip_v4, main_ip_v6, aliasses) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
Do.simulation = simulation |
|
|
|
|
|
|
|
|
|
if file.nil? |
|
|
|
|
raise "Cannot choose files yet" |
|
|
|
|
else |
|
|
|
|
# TODO: why having to force "not_nil!" ? Seems like a compiler bug |
|
|
|
|
NetworkConfigurationParser.parse_file(file.not_nil!).execute |
|
|
|
|
end |