ntopng/scripts/lua/modules/system_config/appliance_config.lua

378 lines
13 KiB
Lua

--
-- (C) 2020 - ntop.org
--
local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/system_config/?.lua;" .. package.path
package.path = dirs.installdir .. "/pro/scripts/lua/modules/?.lua;" .. package.path
require "lua_utils"
local system_config = require "system_config"
local json = require "dkjson"
local ipv4_utils = require "ipv4_utils"
local os_utils = require "os_utils"
local sys_utils = require "sys_utils"
local tz_utils = require "tz_utils"
-- ##############################################
-- Example
-- package.path = dirs.installdir .. "/scripts/lua/modules/system_config/?.lua;" .. package.path
-- -- Editable
-- local appliance_config = require "appliance_config":create(true)
-- -- Readable
-- local appliance_config = require "appliance_config":create()
-- ##############################################
local appliance_config = {}
-- ##############################################
function appliance_config:create(editable)
-- Instance of the base class
local _system_config = system_config:create()
-- Subclass using the base class instance
self.key = "appliance"
-- self is passed as argument so it will be set as base class metatable
-- and this will actually make it possible to override functions
local _appliance_config = _system_config:create(self)
if editable then
_appliance_config:editable()
else
_appliance_config:readable()
end
-- Return the instance
return _appliance_config
end
-- ##############################################
function appliance_config:_get_config_skeleton()
local config = {}
local defaults = { }
local default_global_dns = self:_get_default_global_dns_preset()
local config = {
["defaults"] = defaults,
["disabled_wans"] = {},
["globals"] = {
["available_modes"] = {},
["dns"] = {
["global_preset"] = default_global_dns.id,
["global"] = default_global_dns.primary_dns,
["secondary"] = default_global_dns.secondary_dns,
["forge_global"] = false
},
["lan_recovery_ip"] = {
["ip"] = "192.168.160.10",
["netmask"] = "255.255.255.0",
["comment"] = "Static LAN IP address used to reach the box. In bridging mode it is set on the bridge interface, in routing mode on the first (in case of multiple interfaces) LAN interface"
},
["management_access"] = {
["bind_to"] = "lan",
},
},
["interfaces"] = {
["comment"]= "List of available network interfaces. Only those listed in globals.operating_mode are actually configured",
["configuration"] = {}
},
["gateways"] = {
},
["static_routes"] = {
},
["date_time"] = {
["ntp_sync"] = {
["enabled"] = true,
},
["timezone"] = "Europe/Rome",
},
["dhcp_server"] = {
["enabled"] = false,
["options"] = {
"ddns-update-style none",
"log-facility local7",
"authoritative"
},
["subnet"] = {
["network"] = "192.168.1.0",
["netmask"] = "255.255.255.0",
["first_ip"] = "192.168.1.10",
["last_ip"] = "192.168.1.200",
["gateway"] = "192.168.1.1",
["broadcast"] = "192.168.1.255",
["options"] = {
"option domain-name \"ntop.local\"",
"default-lease-time 600",
"max-lease-time 7200"
}
},
["leases"] = {}
}
}
return config
end
-- ##############################################
function appliance_config:_guess_config()
local config = self:_get_config_skeleton()
local devs = { }
local wifi = { }
local bridge = { }
local wired = { }
local dhcp_ifaces = {}
local wired_default = nil
local wifi_default = nil
local ip_devs = system_config._split_dev_names('ip addr | awk \'$1 == "inet" && $7 { split( $2, addr, "/" ); print $7, addr[1] }\'', " ")
local devs = system_config._split_dev_names('cat /proc/net/dev', ":")
local wifi_devs = system_config._split_dev_names('cat /proc/net/wireless', ":")
local lan_iface
local wan_iface = nil
-- set the timezone to the current system timezone
if not isEmptyString(tz_utils.TimeZone()) then
config["date_time"]["timezone"] = tz_utils.TimeZone()
end
-- Necessary for inactive wifi devices
for dev in pairs(devs) do
local rv = sys_utils.execShellCmd("iwconfig 2>/dev/null | grep \"" .. dev .. "\" | grep \"IEEE 802.11\"")
if not isEmptyString(rv) then
wifi_devs[dev] = 1
end
end
-- Find DHCP interface
local res = sys_utils.execShellCmd('ps aux | grep dhclient')
if not isEmptyString(res) then
for _, line in pairs(split(res, "\n")) do
local name = line:gmatch(".([^.]+).leases")()
if devs[name] ~= nil then
dhcp_ifaces[name] = 1
end
end
end
-- Find the wan interface
local rv = sys_utils.execShellCmd("ip route show | grep \"default via\" | awk '{printf \"%s\", $5}'")
if not isEmptyString(rv) and devs[rv] ~= nil then
wan_iface = rv
end
local some_wired = nil
local static_wired = nil
-- Identify the type of the devices in the system
-- NOTE: we use pairsByKeys to impose an order across multiple guesses
for name, _ in pairsByKeys(devs) do
local addr = ip_devs[name]
-- print("==> "..tostring(name).."\n")
-- Not TUN/dummy/lo interface
if((name ~= "lo") and (not starts(name, "dummy")) and not(ntop.exists('/sys/class/net/'.. name ..'/tun_flags')) and not string.contains(name, ":")) then
if(ntop.exists('/sys/class/net/'.. name ..'/bridge')) then
local devs = system_config._split_dev_names('ls /sys/class/net/'.. name ..'/brif/', nil)
bridge[name] = { ["ports"] = devs }
if(addr ~= nil) then bridge[name]["ip"] = addr end
elseif(wifi_devs[name] ~= nil) then
wifi[name] = { }
if(addr ~= nil) then wifi[name]["ip"] = addr end
if(wifi_default == nil) then wifi_default = name end
else
-- Add per-interface gateway
config.gateways[name] = {interface=name, ping_address="8.8.8.8"}
wired[name] = { }
if(some_wired == nil) then some_wired = name end
if((name ~= wan_iface) and (dhcp_ifaces[name] == nil) and (static_wired == nil)) then static_wired = name end
if(addr ~= nil) then
wired[name]["ip"] = addr
end
-- print("[WIRED] ==> "..name.."\n")
end
end
end
wired_default = static_wired or some_wired
-- TODO modify this after supporting the other modes:
-- e.g. for bridge interface the lan should be br0
lan_iface = wired_default
-- ###################
-- Bridge interfaces
local bridging = {}
local operating_mode = nil
local bridging_defined = false
local bridge_ifname = nil
-- If we currently have a bridge interface, use its configuration
if table.len(bridge) > 0 then
local used_ifaces = {}
for a,b in pairsByKeys(bridge) do
if table.len(b.ports) > 1 then
bridge_ifname = a
bridging["name"] = a
bridging["interfaces"] = {unused={}}
bridging["comment"] = "Transparent bridge"
bridging["interfaces"]["lan"] = {}
bridging["interfaces"]["wan"] = {}
for n,c in pairsByKeys(b.ports) do
if(n == 1) then
table.insert(bridging["interfaces"]["lan"], c)
else
table.insert(bridging["interfaces"]["wan"], c)
end
used_ifaces[c] = true
end
operating_mode = "bridging"
bridging_defined = true
break
end
end
if bridging_defined then
-- add other interfaces to the unused ones
for iface,_ in pairsByKeys(wired) do
if used_ifaces[iface] == nil then
table.insert(bridging["interfaces"]["unused"], iface)
end
end
end
end
-- If we do not have a bridge interface but have enough interfaces
if((not bridging_defined) and(table.len(wired) > 1)) then
local n = 0
bridge_ifname = "br0"
bridging["name"] = bridge_ifname
bridging["interfaces"] = {unused={}}
bridging["comment"] = "Transparent bridge"
for a,b in pairsByKeys(wired) do
if(n == 0) then
bridging["interfaces"]["lan"] = { a }
elseif(n == 1) then
bridging["interfaces"]["wan"] = { a }
else
table.insert(bridging["interfaces"]["unused"], a)
end
n = n + 1
end
bridging_defined = true
end
if(bridging_defined) then
config["globals"]["available_modes"]["bridging"] = bridging
end
-- ###################
-- TODO standard or bridging
operating_mode = "bridging"
if(operating_mode ~= nil) then
config["globals"]["operating_mode"] = operating_mode
end
-- ###################
-- Network configuration
for a,b in pairs(wired) do
local speed = (interface.getMaxIfSpeed(a) or 10) * 1000 -- Mbps to Kbps that are more tc-friendly :)
config["interfaces"]["configuration"][a] = {
["family"] = "wired", ["masquerade"] = true, ["network"] = { },
["speed"] = {["upload"] = speed, ["download"] = speed} }
-- Note: we force static on the wired_default if not in bridging mode
if dhcp_ifaces[a] and ((a ~= wired_default) or (operating_mode == "bridging")) then
config["interfaces"]["configuration"][a]["network"]["mode"] = "dhcp"
config["interfaces"]["configuration"][a]["network"]["ip"] = "192.168.10.1"
config["interfaces"]["configuration"][a]["network"]["netmask"] = "255.255.255.0"
config["interfaces"]["configuration"][a]["network"]["gateway"] = "0.0.0.0"
else
-- non DHCP interface / wired_default
config["interfaces"]["configuration"][a]["network"]["mode"] = "static"
local netmask
if (ip_devs[a] == nil) then
-- e.g. on the wired_default
ip_devs[a] = "192.168.1.1"
netmask = "255.255.255.0"
else
-- The device has an IP address
local addresses = getAllInterfaceAddresses(a)
for _, addr in pairs(addresses) do
if addr.ip == ip_devs[a] then
netmask = addr.netmask
end
end
if isEmptyString(netmask) then netmask = "255.255.255.0" end
end
local gateway = system_config._interface_get_default_gateway(a)
config["interfaces"]["configuration"][a]["network"]["ip"] = ip_devs[a]
config["interfaces"]["configuration"][a]["network"]["netmask"] = netmask
if not isEmptyString(gateway) then
config["interfaces"]["configuration"][a]["network"]["gateway"] = gateway
end
end
end
if(wired_default ~= nil) then
-- Add the aliased interface to the configuration
config["interfaces"]["configuration"][wired_default..":1"] = { ["family"] = "wired", ["network"]={},
["speed"] = {["upload"]= speed, ["download"] = speed} }
config["interfaces"]["configuration"][wired_default..":1"]["network"]["mode"] = "static"
config["interfaces"]["configuration"][wired_default..":1"]["network"]["ip"] = "192.168.10.1"
config["interfaces"]["configuration"][wired_default..":1"]["network"]["netmask"] = "255.255.255.0"
end
if(bridge_ifname ~= nil) then
local speed = 100 * 1000 -- Kbps, not really important here
-- Add the bridge interface to the configuration
config["interfaces"]["configuration"][bridge_ifname] = { ["family"] = "bridge", ["network"]={},
["speed"] = {["upload"]= speed, ["download"] = speed} }
config["interfaces"]["configuration"][bridge_ifname]["network"]["mode"] = "dhcp"
config["interfaces"]["configuration"][bridge_ifname]["network"]["ip"] = "192.168.20.1"
config["interfaces"]["configuration"][bridge_ifname]["network"]["netmask"] = "255.255.255.0"
end
-- Not supported right now
--for a,b in pairs(wifi) do
--config["interfaces"]["configuration"][a] = { ["family"] = "wireless", ["network"] = { ["mode"] = "dhcp" } }
--end
-- Make sure we have a valid DHCP range
self:_fix_dhcp_from_lan(config, lan_iface)
-- Make sure to apply the mode specific settings
self:_apply_operating_mode_settings(config)
return config
end
-- ##############################################
return appliance_config