# TODO: OpenBSD: no '-w' parameter for sysctl # TODO: OpenBSD: test scanning for wireless AP with ifconfig # 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_property cmd_sysctl : SysctlCommand.class | OpenBSDSysctlCommand.class | NotSetup.class = NotSetup class DNS property addresses : Array(String) property search : Array(String) def initialize() @addresses = Array(String).new @search = Array(String).new end def to_s(io : IO) addresses.each do |ip| io << "nameserver #{ip}" end io << "search #{search.join(" ")}" unless search.empty? end def execute if Context.simulation puts "simulation, writing in #{Context.root}/etc/resolv.conf:" @addresses.each do |address| puts "\tnameserver #{address}" end puts "\tsearch #{@search.join(" ")}" unless search.empty? else Dir.mkdir_p("#{Context.root}/etc/") File.open("#{Context.root}/etc/resolv.conf", "w+") do |file| @addresses.each do |address| file.puts "nameserver #{address}" end file.puts "search #{@search.join(" ")}" unless search.empty? end 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: (?[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 WPASupplicant # store the AP passphrase in the wpa_supplicant way def self.passphrase(ssid : String, passphrase : String) dirpath = "#{Context.root}/#{Context.keydir}" Dir.mkdir_p(dirpath) unless Dir.exists?(dirpath) # verify if the passphrase already is stored for wpa_supplicant return if File.exists?("#{dirpath}/#{ssid}.conf") File.open("#{dirpath}/#{ssid}.conf", "w+") do |file| Do.run("wpa_passphrase", [ ssid, "#{passphrase}" ]) do |p| p.output.each_line do |line| file.puts line end end end end def self.connection(ifname : String, ssid : String) dirpath = "#{Context.root}/#{Context.keydir}" unless Do.run("wpa_supplicant", [ "-B", "-c", "#{dirpath}/#{ssid}.conf", "-i", ifname ]).success? raise "(wpa_supplicant) cannot connect to the wireless AP #{ssid} via the interface #{ifname}" end end def self.disconnection(ifname : String, ssid : String) # TODO: this kills every running wpa_supplicant application # not only the instance for ifname dirpath = "#{Context.root}/#{Context.keydir}" unless Do.run("pkill", [ "wpa_supplicant" ]).success? raise "(wpa_supplicant) cannot stop the wpa_supplicant daemon" end 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 OpenBSDDHClientCommand < DHClientCommand def self.dhcp6(ifname : String) puts "TODO: dhcp6 on OpenBSD" 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) cannot get SSID with ifconfig on Linux" end end class OpenBSDIfconfigCommand < IfconfigCommand # get the available SSID def self.scan(ifname : String) : Array(String) ssids = Array(String).new Do.run("ifconfig", [ ifname, "scan" ]) do |p| p.output.each_line do |line| ssid = /nwid (?[a-zA-Z0-9_-]+)/.match(line).try &.["ssid"] unless ssid.nil? ssids << ssid end end end if ssids.empty? raise "(openbsd ifconfig) cannot get ssid list from #{ifname}" end ssids end def self.autoconfiguration(ifname : String) unless Do.run("ifconfig", [ ifname, "inet6", "autoconf" ]).success? raise "(openbsd ifconfig) Cannot set IPv6 autoconfiguration on #{ifname}" end 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}" end end class SysctlCommand def self.autoconfiguration(ifname : String) 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 end class OpenBSDSysctlCommand def self.autoconfiguration(ifname : String) OpenBSDIfconfigCommand.autoconfiguration ifname 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) cmd = @@cmd_sysctl case cmd when NotSetup.class raise "cannot autoconfigure IPv6" else cmd.autoconfiguration ifname 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.store_access_point_keys(ssid : String, security : WirelessAPSetup::WPA) # TODO: only one way to do it NetworkCommands::WPASupplicant.passphrase ssid, security.key end def self.wireless_access_point_connection(ifname : String, ssid : String) NetworkCommands::WPASupplicant.connection ifname, ssid end def self.wireless_access_point_disconnection(ifname : String, ssid : String) NetworkCommands::WPASupplicant.disconnection ifname, ssid end end