-- -- (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() -- ############################################## -- NOTE: -- - The LAN interface in "passive" mode is the Management Interface -- - WAN interfaces in "passive" mode are the Capture Interfaces -- ############################################## 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 -- ############################################## -- Returns the supported modes function appliance_config:getSupportedModes() local all_modes = { { name = "passive", label = i18n("passive") }, { name = "bridging", label = i18n("bridge") }, } return all_modes end -- ############################################## function appliance_config:getOperatingMode() if not self.config.globals.operating_mode then return "passive" end return self.config.globals.operating_mode end -- ############################################## -- Get the physical LAN interfaces, based on the current operating mode -- nf_config overrides this function appliance_config:getPhysicalLanInterfaces() local mode = self:getOperatingMode() if mode == "passive" then return { self.config.globals.available_modes[mode].interfaces.lan, } elseif mode == "bridging" then return self.config.globals.available_modes[mode].interfaces.lan else return {} end end -- Get the physical WAN interfaces, based on the current operating mode -- nf_config and appliance_config overrides this function appliance_config:getPhysicalWanInterfaces() local mode = self:getOperatingMode() if not mode or mode == "passive" then return {} else return self.config.globals.available_modes.bridging.interfaces.wan end 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 set on the bridge 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] -- 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 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 -- ################### -- Passive local passive = {} if(wired_default ~= nil) then passive["interfaces"] = {} passive["interfaces"]["unused"] = {} passive["interfaces"]["lan"] = wired_default passive["comment"] = "Passive monitoring appliance" for a, b in pairsByKeys(wired) do if a ~= wired_default then table.insert(passive["interfaces"]["unused"], a) end end config["globals"]["available_modes"]["passive"] = passive operating_mode = "passive" end -- ################### -- 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 -- ################### 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