From 0ed83ba0a55c7b7f67ab8d643df94efe9e726790 Mon Sep 17 00:00:00 2001 From: Mike Hansen Date: Thu, 9 Jan 2025 09:51:36 -0500 Subject: [PATCH] [OLS-545] Removal of AP specific schema elements from ols-ucentral-schema Remove the AP specific elements of the schema (configuration), and state (reporting) Remove the ucode files that are not used Regenerate the json and documentation files to reflect schema and state changes Signed-off-by: Mike Hansen --- .gitignore | 1 - command/cmd.uc | 90 - command/cmd_certupdate.uc | 29 - command/cmd_factory.uc | 50 - command/cmd_leds.uc | 15 - command/cmd_perform.uc | 15 - command/cmd_reboot.uc | 11 - command/cmd_rrm.uc | 66 - command/cmd_rtty.uc | 35 - command/cmd_script.uc | 93 - command/cmd_stats.uc | 1 - command/cmd_syslog.uc | 20 - command/cmd_trace.uc | 65 - command/cmd_transfer.uc | 20 - command/cmd_upgrade.uc | 91 - command/cmd_wifiscan.uc | 285 - docs/schema_doc.css | 181 + docs/schema_doc.min.js | 1 + docs/ucentral-schema.html | 117 + docs/ucentral-state.html | 28 + docs/ucentral.capabilities.html | 20 + jsdoc/ucode-transpiler.js | 650 -- renderer/qos.json | 153 - renderer/renderer.uc | 1172 --- renderer/signature.uc | 45 - renderer/templates/admin_ui.uc | 42 - renderer/templates/base.uc | 57 - renderer/templates/broadband.uc | 100 - renderer/templates/config_raw.uc | 5 - renderer/templates/eap_users.uc | 4 - renderer/templates/ethernet.uc | 13 - renderer/templates/interface.uc | 216 - renderer/templates/interface/bridge-vlan.uc | 64 - renderer/templates/interface/broadband.uc | 45 - renderer/templates/interface/captive.uc | 119 - renderer/templates/interface/common.uc | 39 - renderer/templates/interface/dhcp.uc | 60 - renderer/templates/interface/firewall.uc | 171 - .../templates/interface/firewall/allow.uc | 20 - .../templates/interface/firewall/forward.uc | 15 - renderer/templates/interface/gre.uc | 45 - renderer/templates/interface/gre6.uc | 44 - renderer/templates/interface/ipv4.uc | 12 - renderer/templates/interface/ipv6.uc | 10 - renderer/templates/interface/l2tp.uc | 31 - renderer/templates/interface/mesh.uc | 17 - renderer/templates/interface/ssid.uc | 616 -- renderer/templates/interface/vxlan.uc | 35 - renderer/templates/interface/wmm.uc | 125 - renderer/templates/metric/dhcp_snooping.uc | 21 - renderer/templates/metric/health.uc | 11 - renderer/templates/metric/realtime.uc | 7 - renderer/templates/metric/statistics.uc | 7 - renderer/templates/metric/telemetry.uc | 8 - renderer/templates/metric/wifi_frames.uc | 9 - renderer/templates/metric/wifi_scan.uc | 3 - renderer/templates/radio.uc | 206 - .../templates/services/airtime_fairness.uc | 15 - renderer/templates/services/captive.uc | 84 - renderer/templates/services/data_plane.uc | 29 - renderer/templates/services/dhcp_relay.uc | 36 - renderer/templates/services/dhcp_snooping.uc | 13 - renderer/templates/services/facebook_wifi.uc | 48 - renderer/templates/services/gps.uc | 11 - renderer/templates/services/http.uc | 36 - renderer/templates/services/ieee8021x.uc | 53 - renderer/templates/services/igmp.uc | 20 - renderer/templates/services/lldp.uc | 16 - renderer/templates/services/log.uc | 18 - renderer/templates/services/mdns.uc | 25 - renderer/templates/services/ntp.uc | 23 - renderer/templates/services/online_check.uc | 27 - .../templates/services/quality_of_service.uc | 87 - .../templates/services/radius_gw_proxy.uc | 7 - renderer/templates/services/radius_proxy.uc | 93 - renderer/templates/services/rrm.uc | 12 - renderer/templates/services/rtty.uc | 12 - renderer/templates/services/ssh.uc | 31 - renderer/templates/services/wifi_steering.uc | 29 - .../templates/services/wireguard_overlay.uc | 116 - renderer/templates/spotfilter.uc | 52 - renderer/templates/switch.uc | 17 - renderer/templates/toplevel.uc | 149 - renderer/templates/unit.uc | 19 - renderer/ucentral.uc | 118 - renderer/wifi/iface.uc | 79 - renderer/wifi/mesh.uc | 40 - renderer/wifi/phy.uc | 170 - renderer/wifi/station.uc | 103 - renderer/wifi/survey.uc | 36 - schema/definitions.yml | 15 - ...als.wireless-multimedia.class-selector.yml | 28 - .../globals.wireless-multimedia.profile.yml | 12 - schema/globals.wireless-multimedia.table.yml | 23 - schema/globals.yml | 4 - schema/interface.ssid.acl.yml | 18 - schema/interface.ssid.certificates.yml | 27 - schema/interface.ssid.encryption.yml | 48 - schema/interface.ssid.multi-psk.yml | 23 - schema/interface.ssid.pass-point.yml | 209 - schema/interface.ssid.quality-thresholds.yml | 21 - schema/interface.ssid.radius.health.yml | 12 - schema/interface.ssid.radius.local-user.yml | 22 - schema/interface.ssid.radius.local.yml | 16 - schema/interface.ssid.radius.server.yml | 133 - schema/interface.ssid.radius.yml | 67 - schema/interface.ssid.rate-limit.yml | 14 - schema/interface.ssid.roaming.yml | 41 - schema/interface.ssid.rrm.yml | 32 - schema/interface.ssid.yml | 181 - schema/interface.yml | 4 - schema/metrics.realtime.yml | 3 - schema/metrics.statistics.yml | 2 - schema/metrics.telemetry.yml | 3 - schema/metrics.wifi-scan.yml | 16 - schema/metrics.yml | 4 - schema/radio.he.yml | 20 - schema/radio.rates.yml | 42 - schema/radio.yml | 153 - schema/service.admin-ui.yml | 32 - schema/service.airtime-fairness.yml | 46 - schema/service.captive.click.yml | 10 - schema/service.captive.credentials.yml | 21 - schema/service.captive.radius.yml | 55 - schema/service.captive.uam.yml | 112 - schema/service.captive.yml | 35 - schema/service.dhcp-relay.yml | 47 - schema/service.facebook-wifi.yml | 16 - schema/service.ieee8021x.yml | 98 - schema/service.online-check.yml | 1 - schema/service.rrm.yml | 13 - schema/service.wifi-steering.yml | 53 - schema/service.yml | 18 +- schema/ucentral.yml | 6 - schemareader.uc | 7331 ++++++----------- state/interface.ssid.association.yml | 126 - state/interface.ssid.yml | 66 - state/interface.yml | 4 +- state/radio.yml | 51 - state/state.yml | 2 - system/capabilities.uc | 101 - system/health.uc | 152 - system/ip-collide.uc | 148 - system/onlinecheck.uc | 76 - system/probe_services.uc | 68 - system/reboot_cause.uc | 5 - system/state.uc | 766 -- system/sysinfo.uc | 26 - system/telemetry.uc | 8 - ucentral.schema.full.json | 2599 +----- ucentral.schema.json | 1759 +--- ucentral.schema.pretty.json | 2009 +---- ucentral.state.pretty.json | 295 - 153 files changed, 2862 insertions(+), 21541 deletions(-) delete mode 100755 command/cmd.uc delete mode 100644 command/cmd_certupdate.uc delete mode 100644 command/cmd_factory.uc delete mode 100644 command/cmd_leds.uc delete mode 100644 command/cmd_perform.uc delete mode 100644 command/cmd_reboot.uc delete mode 100644 command/cmd_rrm.uc delete mode 100644 command/cmd_rtty.uc delete mode 100644 command/cmd_script.uc delete mode 100644 command/cmd_stats.uc delete mode 100644 command/cmd_syslog.uc delete mode 100644 command/cmd_trace.uc delete mode 100644 command/cmd_transfer.uc delete mode 100644 command/cmd_upgrade.uc delete mode 100644 command/cmd_wifiscan.uc create mode 100644 docs/schema_doc.css create mode 100644 docs/schema_doc.min.js create mode 100644 docs/ucentral-schema.html create mode 100644 docs/ucentral-state.html create mode 100644 docs/ucentral.capabilities.html delete mode 100644 jsdoc/ucode-transpiler.js delete mode 100644 renderer/qos.json delete mode 100644 renderer/renderer.uc delete mode 100644 renderer/signature.uc delete mode 100644 renderer/templates/admin_ui.uc delete mode 100644 renderer/templates/base.uc delete mode 100644 renderer/templates/broadband.uc delete mode 100644 renderer/templates/config_raw.uc delete mode 100644 renderer/templates/eap_users.uc delete mode 100644 renderer/templates/ethernet.uc delete mode 100644 renderer/templates/interface.uc delete mode 100644 renderer/templates/interface/bridge-vlan.uc delete mode 100644 renderer/templates/interface/broadband.uc delete mode 100644 renderer/templates/interface/captive.uc delete mode 100644 renderer/templates/interface/common.uc delete mode 100644 renderer/templates/interface/dhcp.uc delete mode 100644 renderer/templates/interface/firewall.uc delete mode 100644 renderer/templates/interface/firewall/allow.uc delete mode 100644 renderer/templates/interface/firewall/forward.uc delete mode 100644 renderer/templates/interface/gre.uc delete mode 100644 renderer/templates/interface/gre6.uc delete mode 100644 renderer/templates/interface/ipv4.uc delete mode 100644 renderer/templates/interface/ipv6.uc delete mode 100644 renderer/templates/interface/l2tp.uc delete mode 100644 renderer/templates/interface/mesh.uc delete mode 100644 renderer/templates/interface/ssid.uc delete mode 100644 renderer/templates/interface/vxlan.uc delete mode 100644 renderer/templates/interface/wmm.uc delete mode 100644 renderer/templates/metric/dhcp_snooping.uc delete mode 100644 renderer/templates/metric/health.uc delete mode 100644 renderer/templates/metric/realtime.uc delete mode 100644 renderer/templates/metric/statistics.uc delete mode 100644 renderer/templates/metric/telemetry.uc delete mode 100644 renderer/templates/metric/wifi_frames.uc delete mode 100644 renderer/templates/metric/wifi_scan.uc delete mode 100644 renderer/templates/radio.uc delete mode 100644 renderer/templates/services/airtime_fairness.uc delete mode 100644 renderer/templates/services/captive.uc delete mode 100644 renderer/templates/services/data_plane.uc delete mode 100644 renderer/templates/services/dhcp_relay.uc delete mode 100644 renderer/templates/services/dhcp_snooping.uc delete mode 100644 renderer/templates/services/facebook_wifi.uc delete mode 100644 renderer/templates/services/gps.uc delete mode 100644 renderer/templates/services/http.uc delete mode 100644 renderer/templates/services/ieee8021x.uc delete mode 100644 renderer/templates/services/igmp.uc delete mode 100644 renderer/templates/services/lldp.uc delete mode 100644 renderer/templates/services/log.uc delete mode 100644 renderer/templates/services/mdns.uc delete mode 100644 renderer/templates/services/ntp.uc delete mode 100644 renderer/templates/services/online_check.uc delete mode 100644 renderer/templates/services/quality_of_service.uc delete mode 100644 renderer/templates/services/radius_gw_proxy.uc delete mode 100644 renderer/templates/services/radius_proxy.uc delete mode 100644 renderer/templates/services/rrm.uc delete mode 100644 renderer/templates/services/rtty.uc delete mode 100644 renderer/templates/services/ssh.uc delete mode 100644 renderer/templates/services/wifi_steering.uc delete mode 100644 renderer/templates/services/wireguard_overlay.uc delete mode 100644 renderer/templates/spotfilter.uc delete mode 100644 renderer/templates/switch.uc delete mode 100644 renderer/templates/toplevel.uc delete mode 100644 renderer/templates/unit.uc delete mode 100755 renderer/ucentral.uc delete mode 100644 renderer/wifi/iface.uc delete mode 100644 renderer/wifi/mesh.uc delete mode 100644 renderer/wifi/phy.uc delete mode 100644 renderer/wifi/station.uc delete mode 100644 renderer/wifi/survey.uc delete mode 100644 schema/definitions.yml delete mode 100644 schema/globals.wireless-multimedia.class-selector.yml delete mode 100644 schema/globals.wireless-multimedia.profile.yml delete mode 100644 schema/globals.wireless-multimedia.table.yml delete mode 100644 schema/interface.ssid.acl.yml delete mode 100644 schema/interface.ssid.certificates.yml delete mode 100644 schema/interface.ssid.encryption.yml delete mode 100644 schema/interface.ssid.multi-psk.yml delete mode 100644 schema/interface.ssid.pass-point.yml delete mode 100644 schema/interface.ssid.quality-thresholds.yml delete mode 100644 schema/interface.ssid.radius.health.yml delete mode 100644 schema/interface.ssid.radius.local-user.yml delete mode 100644 schema/interface.ssid.radius.local.yml delete mode 100644 schema/interface.ssid.radius.server.yml delete mode 100644 schema/interface.ssid.radius.yml delete mode 100644 schema/interface.ssid.rate-limit.yml delete mode 100644 schema/interface.ssid.roaming.yml delete mode 100644 schema/interface.ssid.rrm.yml delete mode 100644 schema/interface.ssid.yml delete mode 100644 schema/metrics.wifi-scan.yml delete mode 100644 schema/radio.he.yml delete mode 100644 schema/radio.rates.yml delete mode 100644 schema/radio.yml delete mode 100644 schema/service.admin-ui.yml delete mode 100644 schema/service.airtime-fairness.yml delete mode 100644 schema/service.captive.click.yml delete mode 100644 schema/service.captive.credentials.yml delete mode 100644 schema/service.captive.radius.yml delete mode 100644 schema/service.captive.uam.yml delete mode 100644 schema/service.captive.yml delete mode 100644 schema/service.dhcp-relay.yml delete mode 100644 schema/service.facebook-wifi.yml delete mode 100644 schema/service.ieee8021x.yml delete mode 100644 schema/service.rrm.yml delete mode 100644 schema/service.wifi-steering.yml delete mode 100644 state/interface.ssid.association.yml delete mode 100644 state/interface.ssid.yml delete mode 100644 state/radio.yml delete mode 100755 system/capabilities.uc delete mode 100755 system/health.uc delete mode 100755 system/ip-collide.uc delete mode 100755 system/onlinecheck.uc delete mode 100755 system/probe_services.uc delete mode 100644 system/reboot_cause.uc delete mode 100755 system/state.uc delete mode 100644 system/sysinfo.uc delete mode 100755 system/telemetry.uc diff --git a/.gitignore b/.gitignore index a48fdaf..73cbb82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /jsdoc.conf.json -docs/ node_modules diff --git a/command/cmd.uc b/command/cmd.uc deleted file mode 100755 index 7f45463..0000000 --- a/command/cmd.uc +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/ucode -push(REQUIRE_SEARCH_PATH, '/usr/share/ucentral/*.uc'); -let fs = require("fs"); -let uci = require("uci"); -let ubus = require("ubus"); -let capabfile = fs.open("/etc/ucentral/capabilities.json", "r"); -let capab = json(capabfile.read("all")); -let pipe = fs.popen('fw_printenv developer'); -let developer = replace(pipe.read("all"), '\n', ''); -pipe.close(); -let restrict = {}; -if (developer != 'developer=1') { - let restrictfile = fs.open("/etc/ucentral/restrictions.json", "r"); - restrict = restrictfile ? json(restrictfile.read("all")) : {}; -} -let cmdfile = fs.open(ARGV[0], "r"); -let cmd = json(cmdfile.read("all")); -let id = ARGV[1]; -let ctx = ubus.connect(); - -if (!ctx) { - warn("Unable to connect to ubus: " + ubus.error() + "\n"); - exit(1); -} - -/* Convenience logger outputting to both stderr and remote central log */ -function log(fmt, ...args) { - let msg = sprintf(fmt, ...args); - - warn(msg + "\n"); - - ctx.call("ucentral", "log", { msg: msg }); -} - -function result(code, fmt, ...args) { - let text = sprintf(fmt, ...args); - - ctx.call("ucentral", "result", { - "status": { - "error": code, - "text": text - }, "id": +id - }); - warn(text + "\n"); -} - -function result_json(status) { - ctx.call("ucentral", "result", {"id": +id, "status": status}); - if (status.text) - warn(status.text + "\n"); - if (status.resultText) - warn(status.resultText + "\n"); -} - -/* Scope of functions and ressources the command includes have access to */ -let scope = { - /* ressources */ - uci, - cursor: uci.cursor(), - ctx, - fs, - restrict, - - /* log helper */ - log, - - /* result helpers */ - result, - result_json, - - /* command argument object */ - args: (cmd.payload || {}), - - /* cmd id */ - id: (id || 0) -}; - -if (match(cmd.command, /^[A-Za-z0-9_]+$/)) { - try { - include(sprintf("cmd_%s.uc", cmd.command), scope); - } - catch (e) { - log("Exception invoking '%s' command module: %s\n%s\n", - cmd.cmd, e, e.stacktrace[0].context); - } -} -else { - log("Invalid command module name specified"); -} - diff --git a/command/cmd_certupdate.uc b/command/cmd_certupdate.uc deleted file mode 100644 index 8686c68..0000000 --- a/command/cmd_certupdate.uc +++ /dev/null @@ -1,29 +0,0 @@ -let tar = b64dec(args.certificates); - -if (!tar || !fs.writefile('/tmp/certs.tar', tar)) { - result_json({ - "error": 2, - "text": 'failed to extract certificates' - }); - return; -} - -if (system('/sbin/certupdate')) { - result_json({ - "error": 2, - "text": 'failed to update certificates' - }); - return; -} - -include('reboot_cause.uc', { reason: 'certupdate' }); - -ctx.call("ucentral", "result", { - "status": { - "error": 0, - "text": 'Success' - }, "id": +id - }); -sleep(5000); -system("(sleep 10; jffs2reset -y -r)&"); -system("/etc/init.d/ucentral stop"); diff --git a/command/cmd_factory.uc b/command/cmd_factory.uc deleted file mode 100644 index 8bdae5f..0000000 --- a/command/cmd_factory.uc +++ /dev/null @@ -1,50 +0,0 @@ -let reset_cmdline = [ 'jffs2reset', '-r', '-y' ]; - -if (length(args) && args.keep_redirector) { - let archive_cmdline = [ - 'tar', 'czf', '/sysupgrade.tgz', - "/etc/config/ucentral" - ]; - - let files = [ - "/etc/ucentral/cas.pem", "/etc/ucentral/cert.pem", - "/etc/ucentral/gateway.json", "/etc/ucentral/dev-id", - "/etc/ucentral/key.pem", "/etc/ucentral/profile.json" - ]; - for (let f in files) - if (fs.stat(f)) - push(archive_cmdline, f); - - let active_config = fs.readlink("/etc/ucentral/ucentral.active"); - - if (active_config) - push(archive_cmdline, '/etc/ucentral/ucentral.active', active_config); - else - result_json({ - "error": 2, - "text": sprintf("Unable to determine active configuration: %s", fs.error()) - }); - - let rc = system(archive_cmdline); - - if (rc != 0) { - result_json({ - "error": 2, - "text": sprintf("Archive command %s exited with non-zero code %d", archive_cmdline, rc) - }); - - return; - } - - push(reset_cmdline, '-k'); -} - -include('reboot_cause.uc', { reason: 'factory' }); - -let rc = system(reset_cmdline); - -if (rc != 0) - result_json({ - "error": 2, - "text": sprintf("Reset command %s exited with non-zero code %d", reset_cmdline, rc) - }); diff --git a/command/cmd_leds.uc b/command/cmd_leds.uc deleted file mode 100644 index 8aba8c3..0000000 --- a/command/cmd_leds.uc +++ /dev/null @@ -1,15 +0,0 @@ -function set_led(val, path) -{ - let cursor = uci.cursor(path); - cursor.set("system", "@system[-1]", "leds_off", val); - cursor.commit(); -} - -let val = 0; -if (args.pattern == "off") - val = 1; - -set_led(val); -set_led(val, "/etc/config-shadow/"); - -system("/etc/init.d/led restart"); diff --git a/command/cmd_perform.uc b/command/cmd_perform.uc deleted file mode 100644 index 026c2ab..0000000 --- a/command/cmd_perform.uc +++ /dev/null @@ -1,15 +0,0 @@ -let rc = system(args.command); - -if (rc != 0) { - result_json({ - "error": 2, - "text": "Command returned an error", - "resultCode": rc - }); - - return; -} -result_json({ - "error": 0, - "text": "Command was executed" -}); diff --git a/command/cmd_reboot.uc b/command/cmd_reboot.uc deleted file mode 100644 index fb09fb1..0000000 --- a/command/cmd_reboot.uc +++ /dev/null @@ -1,11 +0,0 @@ -log("Initiating reboot"); - -include('reboot_cause.uc', { reason: 'reboot' }); - -system("(sleep 10; reboot)&"); -system("/etc/init.d/ucentral stop"); - -let err = ctx.error(); - -if (err != null) - result(2, "Reboot call failed with status %s", err); diff --git a/command/cmd_rrm.uc b/command/cmd_rrm.uc deleted file mode 100644 index be6e587..0000000 --- a/command/cmd_rrm.uc +++ /dev/null @@ -1,66 +0,0 @@ -function log(msg) { - system('logger RRM: ' + msg ); -} - -let handlers = { - // ubus call usteer2 command '{"action": "kick", "addr": "1c:57:dc:37:3c:b1", "reason": 5, "ban_time": 30 }' - kick: function(params) { - if (!params.addr) - return false; - params.reason ??= 5; - params.ban_time ??= 30; - return true; - }, - - // ubus call usteer2 command '{"action": "beacon_request", "addr": "4e:7f:3e:2c:8a:68", "channel": 36 }' - // ubus call usteer2 command '{"action": "beacon_request", "addr": "4e:7f:3e:2c:8a:68", "ssid": "Cockney" }' - beacon_request: function(params) { - if (!params.addr) - return false; - return true; - }, - - // ubus call usteer2 command '{"action": "channel_switch", "bssid": "34:eF:b6:aF:48:b1", "params": "channel": 4, "band": "2G"}' - channel_switch: function(params) { - if (!params.bssid || !params.channel) - return false; - return true; - }, - - // ubus call usteer2 command '{"action": "tx_power", "bssid": "34:eF:b6:aF:48:b1", "level": 20 }' - tx_power: function(params) { - if (!params.bssid || !params.level) - return false; - return true; - }, - - // ubus call usteer2 command '{"action": "bss_transition", "addr": "4e:7f:3e:2c:8a:68", "params": "neighbors": ["34:ef:b6:af:48:b1"] }' - bss_transition: function(params) { - if (!params.addr || !params.neighbors) - return false; - for (let neighbor in params.neighbors) - if (type(neighbor) != 'string') - return false; - return true; - }, - - // ubus call usteer2 command '{"action": "neighbors", "neighbors": [ [ "00:11:22:33:44:55", "OpenWifi", "34efb6af48b1af4900005301070603010300" ], [ "aa:bb:cc:dd:ee:ff", "OpenWifi2", "34efb6af48b1af4900005301070603010300" ] ] }' - neighbors: function(params) { - if (!params.neighbors) - return false; - return true; - } -}; - -if (type(args.actions) != 'array') - return; - -for (let action in args.actions) { - if (type(action) != 'object') - continue; - if (!handlers[action.action] || !handlers[action.action](action)) - continue; - action.event = true; - let result = ctx.call('rrm', 'command', action); - log(result); -} diff --git a/command/cmd_rtty.uc b/command/cmd_rtty.uc deleted file mode 100644 index 8b511c3..0000000 --- a/command/cmd_rtty.uc +++ /dev/null @@ -1,35 +0,0 @@ -if (!args.id || !args.server || !args.port || !args.token || !args.timeout) { - result_json({ - "error": 2, - "text": "Invalid parameters.", - "resultCode": -1 - }); - - return; -} - -if (restrict.rtty) { - result_json({ - "error": 2, - "text": "RTTY is restricted.", - "resultCode": -1 - }); - - return; -} - - -cursor.load("rtty"); -cursor.set("rtty", "@rtty[-1]", "enable", 1); -cursor.set("rtty", "@rtty[-1]", "id", args.id); -cursor.set("rtty", "@rtty[-1]", "host", args.server); -cursor.set("rtty", "@rtty[-1]", "port", args.port); -cursor.set("rtty", "@rtty[-1]", "token", args.token); -cursor.set("rtty", "@rtty[-1]", "timeout", args.timeout); -cursor.commit(); - -system("/etc/init.d/rtty restart"); -result_json({ - "error": 0, - "text": "Command was executed" -}); diff --git a/command/cmd_script.uc b/command/cmd_script.uc deleted file mode 100644 index 21e6732..0000000 --- a/command/cmd_script.uc +++ /dev/null @@ -1,93 +0,0 @@ -let uloop = require('uloop'); -let fs = require('fs'); -let result; -let abort; -let signature = require('signature'); -if (args.type == 'diagnostic') { - system('cp /usr/share/ucentral/diagnostic.uc /tmp/script.cmd'); -} else { - let decoded = b64dec(args.script); - if (!decoded) { - result_json({ - "error": 2, - "result": "invalid base64" - }); - return; - } - - let script = fs.open("/tmp/script.cmd", "w"); - script.write(decoded); - script.close(); - fs.chmod("/tmp/script.cmd", 700); -} - -if (args.type != 'diagnostic' && - restrict.commands && - !signature.verify("/tmp/script.cmd", args.signature)) { - result_json({ - "error": 3, - "result": "invalid signature" - }); - return; -} - -let out = ''; -if (args.uri) { - result_json({ error: 0, result: 'pending'}); - out = `/tmp/bundle.${id}.tar.gz`; -} - -uloop.init(); - -let t = uloop.task( - function(pipe) { - switch (args.type) { - case 'diagnostic': - case 'bundle': - let bundle = require('bundle'); - bundle.init(id); - try { - include('/tmp/script.cmd', { bundle }); - } catch(e) { - //e.stacktrace[0].context - }; - bundle.complete(); - return; - default: - let stdout = fs.popen("/tmp/script.cmd " + out); - let result = stdout.read("all"); - let error = stdout.close(); - return { result, error }; - } - }, - - function(res) { - result = res; - uloop.end(); - } -); -if (args.timeout) - uloop.timer(args.timeout * 1000, function() { - t.kill(); - uloop.end(); - abort = true; - }); - - -uloop.run(); - -if (abort) - result = { - "error": 255, - "result": "timed out" - }; - -if (args.uri && !fs.stat(out)) { - result_json({ error: 1, - result: 'script did not generate any output'}); -} else if (args.uri) { - ctx.call("ucentral", "upload", {file: out, uri: args.uri, uuid: args.serial}); - result_json({ error: 0, - result: 'done'}); -} else - result_json(result || { result: 255, error: 'unknown'}); diff --git a/command/cmd_stats.uc b/command/cmd_stats.uc deleted file mode 100644 index 35e4a7e..0000000 --- a/command/cmd_stats.uc +++ /dev/null @@ -1 +0,0 @@ -include("./state.uc", { stats: args.stats }); diff --git a/command/cmd_syslog.uc b/command/cmd_syslog.uc deleted file mode 100644 index 5354aaf..0000000 --- a/command/cmd_syslog.uc +++ /dev/null @@ -1,20 +0,0 @@ -let log_data = ctx.call("log", "read", { - lines: +args.lines || 100, - oneshot: true, - stream: false -}); - -if (!log_data || !log_data.log) { - result(2, "Unable to obtain system log contents: %s", ubus.error()); - - return; -} - -warn(sprintf("Read %d lines\n", length(log_data.log))); - -result_json({ - "error": 0, - "text": "Success", - "resultCode": 0, - "resultData": log_data -}); diff --git a/command/cmd_trace.uc b/command/cmd_trace.uc deleted file mode 100644 index f48b166..0000000 --- a/command/cmd_trace.uc +++ /dev/null @@ -1,65 +0,0 @@ -let serial = cursor.get("ucentral", "config", "serial"); - -if (!serial) - return; - -if (args.network) { - let net = ctx.call("network.interface", "status", { interface: args.network }); - - if (net && net.l3_device) - args.interface = net.l3_device; -} - -if (!args.interface || !length(args.interface)) - args.interface = args.network; - -if (!match(args.interface, /^[^\/]+$/) || (args.interface != "any" && !fs.stat("/sys/class/net/" + args.interface))) { - result_json({ - "error": 1, - "text": "Failed", - "resultCode": 1, - "resultText": "Invalid network device specified" - }); - - return; -} - -let duration = +args.duration || 0; -let packets = +args.packets || 0; -let filename = sprintf("/tmp/pcap-%s-%d", serial, time()); -let sys = ctx.call('system', 'info'); - -let command = [ - 'tcpdump_timeout', - duration, - '-C', sys.memory.free / 4, - '-W', '1', - '-w', filename, - '-i', args.interface -]; - -if (!duration) { - push(command, '-c'); - push(command, packets); -} -let rc = system(command); - -if (rc != 0) { - result_json({ - "error": 1, - "text": "Failed", - "resultCode": rc, - "resultText": "tcpdump command exited with non-zero code" - }); - - return; -} - -ctx.call("ucentral", "upload", {file: filename, uri: args.uri, uuid: args.serial}); - -result_json({ - "error": 0, - "text": "Success", - "resultCode": 0, - "resultText": "Uploading file" -}); diff --git a/command/cmd_transfer.uc b/command/cmd_transfer.uc deleted file mode 100644 index 1349768..0000000 --- a/command/cmd_transfer.uc +++ /dev/null @@ -1,20 +0,0 @@ -log("Initiating gateway transfer"); - -if (!args.server || !args.port) { - result(2, "invalid arguments"); - return; -} - -fs.writefile('/etc/ucentral/gateway.json', { server: args.server, port: args.port }); -system('cp /etc/ucentral/ucentral.cfg.0000000001 /etc/ucentral/ucentral.cfg.0000000002'); -system('rm /etc/ucentral/ucentral.cfg.1* /etc/ucentral/ucentral.active'); - -include('reboot_cause.uc', { reason: 'transfer' }); - -system("(sleep 10; reboot)&"); -system("/etc/init.d/ucentral stop"); - -let err = ctx.error(); - -if (err != null) - result(2, "Reboot call failed with status %s", err); diff --git a/command/cmd_upgrade.uc b/command/cmd_upgrade.uc deleted file mode 100644 index 1e2246c..0000000 --- a/command/cmd_upgrade.uc +++ /dev/null @@ -1,91 +0,0 @@ -let image_path = "/tmp/ucentral.upgrade"; - -if (!args.uri) { - result(2, "No firmware URL provided"); - return; -} - -let download_cmdline = [ 'wget', '-O', image_path, args.uri ]; -let rc = system(download_cmdline); - -if (rc != 0) { - result(2, "Download command %s exited with non-zero code %d", download_cmdline, rc); - - return; -} - -let validation_result = ctx.call("system", "validate_firmware_image", { path: image_path }); - -if (!validation_result) { - result(2, "Validation call failed with status %s", ubus.error()); - - return; -} -else if (!validation_result.valid) { - result_json({ - "error": 2, - "text": "Firmware image validation failed", - "data": sprintf("Archive command %s exited with non-zero code %d", archive_cmdline, rc) - }); - - warn(sprintf("ucentral-upgrade: firmware file validation failed: %J\n", validation_result)); - - return; -} - -if (restrict.sysupgrade) { - let signature = require('signature'); - if (!signature.verify(image_path, args.signature)) { - result_json({ - "error": 2, - "text": "Invalid signature", - "resultCode": -1 - }); - - return; - } -} - -let archive_cmdline = [ - 'tar', 'czf', '/upgrade.tgz', - '/etc/config/ucentral' -]; - -let files = [ - "/etc/ucentral/cas.pem", "/etc/ucentral/cert.pem", - "/etc/ucentral/redirector.json", "/etc/ucentral/dev-id", - "/etc/ucentral/key.pem", "/etc/ucentral/gateway.json", - "/etc/ucentral/profile.json", "/etc/ucentral/restrictions.json", -]; -for (let f in files) - if (fs.stat(f)) - push(archive_cmdline, f); - -if (args.keep_redirector) { - let active_config = fs.readlink("/etc/ucentral/ucentral.active"); - - if (active_config) - push(archive_cmdline, '/etc/ucentral/ucentral.active', active_config); - else - result(2, "Unable to determine active configuration: %s", fs.error()); -} - -let rc = system(archive_cmdline); - -if (rc != 0) { - result(2, "Archive command %s exited with non-zero code %d", archive_cmdline, rc); - - return; -} - -include('reboot_cause.uc', { reason: 'upgrade' }); - -let sysupgrade_cmdline = sprintf("sysupgrade %s %s", - args.keep_redirector ? "-f /upgrade.tgz" : "-n", - image_path); - -warn("Upgrading firmware\n"); - -system("touch /ucentral.upgrade"); -system("(sleep 10; /etc/init.d/network stop; " + sysupgrade_cmdline + ")&"); -system("/etc/init.d/ucentral stop"); diff --git a/command/cmd_wifiscan.uc b/command/cmd_wifiscan.uc deleted file mode 100644 index fcf0e1f..0000000 --- a/command/cmd_wifiscan.uc +++ /dev/null @@ -1,285 +0,0 @@ -let verbose = args?.verbose == null ? true : args.verbose; -let active = args?.active ? true : false; -let bandwidth = args?.bandwidth || 0; -let override_dfs = args?.override_dfs ? true : false; -let nl = require("nl80211"); -let rtnl = require("rtnl"); -let def = nl.const; - -if (!ctx) { - ubus = require("ubus"); - ctx = ubus.connect(); -} - -const SCAN_FLAG_AP = (1<<2); -const frequency_list_2g = [ 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 ]; -const frequency_list_5g = { '3': [ 5180, 5260, 5500, 5580, 5660, 5745 ], - '2': [ 5180, 5220, 5260, 5300, 5500, 5540, - 5580, 5620, 5660, 5745, 5785, 5825, - 5865, 5920, 5960 ], - '1': [ 5180, 5200, 5220, 5240, 5260, 5280, - 5300, 5320, 5500, 5520, 5540, 5560, - 5580, 5600, 5620, 5640, 5660, 5680, - 5700, 5720, 5745, 5765, 5785, 5805, - 5825, 5845, 5865, 5885 ], -}; -const frequency_offset = { '80': 30, '40': 10 }; -const frequency_width = { '80': 3, '40': 2, '20': 1 }; -const IFTYPE_STATION = 2; -const IFTYPE_AP = 3; -const IFTYPE_MESH = 7; -const IFF_UP = 1; - -function frequency_to_channel(freq) { - /* see 802.11-2007 17.3.8.3.2 and Annex J */ - if (freq == 2484) - return 14; - else if (freq < 2484) - return (freq - 2407) / 5; - else if (freq >= 4910 && freq <= 4980) - return (freq - 4000) / 5; - else if (freq < 5935) /* DMG band lower limit */ - return (freq - 5000) / 5; - else if (freq == 5935) - return 2; - else if (freq >= 5955 && freq <= 7115) - return ((freq - 5955) / 5) + 1; - else if (freq >= 58320 && freq <= 64800) - return (freq - 56160) / 2160; - return 0; -} - -function iface_get(wdev) { - let params = { dev: wdev }; - let res = nl.request(def.NL80211_CMD_GET_INTERFACE, wdev ? null : def.NLM_F_DUMP, wdev ? params : null); - - if (res === false) - warn("Unable to lookup interface: " + nl.error() + "\n"); - return res || []; -} - -function iface_find(wiphy, types, ifaces) { - if (!ifaces) - ifaces = iface_get(); - for (let iface in ifaces) { - if (iface.wiphy != wiphy) - continue; - if (iface.iftype in types) - return iface; - } - return; -} - -function scan_trigger(wdev, frequency, width) { - let params = { dev: wdev, scan_flags: SCAN_FLAG_AP }; - - if (frequency && type(frequency) == 'array') { - params.scan_frequencies = frequency; - } - else if (frequency && width) { - params.wiphy_freq = frequency; - params.center_freq1 = frequency + frequency_offset[width]; - params.channel_width = frequency_width[width]; - } - - if (active) - params.scan_ssids = [ '' ]; - - //printf("%.J\n", params); - let res = nl.request(def.NL80211_CMD_TRIGGER_SCAN, 0, params); - - if (res === false) - die("Unable to trigger scan: " + nl.error() + "\n"); - - else - res = nl.waitfor([ - def.NL80211_CMD_NEW_SCAN_RESULTS, - def.NL80211_CMD_SCAN_ABORTED - ], (frequency && width) ? 500 : 5000); - - if (!res) - warn("Netlink error while awaiting scan results: " + nl.error() + "\n"); - - else if (res.cmd == def.NL80211_CMD_SCAN_ABORTED) - warn("Scan aborted by kernel\n"); -} - -function trigger_scan_width(wdev, freqs, width) { - for (let freq in freqs) - scan_trigger(wdev, freq, width); -} - -function phy_get(wdev) { - let res = nl.request(def.NL80211_CMD_GET_WIPHY, def.NLM_F_DUMP, { split_wiphy_dump: true }); - - if (res === false) - warn("Unable to lookup phys: " + nl.error() + "\n"); - - return res; -} - -function phy_get_frequencies(phy) { - let freqs = []; - - for (let band in phy.wiphy_bands) { - for (let freq in band?.freqs || []) - if (!freq.disabled) - push(freqs, freq.freq); - } - return freqs; -} - -function phy_frequency_dfs(phy, curr) { - let freqs = []; - - for (let band in phy.wiphy_bands) { - for (let freq in band?.freqs || []) - if (freq.freq == curr && freq.dfs_state >= 0) - return true; - } - return false; -} - -let phys = phy_get(); -let ifaces = iface_get(); - -function intersect(list, filter) { - let res = []; - - for (let item in list) - if (index(filter, item) >= 0) - push(res, item); - return res; -} - -function wifi_scan() { - let scan = []; - - for (let phy in phys) { - let iface = iface_find(phy.wiphy, [ IFTYPE_STATION, IFTYPE_AP ], ifaces); - let scan_iface = false; - if (!iface) { - warn('no valid interface found for phy' + phy.wiphy + '\n'); - nl.request(def.NL80211_CMD_NEW_INTERFACE, 0, { wiphy: phy.wiphy, ifname: 'scan', iftype: IFTYPE_STATION }); - nl.waitfor([ def.NL80211_CMD_NEW_INTERFACE ], 1000); - scan_iface = true; - iface = { - dev: 'scan', - channel_width: 1, - }; - rtnl.request(rtnl.const.RTM_NEWLINK, 0, { dev: 'scan', flags: IFF_UP, change: 1}); - sleep(1000); - } - - printf("scanning on phy%d\n", phy.wiphy); - - let freqs = phy_get_frequencies(phy); - if (length(intersect(freqs, frequency_list_2g))) - scan_trigger(iface.dev, frequency_list_2g); - - let ch_width = iface.channel_width; - if (frequency_width[bandwith]) - ch_width = frequency_width[bandwith]; - let freqs_5g = intersect(freqs, frequency_list_5g[ch_width]); - if (length(freqs_5g)) { - if (override_dfs && !scan_iface && phy_frequency_dfs(phy, iface.wiphy_freq)) { - ctx.call(sprintf('hostapd.%s', iface.dev), 'switch_chan', { freq: 5180, bcn_count: 10 }); - sleep(2000) - } - trigger_scan_width(iface.dev, freqs_5g, ch_width); - } - let res = nl.request(def.NL80211_CMD_GET_SCAN, def.NLM_F_DUMP, { dev: iface.dev }); - for (let bss in res) { - bss = bss.bss; - let res = { - bssid: bss.bssid, - frequency: +bss.frequency, - channel: frequency_to_channel(+bss.frequency), - signal: +bss.signal_mbm / 100, - }; - if (verbose) { - res.tsf = +bss.tsf; - res.last_seen = +bss.seen_ms_ago; - res.capability = +bss.capability; - res.ies = []; - } - - - for (let ie in bss.beacon_ies) { - switch (ie.type) { - case 0: - res.ssid = ie.data; - break; - case 114: - if (verbose) - res.meshid = ie.data; - break; - case 0x3d: - if (verbose) - res.ht_oper = b64enc(ie.data); - break; - case 0xc0: - if (verbose) - res.vht_oper = b64enc(ie.data); - break; - case 0xdd: - let oui = hexenc(substr(ie.data, 0, 3)); - let type = ord(ie.data, 3); - let data = substr(ie.data, 4); - switch (oui) { - case '48d017': - res.tip_oui = true; - switch(type) { - case 1: - if (data) - res.tip_name = data; - break; - case 2: - if (data) - res.tip_serial = data; - break; - case 3: - if (data) - res.tip_network_id = data; - break; - } - break; - } - break; - default: - if (verbose) - push(res.ies, { type: ie.type, data: b64enc(ie.data) }); - break; - } - } - - if (args.periodic && !args.information_elements) - delete res.ies; - push(scan, res); - } - if (scan_iface) { - warn('removing temporary interface\n'); - nl.request(def.NL80211_CMD_DEL_INTERFACE, 0, { dev: 'scan' }); - } - } - printf("%.J\n", scan); - return scan; -} - -let scan = wifi_scan(); - -if (args.periodic) { - ctx.call('ucentral', 'send', { - method: 'wifiscan', - params: { data: scan } - }); - return; -} - -result_json({ - error: 0, - text: "Success", - resultCode: 1, - scan, -}); diff --git a/docs/schema_doc.css b/docs/schema_doc.css new file mode 100644 index 0000000..e1f3a51 --- /dev/null +++ b/docs/schema_doc.css @@ -0,0 +1,181 @@ +body { + font: 16px/1.5em "Overpass", "Open Sans", Helvetica, sans-serif; + color: #333; + font-weight: 300; + padding: 40px; +} + +.btn.btn-link { + font-size: 18px; + user-select: text; +} + +.jsfh-animated-property { + animation: eclair; + animation-iteration-count: 1; + animation-fill-mode: forwards; + animation-duration: .75s; + +} + +@keyframes eclair { + 0%,100% { + transform: scale(1); + } + 50% { + transform: scale(1.03); + } +} + +.btn.btn-primary { + margin: 10px; +} + +.btn.example-show.collapsed:before { + content: "show" +} + +.btn.example-show:before { + content: "hide" +} + +.description.collapse:not(.show) { + max-height: 100px !important; + overflow: hidden; + + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +.description.collapsing { + min-height: 100px !important; +} + +.collapse-description-link.collapsed:after { + content: '+ Read More'; +} + +.collapse-description-link:not(.collapsed):after { + content: '- Read Less'; +} + +.badge { + font-size: 100%; + margin-bottom: 0.5rem; + margin-top: 0.5rem; +} + +.badge.value-type { + font-size: 120%; + margin-right: 5px; + margin-bottom: 10px; +} + + +.badge.default-value { + font-size: 120%; + margin-left: 5px; + margin-bottom: 10px; +} + +.badge.restriction { + display: inline-block; +} + +.badge.required-property,.badge.deprecated-property,.badge.pattern-property,.badge.no-additional { + font-size: 100%; + margin-left: 10px; +} + +.accordion div.card:only-child { + border-bottom: 1px solid rgba(0, 0, 0, 0.125); +} + +.examples { + padding: 1rem !important; +} + +.examples pre { + margin-bottom: 0; +} + +.highlight.jumbotron { + padding: 1rem !important; +} + +.generated-by-footer { + margin-top: 1em; + text-align: right; +} + +/* From https://github.com/richleland/pygments-css/blob/master/friendly.css, see https://github.com/trentm/python-markdown2/wiki/fenced-code-blocks */ +.highlight { background: #e9ecef; } /* Changed from #f0f0f0 in the original style to be the same as bootstrap's jumbotron */ +.highlight .hll { background-color: #ffffcc } +.highlight .c { color: #60a0b0; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #007020; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #007020 } /* Comment.Preproc */ +.highlight .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #007020 } /* Keyword.Pseudo */ +.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #902000 } /* Keyword.Type */ +.highlight .m { color: #40a070 } /* Literal.Number */ +.highlight .s { color: #4070a0 } /* Literal.String */ +.highlight .na { color: #4070a0 } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.highlight .no { color: #60add5 } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #007020 } /* Name.Exception */ +.highlight .nf { color: #06287e } /* Name.Function */ +.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #bb60d5 } /* Name.Variable */ +.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #40a070 } /* Literal.Number.Bin */ +.highlight .mf { color: #40a070 } /* Literal.Number.Float */ +.highlight .mh { color: #40a070 } /* Literal.Number.Hex */ +.highlight .mi { color: #40a070 } /* Literal.Number.Integer */ +.highlight .mo { color: #40a070 } /* Literal.Number.Oct */ +.highlight .sa { color: #4070a0 } /* Literal.String.Affix */ +.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ +.highlight .sc { color: #4070a0 } /* Literal.String.Char */ +.highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ +.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ +.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ +.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.highlight .sx { color: #c65d09 } /* Literal.String.Other */ +.highlight .sr { color: #235388 } /* Literal.String.Regex */ +.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ +.highlight .ss { color: #517918 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #06287e } /* Name.Function.Magic */ +.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ +.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ +.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ +.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ +.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ diff --git a/docs/schema_doc.min.js b/docs/schema_doc.min.js new file mode 100644 index 0000000..17eceaf --- /dev/null +++ b/docs/schema_doc.min.js @@ -0,0 +1 @@ +$(document).on("click",'a[href^="#"]',function(event){event.preventDefault();history.pushState({},"",this.href)});function flashElement(elementId){myElement=document.getElementById(elementId);myElement.classList.add("jsfh-animated-property");setTimeout(function(){myElement.classList.remove("jsfh-animated-property")},1e3)}function setAnchor(anchorLinkDestination){history.pushState({},"",anchorLinkDestination)}function anchorOnLoad(){let linkTarget=decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);if(linkTarget[0]==="#"){linkTarget=linkTarget.substr(1)}if(linkTarget.length>0){anchorLink(linkTarget)}}function anchorLink(linkTarget){const target=$("#"+linkTarget);target.parents().addBack().filter(".collapse:not(.show), .tab-pane, [role='tab']").each(function(index){if($(this).hasClass("collapse")){$(this).collapse("show")}else if($(this).hasClass("tab-pane")){const tabToShow=$("a[href='#"+$(this).attr("id")+"']");if(tabToShow){tabToShow.tab("show")}}else if($(this).attr("role")==="tab"){$(this).tab("show")}});setTimeout(function(){let targetElement=document.getElementById(linkTarget);if(targetElement){targetElement.scrollIntoView({block:"center",behavior:"smooth"});setTimeout(function(){flashElement(linkTarget)},500)}},1e3)} \ No newline at end of file diff --git a/docs/ucentral-schema.html b/docs/ucentral-schema.html new file mode 100644 index 0000000..fa27592 --- /dev/null +++ b/docs/ucentral-schema.html @@ -0,0 +1,117 @@ + Schema Docs
Type: object

OpenWrt uCentral schema

Type: boolean Default: false

The device will reject any configuration that causes warnings if strict mode is enabled.

Type: integer

The unique ID of the configuration. This is the unix timestamp of when the config was created.

Type: stringFormat: uc-fqdn

The fqdn to retrieve public ip of internet connection.

Type: object

A device has certain properties that describe its identity and location. These properties are described inside this object.

Type: string

This is a free text field, stating the administrative name of the device. It may contain spaces and special characters.

Type: stringFormat: hostname

The hostname that shall be set on the device. If this field is not set, then the devices serial number is used.

Type: string

This is a free text field, stating the location of the device. It may contain spaces and special characters.

Type: string

This allows you to change the TZ of the device.


Examples:

"UTC"
+
"EST5"
+
"CET-1CEST,M3.5.0,M10.5.0/3"
+

Type: boolean Default: true

This allows forcing all LEDs off.

Type: boolean Default: false

The device shall create a random root password and tell the gateway about it.

Type: string

System-config string that holds the password for main (root / admin) user to apply.

Type: object

The TIP vendor IEs that shall be added to beacons

Type: boolean

Add an IE containing the device's name to beacons.

Type: boolean

Add an IE containing the device's serial to beacons.

Type: integer

A provider specific ID for the network/venue that the device is part of.

Type: object

This section describes the system-wide (unit) PoE controller configuration object.

Type: string

This configuration mode controls the power management algorithm used by the Power sourcing equipment to deliver power to the requesting PDs. "class" option - Class-based power management. "dynamic" option - Power management is done by the POE controller and the maximum power for a port is not reserved for each port. "static" option - The power deducted from the total power pool is the maximum power for that port. This mode ensures that the maximum power specified by you for the interface is always reserved and cannot be shared by other PDs.


Examples:

"class"
+
"dynamic"
+
"dynamic-priority"
+
"static"
+
"static-priority"
+

Type: number Default: 90

Configure a power alarm threshold for the Power sourcing equipment (in percentages %).

Type: object

This sections describes the system-wide (unit) multicast configuration object.

Type: boolean Default: true

Global config for controlling whether IGMP snooping is enabled. If this global setting is disabled, all VLANs are treated as disabled, whether they are enabled or not.

Type: boolean Default: true

Global config for controlling whether MLD snooping is enabled. If this global setting is disabled, all VLANs are treated as disabled, whether they are enabled or not.

Type: boolean Default: false

Global config for the unknown multicast flood control feature. This enables the system to forward unknown multicast packets only to a multicast router (mrouter).

Type: boolean Default: false

Global IGMP querier config. This enables all Vlan interfaces to act as a querier.

Type: object

A device has certain global properties that are used to derive parts of the final configuration that gets applied.

Type: stringFormat: uc-cidr4

Define the IPv4 range that is delegatable to the downstream interfaces This is described as a CIDR block. (192.168.0.0/16, 172.16.128/17)


Example:

"192.168.0.0/16"
+

Type: stringFormat: uc-cidr6

Define the IPv6 range that is delegatable to the downstream interfaces This is described as a CIDR block. (fdca:1234:4567::/48)


Example:

"fdca:1234:4567::/48"
+

Type: array of object

Define a list of non-interface specific BLACKHOLE (to-nowhere) routes.

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: uc-cidr4

Defines a BLACKHOLE route's prefix.


Example:

"192.168.1.0/24"
+

Type: integer

VRF id.

Type: array of object

Define a list of non-interface specific UNREACHABLE routes.

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: uc-cidr4

Defines a UNREACHABLE route's prefix.


Example:

"192.168.1.0/24"
+

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section defines the linkk speed and duplex mode of the physical copper/fiber ports of the device.

Type: array of string

The list of physical network devices that shall be configured. The names are logical ones and wildcardable.

No Additional Items

Each item of this array must be:

Type: string

Examples:

"LAN1"
+
"LAN2"
+
"LAN3"
+
"LAN4"
+
"LAN*"
+
"WAN*"
+
"*"
+

Type: string

This is a free text field, stating the administrative name of the port. It may contain spaces and special characters, not exceeding 64 characters.


Example:

"cloud_uplink_port"
+

Type: enum (of integer) Default: 1000

The link speed that shall be forced.

Must be one of:

  • 10
  • 100
  • 1000
  • 2500
  • 5000
  • 10000
  • 25000
  • 40000
  • 50000
  • 100000
  • 200000

Type: enum (of string) Default: "full"

The duplex mode that shall be forced.

Must be one of:

  • "half"
  • "full"

Type: boolean Default: true

This allows forcing the port to down state by default.

Type: array of string

The services that shall be offered on this L2 interface.

No Additional Items

Each item of this array must be:

Type: string

Example:

"quality-of-service"
+

Type: object

This section describes the ethernet poe-port configuration object.

Type: boolean Default: false

Option to force admin state over selected port. Setting to <false> immediately shuts down power. Setting to <true> starts PoE hanshake (Power sourcing equipment < - > Powered Device) sequence and in case of success, power is being delivered to Powered Device.

Type: boolean

Option to force device's PSE (Power sourcing equipment) to invoke a PoE port reset sequence. This option can be used to reset PoE port without flickering it via <admin-mode> down/up sequence.

Type: string Default: "dot3bt"

The detection mode is used to set the type of devices that are allowed for powering up. The PoE controller can be configured to detect only IEEE standard devices or pre-IEEE legacy devices (which were pre-standard - non-IEEE 802.3af compliant). For example, if "dot3af" is used (PoE, max up to 15.4 W), and Powered Device drains >15.4W, Power sourcing equipment won't allow this port to drain power.


Examples:

"2pt-dot3af"
+
"2pt-dot3af+legacy"
+
"4pt-dot3af"
+
"4pt-dot3af+legacy"
+
"dot3bt"
+
"dot3bt+legacy"
+
"legacy"
+

Type: integer Default: 99900

Option to configure user defined absolute power limit PoE port can dain (in milliwatts, mW).

Type: string Default: "low"

Option to set priority to each PoE port. When the PoE switch has less power available and more ports are required to supply power, higher priority ports are receive power in preference to lower priority ports.


Examples:

"critical"
+
"high"
+
"medium"
+
"low"
+

Type: object

This section describes the per-port specific 802.1X (port access control) configuration.

Type: boolean Default: false

Configure PAE processing on port, as well as select this port as an Authenticator (configure PAC role to authenticator). False configures the switch to not process PAC

Type: enum (of string) Default: "force-authorized"

Configure PAE processing on port, as well as select this port as an Authenticator (configure PAC role to authenticator). force-authorized - Disables IEEE 802.1X authentication and causes the port to change to the authorized state without any authentication exchange required. The port sends and receives normal traffic without IEEE 802.1X-based authentication of the client. force-unauthorized - Causes the port to remain in the unauthorized state, ignoring all attempts by the supplicant to authenticate. The Device cannot provide authentication services to the supplicant through the port. auto - Enables IEEE 802.1X authentication and causes the port to begin in the unauthorized state, allowing only EAPOL frames to be sent and received through the port. The authentication process begins when the link state of the port changes from down to up or when an EAPOL-start frame is received. The Device requests the identity of the supplicant and begins relaying authentication messages between the supplicant and the authentication server. Each supplicant attempting to access the network is uniquely identified by the Device by using the supplicant MAC address.

Must be one of:

  • "force-authorized"
  • "force-unauthorized"
  • "auto"

Type: enum (of string) Default: "multi-auth"

Multi-auth - While in this mode, multiple devices are allowed to independently authenticate through the same port.
Multi-domain - While in this mode, the authenticator will allow one host from the data domain and one from the voice domain.
Multi-host - While in this mode, the first device to authenticate will open to the switchport so that all other devices can use the port. These other devices are not required to be authenticated independently.
Single-host - While in this mode, the switchport will only allow a single host to be authenticated and to pass traffic at a time.

Must be one of:

  • "multi-auth"
  • "multi-domain"
  • "multi-host"
  • "single-host"

Type: integer

Configure a VLAN as a guest VLAN on an interface if the switch receives no response in an authentication event.

Value must be greater or equal to 1 and lesser or equal to 4094

Type: integer

Configure the unauthenticated VLAN to use when the AAA server fails to recognize the client credentials

Value must be greater or equal to 1 and lesser or equal to 4094

Type: boolean

Enables bypass when a device does not support 802.1X authentication (e.g., printers, IP phones)

Type: integer

Defines the time period (in minutes) for which a MAC address is allowed access to the network without requiring reauthentication, after being authenticated or allowed via MAC Authentication Bypass (MAB).

Type: integer

Associates this port to a trunk or a port-channel.

Value must be greater or equal to 1 and lesser or equal to 64

Type: object

This section describes the 802.3ad Link Aggregation Control Protocol (LACP) configuration for the current interface.

Type: boolean Default: false

Enables 802.3ad Link Aggregation Control Protocol (LACP) for the current interface.

Type: enum (of string) Default: "actor"

Configures the port LACP role as actor or partner.

Must be one of:

  • "actor"
  • "partner"

Type: enum (of string) Default: "passive"

Configures the LACP negotiation activity mode as active or passive.

Must be one of:

  • "active"
  • "passive"

Type: integer Default: 1

Configures the port's LACP administration key.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: integer Default: 32768

Configures the LACP port priority.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: integer Default: 32768

Configures the LACP System priority.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: integer

Configures the port channel's LACP administration key (optional).

Value must be greater or equal to 1 and lesser or equal to 65535

Type: enum (of string) Default: "long"

Configures the timeout to wait for the next LACP data unit.

Must be one of:

  • "short"
  • "long"

Type: object

Configurations of LLDP on a specified interface.

Type: enum (of string)

Enables LLDP transmit, receive, or transmit and receive mode on the specified port.

Must be one of:

  • "rx"
  • "tx"
  • "rx-tx"

Type: boolean Default: true

Configures an LLDP-enabled port to advertise the management address for this device.

Type: boolean Default: false

Configures an LLDP-enabled port to advertise the management IPv6 address for this device, if available.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its port description.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its system capabilities.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise the system description.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its system name.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise the supported protocols.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise port-based protocol-related VLAN information.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its default Native VLAN ID (PVID).

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its VLAN name.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its MAC and physical layer specifications.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its maximum frame size.

Type: boolean Default: true

Configures an LLDP-enabled port to advertise its Power-over-Ethernet capabilities.

Type: object

Configures an LLDP-MED-enabled port to advertise its location identification details.

Type: boolean Default: false

Enables or disables the advertisement of this TLV.

Type: string

Configure the two-letter ISO 3166 country code in capital ASCII letters.

Type: integer

The type of device to which the location applies.

Type: array of object

The list of LLDP MED Location CA Types to advertise the physical location of the device, that is the city, street number, building and room information.

No Additional Items

Each item of this array must be:

Type: object

Type: integer

A one-octet descriptor of the data civic address value.

Value must be greater or equal to 0 and lesser or equal to 255

Type: string

Description of a location.

Must be at least 1 characters long

Must be at most 32 characters long

Type: boolean Default: false

Enables the transmission of SNMP trap notifications about LLDP-MED changes.

Type: boolean Default: true

Configures an LLDP-MED-enabled port to advertise its extended Power over Ethernet configuration and usage information.

Type: boolean Default: true

Configures an LLDP-MED-enabled port to advertise its inventory identification details.

Type: boolean Default: true

Configures an LLDP-MED-enabled port to advertise its location identification details.

Type: boolean Default: true

Configures an LLDP-MED-enabled port to advertise its Media Endpoint Device capabilities.

Type: boolean Default: true

Configures an LLDP-MED-enabled port to advertise its network policy configuration.

Type: boolean Default: false

Enables the transmission of SNMP trap notifications about LLDP changes.

Type: object

This section defines the switch fabric specific features of a physical switch.

Type: array of object

Enable mirror of traffic from multiple minotor ports to a single analysis port.

No Additional Items

Each item of this array must be:

Type: object

Type: array of string

The list of ports that we want to mirror.

No Additional Items

Each item of this array must be:

Type: string

The port that mirror'ed packets should be sent to.

Type: object

Enable loop detection on the L2 switches/bridge.

Type: enum (of string) Default: "rstp"

Define which protocol shall be used for loop detection.

Must be one of:

  • "none"
  • "stp"
  • "rstp"
  • "mstp"
  • "pvstp"
  • "rpvstp"

Type: array of enum (of string)

Define on which logical switches/bridges we want to provide loop-detection.

No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "upstream"
  • "downstream"

Type: array of object

Define a list of configuration for each STP instance. Meaning of this field depends on current STP protocol (switch.loop-detection.protocol)

No Additional Items

Each item of this array must be:

Type: object

Type: integer

Indicates instance to configure. Depends on current STP protocol If RPVSTP/PVSTP - vlan id If MSTP - instance id

Type: boolean Default: true

Enable STP on this instance.

Type: integer Default: 32768

Bridge priority.

Type: integer Default: 15

Defines the amount of time a switch port stays in the Listening and Learning states before transitioning to the Forwarding state.

Type: integer Default: 2

Determines how often switches send BPDU.

Type: integer Default: 20

Specifies the maximum time that a switch port should wait to receive a BPDU from its neighbor before considering the link as failed or disconnected.

Type: object

This section describes the global 802.1X (port access control) configuration.

Type: boolean Default: false

Enabled processing of PAE frames on ports that have .1X configured.

Type: array of object

Define a list of RADIUS server to forward auth requests to.

No Additional Items

Each item of this array must be:

Type: object

Type: string

Remote radius server address (IP or hostname).


Examples:

"192.168.1.1"
+
"somehost.com"
+

Type: integer

The port that the RADIUS authentication agent is running on.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: string

Secret key text that is shared between a RADIUS server and the switch.


Example:

"somepassword"
+

Type: integer

The server's priority (used when multiple servers are present. Bigger prio value = higher priority).

Value must be greater or equal to 1 and lesser or equal to 64

Type: object

Additional dynamic authorization (RFC 5176 compliant) - configure option for DAS that enable RM and CoA processing.

Type: enum (of string)

Sets the accepted authorization types for dynamic RADIUS clients. all - Selects all COA client authentication types. All authentication attributes must match for the authentication to succeed. any - Selects any COA client authentication type. Any authentication attribute may match for the authentication to succeed. session-key - Indicates that the session-key must match for authentication to succeed.

Must be one of:

  • "all"
  • "any"
  • "session-key"

Type: boolean Default: false

Sets the switch to ignore bounce-port requests from dynamic authorization clients.

Type: boolean Default: false

Sets the switch to ignore requests from dynamic authorization clients.

Type: boolean Default: false

Do not attmept to authenticate with the server key.

Type: boolean Default: false

Do not attmept to authenticate with the session key.

Type: string

Sets the shared secret to verify client COA requests for this server.

Type: array of object

Configure DAC.

No Additional Items

Each item of this array must be:

Type: object

Type: string

A valid IP address or hostname of a DAC.

Type: string

Sets the shared secret to verify client COA requests for this server.

Type: object

This section describes the per-port specific port-isolation matrix (to which ports selected port can forward traffic to) configuration. Omitting this configuration completely fully disables any port-isolation configuration on this given port.

Type: array of object

Allow selected port to forward traffic in the provided session-based format.

No Additional Items

Each item of this array must be:

Type: object

Type: integer

Session id to configure.

Type: enum (of string) Default: "src-dst-mac"

Sets the load-distribution method among ports in aggregated links for both static and LACP based trunks.

Must be one of:

  • "dst-ip"
  • "dst-mac"
  • "src-dst-ip"
  • "src-dst-mac"
  • "src-ip"
  • "src-mac"

Type: boolean Default: false

Enables Jumbo frames

Type: object

DHCP Snooping configuration parameters

Type: boolean Default: false

Enables DHCP Snooping on the network switch, which is a security feature that prevents unauthorized DHCP servers from offering IP addresses

Type: integer

Sets a limit on the number of DHCP packets per second that can be received on an untrusted interface to prevent DHCP flooding attacks

Value must be greater or equal to 1 and lesser or equal to 2048

Type: boolean Default: false

This option ensures that the MAC address in a DHCP request matches the source MAC address of the packet, providing an additional layer of security

Type: boolean Default: false

This refers to the insertion of information option 82 in DHCP packets, which adds more details about the client’s location and network information for tracking and control purposes

Type: boolean Default: false

This parameter allows for the encoding of sub-options within option 82 to further specify client information

Type: string

It specifies the remote ID sub-option in option 82, which typically includes information like the circuit ID or remote host identifier

Must be at least 1 characters long

Must be at most 32 characters long

Type: enum (of string)

This defines the policy for handling packets with option 82, determining whether they should be forwarded or dropped based on the configuration

Must be one of:

  • "drop"
  • "keep"
  • "replace"

Type: array of object

Contains all the access control rule definitions

No Additional Items

Each item of this array must be:

Type: object

Type: enum (of string)

Type of the access control list

Must be one of:

  • "ipv4"
  • "ipv6"
  • "ipv4Ext"
  • "ipv6Ext"
  • "mac"
  • "arp"

Type: string

The identifier or name for the Access Control List

Must be at least 1 characters long

Must be at most 32 characters long

Type: enum (of string)

Defines whether to permit or deny traffic matching the rule

Must be one of:

  • "permit"
  • "deny"

Type: stringFormat: uc-mac

Specifies the source MAC address to filter on

Type: stringFormat: uc-mac

The mask applied to the source MAC address

Type: stringFormat: uc-mac

Specifies the destination MAC address for the filter

Type: stringFormat: uc-mac

The mask applied to the destination MAC address

Type: enum (of string)

Identifies the protocol encapsulated in the Ethernet frame by its EtherType

Must be one of:

  • "any"
  • "untagged-eth2"
  • "untagged-802.3"
  • "tagged-eth2"
  • "tagged-802.3"

Type: integer

Specifies a VLAN ID to filter traffic from a specific VLAN

Value must be greater or equal to 1 and lesser or equal to 4094

Type: integer

The mask applied to the VLAN ID

Value must be greater or equal to 1 and lesser or equal to 4095

Type: string Default: "800"

Filters packets based on the custom EtherType field (HEX) in the Ethernet frame

Type: string Default: "FFFF"

The mask applied to the EtherType field

Type: integer

Filters based on the Class of Service (CoS) field in the frame

Value must be greater or equal to 0 and lesser or equal to 7

Type: integer

The mask applied to the CoS field

Value must be greater or equal to 0 and lesser or equal to 7

Type: stringFormat: ipv4

The IPv4 address of the source to filter on

Type: stringFormat: ipv4

The subnet mask applied to the source IPv4 address

Type: stringFormat: ipv4

The IPv4 address of the destination to filter on

Type: stringFormat: ipv4

The subnet mask applied to the destination IPv4 address

Type: stringFormat: ipv6

The IPv6 address of the source to filter on

Type: integer

Defines the length of the prefix used in filtering IPv6 source addresses

Value must be greater or equal to 0 and lesser or equal to 128

Type: stringFormat: ipv6

The IPv6 address of the destination to filter on

Type: integer

Defines the length of the prefix used in filtering IPv6 destination addresses

Value must be greater or equal to 0 and lesser or equal to 128

Type: integer

Filters based on the IP protocol number

Value must be greater or equal to 0 and lesser or equal to 255

Type: integer

Specifies the next header type in IPv6 traffic, such as TCP, UDP, or ICMPv6

Value must be greater or equal to 0 and lesser or equal to 255

Type: integer

Filters packets based on the IPv6 Flow Label field

Value must be greater or equal to 0 and lesser or equal to 1048575

Type: integer

Specifies the source port number for filtering

Value must be greater or equal to 0 and lesser or equal to 65535

Type: integer

The mask applied to the source port number

Value must be greater or equal to 0 and lesser or equal to 65535

Type: integer

Specifies the destination port number for filtering

Value must be greater or equal to 0 and lesser or equal to 65535

Type: integer

The mask applied to the destination port number

Value must be greater or equal to 0 and lesser or equal to 65535

Type: integer

Filters based on IPv4 fragment offset for fragmented packets

Value must be greater or equal to 0 and lesser or equal to 8191

Type: integer

Filters based on IPv6 fragment offset for fragmented packets

Value must be greater or equal to 0 and lesser or equal to 8191

Type: integer

Filters IPv4 traffic based on the TTL (Time to Live) value

Value must be greater or equal to 0 and lesser or equal to 255

Type: integer

Filters IPv6 traffic based on the hop limit value, equivalent to IPv4 TTL

Value must be greater or equal to 0 and lesser or equal to 255

Type: object

This section defines the Multicast VLAN Registration (MVR) general configuration.

Type: boolean Default: false

Enable/Disable MVR globally on the switch.

Type: integer Default: 125

This command configures the interval (in seconds) at which the receiver port sends out general queries. The maximum value is determined based on 12 hours as maximum interval, and minimum as 1 second as allowed value.

Value must be greater or equal to 1 and lesser or equal to 43200

Type: boolean Default: false

Enable the MVR proxy switching mode, where the source port acts as a host, and the receiver port acts as an MVR router with querier service enabled.

Type: integer Default: 2

Configure the expected packet loss, and thereby the number of times to generate report and group-specific queries when changes are learned about downstream groups, and the number of times group-specific queries are sent to downstream receiver ports. Right configuration ensures that multicast group memberships are correctly maintained even if some control messages are lost due to network issues.

Value must be greater or equal to 1 and lesser or equal to 255

Type: enum (of string) Default: "forward"

Configure the switch to forward only multicast streams that a source port has dynamically joined or to forward all multicast groups.

Must be one of:

  • "dynamic"
  • "forward"

Type: array of object

Configure the Multicast VLAN Registration (MVR) domains.

No Additional Items

Each item of this array must be:

Type: object

Type: integer Default: 1

Unique identifier for a Multicast Domain defined under the MVR.

Value must be greater or equal to 1 and lesser or equal to 10

Type: boolean Default: false

Enable/disable Multicast VLAN Registration (MVR) for a specific domain.

Type: integer Default: 1

Per domain Level Multicast VLAN ID. Specifies the VLAN through which MVR multicast data is received. This is the VLAN to which all source ports must be assigned.

Value must be greater or equal to 1 and lesser or equal to 4094

Type: stringFormat: ipv4

Configures the source IP address assigned to all MVR control packets sent upstream on all domains or on a specified domain.


Example:

"192.168.0.5"
+

Type: array of object

List of MVR groups (or profiles) configuration.

No Additional Items

Each item of this array must be:

Type: object

Type: string

The name of a MVR group that consists of one or more MVR group addresses

Must be at least 1 characters long

Must be at most 16 characters long

Type: stringFormat: ipv4

Start IP address on the range of MVR group addresses that maps to a profile/MVR group

Type: stringFormat: ipv4

Statically configure all multicast group addresses that will join an MVR VLAN. Map a range of MVR group addresses to a profile

Type: array of integer
No Additional Items

Each item of this array must be:

Type: integer

Value must be greater or equal to 1 and lesser or equal to 10

Type: object

Configuration options for LLDP on a global level in a OLS switch.

Type: boolean Default: true

Enables or disables LLDP globally at a switch level.

Type: integer Default: 4

Configures the time-to-live (TTL) value sent in LLDP advertisements. The TTL tells the receiving LLDP agent how long to retain all information from the sending LLDP agent if it does not transmit updates in a timely manner.

Type: integer Default: 4

Configures how many medFastStart packets are transmitted during the activation process of the LLDP-MED Fast Start mechanism.

Type: integer Default: 30

Configures the periodic transmit interval for LLDP advertisements (in seconds).

Type: integer Default: 2

Configures the delay (in seconds) before reinitializing after LLDP ports are disabled or the link goes down.

Type: integer

Configures a delay (in seconds) between successive transmissions of advertisements initiated by a change in local LLDP state.

Value must be greater or equal to 1 and lesser or equal to 8192

Type: integer Default: 5

Configures the interval (in seconds) for sending SNMP notifications about LLDP changes.

Type: boolean Default: false

Enables MC-LAG or disables it.

Type: object

This section defines the MC-LAG configuration parameters for the switch.

Type: array of object

List of MC-LAG domain configurations for the switch.

No Additional Items

Each item of this array must be:

Type: object

Type: integer Default: 1

Specifies the MC-LAG domain ID to identify the grouping of peer switches.

Value must be greater or equal to 1 and lesser or equal to 1024

Type: object

Configures the MC-LAG group, which binds the interfaces into a multi-chassis LAG.

Type: integer

Defines the unique MC-LAG group identifier.

Value must be greater or equal to 1 and lesser or equal to 128

Type: array of string

List of interfaces that participate in the MC-LAG group.

No Additional Items

Each item of this array must be:

Type: string

Interface names that are part of the MC-LAG group.


Examples:

"eth0"
+
"eth1"
+

Type: object

LACP configuration settings for the MC-LAG group.

Type: boolean Default: true

Enables or disables LACP for the MC-LAG group.

Type: enum (of string) Default: "actor"

Configures the LACP role as 'actor' or 'partner'

Must be one of:

  • "actor"
  • "partner"

Type: enum (of string) Default: "long"

Sets the LACP timeout as either 'short' or 'long'.

Must be one of:

  • "short"
  • "long"

Type: integer Default: 32768

Specifies the system priority used by the switch for LACP negotiations.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: boolean Default: true

Enables dual-active detection to prevent split-brain scenarios in MC-LAG.

Type: object

This parameter enables or disables the overall configuration of the Voice VLAN feature on the switch. When enabled, it allows the system to classify and prioritize voice traffic.

Type: integer

Specifies the VLAN ID assigned to the Voice VLAN. This is the unique identifier for the VLAN that will be used for prioritizing voice traffic.

Value must be greater or equal to 1 and lesser or equal to 4094

Type: integer Default: 1440

Defines the time, in minutes, that a dynamic Voice VLAN entry remains in the VLAN after voice traffic is no longer detected. It helps manage resources by removing inactive voice devices from the VLAN after this time elapses.

Value must be greater or equal to 5 and lesser or equal to 43200

Type: array of object

Configures the Organizationally Unique Identifier (OUI) for identifying the voice devices (like IP phones).

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: uc-mac

The specific MAC address pattern that corresponds to voice devices, as determined by the OUI. It is used for identifying and classifying voice traffic.

Type: stringFormat: uc-mac

A mask applied to the MAC address to help match the OUI more precisely. It ensures that the correct portion of the MAC address is evaluated to identify a device as a voice device.

Type: string

A descriptive label or comment for the OUI configuration. This can help administrators keep track of which OUI belongs to which type of voice device or vendor.

Must be at least 1 characters long

Must be at most 32 characters long


Example:

"A VoIP Phone"
+

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section describes the logical network interfaces of the device. Interfaces as their primary have a role that is upstream, downstream, guest, ....

Type: string

This is a free text field, stating the administrative name of the interface. It may contain spaces and special characters.


Example:

"LAN"
+

Type: enum (of string)

The role defines if the interface is upstream or downstream facing.

Must be one of:

  • "upstream"
  • "downstream"

Type: boolean

This option makes sure that any traffic leaving this interface is isolated and all local IP ranges are blocked. It essentially enforces "guest network" firewall settings.

Type: integer

The routing metric of this logical interface. Lower values have higher priority.

Value must be greater or equal to 0 and lesser or equal to 4294967295

Type: integer

The MTU of this logical interface.

Value must be greater or equal to 1280 and lesser or equal to 1500

Type: array of string

The services that shall be offered on this logical interface. These are just strings such as "ssh", "lldp", "mdns"

No Additional Items

Each item of this array must be:

Type: string

Examples:

"ssh"
+
"lldp"
+

Type: object

Setup additional VLANs inside the bridge

Type: object

This section describes the vlan behaviour of a logical network interface.

Type: integer

This is the pvid of the vlan that shall be assigned to the interface. The individual physical network devices contained within the interface need to be told explicitly if egress traffic shall be tagged.

Value must be lesser or equal to 4050

Type: enum (of string) Default: "802.1q"

Must be one of:

  • "802.1ad"
  • "802.1q"

Type: object

This section describes the bridge behaviour of a logical network interface.

Type: integer

The MTU that shall be used by the network interface.

Value must be greater or equal to 256 and lesser or equal to 65535


Example:

1500
+

Type: integer

The Transmit Queue Length is a TCP/IP stack network interface value that sets the number of packets allowed per kernel transmit queue of a network interface device.


Example:

5000
+

Type: boolean Default: false

Isolates the bridge ports from each other.

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section defines the physical copper/fiber ports that are members of the interface. Network devices are referenced by their logical names.

Type: array of string

The list of physical network devices that shall be added to the interface. The names are logical ones and wildcardable. "WAN" will use whatever the hardwares default upstream facing port is. "LANx" will use the "x'th" downstream facing ethernet port. LAN* will use all downstream ports.

No Additional Items

Each item of this array must be:

Type: string

Examples:

"LAN1"
+
"LAN2"
+
"LAN3"
+
"LAN4"
+
"LAN*"
+
"WAN*"
+
"*"
+

Type: boolean Default: true

Enable multicast support.

Type: boolean Default: true

Controls whether a given port will learn MAC addresses from received traffic or not. If learning if off, the bridge will end up flooding any traffic for which it has no FDB entry. By default this flag is on.

Type: boolean Default: false

Only allow communication with non-isolated bridge ports when enabled.

Type: stringFormat: uc-mac

Enforce a specific MAC to these ports.

Type: boolean Default: false

Reverse Path filtering is a method used by the Linux Kernel to help prevent attacks used by Spoofing IP Addresses.

Type: enum (of string) Default: "auto"

Shall the port have a vlan tag.

Must be one of:

  • "tagged"
  • "un-tagged"
  • "auto"

Type: object

This section describes the IPv4 properties of a logical interface.

Type: enum (of string)

This option defines the method by which the IPv4 address of the interface is chosen.

Must be one of:

  • "dynamic"
  • "static"
  • "none"

Example:

"static"
+

Type: array of object

This option defines a list of CONNECTED routes (with VRF id) in CIDR notation.

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: uc-cidr4

Defines a CONNECTED route's prefix (network).


Example:

"192.168.1.0/24"
+

Type: array of object

This option defines the static IPv4 gateway of the logical interface.

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: uc-cidr4

Defines a NEXTHOP route's prefix (network).


Example:

"192.168.1.0/24"
+

Type: stringFormat: ipv4

Gateway (nexthop) address.


Example:

"192.168.1.1"
+

Type: number

Optional metric value (define a NH route's weight / metric).

Type: array of object

This option defines a list of BROADCAST routes (with VRF id) in CIDR notation.

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: uc-cidr4

Defines a BROADCAST route's prefix (network).


Example:

"192.168.1.0/24"
+

Type: object

Type: boolean Default: true

The unknown multicast flood control feature enables the system to forward unknown multicast packets only to a multicast router (mrouter).

Type: object

Type: boolean Default: true

Enable or disable IGMP snooping on per-VLAN basis.

Type: enum (of integer) Default: 3

Configures the IGMP version. Configurable versions are IGMPv1, IGMPv2, and IGMPv3

Must be one of:

  • 1
  • 2
  • 3

Example:

3
+

Type: boolean Default: false

Configure this interface to act as a querier (multicast router)

Type: boolean Default: false

Removes the group state when it receives an IGMP Leave report without sending an IGMP query message

Type: integer Default: 1000

Defines the interval between sending IGMP general queries

Type: integer Default: 1000

Defines the maximum response time (milliseconds) advertised in IGMP group-specific queries

Type: integer Default: 10

Configures a query maximum response time (in seconds) that is advertised on IGMP queries.

Type: array of object

Configures a Layer 2 port of a VLAN as a static member of an IGMP multicast group(s).

No Additional Items

Each item of this array must be:

Type: object

Type: array of string

Specify egress port(s) to forward mcast traffc of static group to.

No Additional Items

Each item of this array must be:

Type: stringFormat: ipv4

Specify IPV4 address (group) this interface is statically configured to be member of.


Example:

"225.0.0.1"
+

Type: object

MVR attributes on a given interface

Type: enum (of string)

Configure an interface as an MVR receiver or source port. A port which is not configured as an MVR receiver or source port can use IGMP snooping to join or leave multicast groups using the standard rules for multicast filtering.

Must be one of:

  • "none"
  • "source"
  • "receiver"

Type: enum (of string) Default: "by-group"

Switch to immediately remove an interface from a multicast stream as soon as it receives a leave message for that group. Applies to only receiver role ports.

Must be one of:

  • "none"
  • "by-host-ip"
  • "by-group"

Type: integer

Map the port to a specific domain.

Value must be greater or equal to 1 and lesser or equal to 10

Type: boolean Default: true

include the devices hostname inside DHCP requests


Example:

true
+

Type: array of string

Define which DNS servers shall be used. This can either be a list of static IPv4 addresse or dhcp (use the server provided by the DHCP lease)

No Additional Items

Each item of this array must be:

Type: stringFormat: ipv4

Examples:

"8.8.8.8"
+
"4.4.4.4"
+

Type: boolean Default: false

Enables DHCP Snooping on a VLAN

Type: object

Configure the Voice VLAN feature at the interface level, allowing for VoIP traffic to be prioritized on this specific port.

Type: enum (of string) Default: "auto"

Specify the mode of placing this port on the voice VLAN.

Must be one of:

  • "none"
  • "manual"
  • "auto"

Type: integer Default: 6

Define the Class of Service (CoS) priority for VoIP traffic passing through this port, ensuring higher priority over other traffic types.

Value must be greater or equal to 0 and lesser or equal to 6

Type: enum (of string) Default: "oui"

Select the detection method for identifying VoIP traffic on this port, such as OUI-based detection or traffic pattern recognition.

Must be one of:

  • "oui"
  • "lldp"

Type: boolean Default: false

Enable or configure security filtering for VoIP traffic on the interface to protect against unauthorized devices.

Type: object

This section describes the DHCP server configuration

Type: integer

The last octet of the first IPv4 address in this DHCP pool.


Example:

10
+

Type: integer

The number of IPv4 addresses inside the DHCP pool.


Example:

100
+

Type: stringFormat: uc-timeout Default: "6h"

How long the lease is valid before a RENEW must be issued.

Type: stringFormat: ipv4

Use host at this IPv4 address to forward packets between clients and servers on different subnets.

Type: string

This option selects what info shall be contained within a relayed frame's circuit ID. The string passed in has placeholders that are placed inside a bracket pair "{}". Any text not contained within brackets will be included as freetext. Valid placeholders are "Interface, VLAN-ID"

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section describes the static DHCP leases of this logical interface.

Type: stringFormat: uc-mac

The MAC address of the host that this lease shall be used for.


Example:

"00:11:22:33:44:55"
+

Type: integer

The offset of the IP that shall be used in relation to the first IP in the available range.


Example:

10
+

Type: stringFormat: uc-timeout Default: "6h"

How long the lease is valid before a RENEW muss ne issued.

Type: boolean Default: true

Shall the hosts hostname be made available locally via DNS.

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section describes an IPv4 port forwarding.

Type: enum (of string) Default: "any"

The layer 3 protocol to match.

Must be one of:

  • "tcp"
  • "udp"
  • "any"

Type: integer or stringFormat: uc-portrange

The external port(s) to forward.

Type: stringFormat: ipv4

The internal IP to forward to. The address will be masked and concatenated with the effective interface subnet.

Type: integer or stringFormat: uc-portrange

The internal port to forward to. Defaults to the external port if omitted.

Type: object

This section describes the IPv6 properties of a logical interface.

Type: enum (of string)

This option defines the method by which the IPv6 subnet of the interface is acquired. In static addressing mode, the specified subnet and gateway, if any, are configured on the interface in a fixed manner. Also - if a prefix size hint is specified - a prefix of the given size is allocated from each upstream received prefix delegation pool and assigned to the interface. In dynamic addressing mode, a DHCPv6 client will be launched to obtain IPv6 prefixes for the interface itself and for downstream delegation. Note that dynamic addressing usually only ever makes sense on upstream interfaces.

Must be one of:

  • "dynamic"
  • "static"

Type: stringFormat: uc-cidr6

This option defines a static IPv6 prefix in CIDR notation to set on the logical interface. A special notation "auto/64" can be used, causing the configuration agent to automatically allocate a suitable prefix from the IPv6 address pool specified in globals.ipv6-network. This property only applies to static addressing mode. Note that this is usually not needed due to DHCPv6-PD assisted prefix assignment.


Example:

"auto/64"
+

Type: stringFormat: ipv6

This option defines the static IPv6 gateway of the logical interface. It only applies to static addressing mode. Note that this is usually not needed due to DHCPv6-PD assisted prefix assignment.


Example:

"2001:db8:123:456::1"
+

Type: integer

For dynamic addressing interfaces, this property specifies the prefix size to request from an upstream DHCPv6 server through prefix delegation. For static addressing interfaces, it specifies the size of the sub-prefix to allocate from the upstream-received delegation prefixes for assignment to the logical interface.

Value must be greater or equal to 0 and lesser or equal to 64

Type: object

This section describes the DHCPv6 server configuration

Type: enum (of string)

Specifies the DHCPv6 server operation mode. When set to "stateless", the system will announce router advertisements only, without offering stateful DHCPv6 service. When set to "stateful", emitted router advertisements will instruct clients to obtain a DHCPv6 lease. When set to "hybrid", clients can freely chose whether to self-assign a random address through SLAAC, whether to request an address via DHCPv6, or both. For maximum compatibility with different clients, it is recommended to use the hybrid mode. The special mode "relay" will instruct the unit to act as DHCPv6 relay between this interface and any of the IPv6 interfaces in "upstream" mode.

Must be one of:

  • "hybrid"
  • "stateless"
  • "stateful"
  • "relay"

Type: array of string

Overrides the DNS server to announce in DHCPv6 and RA messages. By default, the device will announce its own local interface address as DNS server, essentially acting as proxy for downstream clients. By specifying a non-empty list of IPv6 addresses here, this default behaviour can be overridden.

No Additional Items

Each item of this array must be:

Type: stringFormat: uc-cidr6 Default: "::/0"

Selects a specific downstream prefix or a number of downstream prefix ranges to announce in DHCPv6 and RA messages. By default, all prefixes configured on a given downstream interface are advertised. By specifying an IPv6 prefix in CIDR notation here, only prefixes covered by this CIDR are selected.

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section describes an IPv6 port forwarding.

Type: enum (of string) Default: "any"

The layer 3 protocol to match.

Must be one of:

  • "tcp"
  • "udp"
  • "any"

Type: integer or stringFormat: uc-portrange

The external port(s) to forward.

Type: stringFormat: ipv6

The internal IP to forward to. The address will be masked and concatenated with the effective interface subnet.

Type: integer or stringFormat: uc-portrange

The internal port to forward to. Defaults to the external port if omitted.

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section describes an IPv6 traffic accept rule.

Type: string Default: "any"

The layer 3 protocol to match.

Type: stringFormat: uc-cidr6 Default: "::/0"

The source IP to allow traffic from.

Type: array

The source port(s) to accept.

Must contain a minimum of 1 items

No Additional Items

Each item of this array must be:

Type: stringFormat: ipv6

The destination IP to allow traffic to. The address will be masked and concatenated with the effective interface subnet.

Type: array

The destination ports to accept.

Must contain a minimum of 1 items

No Additional Items

Each item of this array must be:

Type: array of object

A collection of access control entries that define the rules for filtering traffic through a network interface.

No Additional Items

Each item of this array must be:

Type: object

Type: integer Default: 1

Determines the priority of multiple ACL policies when more than one is applied to an interface, if any.

Value must be greater or equal to 1 and lesser or equal to 64

Type: string

Specifies the ACL policy that is applied to incoming traffic on an interface.

Must be at least 1 characters long

Must be at most 32 characters long


Example:

"blacklisted-macs"
+

Type: boolean Default: false

Tracks the number and type of packets that match the ingress ACL rules on an interface.

Type: string

Specifies the ACL policy that is applied to outgoing traffic from an interface.

Must be at least 1 characters long

Must be at most 32 characters long


Example:

"blacklisted-macs"
+

Type: boolean Default: false

Tracks the number and type of packets that match the egress ACL rules on an interface.

Type: object

Configuration for DHCP Snooping on a port level on a switch

Type: boolean Default: false

This parameter designates a switch port as ‘trusted’ for DHCP messages, meaning it can forward DHCP offers and acknowledgments, which is essential for connecting to legitimate DHCP servers

Type: integer

It sets a limit on the number of DHCP clients that can be associated with a single port, helping to prevent a single port from exhausting the network’s IP address pool

Value must be greater or equal to 1

Type: string

Specifies DHCP Option 82 circuit ID suboption information. Often including information like the interface number and VLAN ID, this can be useful for network management and troubleshooting

Must be at least 1 characters long

Must be at most 32 characters long

Type: object

Type: object

This Object defines the properties of a broad-band uplink.

Type: const

This uplink uses WWAN/LTE

Specific value: "wwan"

Type: enum (of string)

The local protocol that the modem supports.

Must be one of:

  • "qmi"
  • "mbim"
  • "wwan"

Type: string

Commonly known as APN. The name of a gateway between a mobile network and the internet.

Type: enum (of string) Default: "none"

The authentication mode that shall be used.

Must be one of:

  • "none"
  • "pap"
  • "chap"
  • "pap-chap"

Type: string

The PIN that shall be used to unlock the SIM card.

Type: string

This option is only required if an authentication-type is defined.

Type: string

This option is only required if an authentication-type is defined.

Type: enum (of string) Default: "dual-stack"

Define what kind of IP stack shall be used.

Must be one of:

  • "ipv4"
  • "ipv6"
  • "dual-stack"
Type: object

This Object defines the properties of a PPPoE uplink.

Type: const

This uplink uses PPPoE

Specific value: "pppoe"

Type: string

The username used to authenticate.

Type: string

The password used to authenticate.

Type: object

Type: object

This Object defines the properties of a mesh interface overlay.

Type: const

This field must be set to mesh.

Specific value: "mesh"
Type: object

This Object defines the properties of a vxlan tunnel.

Type: const

This field must be set to vxlan.

Specific value: "vxlan"

Type: stringFormat: ipv4

This is the IP address of the remote host, that the VXLAN tunnel shall be established with.

Type: integer

The network port that shall be used to establish the VXLAN tunnel.

Value must be greater or equal to 1 and lesser or equal to 65535


Example:

4789
+
Type: object

This Object defines the properties of a l2tp tunnel.

Type: const

This field must be set to vxlan.

Specific value: "l2tp"

Type: stringFormat: ipv4

This is the IP address of the remote host, that the L2TP tunnel shall be established with.

Type: string

The username used to authenticate.

Type: string

The password used to authenticate.

Type: object

This Object defines the properties of a GRE tunnel.

Type: const

This field must be set to gre.

Specific value: "gre"

Type: stringFormat: ipv4

This is the IP address of the remote host, that the GRE tunnel shall be established with.

Type: boolean Default: false

Healthcheck will probe if the remote peer replies to DHCP discovery without sending an ACK.

Type: boolean Default: false

Set “Don't Fragment” flag on encapsulated packets.

Type: object

This Object defines the properties of a GREv6 tunnel.

Type: const

This field must be set to gre6.

Specific value: "gre6"

Type: stringFormat: ipv6

This is the IPv6 address of the remote host, that the GRE tunnel shall be established with.

Type: boolean Default: false

Healthcheck will probe if the remote peer replies to DHCP discovery without sending an ACK.

Type: object

This section describes all of the services that may be present on the AP. Each service is then referenced via its name inside an interface, ...

Type: object

Type: string Default: "uCentral Access Point"

The LLDP description field. If set to "auto" it will be derived from unit.name.

Type: string Default: "uCentral Network"

The LLDP location field. If set to "auto" it will be derived from unit.location.

Type: object

This section can be used to setup a SSH server on the AP.

Type: integer Default: 22

This option defines which port the SSH server shall be available on.

Value must be lesser or equal to 65535

Type: array of string

This allows the upload of public ssh keys. Keys need to be seperated by a newline.

No Additional Items

Each item of this array must be:

Type: string

Examples:

"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC0ghdSd2D2y08TFowZLMZn3x1/Djw3BkNsIeHt/Z+RaXwvfV1NQAnNdaOngMT/3uf5jZtYxhpl+dbZtRhoUPRvKflKBeFHYBqjZVzD3r4ns2Ofm2UpHlbdOpMuy9oeTSCeF0IKZZ6szpkvSirQogeP2fe9KRkzQpiza6YxxaJlWw== user@example"
+
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ4FDjyCsg+1Mh2C5G7ibR3z0Kw1dU57kfXebLRwS6CL bob@work"
+
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBP/JpJ/KHtKKImzISBDwLO0/EwytIr4pGZQXcP6GCSHchLMyfjf147KNlF9gC+3FibzqKH02EiQspVhRgfuK6y0= alice@home"
+

Type: boolean Default: true

This option defines if password authentication shall be enabled. If set to false, only ssh key based authentication is possible.

Type: boolean

This option whether SSH server should be enabled or disabled.

Type: object

This section can be used to setup the upstream NTP servers.

Type: array of string

This is an array of URL/IP of the upstream NTP servers that the unit shall use to acquire its current time.

No Additional Items

Each item of this array must be:

Type: stringFormat: uc-host

Example:

"0.openwrt.pool.ntp.org"
+

Type: boolean

Start a NTP server that provides the time to local clients.


Example:

true
+

Type: object

This section can be used to configure the MDNS server.

Type: boolean Default: false

Enable this option if you would like to enable the MDNS server on the unit.

Type: object

This section can be used to setup a persistent connection to a rTTY server.

Type: stringFormat: uc-host

The server that the device shall connect to.


Example:

"192.168.1.10"
+

Type: integer Default: 5912

This option defines the port that device shall connect to.

Value must be lesser or equal to 65535

Type: string

The security token that shall be used to authenticate with the server.

Must be at least 32 characters long

Must be at most 32 characters long


Example:

"01234567890123456789012345678901"
+

Type: boolean Default: true

Shall the connection enforce mTLS

Type: object

This section can be used to configure remote syslog support.

Type: stringFormat: uc-host

IP address of a syslog server to which the log messages should be sent in addition to the local destination.


Example:

"192.168.1.10"
+

Type: integer

Port number of the remote syslog server specified with log_ip.

Value must be greater or equal to 100 and lesser or equal to 65535


Example:

2000
+

Type: enum (of string) Default: "udp"

Sets the protocol to use for the connection, either tcp or udp.

Must be one of:

  • "tcp"
  • "udp"

Type: integer Default: 1000

Size of the file based log buffer in KiB. This value is used as the fallback value for logbuffersize if the latter is not specified.

Value must be greater or equal to 32

Type: integer Default: 7

Filter messages by their log priority. the value maps directly to the 0-7 range used by syslog.

Value must be greater or equal to 0

Type: object

Enable the webserver with the on-boarding webui

Type: integer Default: 80

The port that the HTTP server should run on.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: boolean

This option whether http server should be enabled or disabled.

Type: object

This section allows enabling the IGMP/Multicast proxy

Type: boolean Default: false

This option defines if the IGMP/Multicast proxy shall be enabled on the device.

Type: object

This section can be used to setup a radius security proxy instance (radsecproxy).

Type: string Default: "secret"

The radius secret used to communicate with the proxy.

Type: array

The various realms that we can proxy to.

No Additional Items

Each item of this array must be:


Type: object

Type: enum (of string) Default: "radsec"

Defines whether the real should use radsec or normal radius.

Must be one of:

  • "radsec"

Type: array of string

The realm that that this server shall be used for.

No Additional Items

Each item of this array must be:

Type: boolean Default: false

Auto discover radsec server address via realm DNS NAPTR record.

Type: stringFormat: uc-host

The remote proxy server that the device shall connect to.


Example:

"192.168.1.10"
+

Type: integer Default: 2083

The remote proxy port that the device shall connect to.

Value must be lesser or equal to 65535

Type: string

The radius secret that will be used for the connection.

Type: boolean Default: false

The device will use its local certificate bundle for the TLS setup and ignores all other certificate options in this section.

Type: string

The local servers CA bundle.

Type: string

The local servers certificate.

Type: string

The local servers private key/

Type: string

The password required to read the private key.

Type: object

Type: enum (of string)

Defines whether the real should use radsec or normal radius.

Must be one of:

  • "radius"

Type: array of string

The realm that that this server shall be used for.

No Additional Items

Each item of this array must be:

Type: stringFormat: uc-host

The URI of our Radius server.


Example:

"192.168.1.10"
+

Type: integer

The network port of our Radius server.

Value must be greater or equal to 1024 and lesser or equal to 65535


Example:

1812
+

Type: string

The shared Radius authentication secret.


Example:

"secret"
+

Type: stringFormat: uc-host

The URI of our Radius server.


Example:

"192.168.1.10"
+

Type: integer

The network port of our Radius server.

Value must be greater or equal to 1024 and lesser or equal to 65535


Example:

1812
+

Type: string

The shared Radius authentication secret.


Example:

"secret"
+
Type: object

Type: enum (of string)

Defines whether the real should use radsec or normal radius.

Must be one of:

  • "block"

Type: array of string

The realm that that this server shall be used for.

No Additional Items

Each item of this array must be:

Type: string

The message that is sent when a realm is blocked.

Type: object

This section can be used to configure the online check service.

Type: array of string

Hosts that shall be pinged to find out if we are online.

No Additional Items

Each item of this array must be:

Type: stringFormat: uc-host

Example:

"192.168.1.10"
+

Type: array of string

URLs to which a http/s connection shall be established to find out if we are online. The service will try to download http://$string/online.txt and expects the content of that file to be "Ok". HTTP 30x is support allowing https redirects.

No Additional Items

Each item of this array must be:

Type: string

Example:

"www.example.org"
+

Type: number Default: 60

The interval in seconds in between each online-check.

Type: number Default: 1

How often does the online check need to fail until the system assumes that it has lost online connectivity.

Type: array of enum (of string)

The action that the device shall execute when it has detected that it is not online.

No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "leds"

Type: object

This section can be used to define eBPF and cBPF blobs that shall be loaded for virtual data-planes and SDN.

Type: array of object

A list of programs that can be loaded as ingress filters on interfaces.

No Additional Items

Each item of this array must be:

Type: object

Type: string

The name of the ingress filter.

Type: stringFormat: uc-base64

The base64 encoded xBPF.

Type: object

This section describes the QoS behaviour of the unit.

Type: array of string

The physical network devices that shall be considered the primary uplink interface. All classification and shaping will happen on this device.

No Additional Items

Each item of this array must be:

Type: integer Default: 0

Defines the upload bandwidth of this device. If it is not known or the device is attached to a shared medium, this value needs to be 0.

Type: integer Default: 0

Defines the download bandwidth of this device. If it is not known or the device is attached to a shared medium, this value needs to be 0.

Type: object

The QoS feature can automatically detect and classify bulk flows. This is based on average packet size and PPS.

Type: enum (of string) Default: "CS0"

The differentiated services code point that shall be assigned to packets that belong to a bulk flow.

Must be one of:

  • "CS0"
  • "CS1"
  • "CS2"
  • "CS3"
  • "CS4"
  • "CS5"
  • "CS6"
  • "CS7"
  • "AF11"
  • "AF12"
  • "AF13"
  • "AF21"
  • "AF22"
  • "AF23"
  • "AF31"
  • "AF32"
  • "AF33"
  • "AF41"
  • "AF42"
  • "AF43"
  • "DF"
  • "EF"
  • "VA"
  • "LE"

Type: number Default: 0

The required PPS rate that will cause a flow to be classified as bulk.

Type: array of string

A list of predefined named services that shall be classified according to the communities DB.

No Additional Items

Each item of this array must be:

Type: array of object

A list of classifiers. Each classifier will map certain traffic to specific ToS/DSCP values based upon the defined constraints.

No Additional Items

Each item of this array must be:

Type: object

Type: enum (of string) Default: "CS1"

The differentiated services code point that shall be assigned to packet that match the rules of this entry.

Must be one of:

  • "CS0"
  • "CS1"
  • "CS2"
  • "CS3"
  • "CS4"
  • "CS5"
  • "CS6"
  • "CS7"
  • "AF11"
  • "AF12"
  • "AF13"
  • "AF21"
  • "AF22"
  • "AF23"
  • "AF31"
  • "AF32"
  • "AF33"
  • "AF41"
  • "AF42"
  • "AF43"
  • "DF"
  • "EF"
  • "VA"
  • "LE"

Type: array of object

Each entry defines a layer3 protocol and a port(range) that will be used to match packets.

No Additional Items

Each item of this array must be:

Type: object

Type: enum (of string) Default: "any"

The port match can apply for TCP, UDP or any IP protocol.

Must be one of:

  • "any"
  • "tcp"
  • "udp"

Type: integer

The port of this match rule.

Type: integer

The last port of this match rule if it is a port range.

Type: boolean Default: true

Ignore the ToS/DSCP of packets and reclassify them.

Type: array of object

Each entry defines a wildcard FQDN. The IP that this resolves to will be used to match packets.

No Additional Items

Each item of this array must be:

Type: object

Type: boolean Default: true

Match for all suffixes of the FQDN.

Type: boolean Default: true

Ignore the ToS/DSCP of packets and reclassify them.

Type: object

This Object defines the properties of a wireguard-overlay.

Type: const

This field must be set to wireguard-overlay.

Specific value: "wireguard-overlay"

Type: string

The private key of the device. This key is used to lookup the host entry inside the config.

Type: integer Default: 3456

The network port that shall be used to establish the wireguard tunnel.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: integer Default: 3458

The network port that shall be used to exchange peer data inside the tunnel.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: object

The descritption of the root node of the overlay.

Type: string

The public key of the host.

Type: stringFormat: uc-ip

The public IP of the host (optional).

Type: array of string

The list of private IPs that a host is reachable on inside the overlay.

No Additional Items

Each item of this array must be:

Type: stringFormat: uc-ip

Type: array of object

The list of all known hosts inside the overlay.

No Additional Items

Each item of this array must be:

Type: object

Type: string

The unique name of the host.

Type: string

The public key of the host.

Type: stringFormat: uc-ip

The public IP of the host (optional).

Type: array of string

The list of subnets that shall be routed to this host.

No Additional Items

Each item of this array must be:

Type: array of string

The list of private IPs that a host is reachable on inside the overlay.

No Additional Items

Each item of this array must be:

Type: object

The descritption of the root node of the overlay.

Type: integer Default: 4789

The network port that shall be used to establish the vxlan overlay.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: integer Default: 1420

The MTU that shall be used by the vxlan tunnel.

Value must be greater or equal to 256 and lesser or equal to 65535

Type: boolean Default: true

If set to true hosts will only be able to talk with the root node and not forward L@ traffic between each other.

Type: object

This section can be used to configure a GPS dongle

Type: boolean Default: false

Adjust the systems clock upon a successful GPS lock.

Type: enum (of integer)

The baudrate used by the attached GPS dongle

Must be one of:

  • 2400
  • 4800
  • 9600
  • 19200

Type: object

This section can be used to setup a Telnet server on the device.

Type: boolean

This option whether telnet server should be enabled or disabled.

Type: object

Enable the webserver with the on-boarding webui

Type: integer Default: 443

The port that the secure HTTP server should run on.

Value must be greater or equal to 1 and lesser or equal to 65535

Type: boolean

This option whether secure http server should be enabled or disabled.

Type: object

There are several types of mertics that shall be reported in certain intervals. This section provides a granual configuration.

Type: object

Statistics are traffic counters, neighbor tables, ...

Type: integer

The reporting interval defined in seconds.

Value must be greater or equal to 60

Type: array of enum (of string)

A list of names of subsystems that shall be reported periodically.

No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "lldp"
  • "clients"

Type: integer

Configure maximum number of FDB entries device's allowed to report. If omitted, device-default number should be used (2000). Setting to zero means no entries should be reported, flag should be raised. If device's current FDB size exceeds configured value, flag should be raised as well.

Type: object

Health check gets executed periodically and will report a health value between 0-100 indicating how healthy the device thinks it is

Type: integer

The reporting interval defined in seconds.

Value must be greater or equal to 60

Type: boolean Default: true

This is makes the AP probe local downstream DHCP servers.

Type: boolean Default: false

This is makes the AP probe remote upstream DHCP servers.

Type: boolean Default: true

This is makes the AP probe DNS servers.

Type: boolean Default: true

This is makes the AP probe DNS servers.

Type: object

DHCP snooping allows us to intercept DHCP packages on interface that are bridged, where DHCP is not offered as a service by the AP.

Type: array of enum (of string)

A list of the message types that shall be sent to the backend.

No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "ack"
  • "discover"
  • "offer"
  • "request"
  • "solicit"
  • "reply"
  • "renew"

Type: object

Configure the unsolicited telemetry stream.

Type: integer

The reporting interval defined in seconds.

Type: array of enum (of string)

The event types that get added to telemetry.

No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "ssh"
  • "health"
  • "health.dns"
  • "health.dhcp"
  • "health.radius"
  • "health.memory"
  • "client"
  • "client.join"
  • "client.leave"
  • "client.key-mismatch"
  • "wired"
  • "wired.carrier-up"
  • "wired.carrier-down"
  • "unit"
  • "unit.boot-up"

Type: object

Configure the realtime events that get sent to the cloud.

Type: array of enum (of string)

The event types that get added to telemetry.

No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "ssh"
  • "health"
  • "health.dns"
  • "health.dhcp"
  • "health.radius"
  • "health.memory"
  • "client"
  • "client.join"
  • "client.leave"
  • "client.key-mismatch"
  • "wired"
  • "wired.carrier-up"
  • "wired.carrier-down"
  • "unit"
  • "unit.boot-up"

Type: array of array

This object allows passing raw uci commands, that get applied after all the other configuration was ben generated.

No Additional Items

Each item of this array must be:

Type: array of string

Must contain a minimum of 2 items

No Additional Items

Each item of this array must be:


Examples:

[
+    "set",
+    "system.@system[0].timezone",
+    "GMT0"
+]
+
[
+    "delete",
+    "firewall.@zone[0]"
+]
+
[
+    "delete",
+    "dhcp.wan"
+]
+
[
+    "add",
+    "dhcp",
+    "dhcp"
+]
+
[
+    "add-list",
+    "system.ntp.server",
+    "0.pool.example.org"
+]
+
[
+    "del-list",
+    "system.ntp.server",
+    "1.openwrt.pool.ntp.org"
+]
+

Type: object

Additional Properties of any type are allowed.

Type: object
\ No newline at end of file diff --git a/docs/ucentral-state.html b/docs/ucentral-state.html new file mode 100644 index 0000000..01e50c3 --- /dev/null +++ b/docs/ucentral-state.html @@ -0,0 +1,28 @@ + Schema Docs
Type: object

OpenWrt uCentral state schema

Type: const
Specific value: 1

Type: integer

The unique ID of the configuration. This is the unix timestamp of when the config was created.

Type: string

The unique serial number of the device.

Type: object

This section describes the current state of the OS running on the device.

Type: array of number

This array hold 3 values describing the average system load for the last 1, 5 and 15 minutes.

No Additional Items

Each item of this array must be:

Type: number

Type: array of number

This array hold the total and per core load in percentage.

No Additional Items

Each item of this array must be:

Type: number

Type: number

This property contains the current unix time of the device.

Type: object

This section describes the current memory uasge of the device.

Type: number

The amount of free memory.

Type: number

The total amount of memory.

Type: number

The total amount of cached memory.

Type: number

The total amount of buffered memory.

Type: number

The number of seconds since the unit last booted.

Type: array of number

The average and maximum thermal reading from the CPU.

No Additional Items

Each item of this array must be:

Type: object

This section describes the current state of the PoE unit on the device

Type: number

Reports the total power available (power budget) (in watts, W) device's Power sourcing equipment is able to source.

Type: number

Reports configured power alarm threshold value for the Power sourcing equipment (in milliwatts, mW).

Type: number

Reports a total power Powered Devices are draining from the device's Power sourcing equipment (in milliwatts, mW).

Type: string

Reports power status of the device's Power sourcing equipment.


Examples:

"ON"
+
"OFF"
+

Type: object

This section describes the global (device-wise) 802.1X (port access control) state and config applied.

Type: object

Reported DAS-related state info.

Type: object

Cumulative statistics for all configured DACs.

Type: number

Number of CoA requests received.

Type: number

Number of CoA ACK responses sent.

Type: number

Number of CoA NAK responses sent.

Type: number

Number of CoA requests ignored.

Type: number

Number of CoA requests received with invalid (unsupported) attributes.

Type: number

Number of CoA requests received with invalid (unsupported) attribute value.

Type: number

Number of CoA requests received with inexisting session context.

Type: number

Number of CoA requests that are sent if the NAS is configured to prohibit honoring of CoA-Request or Disconnect-Request packets for the specified session.

Type: object

This section describes the GPS location of the device.

Type: number

The latitude in WGS84.

Type: number

The longitude in WGS84.

Type: number

The elevation above sea level in meters.

Type: array of object

An array containing the runtime state of all logical interfaces.items

No Additional Items

Each item of this array must be:

Type: object

Type: string

The json-schema path within the configuration where this logical interface is located.

Type: number

The number of seconds since the interface was brought up.

Type: string

The administrative name of this logical interface. This field is freetext

Type: string

The upstream NTP server being used by this interface.

Type: array of string

The list of remote DNS servers that this logical interface uses for domain name resolution.

No Additional Items

Each item of this array must be:

Type: object

Type: array of string

The list of IPv4 addresses being used by this logical interface.

No Additional Items

Each item of this array must be:

Type: string

An IPv4 addreess.

Type: string

The public IP address of internet connection.

Type: number

This is the leasetime if the IPv4 address of this logical interface was acquired via DHCPv4.

Type: string

The IPv4 address of the DHCPv4 server that assigned the active lease.

Type: array of object
No Additional Items

Each item of this array must be:

Type: object

This section describes an IPv4 lease that we have served

Type: string

The IPv4 that has been assigned to the networking client.

Type: string

The unix time stamp when this lease was assigned.

Type: string

The hostname that the client included in his request.

Type: string

The MAC address of the client that this lease was assigned to.

Type: array of object
No Additional Items

Each item of this array must be:

Type: object

An IPv6 address assigned to this logical interface.

Type: string

The actual IPv6 address/mask

Type: number

How much longer this delegation is valid for.

Type: array of object

This section describes an IPv6 lease that we have served.

No Additional Items

Each item of this array must be:

Type: object

An object, that describes a single served IPv6 address.

Type: array of string

A list of all IPv6 addresses that were assigned to this host.

No Additional Items

Each item of this array must be:

Type: string

The hostname that the client sent in its solicit message.

Type: array of object
No Additional Items

Each item of this array must be:

Type: object

This section describes a network neighbour.

Type: string

The MAC address of the neighbour.

Type: array of string

The IPv4 addresses that the neighbour is known to use.

No Additional Items

Each item of this array must be:

Type: array of string

The IPv4 addresses that the neighbour is known to use.

No Additional Items

Each item of this array must be:

Type: array of string

The physical network devices that that the neighbour has been seen on.

No Additional Items

Each item of this array must be:

Type: number

How long along was traffic from this neighbour last seen.

Type: object

This section contains the traffic counters of the logical interface.

Type: number

The number of bytes received.

Type: number

The number of packets received.

Type: number

The number of receive errors.

Type: number

The number of received packets that were dropped.

Type: number

The number of bytes transmitted.

Type: number

The number of packets transmitted.

Type: number

The number of transmit errors.

Type: number

The number of transmitted packets that were dropped.

Type: object

This section contains the traffic counters of the logical interface.

Same definition as counters

Type: object

This section contains info about connected mesh nodes.

Type: object

Detailed information about all multicast-related data (groups joined, src address used etc)

Type: object

Detailed information about IGMP configured / joined multicast groups, outgoing interfaces etc.

Type: array of object

List of joined IGMP multicast groups.

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: ipv4

Address of single group this interface is member of.


Example:

"225.0.0.1"
+

Type: array of string

List of ports where multicast traffic of this group is being replicated to.

No Additional Items

Each item of this array must be:

Type: object

MVR statistics on interface

Type: boolean

Shows if MVR traffic is being forwarded or discarded.

Type: integer

The number of IGMP membership reports received on this interface.

Type: integer

The number of leave messages received on this interface.

Type: integer

The number of general query messages received on this interface.

Type: integer

The number of group specific or group-and-source specific query messages received on this interface.

Type: integer

The number of times a report, leave, or query was dropped.

Type: integer

The number of times a multicast group was successfully joined.

Type: integer

The number of MVR groups active on this interface.

Type: object

Represents the overall statistics for ACLs on the OLS device.

Type: array of object

A list of ACL-related statistics, each corresponding to a specific interface or port.

No Additional Items

Each item of this array must be:

Type: object

Type: string

The identifier for the interface or port to which the ACL statistics apply.

Type: enum (of string)

Type of the access control list.

Must be one of:

  • "none"
  • "ipv4"
  • "ipv6"
  • "ipv4Ext"
  • "ipv6Ext"
  • "mac"
  • "arp"

Type: enum (of string)

Indicates the action (permit or deny) taken when an ACL rule is matched.

Must be one of:

  • "permit"
  • "deny"

Type: number

The number of times an ACL rule has been matched by traffic.

Type: integer

Shows the percentage of this user-configured ACL rule as a percentage of total ACL rules.

Value must be greater or equal to 0 and lesser or equal to 100

Type: object

Represents the overall resource utilization statistics for ACLs.

Type: integer

Percentage of total ACL consumed resources amongst the resources available.

Value must be greater or equal to 0 and lesser or equal to 100

Type: object

State message entry to show the binding table for DHCP Snooping

Type: array of object

List of DHCP Snooping binding entries

No Additional Items

Each item of this array must be:

Type: object

Type: stringFormat: uc-mac

MAC address of the DHCP client in the DHCP Snooping binding table

Type: stringFormat: ipv4

IP address assigned to the MAC address in the DHCP Snooping binding table

Type: integer

This indicates the lease time in seconds for the IP address assigned to the DHCP client, after which the IP address may be reassigned

Type: enum (of string)

Specifies the type of binding entry, such as dynamic or static, indicating how the IP address was assigned to the client

Must be one of:

  • "dynamic"
  • "static"

Type: integer

VLAN ID associated with the DHCP client’s IP address, which helps in managing network segments

Value must be greater or equal to 1 and lesser or equal to 4094

Type: string

Identifies the interface through which the DHCP client is connected, aiding in network topology management


Examples:

"Ethernet1"
+
"Unit-1/Port-2"
+
"1-2"
+
"Trunk 1"
+

Type: object

Type: object

All properties whose name matches the following regular expression must respect the following conditions

Property name regular expression: ^(eth|lan|wan)[0-9]*$
Type: array of object
No Additional Items

Each item of this array must be:

Type: object

A list of all LLDP peers that this logical interface is connected to.

Type: array of string

The device capabilities that our neighbour is announcing.

No Additional Items

Each item of this array must be:

Type: string

The chassis description that our neighbour is announcing.

Type: string

The chassis ID/MAC that our neighbour is announcing.

Type: array of string

The management IPs that our neighbour is announcing.

No Additional Items

Each item of this array must be:

Type: string

The physical network port that we see this neighbour on.

Type: object

Provides information about the remote port, such as the interface name and its operational status on the neighboring device.

Type: string

The identifier for the remote port connected to the local port, often displayed as a string or number representing the remote interface.

Type: enum (of string)

Specifies the type of identifier used for the remote port, such as MAC address, interface name, or ifIndex.

Must be one of:

  • "MAC Address"
  • "Interface Name"
  • "ifIndex"

Type: integer

Shows the time-to-live (TTL) value for LLDP advertisements from the remote device. This is the duration (in seconds) the LLDP information remains valid before expiring."

Type: string

Provides a textual description of the remote port, typically describing the interface or its purpose.

Type: string

Displays the maximum transmission unit (MTU) size that the remote port supports, indicating the largest packet size the port can handle.

Type: string

A textual description of the remote system, including the device model, software version, or any user-configured description.

Type: array of string

Indicates the system capabilities advertised by the remote device, such as whether it supports routing, switching, bridging, etc.

No Additional Items

Each item of this array must be:

Type: array of string

Displays the capabilities that are actually enabled on the remote device, such as routing, switching, or bridging functionality.

No Additional Items

Each item of this array must be:

Type: string

The management IP address of the remote device, used to access the device for administrative purposes.

Type: integer

Shows the VLAN ID associated with the remote port, which could indicate the native VLAN or the port's trunk configuration.

Type: string

Identifies any protocol-specific information (in HEX) advertised by the remote device, such as VLAN, Spanning Tree, or Link Aggregation protocol.

Type: object

Displays the physical layer status of the remote port.

Type: boolean

Indicates whether the remote port supports auto-negotiation of link parameters like speed and duplex settings.

Type: string

Lists the capabilities (in HEX) the remote port is advertising during auto-negotiation, such as supported speeds and duplex modes.

Type: integer

Displays the Media Access Unit (MAU) type, describing the port's physical characteristics (e.g., copper or fiber, 10/100/1000Base-T, etc.).

Type: object

Provides detailed PoE information.

Type: enum (of string)

Indicates if the device is a PSE or a PD.

Must be one of:

  • "pse"
  • "pd"

Type: boolean

Indicates whether the remote device supports Media Dependent Interface (MDI) power supply, allowing power to be supplied through the Ethernet cable.

Type: boolean

Displays whether the remote device has control over which wire pairs in the Ethernet cable provide power (important for PoE).

Type: enum (of string)

Displays the PoE class of the remote device, indicating its power consumption classification.

Must be one of:

  • "Class-1"
  • "Class-2"
  • "Class-3"
  • "Class-4"
  • "Class-5"
  • "Class-6"
  • "Class-7"
  • "Class-8"

Type: object

Provides information about the remote port’s involvement in Link Aggregation Groups (LAG), if applicable.

Type: boolean

Indicates whether the remote port supports Link Aggregation (static or LACP).

Type: integer

Displays the identifier of the Link Aggregation Group (LAG) that the remote port belongs to.

Type: object

All properties whose name matches the following regular expression must respect the following conditions

Property name regular expression: ^(eth|lan|wan)[0-9]*$
Type: array of object
Same definition as lldp-peers_upstream_pattern1

Type: array
No Additional Items

Each item of this array must be:

Type: object

This section contains the traffic counters of the logical interface.

Type: number

The number of bytes received.

Type: number

The number of packets received.

Type: number

The number of bytes transmitted.

Type: number

The number of packets transmitted.

Type: object

This section describes the global (device-wise) mac-address-list (FDB table / wired clients).


Example:

{
+    "overflow": true,
+    "Ethernet1": {
+        "1": [
+            "AABBCCDDEEFF",
+            "112233445566"
+        ]
+    },
+    "Ethernet2": {
+        "10": [
+            "11BBCCDDEEFF",
+            "332233445566"
+        ]
+    }
+}
+

Type: boolean

Flag indicates that device could report an amount of FDB entries, which is bigger than what cloud had requested.

Each additional property must conform to the following schema

Type: object

Each additional property must conform to the following schema

Type: array of string

VID (vlan id) identifier

No Additional Items

Each item of this array must be:

Type: array of object
No Additional Items

Each item of this array must be:

Type: object

List of statically created trunks.

Type: integer

Logical identifier for the trunk.

Value must be greater or equal to 1 and lesser or equal to 64

Type: array of string

List of member ports under this static trunk.

No Additional Items

Each item of this array must be:

Type: array of object
No Additional Items

Each item of this array must be:

Type: object

List of dynamically created trunks.

Type: integer

Logical identifier for the trunk.

Value must be greater or equal to 1 and lesser or equal to 64

Type: string

List of member ports under this trunk.

Type: number

LACP System priority.

Type: number

LACP port priority.

Type: string

Port state.

Type: number

Number of LACP Data Units (PDUs) sent.

Type: number

Number of LACP Data Units (PDUs) received.

Type: number

Number of Marker PDUs sent.

Type: number

Number of Marker PDUs received.

Type: number

Number of unknown packets received.

Type: number

Number of illegal packets received.

Type: number

Operational key for the port.

Type: number

Operational key for the partner.

Type: string

Operational state.

Type: string

Operational state of the partner.

\ No newline at end of file diff --git a/docs/ucentral.capabilities.html b/docs/ucentral.capabilities.html new file mode 100644 index 0000000..aa3bb3a --- /dev/null +++ b/docs/ucentral.capabilities.html @@ -0,0 +1,20 @@ + Schema Docs
Type: object

uCentral protocol (OpenLan) device and features capabilities schema

Type: string

Example:

"aabbccddeeff"
+

Type: string

Platform revision


Example:

"Rel 1.6 build 5"
+

Type: object

The ols schema version to be used with this Switch


Example:

{
+    "major": 3,
+    "minor": 2,
+    "patch": 0
+}
+

Type: integer

Type: integer

Type: integer

Type: enum (of string)

Must be one of:

  • "Switch"
  • "AP"

Type: string

Device model

Type: string

Stock keeping unit

Type: string

Compatibility string, that defines the family of the device

Type: stringFormat: uc-mac

Switch MAC address


Example:

"aa:bb:cc:dd:ee:ff"
+

Type: array of object

The list of physical network devices

No Additional Items

Each item of this array must be:

Type: object

Type: string

The logical name of the port that is used by the OS


Examples:

"Ethernet0"
+
"Ethernet1"
+
"Ethernet76"
+

Type: integer

The identification number of the port as can be seen on the front-panel of the device

Type: object

Description of physical ports and their form-factors

Type: array of enum (of string)
No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "RJ45"
  • "SFP"
  • "SFP+"
  • "SFP28"
  • "SFP-DD"
  • "QSFP"
  • "QSFP+"
  • "QSFP28"
  • "QSFP-DD"

Type: array of object
No Additional Items

Each item of this array must be:

Type: object

Type: string

Example:

"RJ45"
+

Type: array of string
No Additional Items

Each item of this array must be:

Type: string

Example:

"Ethernet1"
+

Type: object

Description of physical ports and their PoE capabilities

Type: array of enum (of string)
No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • ".3AF-POE"
  • ".3AT-POE+"
  • ".3BT-PoE++"
  • "PreStandard-Passive"

Type: integer

Example:

2000
+

Type: array of object
No Additional Items

Each item of this array must be:

Type: object

Type: string

Example:

".3AF-POE"
+

Type: array of string
No Additional Items

Each item of this array must be:

Type: string

Example:

"Ethernet1"
+

Type: object

Capabilities of the MC-LAG (Multi-Chassis Link Aggregation) feature in the switch

Type: integer

Defines the maximum number of MC-LAG groups that can be configured on the switch.

Type: integer

Specifies the maximum number of physical ports that can be part of a single MC-LAG group.

Type: integer

Indicates the maximum number of VLANs that can be supported within a single MC-LAG group.

Type: enum (of string)

Describes the dual-active detection mechanism to prevent both switches from becoming active simultaneously.

Must be one of:

  • "ICCP"
  • "Backup-Link"
  • "None"

Type: integer

Specifies the time (in milliseconds) required for traffic to fail over to the secondary switch when there is a failure in the primary switch.

Type: boolean

Indicates whether VLAN synchronization across MC-LAG peers is supported and the number of VLANs that can be synchronized.

Type: integer

Maximum number of MAC address entries that can be synchronized across MC-LAG peers.

Type: object

Description of LLDP capabilities across different switch models/vendors.

Type: array of enum (of string)
No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "lldp-basic-tlv-mgmt-ip-v4"
  • "lldp-basic-tlv-mgmt-ip-v6"
  • "lldp-basic-tlv-port-descr"
  • "lldp-basic-tlv-sys-capab"
  • "lldp-basic-tlv-sys-descr"
  • "lldp-basic-tlv-sys-name"
  • "lldp-dot1-tlv-proto-ident"
  • "lldp-dot1-tlv-proto-vid"
  • "lldp-dot1-tlv-pvid"
  • "lldp-dot1-tlv-vlan-name"
  • "lldp-dot3-tlv-link-agg"
  • "lldp-dot3-tlv-mac-phy"
  • "lldp-dot3-tlv-max-frame"
  • "lldp-dot3-tlv-poe"
  • "lldp-med-location-civic-addr"
  • "lldp-med-tlv-ext-poe"
  • "lldp-med-tlv-inventory"
  • "lldp-med-tlv-location"
  • "lldp-med-tlv-med-cap"
  • "lldp-med-tlv-network-policy"

Type: integer

Maximum number of LLDP neighbors a switch can discover and maintain.


Examples:

64
+
128
+
256
+

Type: array of enum (of string)

List of all features supported by the device

No Additional Items

Each item of this array must be:

Type: enum (of string)

Must be one of:

  • "VLAN"
  • "VLAN-Voice"
  • "Jumbo-Frames"
  • "Link-Aggregation-LACP"
  • "Link-Aggregation-Static"
  • "Link-Aggregation-MCLAG"
  • "Port-Isolation"
  • "Spanning-Tree"
  • "Spanning-Tree-Rapid"
  • "Spanning-Tree-Per-VLAN"
  • "Spanning-Tree-Per-VLAN-Rapid"
  • "Spanning-Tree-MSTP"
  • "SVI-StaticIPv4"
  • "SVI-StaticIPv6"
  • "Interface-StaticIPv4"
  • "Interface-StaticIPv6"
  • "Routing-VRF"
  • "Routing-IPv4-Route-Blackhole"
  • "Routing-IPv4-Route-Unreachable"
  • "Routing-IPv4-Nexthop"
  • "Routing-IPv4-Broadcast"
  • "Routing-IPv4-Multicast-IGMP-Snooping"
  • "Routing-IPv4-Multicast-IGMP-Querier"
  • "Routing-IPv4-Multicast-IGMP-Static"
  • "Routing-IPv4-DHCP-Server"
  • "Routing-IPv4-DHCP-Relay"
  • "Routing-IPv4-DHCP-Snooping"
  • "Routing-IPv4-Port-Forward"
  • "Routing-IPv6-DHCP-Relay"
  • "Routing-IPv6-DHCP-Stateful"
  • "Routing-IPv6-DHCP-Stateless"
  • "Routing-IPv6-Port-Forward"
  • "Multicast-VLAN-Registration"
  • "PoE-Reset"
  • "Port-Access-Control"
  • "PAC-Dynamic-Auth"
  • "mac-address-bypass"
  • "System-PasswordChange"
  • "System-SwUpdate"
  • "System-SwUpdate-Partial"
  • "Port-Mirroring"
  • "MAC-ACL"
  • "IP-ACL"
  • "Guest-VLAN"
  • "Service-SSH"
  • "Service-RSSH"
  • "Service-Telnet"
  • "Service-LLDP"
  • "Service-HTTP"
  • "Service-HTTPS"
  • "Service-GPS"
  • "Service-IGMP"
  • "Service-NTP"
  • "Service-NTP-Client"
  • "Service-MDNS"
  • "Service-QoS"
  • "Service-Syslog"
  • "Service-PAC"
  • "Service-Wireguard-Overlay"
  • "Service-Radius-Proxy"
  • "Service-Online-Check"
  • "Service-CaptivePortal"
  • "Service-PublicIpCheck"
  • "Tunneling-VxLAN"
  • "Tunneling-GRE"
  • "Tunneling-GRE6"
  • "Tunneling-L2TP"
  • "Tunneling-Mesh"
\ No newline at end of file diff --git a/jsdoc/ucode-transpiler.js b/jsdoc/ucode-transpiler.js deleted file mode 100644 index 43b4546..0000000 --- a/jsdoc/ucode-transpiler.js +++ /dev/null @@ -1,650 +0,0 @@ -/* - * ucode-transpiler.js - JSDoc plugin to naively transpile ucode into JS. - * - * Copyright (C) 2021 Jo-Philipp Wich - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -'use strict'; - -function skipString(s) { - let q = s.charAt(0); - let esc = false; - - for (let i = 1; i < s.length; i++) { - let c = s.charAt(i); - - if (esc) { - esc = false; - continue; - } - else if (c == '\\') { - esc = true; - continue; - } - else if (c == q) { - // consume regex literal flags - while (q == '/' && s.charAt(i + 1).match(/[gis]/)) - i++; - - return [ s.substring(0, i + 1), s.substring(i + 1) ]; - } - } - - throw 'Unterminated string literal'; -} - -function skipComment(s) { - let q = s.charAt(1), - end = (q == '/') ? '\n' : '*/', - esc = false; - - for (let i = 2; i < s.length; i++) { - let c = s.charAt(i); - - if (esc) { - esc = false; - continue; - } - else if (c == '\\') { - esc = true; - continue; - } - else if (s.substring(i, i + end.length) == end) { - return [ s.substring(0, i + end.length), s.substring(i + end.length) ]; - } - } - - if (q == '*') - throw 'Unterminated multiline comment'; - - return [ s, '' ]; -} - -function escapeString(s) { - return "'" + s.replace(/[\\\n']/g, '\\$&') + "';"; -} - -const keywords = [ - 'break', - 'case', - 'catch', - 'const', - 'continue', - 'default', - 'delete', - 'elif', - 'else', - 'endfor', - 'endfunction', - 'endif', - 'endwhile', - 'false', - 'for', - 'function', - 'if', - 'in', - 'let', - 'null', - 'return', - 'switch', - 'this', - 'true', - 'try', - 'while' -]; - -const reserved = [ - 'await', - 'class', - 'debugger', - 'enum', - 'export', - 'extends', - 'finally', - 'implements', - 'import', - 'instanceof', - 'interface', - 'new', - 'package', - 'private', - 'protected', - 'public', - 'super', - 'throw', - 'typeof', - 'var', - 'void', - 'with', - 'yield' -]; - -function Transpiler(s, raw) { - this.source = s; - this.offset = 0; - this.tokens = []; - - if (raw) { - this.state = 'identify_token'; - this.block = 'block_statement'; - } - else { - this.state = 'identify_block'; - } - - let token = null; - - do { - token = this.parse(); - - switch (token.type) { - case '-}}': - case '-%}': - case '}}': - case '%}': - if (raw) - throw 'Unexpected token "' + token.type + '"'; - - break; - } - - this.tokens.push(token); - } - while (token.type != 'eof'); -} - -Transpiler.prototype = { - parse: function() { - let m; - - switch (this.state) { - case 'identify_block': - m = this.source.match(/^((?:.|\n)*?)((\{[{%#])(?:.|\n)*)$/); - - if (m) { - switch (m[3]) { - case '{#': - this.state = 'block_comment'; - break; - - case '{{': - this.state = 'block_expression'; - break; - - case '{%': - this.state = 'block_statement'; - break; - } - - this.source = m[2]; - - return { type: 'text', value: escapeString(m[1]), prefix: '' }; - } - else if (this.source.length) { - let t = { type: 'text', value: escapeString(this.source), prefix: '' }; - - this.source = ''; - - return t; - } - else { - return { type: 'eof', value: '', prefix: '' }; - } - - break; - - case 'block_comment': - m = this.source.match(/^((?:.|\n)*?#\})((?:.|\n)*)$/); - - if (!m) - throw 'Unterminated comment block'; - - this.source = m[2]; - this.state = 'identify_block'; - this.block = null; - - return { - type: 'comment', - value: m[1].replace(/\*\}/g, '*\\}').replace(/^\{##/, '/**').replace(/^\{#/, '/*').replace(/#\}$/, '*/'), - prefix: '' - }; - - case 'block_expression': - this.state = 'identify_token'; - this.block = 'expression'; - this.source = this.source.replace(/^\{\{[+-]?/, ''); - - return this.parse(); - - case 'block_statement': - this.state = 'identify_token'; - this.block = 'statement'; - this.source = this.source.replace(/^\{%[+-]?/, ''); - - return this.parse(); - - case 'identify_token': - let t = this.parsetoken(); - - if ((this.block == 'expression' && (t.type == '-}}' || t.type == '}}')) || - (this.block == 'statement' && (t.type == '-%}' || t.type == '%}'))) { - this.state = 'identify_block'; - this.block = null; - - return { - type: ';', - value: ';', - prefix: t.prefix - }; - } - - if (this.block == 'expression' && t.type == 'eof') - throw 'Unterminated expression block'; - - return t; - } - }, - - parsetoken: function() { - let token = this.source.match(/^((?:\s|\n)*)(-[%}]\}|<<=|>>=|===|!==|\.\.\.|\?\.\[|\?\.\(|[%}]\}|\/\*|\/\/|&&|[+&|^\/%*-=!<>]=|--|\+\+|<<|>>|\|\||=>|\?\.|[+=&|[\]\^{}:,~\/>!<%*()?;.'"-]|(\d+(?:\.\d+)?)|(\w+))((?:.|\n)*)$/); - let rv, r, t; - - if (token) { - switch (token[2]) { - case '"': - case "'": - r = skipString(token[2] + token[5]); - rv = r[0]; - t = 'string'; - this.source = r[1]; - break; - - case '//': - case '/*': - r = skipComment(token[2] + token[5]); - rv = r[0]; - t = 'comment'; - this.source = r[1]; - break; - - case '/': - case '/=': - if (this.lastToken.match(/[(,=:[!&|?{};]/)) { - r = skipString(token[2] + token[5]); - rv = r[0]; - t = 'regexp'; - this.source = r[1]; - } - else { - rv = token[2]; - t = token[2]; - this.source = token[5]; - } - - break; - - default: - this.source = token[5]; - - if (token[3]) { - rv = token[3]; - - if (token[3].indexOf('.') != -1) - t = 'double'; - else - t = 'number'; - } - else if (token[4]) { - rv = token[4]; - - if (keywords.indexOf(token[4]) != -1) { - t = token[4]; - } - else { - t = 'label'; - - if (reserved.indexOf(token[4]) != -1) - rv += '_'; - } - } - else { - rv = token[2]; - t = token[2]; - } - - break; - } - - this.lastToken = token[2]; - - return { - type: t, - value: rv, - prefix: token[1] - }; - } - else if (this.source.match(/^\s*$/)) { - rv = this.source; - this.source = ''; - - return { - type: 'eof', - value: '', - prefix: rv - }; - } - else { - throw 'Unrecognized character near [...' + this.source + ']'; - } - }, - - next: function() { - let idx = this.offset++; - - return this.tokens[Math.min(idx, this.tokens.length - 1)]; - }, - - skip_statement: function(tokens, ends) { - let nest = 0; - - while (true) { - let token = this.next(); - - if (token.type == 'eof') { - this.offset--; - - break; - } - - if (nest == 0 && ends.indexOf(token.type) != -1) { - this.offset--; - - break; - } - - switch (token.type) { - case '(': - case '[': - case '{': - nest++; - break; - - case ')': - case ']': - case '}': - nest--; - break; - } - - tokens.push(token); - - if (token.type == ';') - break; - } - - return tokens; - }, - - skip_paren: function(tokens) { - let token = this.next(); - let depth = 0; - - if (token.type != '(') - throw 'Unexpected token, expected "(", got "' + token.type + '"'; - - do { - tokens.push(token); - - switch (token.type) { - case '(': - depth++; - break; - - case ')': - depth--; - - if (depth == 0) - return token; - - break; - - case 'eof': - throw 'Unexpected EOF'; - } - - token = this.next(); - } - while (depth != 0); - }, - - assert_token: function(tokens, type) { - let token = this.next(); - - if (token.type != type) - throw 'Unexpected token, expected "' + type + '", got "' + token.type + '"'; - - tokens.push(token); - - return tokens; - }, - - check_token: function(tokens, type) { - let token = this.next(); - - if (token.type != type) { - this.offset--; - - return false; - } - - tokens.push(token); - - return true; - }, - - patch: function(tokens, type, value) { - tokens[tokens.length - 1].type = type; - tokens[tokens.length - 1].value = (value != null) ? value : type; - }, - - skip_block: function(tokens, ends) { - while (true) { - let off = tokens.length; - - if (this.check_token(tokens, 'if')) { - this.skip_paren(tokens); - - if (this.check_token(tokens, ':')) { - this.patch(tokens, '{'); - - this.skip_block(tokens, ['else', 'elif', 'endif']); - - while (tokens[tokens.length - 1].type == 'elif') { - let elif = tokens.pop(); - - tokens.push( - { type: '}', value: '}', prefix: '' }, - { type: 'else', value: 'else', prefix: elif.prefix }, - { type: 'if', value: 'if', prefix: ' ' } - ); - - this.skip_paren(tokens); - - this.assert_token(tokens, ':'); - this.patch(tokens, '{'); - - this.skip_block(tokens, ['elif', 'else', 'endif']); - } - - if (tokens[tokens.length - 1].type == 'else') { - let else_ = tokens.pop(); - - tokens.push( - { type: '}', value: '}', prefix: '' }, - { type: 'else', value: 'else', prefix: else_.prefix }, - { type: '{', value: '{', prefix: ' ' } - ); - - this.skip_block(tokens, ['endif']); - } - - this.patch(tokens, '}'); - } - else if (this.check_token(tokens, '{')) { - this.skip_block(tokens, ['}']); - - if (!this.check_token(tokens, 'else')) - continue; - - if (this.check_token(tokens, '{')) - this.skip_block(tokens, ['}']); - else - this.skip_statement(tokens, ends); - } - else { - this.skip_statement(tokens, ends); - } - } - else if (this.check_token(tokens, 'for')) { - let cond = []; - - this.skip_paren(cond); - - // Transform `for (x, y in ...)` into `for (x/*, y*/ in ...)` - if (cond.length > 5 && - cond[1].type == 'label' && - cond[2].type == ',' && - cond[3].type == 'label' && - cond[4].type == 'in') { - cond[2].type = 'comment'; - cond[2].value = '/*' + cond[2].value; - cond[3].type = 'comment'; - cond[3].value = cond[3].value + '*/'; - } - - // Transform `for (let x, y in ...)` into `for (let x/*, y*/ in ...)` - else if (cond.length > 6 && - cond[1].type == 'let' && - cond[2].type == 'label' && - cond[3].type == ',' && - cond[4].type == 'label' && - cond[5].type == 'in') { - cond[3].type = 'comment'; - cond[3].value = '/*' + cond[3].value; - cond[4].type = 'comment'; - cond[4].value = cond[4].value + '*/'; - } - - tokens.push(...cond); - - if (this.check_token(tokens, ':')) { - this.patch(tokens, '{'); - this.skip_block(tokens, ['endfor']); - this.patch(tokens, '}'); - } - else if (this.check_token(tokens, '{')) - this.skip_block(tokens, ['}']); - else - this.skip_statement(tokens, ends); - } - else if (this.check_token(tokens, 'while')) { - this.skip_paren(tokens); - - if (this.check_token(tokens, ':')) { - this.patch(tokens, '{'); - this.skip_block(tokens, ['endwhile']); - this.patch(tokens, '}'); - } - else if (this.check_token(tokens, '{')) - this.skip_block(tokens, ['}']); - else - this.skip_statement(tokens, ends); - } - else if (this.check_token(tokens, 'function')) { - this.check_token(tokens, 'label'); - this.skip_paren(tokens); - - if (this.check_token(tokens, ':')) { - this.patch(tokens, '{'); - this.skip_block(tokens, ['endfunction']); - this.patch(tokens, '}'); - } - else if (this.check_token(tokens, '{')) - this.skip_block(tokens, ['}']); - } - else if (this.check_token(tokens, 'try')) { - this.assert_token(tokens, '{'); - this.skip_block(tokens, ['}']); - this.assert_token(tokens, 'catch'); - - // Transform `try { ... } catch { ... }` into `try { ... } catch(e) { ... }` - if (this.tokens[this.offset].type == '(') - this.skip_paren(tokens); - else - tokens.push( - { type: '(', value: '(', prefix: '' }, - { type: 'label', value: 'e', prefix: '' }, - { type: ')', value: ')', prefix: '' } - ); - - this.assert_token(tokens, '{'); - this.skip_block(tokens, ['}']); - } - else if (this.check_token(tokens, 'switch')) { - this.skip_paren(tokens); - this.assert_token(tokens, '{'); - this.skip_block(tokens, ['}']); - } - else if (this.check_token(tokens, '{')) { - this.skip_block(tokens, ['}']); - } - else if (this.check_token(tokens, 'text')) { - /* pass */ - } - else if (this.check_token(tokens, 'comment')) { - /* pass */ - } - else { - this.skip_statement(tokens, ends); - } - - for (let type of ends) - if (this.check_token(tokens, type)) - return tokens; - - if (this.check_token([], 'eof')) - break; - } - - throw 'Unexpected EOF'; - }, - - transpile: function() { - let tokens = []; - - this.skip_block(tokens, ['eof']); - - return tokens.map(t => t.prefix + t.value).join(''); - } -}; - -exports.handlers = { - beforeParse: function(e) { - let raw = !e.source.match(/\{[{%]/) || e.source.match(/^#!([a-z\/]*)ucode[ \t]+-[A-Z]*R/), - t = new Transpiler(e.source, raw); - - e.source = t.transpile(); - } -}; diff --git a/renderer/qos.json b/renderer/qos.json deleted file mode 100644 index 0c73a2c..0000000 --- a/renderer/qos.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "classes": { - "network_services": { - "ingress": "CS3", - "eggress": "CS3", - "bulk-pps": 100, - "bulk-timeout": 5, - "bulk-dscp": "CS0" - }, - - "conferencing": { - "source": "https://support.zoom.us/hc/en-us/articles/207368756-Using-QoS-DSCP-Marking", - "ingress": "EF", - "egress": "EF" - }, - - "telephony": { - "source": "https://support.zoom.us/hc/en-us/articles/207368756-Using-QoS-DSCP-Marking", - "ingress": "EF", - "egress": "EF" - }, - - "streaming": { - "ingress": "AF41", - "egress": "AF41" - }, - - "browsing": { - "ingress": "CS0", - "egress": "CS0" - } - }, - - "services": { - "networking": { - "classifier": "network_services", - "tcp": [ 22, 123, 53, 5353 ], - "udp": [ 53, 5353 ] - }, - - "browsing": { - "classifier": "browsing", - "tcp": [ 80, 443 ], - "udp": [ 80, 443 ] - }, - - "youtube": { - "source": "https://services.google.com/fh/files/blogs/enabling_remote_working_with_hangouts_meet_quick_deployment_guide.pdf", - "classifier": "streaming", - "fqdn": [ "*.googlevideo.com", "*.youtube-nocookie.com", "*.ytimg.com" ], - "uses": [ "rtmp" ] - }, - - "netflix": { - "source": "https://www.netify.ai/resources/applications/netflix", - "classifier": "streaming", - "fqdn": [ "*.nflxvideo.com", "*.nflxvideo.net" ] - }, - - "amazon-prime": { - "source": "https://www.netify.ai/resources/applications/amazon-prime", - "classifier": "streaming", - "fqdn": [ - "*aiv-cdn.net", "*aiv-delivery.net", "*amazonvideo.com", "*atv-ext.amazon.com", "*atv-ext-eu.amazon.com", - "atv-ext-fe.amazon.com", "atv-ps.amazon.com", "atv-ps-eu.amazon.com", "atv-ps-eu.amazon.co.uk", - "atv-ps-fe.amazon.co.jp", "atv-ps-fe.amazon.com", "primevideo.com", "pv-cdn.net", "video.a2z.com" - ] - }, - - "disney-plus": { - "source": "https://www.netify.ai/resources/applications/disney-plus", - "classifier": "streaming", - "fqdn": [ - "*disneyplus.com", "*disneyplus.disney.co.jp", "*disney-plus.net", "*disneystreaming.service-now.com", - "*disney-vod-na-west-1.top.comcast.net", "*dssott.com" - ] - }, - - "hbo": { - "source": "https://www.netify.ai/resources/applications/hbo", - "classifier": "streaming", - "fqdn": [ - "*hbo.com", "*hbogoasia.com", "*hbogoasia.id", "*hbogoasia.ph", "*hbogo.com", "*hbogo.co.th", "*hbogo.eu", "*hbomaxcdn.com", - "*hbomax.com", "*hbomax-images.warnermediacdn.com", "*hbonow.com", "maxgo.com" - ] - }, - - "rtmp": { - "comment": "RTMP (YouTube Live, Twitch, Vimeo and LinkedIn Live)", - "classifier": "streaming", - "tcp": [ "1935-1936", 2396, 2935 ] - }, - - "stun": { - "comment": "STUN (Session Traversal Utilities for NAT)", - "classifier": "conferencing", - "udp": [ "3478-3497" ] - }, - - "zoom": { - "source": "https://support.zoom.us/hc/en-us/articles/201362683-Zoom-network-firewall-or-proxy-server-settings", - "classifier": "conferencing", - "fqdn": [ "*zoom.us" ], - "tcp": [ "8801-8802" ], - "udp": [ "8801-8810" ], - "uses": [ "stun" ] - }, - - "facetime": { - "source": "https://support.apple.com/en-us/HT202078", - "classifier": "conferencing", - "udp": [ "16384-16387", "16393-16402" ] - }, - - "webex": { - "source": "https://help.webex.com/en-us/article/WBX264/How-Do-I-Allow-Webex-Meetings-Traffic-on-My-Network?#id_135400", - "classifier": "conferencing", - "tcp": [ 5004 ], - "udp": [ 9000 ] - }, - - "jitsi": { - "source": "https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart/#setup-and-configure-your-firewall", - "classifier": "conferencing", - "tcp": [ 5349 ], - "udp": [ 10000 ], - "uses": [ "stun" ] - }, - - "google-meet": { - "source": "https://services.google.com/fh/files/blogs/enabling_remote_working_with_hangouts_meet_quick_deployment_guide.pdf", - "classifier": "conferencing", - "udp": [ "19302-19309" ] - }, - - "teams": { - "source": "https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide#skype-for-business-online-and-microsoft-teams", - "classifier": "conferencing", - "uses": [ "stun" ] - }, - - "voip": { - "classifier": "telephony", - "tcp": [ "5060-5061" ], - "udp": [ "5060-5061" ] - }, - - "vowifi": { - "classifier": "telephony", - "udp": [ 500, 4500 ] - } - } -} diff --git a/renderer/renderer.uc b/renderer/renderer.uc deleted file mode 100644 index 5c2ee9d..0000000 --- a/renderer/renderer.uc +++ /dev/null @@ -1,1172 +0,0 @@ -// UCI batch output master template - -"use strict"; - -let uci = require("uci"); -let ubus = require("ubus"); -let fs = require("fs"); - -let cursor = uci ? uci.cursor() : null; -let conn = ubus ? ubus.connect() : null; - -let capabfile = fs.open("/etc/ucentral/capabilities.json", "r"); -let capab = capabfile ? json(capabfile.read("all")) : null; - -let pipe = fs.popen('fw_printenv developer'); -let developer = replace(pipe.read("all"), '\n', ''); -pipe.close(); -let restrict = {}; -if (developer != 'developer=1') { - let restrictfile = fs.open("/etc/ucentral/restrictions.json", "r"); - restrict = restrictfile ? json(restrictfile.read("all")) : {}; -} - -let serial = cursor.get("ucentral", "config", "serial"); - -assert(cursor, "Unable to instantiate uci"); -assert(conn, "Unable to connect to ubus"); -assert(capab, "Unable to load capabilities"); - -let topdir = sourcepath(0, true); - -/** - * Formats a given input value as uci boolean value. - * - * @memberof uCentral.prototype - * @param {*} val The value to format - * @returns {string} - * Returns '1' if the given value is truish (not `false`, `null`, `0`, - * `0.0` or an empty string), or `0` in all other cases. - */ -function b(val) { - return val ? '1' : '0'; -} - -/** - * Formats a given input value as single quoted string, honouring uci - * specific escaping semantics. - * - * @memberof uCentral.prototype - * @param {*} str The string to format - * @returns {string} - * Returns an empty string if the given input value is `null` or an - * empty string. Returns the escaped and quoted string in all other - * cases. - */ -function s(str) { - if (str === null || str === '') - return ''; - - return sprintf("'%s'", replace(str, /'/g, "'\\''")); -} - -/** - * Attempt to include a file, catching potential exceptions. - * - * Try to include the given file path in a safe manner. The - * path is resolved relative to the path of the currently - * executed template and may only contain the character `A-Z`, - * `a-z`, `0-9`, `_`, `/` and `-` as must contain a final - * `.uc` file extension. - * - * Exception occuring while including the file are catched - * and a warning is emitted instead. - * - * @memberof uCentral.prototype - * @param {string} path Path of the file to include - * @param {object} scope The scope to pass to the include file - */ -function tryinclude(path, scope) { - if (!match(path, /^[A-Za-z0-9_\/-]+\.uc$/)) { - warn("Refusing to handle invalid include path '%s'", path); - return; - } - - let parent_path = sourcepath(1, true); - - assert(parent_path, "Unable to determine calling template path"); - - try { - include(parent_path + "/" + path, scope); - } - catch (e) { - warn("Unable to include path '%s': %s\n%s", path, e, e.stacktrace[0].context); - } -} - -function discover_ports() { - let roles = {}; - - /* Derive ethernet port names and roles from default config */ - for (let role, spec in capab.network) { - for (let i, ifname in spec) { - role = uc(role); - let netdev = split(ifname, ':'); - let port = { - netdev: netdev[0], - index: i - }; - if (netdev[1]) { - port.swconfig = netdev[1]; - port.swdev = split(ifname, ':')[0]; - } - push(roles[role] = roles[role] || [], port); - } - } - - /* Sort ports in each role group according to their index, then normalize - * names into uppercase role name with 1-based index suffix in case of multiple - * ports or just uppercase role name in case of single ports */ - let rv = {}; - - for (let role, ports in roles) { - switch (length(ports)) { - case 0: - break; - - case 1: - rv[role] = ports[0]; - break; - - default: - map(sort(ports, (a, b) => (a.index - b.index)), (port, i) => { - rv[role + (i + 1)] = port; - }); - } - } - - return rv; -} - -/** - * @class uCentral.wiphy - * @classdesc - * - * This is the wireless PHY base class. It is automatically instantiated and accessible - * using the global 'wiphy' variable. - */ - -/** @lends uCentral.wiphy.prototype */ - -let wiphy = { - /** - * Return a list of PHY information structures - * - * This function returns a list of all available PHYs including - * the relevant data describing their properties and capabilities - * such as HT Modes, channels, ... - * - * @method - * - * @returns {Array} - * Returns an array of all available PHYs. - */ - phys: require("wifi.phy"), - - /** @private */ - band_freqs: { - '2G': [ 2412, 2484 ], - '5G': [ 5160, 5885 ], - '5G-lower': [ 5160, 5340 ], - '5G-upper': [ 5480, 5885 ], - '6G': [ 5925, 7125 ], - '60G': [ 58320, 69120 ] - }, - - /** @private */ - band_channels: { - '2G': [ 1, 14 ], - '5G': [ 7, 196 ], - '5G-lower': [ 7, 68 ], - '5G-upper': [ 96, 177 ], - '6G': [ 200, 600 ], // FIXME - '60G': [ 1, 6 ] - }, - - /** - * Convert a wireless channel to a wireless frequency - * - * @param {string} wireless band - * @param {number} channel - * - * @returns {?number} - * Returns the coverted wireless frequency for this specific - * channel. - */ - channel_to_freq: function(band, channel) { - if (band == '2G' && channel >= 1 && channel <= 13) - return 2407 + channel * 5; - else if (band == '2G' && channel == 14) - return 2484; - else if (band == '5G' && channel >= 7 && channel <= 177) - return 5000 + channel * 5; - else if (band == '5G' && channel >= 183 && channel <= 196) - return 4000 + channel * 5; - else if (band == '60G' && channel >= 1 && channel <= 6) - return 56160 + channel * 2160; - - return null; - }, - - /** - * Convert the unique sysfs path describing a wireless PHY to - * the corresponding UCI section name - * - * @param {string} path - * - * @returns {string|false} - * Returns the UCI section name of a specific PHY - */ - path_to_section: function(path) { - let sid = null; - - cursor.load("wireless"); - cursor.foreach("wireless", "wifi-device", (s) => { - if (s.path == path && s.scanning != 1) { - sid = s['.name']; - - return false; - } - }); - - return sid; - }, - - /** - * Get a list of all wireless PHYs for a specific wireless band - * - * @param {string} band - * - * @returns {object[]} - * Returns an array of all wireless PHYs for a specific wireless - * band. - */ - lookup_by_band: function(band) { - let baseband = band; - let phys = []; - - if (band in ['5G-lower', '5G-upper']) - baseband = '5G'; - - for (let path, phy in this.phys) { - if (!(baseband in phy.band)) - continue; - - let phy_min_freq, phy_max_freq; - - if (phy.frequencies) { - phy_min_freq = min(...phy.frequencies); - phy_max_freq = max(...phy.frequencies); - } - else { - /* NB: this code is superfluous once ubus call wifi phy reports - supported frequencies directly */ - - let min_ch = max(min(...phy.channels), this.band_channels[band][0]), - max_ch = min(max(...phy.channels), this.band_channels[band][1]); - - phy_min_freq = this.channel_to_freq(baseband, min_ch); - phy_max_freq = this.channel_to_freq(baseband, max_ch); - - if (phy_min_freq === null) { - warn("Unable to map channel %d in band %s to frequency", min_ch, baseband); - continue; - } - - if (phy_max_freq === null) { - warn("Unable to map channel %d in band %s to frequency", max_ch, baseband); - continue; - } - } - - /* phy's frequency range does not overlap with band's frequency range, skip phy */ - if (phy_max_freq < this.band_freqs[band][0] || phy_min_freq > this.band_freqs[band][1]) - continue; - - let sid = this.path_to_section(path); - - if (sid) - push(phys, { ...phy, section: sid }); - } - - return phys; - }, - - allocate_ssid_section_id: function(phy) { - phy.networks = ++phy.networks || 1; - - assert(phy.section, "Radio has no related uci section"); - - return phy.section + 'net' + phy.networks; - } -}; - -/** - * @class uCentral.ethernet - * @classdesc - * - * This is the ethernet base class. It is automatically instantiated and - * accessible using the global 'ethernet' variable. - */ - -/** @lends uCentral.ethernet.prototype */ - -let ethernet = { - ports: discover_ports(), - - /** - * Get a list of all wireless PHYs for a specific wireless band - * - * @param {string} band - * - * @returns {object} - * Returns an array of all wireless PHYs for a specific wireless - * band. - */ - lookup: function(globs) { - let matched = {}; - - for (let glob, tag_state in globs) { - for (let name, spec in this.ports) { - if (wildcard(name, glob)) { - if (spec.netdev) - matched[spec.netdev] = tag_state; - else - warn("Not implemented yet: mapping switch port to netdev"); - } - } - } - - return matched; - }, - - lookup_by_interface_vlan: function(interface, raw) { - // Gather the glob patterns in all `ethernet: [ { select-ports: ... }]` specs, - // dedup them and turn them into one global regular expression pattern, then - // match this pattern against all known system ethernet ports, remember the - // related netdevs and return them. - let globs = {}; - map(interface.ethernet, eth => map(eth.select_ports, glob => globs[glob] = eth.vlan_tag)); - - let lookup = this.lookup(globs); - if (raw) - return lookup; - - let rv = {}; - for (let k, v in lookup) { - /* tagged swconfig downstream ports are not allowed */ - if (interface.role == 'downstream') { - if (this.swconfig && this.swconfig[k].switch && v == 'tagged') - warn('%s:%d - vlan tagging on downstream swconfig ports is not supported', this.swconfig[k]?.switch.name, this.swconfig[k].swconfig); - else - rv[k] = v; - continue; - } - /* resolve upstream vlans on swconfig ports */ - if (this.swconfig && interface.role == 'upstream' && interface.vlan.id && this.swconfig[k]?.switch && v != 'un-tagged') { - rv[split(k, '.')[0] + '.' + interface.vlan.id] = 'un-tagged'; - continue; - } - rv[k] = v; - } - return rv; - }, - - switch_by_interface_vlan: function(interface, raw) { - let ports = this.lookup_by_interface_vlan(interface, true); - let rv = { ports: "" }; - let cpu_port = 0; - for (let port, tag in ports) { - if (!this.swconfig || !this.swconfig[port]?.switch) continue; - rv.name = this.swconfig[port].switch.name; - cpu_port = this.swconfig[port].switch.port; - rv.ports += ' ' + this.swconfig[port].swconfig; - if (tag != 'un-tagged') - rv.ports += 't'; - } - if (!rv.name) - return null; - rv.ports = cpu_port + 't' + rv.ports; - - return rv; - }, - - lookup_by_interface_spec: function(interface) { - return sort(keys(this.lookup_by_interface_vlan(interface))); - }, - - lookup_by_select_ports: function(select_ports) { - let globs = {}; - map(select_ports, glob => globs[glob] = true); - - return sort(keys(this.lookup(globs))); - }, - - lookup_by_ethernet: function(ethernets) { - let result = []; - - for (let ethernet in ethernets) - result = [ ...result, ...this.lookup_by_select_ports(ethernet.select_ports) ]; - return result; - }, - - reserve_port: function(port) { - delete this.ports[port]; - }, - - is_single_config: function(interface) { - let ipv4_mode = interface.ipv4 ? interface.ipv4.addressing : 'none'; - let ipv6_mode = interface.ipv6 ? interface.ipv6.addressing : 'none'; - - return ( - (ipv4_mode == 'none') || (ipv6_mode == 'none') || - (ipv4_mode == 'static' && ipv6_mode == 'static') - ); - }, - - calculate_name: function(interface) { - let vid = interface.vlan.id; - if (interface.admin_ui) - return 'admin_ui'; - return (interface.role == 'upstream' ? 'up' : 'down') + interface.index + 'v' + vid; - }, - - calculate_names: function(interface) { - let name = this.calculate_name(interface); - - return this.is_single_config(interface) ? [ name ] : [ name + '_4', name + '_6' ]; - }, - - calculate_ipv4_name: function(interface) { - let name = this.calculate_name(interface); - - return this.is_single_config(interface) ? name : name + '_4'; - }, - - calculate_ipv6_name: function(interface) { - let name = this.calculate_name(interface); - - return this.is_single_config(interface) ? name : name + '_6'; - }, - - has_vlan: function(interface) { - return interface.vlan && interface.vlan.id; - }, - - port_vlan: function(interface, port) { - if (port == "tagged") - return ':t'; - if (port == "un-tagged") - return ''; - return ((interface.role == 'upstream') && this.has_vlan(interface)) ? ':t' : ''; - }, - - find_interface: function(role, vid) { - for (let interface in state.interfaces) - if (interface.role == role && - interface.vlan?.id == vid) - return this.calculate_name(interface); - return ''; - }, - - get_interface: function(role, vid) { - for (let interface in state.interfaces) - if (interface.role == role && - interface.vlan.id == vid) - return interface; - return null; - }, - - get_speed: function(dev) { - let fp = fs.open(sprintf("/sys/class/net/%s/speed", dev)); - if (!fp) - return 1000; - let speed = fp.read("all"); - fp.close(); - return +speed; - } -}; - -/** - * @class uCentral.ipcalc - * @classdesc - * - * The ipcalc utility class provides methods for manipulating and testing - * IP address ranges. - */ - -/** @lends uCentral.ipcalc.prototype */ - -let ipcalc = { - used_prefixes: [], - - /** - * Convert the given amount of prefix bits to a network mask in IP address - * notation. - * - * @param {number} bits The amounts of prefix bits - * @param {?boolean} v6 If true, produce an IPv6 mask, otherwise use IPv4 - * - * @returns {string} - * Returns a string containing the corresponding netmask. - * - * @throws - * Throws an exception when the amount of bits is not representable as netmask. - */ - convert_bits_to_mask: function(bits, v6) { - let width = v6 ? 128 : 32, - mask = []; - - assert(bits <= width, "Invalid bit length"); - - bits = width - bits; - - for (let i = width / 8; i > 0; i--) { - let b = (bits < 8) ? bits : 8; - mask[i - 1] = ~((1 << b) - 1) & 0xff; - bits -= b; - } - - return mask; - }, - - apply_mask: function(addr, mask) { - assert(length(addr) == length(mask), "Incompatible mask"); - - return map(addr, (byte, i) => byte & mask[i]); - }, - - is_intersecting_prefix: function(addr1, bits1, addr2, bits2) { - assert(length(addr1) == length(addr2), "Incompatible addresses"); - - let mask = this.convert_bits_to_mask((bits1 < bits2) ? bits1 : bits2, length(addr1) == 16); - - for (let i = 0; i < length(addr1); i++) - if ((addr1[i] & mask[i]) != (addr2[i] & mask[i])) - return false; - - return true; - }, - - add_amount: function(addr, amount) { - for (let i = length(addr); i > 0; i--) { - let t = addr[i - 1] + amount; - addr[i - 1] = t & 0xff; - amount = t >> 8; - } - - return addr; - }, - - reserve_prefix: function(addr, mask) { - for (let i = 0; i < length(this.used_prefixes); i += 2) { - let addr2 = this.used_prefixes[i + 0], - mask2 = this.used_prefixes[i + 1]; - - if (length(addr2) != length(addr)) - continue; - - if (this.is_intersecting_prefix(addr, mask, addr2, mask2)) - return false; - } - - push(this.used_prefixes, addr, mask); - - return true; - }, - - generate_prefix: function(state, template, ipv6) { - let prefix = match(template, /^(auto|[0-9a-fA-F:.]+)\/([0-9]+)$/); - - if (prefix && prefix[1] == 'auto') { - assert(state.globals && state.globals[ipv6 ? 'ipv6_network' : 'ipv4_network'], - "No global prefix pool configured"); - - let pool = match(state.globals[ipv6 ? 'ipv6_network' : 'ipv4_network'], /^([0-9a-fA-F:.]+)\/([0-9]+)$/); - - assert(prefix[2] >= pool[2], - "Interface " + (ipv6 ? "IPv6" : "IPv4") + " prefix size exceeds available allocation pool size"); - - let available_prefixes = 1 << (prefix[2] - pool[2]), - prefix_mask = this.convert_bits_to_mask(prefix[2], ipv6), - address_base = iptoarr(pool[1]); - - for (let offset = 0; offset < available_prefixes; offset++) { - if (this.reserve_prefix(address_base, prefix[2])) { - this.add_amount(address_base, 1); - - return arrtoip(address_base) + '/' + prefix[2]; - } - - for (let i = length(address_base), carry = 1; i > 0; i--) { - let t = address_base[i - 1] + (~prefix_mask[i - 1] & 0xff) + carry; - address_base[i - 1] = t & 0xff; - carry = t >> 8; - } - } - - die("No prefix of size /" + prefix[2] + " available"); - } - - return template; - }, - - expand_wildcard_address: function(wcaddr, prefix) { - let addr = iptoarr(wcaddr), - cidr = match(prefix, /^([0-9a-fA-F:.]+)\/([0-9]+)$/); - - assert(addr, "Invalid wildcard address '" + wcaddr + '"'); - assert(cidr, "Invalid prefix range '" + prefix + '"'); - - let mask = this.convert_bits_to_mask(+cidr[2], length(addr) == 16), - base = this.apply_mask(iptoarr(cidr[1]), mask), - result = []; - - for (let i, b in addr) { - if (b & mask[i]) { - warn("Wildcard address '" + wcaddr + "' is partially masked by interface subnet mask '" + arrtoip(mask) + '"'); - break; - } - } - - for (let i, b in addr) - result[i] = base[i] | (b & ~mask[i]); - - return arrtoip(result); - } -}; - -/** - * @class uCentral.services - * @classdesc - * - * The services utility class provides methods for managing and querying - * service states. - */ - -/** @lends uCentral.services.prototype */ - -let services = { - state: {}, - - set_enabled: function(name, state) { - if (!this.state[name]) { - if (state == 'early') - this.state[name] = 'early'; - else - this.state[name] = state ? true : false; - } - }, - - is_present: function(name) { - return length(fs.stat("/etc/init.d/" + name)) > 0; - }, - - lookup_interfaces: function(service) { - let interfaces = []; - - for (let interface in state.interfaces) { - if (!interface.services || index(interface.services, service) < 0) - continue; - push(interfaces, interface); - } - - return interfaces; - }, - - lookup_interfaces_by_ssids: function(service) { - let interfaces = []; - - for (let interface in state.interfaces) { - if (!interface.ssids) - continue; - for (let ssid in interface.ssids) { - if (!ssid.services || index(ssid.services, service) < 0) - continue; - push(interfaces, interface); - } - } - - return uniq(interfaces); - }, - - lookup_ssids: function(service) { - let ssids = []; - - for (let interface in state.interfaces) { - if (!interface.ssids) - continue; - for (let ssid in interface.ssids) { - if (!ssid.services || index(ssid.services, service) < 0) - continue; - push(ssids, ssid); - } - } - - return ssids; - }, - - lookup_ethernet: function(service) { - let ethernets = []; - - for (let ethernet in state.ethernet) { - if (!ethernet.services || index(ethernet.services, service) < 0) - continue; - push(ethernets, ethernet); - } - - return ethernets; - }, - - lookup_services: function() { - let rv = []; - - for (let incfile in fs.glob(topdir + '/templates/services/*.uc')) { - let m = match(incfile, /^.+\/([^\/]+)\.uc$/); - - if (m) - push(rv, m[1]); - } - - return rv; - }, - - lookup_metrics: function() { - let rv = []; - - for (let incfile in fs.glob(topdir + '/templates/metric/*.uc')) { - let m = match(incfile, /^.+\/([^\/]+)\.uc$/); - - if (m) - push(rv, m[1]); - } - - return rv; - } -}; - -/** - * @class uCentral.local_profile - * @classdesc - * - * The local profile utility class provides access to the uCentral runtome - * profile information. - */ - -/** @lends uCentral.local_profile.prototype */ - -let local_profile = { - /** - * Retrieve the local uCentral profile data. - * - * Parses the local uCentral profile JSON data and returns the - * resulting object. - * - * @return {?object} - * Returns an object containing the profile data or `null` on error. - */ - get: function() { - let profile_file = fs.open("/etc/ucentral/profile.json"); - - if (profile_file) { - let profile = json(profile_file.read("all")); - - profile_file.close(); - - return profile; - } - return null; - } -}; - -/** - * @class uCentral.files - * @classdesc - * - * The files utility class manages non-uci file attachments which are - * produced during schema rendering. - */ - -/** @lends uCentral.files.prototype */ - -let files = { - /** @private */ - files: {}, - - /** - * The base directory for file attachments. - * - * @readonly - */ - basedir: '/tmp/ucentral', - - /** - * Escape the given string. - * - * Escape any slash and tilde characters in the given string to allow - * using it as part of a JSON pointer expression. - * - * @param {string} s The string to escape - * @returns {string} The escaped string - */ - escape: function(s) { - return replace(s, /[~\/]/g, m => (m == '~' ? '~0' : '~1')); - }, - - /** - * Add a named file attachment. - * - * Stores the given content in a file at the given path. Expands the - * path relative to the `basedir` if it is not absolute. - * - * @param {string} path The file path - * @param {*} content The content to store - */ - add_named: function(path, content) { - if (index(path, '/') != 0) - path = this.basedir + '/' + path; - - this.files[path] = content; - }, - - /** - * Add an anonymous file attachment. - * - * Stores the given content in a file with a random name derived from - * the given location pointer and name hint. - * - * @param {string} location The current location within the state we're traversing - * @param {string} name The name hint - * @param {*} content The content to store - * - * @returns {string} - * Returns the generated random file path. - */ - add_anonymous: function(location, name, content) { - let path = this.basedir + '/' + this.escape(location) + '/' + this.escape(name); - - this.files[path] = content; - - return path; - }, - - /** - * Recursively create the parent directories of the given path. - * - * Recursively creates the parent directory structure of the given - * path and places any error messages in the given logs array. - * - * @param {array} logs The array to store log messages into - * @param {string} path The path to create directories for - * @return {boolean} - * Returns `true` if the parent directories were successfully created - * or did already exist, returns `false` in case an error occurred. - */ - mkdir_path: function(logs, path) { - assert(index(path, '/') == 0, "Expecting absolute path"); - - let segments = split(path, '/'), - tmppath = shift(segments); - - for (let i = 0; i < length(segments) - 1; i++) { - tmppath += '/' + segments[i]; - - let s = fs.stat(tmppath); - - if (s != null && s.type == 'directory') - continue; - - if (fs.mkdir(tmppath)) - continue; - - push(logs, sprintf("[E] Unable to mkdir() path '%s': %s", tmppath, fs.error())); - - return false; - } - - return true; - }, - - /** - * Write the staged file attachement contents to the filesystem. - * - * Writes the staged attachment contents that were gathered during state - * rendering to the file system and places any encountered errors into - * the logs array. - * - * @param {array} logs The array to store error messages into - * @return {boolean} - * Returns `true` if all attachments were written succefully, returns - * `false` if one or more attachments could not be written. - */ - write: function(logs) { - let success = true; - - for (let path, content in this.files) { - if (!this.mkdir_path(logs, path)) { - success = false; - continue; - } - - let f = fs.open(path, "w"); - - if (f) { - f.write(content); - f.close(); - } - else { - push(logs, sprintf("[E] Unable to open() path '%s' for writing: %s", path, fs.error())); - success = false; - } - } - - return success; - } -}; - -/** - * @class uCentral.shell - * @classdesc - * - * The shell utility class provides high level abstractions for various - * shell interaction tasks. - */ - -/** @lends uCentral.shell.prototype */ - -let shell = { - /** - * Set a random root password. - * - * Generate a random passphrase and set it as root password, - * do not change the password if a random password has been - * set already since the last reboot. - */ - password: function(random) { - let passwd = "openwifi"; - - if (random) { - let math = require("math"); - passwd = ''; - for (let i = 0; i < 32; i++) { - let r = math.rand() % 62; - if (r < 10) - passwd += r; - else if (r < 36) - passwd += sprintf("%c", 55 + r); - else - passwd += sprintf("%c", 61 + r); - } - } - system("(echo " + passwd + "; sleep 1; echo " + passwd + ") | passwd root"); - conn.call("ucentral", "password", { passwd }); - } -}; - -/** - * @class uCentral.routing_table - * @classdesc - * - * The routing table utility class allows querying system routing tables. - */ - -/** @lends uCentral.routing_table.prototype */ - -let routing_table = { - used_tables: {}, - - next: 1, - - /** - * Allocate a route table index for the given ID - * - * @param {string} id The ID to lookup or reserve - * @returns {number} The table number allocated for the given ID - */ - get: function(id) { - if (!this.used_tables[id]) - this.used_tables[id] = this.next++; - return this.used_tables[id]; - } -}; - -/** - * @class uCentral.captive - * @classdesc - * - * The captive portal utility class allows assigning consecutive names to wifi-ifaces. - */ - -/** @lends uCentral.captive.prototype */ - -let captive = { - interfaces: {}, - - next: 0, - - /** - * Allocate a route table index for the given ID - * - * @param {string} id The ID to lookup or reserve - * @returns {number} The table number allocated for the given ID - */ - get: function(name) { - let iface = this.next++; - push(this.interfaces[name].iface, iface); - return iface; - }, - - /** - * Add an interface - */ - interface: function(name, config) { - this.interfaces[name] = {}; - for (let k, v in config) - this.interfaces[name][k] = v; - this.interfaces[name].iface = []; - }, -}; - -/** - * @class uCentral.latency - * @classdesc - * - * The latency measurement utility class allows registering IPs and URLs that will - * get pinged periodically to find out the latency. - */ - -/** @lends uCentral.routing_table.prototype */ - -let latency = { - ipv4: [], - - ipv6: [], - - /** - * Add an IP/URL that shall have its latency measured - * - * @param {string} ip/url The IP/URL to measure - * @param {number} network family - */ - add: function(host, family) { - switch(family) { - case 4: - push(this.ipv4, host); - break; - case 6: - push(this.ipv6, host); - break; - } - }, - - /** - * create the files in /tmp that hold the hosts that we want to measure - */ - write: function() { - for (let family in ['ipv4', 'ipv6']) { - let file = fs.open(`/tmp/latency.${family}`, 'w'); - if (!file) { - warn(`failed to open /tmp/latency.${family}\n`); - continue; - } - for (let ip in this[family]) - file.write(ip + '\n'); - file.close(); - } - }, -}; - -/** - * @constructs - * @name uCentral - * @classdesc - * - * The uCentral namespace is not an actual class but merely a virtual - * namespace for documentation purposes. - * - * From the perspective of a template author, the uCentral namespace - * is the global root level scope available to embedded code, so - * methods like `uCentral.b()` or `uCentral.info()` or utlity classes - * like `uCentral.files` or `uCentral.wiphy` are available to templates - * as `b()`, `info()`, `files` and `wiphy` respectively. - */ -return /** @lends uCentral.prototype */ { - render: function(state, logs) { - logs = logs || []; - - /** @lends uCentral.prototype */ - return render('templates/toplevel.uc', { - b, - s, - tryinclude, - state, - - /** @member {uCentral.wiphy} */ - wiphy, - - /** @member {uCentral.ethernet} */ - ethernet, - - /** @member {uCentral.ipcalc} */ - ipcalc, - - /** @member {uCentral.services} */ - services, - - /** @member {uCentral.local_profile} */ - local_profile, - location: '/', - cursor, - capab, - restrict, - - /** @member {uCentral.files} */ - files, - - /** @member {uCentral.latency} */ - latency, - - /** @member {uCentral.shell} */ - shell, - - /** @member {uCentral.routing_table} */ - routing_table, - serial, - - /** @member {uCentral.captive} */ - captive, - - /** - * Emit a warning message. - * - * @memberof uCentral.prototype - * @param {string} fmt The warning message format string - * @param {...*} args Optional format arguments - */ - warn: (fmt, ...args) => push(logs, sprintf("[W] (In %s) ", location || '/') + sprintf(fmt, ...args)), - - /** - * Emit an informational message. - * - * @memberof uCentral.prototype - * @param {string} fmt The information message format string - * @param {...*} args Optional format arguments - */ - info: (fmt, ...args) => push(logs, sprintf("[!] (In %s) ", location || '/') + sprintf(fmt, ...args)) - }); - }, - - write_files: function(logs) { - logs = logs || []; - - return files.write(logs); - }, - - files_state: function() { - return files.files; - }, - - services_state: function() { - return services.state; - } -}; diff --git a/renderer/signature.uc b/renderer/signature.uc deleted file mode 100644 index 2e06a3b..0000000 --- a/renderer/signature.uc +++ /dev/null @@ -1,45 +0,0 @@ -let fs = require('fs'); - -let key_info = { - 'dummy_static': function(file, signature) { - return signature == 'aaaaaaaaaa'; - }, - 'cig_sha256': function(file, signature) { - // Decrypt from base64 to binary and write to a tmp file - let decoded = b64dec(signature); - if (!decoded) { - return false; - } - let pub_key_file_name = "/etc/ucentral/sign_pubkey.pem"; - let sign_file_name = "/tmp/sign_file.txt"; - let sign_file = fs.open(sign_file_name, "w"); - sign_file.write(decoded); - sign_file.close(); - - // Verify the signature - let sign_verify_cmd = "openssl dgst -sha256 -verify " + pub_key_file_name + " -signature " + sign_file_name + " " + file; - let pipe = fs.popen(sign_verify_cmd); - let result = pipe.read("all"); - let retcode = pipe.close(); - // Return code of 0 is valid signature - if (retcode == 0) { - return true; - } else { - return false; - } - }, -}; - -return { - -verify: function(file, signature) { - let func = key_info[restrict?.key_info?.vendor + '_' + restrict?.key_info?.algo]; - - if (!func) - return false; - - return func(file, signature); -}, - -} - diff --git a/renderer/templates/admin_ui.uc b/renderer/templates/admin_ui.uc deleted file mode 100644 index fec6cae..0000000 --- a/renderer/templates/admin_ui.uc +++ /dev/null @@ -1,42 +0,0 @@ -{% - let admin_ui = state.services?.admin_ui; - if (!admin_ui?.wifi_ssid) - return; - - let interface = { - admin_ui: true, - name: 'Admin-UI', - role: 'downstream', - auto_start: 0, - services: [ 'ssh', 'http' ], - ipv4: { - addressing: 'static', - subnet: '10.254.254.1/24', - dhcp: { - lease_first: 10, - lease_count: 10, - lease_time: '6h' - } - }, - ssids: [ - { - name: admin_ui.wifi_ssid, - wifi_bands: [ '2G', '5G' ], - bss_mode: 'ap', - encryption: { - proto: 'none' - } - } - ], - }; - - if (admin_ui.wifi_bands) - interface.ssids[0].wifi_bands = admin_ui.wifi_bands; - if (admin_ui.wifi_key) { - interface.ssids[0].encryption.proto = 'psk2'; - interface.ssids[0].encryption.key = admin_ui.wifi_key; - } - push(state.interfaces, interface); -%} - -set state.ui.offline_trigger={{ admin_ui.offline_trigger }} diff --git a/renderer/templates/base.uc b/renderer/templates/base.uc deleted file mode 100644 index ccc8656..0000000 --- a/renderer/templates/base.uc +++ /dev/null @@ -1,57 +0,0 @@ -{% -let roles = (state.switch && state.switch.loop_detection && - state.switch.loop_detection.roles) ? - state.switch.loop_detection.roles : []; - -services.set_enabled("ustpd", length(roles)); - -function loop_detect(role) { - return (index(roles, role) >= 0) ? 1 : 0; -} -%} - -# Basic configuration -set network.loopback=interface -set network.loopback.ifname='lo' -set network.loopback.proto='static' -set network.loopback.ipaddr='127.0.0.1' -set network.loopback.netmask='255.0.0.0' - -set network.up=device -set network.up.name=up -set network.up.type=bridge -set network.up.stp={{ loop_detect("upstream") }} -set network.up.igmp_snooping='1' - -{% if (capab.platform != "switch"): %} -set network.down=device -set network.down.name=down -set network.down.type=bridge -set network.down.stp={{ loop_detect("downstream") }} -set network.down.igmp_snooping='1' - -{% endif %} -set network.up_none=interface -set network.up_none.ifname=up -set network.up_none.proto=none - -{% for (let k, v in capab.macaddr): %} -add network device -set network.@device[-1].name={{ s(k) }} -set network.@device[-1].macaddr={{ s(v) }} -{% endfor %} - -{% for (let k, v in capab.switch): %} -add network switch -set network.@switch[-1].name={{ s(v.name) }} -set network.@switch[-1].reset={{ b(v.reset) }} -set network.@switch[-1].enable_vlan={{ b(v.enable) }} -{% endfor %} - -{% for (let k, port in ethernet.ports): %} -{% if (!port.switch) continue; %} -add network switch_vlan -set network.@switch_vlan[-1].device={{ s(port.switch.name) }} -set network.@switch_vlan[-1].vlan={{ s(port.vlan) }} -set network.@switch_vlan[-1].ports={{s(port.switch.port + 't ' + port.swconfig)}} -{% endfor %} diff --git a/renderer/templates/broadband.uc b/renderer/templates/broadband.uc deleted file mode 100644 index e1bd0f1..0000000 --- a/renderer/templates/broadband.uc +++ /dev/null @@ -1,100 +0,0 @@ -{% -/* find and replace the first upstream interface using vid with - * the broadband settings */ -let uplink = ethernet.get_interface("upstream", 0); -if (!uplink) - return; - -if (index([ "wwan", "pppoe", "static", "dhcp", "wds" ], broadband.protocol) < 0) - return; - -for (let k, v in uplink) - if (index([ "vlan", "index" ], k) < 0) - delete uplink[k]; - -uplink.name = "ISP_UPLINK"; -uplink.role = "upstream"; -uplink.vlan = { id: 0 }; -uplink.metric = 1; - -if (broadband.protocol == "wwan") { - let wwan = { }; - - wwan.protocol = 'wwan'; - wwan.modem_type = broadband['modem-type'] || 'wwan'; - wwan.pin_code = broadband['pin-code'] || ''; - wwan.access_point_name = broadband['access-point-name'] || ''; - wwan.packet_data_protocol = broadband['packet-data-protocol'] || 'dual-stack'; - wwan.authentication_type = broadband['authentication-type'] || 'none'; - wwan.username = broadband.username || ''; - wwan.password = broadband.password || ''; - - uplink.broad_band = wwan; -} - -if (broadband.protocol == "pppoe") { - let pppoe = { }; - - pppoe.protocol = 'pppoe'; - pppoe.username = broadband['user-name'] || ''; - pppoe.password = broadband.password || ''; - pppoe.timeout = broadband.timeout || '30'; - - uplink.broad_band = pppoe; - uplink.ethernet = [ - { - "select_ports": [ "WAN*" ] - } - ]; -} - -if (broadband.protocol == "static") { - uplink.ethernet = [ - { - "select_ports": [ "WAN*" ] - } - ]; - uplink.ipv4 = { - addressing: "static", - subnet: broadband['ipv4-address'], - gateway: broadband['ipv4-gateway'], - use_dns: broadband['use-dns'], - }; -} - -if (broadband.protocol == "dhcp") { - uplink.ethernet = [ - { - "select_ports": [ "WAN*" ] - } - ]; - uplink.ipv4 = { - addressing: "dynamic", - }; -} - -if (broadband.protocol == "wds") { - uplink.ipv4 = { - addressing: "dynamic", - }; - - let wds = { - "name": broadband.ssid, - "wifi_bands": [ - "2G", "5G" - ], - "bss_mode": "wds-sta", - "encryption": { - "proto": broadband.encryption, - "key": broadband.passphrase, - "ieee80211w": "optional" - } - }; - - if (!uplink.ssids) - uplink.ssids = [ wds ]; - else - push(uplink.ssids, wds); -} - -%} diff --git a/renderer/templates/config_raw.uc b/renderer/templates/config_raw.uc deleted file mode 100644 index 359addd..0000000 --- a/renderer/templates/config_raw.uc +++ /dev/null @@ -1,5 +0,0 @@ - -# Raw Configuration -{% for (let config in config_raw): %} -{{ config[0] }} {{ config[1] }}{{config[2] ? '=' + config[2] : ''}} -{% endfor %} diff --git a/renderer/templates/eap_users.uc b/renderer/templates/eap_users.uc deleted file mode 100644 index 48c6987..0000000 --- a/renderer/templates/eap_users.uc +++ /dev/null @@ -1,4 +0,0 @@ -{% for (let user in users): %} -"{{ user.user_name }}" PWD "{{ user.password }}" -{% endfor %} -* TLS,TTLS diff --git a/renderer/templates/ethernet.uc b/renderer/templates/ethernet.uc deleted file mode 100644 index 24342b2..0000000 --- a/renderer/templates/ethernet.uc +++ /dev/null @@ -1,13 +0,0 @@ -{% let eth_ports = ethernet.lookup_by_select_ports(ports.select_ports) %} -{% for (let port in eth_ports): - port = replace(port, '.', '_'); -%} -set network.{{ port }}=device -set network.{{ port }}.name={{ s(port) }} -set network.{{ port }}.ifname={{ s(port) }} -set network.{{ port }}.enabled={{ ports.enabled }} -{% if (!ports.speed && !ports.duplex) continue %} -set network.{{ port }}.speed={{ ports.speed }} -set network.{{ port }}.duplex={{ ports.duplex == "full" ? true : false }} - -{% endfor %} diff --git a/renderer/templates/interface.uc b/renderer/templates/interface.uc deleted file mode 100644 index 353be1a..0000000 --- a/renderer/templates/interface.uc +++ /dev/null @@ -1,216 +0,0 @@ -{% - let has_downstream_relays = false; - let dest; - - // Skip interfaces previously marked as conflicting. - if (interface.conflicting) { - warn("Skipping conflicting interface declaration"); - - return; - } - - // Skip upstream interfaces that try to use a wireguard overlay - if (interface.role == 'upstream' && 'wireguard-overlay' in interface.services) { - warn("Skipping interface. wireguard-overlay is not allowed on upstream interfaces."); - - return; - } - - // Check this interface for role/vlan uniqueness... - let this_vid = interface.vlan.id || interface.vlan.dyn_id; - - for (let other_interface in state.interfaces) { - if (other_interface == interface) - continue; - - if (!other_interface.ethernet && length(interface.ssids) == 1) - continue; - - let other_vid = other_interface.vlan.id || ''; - - if (interface.role === other_interface.role && this_vid === other_vid) { - warn("Multiple interfaces with same role and VLAN ID defined, ignoring conflicting interface"); - other_interface.conflicting = true; - } - - if (other_interface.role == 'downstream' && - other_interface.ipv6 && - other_interface.ipv6.dhcpv6 && - other_interface.ipv6.dhcpv6.mode == 'relay') - has_downstream_relays = true; - } - - // check if a downstream interface with a vlan has a matching upstream interface - if (ethernet.has_vlan(interface) && interface.role == "downstream" && index(vlans, this_vid) < 0) { - warn("Trying to create a downstream interface with a VLAN ID, without matching upstream interface."); - return; - } - - // reject static config that has no subnet - if (interface.role == 'upstream' && interface.ipv4?.addressing == 'static') - if (!interface.ipv4?.subnet || !interface.ipv4?.use_dns || !interface.ipv4?.gateway) - die('invalid static interface settings'); - - // resolve auto prefixes - if (wildcard(interface.ipv4?.subnet, 'auto/*')) { - try { - interface.ipv4.subnet = ipcalc.generate_prefix(state, interface.ipv4.subnet, false); - } - catch (e) { - warn("Unable to allocate a suitable IPv4 prefix: %s, ignoring interface", e); - return; - } - } - - if (wildcard(interface.ipv6?.subnet, 'auto/*')) { - try { - interface.ipv6.subnet = ipcalc.generate_prefix(state, interface.ipv6.subnet, true); - } - catch (e) { - warn("Unable to allocate a suitable IPv6 prefix: %s, ignoring interface", e); - return; - } - } - - // Captive Portal is only supported on downstream interfaces - if (interface.captive && interface.role != 'downstream') { - warn("Trying to create a Captive Portal on a none downstream interface."); - return; - } - - // Port forwardings are only supported on downstream interfaces - if ((interface.ipv4?.port_forward || interface.ipv6?.port_forward) && interface.role != 'downstream') { - warn("Port forwardings are only supported on downstream interfaces."); - return; - } - - // Traffic accept rules are only supported on downstream interfaces - if (interface.ipv6?.traffic_allow && interface.role != 'downstream') { - warn("Traffic accept rules are only supported on downstream interfaces."); - return; - } - - // Gather related BSS modes and ethernet ports. - let bss_modes = map(interface.ssids, ssid => ssid.bss_mode); - let eth_ports = ethernet.lookup_by_interface_vlan(interface); - let swconfig; - if (interface.role == 'upstream') - swconfig = ethernet.switch_by_interface_vlan(interface); - - // If at least one station mode SSID is part of this interface then we must - // not bridge at all. Having any other SSID or any number of matching ethernet - // ports in such a case is a semantic error. - if ('sta' in bss_modes && (length(eth_ports) > 0 || length(bss_modes) > 1)) { - warn("Station mode SSIDs cannot be bridged with ethernet ports or other SSIDs, ignoring interface"); - - return; - } - - // Compute unique logical name and netdev name to use - let name = ethernet.calculate_name(interface); - let bridgedev = 'up'; - if (capab.platform != "switch" && interface.role == "downstream") - bridgedev = 'down'; - let netdev = name; - let network = name; - - // Determine the IPv4 and IPv6 configuration modes and figure out if we - // can set them both in a single interface (automatic) or whether we need - // two logical interfaces due to different protocols. - let ipv4_mode = interface.ipv4 ? interface.ipv4.addressing : 'none'; - let ipv6_mode = interface.ipv6 ? interface.ipv6.addressing : 'none'; - - // If no metric is defined explicitly, any upstream interfaces will default - // to 5 and downstream interfaces will default to 10 - if (!interface.metric && interface.role == "upstream") - interface.metric = 5; - if (!interface.metric && interface.role == "downstream") - interface.metric = 10; - - // If this interface is a tunnel, we need to create the interface - // in a different way - let tunnel_proto = interface.tunnel ? interface.tunnel.proto : ''; - - // - // Create the actual UCI sections - // - - if (interface.broad_band) { - include("interface/broadband.uc", { interface, name, location, eth_ports, raw_ports }); - return; - } - - // tunnel interfaces need additional sections - if (tunnel_proto in [ "mesh", "l2tp", "vxlan", "gre", "gre6" ]) - include("interface/" + tunnel_proto + ".uc", { interface, name, eth_ports, location, netdev, ipv4_mode, ipv6_mode, this_vid }); - - if (!interface.ethernet && length(interface.ssids) == 1 && !tunnel_proto && !("vxlan-overlay" in interface.services)) { - if (interface.role == 'downstream') - interface.type = 'bridge'; - netdev = ''; - } else if (tunnel_proto == 'vxlan') { - netdev = '@' + name + '_vx'; - interface.type = 'bridge'; - } else if (tunnel_proto != 'gre' && tunnel_proto != 'gre6') - // anything else requires a bridge-vlan - include("interface/bridge-vlan.uc", { interface, name, eth_ports, this_vid, bridgedev, swconfig }); - - if (interface.role == "downstream" && "wireguard-overlay" in interface.services) - dest = 'unet'; - - include("interface/common.uc", { - name, this_vid, netdev, - ipv4_mode, ipv4: interface.ipv4 || {}, - ipv6_mode, ipv6: interface.ipv6 || {} - }); - - include('interface/firewall.uc', { name, ipv4_mode, ipv6_mode, dest }); - - if (interface.ipv4 || interface.ipv6) { - include('interface/dhcp.uc', { - ipv4: interface.ipv4 || {}, - ipv6: interface.ipv6 || {}, - has_downstream_relays - }); - } - - let count = 0; - for (let i, ssid in interface.ssids) { - let modes = (ssid.bss_mode == "wds-repeater") ? - [ "wds-sta", "wds-ap" ] : [ ssid.bss_mode ]; - for (let mode in modes) { - include('interface/ssid.uc', { - location: location + '/ssids/' + i, - ssid: { ...ssid, bss_mode: mode }, - count, - name, - network, - }); - if (ssid?.encryption?.proto == 'owe-transition') { - ssid.encryption.proto = 'none'; - include('interface/ssid.uc', { - location: location + '/ssids/' + i + '_owe', - ssid: { ...ssid, bss_mode: mode }, - count, - name, - network, - owe: true, - }); - - } - count++; - } - } - - if (interface.captive) - include('interface/captive.uc', { name }); -%} -{% if (tunnel_proto == 'mesh'): %} -set network.{{ name }}.batman=1 -{% endif %} - -{% if (interface.role == "downstream" && "wireguard-overlay" in interface.services): %} -add network rule -set network.@rule[-1].in='{{name}}' -set network.@rule[-1].lookup='{{ routing_table.get('wireguard_overlay') }}' -{% endif %} diff --git a/renderer/templates/interface/bridge-vlan.uc b/renderer/templates/interface/bridge-vlan.uc deleted file mode 100644 index 2e08c68..0000000 --- a/renderer/templates/interface/bridge-vlan.uc +++ /dev/null @@ -1,64 +0,0 @@ -add network bridge-vlan -set network.@bridge-vlan[-1].device={{ bridgedev }} -set network.@bridge-vlan[-1].vlan={{ this_vid }} -{% for (let port in keys(eth_ports)): %} -add_list network.@bridge-vlan[-1].ports={{ port }}{{ ethernet.port_vlan(interface, eth_ports[port]) }} -{% endfor %} -{% if (interface.tunnel?.proto == "mesh"): %} -add_list network.@bridge-vlan[-1].ports=batman{{ ethernet.has_vlan(interface) ? "." + this_vid + ":t" : '' }} -{% endif %} -{% if (interface.tunnel?.proto == "vxlan"): %} -add_list network.@bridge-vlan[-1].ports={{ name }}_vx -{% endif %} -{% if (interface.tunnel?.proto == "gre"): %} -add_list network.@bridge-vlan[-1].ports=gre4t-gre.{{ interface.vlan.id }} -{% endif %} -{% if (interface.tunnel?.proto == "gre6"): %} -add_list network.@bridge-vlan[-1].ports=gre6t-greip6.{{ interface.vlan.id }} -{% endif %} -{% if ('vxlan-overlay' in interface.services): %} -add_list network.@bridge-vlan[-1].ports=vx-unet -{% endif %} -{% if (interface.bridge): %} -set network.@bridge-vlan[-1].txqueuelen={{ interface.bridge.tx_queue_len }} -set network.@bridge-vlan[-1].isolate={{interface.bridge.isolate_ports }} -set network.@bridge-vlan[-1].mtu={{ interface.bridge.mtu }} -{% endif %} - -add network device -set network.@device[-1].type=8021q -set network.@device[-1].name={{ name }} -set network.@device[-1].ifname={{ bridgedev }} -set network.@device[-1].vid={{ this_vid }} - -{% if (interface.vlan_awareness?.first): %} -{% let vlan = interface.vlan_awareness.first; - if (interface.vlan_awareness.last) - vlan += '-' + interface.vlan_awareness.last; %} -{% for (let port in keys(eth_ports)): %} -add network device -set network.@device[-1].name={{ port }} -set network.@device[-1].vlan={{ vlan }} -{% endfor %} -{% if (interface.role == 'upstream'): %} -set network.up.vlan={{ vlan }} -{% endif %} -{% if (interface.role == 'downstream'): %} -set network.down.vlan={{ vlan }} -{% endif %} -{% endif %} - -{% if (interface.role == 'upstream'): %} -{% for (let port in keys(eth_ports)): %} -set udevstats.{{ port }}=device -set udevstats.{{ port }}.name={{ s(port) }} -add_list udevstats.{{ port }}.vlan={{ s(interface.vlan.id || 0) }} -{% endfor %} -{% endif %} - -{% if (interface.vlan.id && swconfig): %} -add network switch_vlan -set network.@switch_vlan[-1].device={{ s(swconfig.name) }} -set network.@switch_vlan[-1].vlan={{ s(this_vid) }} -set network.@switch_vlan[-1].ports={{s(swconfig.ports)}} -{% endif %} diff --git a/renderer/templates/interface/broadband.uc b/renderer/templates/interface/broadband.uc deleted file mode 100644 index f162ff8..0000000 --- a/renderer/templates/interface/broadband.uc +++ /dev/null @@ -1,45 +0,0 @@ -{% if (interface.broad_band.protocol == 'wwan'): %} -{% -function match_pdptype() { - let pdptypes = { - 'ipv4': 'ip', - 'ipv6': 'ipv6', - 'dual-stack': 'ipv4v6' - }; - return pdptypes[interface.broad_band.packet_data_protocol]; -} - -function match_authtype() { - if (interface.broad_band.authentication_type == 'papchap') - return 'both'; - return interface.broad_band.authentication_type; -} -%} - -set network.{{ name }}=interface -set network.{{ name }}.ucentral_name={{ s(interface.name) }} -set network.{{ name }}.ucentral_path={{ s(location) }} -set network.{{ name }}.proto={{ s(interface.broad_band.modem_type) }} -set network.{{ name }}.pincode={{ s(interface.broad_band.pin_code) }} -set network.{{ name }}.apn={{ s(interface.broad_band.access_point_name) }} -set network.{{ name }}.device='/dev/cdc-wdm0' -set network.{{ name }}.pdptype={{ s(match_pdptype()) }} -set network.{{ name }}.auth={{ s(match_authtype()) }} -set network.{{ name }}.username={{ s(interface.broad_band.user_name) }} -set network.{{ name }}.password={{ s(interface.broad_band.password) }} -{% endif %} - -{% if (interface.broad_band.protocol == 'pppoe'): %} - -set network.{{ name }}=interface -set network.{{ name }}.ucentral_name={{ s(interface.name) }} -set network.{{ name }}.ucentral_path={{ s(location) }} -set network.{{ name }}.ifname={{ s(keys(eth_ports)[0]) }} -set network.{{ name }}.proto='pppoe' -set network.{{ name }}.username={{ s(interface.broad_band.user_name) }} -set network.{{ name }}.password={{ s(interface.broad_band.password) }} -set network.{{ name }}.timeout={{ s(interface.broad_band.timeout) }} -{% endif %} - - -{% include('firewall.uc', { name, ipv4: true, ipv6: true }); %} diff --git a/renderer/templates/interface/captive.uc b/renderer/templates/interface/captive.uc deleted file mode 100644 index b1e3e92..0000000 --- a/renderer/templates/interface/captive.uc +++ /dev/null @@ -1,119 +0,0 @@ -{% -if (config.radius_gw_proxy) - services.set_enabled("radius-gw-proxy", true); - -function radius_proxy_tlv(server, port, name) { - let tlv = replace(serial, /^(..)(..)(..)(..)(..)(..)$/, "$1$2$3$4$5$6") + sprintf(":%s:%s:%s", server, port, name); - return tlv; -} - -captive.interface(section, config); -let name = split(section, '_')[0]; - -if (!captive.web_root) - system('cp -r /www-uspot /tmp/ucentral/'); -else { - let fs = require('fs'); - fs.mkdir('/tmp/ucentral/www-uspot'); - let web_root = fs.open('/tmp/ucentral/web-root.tar', 'w'); - web_root.write(b64dec(captive.web_root)); - web_root.close(); - system('tar x -C /tmp/ucentral/www-uspot -f /tmp/ucentral/web-root.tar'); -} - -if (captive.radius_gw_proxy) - services.set_enabled("radius-gw-proxy", true); -%} - -# Captive Portal service configuration - -set uspot.{{ section }}=uspot -set uspot.{{ section }}.auth_mode={{ s(config.auth_mode) }} -set uspot.{{ section }}.web_root={{ b(config.web_root) }} -set uspot.{{ section }}.idle_timeout={{ config.idle_timeout }} -set uspot.{{ section }}.session_timeout={{ config.session_timeout }} - -{% if (config.auth_mode in [ 'radius', 'uam']): %} -{% if (config.radius_gw_proxy): %} -set uspot.{{ section }}.auth_server='127.0.0.1' -set uspot.{{ section }}.auth_port='1812' -set uspot.{{ section }}.auth_proxy={{ s(radius_proxy_tlv(config.auth_server, config.auth_port, 'captive')) }} -{% if (config.acct_server): %} -set uspot.{{ section }}.acct_server='127.0.0.1' -set uspot.{{ section }}.acct_port='1813' -set uspot.{{ section }}.acct_proxy={{ s(radius_proxy_tlv(config.acct_server, config.acct_port, 'captive')) }} -{% endif %} -{% else %} -set uspot.{{ section }}.auth_server={{ s(config.auth_server) }} -set uspot.{{ section }}.auth_port={{ s(config.auth_port) }} -set uspot.{{ section }}.acct_server={{ s(config.acct_server) }} -set uspot.{{ section }}.acct_port={{ s(config.acct_port) }} -{% endif %} -set uspot.{{ section }}.auth_secret={{ s(config.auth_secret) }} -set uspot.{{ section }}.acct_secret={{ s(config.acct_secret) }} -set uspot.{{ section }}.acct_interval={{ config.acct_interval }} -{% endif %} - -{% if (config.auth_mode == 'credentials'): %} -{% for (let cred in config.credentials): %} -add uspot credentials -set uspot.@credentials[-1].username={{ s(cred.username) }} -set uspot.@credentials[-1].password={{ s(cred.password) }} -set uspot.@credentials[-1].interface={{ s(section) }} -{% endfor %} -{% endif %} - -{% if (config.auth_mode == 'uam'): %} -{% -let math = require('math'); -let challenge = ""; -for (let i = 0; i < 16; i++) - challenge += sprintf('%02x', math.rand() % 255); -%} -set uspot.{{ section }}.challenge={{ s(challenge) }} - -set uspot.{{ section }}.uam_port={{ s(config.uam_port) }} -set uspot.{{ section }}.uam_secret={{ s(config.uam_secret) }} -set uspot.{{ section }}.uam_server={{ s(config.uam_server) }} -set uspot.{{ section }}.nasid={{ s(config.nasid) }} -set uspot.{{ section }}.nasmac={{ s(config.nasmac || serial) }} -set uspot.{{ section }}.ssid={{ s(config.ssid) }} -set uspot.{{ section }}.mac_format={{ s(config.mac_format) }} -set uspot.{{ section }}.final_redirect_url={{ s(config.final_redirect_url) }} -set uspot.{{ section }}.mac_auth={{ b(config.mac_auth) }} - -set uhttpd.uam{{ config.uam_port }}=uhttpd -set uhttpd.@uhttpd[-1].redirect_https='0' -set uhttpd.@uhttpd[-1].rfc1918_filter='1' -set uhttpd.@uhttpd[-1].max_requests='5' -set uhttpd.@uhttpd[-1].max_connections='100' -set uhttpd.@uhttpd[-1].cert='/etc/uhttpd.crt' -set uhttpd.@uhttpd[-1].key='/etc/uhttpd.key' -set uhttpd.@uhttpd[-1].script_timeout='60' -set uhttpd.@uhttpd[-1].network_timeout='30' -set uhttpd.@uhttpd[-1].http_keepalive='20' -set uhttpd.@uhttpd[-1].tcp_keepalive='1' -add_list uhttpd.@uhttpd[-1].listen_http='0.0.0.0:{{ config.uam_port }}' -add_list uhttpd.@uhttpd[-1].listen_http='[::]:{{ config.uam_port }}' -set uhttpd.@uhttpd[-1].home=/tmp/ucentral/www-uspot -add_list uhttpd.@uhttpd[-1].ucode_prefix='/logon=/usr/share/uspot/handler-uam.uc' -add_list uhttpd.@uhttpd[-1].ucode_prefix='/logoff=/usr/share/uspot/handler-uam.uc' -add_list uhttpd.@uhttpd[-1].ucode_prefix='/logout=/usr/share/uspot/handler-uam.uc' - -set firewall.{{ name + config.uam_port}}_1=rule -set firewall.@rule[-1].name='Allow-UAM-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='{{ config.uam_port }}' -set firewall.@rule[-1].proto='tcp' -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].mark='1/127' - -set firewall.{{ name + config.uam_port}}_2=rule -set firewall.@rule[-1].name='Allow-UAM-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='{{ config.uam_port }}' -set firewall.@rule[-1].proto='tcp' -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].mark='2/127' - -{% endif %} diff --git a/renderer/templates/interface/common.uc b/renderer/templates/interface/common.uc deleted file mode 100644 index 535f239..0000000 --- a/renderer/templates/interface/common.uc +++ /dev/null @@ -1,39 +0,0 @@ -{% let afnames = ethernet.calculate_names(interface) %} -{% if (length(afnames) >= 2): %} -set network.{{ netdev }}=interface -set network.{{ netdev }}.ucentral_name={{ s(interface.name) }} -set network.{{ netdev }}.ucentral_path={{ s(location) }} -set network.{{ netdev }}.ifname={{ netdev }} -set network.{{ netdev }}.metric={{ interface.metric }} -set network.{{ netdev }}.proto=none -{% endif %} -{% for (let afidx, afname in afnames): %} -set network.{{ afname }}=interface -set network.{{ afname }}.ucentral_name={{ s(interface.name) }} -set network.{{ afname }}.ucentral_path={{ s(location) }} -set network.{{ afname }}.ifname={{ netdev }} -set network.{{ afname }}.metric={{ interface.metric }} -set network.{{ afname }}.mtu={{ interface.mtu }} -set network.{{ afname }}.type={{ interface.type }} -set network.{{ afname }}.auto={{ interface.auto_start }} -{% if (ipv4_mode == 'static' || ipv6_mode == 'static'): %} -set network.{{ afname }}.proto=static -{% elif ((length(afnames) == 1 || afidx == 0) && ipv4_mode == 'dynamic'): %} -set network.{{ afname }}.proto=dhcp -{% elif ((length(afnames) == 1 || afidx == 1) && ipv6_mode == 'dynamic'): %} -set network.{{ afname }}.proto=dhcpv6 -{% else %} -set network.{{ afname }}.proto=none -{% endif %} -{% if (interface.role == "downstream" && ethernet.has_vlan(interface)): %} -add network rule -set network.@rule[-1].in={{ afname }} -set network.@rule[-1].lookup={{ routing_table.get(interface.vlan.id) }} -{% endif %} -{% if ((length(afnames) == 1 && ipv4_mode != 'none') || (afidx == 0 && ipv4_mode != 'none')): %} -{% include('ipv4.uc', { name: afname }) %} -{% endif %} -{% if ((length(afnames) == 1 && ipv6_mode != 'none') || (afidx == 1 && ipv6_mode != 'none')): %} -{% include('ipv6.uc', { name: afname }) %} -{% endif %} -{% endfor %} diff --git a/renderer/templates/interface/dhcp.uc b/renderer/templates/interface/dhcp.uc deleted file mode 100644 index 2600615..0000000 --- a/renderer/templates/interface/dhcp.uc +++ /dev/null @@ -1,60 +0,0 @@ -{% let name = ethernet.calculate_name(interface) %} -{% let dhcp = ipv4.dhcp || { ignore: 1 } %} -{% let dhcpv6 = ipv6.dhcpv6 || {} %} - -set dhcp.{{ name }}=dhcp -set dhcp.{{ name }}.interface={{ s(ethernet.calculate_ipv4_name(interface)) }} -set dhcp.{{ name }}.start={{ dhcp.lease_first }} -set dhcp.{{ name }}.limit={{ dhcp.lease_count }} -set dhcp.{{ name }}.leasetime={{ dhcp.lease_time }} -set dhcp.{{ name }}.ignore={{ b(dhcp.ignore) }} -{% if (interface.role != 'upstream'): %} -{% if (dhcpv6.mode == 'hybrid'): %} -set dhcp.{{ name }}.ra=server -set dhcp.{{ name }}.dhcpv6=server -set dhcp.{{ name }}.ndp=disabled -set dhcp.{{ name }}.ra_slaac=1 -add_list dhcp.{{ name }}.ra_flags=other-config -add_list dhcp.{{ name }}.ra_flags=managed-config -{% elif (dhcpv6.mode == 'stateful'): %} -set dhcp.{{ name }}.ra=server -set dhcp.{{ name }}.dhcpv6=server -set dhcp.{{ name }}.ndp=disabled -set dhcp.{{ name }}.ra_slaac=0 -add_list dhcp.{{ name }}.ra_flags=other-config -add_list dhcp.{{ name }}.ra_flags=managed-config -{% elif (dhcpv6.mode == 'stateless'): %} -set dhcp.{{ name }}.ra=server -set dhcp.{{ name }}.dhcpv6=server -set dhcp.{{ name }}.ndp=disabled -set dhcp.{{ name }}.ra_slaac=1 -add_list dhcp.{{ name }}.ra_flags=other-config -{% elif (dhcpv6.mode == 'relay'): %} -set dhcp.{{ name }}.ra=relay -set dhcp.{{ name }}.dhcpv6=relay -set dhcp.{{ name }}.ndp=relay -{% else %} -set dhcp.{{ name }}.ra=disabled -set dhcp.{{ name }}.dhcpv6=disabled -set dhcp.{{ name }}.ndp=disabled -{% endif %} -set dhcp.{{ name }}.prefix_filter={{ s(dhcpv6.filter_prefix) }} -set dhcp.{{ name }}.dns_service={{ b(!length(dhcpv6.announce_dns)) }} -{% for (let i, addr in dhcpv6.announce_dns): %} -add_list dhcp.{{ name }}.dns={{ s(addr) }} -{% endfor %} -{% else %} -set dhcp.{{ name }}.master={{ b(has_downstream_relays) }} -set dhcp.{{ name }}.ra={{ has_downstream_relays ? 'relay' : 'disabled' }} -set dhcp.{{ name }}.dhcpv6={{ has_downstream_relays ? 'relay' : 'disabled' }} -set dhcp.{{ name }}.ndp={{ has_downstream_relays ? 'relay' : 'disabled' }} -{% endif %} -{% for (let lease in interface.ipv4.dhcp_leases): %} - -add dhcp host -set dhcp.@host[-1].hostname={{ lease.hostname }} -set dhcp.@host[-1].mac={{ lease.macaddr }} -set dhcp.@host[-1].ip={{ lease.static_lease_offset }} -set dhcp.@host[-1].leasetime={{ lease.lease_time }} -set dhcp.@host[-1].instance={{ s(name) }} -{% endfor %} diff --git a/renderer/templates/interface/firewall.uc b/renderer/templates/interface/firewall.uc deleted file mode 100644 index 85044aa..0000000 --- a/renderer/templates/interface/firewall.uc +++ /dev/null @@ -1,171 +0,0 @@ - -add firewall zone -set firewall.@zone[-1].name={{ s(name) }} -{% if (interface.role == 'upstream'): %} -set firewall.@zone[-1].input='REJECT' -set firewall.@zone[-1].output='ACCEPT' -set firewall.@zone[-1].forward='REJECT' -set firewall.@zone[-1].masq=1 -set firewall.@zone[-1].mtu_fix=1 -{% else %} -set firewall.@zone[-1].input='REJECT' -set firewall.@zone[-1].output='ACCEPT' -set firewall.@zone[-1].forward='ACCEPT' - -add firewall forwarding -set firewall.@forwarding[-1].src={{ s(name) }} -set firewall.@forwarding[-1].dest='{{ s(dest || ethernet.find_interface("upstream", interface.vlan.id)) }}' -{% endif %} -{% for (let network in networks || ethernet.calculate_names(interface)): %} -add_list firewall.@zone[-1].network={{ s(network) }} -{% endfor %} - -add firewall rule -set firewall.@rule[-1].name='Allow-Ping' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].proto='icmp' -set firewall.@rule[-1].icmp_type='echo-request' -set firewall.@rule[-1].family='ipv4' -set firewall.@rule[-1].target='ACCEPT' - -add firewall rule -set firewall.@rule[-1].name='Allow-IGMP' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].proto='igmp' -set firewall.@rule[-1].family='ipv4' -set firewall.@rule[-1].target='ACCEPT' - - -{% if (ipv4_mode || !ipv6_mode): %} -add firewall rule -set firewall.@rule[-1].name='Support-UDP-Traceroute' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].dest_port='33434:33689' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].family='ipv4' -set firewall.@rule[-1].target='REJECT' -set firewall.@rule[-1].enabled='false' - -add firewall rule -set firewall.@rule[-1].name='Allow-DHCP-Renew' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].dest_port='68' -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].family='ipv4' -{% endif %} - -{% if (ipv6_mode || !ipv4_mode): %} -add firewall rule -set firewall.@rule[-1].name='Allow-DHCPv6' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].src_ip='fc00::/6' -set firewall.@rule[-1].dest_ip='fc00::/6' -set firewall.@rule[-1].dest_port='546' -set firewall.@rule[-1].family='ipv6' -set firewall.@rule[-1].target='ACCEPT' - -add firewall rule -set firewall.@rule[-1].name='Allow-MLD' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].proto='icmp' -set firewall.@rule[-1].src_ip='fe80::/10' -set firewall.@rule[-1].icmp_type='130/0' -set firewall.@rule[-1].icmp_type='131/0' -set firewall.@rule[-1].icmp_type='132/0' -set firewall.@rule[-1].icmp_type='143/0' -set firewall.@rule[-1].family='ipv6' -set firewall.@rule[-1].target='ACCEPT' - -add firewall rule -set firewall.@rule[-1].name='Allow-ICMPv6-Input' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].proto='icmp' -add_list firewall.@rule[-1].icmp_type='echo-request' -add_list firewall.@rule[-1].icmp_type='echo-reply' -add_list firewall.@rule[-1].icmp_type='destination-unreachable' -add_list firewall.@rule[-1].icmp_type='packet-too-big' -add_list firewall.@rule[-1].icmp_type='time-exceeded' -add_list firewall.@rule[-1].icmp_type='bad-header' -add_list firewall.@rule[-1].icmp_type='unknown-header-type' -add_list firewall.@rule[-1].icmp_type='router-solicitation' -add_list firewall.@rule[-1].icmp_type='neighbour-solicitation' -add_list firewall.@rule[-1].icmp_type='router-advertisement' -add_list firewall.@rule[-1].icmp_type='neighbour-advertisement' -set firewall.@rule[-1].limit='1000/sec' -set firewall.@rule[-1].family='ipv6' -set firewall.@rule[-1].target='ACCEPT' - -add firewall rule -set firewall.@rule[-1].name='Allow-ICMPv6-Forward' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].dest='*' -set firewall.@rule[-1].proto='icmp' -add_list firewall.@rule[-1].icmp_type='echo-request' -add_list firewall.@rule[-1].icmp_type='echo-reply' -add_list firewall.@rule[-1].icmp_type='destination-unreachable' -add_list firewall.@rule[-1].icmp_type='packet-too-big' -add_list firewall.@rule[-1].icmp_type='time-exceeded' -add_list firewall.@rule[-1].icmp_type='bad-header' -add_list firewall.@rule[-1].icmp_type='unknown-header-type' -set firewall.@rule[-1].limit='1000/sec' -set firewall.@rule[-1].family='ipv6' -set firewall.@rule[-1].target='ACCEPT' -{% endif %} - -{% if (interface.role == "downstream"): %} -add firewall rule -set firewall.@rule[-1].name='Allow-DNS-{{ name }}' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].dest_port='53' -set firewall.@rule[-1].family='ipv4' -add_list firewall.@rule[-1].proto='tcp' -add_list firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' - -add firewall rule -set firewall.@rule[-1].name='Allow-DHCP-{{ name }}' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].dest_port=67 -set firewall.@rule[-1].family='ipv4' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' - -add firewall rule -set firewall.@rule[-1].name='Allow-DHCPv6-{{ name }}' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].dest_port=547 -set firewall.@rule[-1].family='ipv6' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' -{% endif %} - -{% - for (let forward in interface.ipv4?.port_forward) - include('firewall/forward.uc', { - forward, - family: 'ipv4', - source_zone: ethernet.find_interface('upstream', interface.vlan?.id), - destination_zone: name, - destination_subnet: interface.ipv4.subnet - }); - - for (let forward in interface.ipv6?.port_forward) - include('firewall/forward.uc', { - forward, - family: 'ipv6', - source_zone: ethernet.find_interface('upstream', interface.vlan?.id), - destination_zone: name, - destination_subnet: interface.ipv6.subnet - }); - - for (let allow in interface.ipv6?.traffic_allow) - include('firewall/allow.uc', { - allow, - family: 'ipv6', - source_zone: ethernet.find_interface('upstream', interface.vlan?.id), - destination_zone: name, - destination_subnet: interface.ipv6.subnet - }); -%} diff --git a/renderer/templates/interface/firewall/allow.uc b/renderer/templates/interface/firewall/allow.uc deleted file mode 100644 index 44ec208..0000000 --- a/renderer/templates/interface/firewall/allow.uc +++ /dev/null @@ -1,20 +0,0 @@ -add firewall rule -set firewall.@rule[-1].name='Allow traffic to {{ allow.destination_address }}' -set firewall.@rule[-1].family={{ s(family) }} -set firewall.@rule[-1].src={{ s(source_zone || '*') }} -set firewall.@rule[-1].dest={{ s(destination_zone) }} -{% for (let proto in ((allow.protocol in ['any', 'all', '*'] && (allow.source_ports || allow.destination_ports)) ? ['tcp', 'udp'] : [ allow.protocol ])): %} -add_list firewall.@rule[-1].proto={{ s(proto) }} -{% endfor %} -{% if (allow.source_address): %} -set firewall.@rule[-1].src_ip={{ s(allow.source_address) }} -{% endif %} -{% for (let sport in allow.source_ports): %} -add_list firewall.@rule[-1].src_port={{ s(sport) }} -{% endfor %} -set firewall.@rule[-1].dest_ip={{ ipcalc.expand_wildcard_address(allow.destination_address, destination_subnet) }} -{% for (let dport in allow.destination_ports): %} -add_list firewall.@rule[-1].dest_port={{ s(dport) }} -{% endfor %} -set firewall.@rule[-1].target=ACCEPT - diff --git a/renderer/templates/interface/firewall/forward.uc b/renderer/templates/interface/firewall/forward.uc deleted file mode 100644 index d9f8f80..0000000 --- a/renderer/templates/interface/firewall/forward.uc +++ /dev/null @@ -1,15 +0,0 @@ -{% if (true || source_zone): %} -add firewall redirect -set firewall.@redirect[-1].name='Forward port {{ forward.external_port }} to {{ forward.internal_address }}' -set firewall.@redirect[-1].family={{ s(family) }} -set firewall.@redirect[-1].src={{ s(source_zone || '*') }} -set firewall.@redirect[-1].dest={{ s(destination_zone) }} -{% for (let proto in ((forward.protocol in ['any', 'all', '*']) ? ['tcp', 'udp'] : [ forward.protocol ])): %} -add_list firewall.@redirect[-1].proto={{ s(proto) }} -{% endfor %} -set firewall.@redirect[-1].src_dport={{ s(forward.external_port) }} -set firewall.@redirect[-1].dest_ip={{ ipcalc.expand_wildcard_address(forward.internal_address, destination_subnet) }} -set firewall.@redirect[-1].dest_port={{ s(forward.internal_port) }} -set firewall.@redirect[-1].target=DNAT - -{% endif %} diff --git a/renderer/templates/interface/gre.uc b/renderer/templates/interface/gre.uc deleted file mode 100644 index 20fc909..0000000 --- a/renderer/templates/interface/gre.uc +++ /dev/null @@ -1,45 +0,0 @@ -{% -if (!interface.tunnel.peer_address) { - warn("A GRE tunnel requires a valid peer-address"); - return; -} -%} - -# GRE Configuration -set network.gre=interface -set network.gre.proto='gretap' -set network.gre.peeraddr='{{ interface.tunnel.peer_address }}' -set network.gre.nohostroute='1' -set network.gre.df='{{ b(interface.tunnel.dont_fragment) }}' - -{% -let suffix = ''; -let cfg = { - name: 'gretun', - netdev: 'gre4t-gre', - ipv4_mode, ipv4: interface.ipv4 || {}, - ipv6_mode, ipv6: interface.ipv6 || {} -}; - -if (ethernet.has_vlan(interface)) { - cfg.name = 'gretun_' + interface.vlan.id; - cfg.netdev = 'gre4t-gre.' + interface.vlan.id; - cfg.this_vid = interface.vlan.id; - suffix = '.' + interface.vlan.id; -} - -include("common.uc", cfg); -%} - -add firewall rule -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].family='ipv4' -set firewall.@rule[-1].proto='47' -set firewall.@rule[-1].name='Allow-GRE-{{ name }}' - -add network device -set network.@device[-1].name={{ s(name) }} -set network.@device[-1].type='bridge' -set network.@device[-1].ports='gre4t-gre{{ suffix }}' -set network.@device[-1].dhcp_healthcheck='{{ b(interface.tunnel.dhcp_healthcheck) }}' diff --git a/renderer/templates/interface/gre6.uc b/renderer/templates/interface/gre6.uc deleted file mode 100644 index fa3c931..0000000 --- a/renderer/templates/interface/gre6.uc +++ /dev/null @@ -1,44 +0,0 @@ -{% -if (!interface.tunnel.peer_address) { - warn("A GRE tunnel requires a valid peer-address"); - return; -} -%} - -# GRE Configuration -set network.greip6=interface -set network.greip6.proto='grev6tap' -set network.greip6.peer6addr='{{ interface.tunnel.peer_address }}' -set network.greip6.nohostroute='1' - -{% -let suffix = ''; -let cfg = { - name: 'gretun6', - netdev: 'gre6t-greip6', - ipv4_mode, ipv4: interface.ipv4 || {}, - ipv6_mode, ipv6: interface.ipv6 || {} -}; - -if (ethernet.has_vlan(interface)) { - cfg.name = 'gretun6_' + interface.vlan.id; - cfg.netdev = 'gre6t-greip6.' + interface.vlan.id; - cfg.this_vid = interface.vlan.id; - suffix = '.' + interface.vlan.id; -} - -include("common.uc", cfg); -%} - -add firewall rule -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].src={{ s(name) }} -set firewall.@rule[-1].family='ipv6' -set firewall.@rule[-1].proto='47' -set firewall.@rule[-1].name='Allow-GREv6-{{ name }}' - -add network device -set network.@device[-1].name={{ s(name) }} -set network.@device[-1].type='bridge' -set network.@device[-1].ports='gre6t-greip6{{ suffix }}' -set network.@device[-1].dhcp_healthcheck='{{ b(interface.tunnel.dhcp_healthcheck) }}' diff --git a/renderer/templates/interface/ipv4.uc b/renderer/templates/interface/ipv4.uc deleted file mode 100644 index ea1346e..0000000 --- a/renderer/templates/interface/ipv4.uc +++ /dev/null @@ -1,12 +0,0 @@ -{% if (interface.role == 'upstream' && ethernet.has_vlan(interface)): %} -set network.{{ name }}.ip4table={{ routing_table.get(this_vid) }} -{% endif %} -{% if (ipv4_mode == 'static'): %} -set network.{{ name }}.ipaddr={{ ipv4.subnet }} -set network.{{ name }}.gateway={{ ipv4.gateway }} -{% else %} -set network.{{ name }}.peerdns={{ b(!length(ipv4.use_dns)) }} -{% endif %} -{% for (let dns in ipv4.use_dns): %} -add_list network.{{ name }}.dns={{ dns }} -{% endfor %} diff --git a/renderer/templates/interface/ipv6.uc b/renderer/templates/interface/ipv6.uc deleted file mode 100644 index f7d5950..0000000 --- a/renderer/templates/interface/ipv6.uc +++ /dev/null @@ -1,10 +0,0 @@ -{% if (interface.role == 'upstream' && ethernet.has_vlan(interface)): %} -set network.{{ name }}.ip6table={{ routing_table.get(this_vid) }} -{% endif %} -{% if (ipv6_mode == 'static'): %} -set network.{{ name }}.ip6addr={{ ipv6.subnet }} -set network.{{ name }}.ip6gw={{ ipv6.gateway }} -set network.{{ name }}.ip6assign={{ ipv6.prefix_size || '64' }} -{% else %} -set network.{{ name }}.reqprefix={{ ipv6.prefix_size || 'auto' }} -{% endif %} diff --git a/renderer/templates/interface/l2tp.uc b/renderer/templates/interface/l2tp.uc deleted file mode 100644 index 502fd2c..0000000 --- a/renderer/templates/interface/l2tp.uc +++ /dev/null @@ -1,31 +0,0 @@ -{% -if (!interface.tunnel.server || !interface.tunnel.user_name || !interface.tunnel.password ) { - warn("A L2TP tunnel can only be created with a server, username and password"); - return; -} -%} - -# L2TP Configuration -set network.l2tp="interface" -set network.l2tp.proto="l2tp" -set network.l2tp.server={{ s(interface.tunnel.server) }} -set network.l2tp.username={{ s(interface.tunnel.user_name) }} -set network.l2tp.password={{ s(interface.tunnel.password) }} -set network.l2tp.ip4table="{{ routing_table.get(this_vid) }}" - -add firewall zone -set firewall.@zone[-1].name="l2tp" -set firewall.@zone[-1].network="l2tp" -set firewall.@zone[-1].input="REJECT" -set firewall.@zone[-1].forward="REJECT" -set firewall.@zone[-1].output="REJECT" -set firewall.@zone[-1].masq="1" -set firewall.@zone[-1].mtu_fix="1" - -add firewall forwarding -set firewall.@forwarding[-1].src="{{ name }}" -set firewall.@forwarding[-1].dest="l2tp" - -add network rule -set network.@rule[-1].in="{{ name }}" -set network.@rule[-1].lookup="{{ routing_table.get(this_vid) }}" diff --git a/renderer/templates/interface/mesh.uc b/renderer/templates/interface/mesh.uc deleted file mode 100644 index 9fe2776..0000000 --- a/renderer/templates/interface/mesh.uc +++ /dev/null @@ -1,17 +0,0 @@ - -set network.batman=interface -set network.batman.proto=batadv -set network.batman.multicast_mode=0 -set network.batman.distributed_arp_table=0 -set network.batman.orig_interval=5000 - -{% if (ethernet.has_vlan(interface)): %} -set network.batman_v{{ this_vid }}=interface -set network.batman_v{{ this_vid }}.proto=batadv_vlan -set network.batman_v{{ this_vid }}.ifname='batman.{{ this_vid }}' -{% else %} -set network.batman_mesh=interface -set network.batman_mesh.proto=batadv_hardif -set network.batman_mesh.master=batman -set network.batman_mesh.mtu=1532 -{% endif %} diff --git a/renderer/templates/interface/ssid.uc b/renderer/templates/interface/ssid.uc deleted file mode 100644 index 6efab7d..0000000 --- a/renderer/templates/interface/ssid.uc +++ /dev/null @@ -1,616 +0,0 @@ -{% - let purpose = { - "onboarding-ap": { - "name": "OpenWifi-onboarding", - "isolate_clients": true, - "hidden": true, - "wifi_bands": [ - "2G" - ], - "bss_mode": "ap", - "encryption": { - "proto": "wpa2", - "ieee80211w": "required" - }, - "certificates": { - "use_local_certificates": true - }, - "radius": { - "local": { - "server-identity": "uCentral-EAP" - } - } - }, - "onboarding-sta": { - "name": "OpenWifi-onboarding", - "wifi_bands": [ - "2G" - ], - "bss_mode": "sta", - "encryption": { - "proto": "wpa2", - "ieee80211w": "required" - }, - "certificates": { - "use_local_certificates": true - } - } - }; - - if (purpose[ssid.purpose]) - ssid = purpose[ssid.purpose]; - - let phys = []; - - for (let band in ssid.wifi_bands) - for (let phy in wiphy.lookup_by_band(band)) - if (phy.section) - push(phys, phy); - - if (!length(phys)) { - warn("Can't find any suitable radio phy for SSID '%s' settings", ssid.name); - - return; - } - - if (type(ssid.roaming) == 'bool') - ssid.roaming = { - message_exchange: true - }; - - if (ssid.roaming && ssid.encryption.proto in [ "wpa", "psk", "none" ]) { - delete ssid.roaming; - warn("Roaming requires wpa2 or later"); - } - - let certificates = ssid.certificates || {}; - if (certificates.use_local_certificates) { - cursor.load("system"); - let certs = cursor.get_all("system", "@certificates[-1]"); - certificates.ca_certificate = certs.ca; - certificates.certificate = certs.cert; - certificates.private_key = certs.key; - } - - if (ssid.radius?.dynamic_authorization && 'radius-gw-proxy' in ssid.services) { - ssid.radius.dynamic_authorization.host = '127.0.0.1'; - ssid.radius.dynamic_authorization.port = 3799; - } - - function validate_encryption_ap() { - if (ssid.encryption.proto in [ "wpa", "wpa2", "wpa-mixed", "wpa3", "wpa3-mixed", "wpa3-192", "psk2-radius" ] && - ssid.radius && ssid.radius.local && - length(certificates)) - return { - proto: ssid.encryption.proto, - eap_local: ssid.radius.local, - eap_user: "/tmp/ucentral/" + replace(location, "/", "_") + ".eap_user" - }; - - - if (ssid.encryption.proto in [ "wpa", "wpa2", "wpa-mixed", "wpa3", "wpa3-mixed", "wpa3-192", "psk2-radius" ] && - ssid.radius && ssid.radius.authentication && - ssid.radius.authentication.host && - ssid.radius.authentication.port && - ssid.radius.authentication.secret) - return { - proto: ssid.encryption.proto, - auth: ssid.radius.authentication, - acct: ssid.radius.accounting, - health: ssid.radius.health || {}, - dyn_auth: ssid.radius?.dynamic_authorization, - radius: ssid.radius - }; - warn("Can't find any valid encryption settings"); - return false; - } - - function validate_encryption_sta() { - if (ssid.encryption.proto in [ "wpa", "wpa2", "wpa-mixed", "wpa3", "wpa3-mixed", "wpa3-192" ] && - length(certificates)) - return { - proto: ssid.encryption.proto, - client_tls: certificates - }; - warn("Can't find any valid encryption settings"); - return false; - } - - function validate_encryption(phy) { - if ('6G' in phy.band && !(ssid?.encryption.proto in [ "wpa3", "wpa3-mixed", "wpa3-192", "sae", "sae-mixed", "owe" ])) { - warn("Invalid encryption settings for 6G band"); - return null; - } - - if (!ssid.encryption || ssid.encryption.proto in [ "none" ]) { - if (ssid.radius?.authentication?.mac_filter && - ssid.radius.authentication?.host && - ssid.radius.authentication?.port && - ssid.radius.authentication?.secret) - return { - proto: 'none', - auth: ssid.radius.authentication, - acct: ssid.radius.accounting, - dyn_auth: ssid.radius?.dynamic_authorization, - health: ssid.radius.health || {}, - radius: ssid.radius - }; - return { - proto: 'none', - dyn_auth: ssid.radius?.dynamic_authorization, - }; - } - - if (ssid?.encryption?.proto in [ "owe", "owe-transition" ]) - return { - proto: 'owe' - }; - - if (ssid.encryption.proto in [ "psk", "psk2", "psk-mixed", "sae", "sae-mixed" ] && - ssid.encryption.key) { - if (ssid.radius?.authentication?.mac_filter && - ssid.radius.authentication?.host && - ssid.radius.authentication?.port && - ssid.radius.authentication?.secret) - return { - proto: ssid.encryption.proto, - key: ssid.encryption.key, - auth: ssid.radius.authentication, - acct: ssid.radius.accounting, - dyn_auth: ssid.radius?.dynamic_authorization, - health: ssid.radius.health || {}, - radius: ssid.radius - }; - - return { - proto: ssid.encryption.proto, - key: ssid.encryption.key, - dyn_auth: ssid.radius?.dynamic_authorization, - }; - }; - - switch(ssid.bss_mode) { - case 'ap': - case 'wds-ap': - return validate_encryption_ap(); - - case 'sta': - case 'wds-sta': - return validate_encryption_sta(); - - } - warn("Can't find any valid encryption settings"); - } - - function match_ieee80211w(phy) { - if ('6G' in phy.band) - return 2; - - if (!ssid.encryption) - return 0; - - if (ssid.encryption.proto in [ "sae-mixed", "wpa3-mixed" ]) - return 1; - - if (ssid.encryption.proto in [ "sae", "wpa3", "wpa3-192" ]) - return 2; - - return index([ "disabled", "optional", "required" ], ssid.encryption.ieee80211w); - } - - function match_sae_pwe(phy) { - if ('6G' in phy.band) - return 1; - return ''; - } - - function match_wds() { - return index([ "wds-ap", "wds-sta", "wds-repeater" ], ssid.bss_mode) >= 0; - } - - function match_hs20_auth_type(auth_type) { - let types = { - "terms-and-conditions": "00", - "online-enrollment": "01", - "http-redirection": "02", - "dns-redirection": "03" - }; - return (auth_type && auth_type.type) ? types[auth_type.type] : ''; - } - - function get_hs20_wan_metrics() { - if (!ssid.pass_point.wan_metrics || - !ssid.pass_point.wan_metrics.info || - !ssid.pass_point.wan_metrics.downlink || - ! ssid.pass_point.wan_metrics.uplink) - return ''; - let map = {"up": 1, "down": 2, "testing": 3}; - let info = map[ssid.pass_point.wan_metrics.info] ? map[ssid.pass_point.wan_metrics.info] : 1; - return sprintf("%02d:%d:%d:0:0:0", info, ssid.pass_point.wan_metrics.downlink, ssid.pass_point.wan_metrics.uplink); - } - - let bss_mode = ssid.bss_mode; - if (ssid.bss_mode == "wds-ap") - bss_mode = "ap"; - if (ssid.bss_mode == "wds-sta") - bss_mode = "sta"; - - function radius_vendor_tlv(server, port) { - let radius_serial = replace(serial, /^(..)(..)(..)(..)(..)(..)$/, "$1-$2-$3-$4-$5-$6"); - let radius_serial_len = length(radius_serial) + 2; - let radius_vendor = "26:x:0000e608" + // vendor element - "0113" + replace(radius_serial, /./g, (m) => sprintf("%02x", ord(m))); - - let radius_ip = sprintf("%s:%s", server, port); - let radius_ip_len = length(radius_ip) + 2; - radius_vendor += "02" + sprintf("%02x", radius_ip_len) + replace(radius_ip, /./g, (m) => sprintf("%02x", ord(m))); - return radius_vendor; - } - - function radius_proxy_tlv(server, port, name) { - let tlv = "33:x:" + - replace(replace(serial, /^(..)(..)(..)(..)(..)(..)$/, "$1$2$3$4$5$6") + sprintf(":%s:%s:%s", server, port, name), - /./g, (m) => sprintf("%02x", ord(m))); - return tlv; - } - - function radius_request_attribute(request) { - if (request.id && request.hex_value) - return sprintf('%d:x:%s', request.id, request.hex_value); - if (request.id && type(request.value) == 'string') - return sprintf('%d:s:%s', request.id, request.value); - if (request.id && type(request.value) == 'int') - return sprintf('%d:d:%d', request.id, request.value); - if (request.vendor_id && request.vendor_attributes) { - let tlv = sprintf('26:x:%04x', request.vendor_id); - for (let vsa in request.vendor_attributes) - tlv += sprintf('%02x%02x', vsa.type, length(vsa.value)) + vsa.id; - return tlv; - } - return ''; - } - - function calculate_ifname(name) { - if ('captive' in ssid.services) - return 'wlanc' + captive.get(name); - return ''; - } - - let radius_gw_proxy = ssid.services && (index(ssid.services, "radius-gw-proxy") >= 0); - - if ('captive' in ssid.services && !ssid.captive) - ssid.captive = state?.services?.captive || {}; - - if (ssid.captive) - include("captive.uc", { - section: name + '_' + count, - config: ssid.captive - }); - if (ssid.strict_forwarding) - services.set_enabled("bridger", 'early'); - - ssid.vendor_elements ??= ''; - - if (ssid.tip_information_element) { - if (state.unit?.beacon_advertisement) { - if (state.unit.beacon_advertisement.device_serial) - ssid.vendor_elements += 'dd1048d01701' + replace(serial, /./g, (m) => sprintf("%02x", ord(m))); - if (state.unit.beacon_advertisement.device_name && state.unit.name) - ssid.vendor_elements += 'dd' + sprintf('%02x', 4 + length(state.unit.name)) + '48d01702' + replace(state.unit.name, /./g, (m) => sprintf("%02x", ord(m))); - if (state.unit.beacon_advertisement.network_id) { - let id = sprintf('%d', state.unit.beacon_advertisement.network_id); - ssid.vendor_elements += 'dd' + sprintf('%02x', 4 + length(id)) + '48d01703' + replace(id, /./g, (m) => sprintf("%02x", ord(m))); - } - } else { - ssid.vendor_elements += 'dd0448d01700'; - } - } -%} - -# Wireless configuration -{% for (let n, phy in phys): %} -{% let basename = name + '_' + count; %} -{% let ssidname = basename + '_' + n + '_' + count; %} -{% let section = (owe ? 'o' : '' ) + ssidname; %} -{% let id = wiphy.allocate_ssid_section_id(phy) %} -{% let crypto = validate_encryption(phy); %} -{% let ifname = calculate_ifname(basename) %} -{% if (!crypto) continue; %} -set wireless.{{ section }}=wifi-iface -set wireless.{{ section }}.ucentral_path={{ s(location) }} -set wireless.{{ section }}.uci_section={{ s(section) }} -set wireless.{{ section }}.device={{ phy.section }} -{% if ('captive' in ssid.services): %} -set wireless.{{ section }}.ifname={{ s(ifname) }} -add_list uspot.{{ basename}}.ifname={{ ifname }} -add_list bridger.@defaults[0].blacklist={{ ifname }} -{% endif %} -{% if (ssid?.encryption?.proto == 'owe-transition'): %} -{% ssid.hidden_ssid = 1 %} -{% ssid.name += '-OWE' %} -set wireless.{{ section }}.ifname={{ s(section) }} -set wireless.{{ section }}.owe_transition_ifname={{ s('o' + section) }} -{% endif %} -{% if (owe): %} -set wireless.{{ section }}.ifname={{ s(section) }} -set wireless.{{ section }}.owe_transition_ifname={{ s(ssidname) }} -{% endif %} -{% if (bss_mode == 'mesh'): %} -set wireless.{{ section }}.mode={{ bss_mode }} -set wireless.{{ section }}.mesh_id={{ s(ssid.name) }} -set wireless.{{ section }}.mesh_fwding=0 -set wireless.{{ section }}.network=batman_mesh -set wireless.{{ section }}.mcast_rate=24000 -{% endif %} - -{% if (index([ 'ap', 'sta' ], bss_mode) >= 0): %} -set wireless.{{ section }}.network={{ network }} -set wireless.{{ section }}.ssid={{ s(ssid.name) }} -set wireless.{{ section }}.mode={{ s(bss_mode) }} -set wireless.{{ section }}.bssid={{ ssid.bssid }} -set wireless.{{ section }}.wds='{{ b(match_wds()) }}' -set wireless.{{ section }}.wpa_disable_eapol_key_retries='{{ b(ssid.wpa_disable_eapol_key_retries) }}' -set wireless.{{ section }}.vendor_elements='{{ ssid.vendor_elements }}' -set wireless.{{ section }}.disassoc_low_ack='{{ b(ssid.disassoc_low_ack) }}' -set wireless.{{ section }}.auth_cache='{{ b(ssid.encryption?.key_caching) }}' -{% endif %} - -{% if ('6G' in phy.band): %} -set wireless.{{ section }}.fils_discovery_max_interval={{ ssid.fils_discovery_interval }} -{% endif %} - -# Crypto settings -set wireless.{{ section }}.ieee80211w={{ match_ieee80211w(phy) }} -set wireless.{{ section }}.sae_pwe={{ match_sae_pwe(phy) }} -set wireless.{{ section }}.encryption={{ crypto.proto }} -set wireless.{{ section }}.key={{ s(crypto.key) }} - -{% if (crypto.eap_local): %} -set wireless.{{ section }}.eap_server=1 -set wireless.{{ section }}.ca_cert={{ s(certificates.ca_certificate) }} -set wireless.{{ section }}.server_cert={{ s(certificates.certificate) }} -set wireless.{{ section }}.private_key={{ s(certificates.private_key) }} -set wireless.{{ section }}.private_key_passwd={{ s(certificates.private_key_password) }} -set wireless.{{ section }}.server_id={{ s(crypto.eap_local.server_identity) }} -set wireless.{{ section }}.eap_user_file={{ s(crypto.eap_user) }} -{% files.add_named(crypto.eap_user, render("../eap_users.uc", { users: crypto.eap_local.users })) %} -{% endif %} - -{% if (crypto.auth): %} -{% if (radius_gw_proxy): %} -set wireless.{{ section }}.radius_gw_proxy=1 -{% endif %} -set wireless.{{ section }}.auth_server={{ radius_gw_proxy ? '127.0.0.1' : crypto.auth.host }} -set wireless.{{ section }}.auth_port={{ radius_gw_proxy ? 1812 : crypto.auth.port }} -set wireless.{{ section }}.auth_secret={{ crypto.auth.secret }} -{% for (let request in crypto.auth.request_attribute): %} -add_list wireless.{{ section }}.radius_auth_req_attr={{ s(radius_request_attribute(request)) }} -{% endfor %} -{% if (radius_gw_proxy): %} -add_list wireless.{{ section }}.radius_auth_req_attr={{ s(radius_proxy_tlv(crypto.auth.host, crypto.auth.port, name + '_' + n + '_' + count)) }} -{% else %} -add_list wireless.{{ section }}.radius_auth_req_attr={{ s(radius_vendor_tlv(crypto.auth.host, crypto.auth.port)) }} -{% endif %} -{% if (crypto.auth.secondary): %} -set wireless.{{ section }}.auth_server_secondary={{ crypto.auth.secondary.host }} -set wireless.{{ section }}.auth_port_secondary={{ crypto.auth.secondary.port }} -set wireless.{{ section }}.auth_secret_secondary={{ crypto.auth.secondary.secret }} -{% endif %} -{% endif %} - -{% if (crypto.acct): %} -set wireless.{{ section }}.acct_server={{ radius_gw_proxy ? '127.0.0.1' : crypto.acct.host }} -set wireless.{{ section }}.acct_port={{ radius_gw_proxy ? 1813 : crypto.acct.port }} -set wireless.{{ section }}.acct_secret={{ crypto.acct.secret }} -set wireless.{{ section }}.acct_interval={{ crypto.acct.interval }} -{% for (let request in crypto.acct.request_attribute): %} -add_list wireless.{{ section }}.radius_acct_req_attr={{ s(radius_request_attribute(request)) }} -{% endfor %} -{% if (radius_gw_proxy): %} -add_list wireless.{{ section }}.radius_acct_req_attr={{ s(radius_proxy_tlv(crypto.acct.host, crypto.acct.port, name + '_' + n + '_' + count)) }} -{% else %} -add_list wireless.{{ section }}.radius_acct_req_attr={{ s(radius_vendor_tlv(crypto.acct.host, crypto.acct.port)) }} -{% endif %} -{% if (crypto.acct.secondary): %} -set wireless.{{ section }}.acct_server_secondary={{ crypto.acct.secondary.host }} -set wireless.{{ section }}.acct_port_secondary={{ crypto.acct.secondary.port }} -set wireless.{{ section }}.acct_secret_secondary={{ crypto.acct.secondary.secret }} -{% endif %} -{% endif %} - -{% if (crypto.health): %} -set wireless.{{ section }}.health_username={{ s(crypto.health.username) }} -set wireless.{{ section }}.health_password={{ s(crypto.health.password) }} -{% endif %} - -{% if (crypto.dyn_auth): %} -set wireless.{{ section }}.dae_client={{ crypto.dyn_auth.host }} -set wireless.{{ section }}.dae_port={{ crypto.dyn_auth.port }} -set wireless.{{ section }}.dae_secret={{ crypto.dyn_auth.secret }} - -set firewall.dyn_auth=rule -set firewall.dyn_auth.name='Allow-CoA' -set firewall.dyn_auth.src='{{ s(ethernet.find_interface("upstream", 0)) }}' -set firewall.dyn_auth.dest_port='{{ crypto.dyn_auth.port }}' -set firewall.dyn_auth.proto='udp' -set firewall.dyn_auth.target='ACCEPT' -{% endif %} - -{% if (crypto.radius): %} -set wireless.{{ section }}.request_cui={{ b(crypto.radius.chargeable_user_id) }} -set wireless.{{ section }}.nasid={{ s(crypto.radius.nas_identifier) }} -set wireless.{{ section }}.dynamic_vlan=1 -{% if (crypto.radius?.authentication?.mac_filter): %} -set wireless.{{ section }}.macfilter=radius -{% endif %} -{% endif %} - -{% if (crypto.client_tls): %} -set wireless.{{ section }}.eap_type='tls' -set wireless.{{ section }}.ca_cert={{ s(certificates.ca_certificate) }} -set wireless.{{ section }}.client_cert={{ s(certificates.certificate)}} -set wireless.{{ section }}.priv_key={{ s(certificates.private_key) }} -set wireless.{{ section }}.priv_key_pwd={{ s(certificates.private_key_password) }} -set wireless.{{ section }}.identity='uCentral' -{% endif %} - -{% if (interface.vlan_awareness?.first): %} -{% let vlan = interface.vlan_awareness.first; - if (interface.vlan_awareness.last) - vlan += '-' + interface.vlan_awareness.last; %} -set wireless.{{ section }}.network_vlan={{ vlan }} -{% endif %} - - -# AP specific setings -{% if (bss_mode == 'ap'): %} -set wireless.{{ section }}.proxy_arp={{ b(length(network) ? ssid.proxy_arp : false) }} -set wireless.{{ section }}.hidden={{ b(ssid.hidden_ssid) }} -set wireless.{{ section }}.time_advertisement={{ ssid.broadcast_time ? 2 : 0 }} -set wireless.{{ section }}.isolate={{ b(ssid.isolate_clients) }} -set wireless.{{ section }}.uapsd={{ b(ssid.power_save) }} -set wireless.{{ section }}.rts_threshold={{ ssid.rts_threshold }} -set wireless.{{ section }}.multicast_to_unicast={{ b(ssid.unicast_conversion) }} -set wireless.{{ section }}.maxassoc={{ ssid.maximum_clients }} -set wireless.{{ section }}.dtim_period={{ ssid.dtim_period }} -set wireless.{{ section }}.strict_forwarding={{ b(ssid.strict_forwarding) }} - -{% if (interface?.vlan.id): %} -set wireless.{{ section }}.vlan_id={{ interface.vlan.id }} -{% endif %} - - -{% if (ssid.rate_limit): %} -set wireless.{{ section }}.ratelimit=1 -{% endif %} - -{% if (ssid.access_control_list?.mode): %} -set wireless.{{ section }}.macfilter={{ s(ssid.access_control_list.mode) }} -{% for (let mac in ssid.access_control_list.mac_address): %} -add_list wireless.{{ section }}.maclist={{ s(mac) }} -{% endfor %} -{% endif %} - -{% if (ssid.rrm): %} -set wireless.{{ section }}.ieee80211k={{ b(ssid.rrm.neighbor_reporting) }} -set wireless.{{ section }}.rnr={{ b(ssid.rrm.reduced_neighbor_reporting) }} -set wireless.{{ section }}.ftm_responder={{ b(ssid.rrm.ftm_responder) }} -set wireless.{{ section }}.stationary_ap={{ b(ssid.rrm.stationary_ap) }} -set wireless.{{ section }}.lci={{ b(ssid.rrm.lci) }} -set wireless.{{ section }}.civic={{ ssid.rrm.civic }} -{% endif %} - -{% if (ssid.roaming): %} -set wireless.{{ section }}.ieee80211r=1 -set wireless.{{ section }}.ft_over_ds={{ b(ssid.roaming.message_exchange == "ds") }} -set wireless.{{ section }}.ft_psk_generate_local={{ b(ssid.roaming.generate_psk) }} -set wireless.{{ section }}.mobility_domain={{ ssid.roaming.domain_identifier }} -set wireless.{{ section }}.r0kh={{ s(ssid.roaming.pmk_r0_key_holder) }} -set wireless.{{ section }}.r1kh={{ s(ssid.roaming.pmk_r1_key_holder) }} -{% endif %} - -{% if (ssid.quality_thresholds): %} -set wireless.{{ phy.section }}.rssi_reject_assoc_rssi={{ ssid.quality_thresholds.association_request_rssi }} -set wireless.{{ phy.section }}.rssi_ignore_probe_request={{ ssid.quality_thresholds.probe_request_rssi }} -{% if (ssid.quality_thresholds.probe_request_rssi): %} -set wireless.{{ section }}.hidden=1 -set wireless.{{ section }}.dynamic_probe_resp=1 -{% endif %} -set usteer2.{{ section }}=ssid -set usteer2.{{ section }}.client_kick_rssi={{ ssid.quality_thresholds.client_kick_rssi }} -set usteer2.{{ section }}.client_kick_ban_time={{ ssid.quality_thresholds.client_kick_ban_time }} -{% endif %} - -{% for (let raw in ssid.hostapd_bss_raw): %} -add_list wireless.{{ section }}.hostapd_bss_options={{ s(raw) }} -{% endfor %} - -{% if (ssid.pass_point): %} -set wireless.{{ section }}.iw_enabled=1 -set wireless.{{ section }}.hs20=1 -{% for (let name in ssid.pass_point.venue_name): %} -add_list wireless.{{ section }}.iw_venue_name={{ s(name) }} -{% endfor %} -set wireless.{{ section }}.iw_venue_group='{{ ssid.pass_point.venue_group }}' -set wireless.{{ section }}.iw_venue_type='{{ ssid.pass_point.venue_type }}' -{% for (let n, url in ssid.pass_point.venue_url): %} -add_list wireless.{{ section }}.iw_venue_url={{ s((n + 1) + ":" +url) }} -{% endfor %} -set wireless.{{ section }}.iw_network_auth_type='{{ match_hs20_auth_type(ssid.pass_point.auth_type) }}' -set wireless.{{ section }}.iw_domain_name={{ s(join(",", ssid.pass_point.domain_name)) }} -{% for (let realm in ssid.pass_point.nai_realm): %} -add_list wireless.{{ section }}.iw_nai_realm='{{ realm }}' -{% endfor %} -set wireless.{{ section }}.osen={{ b(ssid.pass_point.osen) }} -set wireless.{{ section }}.anqp_domain_id='{{ ssid.pass_point.anqp_domain }}' -{% for (let cell_net in ssid.pass_point.anqp_3gpp_cell_net): %} -add_list wireless.{{ section }}.iw_anqp_3gpp_cell_net='{{ s(cell_net) }}' -{% endfor %} -{% for (let name in ssid.pass_point.friendly_name): %} -add_list wireless.{{ section }}.hs20_oper_friendly_name={{ s(name) }} -{% endfor %} -set wireless.{{ section }}.iw_access_network_type='{{ ssid.pass_point.access_network_type }}' -set wireless.{{ section }}.iw_internet={{ b(ssid.pass_point.internet) }} -set wireless.{{ section }}.iw_asra={{ b(ssid.pass_point.asra) }} -set wireless.{{ section }}.iw_esr={{ b(ssid.pass_point.esr) }} -set wireless.{{ section }}.iw_uesa={{ b(ssid.pass_point.uesa) }} -set wireless.{{ section }}.iw_hessid={{ s(ssid.pass_point.hessid) }} -{% for (let name in ssid.pass_point.roaming_consortium): %} -add_list wireless.{{ section }}.iw_roaming_consortium={{ s(name) }} -{% endfor %} -set wireless.{{ section }}.disable_dgaf={{ b(ssid.pass_point.disable_dgaf) }} -set wireless.{{ section }}.hs20_release='3' -set wireless.{{ section }}.iw_ipaddr_type_availability={{ s(sprintf("%02x", ssid.pass_point.ipaddr_type_availability)) }} -{% for (let name in ssid.pass_point.connection_capability): %} -add_list wireless.{{ section }}.hs20_conn_capab={{ s(name) }} -{% endfor %} -set wireless.{{ section }}.hs20_wan_metrics={{ s(get_hs20_wan_metrics()) }} -{% endif %} - -{% include("wmm.uc", { section }); %} - -{% if (length(ssid.multi_psk)): %} -set wireless.{{ section }}.reassociation_deadline=3000 -{% endif %} - - -{% if (ssid.pass_point): %} -{% for (let id, icon in ssid.pass_point.icons): %} -add wireless hs20-icon -set wireless.@hs20-icon[-1].width={{ s(icon.width) }} -set wireless.@hs20-icon[-1].height={{ s(icon.height) }} -set wireless.@hs20-icon[-1].type={{ s(icon.type) }} -set wireless.@hs20-icon[-1].lang={{ s(icon.language) }} -set wireless.@hs20-icon[-1].path={{ s(files.add_anonymous(location, 'hs20_icon_' + id, b64dec(icon.icon))) }} -{% endfor %} - - - -{% endif %} - -add wireless wifi-vlan -set wireless.@wifi-vlan[-1].iface={{ section }} -set wireless.@wifi-vlan[-1].name='v#' -set wireless.@wifi-vlan[-1].vid='*' -{% if (ssid.rate_limit && (ssid.rate_limit.ingress_rate || ssid.rate_limit.egress_rate)): %} - -add ratelimit rate -set ratelimit.@rate[-1].ssid={{ s(ssid.name) }} -set ratelimit.@rate[-1].ingress={{ ssid.rate_limit.ingress_rate }} -set ratelimit.@rate[-1].egress={{ ssid.rate_limit.egress_rate }} -{% endif %} -{% for (let i = length(ssid.multi_psk); i > 0; i--): %} -{% let psk = ssid.multi_psk[i - 1]; %} -{% if (!psk.key) continue %} - -add wireless wifi-station -set wireless.@wifi-station[-1].iface={{ s(section) }} -set wireless.@wifi-station[-1].mac={{ psk.mac }} -set wireless.@wifi-station[-1].key={{ psk.key }} -set wireless.@wifi-station[-1].vid={{ psk.vlan_id }} -{% endfor %} -{% else %} - -# STA specific settings -{% endif %} -{% endfor %} diff --git a/renderer/templates/interface/vxlan.uc b/renderer/templates/interface/vxlan.uc deleted file mode 100644 index 3184c02..0000000 --- a/renderer/templates/interface/vxlan.uc +++ /dev/null @@ -1,35 +0,0 @@ -{% -if (!interface.ipv4 || !interface.ipv4.subnet || interface.ipv4.addressing != 'static' ) { - warn("A VXLAN tunnel can only be created with a valid and static ivp4 address"); - return; -} -if (!ethernet.has_vlan(interface)) { - warn("A VXLAN tunnel can only be created with a valid and static ivp4 address"); - return; -} -if (!interface.tunnel.peer_address) { - warn("A VXLAN tunnel requires a valid peer-address"); - return; -} -%} - -# VXLAN Configuration -set network.{{ name }}_vx=interface -set network.{{ name }}_vx.proto=vxlan -set network.{{ name }}_vx.peeraddr={{ s(interface.tunnel.peer_address) }} -set network.{{ name }}_vx.port={{ interface.tunnel.peer_port }} -set network.{{ name }}_vx.vid={{ interface.vlan.id }} - -set network.{{ name }}=interface -set network.{{ name }}.proto='static' -set network.{{ name }}.ifname='@{{ name }}_vx' -set network.{{ name }}.ipaddr={{ ipcalc.generate_prefix(state, interface.ipv4.subnet) }} -set network.{{ name }}.layer=2 -set network.{{ name }}.type='bridge' - -add firewall rule -set firewall.@rule[-1].name='Allow-VXLAN' -set firewall.@rule[-1].src='{{ s(ethernet.find_interface("upstream", 0)) }}' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].dest_port={{ interface.tunnel.peer_port }} diff --git a/renderer/templates/interface/wmm.uc b/renderer/templates/interface/wmm.uc deleted file mode 100644 index adb7368..0000000 --- a/renderer/templates/interface/wmm.uc +++ /dev/null @@ -1,125 +0,0 @@ -{% - -let wmm = state?.globals?.wireless_multimedia; - -if (!length(wmm)) - return; - -let class = { - "CS0": 0, - "CS1": 8, - "CS2": 16, - "CS3": 24, - "CS4": 32, - "CS5": 40, - "CS6": 48, - "CS7": 56, - "AF11": 10, - "AF12": 12, - "AF13": 14, - "AF21": 18, - "AF22": 20, - "AF23": 22, - "AF31": 26, - "AF32": 28, - "AF33": 30, - "AF41": 34, - "AF42": 36, - "AF43": 38, - "EF": 46, - "VA": 44, - "LE": 1, - "DF": 0, - - /* fake entry used by rfc8325 */ - "MIN": 2 -}; - -let profiles = { - "rfc8325": { - "defaults": { - "UP0": [ "MIN", "CS2" ], - "UP1": [ "LE" ], - "UP3": [ "AF21", "AF23" ], - "UP4": [ "CS3", "AF43" ], - "UP5": [ "CS5" ], - "UP6": [ "VA", "EF" ], - "UP7": [ "CS6", "CS7" ] - } - }, - "3gpp": { - "defaults": { - "UP0": [ "DF" ], - "UP1": [ "CS1" ], - "UP2": [ "AF11", "AF13" ], - "UP3": [ "AF21", "AF23" ], - "UP4": [ "CS3", "AF33" ], - "UP5": [ "CS5", "AF43" ], - "UP6": [ "CS4" ], - "UP7": [ "CS6" ] - }, - "exceptions": { - "UP6": [ "EF" ] - } - }, - "enterprise": { - "defaults": { - "UP0": [ "DF" ], - "UP1": [ "CS1" ], - "UP2": [ "AF11", "AF13" ], - "UP3": [ "CS2", "AF23" ], - "UP4": [ "CS3", "AF33" ], - "UP5": [ "CS5" ], - "UP6": [ "CS4" ], - "UP7": [ "CS6" ] - }, - "exceptions": { - "UP5": [ "AF41", "AF42", "AF43" ], - "UP6": [ "EF" ], - "UP7": [ "CS6" ] - } - } -}; - -function qos_map() { - let up_map = []; - - if (wmm.profile) - wmm = profiles[wmm.profile]; - - if (!length(wmm.defaults)) - wmm.defaults = { }; - - if (!length(wmm.exceptions)) - wmm.exceptions = { }; - - for (let prio = 0; prio < 8; prio++) { - let up = wmm.exceptions["UP" + prio] || []; - let len = length(up); - - if (!length(up)) - continue; - - for (let idx = 0; idx < len; idx++) { - push(up_map, class[up[idx]]); - push(up_map, prio); - } - } - - for (let prio = 0; prio < 8; prio++) { - let up = wmm.defaults["UP" + prio]; - - if (length(up)) { - push(up_map, class[up[0]]); - push(up_map, class[up[1] || up[0]]); - } else { - push(up_map, 255); - push(up_map, 255); - } - } - let qos_map = join(",", up_map); - - return qos_map; -} -%} -set wireless.{{ section }}.iw_qos_map_set={{ s(qos_map()) }} diff --git a/renderer/templates/metric/dhcp_snooping.uc b/renderer/templates/metric/dhcp_snooping.uc deleted file mode 100644 index dbdd046..0000000 --- a/renderer/templates/metric/dhcp_snooping.uc +++ /dev/null @@ -1,21 +0,0 @@ -{% let interfaces = services.lookup_interfaces("dhcp-snooping") %} -{% let enable = length(interfaces) %} -{% if (!enable) return %} - -# DHCP Snooping configuration - -set event.dhcp=event -set event.dhcp.type=dhcp -set event.dhcp.filter='*' -{% for (let n, filter in dhcp_snooping.filters): %} -{{ n ? 'add_list' : 'set' }} event.dhcp.filter={{ filter }} -{% endfor %} - -{% for (let interface in interfaces): %} -{% if (interface.role != "downstream") continue %} -{% let name = ethernet.calculate_name(interface) %} -add dhcpsnoop device -set dhcpsnoop.@device[-1].name={{ s(name) }} -set dhcpsnoop.@device[-1].ingress=1 -set dhcpsnoop.@device[-1].egress=1 -{% endfor %} diff --git a/renderer/templates/metric/health.uc b/renderer/templates/metric/health.uc deleted file mode 100644 index 1961573..0000000 --- a/renderer/templates/metric/health.uc +++ /dev/null @@ -1,11 +0,0 @@ -{% - if (!health) - return; -%} - -# Health configuration -set state.health.interval={{ health.interval }} -set state.health.dhcp_local={{ b(health.dhcp_local) }} -set state.health.dhcp_remote={{ b(health.dhcp_remote) }} -set state.health.dns_local={{ b(health.dns_local) }} -set state.health.dns_remote={{ b(health.dns_remote) }} diff --git a/renderer/templates/metric/realtime.uc b/renderer/templates/metric/realtime.uc deleted file mode 100644 index a17b71b..0000000 --- a/renderer/templates/metric/realtime.uc +++ /dev/null @@ -1,7 +0,0 @@ -{% if (!realtime) return %} - -# Realtime event configuration -{% for (let real in realtime.types): %} -{% if (!(real in events)) continue; %} -add_list event.realtime.filter={{ real }} -{% endfor %} diff --git a/renderer/templates/metric/statistics.uc b/renderer/templates/metric/statistics.uc deleted file mode 100644 index c6e5693..0000000 --- a/renderer/templates/metric/statistics.uc +++ /dev/null @@ -1,7 +0,0 @@ -{% if (!statistics) return %} - -# Statistics configuration -set state.stats.interval={{ statistics.interval }} -{% for (let statistic in statistics.types): %} -add_list state.stats.types={{ statistic }} -{% endfor %} diff --git a/renderer/templates/metric/telemetry.uc b/renderer/templates/metric/telemetry.uc deleted file mode 100644 index ae83139..0000000 --- a/renderer/templates/metric/telemetry.uc +++ /dev/null @@ -1,8 +0,0 @@ -{% if (!telemetry) return %} - -# Telemetry streaming configuration -set event.bulk.interval={{ telemetry.interval }} -{% for (let type in telemetry.types): %} -{% if (!(type in events)) continue; %} -add_list event.bulk.filter={{ type }} -{% endfor %} diff --git a/renderer/templates/metric/wifi_frames.uc b/renderer/templates/metric/wifi_frames.uc deleted file mode 100644 index b342cb7..0000000 --- a/renderer/templates/metric/wifi_frames.uc +++ /dev/null @@ -1,9 +0,0 @@ -{% if (!wifi_frames) return %} - -# Wifi-frame reporting configuration -set event.wifi=event -set event.wifi.type=wifi -set event.wifi.filter='*' -{% for (let n, filter in wifi_frames.filters): %} -{{ n ? 'add_list' : 'set' }} event.wifi.filter={{ filter }} -{% endfor %} diff --git a/renderer/templates/metric/wifi_scan.uc b/renderer/templates/metric/wifi_scan.uc deleted file mode 100644 index e2db150..0000000 --- a/renderer/templates/metric/wifi_scan.uc +++ /dev/null @@ -1,3 +0,0 @@ -set event.wifiscan.interval={{ wifi_scan.interval }} -set event.wifiscan.verbose={{ b(wifi_scan.verbose) }} -set event.wifiscan.information_elements={{ b(wifi_scan.information_elements) }} diff --git a/renderer/templates/radio.uc b/renderer/templates/radio.uc deleted file mode 100644 index 43e4c28..0000000 --- a/renderer/templates/radio.uc +++ /dev/null @@ -1,206 +0,0 @@ -{% - let phys = wiphy.lookup_by_band(radio.band); - - if (!length(phys)) { - warn("Can't find any suitable radio phy for band %s radio settings", radio.band); - - return; - } - - function match_htmode(phy, radio) { - let channel_mode = radio.channel_mode; - let channel_width = radio.channel_width; - let fallback_modes = { EHT: /^(EHT|HE|VHT|HT)/, HE: /^(HE|VHT|HT)/, VHT: /^(VHT|HT)/, HT: /^HT/ }; - let mode_weight = { HT: 1, VHT: 10, HE: 100, EHT: 1000 }; - let wanted_mode = channel_mode + (channel_width == 8080 ? "80+80" : channel_width); - - let supported_phy_modes = map(sort(map(phy.htmode, (mode) => { - let m = match(mode, /^([A-Z]+)(.+)$/); - return [ mode, mode_weight[m[1]] * (m[2] == "80+80" ? 159 : +m[2]) ]; - }), (a, b) => (b[1] - a[1])), i => i[0]); - supported_phy_modes = filter(supported_phy_modes, mode => - !(index(phy.band, "2G") >= 0 && mode == "VHT80")); - if (wanted_mode in supported_phy_modes) - return wanted_mode; - - for (let supported_mode in supported_phy_modes) { - if (match(supported_mode, fallback_modes[channel_mode])) { - warn("Selected radio does not support requested HT mode %s, falling back to %s", - wanted_mode, supported_mode); - delete radio.channel; - return supported_mode; - } - } - - warn("Selected radio does not support any HT modes"); - die("Selected radio does not support any HT modes"); - } - - let channel_list = { - "320": [ 0 ], - "160": [ 36, 100 ], - "80": [ 36, 52, 100, 116, 132, 149 ], - "40": [ 36, 44, 52, 60, 100, 108, - 116, 124, 132, 140, 149, 157, 165, 173, - 184, 192 ] - }; - - if (!length(radio.valid_channels) && radio.band == "5G") - radio.valid_channels = [ 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 173, 184, 192 ]; - if (!length(radio.valid_channels) && radio.band == "6G") - radio.valid_channels = [ 1, 2, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, - 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, - 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, 193, 197, 201, 205, - 209, 213, 217, 221, 225, 229, 233 ]; - - if (capab.country_code && !(radio.country in capab.country_code)) { - warn("Overriding country code to %s", capab.country_code[0]); - radio.country = capab.country_code[0]; - } - - if (length(restrict.country) && !(radio.country in restrict.country)) { - warn("Country code is restricted"); - die("Country code is restricted"); - } - - function allowed_channel(phy, radio) { - if (restrict.dfs && radio.channel in phy.dfs_channels) - return false; - if (radio.channel_width == 20) - return true; - if (!channel_list[radio.channel_width]) - return false; - if (!(radio.channel in channel_list[radio.channel_width])) - return false; - if (radio.valid_channels && !(radio.channel in radio.valid_channels)) - return false; - return true; - } - - function match_channel(phy, radio) { - let wanted_channel = radio.channel; - if (!wanted_channel || wanted_channel == "auto") - return 0; - - if (index(phy.band, "5G") >= 0 && !allowed_channel(phy, radio)) { - warn("Selected radio does not support requested channel %d, falling back to ACS", - wanted_channel); - return 0; - } - - if (wanted_channel in phy.channels) - return wanted_channel; - - let min = (wanted_channel <= 14) ? 1 : 32; - let max = (wanted_channel <= 14) ? 14 : 196; - let eligible_channels = filter(phy.channels, (ch) => (ch >= min && ch <= max)); - - // try to find a channel next to the wanted one - for (let i = length(eligible_channels); i > 0; i--) { - let candidate = eligible_channels[i - 1]; - - if (candidate < wanted_channel || i == 1) { - warn("Selected radio does not support requested channel %d, falling back to %d", - wanted_channel, candidate); - - return candidate; - } - } - - warn("Selected radio does not support any channel in the target frequency range, falling back to %d", - phy.channels[0]); - - return phy.channels[0]; - } - - function match_mimo(available_ant, wanted_mimo) { - if (!radio.mimo) - return available_ant; - - let shift = ((available_ant & 0xf0) == available_ant) ? 4 : 0; - let m = match(wanted_mimo, /^([0-9]+)x([0-9]+$)/); - if (!m) { - warn("Failed to parse MIMO mode, falling back to %d", available_ant); - - return available_ant; - } - - let use_ant = 0; - for (let i = 0; i < m[1]; i++) - use_ant += 1 << i; - - if (shift == 4) - switch(use_ant) { - case 0x1: - use_ant = 0x8; - break; - case 0x3: - use_ant = 0xc; - break; - case 0x7: - use_ant = 0xe; - break; - } - - if (!use_ant || (use_ant << shift) > available_ant) { - warn("Invalid or unsupported MIMO mode %s specified, falling back to %d", - wanted_mimo || 'none', available_ant); - - return available_ant; - } - - return use_ant << shift; - } - - function match_require_mode(require_mode) { - let modes = { HT: "n", VHT: "ac", HE: "ax" }; - - return modes[require_mode] || ''; - } - - if (restrict.dfs && radio.allow_dfs && radio.band == "5G") { - warn('DFS is restricted.'); - radio.allow_dfs = false; - } -%} - -# Wireless Configuration -{% for (let phy in phys): %} -{% let htmode = match_htmode(phy, radio) %} -set wireless.{{ phy.section }}.disabled=0 -set wireless.{{ phy.section }}.ucentral_path={{ s(location) }} -set wireless.{{ phy.section }}.htmode={{ htmode }} -set wireless.{{ phy.section }}.channel={{ match_channel(phy, radio) }} -set wireless.{{ phy.section }}.txantenna={{ match_mimo(phy.tx_ant_avail, radio.mimo) }} -set wireless.{{ phy.section }}.rxantenna={{ match_mimo(phy.rx_ant_avail, radio.mimo) }} -set wireless.{{ phy.section }}.beacon_int={{ radio.beacon_interval }} -set wireless.{{ phy.section }}.country={{ s(radio.country) }} -set wireless.{{ phy.section }}.require_mode={{ s(match_require_mode(radio.require_mode)) }} -set wireless.{{ phy.section }}.txpower={{ radio.tx_power }} -set wireless.{{ phy.section }}.legacy_rates={{ b(radio.legacy_rates) }} -set wireless.{{ phy.section }}.chan_bw={{ radio.bandwidth }} -set wireless.{{ phy.section }}.maxassoc={{ radio.maximum_clients }} -set wireless.{{ phy.section }}.maxassoc_ignore_probe={{ b(radio.maximum_clients_ignore_probe) }} -set wireless.{{ phy.section }}.noscan=1 -set wireless.{{ phy.section }}.reconf=1 -set wireless.{{ phy.section }}.acs_exclude_dfs={{ b(!radio.allow_dfs) }} -{% for (let channel in radio.valid_channels): %} -{% if (!radio.allow_dfs && channel in phy.dfs_channels) continue %} -add_list wireless.{{ phy.section }}.channels={{ channel }} -{% endfor %} -{% if (radio.he_settings && phy.he_mac_capa && match(htmode, /HE.*/)): %} -set wireless.{{ phy.section }}.he_bss_color={{ radio.he_settings.bss_color }} -set wireless.{{ phy.section }}.multiple_bssid={{ b(radio.he_settings.multiple_bssid) }} -set wireless.{{ phy.section }}.ema={{ b(radio.he_settings.ema) }} -{% endif %} -{% if (radio.rates): %} -set wireless.{{ phy.section }}.basic_rate={{ radio.rates.beacon }} -set wireless.{{ phy.section }}.mcast_rate={{ radio.rates.multicast }} -{% endif %} -{% for (let raw in radio.hostapd_iface_raw): %} -add_list wireless.{{ phy.section }}.hostapd_options={{ s(raw) }} -{% endfor %} -{% if (radio.band == "6G"): %} -set wireless.{{ phy.section }}.he_co_locate={{ b(1) }} -{% endif %} -{% endfor %} diff --git a/renderer/templates/services/airtime_fairness.uc b/renderer/templates/services/airtime_fairness.uc deleted file mode 100644 index 1a2303b..0000000 --- a/renderer/templates/services/airtime_fairness.uc +++ /dev/null @@ -1,15 +0,0 @@ -{% -let enable = length(airtime_fairness); -services.set_enabled("atfpolicy", enable); -if (!enable) - return; -%} - -set atfpolicy.@defaults[0].vo_queue_weight={{ airtime_fairness.voice_weight }} -set atfpolicy.@defaults[0].update_pkt_threshold={{ airtime_fairness.packet_threshold }} -set atfpolicy.@defaults[0].bulk_percent_thresh={{ airtime_fairness.bulk_threshold }} -set atfpolicy.@defaults[0].prio_percent_thresh={{ airtime_fairness.priority_threshold }} -set atfpolicy.@defaults[0].weight_normal={{ airtime_fairness.weight_normal }} -set atfpolicy.@defaults[0].weight_prio={{ airtime_fairness.weight_priority }} -set atfpolicy.@defaults[0].weight_bulk={{ airtime_fairness.weight_bulk }} - diff --git a/renderer/templates/services/captive.uc b/renderer/templates/services/captive.uc deleted file mode 100644 index b4ff407..0000000 --- a/renderer/templates/services/captive.uc +++ /dev/null @@ -1,84 +0,0 @@ -{% -if (!services.is_present("spotfilter")) - return; -let interfaces = services.lookup_interfaces_by_ssids("captive"); -let enable = length(interfaces); -if (enable && enable > 1) { - warn('captive portal can only run on a single interface'); - enable = false; - -} -services.set_enabled("spotfilter", enable); -services.set_enabled("uspot", enable); -if (!enable) - return; -%} - -{% for (let interface in uniq(interfaces)): %} -{% let name = ethernet.calculate_name(interface) %} -add firewall redirect -set firewall.@redirect[-1].name='Redirect-captive-{{ name }}' -set firewall.@redirect[-1].src='{{ name }}' -set firewall.@redirect[-1].src_dport='80' -set firewall.@redirect[-1].proto='tcp' -set firewall.@redirect[-1].target='DNAT' -set firewall.@redirect[-1].mark='1/127' - -add firewall rule -set firewall.@rule[-1].name='Allow-pre-captive-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='80' -set firewall.@rule[-1].proto='tcp' -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].mark='1/127' - -add firewall rule -set firewall.@rule[-1].name='Allow-captive-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='80' -set firewall.@rule[-1].proto='tcp' -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].mark='2/127' - -{% if (interface.role == 'downstream'): %} -add firewall rule -set firewall.@rule[-1].name='Allow-pre-captive-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest='{{ ethernet.find_interface("upstream", interface.vlan.id) }}' -set firewall.@rule[-1].proto='any' -set firewall.@rule[-1].target='DROP' -set firewall.@rule[-1].mark='1/127' - -add firewall include -set firewall.@include[-1].type=restore -set firewall.@include[-1].family=ipv4 -set firewall.@include[-1].path='/usr/share/uspot/firewall.ipt' -set firewall.@include[-1].reload=1 - -add firewall include -set firewall.@include[-1].type=restore -set firewall.@include[-1].family=ipv6 -set firewall.@include[-1].path='/usr/share/uspot/firewall.ipt' -set firewall.@include[-1].reload=1 -{% endif %} -{% endfor %} - -add uhttpd uhttpd -set uhttpd.@uhttpd[-1].redirect_https='0' -set uhttpd.@uhttpd[-1].rfc1918_filter='1' -set uhttpd.@uhttpd[-1].max_requests='5' -set uhttpd.@uhttpd[-1].max_connections='100' -set uhttpd.@uhttpd[-1].cert='/etc/uhttpd.crt' -set uhttpd.@uhttpd[-1].key='/etc/uhttpd.key' -set uhttpd.@uhttpd[-1].script_timeout='60' -set uhttpd.@uhttpd[-1].network_timeout='30' -set uhttpd.@uhttpd[-1].http_keepalive='20' -set uhttpd.@uhttpd[-1].tcp_keepalive='1' -set uhttpd.@uhttpd[-1].no_dirlists='1' -add_list uhttpd.@uhttpd[-1].listen_http='0.0.0.0:80' -add_list uhttpd.@uhttpd[-1].listen_http='[::]:80' -set uhttpd.@uhttpd[-1].home=/tmp/ucentral/www-uspot -add_list uhttpd.@uhttpd[-1].ucode_prefix='/hotspot=/usr/share/uspot/handler.uc' -add_list uhttpd.@uhttpd[-1].ucode_prefix='/cpd=/usr/share/uspot/handler-cpd.uc' -add_list uhttpd.@uhttpd[-1].ucode_prefix='/env=/usr/share/uspot/handler-env.uc' -set uhttpd.@uhttpd[-1].error_page='/cpd' diff --git a/renderer/templates/services/data_plane.uc b/renderer/templates/services/data_plane.uc deleted file mode 100644 index 41c5a50..0000000 --- a/renderer/templates/services/data_plane.uc +++ /dev/null @@ -1,29 +0,0 @@ -# Data Plane service configuration -{% -let iface = {}; - -function render(dict, type) { - for (let idx, filter in dict) { -%} -set dataplane.{{ filter.name }}=program -set dataplane.{{ filter.name }}.type={{ type }} -set dataplane.{{ filter.name }}.program={{ s(files.add_anonymous(location, 'ingress_' + idx, b64dec(filter.program))) }} -{% - for (let i in services.lookup_interfaces("data-plane:" + filter.name)) { - let name = ethernet.calculate_name(i); - if (!length(iface[name])) - iface[name] = []; - push(iface[name], filter.name); - } - } -} - -render(data_plane.ingress_filters, "ingress"); -%} - -{% for (let k, v in iface): %} -set dataplane.{{ k }}=interface -{% for (let i, p in v): %} -add_list dataplane.{{ k }}.program={{ s(p) }} -{% endfor %} -{% endfor %} diff --git a/renderer/templates/services/dhcp_relay.uc b/renderer/templates/services/dhcp_relay.uc deleted file mode 100644 index 49e77df..0000000 --- a/renderer/templates/services/dhcp_relay.uc +++ /dev/null @@ -1,36 +0,0 @@ -{% -if (!services.is_present("dhcprelay") || !dhcp_relay) - return; -let interfaces = services.lookup_interfaces("dhcp-relay"); -let ports = ethernet.lookup_by_select_ports(dhcp_relay.select_ports); -let enable = length(interfaces) && length(ports); -services.set_enabled("dhcprelay", enable); -if (!enable) - return; - -%} - -# DHCP-relay service configuration - -set firewall.dhcp_relay=rule -set firewall.dhcp_relay.name='Allow-DHCP-Relay' -set firewall.dhcp_relay.src='{{ s(ethernet.find_interface("upstream", 0)) }}' -set firewall.dhcp_relay.dest_port='67' -set firewall.dhcp_relay.family='ipv4' -set firewall.dhcp_relay.proto='udp' -set firewall.dhcp_relay.target='ACCEPT' - -set dhcprelay.relay=bridge -set dhcprelay.relay.name=up -{% for (let vlan in dhcp_relay.vlans||[]): %} -add_list dhcprelay.relay.vlans={{ vlan.vlan }} -{% endfor %} -{% for (let port in ports): %} -add_list dhcprelay.relay.upstream={{ port }} -{% endfor %} -{% for (let vlan in dhcp_relay.vlans||[]): %} -set dhcprelay.vlan{{vlan.vlan}}=config -set dhcprelay.vlan{{vlan.vlan}}.server={{ s(vlan.relay_server) }} -set dhcprelay.vlan{{vlan.vlan}}.circuit_id={{ s(vlan?.circuit_id_format) }} -set dhcprelay.vlan{{vlan.vlan}}.remote_id={{ s(vlan?.remote_id_format) }} -{% endfor %} diff --git a/renderer/templates/services/dhcp_snooping.uc b/renderer/templates/services/dhcp_snooping.uc deleted file mode 100644 index 68434cc..0000000 --- a/renderer/templates/services/dhcp_snooping.uc +++ /dev/null @@ -1,13 +0,0 @@ -{% services.set_enabled("dhcpsnoop", true) %} - -# DHCP Snooping configuration - -{% for (let interface in state.interfaces): %} -{% if (interface.role != 'upstream') continue %} -{% for (let name in ethernet.lookup_by_interface_vlan(interface)): %} -add dhcpsnoop device -set dhcpsnoop.@device[-1].name={{ s(name) }} -set dhcpsnoop.@device[-1].ingress=1 -set dhcpsnoop.@device[-1].egress=1 -{% endfor %} -{% endfor %} diff --git a/renderer/templates/services/facebook_wifi.uc b/renderer/templates/services/facebook_wifi.uc deleted file mode 100644 index 0c29faf..0000000 --- a/renderer/templates/services/facebook_wifi.uc +++ /dev/null @@ -1,48 +0,0 @@ -{% if (!services.is_present("fbwifi")) return %} -{% let interfaces = services.lookup_interfaces("facebook-wifi") %} -{% let enable = length(interfaces) %} -{% services.set_enabled("fbwifi", enable) %} -{% if (!enable) return %} - -# Facebook-wifi service configuration - -set fbwifi.main.enabled=1 -set fbwifi.main.zone={{ s(ethernet.calculate_name(interfaces[0])) }} -set fbwifi.main.gateway_token='FBWIFI:GATEWAY|{{ facebook_wifi.vendor_id }}|{{ facebook_wifi.gateway_id }}|{{ facebook_wifi.secret }}' - -set uhttpd.main=uhttpd -add_list uhttpd.main.listen_http='0.0.0.0:80' -add_list uhttpd.main.listen_http='[::]:80' -add_list uhttpd.main.listen_https='0.0.0.0:443' -add_list uhttpd.main.listen_https='[::]:443' -set uhttpd.main.redirect_https='0' -set uhttpd.main.home='/www' -set uhttpd.main.max_requests='3' -set uhttpd.main.max_connections='100' -set uhttpd.main.cgi_prefix='/cgi-bin' -add_list uhttpd.main.lua_prefix='/cgi-bin/luci=/usr/lib/lua/luci/sgi/uhttpd.lua' -set uhttpd.main.script_timeout='60' -set uhttpd.main.network_timeout='30' -set uhttpd.main.http_keepalive='20' -set uhttpd.main.tcp_keepalive='1' -set uhttpd.main.cert='/tmp/fbwifi/https_server_cert' -set uhttpd.main.key='/tmp/fbwifi/https_server_key' -set uhttpd.main.json_script='/usr/share/fbwifi/uhttpd.json' -set uhttpd.main.rfc1918_filter='0' - -set firewall.fbwifi=include -set firewall.fbwifi.enabled=1 -set firewall.fbwifi.family=ipv4 -set firewall.fbwifi.path=/usr/share/fbwifi/firewall.include -set firewall.fbwifi.reload=1 -set firewall.fbwifi.type=script - -set uhttpd.fbwifi_redirect=uhttpd -set uhttpd.fbwifi_redirect.enabled=1 -set uhttpd.fbwifi_redirect.listen_http='0.0.0.0:2060' -set uhttpd.fbwifi_redirect.listen_https='0.0.0.0:2061' -set uhttpd.fbwifi_redirect.cert='/tmp/fbwifi/https_server_cert' -set uhttpd.fbwifi_redirect.key='/tmp/fbwifi/https_server_key' -set uhttpd.fbwifi_redirect.json_script='/tmp/fbwifi/uhttpd-redirect.json' - -add_list dhcp.@dnsmasq[0].rebind_domain=fbwifigateway.net diff --git a/renderer/templates/services/gps.uc b/renderer/templates/services/gps.uc deleted file mode 100644 index 6b18d00..0000000 --- a/renderer/templates/services/gps.uc +++ /dev/null @@ -1,11 +0,0 @@ -{%- - let enable = length(gps); - services.set_enabled("umdns", enable); - if (!enable) - return; -%} - -# Configure GPS -set gps.@gps[-1].disabled=0 -set gps.@gps[-1].adjust_time={{ b(gps.adjust_time) }} -set gps.@gps[-1].baudrate={{ s(gps.baud_rate) }} diff --git a/renderer/templates/services/http.uc b/renderer/templates/services/http.uc deleted file mode 100644 index 62d7aaf..0000000 --- a/renderer/templates/services/http.uc +++ /dev/null @@ -1,36 +0,0 @@ -{% if (!services.is_present("uhttpd")) return %} -{% let interfaces = services.lookup_interfaces("http") %} -{% let enable = length(interfaces) %} -{% services.set_enabled("uhttpd", enable) %} -{% services.set_enabled("rpcd", enable) %} -{% if (!enable) return %} - -# HTTP service configuration - -add uhttpd uhttpd -set uhttpd.@uhttpd[-1].redirect_https='0' -set uhttpd.@uhttpd[-1].home='/www' -set uhttpd.@uhttpd[-1].rfc1918_filter='1' -set uhttpd.@uhttpd[-1].max_requests='3' -set uhttpd.@uhttpd[-1].max_connections='100' -set uhttpd.@uhttpd[-1].cert='/etc/uhttpd.crt' -set uhttpd.@uhttpd[-1].key='/etc/uhttpd.key' -set uhttpd.@uhttpd[-1].cgi_prefix='/cgi-bin' -set uhttpd.@uhttpd[-1].lua_prefix='/cgi-bin/luci=/usr/lib/lua/luci/sgi/uhttpd.lua' -set uhttpd.@uhttpd[-1].script_timeout='60' -set uhttpd.@uhttpd[-1].network_timeout='30' -set uhttpd.@uhttpd[-1].http_keepalive='20' -set uhttpd.@uhttpd[-1].tcp_keepalive='1' -set uhttpd.@uhttpd[-1].ubus_prefix='/ubus' -add_list uhttpd.@uhttpd[-1].listen_http='0.0.0.0:{{ http.http_port || 80 }}' -{% let interfaces = services.lookup_interfaces("http") %} -{% for (let interface in interfaces): %} -{% let name = ethernet.calculate_name(interface) %} - -add firewall rule -set firewall.@rule[-1].name='Allow-http-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].port='{{ http.http_port || 80 }}' -set firewall.@rule[-1].proto='tcp' -set firewall.@rule[-1].target='ACCEPT' -{% endfor %} diff --git a/renderer/templates/services/ieee8021x.uc b/renderer/templates/services/ieee8021x.uc deleted file mode 100644 index e96370b..0000000 --- a/renderer/templates/services/ieee8021x.uc +++ /dev/null @@ -1,53 +0,0 @@ -{% - if (!services.is_present("ieee8021x")) - return; - let interfaces = services.lookup_interfaces("ieee8021x"); - let enable = length(interfaces); - if (ieee8021x.mode == "radius") { - if (!ieee8021x.radius.auth_server_addr || - !ieee8021x.radius.auth_server_port || - !ieee8021x.radius.auth_server_secret) { - warn(invalid radius configuration); - enable = false; - } - } - services.set_enabled("ieee8021x", enable); - if (!enable) - return; - - let ports = []; - for (let p in ieee8021x.port_filter) - if (ethernet.ports[p]) - push(ports, ethernet.ports[p].netdev); - cursor.load("system") - let certs = cursor.get_all("system", "@certificates[-1]") -%} -# IEEE8021x service configuration - -add ieee8021x config -{% if (ieee8021x.mode == "radius"): %} -add ieee8021x config -set ieee8021x.@config[-1].nas_identifier={{ s(ieee8021x.radius.nas_identifier) }} -set ieee8021x.@config[-1].auth_server_addr={{ s(ieee8021x.radius.auth_server_addr) }} -set ieee8021x.@config[-1].auth_server_port={{ s(ieee8021x.radius.auth_server_port) }} -set ieee8021x.@config[-1].auth_server_secret={{ s(ieee8021x.radius.auth_server_secret) }} -set ieee8021x.@config[-1].acct_server_addr={{ s(ieee8021x.radius.acct_server_addr) }} -set ieee8021x.@config[-1].acct_server_port={{ s(ieee8021x.radius.acct_server_port) }} -set ieee8021x.@config[-1].acct_server_secret={{ s(ieee8021x.radius.acct_server_secret) }} -set ieee8021x.@config[-1].coa_server_addr={{ s(ieee8021x.radius.coa_server_addr) }} -set ieee8021x.@config[-1].coa_server_port={{ s(ieee8021x.radius.coa_server_port) }} -set ieee8021x.@config[-1].coa_server_secret={{ s(ieee8021x.radius.coa_server_secret) }} -{% else - files.add_named("/var/run/hostapd-ieee8021x.eap_user", render("../eap_users.uc", { users: ieee8021x.users })) %} - endif -%} -set ieee8021x.@config[-1].ca={{ s(certs.ca) }} -set ieee8021x.@config[-1].cert={{ s(certs.cert) }} -set ieee8021x.@config[-1].key={{ s(certs.key) }} - -{% for (let port in ports): %} -add_list ieee8021x.@config[-1].ports={{ s(port) }} -set network.{{ replace(port, '.', '_') }}=device -set network.@device[-1].name={{ s(port) }} -set network.@device[-1].auth='1' -{% endfor %} diff --git a/renderer/templates/services/igmp.uc b/renderer/templates/services/igmp.uc deleted file mode 100644 index 111daa6..0000000 --- a/renderer/templates/services/igmp.uc +++ /dev/null @@ -1,20 +0,0 @@ -{% if (!services.is_present("igmpproxy")) return %} -{% let interfaces = services.lookup_interfaces("igmp") %} -{% let enable = length(interfaces) %} -{% services.set_enabled("igmpproxy", enable) %} -{% if (!enable) return %} - -# IGMP service configuration - -{% let interfaces = services.lookup_interfaces("igmp") %} -{% for (let interface in interfaces): %} -{% if (!interface.ipv4) continue; %} -{% let name = ethernet.calculate_name(interface) %} -add igmpproxy phyint -set igmpproxy.@phyint[-1].network={{ name }} -set igmpproxy.@phyint[-1].zone={{ s((interface.role == "usptream") ? "wan" : name) }} -set igmpproxy.@phyint[-1].direction={{ s(interface.role) }} -{% if (interface.role == "upstream"): %} -set igmpproxy.@phyint[-1].altnet='0.0.0.0/0' -{% endif %} -{% endfor %} diff --git a/renderer/templates/services/lldp.uc b/renderer/templates/services/lldp.uc deleted file mode 100644 index 71fd78c..0000000 --- a/renderer/templates/services/lldp.uc +++ /dev/null @@ -1,16 +0,0 @@ -{% if (!services.is_present("lldpd")) return %} -{% let interfaces = services.lookup_interfaces("lldp") %} -{% let enable = length(interfaces) %} -{% services.set_enabled("lldpd", enable) %} -{% if (!enable) return %} - -# LLDP service configuration - -set lldpd.config.enable=1 -set lldpd.config.lldp_description={{ s(lldp.describe) }} -set lldpd.config.lldp_location={{ s(lldp.location) }} -{% for (let interface in interfaces): %} -{% for (let port in ethernet.lookup_by_interface_spec(interface)): %} -add_list lldpd.config.interface={{ s(port) }} -{% endfor %} -{% endfor %} diff --git a/renderer/templates/services/log.uc b/renderer/templates/services/log.uc deleted file mode 100644 index c1bd53a..0000000 --- a/renderer/templates/services/log.uc +++ /dev/null @@ -1,18 +0,0 @@ -{% -if (!length(log)) return; -let hostname = state.unit?.hostname; -if (!hostname) { - cursor.load("system"); - let system = cursor.get_all("system", "@system[-1]"); - hostname = system?.hostname || OpenWifi; -} -%} - -# Syslog service configuration - -set system.@system[-1].log_ip={{ s(log.host) }} -set system.@system[-1].log_port={{ s(log.port) }} -set system.@system[-1].log_proto={{ s(log.proto) }} -set system.@system[-1].log_size={{ s(log.size) }} -set system.@system[-1].log_priority={{ s(log.priority) }} -set system.@system[-1].log_hostname={{ s(hostname) }} diff --git a/renderer/templates/services/mdns.uc b/renderer/templates/services/mdns.uc deleted file mode 100644 index 1c35e2a..0000000 --- a/renderer/templates/services/mdns.uc +++ /dev/null @@ -1,25 +0,0 @@ -{% if (!services.is_present("umdns")) return %} -{% let interfaces = services.lookup_interfaces("mdns") %} -{% let enable = length(interfaces) %} -{% services.set_enabled("umdns", enable) %} -{% if (!enable) return %} - - -# MDNS service configuration - -add umdns umdns -set umdns.@umdns[-1].enable=1 -{% for (let interface in interfaces): %} -add_list umdns.@umdns[-1].network={{ s(ethernet.calculate_name(interface)) }} -{% endfor %} - -{% for (let interface in interfaces): %} -{% let name = ethernet.calculate_name(interface) %} -add firewall rule -set firewall.@rule[-1].name='Allow-mdns-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='5353' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' -{% endfor %} - diff --git a/renderer/templates/services/ntp.uc b/renderer/templates/services/ntp.uc deleted file mode 100644 index a5ae8ac..0000000 --- a/renderer/templates/services/ntp.uc +++ /dev/null @@ -1,23 +0,0 @@ -{% - if (!length(ntp)) - return; - let interfaces = services.lookup_interfaces("ntp"); -%} -delete system.ntp.server -set system.ntp.enabled={{ b(ntp.local_server) }} -set system.ntp.enable_server={{ b(ntp.servers) }} -{% for (let server in ntp.servers): %} -add_list system.ntp.server={{ s(server) }} -{% endfor - - /* open the port on all interfaces that select ssh */ - for (let interface in interfaces): - let name = ethernet.calculate_name(interface); -%} -add firewall rule -set firewall.@rule[-1].name='Allow-ntp-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='123' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' -{% endfor %} diff --git a/renderer/templates/services/online_check.uc b/renderer/templates/services/online_check.uc deleted file mode 100644 index 0168dbb..0000000 --- a/renderer/templates/services/online_check.uc +++ /dev/null @@ -1,27 +0,0 @@ -{% -let enable = true; -if (!services.is_present("onlinecheck") || - !length(online_check) || - (!length(online_check.ping_hosts) && - !length(online_check.download_hosts))) - enable = false; - -services.set_enabled("onlinecheck", enable); -if (!enable) - return; -%} - - -# Online check service configuration -add onlinecheck config -set onlinecheck.@config[-1].check_interval={{ s(online_check.check_interval) }} -set onlinecheck.@config[-1].check_threshold={{ s(online_check.check_threshold) }} -{% for (let action in online_check.action): %} -add_list onlinecheck.@config[-1].action={{ s(action) }} -{% endfor %} -{% for (let host in online_check.ping_hosts): %} -add_list onlinecheck.@config[-1].ping_hosts={{ s(host) }} -{% endfor %} -{% for (let host in online_check.download_hosts): %} -add_list onlinecheck.@config[-1].download_hosts={{ s(host) }} -{% endfor %} diff --git a/renderer/templates/services/quality_of_service.uc b/renderer/templates/services/quality_of_service.uc deleted file mode 100644 index b017ba9..0000000 --- a/renderer/templates/services/quality_of_service.uc +++ /dev/null @@ -1,87 +0,0 @@ -{% -if (!quality_of_service) - quality_of_service = {}; -let egress = ethernet.lookup_by_select_ports(quality_of_service.select_ports); -let enable = length(egress); -services.set_enabled("qosify", enable); -if (!enable) - return; - -function get_speed(dev, speed) { - if (!speed) - speed = ethernet.get_speed(dev); - return speed; -} - -function get_proto(proto) { - if (proto == "any") - return [ "udp", "tcp" ]; - return [ proto ]; -} - -function get_range(port) { - if (port.range_end) - return sprintf("-%d", port.range_end) -} - -let fs = require("fs"); -let file = fs.open("/tmp/qosify.conf", "w"); -for (let class in quality_of_service.classifier) { - for (let port in class.ports) - for (let proto in get_proto(port.protocol)) - file.write(sprintf("%s:%d%s %s%s\n", proto, port.port, - port.range_end ? sprintf("-%d", port.range_end) : "", - port.reclassify ? "" : "+", class.dscp)); - for (let fqdn in class.dns) - file.write(sprintf("dns:%s%s %s%s\n", - fqdn.suffix_matching ? "*." : "", fqdn.fqdn, - fqdn.reclassify ? "" : "+", class.dscp)); -} - -if (quality_of_service.services) { - let inputfile = fs.open('/usr/share/ucentral/qos.json', "r"); - let db = json(inputfile.read("all")); - - for (let k, v in db.classes) { -%} -set qosify.{{ k }}=class -set qosify.{{ k }}.ingress={{ s(v.ingress) }} -set qosify.{{ k }}.egress={{ s(v.egress) }} -set qosify.{{ k }}.bulk_trigger_pps={{ s(v.bulk_pps) }} -set qosify.{{ k }}.bulk_trigger_timeout={{ s(v.bulk_timeout) }} -set qosify.{{ k }}.dscp_bulk={{ s(v.bulk_dscp) }} -{% - } - - let rules = []; - let all = 'all' in quality_of_service.services; - for (let k, v in db.services) - if (all || (k in quality_of_service.services)) - for (let uses in v.uses) - push(quality_of_service.services, uses); - for (let k, v in db.services) - if (all || (k in quality_of_service.services)) { - for (let port in v.tcp) - push(rules, 'tcp:' + port + ' ' + v.classifier); - for (let port in v.udp) - push(rules, 'udp:' + port + ' ' + v.classifier); - for (let dns in v.fqdn) - push(rules, 'dns:' + dns + ' ' + v.classifier); - } - - for (let rule in uniq(rules)) - file.write(rule + '\n'); -} - -file.close(); -%} - -set qosify.@defaults[0].bulk_trigger_pps={{ quality_of_service?.bulk_detection?.packets_per_second || 0}} -set qosify.@defaults[0].dscp_bulk={{ quality_of_service?.bulk_detection?.dscp }} - -{% for (let dev in egress): %} -set qosify.{{ dev }}=device -set qosify.{{ dev }}.name={{ s(dev) }} -set qosify.{{ dev }}.bandwidth_up='{{ get_speed(dev, quality_of_service.bandwidth_up) }}mbit' -set qosify.{{ dev }}.bandwidth_down='{{ get_speed(dev, quality_of_service.bandwidth_down) }}mbit' -{% endfor %} diff --git a/renderer/templates/services/radius_gw_proxy.uc b/renderer/templates/services/radius_gw_proxy.uc deleted file mode 100644 index 8a3bf7a..0000000 --- a/renderer/templates/services/radius_gw_proxy.uc +++ /dev/null @@ -1,7 +0,0 @@ -{% -if (!services.is_present("radius-gw-proxy")) - return; -let ssids = services.lookup_ssids("radius-gw-proxy"); -let enable = length(ssids); -services.set_enabled("radius-gw-proxy", enable); -%} diff --git a/renderer/templates/services/radius_proxy.uc b/renderer/templates/services/radius_proxy.uc deleted file mode 100644 index dfd9551..0000000 --- a/renderer/templates/services/radius_proxy.uc +++ /dev/null @@ -1,93 +0,0 @@ -{% if (!services.is_present("radsecproxy")) return %} -{% let enable = (length(radius_proxy) && length(radius_proxy.realms)) %} -{% services.set_enabled("radsecproxy", enable) %} -{% if (!enable) return %} - -add radsecproxy options -add_list radsecproxy.@options[-1].ListenUDP='localhost:1812' -add_list radsecproxy.@options[-1].ListenUDP='localhost:1813' - -add radsecproxy client -set radsecproxy.@client[-1].name='client' -set radsecproxy.@client[-1].host='localhost' -set radsecproxy.@client[-1].type='udp' -set radsecproxy.@client[-1].secret={{ s(radius_proxy.proxy_secret) }} - -{% for (idx, realm in radius_proxy.realms): %} - -{% let certs = {}; - if (realm.use_local_certificates) { - cursor.load("system"); - certs = cursor.get_all("system", "@certificates[-1]"); - } else if (realm.ca_certificate && realm.certificate && realm.private_key) { - certs.ca = files.add_anonymous(location, 'ca' + idx, b64dec(realm.ca_certificate)); - certs.cert = files.add_anonymous(location, 'cert' + idx, b64dec(realm.certificate)); - certs.key = files.add_anonymous(location, 'key' + idx, b64dec(realm.private_key)); - } else if (realm.protocol == "radsec") { - warn("invalid certificate settings"); - continue; - } -%} - -{% if (realm.protocol == "radsec"): %} -set radsecproxy.tls{{ idx }}=tls -set radsecproxy.@tls[-1].name='tls{{ idx }}' -set radsecproxy.@tls[-1].CACertificateFile={{ s(certs.ca) }} -set radsecproxy.@tls[-1].certificateFile={{ s(certs.cert) }} -set radsecproxy.@tls[-1].certificateKeyFile={{ s(certs.key) }} -set radsecproxy.@tls[-1].certificateKeyPassword='' - -set radsecproxy.server{{ idx }}=server -set radsecproxy.@server[-1].name='server{{ idx }}' -{% if (realm.auto_discover): %} -set radsecproxy.@server[-1].dynamicLookupCommand='/usr/libexec/naptr_lookup.sh' -{% else %} -set radsecproxy.@server[-1].host={{ s(realm.host) }} -set radsecproxy.@server[-1].port={{ s(realm.port) }} -set radsecproxy.@server[-1].secret={{ s(realm.secret) }} -{% endif %} -set radsecproxy.@server[-1].type='tls' -set radsecproxy.@server[-1].tls='tls{{ idx }}' -set radsecproxy.@server[-1].statusServer='0' -set radsecproxy.@server[-1].certificateNameCheck='0' -{% for (name in realm.realm): %} -add radsecproxy realm -set radsecproxy.@realm[-1].name='{{ name }}' -set radsecproxy.@realm[-1].server='server{{ idx }}' -set radsecproxy.@realm[-1].accountingServer='server{{ idx }}' -{% endfor %} -{% endif %} - -{% if (realm.protocol == "radius"): %} -set radsecproxy.server{{ idx + "auth" }}=server -set radsecproxy.@server[-1].name='server{{ idx }}auth' -set radsecproxy.@server[-1].host={{ s(realm.auth_server) }} -set radsecproxy.@server[-1].port={{ s(realm.auth_port) }} -set radsecproxy.@server[-1].secret={{ s(realm.auth_secret) }} -set radsecproxy.@server[-1].type='udp' -{% if (realm.acct_server): %} -set radsecproxy.server{{ idx + "acct" }}=server -set radsecproxy.@server[-1].name='server{{ idx }}acct' -set radsecproxy.@server[-1].host={{ s(realm.acct_server) }} -set radsecproxy.@server[-1].port={{ s(realm.acct_port) }} -set radsecproxy.@server[-1].secret={{ s(realm.acct_secret) }} -set radsecproxy.@server[-1].type='udp' -{% endif %} -{% for (name in realm.realm): %} -add radsecproxy realm -set radsecproxy.@realm[-1].name='{{ name }}' -set radsecproxy.@realm[-1].server='server{{ idx }}auth' -{% if (realm.acct_server): %} -set radsecproxy.@realm[-1].accountingServer='server{{ idx }}acct' -{% endif %} -{% endfor %} -{% endif %} - -{% if (realm.protocol == "block"): %} -{% for (name in realm.realm): %} -add radsecproxy realm -set radsecproxy.@realm[-1].name='{{ name }}' -set radsecproxy.@realm[-1].replyMessage={{ s(realm.message) }} -{% endfor %} -{% endif %} -{% endfor %} diff --git a/renderer/templates/services/rrm.uc b/renderer/templates/services/rrm.uc deleted file mode 100644 index cbf38b1..0000000 --- a/renderer/templates/services/rrm.uc +++ /dev/null @@ -1,12 +0,0 @@ -{% -if (!services.is_present("rrmd")) - return; -let interfaces = services.lookup_interfaces("mdns"); -let enable = length(rrm); -services.set_enabled("rrmd", enable); -if (!enable) - return ; -%} - -set rrmd.@base[0].beacon_request_assoc={{ rrm.beacon_request_assoc || 0 }} -set rrmd.@base[0].station_stats_interval={{ rrm.station_stats_interval || 0 }} diff --git a/renderer/templates/services/rtty.uc b/renderer/templates/services/rtty.uc deleted file mode 100644 index 68950a3..0000000 --- a/renderer/templates/services/rtty.uc +++ /dev/null @@ -1,12 +0,0 @@ -{% if (!services.is_present("rtty")) return %} -{% let enable = length(rtty) %} -{% services.set_enabled("rtty", enable) %} -{% if (!enable) return %} - -# RTTY service configuration - -set rtty.@rtty[-1].enable={{ b((rtty.token && rtty.host && rtty.port)) }} -set rtty.@rtty[-1].token={{ s(rtty.token) }} -set rtty.@rtty[-1].host={{ s(rtty.host) }} -set rtty.@rtty[-1].port={{ s(rtty.port) }} -set rtty.@rtty[-1].ssl={{ b(rtty.mutual_tls) }} diff --git a/renderer/templates/services/ssh.uc b/renderer/templates/services/ssh.uc deleted file mode 100644 index 9642119..0000000 --- a/renderer/templates/services/ssh.uc +++ /dev/null @@ -1,31 +0,0 @@ -{% -let interfaces = services.lookup_interfaces("ssh"); -let enable = length(interfaces); - -if (restrict.ssh && enable) { - warn('SSH is restricted'); - enable = false; -} - -services.set_enabled("dropbear", enable); -if (!enable) - return; -files.add_named("/etc/dropbear/authorized_keys", join("\n", ssh.authorized_keys || []) + "\n"); -%} - -# SSH service configuration - -set dropbear.@dropbear[-1].enable={{ b(enable) }} -set dropbear.@dropbear[-1].Port={{ s(ssh.port) }} -set dropbear.@dropbear[-1].PasswordAuth={{ b(ssh.password_authentication) }} - -{% for (let interface in interfaces): %} -{% let name = ethernet.calculate_name(interface) %} - -add firewall rule -set firewall.@rule[-1].name='Allow-ssh-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='{{ ssh.port }}' -set firewall.@rule[-1].proto='tcp' -set firewall.@rule[-1].target='ACCEPT' -{% endfor %} diff --git a/renderer/templates/services/wifi_steering.uc b/renderer/templates/services/wifi_steering.uc deleted file mode 100644 index 1457c30..0000000 --- a/renderer/templates/services/wifi_steering.uc +++ /dev/null @@ -1,29 +0,0 @@ -{% if (!services.is_present("usteer")) return %} -{% let ssids = services.lookup_ssids("wifi-steering") %} -{% let enable = length(ssids) %} -{% services.set_enabled("usteer", enable) %} -{% if (!enable) return %} -{% let name = wifi_steering.mode == 'local' ? ethernet.find_interface("upstream", 0) : '' %} -# Wifi-Steering service configuration - -add usteer usteer -set usteer.@usteer[-1].network={{ s(name) }} -set usteer.@usteer[-1].ipv6={{ b(wifi_steering.ipv6) }} -set usteer.@usteer[-1].key={{ s(wifi_steering.key) }} -set usteer.@usteer[-1].assoc_steering={{ b(wifi_steering.assoc_steering) }} -set usteer.@usteer[-1].min_snr={{ wifi_steering.required_snr }} -set usteer.@usteer[-1].min_connect_snr={{ wifi_steering.required_probe_snr }} -set usteer.@usteer[-1].roam_scan_snr={{ wifi_steering.required_roam_snr }} -set usteer.@usteer[-1].load_kick_enabled={{ b(wifi_steering.load_kick_threshold) }} -set usteer.@usteer[-1].load_kick_threshold={{ wifi_steering.load_kick_threshold }} -set usteer.@usteer[-1].autochannel={{ b(wifi_steering.auto_channel) }} -{% for (let ssid in ssids): %} -add_list usteer.@usteer[-1].ssid_list={{ s(ssid.name) }} -{% endfor %} - -add firewall rule -set firewall.@rule[-1].name='Allow-usteer-{{ name }}' -set firewall.@rule[-1].src='{{ name }}' -set firewall.@rule[-1].dest_port='16720' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' diff --git a/renderer/templates/services/wireguard_overlay.uc b/renderer/templates/services/wireguard_overlay.uc deleted file mode 100644 index f808e95..0000000 --- a/renderer/templates/services/wireguard_overlay.uc +++ /dev/null @@ -1,116 +0,0 @@ -{% -let wireguard = length(services.lookup_interfaces("wireguard-overlay")); -let vxlan = length(services.lookup_interfaces("vxlan-overlay")); - -if (!wireguard && !vxlan) { - services.set_enabled("unetd", false); - return; -} - -if (wireguard + vlxan > 1) { - warn('only a single wireguard/vxlan-overlay is allowed\n'); - services.set_enabled("unetd", false); - return; -} - -if (!wireguard_overlay.root_node.key || - !wireguard_overlay.root_node.endpoint || - !wireguard_overlay.root_node.ipaddr) { - warn('root node is not configured correctly\n'); - services.set_enabled("unetd", false); - return; -} - -services.set_enabled("unetd", true); - -let ips = []; - -wireguard_overlay.root_node.name = "gateway"; -wireguard_overlay.root_node.groups = [ "gateway" ]; - -for (let ip in wireguard_overlay.root_node.ipaddr) - push(ips, ip); - -if (wireguard) - wireguard_overlay.root_node.subnet = [ '0.0.0.0/0' ]; - -latency.add(wireguard_overlay.root_node.endpoint, 4); - -let cfg = { - 'config': { - 'port': wireguard_overlay.peer_port, - 'peer-exchange-port': wireguard_overlay.peer_exchange_port, - 'keepalive': 10 - }, - 'hosts': { - gateway: wireguard_overlay.root_node, - } -}; - -let pipe = require('fs').popen(sprintf('echo "%s" | wg pubkey', wireguard_overlay.private_key)); -let pubkey = replace(pipe.read("all"), '\n', ''); -pipe.close(); -for (let host in wireguard_overlay.hosts) - if (host.name) { - if (!host.name || !host.key) { - warn('host is not configured correctly\n'); - return; - } - - cfg.hosts[host.name] = host; - cfg.hosts[host.name].groups = [ 'ap' ]; - if (host.key == pubkey) - continue; - for (let ip in host.ipaddr) - push(ips, ip); - } -if (vxlan) { - cfg.services = { - "l2-tunnel": { - "type": "vxlan", - "config": { - port: wireguard_overlay?.vxlan?.port || 4789, - }, - "members": [ "gateway", "@ap" ] - } - }; - - if (wireguard_overlay?.vxlan?.isolate ?? true) - cfg.services['l2-tunnel'].config.forward_ports = [ "gateway" ]; -} - -system('rm /tmp/unet.*.json'); -let filename = '/tmp/unet.' + time() + '.json'; - -files.add_named(filename, cfg); - -include('../interface/firewall.uc', { name: 'unet', ipv4_mode: true, ipv6_mode: true, interface: { role: 'upstream' }, networks: [ 'unet' ] }); -%} - - -# Wireguard Overlay Configuration -set network.unet=interface -set network.unet.proto=unet -set network.unet.device=unet -set network.unet.file={{ s(filename) }} -set network.unet.key={{ s(wireguard_overlay.private_key) }} -set network.unet.domain=unet -set network.unet.ip4table='{{ routing_table.get('wireguard_overlay') }}' -{% if (vxlan): %} -set network.unet.tunnels='vx-unet=l2-tunnel' - -add firewall rule -set firewall.@rule[-1].name='Allow-VXLAN-unet' -set firewall.@rule[-1].src='unet' -set firewall.@rule[-1].proto='udp' -set firewall.@rule[-1].target='ACCEPT' -set firewall.@rule[-1].dest_port={{ wireguard_overlay?.vxlan?.port || 3457 }} -{% endif %} - -{% for (let ip in ips): %} -add network route -set network.@route[-1].interface='unet' -set network.@route[-1].target={{ s(ip) }} -set network.@route[-1].table='local' -{% latency.add(ip, 4) %} -{% endfor %} diff --git a/renderer/templates/spotfilter.uc b/renderer/templates/spotfilter.uc deleted file mode 100644 index 08a7eed..0000000 --- a/renderer/templates/spotfilter.uc +++ /dev/null @@ -1,52 +0,0 @@ -{% -let interfaces = services.lookup_interfaces_by_ssids("captive"); -let enable = length(interfaces); -if (enable != 1) - return; - -for (let name, data in captive.interfaces) { - let config = { - name, - devices: [], - config: { - default_class: 0, - default_dns_class: 1, - client_autoremove: false, - class: [ - { - index: 0, - device_macaddr: split(name, '_')[0], - fwmark: 1, - fwmark_mask: 127 - }, { - index: 1, - fwmark: 2, - fwmark_mask: 127 - } - ], - whitelist: [ - { - "class": 1, - "hosts": [ ], - "address": [], - } - ] - } - }; - - for (let iface in data.iface) - push(config.devices, 'wlanc' + iface); - - for (let fqdn in data.walled_garden_fqdn) - push(config.config.whitelist[0].hosts, fqdn); - - for (let ipaddr in data.walled_garden_ipaddr) - push(config.config.whitelist[0].address, ipaddr); - - let fs = require('fs'); - let file = fs.open('/tmp/spotfilter-' + name + '.json', 'w'); - file.write(config); - file.close(); - services.set_enabled("uhttpd", true) -} -%} diff --git a/renderer/templates/switch.uc b/renderer/templates/switch.uc deleted file mode 100644 index 336b6ed..0000000 --- a/renderer/templates/switch.uc +++ /dev/null @@ -1,17 +0,0 @@ -{% if (state.switch.port_mirror && state.switch.port_mirror.monitor_ports && state.switch.port_mirror.analysis_port): %} - -{% - let analysis = ethernet.lookup_by_select_ports([state.switch.port_mirror.analysis_port]); - ethernet.reserve_port(state.switch.port_mirror.analysis_port); - let mirrors = ethernet.lookup_by_select_ports(state.switch.port_mirror.monitor_ports); -%} - -# Switch port-mirror configuration - -set switch.mirror=port-mirror -{% for (let mirror in mirrors): %} -add_list switch.mirror.monitor={{ s(mirror) }} -{% endfor %} -set switch.mirror.analysis={{ s(analysis[0]) }} - -{% endif %} diff --git a/renderer/templates/toplevel.uc b/renderer/templates/toplevel.uc deleted file mode 100644 index a7902fa..0000000 --- a/renderer/templates/toplevel.uc +++ /dev/null @@ -1,149 +0,0 @@ -{% - let fs = require('fs'); - - // reject the config if there is no valid upstream configuration - if (!state.uuid) { - warn('Configuration must contain a valid UUID. Rejecting whole file'); - die('Configuration must contain a valid UUID. Rejecting whole file'); - } - - include('admin_ui.uc'); - - // reject the config if there is no valid upstream configuration - let upstream; - for (let i, interface in state.interfaces) { - if (interface.role != 'upstream') - continue; - upstream = interface; - } - - if (!upstream) { - warn('Configuration must contain at least one valid upstream interface. Rejecting whole file'); - die('Configuration must contain at least one valid upstream interface. Rejecting whole file'); - } - - - for (let i, interface in state.interfaces) - interface.index = i; - - /* find out which vlans are used and which should be assigned dynamically */ - let vlans = []; - for (let i, interface in state.interfaces) - if (ethernet.has_vlan(interface)) - push(vlans, interface.vlan.id); - else - interface.vlan = { id: 0}; - - // populate the broad-band profile if present. This needs to happen after the default vlans - // and before the dynamic vlan are assigned - let profile = local_profile.get(); - if (profile && profile.broadband) - include('broadband.uc', { broadband: profile.broadband }); - - let vid = 4090; - function next_free_vid() { - while (index(vlans, vid) >= 0) - vid--; - return vid--; - } - - /* dynamically assign vlan ids to all interfaces that have none yet */ - for (let i, interface in state.interfaces) - if (!interface.vlan.id) - interface.vlan.dyn_id = next_free_vid(); - - /* dynamically assign vlans to all swconfig ports */ - let swconfig = false; - for (let k, port in ethernet.ports) { - if (port.swconfig == null) - continue; - port.vlan = next_free_vid(); - port.switch = capab.switch_ports[port.netdev]; - port.netdev += '.' + port.vlan; - swconfig = true; - } - - if (swconfig) { - ethernet.swconfig = {}; - for (let k, port in ethernet.ports) { - if (!port.switch) - continue; - ethernet.swconfig[port.netdev] = port; - } - } - - include('base.uc'); - - if (state.unit) - include('unit.uc', { location: '/unit', unit: state.unit }); - - if (!state.services) - state.services = {}; - - for (let service in services.lookup_services()) - tryinclude('services/' + service + '.uc', { - location: '/services/' + service, - [service]: state.services[service] || {} - }); - - if (!state.metrics) - state.metrics = {}; - - let file = fs.open('/etc/events.json', 'r'); - let events = []; - if (file) { - try { - events = json(file.read('all')); - } catch(e) { - - } - file.close(); - } - for (let metric in services.lookup_metrics()) - tryinclude('metric/' + metric + '.uc', { - location: '/metric/' + metric, - [metric]: state.metrics[metric] || {}, - events - }); - - if (state.switch) - tryinclude('switch.uc', { - location: '/switch/' - }); - - for (let i, ports in state.ethernet) - include('ethernet.uc', { location: '/ethernet/' + i, ports }); - - for (let i, radio in state.radios) - include('radio.uc', { location: '/radios/' + i, radio }); - - function iterate_interfaces(role) { - for (let i, interface in state.interfaces) { - if (interface.role != role) - continue; - include('interface.uc', { location: '/interfaces/' + i, interface, vlans }); - } - } - - iterate_interfaces("upstream"); - iterate_interfaces("downstream"); - - let fs = require('fs'); - for (let name in fs.glob('/usr/share/ucentral/templates/third-party/*.uc')) { - name = split(fs.basename(name), '.')[0]; - let config = state.third_party ? state.third_party[name] : {}; - tryinclude('third-party/' + name + '.uc', { - location: '/third-party/' + name, - [replace(name, '-', '_')]: config - }); - } - services.set_enabled("usteer2", true); - - include('spotfilter.uc'); - - if (state.config_raw) - include("config_raw.uc", { location: '/config_raw', config_raw: state.config_raw }); - - latency.write(); - services.set_enabled("bridger", false); -%} diff --git a/renderer/templates/unit.uc b/renderer/templates/unit.uc deleted file mode 100644 index c93fef9..0000000 --- a/renderer/templates/unit.uc +++ /dev/null @@ -1,19 +0,0 @@ - -# Basic unit configuration -{% if (unit.name): %} -set system.@system[-1].description={{ s(unit.name) }} -{% endif %} -{% if (unit.hostname): %} -set system.@system[-1].hostname={{ s(unit.hostname) }} -{% endif %} -{% if (unit.location): %} -set system.@system[-1].notes={{ s(unit.location) }} -{% endif %} -{% if (unit.timezone): %} -set system.@system[-1].timezone={{ s(unit.timezone) }} -{% endif %} -set system.@system[-1].leds_off={{ b(!unit.leds_active) }} -{% -shell.password(unit.random_password); -services.set_enabled("led", true); -%} diff --git a/renderer/ucentral.uc b/renderer/ucentral.uc deleted file mode 100755 index 8f7ddcd..0000000 --- a/renderer/ucentral.uc +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/ucode -push(REQUIRE_SEARCH_PATH, - "/usr/lib/ucode/*.so", - "/usr/share/ucentral/*.uc"); - -let schemareader = require("schemareader"); -let renderer = require("renderer"); -let fs = require("fs"); -let ubus = require("ubus").connect(); - -let inputfile = fs.open(ARGV[0], "r"); -let inputjson = json(inputfile.read("all")); -let custom_config = (split(ARGV[0], ".")[0] != "/etc/ucentral/ucentral"); - -let error = 0; - -inputfile.close(); -let logs = []; - -function set_service_state(state) { - for (let service, enable in renderer.services_state()) { - if (enable != state) - continue; - printf("%s %s\n", service, enable ? "starting" : "stopping"); - system(sprintf("/etc/init.d/%s %s", service, (enable || enable == 'early') ? "restart" : "stop")); - } - system("/etc/init.d/dnsmasq restart"); -} - -try { - for (let cmd in [ 'rm -rf /tmp/ucentral', - 'mkdir /tmp/ucentral', - 'rm /tmp/dnsmasq.conf', - '/etc/init.d/spotfilter stop', - 'touch /tmp/dnsmasq.conf' ]) - system(cmd); - - let state = schemareader.validate(inputjson, logs); - - let batch = state ? renderer.render(state, logs) : ""; - - if (state.strict && length(logs)) { - push(logs, 'Rejecting config due to strict-mode validation'); - state = null; - } - - fs.stdout.write("Log messages:\n" + join("\n", logs) + "\n\n"); - - if (state) { - fs.stdout.write("UCI batch output:\n" + batch + "\n"); - - let outputjson = fs.open("/tmp/ucentral.uci", "w"); - outputjson.write(batch); - outputjson.close(); - - for (let cmd in [ 'rm -rf /tmp/config-shadow', - 'cp -r /etc/config-shadow /tmp' ]) - system(cmd); - - let apply = fs.popen("/sbin/uci -c /tmp/config-shadow batch", "w"); - apply.write(batch); - apply.close(); - - renderer.write_files(logs); - - set_service_state(false); - - for (let cmd in [ 'uci -c /tmp/config-shadow commit', - 'cp /tmp/config-shadow/* /etc/config/', - 'rm -rf /tmp/config-shadow']) - system(cmd); - - set_service_state('early'); - - ubus.call('state', 'reload'); - - for (let cmd in [ 'reload_config', - '/etc/init.d/ratelimit restart', - '/etc/init.d/dnsmasq restart', - 'ubus call state reload']) - system(cmd); - - if (!custom_config) { - fs.unlink('/etc/ucentral/ucentral.active'); - fs.symlink(ARGV[0], '/etc/ucentral/ucentral.active'); - } - - set_service_state(true); - } else { - error = 1; - } - if (!length(batch) || !state) - error = 2; - else if (length(logs)) - error = 1; -} -catch (e) { - error = 2; - warn("Fatal error while generating UCI: ", e, "\n", e.stacktrace[0].context, "\n"); -} - -if (inputjson.uuid && inputjson.uuid > 1 && !custom_config) { - let text = [ 'Success', 'Rejects', 'Failed' ]; - let status = { - error, - text: text[error] || "Failed", - }; - if (length(logs)) - status.rejected = logs; - - ubus.call("ucentral", "result", { - uuid: inputjson.uuid || 0, - id: +ARGV[1] || 0, - status, - }); - if (error > 1) - exit(1); -} diff --git a/renderer/wifi/iface.uc b/renderer/wifi/iface.uc deleted file mode 100644 index b69b5eb..0000000 --- a/renderer/wifi/iface.uc +++ /dev/null @@ -1,79 +0,0 @@ -let nl = require("nl80211"); -let def = nl.const; - -const NL80211_IFTYPE_STATION = 2; -const NL80211_IFTYPE_AP = 3; -const NL80211_IFTYPE_MESH_POINT = 7; -let iftypes = { - [NL80211_IFTYPE_STATION]: "station", - [NL80211_IFTYPE_AP]: "ap", - [NL80211_IFTYPE_MESH_POINT]: "mesh", -}; - -const NL80211_CHAN_WIDTH_20 = 1; -const NL80211_CHAN_WIDTH_40 = 2; -const NL80211_CHAN_WIDTH_80 = 3; -const NL80211_CHAN_WIDTH_80P80 = 4; -const NL80211_CHAN_WIDTH_160 = 5; -const NL80211_CHAN_WIDTH_5 = 6; -const NL80211_CHAN_WIDTH_10 = 7; -let chwidth = { - [NL80211_CHAN_WIDTH_20]: "20", - [NL80211_CHAN_WIDTH_40]: "40", - [NL80211_CHAN_WIDTH_80]: "80", - [NL80211_CHAN_WIDTH_80P80]: "80p80", - [NL80211_CHAN_WIDTH_160]: "160", - [NL80211_CHAN_WIDTH_5]: "5", - [NL80211_CHAN_WIDTH_10]: "10", -}; - -function freq2channel(freq) { - if (freq == 2484) - return 14; - else if (freq < 2484) - return (freq - 2407) / 5; - else if (freq >= 4910 && freq <= 4980) - return (freq - 4000) / 5; - else if(freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) - return (freq - 56160) / 2160; - else if (freq >= 5955 && freq <= 7115) - return (freq - 5950) / 5; - else - return (freq - 5000) / 5; -} - -function wif_get(wdev) { - let res = nl.request(def.NL80211_CMD_GET_INTERFACE, def.NLM_F_DUMP); - - if (res === false) - warn("Unable to lookup interfaces: " + nl.error() + "\n"); - - return res; -} - -function lookup_wifs() { - let wifs = wif_get(); - let rv = {}; - for (let wif in wifs) { - if (!wif.wiphy_freq || !iftypes[wif.iftype]) - continue; - let w = {}; - w.ssid = wif.ssid; - w.bssid = wif.mac; - w.mode = iftypes[wif.iftype]; - w.channel = []; - w.frequency = []; - w.tx_power = (wif.wiphy_tx_power_level / 100) || 0; - for (let f in [ wif.wiphy_freq, wif.center_freq1, wif.center_freq2 ]) - if (f) { - push(w.channel, freq2channel(f)); - push(w.frequency, f); - } - if (chwidth[wif.channel_width]) - w.ch_width = chwidth[wif.channel_width]; - rv[wif.ifname] = w; - } - return rv; -} - -return lookup_wifs(); diff --git a/renderer/wifi/mesh.uc b/renderer/wifi/mesh.uc deleted file mode 100644 index 21bf18c..0000000 --- a/renderer/wifi/mesh.uc +++ /dev/null @@ -1,40 +0,0 @@ -let nl = require("nl80211"); -let def = nl.const; - -const NL80211_IFTYPE_MESH_POINT = 7; - -function wif_get(wdev) { - let res = nl.request(def.NL80211_CMD_GET_INTERFACE, def.NLM_F_DUMP); - - if (res === false) - warn("Unable to lookup interfaces: " + nl.error() + "\n"); - - return res; -} - -function lookup_mesh() { - let wifs = wif_get(); - let rv = {}; - for (let wif in wifs) { - if (!wif.wiphy_freq || wif.iftype != NL80211_IFTYPE_MESH_POINT) - continue; - let w = []; - let params = { dev: wif.ifname }; - let mpath = nl.request(def.NL80211_CMD_GET_MPATH, def.NLM_F_DUMP, params); - for (let path in mpath) { - push(w, { - destinantion: path.mac, - next_hop: path.mpath_next_hop, - metric: path.mpath_info.metric, - expire: path.mpath_info.expire, - discovery_timeout: path.mpath_info.discovery_timeout, - discovery_retries: path.mpath_info.discovery_retries, - hop_count: path.mpath_info.hop_count, - }); - } - rv[wif.ifname] = w; - } - return rv; -} - -return lookup_mesh(); diff --git a/renderer/wifi/phy.uc b/renderer/wifi/phy.uc deleted file mode 100644 index 95510ba..0000000 --- a/renderer/wifi/phy.uc +++ /dev/null @@ -1,170 +0,0 @@ -let fs = require('fs'); -let uci = require("uci"); -let cursor = uci ? uci.cursor() : null; -let nl = require("nl80211"); -let def = nl.const; - -function freq2channel(freq) { - if (freq == 2484) - return 14; - else if (freq < 2484) - return (freq - 2407) / 5; - else if (freq >= 4910 && freq <= 4980) - return (freq - 4000) / 5; - else if(freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) - return (freq - 56160) / 2160; - else if (freq >= 5955 && freq <= 7115) - return (freq - 5950) / 5; - else - return (freq - 5000) / 5; -} - -function phy_get(wdev) { - let res = nl.request(def.NL80211_CMD_GET_WIPHY, def.NLM_F_DUMP, { split_wiphy_dump: true }); - - if (res === false) - warn("Unable to lookup phys: " + nl.error() + "\n"); - - return res; -} - -let paths = {}; - -function add_path(path, phy, index) { - if (!phy) - return; - phy = fs.basename(phy); - paths[phy] = path; - if (index) - paths[phy] += '+' + index; -} - -function lookup_paths() { - let wireless = cursor.get_all('wireless'); - for (let k, section in wireless) { - if (section['.type'] != 'wifi-device' || !section.path) - continue; - let phys = fs.glob(sprintf('/sys/devices/%s/ieee80211/phy*', section.path)); - if (!length(phys)) - phys = fs.glob(sprintf('/sys/devices/platform/%s/ieee80211/phy*', section.path)); - if (!length(phys)) - continue; - sort(phys); - let index = 0; - for (let phy in phys) - add_path(section.path, phy, index++); - } -} - -function get_hwmon(phy) { - let hwmon = fs.glob(sprintf('/sys/class/ieee80211/%s/hwmon*/temp*_input', phy)); - if (!hwmon) - return 0; - let file = fs.open(hwmon[0], 'r'); - if (!file) - return 0; - let temp = +file.read('all'); - file.close(); - return temp; -} - -function lookup_phys() { - lookup_paths(); - - let phys = phy_get(); - let ret = {}; - for (let phy in phys) { - let phyname = 'phy' + phy.wiphy; - let path = paths[phyname]; - if (!path) - continue; - - let p = {}; - let temp = get_hwmon('phy' + phy.wiphy); - if (temp) - p.temperature = temp / 1000; - - p.tx_ant = phy.wiphy_antenna_tx; - p.rx_ant = phy.wiphy_antenna_rx; - p.tx_ant_avail = phy.wiphy_antenna_avail_tx; - p.rx_ant_avail = phy.wiphy_antenna_avail_rx; - p.frequencies = []; - p.channels = []; - p.dfs_channels = []; - p.htmode = []; - p.band = []; - for (let band in phy.wiphy_bands) { - for (let freq in band?.freqs) { - if (freq.disabled) - continue; - push(p.frequencies, freq.freq); - push(p.channels, freq2channel(freq.freq)); - if (freq.radar) - push(p.dfs_channels, freq2channel(freq.freq)); - if (freq.freq >= 6000) - push(p.band, '6G'); - else if (freq.freq <= 2484) - push(p.band, '2G'); - else if (freq.freq >= 5160 && freq.freq <= 5885) - push(p.band, '5G'); - } - if (band?.ht_capa) { - p.ht_capa = band.ht_capa; - push(p.htmode, 'HT20'); - if (band.ht_capa & 0x2) - push(p.htmode, 'HT40'); - } - if (band?.vht_capa) { - p.vht_capa = band.vht_capa; - push(p.htmode, 'VHT20', 'VHT40', 'VHT80'); - let chwidth = (band?.vht_capa >> 2) & 0x3; - switch(chwidth) { - case 2: - push(p.htmode, 'VHT80+80'); - /* fall through */ - case 1: - push(p.htmode, 'VHT160'); - } - } - for (let iftype in band?.iftype_data) { - if (iftype.iftypes?.ap) { - p.he_phy_capa = iftype?.he_cap_phy; - p.he_mac_capa = iftype?.he_cap_mac; - push(p.htmode, 'HE20'); - let chwidth = (iftype?.he_cap_phy[0] || 0) & 0xff; - if (chwidth & 0x2 || chwidth & 0x4) - push(p.htmode, 'HE40'); - if (chwidth & 0x4) - push(p.htmode, 'HE80'); - if (chwidth & 0x8 || chwidth & 0x10) - push(p.htmode, 'HE160'); - if (chwidth & 0x10) - push(p.htmode, 'HE80+80'); - if (iftype.eht_cap_phy) { - p.eht_phy_capa = iftype?.eht_cap_phy; - p.eht_mac_capa = iftype?.eht_cap_mac; - push(p.htmode, 'EHT20'); - if (chwidth & 0x2 || chwidth & 0x4) - push(p.htmode, 'EHT40'); - if (chwidth & 0x4) - push(p.htmode, 'EHT80'); - if (chwidth & 0x8 || chwidth & 0x10) - push(p.htmode, 'EHT160'); - if (chwidth & 0x10) - push(p.htmode, 'EHT80+80'); - if ('6G' in p.band) - push(p.htmode, 'EHT320'); - } - } - } - } - - p.band = uniq(p.band); - if (!length(p.dfs_channels)) - delete p.dfs_channels; - ret[path] = p; - } - return ret; -} - -return lookup_phys(); diff --git a/renderer/wifi/station.uc b/renderer/wifi/station.uc deleted file mode 100644 index 20b2e95..0000000 --- a/renderer/wifi/station.uc +++ /dev/null @@ -1,103 +0,0 @@ -let nl = require("nl80211"); -let def = nl.const; - - -function parse_bitrate(info) { - let rate = { - bitrate: (info.bitrate32 || info.bitrate) * 100 - }; - - if (info.short_gi) - rate.sgi = true; - - if (info.mcs) { - rate.ht = true; - rate.mcs = info.mcs; - } - else if (info.vht_mcs) { - rate.vht = true; - rate.mcs = info.vht_mcs; - rate.nss = info.vht_nss; - } - else if (info.he_mcs) { - rate.he = true; - rate.mcs = info.he_mcs; - rate.nss = info.he_nss; - rate.he_gi = info.he_gi; - rate.he_dcm = info.he_dcm; - } - - if (info.width_40) - rate.chwidth = 40; - else if (info.width_80) - rate.chwidth = 80; - else if (info.width_80p80) - rate.chwidth = 8080; - else if (info.width_160) - rate.chwidth = 160; - else if (info.width_10) - rate.chwidth = 10; - else if (info.width_5) - rate.chwidth = 5; - else - rate.chwidth = 20; - return rate; -} - -function iface_assoclist(wif) { - let params = { dev: wif.ifname }; - let res = nl.request(def.NL80211_CMD_GET_STATION, def.NLM_F_DUMP, params); - - if (res === false) { - warn("Unable to lookup associations: " + nl.error() + "\n"); - return []; - } - let assocdev = []; - for (let sta in res) { - let assoc = { - bssid: wif.mac, - station: sta.mac, - connected: +sta.sta_info?.connected_time, - inactive: +sta.sta_info?.inactive_time / 1000, - tx_duration: +sta.sta_info?.tx_duration, - rx_duration: +sta.sta_info?.rx_duration, - rssi: +sta.sta_info?.signal, - ack_signal: +sta.sta_info?.ack_signal, - ack_signal_avg: +sta.sta_info?.ack_signal_avg, - rx_packets: +sta.sta_info?.rx_packets, - tx_packets: +sta.sta_info?.tx_packets, - rx_bytes: +sta.sta_info?.rx_bytes64, - tx_bytes: +sta.sta_info?.tx_bytes64, - tx_retries: +sta.sta_info?.tx_retries, - tx_failed: +sta.sta_info?.tx_failed, - rx_rate: parse_bitrate(sta.sta_info?.rx_bitrate || {}), - tx_rate: parse_bitrate(sta.sta_info?.tx_bitrate || {}), - }; - if (global.tid_stats) - assoc.tid_stats = sta.sta_info?.tid_stats || []; - push(assocdev, assoc); - }; - return assocdev; -} - -function wif_get(wdev) { - let res = nl.request(def.NL80211_CMD_GET_INTERFACE, def.NLM_F_DUMP); - - if (res === false) - warn("Unable to lookup interfaces: " + nl.error() + "\n"); - - return res; -} - -function lookup_stations() { - let rv = {}; - let wifs = wif_get(); - for (let wif in wifs) { - let assoc = iface_assoclist(wif); - if (length(assoc)) - rv[wif.ifname] = assoc; - } - return rv; -} - -return lookup_stations(); diff --git a/renderer/wifi/survey.uc b/renderer/wifi/survey.uc deleted file mode 100644 index 365c3d2..0000000 --- a/renderer/wifi/survey.uc +++ /dev/null @@ -1,36 +0,0 @@ -let nl = require("nl80211"); -let def = nl.const; -let rv = { survey: [] }; -//let frequency = 5660; - -function survey_get(dev) { - let res = nl.request(def.NL80211_CMD_GET_SURVEY, def.NLM_F_DUMP, { dev }); - - if (res === false) - warn("Unable to lookup survey: " + nl.error() + "\n"); - - return res; -} - -function wif_get(wdev) { - let res = nl.request(def.NL80211_CMD_GET_INTERFACE, def.NLM_F_DUMP); - - if (res === false) - warn("Unable to lookup interfaces: " + nl.error() + "\n"); - - return res; -} - -function lookup_survey() { - let wifs = wif_get(); - - for (let wif in wifs) - for (let survey in survey_get(wif.ifname)) - if (!frequency || survey.survey_info.frequency == frequency) - if (survey.survey_info?.time) - push(rv.survey, survey.survey_info); -} - -lookup_survey(); -//printf('%.J\n', rv); -return rv; diff --git a/schema/definitions.yml b/schema/definitions.yml deleted file mode 100644 index daed0cd..0000000 --- a/schema/definitions.yml +++ /dev/null @@ -1,15 +0,0 @@ -description: - This section is used to define templates that can be referenced by a - configuration. This avoids duplication of data. A RADIUS server can be - defined here for example and then referenced by several SSIDs. -type: object -properties: - wireless-encryption: - type: object - description: - A dictionary of wireless encryption templates which can be referenced - by the corresponding property name. - patternProperties: - ".+": - $ref: "https://ucentral.io/schema/v1/interface/ssid/encryption/" - additionalProperties: false diff --git a/schema/globals.wireless-multimedia.class-selector.yml b/schema/globals.wireless-multimedia.class-selector.yml deleted file mode 100644 index 35bb21b..0000000 --- a/schema/globals.wireless-multimedia.class-selector.yml +++ /dev/null @@ -1,28 +0,0 @@ -type: array -items: - type: string - enum: - - CS0 - - CS1 - - CS2 - - CS3 - - CS4 - - CS5 - - CS6 - - CS7 - - AF11 - - AF12 - - AF13 - - AF21 - - AF22 - - AF23 - - AF31 - - AF32 - - AF33 - - AF41 - - AF42 - - AF43 - - DF - - EF - - VA - - LE diff --git a/schema/globals.wireless-multimedia.profile.yml b/schema/globals.wireless-multimedia.profile.yml deleted file mode 100644 index b0c6076..0000000 --- a/schema/globals.wireless-multimedia.profile.yml +++ /dev/null @@ -1,12 +0,0 @@ -type: object -additionalProperties: false -properties: - profile: - description: - Define a default profile that shall be used for the WMM behaviour of all SSIDs on - the device. - type: string - enum: - - enterprise - - rfc8325 - - 3gpp diff --git a/schema/globals.wireless-multimedia.table.yml b/schema/globals.wireless-multimedia.table.yml deleted file mode 100644 index 3978beb..0000000 --- a/schema/globals.wireless-multimedia.table.yml +++ /dev/null @@ -1,23 +0,0 @@ -description: - Define the default WMM behaviour of all SSIDs on the device. Each access - category can be assigned a default class selector that gets used for packet - matching. -type: object -additionalProperties: false -properties: - UP0: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' - UP1: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' - UP2: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' - UP3: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' - UP4: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' - UP5: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' - UP6: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' - UP7: - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/class-selector/' diff --git a/schema/globals.yml b/schema/globals.yml index bf538b0..ef48aee 100644 --- a/schema/globals.yml +++ b/schema/globals.yml @@ -19,10 +19,6 @@ properties: format: uc-cidr6 examples: - fdca:1234:4567::/48 - wireless-multimedia: - anyOf: - - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/table/' - - $ref: 'https://ucentral.io/schema/v1/globals/wireless-multimedia/profile/' ipv4-blackhole: description: Define a list of non-interface specific BLACKHOLE (to-nowhere) routes. diff --git a/schema/interface.ssid.acl.yml b/schema/interface.ssid.acl.yml deleted file mode 100644 index a918754..0000000 --- a/schema/interface.ssid.acl.yml +++ /dev/null @@ -1,18 +0,0 @@ -description: - The MAC ACL that defines which clients are allowed or denied to associations. -type: object -properties: - mode: - description: - Defines if this is an allow or deny list. - type: string - enum: - - allow - - deny - mac-address: - description: - Association requests will be denied if the rssi is below this threshold. - type: array - items: - type: string - format: uc-mac diff --git a/schema/interface.ssid.certificates.yml b/schema/interface.ssid.certificates.yml deleted file mode 100644 index 17a4e60..0000000 --- a/schema/interface.ssid.certificates.yml +++ /dev/null @@ -1,27 +0,0 @@ -description: - When running a local EAP server or using STA/MESH to connect to another BSS - a set of certificates is required. -type: object -properties: - use-local-certificates: - description: - The device will use its local certificate bundle for the TLS setup and - ignores all other certificate options in this section. - type: boolean - default: false - ca-certificate: - description: - The local servers CA bundle. - type: string - certificate: - description: - The local servers certificate. - type: string - private-key: - description: - The local servers private key/ - type: string - private-key-password: - description: - The password required to read the private key. - type: string diff --git a/schema/interface.ssid.encryption.yml b/schema/interface.ssid.encryption.yml deleted file mode 100644 index 357bf12..0000000 --- a/schema/interface.ssid.encryption.yml +++ /dev/null @@ -1,48 +0,0 @@ -description: - A device has certain properties that describe its identity and location. - These properties are described inside this object. -type: object -properties: - proto: - description: - The wireless encryption protocol that shall be used for this BSS - type: string - enum: - - none - - owe - - owe-transition - - psk - - psk2 - - psk-mixed - - psk2-radius - - wpa - - wpa2 - - wpa-mixed - - sae - - sae-mixed - - wpa3 - - wpa3-192 - - wpa3-mixed - examples: - - psk2 - key: - description: - The Pre Shared Key (PSK) that is used for encryption on the BSS when - using any of the WPA-PSK modes. - type: string - maxLength: 63 - minLength: 8 - ieee80211w: - description: - Enable 802.11w Management Frame Protection (MFP) for this BSS. - type: string - enum: - - disabled - - optional - - required - default: disabled - key-caching: - description: - PMKSA created through EAP authentication and RSN preauthentication can be cached. - type: boolean - default: true diff --git a/schema/interface.ssid.multi-psk.yml b/schema/interface.ssid.multi-psk.yml deleted file mode 100644 index c789be7..0000000 --- a/schema/interface.ssid.multi-psk.yml +++ /dev/null @@ -1,23 +0,0 @@ -type: object -description: - A SSID can have multiple PSK/VID mappings. Each one of them can be bound to a - specific MAC or be a wildcard. -properties: - mac: - type: string - format: uc-mac - key: - description: - The Pre Shared Key (PSK) that is used for encryption on the BSS when - using any of the WPA-PSK modes. - type: string - maxLength: 63 - minLength: 8 - vlan-id: - type: integer - maximum: 4096 - examples: - - 3 - - 100 - - 200 - - 4094 diff --git a/schema/interface.ssid.pass-point.yml b/schema/interface.ssid.pass-point.yml deleted file mode 100644 index 45667e0..0000000 --- a/schema/interface.ssid.pass-point.yml +++ /dev/null @@ -1,209 +0,0 @@ -description: - Enable Hotspot 2.0 support. -type: object -properties: - venue-name: - description: - This parameter can be used to configure one or more Venue Name Duples - for Venue Name ANQP information. - type: array - items: - type: string - venue-group: - description: - The available values are defined in 802.11u. - type: integer - maximum: 32 - venue-type: - description: - The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34 - type: integer - maximum: 32 - venue-url: - description: - This parameter can be used to configure one or more Venue URL Duples to - provide additional information corresponding to Venue Name information. - type: array - items: - type: string - format: uri - auth-type: - description: - This parameter indicates what type of network authentication is used in - the network. - type: object - properties: - type: - description: - Specifies the specific network authentication type in use. - type: string - enum: - - "terms-and-conditions" - - "online-enrollment" - - "http-redirection" - - "dns-redirection" - uri: - description: - Specifies the redirect URL applicable to the indicated authentication type. - type: string - format: uri - examples: - - https://operator.example.org/wireless-access/terms-and-conditions.html - - http://www.example.com/redirect/me/here/ - minLength: 2 - maxLength: 2 - domain-name: - description: - The IEEE 802.11u Domain Name. - type: array - items: - type: string - format: hostname - nai-realm: - description: - NAI Realm information - type: array - items: - type: string - osen: - description: - OSU Server-Only Authenticated L2 Encryption Network; - type: boolean - anqp-domain: - description: - ANQP Domain ID, An identifier for a set of APs in an ESS that share the - same common ANQP information. - type: integer - maximum: 65535 - minimum: 0 - anqp-3gpp-cell-net: - description: - The ANQP 3GPP Cellular Network information. - type: array - items: - type: string - friendly-name: - description: - This parameter can be used to configure one or more Operator Friendly - Name Duples. - type: array - items: - type: string - access-network-type: - description: - Indicate the type of network. This is part of the interworking IE. - type: integer - maximum: 15 - default: 0 - internet: - description: - Whether the network provides connectivity to the Internet - type: boolean - default: true - asra: - description: - Additional Step Required for Access. - type: boolean - default: false - esr: - description: - Emergency services reachable. - type: boolean - default: false - uesa: - description: - Unauthenticated emergency service accessible. - type: boolean - default: false - hessid: - description: - Homogeneous ESS identifier - type: string - example: 00:11:22:33:44:55 - roaming-consortium: - description: - Roaming Consortium OIs can be configured here. Each OI is between 3 and 15 - octets and is configured as a hexstring. - type: array - items: - type: string - disable-dgaf: - description: - Disable Downstream Group-Addressed Forwarding. This can be used to configure - a network where no group-addressed frames are allowed. - type: boolean - default: false - ipaddr-type-available: - description: - IP Address Type Availability. - type: integer - maximum: 255 - connection-capability: - description: - This can be used to advertise what type of IP traffic can be sent through the - hotspot. - type: array - items: - type: string - icons: - description: - The operator icons. - type: array - items: - type: object - properties: - width: - type: integer - description: The width of the operator icon in pixel - examples: - - 64 - height: - type: integer - description: The height of the operator icon in pixel - examples: - - 64 - type: - type: string - description: The mimetype of the operator icon - examples: - - image/png - icon: - type: string - format: uc-base64 - description: The base64 encoded image - language: - type: string - description: ISO 639-2 language code of the icon - pattern: "^[a-z][a-z][a-z]$" - examples: - - eng - - fre - - ger - - ita - examples: - - width: 32 - height: 32 - type: image/png - language: eng - icon: R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7 - wan-metrics: - description: - A description of the wan metric offered by this device. - type: object - properties: - info: - description: - The state of the devices uplink - type: string - enum: - - up - - down - - testing - downlink: - description: - Estimate of WAN backhaul link current downlink speed in kbps. - type: integer - uplink: - description: - Estimate of WAN backhaul link current uplink speed in kbps. - type: integer diff --git a/schema/interface.ssid.quality-thresholds.yml b/schema/interface.ssid.quality-thresholds.yml deleted file mode 100644 index ea9e4b1..0000000 --- a/schema/interface.ssid.quality-thresholds.yml +++ /dev/null @@ -1,21 +0,0 @@ -description: - The thresholds that need to be meet for a clien association to be allowed. -type: object -properties: - probe-request-rssi: - description: - Probe requests will be ignored if the rssi is below this threshold. - type: integer - association-request-rssi: - description: - Association requests will be denied if the rssi is below this threshold. - type: integer - client-kick-rssi: - description: - Clients will get kicked if their SNR drops below this value. - type: integer - client-kick-ban-time: - description: - The duration that a client is banned from re-joining after it was kicked. - type: integer - default: 0 diff --git a/schema/interface.ssid.radius.health.yml b/schema/interface.ssid.radius.health.yml deleted file mode 100644 index fad42a8..0000000 --- a/schema/interface.ssid.radius.health.yml +++ /dev/null @@ -1,12 +0,0 @@ -description: - The credentials used when health check probes this radius server. -type: object -properties: - username: - description: - The username that gets used when doing a healthcheck on this radius server. - type: string - password: - description: - The password that gets used when doing a healthcheck on this radius server. - type: string diff --git a/schema/interface.ssid.radius.local-user.yml b/schema/interface.ssid.radius.local-user.yml deleted file mode 100644 index bb5146f..0000000 --- a/schema/interface.ssid.radius.local-user.yml +++ /dev/null @@ -1,22 +0,0 @@ -type: object -description: - Describes a local EAP user/psk/vid triplet. -properties: - mac: - type: string - format: uc-mac - user-name: - type: string - minLength: 1 - password: - type: string - maxLength: 63 - minLength: 8 - vlan-id: - type: integer - maximum: 4096 - examples: - - 3 - - 100 - - 200 - - 4094 diff --git a/schema/interface.ssid.radius.local.yml b/schema/interface.ssid.radius.local.yml deleted file mode 100644 index 8bfb7de..0000000 --- a/schema/interface.ssid.radius.local.yml +++ /dev/null @@ -1,16 +0,0 @@ -description: - Describe the properties of the local Radius server inside hostapd. -type: object -properties: - server-identity: - description: - EAP methods that provide mechanism for authenticated server identity delivery - use this value. - type: string - default: uCentral - users: - description: - Specifies a collection of local EAP user/psk/vid triplets. - type: array - items: - $ref: "https://ucentral.io/schema/v1/interface/ssid/radius/local-user/" diff --git a/schema/interface.ssid.radius.server.yml b/schema/interface.ssid.radius.server.yml deleted file mode 100644 index 876df6f..0000000 --- a/schema/interface.ssid.radius.server.yml +++ /dev/null @@ -1,133 +0,0 @@ -description: - Describe the properties of a Radius server. -type: object -properties: - host: - description: - The URI of our Radius server. - type: string - format: uc-host - examples: - - 192.168.1.10 - port: - description: - The network port of our Radius server. - type: integer - maximum: 65535 - minimum: 1024 - examples: - - 1812 - secret: - description: - The shared Radius authentication secret. - type: string - examples: - - secret - secondary: - description: - Definition of the secondary/failsafe radius server. - type: object - properties: - host: - description: - The URI of our Radius server. - type: string - format: uc-host - examples: - - 192.168.1.10 - port: - description: - The network port of our Radius server. - type: integer - maximum: 65535 - minimum: 1024 - examples: - - 1812 - secret: - description: - The shared Radius authentication secret. - type: string - examples: - - secret - request-attribute: - description: - The additional Access-Request attributes that gets sent to the server. - type: array - items: - anyOf: - - type: object - properties: - vendor-id: - type: integer - description: - The ID of the vendor specific RADIUS attribute - maximum: 65535 - minimum: 1 - vendor-attributes: - type: array - items: - type: object - description: - The numeric RADIUS attribute value - properties: - id: - type: integer - description: - The ID of the vendor specific RADIUS attribute - maximum: 255 - minimum: 1 - value: - type: string - description: - The vendor specific RADIUS attribute value. This needs to be a hexadecimal string. - - type: object - properties: - id: - type: integer - description: - The ID of the RADIUS attribute - maximum: 255 - minimum: 1 - value: - type: integer - description: - The numeric RADIUS attribute value - maximum: 4294967295 - minimum: 0 - examples: - - id: 27 - value: 900 - - id: 56 - value: 1004 - - type: object - properties: - id: - type: integer - description: - The ID of the RADIUS attribute - maximum: 255 - minimum: 1 - value: - type: string - description: - The RADIUS attribute value string - examples: - - id: 32 - value: My NAS ID - - id: 126 - value: Example Operator - - type: object - properties: - id: - type: integer - description: - The ID of the RADIUS attribute - maximum: 255 - minimum: 1 - hex-value: - type: string - description: - The RADIUS attribute value string - examples: - - id: 32 - value: 0a0b0c0d diff --git a/schema/interface.ssid.radius.yml b/schema/interface.ssid.radius.yml deleted file mode 100644 index 93eb5db..0000000 --- a/schema/interface.ssid.radius.yml +++ /dev/null @@ -1,67 +0,0 @@ -description: - When using EAP encryption we need to provide the required information - allowing us to connect to the AAA servers. -type: object -properties: - nas-identifier: - description: - NAS-Identifier string for RADIUS messages. When used, this should be unique - to the NAS within the scope of the RADIUS server. - type: string - chargeable-user-id: - description: - This will enable support for Chargeable-User-Identity (RFC 4372). - type: boolean - default: false - local: - $ref: "https://ucentral.io/schema/v1/interface/ssid/radius/local/" - dynamic-authorization: - description: - Dynamic Authorization Extensions (DAE) is an extension to Radius. - type: object - properties: - host: - description: - The IP of the DAE client. - type: string - format: uc-ip - examples: - - 192.168.1.10 - port: - description: - The network port that the DAE client can connet on. - type: integer - maximum: 65535 - minimum: 1024 - examples: - - 1812 - secret: - description: - The shared DAE authentication secret. - type: string - examples: - - secret - authentication: - allOf: - - $ref: "https://ucentral.io/schema/v1/interface/ssid/radius/server/" - - type: object - properties: - mac-filter: - description: - Should the radius server be used for MAC address ACL. - type: boolean - default: false - accounting: - allOf: - - $ref: "https://ucentral.io/schema/v1/interface/ssid/radius/server/" - - type: object - properties: - interval: - description: - The interim accounting update interval. This value is defined in seconds. - type: integer - maximum: 600 - minimum: 60 - default: 60 - health: - $ref: "https://ucentral.io/schema/v1/interface/ssid/radius/health/" diff --git a/schema/interface.ssid.rate-limit.yml b/schema/interface.ssid.rate-limit.yml deleted file mode 100644 index d34dfe9..0000000 --- a/schema/interface.ssid.rate-limit.yml +++ /dev/null @@ -1,14 +0,0 @@ -description: - The UE rate-limiting configuration of this BSS. -type: object -properties: - ingress-rate: - description: - The ingress rate to which hosts will be shaped. Values are in Mbps - type: integer - default: 0 - egress-rate: - description: - The egress rate to which hosts will be shaped. Values are in Mbps - type: integer - default: 0 diff --git a/schema/interface.ssid.roaming.yml b/schema/interface.ssid.roaming.yml deleted file mode 100644 index f274720..0000000 --- a/schema/interface.ssid.roaming.yml +++ /dev/null @@ -1,41 +0,0 @@ -description: - Enable 802.11r Fast Roaming for this BSS. -type: object -properties: - message-exchange: - description: - Shall the pre authenticated message exchange happen over the air or - distribution system. - type: string - enum: - - air - - ds - default: ds - generate-psk: - description: - Whether to generate FT response locally for PSK networks. This avoids - use of PMK-R1 push/pull from other APs with FT-PSK networks. - type: boolean - default: false - domain-identifier: - description: - Mobility Domain identifier (dot11FTMobilityDomainID, MDID). - type: string - maxLength: 4 - minLength: 4 - examples: - - abcd - pmk-r0-key-holder: - description: - The pairwise master key R0. This is unique to the mobility domain - and is required for fast roaming over the air. If the field is left - empty a deterministic key is generated. - type: string - example: 14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff - pmk-r1-key-holder: - description: - The pairwise master key R1. This is unique to the mobility domain - and is required for fast roaming over the air. If the field is left - empty a deterministic key is generated. - type: string - example: 14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff diff --git a/schema/interface.ssid.rrm.yml b/schema/interface.ssid.rrm.yml deleted file mode 100644 index ef982d6..0000000 --- a/schema/interface.ssid.rrm.yml +++ /dev/null @@ -1,32 +0,0 @@ -description: - Enable 802.11k Radio Resource Management (RRM) for this BSS. -type: object -properties: - neighbor-reporting: - description: - Enable neighbor report via radio measurements (802.11k). - type: boolean - default: false - reduced-neighbor-reporting: - description: - Enable reduced neighbor reports. - type: boolean - default: false - lci: - description: - The content of a LCI measurement subelement - type: string - civic-location: - description: - The content of a location civic measurement subelement - type: string - ftm-responder: - description: - Publish fine timing measurement (FTM) responder functionality on this BSS. - type: boolean - default: false - stationary-ap: - description: - Stationary AP config indicates that the AP doesn't move. - type: boolean - default: false diff --git a/schema/interface.ssid.yml b/schema/interface.ssid.yml deleted file mode 100644 index 04f35b8..0000000 --- a/schema/interface.ssid.yml +++ /dev/null @@ -1,181 +0,0 @@ -description: - A device has certain properties that describe its identity and location. - These properties are described inside this object. -type: object -properties: - purpose: - description: - An SSID can have a special purpose such as the hidden on-boarding BSS. - All purposes other than "user-defined" are static pre-defined configurations. - type: string - enum: - - user-defined - - onboarding-ap - - onboarding-sta - default: user-defined - name: - description: - The broadcasted SSID of the wireless network and for for managed mode - the SSID of the network you’re connecting to - type: string - maxLength: 32 - minLength: 1 - wifi-bands: - description: - The band that the SSID should be broadcasted on. The configuration layer - will use the first matching band. - type: array - items: - type: string - enum: - - 2G - - 5G - - 5G-lower - - 5G-upper - - 6G - bss-mode: - description: - Selects the operation mode of the wireless network interface controller. - type: string - enum: - - ap - - sta - - mesh - - wds-ap - - wds-sta - - wds-repeater - default: ap - bssid: - description: - Override the BSSID of the network, only applicable in adhoc or sta mode. - type: string - format: uc-mac - hidden-ssid: - description: - Disables the broadcasting of beacon frames if set to 1 and,in doing so, - hides the ESSID. - type: boolean - isolate-clients: - description: - Isolates wireless clients from each other on this BSS. - type: boolean - strict-forwarding: - description: - Isolate the BSS from all other members on the bridge apart from the first wired port. - type: boolean - default: false - power-save: - description: - Unscheduled Automatic Power Save Delivery. - type: boolean - rts-threshold: - description: - Set the RTS/CTS threshold of the BSS. - type: integer - maximum: 65535 - minimum: 1 - broadcast-time: - description: - This option will make the unit braodcast the time inside its beacons. - type: boolean - unicast-conversion: - description: - Convert multicast traffic to unicast on this BSS. - type: boolean - default: true - services: - description: - The services that shall be offered on this logical interface. These are - just strings such as "wifi-steering" - type: array - items: - type: string - examples: - - wifi-steering - dtim-period: - description: - Set the DTIM (delivery traffic information message) period. There will - be one DTIM per this many beacon frames. This may be set between 1 and - 255. This option only has an effect on ap wifi-ifaces. - type: integer - default: 2 - maximum: 255 - minimum: 1 - maximum-clients: - description: - Set the maximum number of clients that may connect to this VAP. - type: integer - example: 64 - proxy-arp: - description: - Proxy ARP is the technique in which the host router, answers ARP requests - intended for another machine. - type: boolean - default: true - disassoc-low-ack: - decription: - Disassociate stations based on excessive transmission failures or other - indications of connection loss. - type: boolean - default: false - vendor-elements: - decription: - This option allows embedding custom vendor specific IEs inside the beacons of - a BSS in AP mode. - type: string - tip-information-element: - decription: - The device will broadcast the TIP vendor IE inside its beacons if this option is enabled. - type: boolean - default: true - fils-discovery-interval: - description: - The maximum interval for FILS discovery announcement frames. This is a condensed - beacon used in 6GHz channels for passive BSS discovery. - type: integer - default: 20 - maximum: 20 - encryption: - $ref: "https://ucentral.io/schema/v1/interface/ssid/encryption/" - multi-psk: - type: array - items: - $ref: "https://ucentral.io/schema/v1/interface/ssid/multi-psk/" - rrm: - $ref: "https://ucentral.io/schema/v1/interface/ssid/rrm/" - rate-limit: - $ref: "https://ucentral.io/schema/v1/interface/ssid/rate-limit/" - roaming: - anyOf: - - $ref: "https://ucentral.io/schema/v1/interface/ssid/roaming/" - - description: - Enable 802.11r Fast Roaming for this BSS. This will enable "auto" mode - which will work for most scenarios. - type: boolean - radius: - $ref: "https://ucentral.io/schema/v1/interface/ssid/radius/" - certificates: - $ref: "https://ucentral.io/schema/v1/interface/ssid/certificates/" - pass-point: - $ref: "https://ucentral.io/schema/v1/interface/ssid/pass-point/" - quality-thresholds: - $ref: "https://ucentral.io/schema/v1/interface/ssid/quality-thresholds/" - access-control-list: - $ref: "https://ucentral.io/schema/v1/interface/ssid/acl/" - captive: - $ref: 'https://ucentral.io/schema/v1/service/captive/' - hostapd-bss-raw: - description: - This array allows passing raw hostapd.conf lines. - type: array - items: - type: string - examples: - - 'ap_table_expiration_time=3600' - - 'device_type=6-0050F204-1' - - 'ieee80211h=1' - - 'rssi_ignore_probe_request=-75' - - 'time_zone=EST5' - - 'uuid=12345678-9abc-def0-1234-56789abcdef0' - - 'venue_url=1:http://www.example.com/info-eng' - - 'wpa_deny_ptk0_rekey=0' diff --git a/schema/interface.yml b/schema/interface.yml index dea1e4c..d3250eb 100644 --- a/schema/interface.yml +++ b/schema/interface.yml @@ -74,9 +74,5 @@ properties: $ref: "https://ucentral.io/schema/v1/interface/dhcp-snoop-port/" broad-band: $ref: "https://ucentral.io/schema/v1/interface/broad-band/" - ssids: - type: array - items: - $ref: "https://ucentral.io/schema/v1/interface/ssid/" tunnel: $ref: "https://ucentral.io/schema/v1/interface/tunnel/" diff --git a/schema/metrics.realtime.yml b/schema/metrics.realtime.yml index 82ff4f3..ab3f7ce 100644 --- a/schema/metrics.realtime.yml +++ b/schema/metrics.realtime.yml @@ -19,9 +19,6 @@ properties: - client.join - client.leave - client.key-mismatch - - wifi - - wifi.start - - wifi.stop - wired - wired.carrier-up - wired.carrier-down diff --git a/schema/metrics.statistics.yml b/schema/metrics.statistics.yml index 698080d..e10e2eb 100644 --- a/schema/metrics.statistics.yml +++ b/schema/metrics.statistics.yml @@ -14,10 +14,8 @@ properties: items: type: string enum: - - ssids - lldp - clients - - tid-stats wired-clients-max-num: description: Configure maximum number of FDB entries device's allowed to report. diff --git a/schema/metrics.telemetry.yml b/schema/metrics.telemetry.yml index 0f86038..0f19365 100644 --- a/schema/metrics.telemetry.yml +++ b/schema/metrics.telemetry.yml @@ -23,9 +23,6 @@ properties: - client.join - client.leave - client.key-mismatch - - wifi - - wifi.start - - wifi.stop - wired - wired.carrier-up - wired.carrier-down diff --git a/schema/metrics.wifi-scan.yml b/schema/metrics.wifi-scan.yml deleted file mode 100644 index 14ff04e..0000000 --- a/schema/metrics.wifi-scan.yml +++ /dev/null @@ -1,16 +0,0 @@ -description: - Define the behaviour of the periodic wifi scanning interface. -type: object -properties: - interval: - description: - The periodicity at which the scan shall be performed. - type: integer - verbose: - description: - Add capabilities, v/ht_oper, ... to the resulting scan info. - type: boolean - information-elements: - description: - Add all IEs to the resulting scan info. - type: boolean diff --git a/schema/metrics.yml b/schema/metrics.yml index 06c2dc6..affde06 100644 --- a/schema/metrics.yml +++ b/schema/metrics.yml @@ -7,12 +7,8 @@ properties: $ref: "https://ucentral.io/schema/v1/metrics/statistics/" health: $ref: "https://ucentral.io/schema/v1/metrics/health/" - wifi-frames: - $ref: "https://ucentral.io/schema/v1/metrics/wifi-frames/" dhcp-snooping: $ref: "https://ucentral.io/schema/v1/metrics/dhcp-snooping/" - wifi-scan: - $ref: "https://ucentral.io/schema/v1/metrics/wifi-scan/" telemetry: $ref: "https://ucentral.io/schema/v1/metrics/telemetry/" realtime: diff --git a/schema/radio.he.yml b/schema/radio.he.yml deleted file mode 100644 index ab1b73e..0000000 --- a/schema/radio.he.yml +++ /dev/null @@ -1,20 +0,0 @@ -description: - This section describes the HE specific configuration options of the BSS. -type: object -properties: - multiple-bssid: - description: - Enabling this option will make the PHY broadcast its BSSs using the multiple BSSID beacon IE. - type: boolean - default: false - ema: - description: - Enableing this option will make the PHY broadcast its multiple BSSID beacons using EMA. - type: boolean - default: false - bss-color: - description: - This enables BSS Coloring on the PHY. setting it to 0 disables the feature 1-63 sets the color - and 64 will make hostapd pick a random color. - type: integer - default: 64 diff --git a/schema/radio.rates.yml b/schema/radio.rates.yml deleted file mode 100644 index 0f5d979..0000000 --- a/schema/radio.rates.yml +++ /dev/null @@ -1,42 +0,0 @@ -description: - The rate configuration of this BSS. -type: object -properties: - beacon: - description: - The beacon rate that shall be used by the BSS. Values are in Mbps. - type: integer - default: 6000 - enum: - - 0 - - 1000 - - 2000 - - 5500 - - 6000 - - 9000 - - 11000 - - 12000 - - 18000 - - 24000 - - 36000 - - 48000 - - 54000 - multicast: - description: - The multicast rate that shall be used by the BSS. Values are in Mbps. - type: integer - default: 24000 - enum: - - 0 - - 1000 - - 2000 - - 5500 - - 6000 - - 9000 - - 11000 - - 12000 - - 18000 - - 24000 - - 36000 - - 48000 - - 54000 diff --git a/schema/radio.yml b/schema/radio.yml deleted file mode 100644 index cafc9b1..0000000 --- a/schema/radio.yml +++ /dev/null @@ -1,153 +0,0 @@ -description: - Describe a physical radio on the AP. A radio is be parent to several VAPs. - They all share the same physical properties. -type: object -properties: - band: - description: - Specifies the wireless band to configure the radio for. Available radio device - phys on the target system are matched by the wireless band given here. - If multiple radio phys support the same band, the settings specified here will - be applied to all of them. - type: string - enum: - - 2G - - 5G - - 5G-lower - - 5G-upper - - 6G - bandwidth: - description: - Specifies a narrow channel width in MHz, possible values are 5, 10, 20. - type: integer - enum: - - 5 - - 10 - - 20 - channel: - description: - Specifies the wireless channel to use. A value of 'auto' starts the ACS - algorithm. - oneOf: - - type: integer - maximum: 196 - minimum: 1 - - type: string - const: auto - valid-channels: - description: - Pass a list of valid-channels that can be used during ACS. - type: array - items: - type: integer - maximum: 196 - minimum: 1 - country: - description: - Specifies the country code, affects the available channels and - transmission powers. - type: string - maxLength: 2 - minLength: 2 - examples: - - US - allow-dfs: - description: - This property defines whether a radio may use DFS channels. - type: boolean - default: true - channel-mode: - description: - Define the ideal channel mode that the radio shall use. This can be 802.11n, 802.11ac - or 802.11ax. This is just a hint for the AP. If the requested value is not supported - then the AP will use the highest common denominator. - type: string - enum: - - HT - - VHT - - HE - - EHT - default: HE - channel-width: - description: - The channel width that the radio shall use. This is just a hint for the AP. If the - requested value is not supported then the AP will use the highest common denominator. - type: integer - enum: - - 20 - - 40 - - 80 - - 160 - - 320 - - 8080 - default: 80 - require-mode: - description: - Stations that do no fulfill these HT modes will be rejected. - type: string - enum: - - HT - - VHT - - HE - mimo: - description: - This option allows configuring the antenna pairs that shall be used. - This is just a hint for the AP. If the requested value is not supported - then the AP will use the highest common denominator. - type: string - enum: - - 1x1 - - 2x2 - - 3x3 - - 4x4 - - 5x5 - - 6x6 - - 7x7 - - 8x8 - tx-power: - description: - This option specifies the transmission power in dBm - type: integer - maximum: 30 - minimum: 0 - legacy-rates: - description: - Allow legacy 802.11b data rates. - type: boolean - default: false - beacon-interval: - description: - Beacon interval in kus (1.024 ms). - type: integer - default: 100 - maximum: 65535 - minimum: 15 - maximum-clients: - description: - Set the maximum number of clients that may connect to this radio. This - value is accumulative for all attached VAP interfaces. - type: integer - example: 64 - maximum-clients-ignore-probe: - description: - Ignore probe requests if maximum-clients was reached. - type: boolean - rates: - $ref: "https://ucentral.io/schema/v1/radio/rates/" - he-settings: - $ref: "https://ucentral.io/schema/v1/radio/he/" - hostapd-iface-raw: - description: - This array allows passing raw hostapd.conf lines. - type: array - items: - type: string - examples: - - 'ap_table_expiration_time=3600' - - 'device_type=6-0050F204-1' - - 'ieee80211h=1' - - 'rssi_ignore_probe_request=-75' - - 'time_zone=EST5' - - 'uuid=12345678-9abc-def0-1234-56789abcdef0' - - 'venue_url=1:http://www.example.com/info-eng' - - 'wpa_deny_ptk0_rekey=0' diff --git a/schema/service.admin-ui.yml b/schema/service.admin-ui.yml deleted file mode 100644 index 993cec2..0000000 --- a/schema/service.admin-ui.yml +++ /dev/null @@ -1,32 +0,0 @@ -type: object -properties: - wifi-ssid: - description: - The name of the admin ssid. - type: string - default: Maverick - maxLength: 32 - minLength: 1 - wifi-key: - description: - The Pre Shared Key (PSK) that is used for encryption on the BSS. - type: string - maxLength: 63 - minLength: 8 - wifi-bands: - description: - The band that the SSID should be broadcasted on. The configuration layer - will use the first matching band. - type: array - items: - type: string - enum: - - 2G - - 5G - - 5G-lower - - 5G-upper - - 6G - offline-trigger: - description: - The admin-ui will be spawned when this offline threshold was exceeded. - type: number diff --git a/schema/service.airtime-fairness.yml b/schema/service.airtime-fairness.yml deleted file mode 100644 index e05ad3f..0000000 --- a/schema/service.airtime-fairness.yml +++ /dev/null @@ -1,46 +0,0 @@ -description: - This section describes the vlan behaviour of a logical network interface. -type: object -properties: - voice-weight: - description: - Voice traffic does not get aggregated. As voice and video are both considered - priotity voice is considered to have a heavier weight when calculation priority - average. - type: number - default: 4 - packet-threshold: - description: - The amount of packets that need to be received for a specific type of traffic - before new averageg is calculated. - type: number - default: 100 - bulk-threshold: - description: - This option is a percentual value. If more the X% of the traffic is bulk, we assign - the bulk weight. - type: number - default: 50 - priority-threshold: - description: - This option is a percentual value. If more the X% of the traffic is priority, we assign - the priority weight. Priority classification will take precedence over bulk. - type: number - default: 30 - weight-normal: - description: - The default ATF weight that UEs get assigned. - type: number - default: 256 - weight-priority: - description: - The default ATF weight that UEs get assigned when priority traffic above the configured - percentage is detected. - type: number - default: 394 - weight-bulk: - description: - The default ATF weight that UEs get assigned when bulk traffic above the configured - percentage is detected. - type: number - default: 128 diff --git a/schema/service.captive.click.yml b/schema/service.captive.click.yml deleted file mode 100644 index 876d0e0..0000000 --- a/schema/service.captive.click.yml +++ /dev/null @@ -1,10 +0,0 @@ -description: - This section can be used to setup a captive portal on the AP with a - click-to-continue splash page. -type: object -properties: - auth-mode: - description: - This field must be set to 'click-to-continue' - type: string - const: click-to-continue diff --git a/schema/service.captive.credentials.yml b/schema/service.captive.credentials.yml deleted file mode 100644 index 56ebcec..0000000 --- a/schema/service.captive.credentials.yml +++ /dev/null @@ -1,21 +0,0 @@ -description: - This section can be used to setup a captive portal on the AP with a - credentials splash page. -type: object -properties: - auth-mode: - description: - This field must be set to 'credentials' - type: string - const: credentials - credentials: - description: - The list of local username/password pairs that can be used to login. - type: array - items: - type: object - properties: - username: - type: string - password: - type: string diff --git a/schema/service.captive.radius.yml b/schema/service.captive.radius.yml deleted file mode 100644 index 259382a..0000000 --- a/schema/service.captive.radius.yml +++ /dev/null @@ -1,55 +0,0 @@ -description: - This section can be used to setup a captive portal on the AP with a - click-to-continue splash page. -type: object -properties: - auth-mode: - description: - This field must be set to 'radius' - type: string - const: radius - auth-server: - description: - The URI of our Radius Authentication server. - type: string - format: uc-host - examples: - - 192.168.1.10 - auth-port: - description: - The network port of our Radius Authentication server. - type: integer - maximum: 65535 - minimum: 1024 - default: 1812 - auth-secret: - description: - The shared Radius authentication Authentication secret. - type: string - examples: - - secret - acct-server: - description: - The URI of our Radius Authentication server. - type: string - format: uc-host - examples: - - 192.168.1.10 - acct-port: - description: - The network port of our Radius Authentication server. - type: integer - maximum: 65535 - minimum: 1024 - default: 1812 - acct-secret: - description: - The shared Radius authentication Authentication secret. - type: string - examples: - - secret - acct-interval: - description: - The timeout used for interim messages. - type: integer - default: 600 diff --git a/schema/service.captive.uam.yml b/schema/service.captive.uam.yml deleted file mode 100644 index 00f809d..0000000 --- a/schema/service.captive.uam.yml +++ /dev/null @@ -1,112 +0,0 @@ -description: - This section can be used to setup a captive portal on the AP with a - remote UAM server. -type: object -properties: - auth-mode: - description: - This field must be set to 'uam' - type: string - const: uam - uam-port: - description: - The local UAM port. - type: integer - maximum: 65535 - minimum: 1024 - default: 3990 - uam-secret: - description: - The pre-shared UAM secret. - type: string - uam-server: - description: - The fqdn of the UAM server. - type: string - nasid: - description: - The NASID that gets sent to the UAM server. - type: string - nasmac: - description: - The NAS MAC that gets send to the UAM server. The devices serial is used - if this value is not provided. - type: string - auth-server: - description: - The URI of our Radius Authentication server. - type: string - format: uc-host - examples: - - 192.168.1.10 - auth-port: - description: - The network port of our Radius Authentication server. - type: integer - maximum: 65535 - minimum: 1024 - default: 1812 - auth-secret: - description: - The shared Radius authentication Authentication secret. - type: string - examples: - - secret - acct-server: - description: - The URI of our Radius Authentication server. - type: string - format: uc-host - examples: - - 192.168.1.10 - acct-port: - description: - The network port of our Radius Authentication server. - type: integer - maximum: 65535 - minimum: 1024 - default: 1812 - acct-secret: - description: - The shared Radius authentication Authentication secret. - type: string - examples: - - secret - acct-interval: - description: - The timeout used for interim messages. - type: integer - default: 600 - ssid: - description: - The name of the SSID that shall be sent as part of the UAM redirect. - type: string - mac-format: - description: - Defines the format used to send the MAC address inside AAA frames. - type: string - enum: - - aabbccddeeff - - aa-bb-cc-dd-ee-ff - - aa:bb:cc:dd:ee:ff - - AABBCCDDEEFF - - AA:BB:CC:DD:EE:FF - - AA-BB-CC-DD-EE-FF - final-redirect-url: - description: - Define the behaviour off the final redirect. Default will honour "userurl" and fallback to "local". - Alternatively it is possible to force a redirect to the "UAM" or "local" URL. - type: string - enum: - - default - - uam - mac-auth: - description: - Try to authenticate new clients using macauth. - type: boolean - default: default - radius-gw-proxy: - description: - Tunnel all radius traffic via the radius-gw-proxy. - type: boolean - default: false diff --git a/schema/service.captive.yml b/schema/service.captive.yml deleted file mode 100644 index b10d682..0000000 --- a/schema/service.captive.yml +++ /dev/null @@ -1,35 +0,0 @@ -allOf: - - oneOf: - - $ref: 'https://ucentral.io/schema/v1/service/captive/click/' - - $ref: 'https://ucentral.io/schema/v1/service/captive/radius/' - - $ref: 'https://ucentral.io/schema/v1/service/captive/credentials/' - - $ref: 'https://ucentral.io/schema/v1/service/captive/uam/' - - type: object - properties: - walled-garden-fqdn: - description: - The list of FQDNs that a non-authenticated client is allowed to connect to. - type: array - items: - type: string - walled-garden-ipaddr: - description: - The list of IP addresses that a non-authenticated client is allowed to connect to. - type: array - items: - type: string - format: uc-ip - web-root: - description: - A base64 encoded TAR file with the custom web-root. - type: string - format: uc-base64 - idle-timeout: - description: - How long may a client be idle before getting removed. - type: integer - default: 600 - session-timeout: - description: - How long may a client be idle before getting removed. - type: integer diff --git a/schema/service.dhcp-relay.yml b/schema/service.dhcp-relay.yml deleted file mode 100644 index 35abc49..0000000 --- a/schema/service.dhcp-relay.yml +++ /dev/null @@ -1,47 +0,0 @@ -description: - Define the vlans on which the dhcp shall be relayed. -type: object -properties: - select-ports: - description: - The list of physical network devices that shall be used to fwd the - DHCP frames. - type: array - items: - type: string - vlans: - description: - The list of all vlans - type: array - items: - type: object - properties: - vlan: - description: - The vlan id. - type: number - relay-server: - description: - The unicast target DHCP pool server where frames get relayed to. - type: string - format: uc-ip - circuit-id-format: - description: - This option selects what info shall be contained within a relayed - frame's circuit ID. - type: string - enum: - - vlan-id - - ap-mac - - ssid - default: vlan-id - remote-id-format: - description: - This option selects what info shall be contained within a relayed - frame's remote ID. - type: string - enum: - - vlan-id - - ap-mac - - ssid - default: ap-mac diff --git a/schema/service.facebook-wifi.yml b/schema/service.facebook-wifi.yml deleted file mode 100644 index 0d0966e..0000000 --- a/schema/service.facebook-wifi.yml +++ /dev/null @@ -1,16 +0,0 @@ -description: - This section describes the FaceBook Wifi behaviour of the unit. -type: object -properties: - vendor-id: - description: - The Vendors ID. - type: string - gateway-id: - description: - The Gateways ID. - type: string - secret: - description: - The Device specific secret - type: string diff --git a/schema/service.ieee8021x.yml b/schema/service.ieee8021x.yml deleted file mode 100644 index 7baf490..0000000 --- a/schema/service.ieee8021x.yml +++ /dev/null @@ -1,98 +0,0 @@ -description: - This section allows enabling wired ieee802.1X -type: object -properties: - mode: - description: - This field must be set to 'radius or user' - type: string - enum: - - radius - - user - port-filter: - description: - Specifies a list of ports that we want to filter. - type: array - items: - type: string - examples: - - LAN1: - users: - description: - Specifies a collection of local EAP user/psk/vid triplets. - type: array - items: - $ref: "https://ucentral.io/schema/v1/interface/ssid/radius/local-user/" - radius: - description: - Specifies the information about radius account authentication and accounting - type: object - properties: - nas-identifier: - description: - NAS-Identifier string for RADIUS messages. When used, this should be unique - to the NAS within the scope of the RADIUS server. - type: string - auth-server-addr: - description: - The URI of our Radius server. - type: string - format: uc-host - examples: - - 192.168.1.10 - auth-server-port: - description: - The network port of our Radius server. - type: integer - maximum: 65535 - minimum: 1024 - examples: - - 1812 - auth-server-secret: - description: - The shared Radius authentication secret. - type: string - examples: - - secret - acct-server-addr: - description: - The URI of our Radius server. - type: string - format: uc-host - examples: - - 192.168.1.10 - acct-server-port: - description: - The network port of our Radius server. - type: integer - maximum: 65535 - minimum: 1024 - examples: - - 1813 - acct-server-secret: - description: - The shared Radius accounting secret. - type: string - examples: - - secret - coa-server-addr: - description: - The URI of our Radius server. - type: string - format: uc-host - examples: - - 192.168.1.10 - coa-server-port: - description: - The network port of our Radius server. - type: integer - maximum: 65535 - minimum: 1024 - examples: - - 1814 - coa-server-secret: - description: - The shared Radius accounting secret. - type: string - examples: - - secret diff --git a/schema/service.online-check.yml b/schema/service.online-check.yml index 36c4764..0c0a2b9 100644 --- a/schema/service.online-check.yml +++ b/schema/service.online-check.yml @@ -41,5 +41,4 @@ properties: items: type: string enum: - - wifi - leds diff --git a/schema/service.rrm.yml b/schema/service.rrm.yml deleted file mode 100644 index 72899a7..0000000 --- a/schema/service.rrm.yml +++ /dev/null @@ -1,13 +0,0 @@ -description: - This section describes the band steering behaviour of the unit. -type: object -properties: - beacon-request-assoc: - description: - Tell stations to send a beacon request scan when they associate. - type: boolean - default: true - station-stats-interval: - description: - Periodically send station statistics every N seconds. - type: number diff --git a/schema/service.wifi-steering.yml b/schema/service.wifi-steering.yml deleted file mode 100644 index d716075..0000000 --- a/schema/service.wifi-steering.yml +++ /dev/null @@ -1,53 +0,0 @@ -description: - This section describes the band steering behaviour of the unit. -type: object -properties: - mode: - description: - Wifi sterring can happen either locally or via the backend gateway. - type: string - enum: - - local - - none - examples: - - local - assoc-steering: - description: - Allow rejecting assoc requests for steering purposes. - type: boolean - default: false - required-snr: - description: - Minimum required signal level (dBm) for connected clients. If the client - will be kicked if the SNR drops below this value. - type: integer - default: 0 - required-probe-snr: - description: - Minimum required signal level (dBm) to allow connections. If the SNR is - below this value, probe requests will not be replied to. - type: integer - default: 0 - required-roam-snr: - description: - Minimum required signal level (dBm) before an attempt is made to roam the - client to a better AP. - type: integer - default: 0 - load-kick-threshold: - description: - Minimum channel load (%) before kicking clients - type: integer - default: 0 - auto-channel: - description: - Allow multiple instances of the steering daemon to coordinate the best channel - usage amongst eachother. - type: boolean - default: false - ipv6: - description: - Use IPv6 multicast to communicate with remote usteerd instances, rather - than IPv4 broadcast. - type: boolean - default: false diff --git a/schema/service.yml b/schema/service.yml index 08ba30c..61ab1b2 100644 --- a/schema/service.yml +++ b/schema/service.yml @@ -1,6 +1,6 @@ description: This section describes all of the services that may be present on the AP. - Each service is then referenced via its name inside an interface, ssid, ... + Each service is then referenced via its name inside an interface, ... type: object properties: lldp: @@ -19,34 +19,18 @@ properties: $ref: "https://ucentral.io/schema/v1/service/http/" igmp: $ref: "https://ucentral.io/schema/v1/service/igmp/" - ieee8021x: - $ref: "https://ucentral.io/schema/v1/service/ieee8021x/" radius-proxy: $ref: "https://ucentral.io/schema/v1/service/radius-proxy/" online-check: $ref: "https://ucentral.io/schema/v1/service/online-check/" data-plane: $ref: "https://ucentral.io/schema/v1/service/data-plane/" - wifi-steering: - $ref: "https://ucentral.io/schema/v1/service/wifi-steering/" quality-of-service: $ref: "https://ucentral.io/schema/v1/service/quality-of-service/" - facebook-wifi: - $ref: "https://ucentral.io/schema/v1/service/facebook-wifi/" - airtime-fairness: - $ref: "https://ucentral.io/schema/v1/service/airtime-fairness/" wireguard-overlay: $ref: 'https://ucentral.io/schema/v1/service/wireguard-overlay/' - captive: - $ref: 'https://ucentral.io/schema/v1/service/captive/' gps: $ref: 'https://ucentral.io/schema/v1/service/gps/' - dhcp-relay: - $ref: 'https://ucentral.io/schema/v1/service/dhcp-relay/' - admin-ui: - $ref: 'https://ucentral.io/schema/v1/service/admin-ui/' - rrm: - $ref: 'https://ucentral.io/schema/v1/service/rrm/' telnet: $ref: "https://ucentral.io/schema/v1/service/telnet/" https: diff --git a/schema/ucentral.yml b/schema/ucentral.yml index 47cb4ad..b842568 100644 --- a/schema/ucentral.yml +++ b/schema/ucentral.yml @@ -21,18 +21,12 @@ properties: $ref: "https://ucentral.io/schema/v1/unit/" globals: $ref: "https://ucentral.io/schema/v1/globals/" - definitions: - $ref: "https://ucentral.io/schema/v1/definitions/" ethernet: type: array items: $ref: "https://ucentral.io/schema/v1/ethernet/" switch: $ref: "https://ucentral.io/schema/v1/switch/" - radios: - type: array - items: - $ref: "https://ucentral.io/schema/v1/radio/" interfaces: type: array items: diff --git a/schemareader.uc b/schemareader.uc index 314f57c..37d767c 100644 --- a/schemareader.uc +++ b/schemareader.uc @@ -345,99 +345,6 @@ function instantiateUnit(location, value, errors) { return value; } -function instantiateGlobalsWirelessMultimediaClassSelector(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "CS0", "CS1", "CS2", "CS3", "CS4", "CS5", "CS6", "CS7", "AF11", "AF12", "AF13", "AF21", "AF22", "AF23", "AF31", "AF32", "AF33", "AF41", "AF42", "AF43", "DF", "EF", "VA", "LE" ])) - push(errors, [ location, "must be one of \"CS0\", \"CS1\", \"CS2\", \"CS3\", \"CS4\", \"CS5\", \"CS6\", \"CS7\", \"AF11\", \"AF12\", \"AF13\", \"AF21\", \"AF22\", \"AF23\", \"AF31\", \"AF32\", \"AF33\", \"AF41\", \"AF42\", \"AF43\", \"DF\", \"EF\", \"VA\" or \"LE\"" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; -} - -function instantiateGlobalsWirelessMultimediaTable(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - if (exists(value, "UP0")) { - obj.UP0 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP0", value["UP0"], errors); - } - - if (exists(value, "UP1")) { - obj.UP1 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP1", value["UP1"], errors); - } - - if (exists(value, "UP2")) { - obj.UP2 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP2", value["UP2"], errors); - } - - if (exists(value, "UP3")) { - obj.UP3 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP3", value["UP3"], errors); - } - - if (exists(value, "UP4")) { - obj.UP4 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP4", value["UP4"], errors); - } - - if (exists(value, "UP5")) { - obj.UP5 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP5", value["UP5"], errors); - } - - if (exists(value, "UP6")) { - obj.UP6 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP6", value["UP6"], errors); - } - - if (exists(value, "UP7")) { - obj.UP7 = instantiateGlobalsWirelessMultimediaClassSelector(location + "/UP7", value["UP7"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateGlobalsWirelessMultimediaProfile(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseProfile(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "enterprise", "rfc8325", "3gpp" ])) - push(errors, [ location, "must be one of \"enterprise\", \"rfc8325\" or \"3gpp\"" ]); - - return value; - } - - if (exists(value, "profile")) { - obj.profile = parseProfile(location + "/profile", value["profile"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateGlobals(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -476,66 +383,6 @@ function instantiateGlobals(location, value, errors) { obj.ipv6_network = parseIpv6Network(location + "/ipv6-network", value["ipv6-network"], errors); } - function parseWirelessMultimedia(location, value, errors) { - function parseVariant0(location, value, errors) { - value = instantiateGlobalsWirelessMultimediaTable(location, value, errors); - - return value; - } - - function parseVariant1(location, value, errors) { - value = instantiateGlobalsWirelessMultimediaProfile(location, value, errors); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success == 0) { - if (length(verrors)) - push(errors, [ location, "must match at least one of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; - } - - if (exists(value, "wireless-multimedia")) { - obj.wireless_multimedia = parseWirelessMultimedia(location + "/wireless-multimedia", value["wireless-multimedia"], errors); - } - function parseIpv4Blackhole(location, value, errors) { if (type(value) == "array") { function parseItem(location, value, errors) { @@ -657,108 +504,6 @@ function instantiateGlobals(location, value, errors) { return value; } -function instantiateInterfaceSsidEncryption(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseProto(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "none", "owe", "owe-transition", "psk", "psk2", "psk-mixed", "psk2-radius", "wpa", "wpa2", "wpa-mixed", "sae", "sae-mixed", "wpa3", "wpa3-192", "wpa3-mixed" ])) - push(errors, [ location, "must be one of \"none\", \"owe\", \"owe-transition\", \"psk\", \"psk2\", \"psk-mixed\", \"psk2-radius\", \"wpa\", \"wpa2\", \"wpa-mixed\", \"sae\", \"sae-mixed\", \"wpa3\", \"wpa3-192\" or \"wpa3-mixed\"" ]); - - return value; - } - - if (exists(value, "proto")) { - obj.proto = parseProto(location + "/proto", value["proto"], errors); - } - - function parseKey(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 63) - push(errors, [ location, "must be at most 63 characters long" ]); - - if (length(value) < 8) - push(errors, [ location, "must be at least 8 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "key")) { - obj.key = parseKey(location + "/key", value["key"], errors); - } - - function parseIeee80211w(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "disabled", "optional", "required" ])) - push(errors, [ location, "must be one of \"disabled\", \"optional\" or \"required\"" ]); - - return value; - } - - if (exists(value, "ieee80211w")) { - obj.ieee80211w = parseIeee80211w(location + "/ieee80211w", value["ieee80211w"], errors); - } - else { - obj.ieee80211w = "disabled"; - } - - function parseKeyCaching(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "key-caching")) { - obj.key_caching = parseKeyCaching(location + "/key-caching", value["key-caching"], errors); - } - else { - obj.key_caching = true; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateDefinitions(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseWirelessEncryption(location, value, errors) { - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - if (exists(value, "wireless-encryption")) { - obj.wireless_encryption = parseWirelessEncryption(location + "/wireless-encryption", value["wireless-encryption"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateEthernet(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -785,12 +530,23 @@ function instantiateEthernet(location, value, errors) { obj.select_ports = parseSelectPorts(location + "/select-ports", value["select-ports"], errors); } + function parseName(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "name")) { + obj.name = parseName(location + "/name", value["name"], errors); + } + function parseSpeed(location, value, errors) { if (type(value) != "int") push(errors, [ location, "must be of type integer" ]); - if (!(value in [ 10, 100, 1000, 2500, 5000, 10000, 25000, 100000 ])) - push(errors, [ location, "must be one of 10, 100, 1000, 2500, 5000, 10000, 25000 or 100000" ]); + if (!(value in [ 10, 100, 1000, 2500, 5000, 10000, 25000, 40000, 50000, 100000, 200000 ])) + push(errors, [ location, "must be one of 10, 100, 1000, 2500, 5000, 10000, 25000, 40000, 50000, 100000 or 200000" ]); return value; } @@ -1031,6 +787,28 @@ function instantiateEthernet(location, value, errors) { obj.unauthenticated_vlan = parseUnauthenticatedVlan(location + "/unauthenticated-vlan", value["unauthenticated-vlan"], errors); } + function parseMacAddressBypass(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "mac-address-bypass")) { + obj.mac_address_bypass = parseMacAddressBypass(location + "/mac-address-bypass", value["mac-address-bypass"], errors); + } + + function parseMacAddressBypassTimeoutMinutes(location, value, errors) { + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "mac-address-bypass-timeout-minutes")) { + obj.mac_address_bypass_timeout_minutes = parseMacAddressBypassTimeoutMinutes(location + "/mac-address-bypass-timeout-minutes", value["mac-address-bypass-timeout-minutes"], errors); + } + return obj; } @@ -1044,6 +822,26 @@ function instantiateEthernet(location, value, errors) { obj.ieee8021x = parseIeee8021x(location + "/ieee8021x", value["ieee8021x"], errors); } + function parseTrunkGroup(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 64) + push(errors, [ location, "must be lower than or equal to 64" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "trunk-group")) { + obj.trunk_group = parseTrunkGroup(location + "/trunk-group", value["trunk-group"], errors); + } + function parseLacpConfig(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -1215,24 +1013,450 @@ function instantiateEthernet(location, value, errors) { obj.lacp_config = parseLacpConfig(location + "/lacp-config", value["lacp-config"], errors); } - function parseTrunkGroup(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 64) - push(errors, [ location, "must be lower than or equal to 64" ]); + function parseLldpInterfaceConfig(location, value, errors) { + if (type(value) == "object") { + let obj = {}; - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); + function parseLldpAdminStatus(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + if (!(value in [ "rx", "tx", "rx-tx" ])) + push(errors, [ location, "must be one of \"rx\", \"tx\" or \"rx-tx\"" ]); + + return value; + } + + if (exists(value, "lldp-admin-status")) { + obj.lldp_admin_status = parseLldpAdminStatus(location + "/lldp-admin-status", value["lldp-admin-status"], errors); + } + + function parseLldpBasicTlvMgmtIpV4(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-basic-tlv-mgmt-ip-v4")) { + obj.lldp_basic_tlv_mgmt_ip_v4 = parseLldpBasicTlvMgmtIpV4(location + "/lldp-basic-tlv-mgmt-ip-v4", value["lldp-basic-tlv-mgmt-ip-v4"], errors); + } + else { + obj.lldp_basic_tlv_mgmt_ip_v4 = true; + } + + function parseLldpBasicTlvMgmtIpV6(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-basic-tlv-mgmt-ip-v6")) { + obj.lldp_basic_tlv_mgmt_ip_v6 = parseLldpBasicTlvMgmtIpV6(location + "/lldp-basic-tlv-mgmt-ip-v6", value["lldp-basic-tlv-mgmt-ip-v6"], errors); + } + else { + obj.lldp_basic_tlv_mgmt_ip_v6 = false; + } + + function parseLldpBasicTlvPortDescr(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-basic-tlv-port-descr")) { + obj.lldp_basic_tlv_port_descr = parseLldpBasicTlvPortDescr(location + "/lldp-basic-tlv-port-descr", value["lldp-basic-tlv-port-descr"], errors); + } + else { + obj.lldp_basic_tlv_port_descr = true; + } + + function parseLldpBasicTlvSysCapab(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-basic-tlv-sys-capab")) { + obj.lldp_basic_tlv_sys_capab = parseLldpBasicTlvSysCapab(location + "/lldp-basic-tlv-sys-capab", value["lldp-basic-tlv-sys-capab"], errors); + } + else { + obj.lldp_basic_tlv_sys_capab = true; + } + + function parseLldpBasicTlvSysDescr(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-basic-tlv-sys-descr")) { + obj.lldp_basic_tlv_sys_descr = parseLldpBasicTlvSysDescr(location + "/lldp-basic-tlv-sys-descr", value["lldp-basic-tlv-sys-descr"], errors); + } + else { + obj.lldp_basic_tlv_sys_descr = true; + } + + function parseLldpBasicTlvSysName(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-basic-tlv-sys-name")) { + obj.lldp_basic_tlv_sys_name = parseLldpBasicTlvSysName(location + "/lldp-basic-tlv-sys-name", value["lldp-basic-tlv-sys-name"], errors); + } + else { + obj.lldp_basic_tlv_sys_name = true; + } + + function parseLldpDot1TlvProtoIdent(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot1-tlv-proto-ident")) { + obj.lldp_dot1_tlv_proto_ident = parseLldpDot1TlvProtoIdent(location + "/lldp-dot1-tlv-proto-ident", value["lldp-dot1-tlv-proto-ident"], errors); + } + else { + obj.lldp_dot1_tlv_proto_ident = true; + } + + function parseLldpDot1TlvProtoVid(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot1-tlv-proto-vid")) { + obj.lldp_dot1_tlv_proto_vid = parseLldpDot1TlvProtoVid(location + "/lldp-dot1-tlv-proto-vid", value["lldp-dot1-tlv-proto-vid"], errors); + } + else { + obj.lldp_dot1_tlv_proto_vid = true; + } + + function parseLldpDot1TlvPvid(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot1-tlv-pvid")) { + obj.lldp_dot1_tlv_pvid = parseLldpDot1TlvPvid(location + "/lldp-dot1-tlv-pvid", value["lldp-dot1-tlv-pvid"], errors); + } + else { + obj.lldp_dot1_tlv_pvid = true; + } + + function parseLldpDot1TlvVlanName(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot1-tlv-vlan-name")) { + obj.lldp_dot1_tlv_vlan_name = parseLldpDot1TlvVlanName(location + "/lldp-dot1-tlv-vlan-name", value["lldp-dot1-tlv-vlan-name"], errors); + } + else { + obj.lldp_dot1_tlv_vlan_name = true; + } + + function parseLldpDot3TlvLinkAgg(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot3-tlv-link-agg")) { + obj.lldp_dot3_tlv_link_agg = parseLldpDot3TlvLinkAgg(location + "/lldp-dot3-tlv-link-agg", value["lldp-dot3-tlv-link-agg"], errors); + } + else { + obj.lldp_dot3_tlv_link_agg = true; + } + + function parseLldpDot3TlvMacPhy(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot3-tlv-mac-phy")) { + obj.lldp_dot3_tlv_mac_phy = parseLldpDot3TlvMacPhy(location + "/lldp-dot3-tlv-mac-phy", value["lldp-dot3-tlv-mac-phy"], errors); + } + else { + obj.lldp_dot3_tlv_mac_phy = true; + } + + function parseLldpDot3TlvMaxFrame(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot3-tlv-max-frame")) { + obj.lldp_dot3_tlv_max_frame = parseLldpDot3TlvMaxFrame(location + "/lldp-dot3-tlv-max-frame", value["lldp-dot3-tlv-max-frame"], errors); + } + else { + obj.lldp_dot3_tlv_max_frame = true; + } + + function parseLldpDot3TlvPoe(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-dot3-tlv-poe")) { + obj.lldp_dot3_tlv_poe = parseLldpDot3TlvPoe(location + "/lldp-dot3-tlv-poe", value["lldp-dot3-tlv-poe"], errors); + } + else { + obj.lldp_dot3_tlv_poe = true; + } + + function parseLldpMedLocationCivicAddr(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseLldpMedLocationCivicAddrAdminStatus(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-med-location-civic-addr-admin-status")) { + obj.lldp_med_location_civic_addr_admin_status = parseLldpMedLocationCivicAddrAdminStatus(location + "/lldp-med-location-civic-addr-admin-status", value["lldp-med-location-civic-addr-admin-status"], errors); + } + else { + obj.lldp_med_location_civic_addr_admin_status = false; + } + + function parseLldpMedLocationCivicCountryCode(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "lldp-med-location-civic-country-code")) { + obj.lldp_med_location_civic_country_code = parseLldpMedLocationCivicCountryCode(location + "/lldp-med-location-civic-country-code", value["lldp-med-location-civic-country-code"], errors); + } + + function parseLldpMedLocationCivicDeviceType(location, value, errors) { + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-med-location-civic-device-type")) { + obj.lldp_med_location_civic_device_type = parseLldpMedLocationCivicDeviceType(location + "/lldp-med-location-civic-device-type", value["lldp-med-location-civic-device-type"], errors); + } + + function parseLldpMedLocationCivicCa(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseLldpMedLocationCivicCaType(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 255) + push(errors, [ location, "must be lower than or equal to 255" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-med-location-civic-ca-type")) { + obj.lldp_med_location_civic_ca_type = parseLldpMedLocationCivicCaType(location + "/lldp-med-location-civic-ca-type", value["lldp-med-location-civic-ca-type"], errors); + } + + function parseLldpMedLocationCivicCaValue(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 32) + push(errors, [ location, "must be at most 32 characters long" ]); + + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "lldp-med-location-civic-ca-value")) { + obj.lldp_med_location_civic_ca_value = parseLldpMedLocationCivicCaValue(location + "/lldp-med-location-civic-ca-value", value["lldp-med-location-civic-ca-value"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "lldp-med-location-civic-ca")) { + obj.lldp_med_location_civic_ca = parseLldpMedLocationCivicCa(location + "/lldp-med-location-civic-ca", value["lldp-med-location-civic-ca"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "lldp-med-location-civic-addr")) { + obj.lldp_med_location_civic_addr = parseLldpMedLocationCivicAddr(location + "/lldp-med-location-civic-addr", value["lldp-med-location-civic-addr"], errors); + } + + function parseLldpMedNotification(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-med-notification")) { + obj.lldp_med_notification = parseLldpMedNotification(location + "/lldp-med-notification", value["lldp-med-notification"], errors); + } + else { + obj.lldp_med_notification = false; + } + + function parseLldpMedTlvExtPoe(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-med-tlv-ext-poe")) { + obj.lldp_med_tlv_ext_poe = parseLldpMedTlvExtPoe(location + "/lldp-med-tlv-ext-poe", value["lldp-med-tlv-ext-poe"], errors); + } + else { + obj.lldp_med_tlv_ext_poe = true; + } + + function parseLldpMedTlvInventory(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-med-tlv-inventory")) { + obj.lldp_med_tlv_inventory = parseLldpMedTlvInventory(location + "/lldp-med-tlv-inventory", value["lldp-med-tlv-inventory"], errors); + } + else { + obj.lldp_med_tlv_inventory = true; + } + + function parseLldpMedTlvLocation(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-med-tlv-location")) { + obj.lldp_med_tlv_location = parseLldpMedTlvLocation(location + "/lldp-med-tlv-location", value["lldp-med-tlv-location"], errors); + } + else { + obj.lldp_med_tlv_location = true; + } + + function parseLldpMedTlvMedCap(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-med-tlv-med-cap")) { + obj.lldp_med_tlv_med_cap = parseLldpMedTlvMedCap(location + "/lldp-med-tlv-med-cap", value["lldp-med-tlv-med-cap"], errors); + } + else { + obj.lldp_med_tlv_med_cap = true; + } + + function parseLldpMedTlvNetworkPolicy(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-med-tlv-network-policy")) { + obj.lldp_med_tlv_network_policy = parseLldpMedTlvNetworkPolicy(location + "/lldp-med-tlv-network-policy", value["lldp-med-tlv-network-policy"], errors); + } + else { + obj.lldp_med_tlv_network_policy = true; + } + + function parseLldpNotification(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-notification")) { + obj.lldp_notification = parseLldpNotification(location + "/lldp-notification", value["lldp-notification"], errors); + } + else { + obj.lldp_notification = false; + } + + return obj; } - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); return value; } - if (exists(value, "trunk-group")) { - obj.trunk_group = parseTrunkGroup(location + "/trunk-group", value["trunk-group"], errors); + if (exists(value, "lldp-interface-config")) { + obj.lldp_interface_config = parseLldpInterfaceConfig(location + "/lldp-interface-config", value["lldp-interface-config"], errors); } return obj; @@ -1249,47 +1473,58 @@ function instantiateSwitch(location, value, errors) { let obj = {}; function parsePortMirror(location, value, errors) { - if (type(value) == "object") { - let obj = {}; + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; - function parseMonitorPorts(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { + function parseMonitorPorts(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "monitor-ports")) { + obj.monitor_ports = parseMonitorPorts(location + "/monitor-ports", value["monitor-ports"], errors); + } + + function parseAnalysisPort(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); return value; } - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + if (exists(value, "analysis-port")) { + obj.analysis_port = parseAnalysisPort(location + "/analysis-port", value["analysis-port"], errors); + } + + return obj; } - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); return value; } - if (exists(value, "monitor-ports")) { - obj.monitor_ports = parseMonitorPorts(location + "/monitor-ports", value["monitor-ports"], errors); - } - - function parseAnalysisPort(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "analysis-port")) { - obj.analysis_port = parseAnalysisPort(location + "/analysis-port", value["analysis-port"], errors); - } - - return obj; + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); } - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); return value; } @@ -1869,20 +2104,6 @@ function instantiateSwitch(location, value, errors) { obj.port_isolation = parsePortIsolation(location + "/port-isolation", value["port-isolation"], errors); } - function parseJumboFrames(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "jumbo-frames")) { - obj.jumbo_frames = parseJumboFrames(location + "/jumbo-frames", value["jumbo-frames"], errors); - } - else { - obj.jumbo_frames = false; - } - function parseTrunkBalanceMethod(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); @@ -1900,229 +2121,42 @@ function instantiateSwitch(location, value, errors) { obj.trunk_balance_method = "src-dst-mac"; } - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateRadioRates(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseBeacon(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - if (!(value in [ 0, 1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000 ])) - push(errors, [ location, "must be one of 0, 1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000 or 54000" ]); - - return value; - } - - if (exists(value, "beacon")) { - obj.beacon = parseBeacon(location + "/beacon", value["beacon"], errors); - } - else { - obj.beacon = 6000; - } - - function parseMulticast(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - if (!(value in [ 0, 1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000 ])) - push(errors, [ location, "must be one of 0, 1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000 or 54000" ]); - - return value; - } - - if (exists(value, "multicast")) { - obj.multicast = parseMulticast(location + "/multicast", value["multicast"], errors); - } - else { - obj.multicast = 24000; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateRadioHe(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMultipleBssid(location, value, errors) { + function parseJumboFrames(location, value, errors) { if (type(value) != "bool") push(errors, [ location, "must be of type boolean" ]); return value; } - if (exists(value, "multiple-bssid")) { - obj.multiple_bssid = parseMultipleBssid(location + "/multiple-bssid", value["multiple-bssid"], errors); + if (exists(value, "jumbo-frames")) { + obj.jumbo_frames = parseJumboFrames(location + "/jumbo-frames", value["jumbo-frames"], errors); } else { - obj.multiple_bssid = false; + obj.jumbo_frames = false; } - function parseEma(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); + function parseDhcpSnooping(location, value, errors) { + if (type(value) == "object") { + let obj = {}; - return value; - } - - if (exists(value, "ema")) { - obj.ema = parseEma(location + "/ema", value["ema"], errors); - } - else { - obj.ema = false; - } - - function parseBssColor(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "bss-color")) { - obj.bss_color = parseBssColor(location + "/bss-color", value["bss-color"], errors); - } - else { - obj.bss_color = 64; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateRadio(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseBand(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "2G", "5G", "5G-lower", "5G-upper", "6G" ])) - push(errors, [ location, "must be one of \"2G\", \"5G\", \"5G-lower\", \"5G-upper\" or \"6G\"" ]); - - return value; - } - - if (exists(value, "band")) { - obj.band = parseBand(location + "/band", value["band"], errors); - } - - function parseBandwidth(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - if (!(value in [ 5, 10, 20 ])) - push(errors, [ location, "must be one of 5, 10 or 20" ]); - - return value; - } - - if (exists(value, "bandwidth")) { - obj.bandwidth = parseBandwidth(location + "/bandwidth", value["bandwidth"], errors); - } - - function parseChannel(location, value, errors) { - function parseVariant0(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 196) - push(errors, [ location, "must be lower than or equal to 196" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); + function parseDhcpSnoopEnable(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + return value; } - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); + if (exists(value, "dhcp-snoop-enable")) { + obj.dhcp_snoop_enable = parseDhcpSnoopEnable(location + "/dhcp-snoop-enable", value["dhcp-snoop-enable"], errors); + } + else { + obj.dhcp_snoop_enable = false; + } - return value; - } - - function parseVariant1(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (value != "auto") - push(errors, [ location, "must have value \"auto\"" ]); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success != 1) { - if (length(verrors)) - push(errors, [ location, "must match exactly one of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; - } - - if (exists(value, "channel")) { - obj.channel = parseChannel(location + "/channel", value["channel"], errors); - } - - function parseValidChannels(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { + function parseDhcpSnoopRateLimit(location, value, errors) { if (type(value) in [ "int", "double" ]) { - if (value > 196) - push(errors, [ location, "must be lower than or equal to 196" ]); + if (value > 2048) + push(errors, [ location, "must be lower than or equal to 2048" ]); if (value < 1) push(errors, [ location, "must be bigger than or equal to 1" ]); @@ -2135,211 +2169,714 @@ function instantiateRadio(location, value, errors) { return value; } - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } + if (exists(value, "dhcp-snoop-rate-limit")) { + obj.dhcp_snoop_rate_limit = parseDhcpSnoopRateLimit(location + "/dhcp-snoop-rate-limit", value["dhcp-snoop-rate-limit"], errors); + } - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); + function parseDhcpSnoopMacVerify(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); - return value; - } + return value; + } - if (exists(value, "valid-channels")) { - obj.valid_channels = parseValidChannels(location + "/valid-channels", value["valid-channels"], errors); - } + if (exists(value, "dhcp-snoop-mac-verify")) { + obj.dhcp_snoop_mac_verify = parseDhcpSnoopMacVerify(location + "/dhcp-snoop-mac-verify", value["dhcp-snoop-mac-verify"], errors); + } + else { + obj.dhcp_snoop_mac_verify = false; + } - function parseCountry(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 2) - push(errors, [ location, "must be at most 2 characters long" ]); + function parseDhcpSnoopInfOpt82(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); - if (length(value) < 2) - push(errors, [ location, "must be at least 2 characters long" ]); + return value; + } - } + if (exists(value, "dhcp-snoop-inf-opt-82")) { + obj.dhcp_snoop_inf_opt_82 = parseDhcpSnoopInfOpt82(location + "/dhcp-snoop-inf-opt-82", value["dhcp-snoop-inf-opt-82"], errors); + } + else { + obj.dhcp_snoop_inf_opt_82 = false; + } - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); + function parseDhcpSnoopInfOptEncodeSubopt(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); - return value; - } + return value; + } - if (exists(value, "country")) { - obj.country = parseCountry(location + "/country", value["country"], errors); - } + if (exists(value, "dhcp-snoop-inf-opt-encode-subopt")) { + obj.dhcp_snoop_inf_opt_encode_subopt = parseDhcpSnoopInfOptEncodeSubopt(location + "/dhcp-snoop-inf-opt-encode-subopt", value["dhcp-snoop-inf-opt-encode-subopt"], errors); + } + else { + obj.dhcp_snoop_inf_opt_encode_subopt = false; + } - function parseAllowDfs(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); + function parseDhcpSnoopInfOptRemoteid(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 32) + push(errors, [ location, "must be at most 32 characters long" ]); - return value; - } + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); - if (exists(value, "allow-dfs")) { - obj.allow_dfs = parseAllowDfs(location + "/allow-dfs", value["allow-dfs"], errors); - } - else { - obj.allow_dfs = true; - } + } - function parseChannelMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "HT", "VHT", "HE", "EHT" ])) - push(errors, [ location, "must be one of \"HT\", \"VHT\", \"HE\" or \"EHT\"" ]); - - return value; - } - - if (exists(value, "channel-mode")) { - obj.channel_mode = parseChannelMode(location + "/channel-mode", value["channel-mode"], errors); - } - else { - obj.channel_mode = "HE"; - } - - function parseChannelWidth(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - if (!(value in [ 20, 40, 80, 160, 320, 8080 ])) - push(errors, [ location, "must be one of 20, 40, 80, 160, 320 or 8080" ]); - - return value; - } - - if (exists(value, "channel-width")) { - obj.channel_width = parseChannelWidth(location + "/channel-width", value["channel-width"], errors); - } - else { - obj.channel_width = 80; - } - - function parseRequireMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "HT", "VHT", "HE" ])) - push(errors, [ location, "must be one of \"HT\", \"VHT\" or \"HE\"" ]); - - return value; - } - - if (exists(value, "require-mode")) { - obj.require_mode = parseRequireMode(location + "/require-mode", value["require-mode"], errors); - } - - function parseMimo(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "1x1", "2x2", "3x3", "4x4", "5x5", "6x6", "7x7", "8x8" ])) - push(errors, [ location, "must be one of \"1x1\", \"2x2\", \"3x3\", \"4x4\", \"5x5\", \"6x6\", \"7x7\" or \"8x8\"" ]); - - return value; - } - - if (exists(value, "mimo")) { - obj.mimo = parseMimo(location + "/mimo", value["mimo"], errors); - } - - function parseTxPower(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 30) - push(errors, [ location, "must be lower than or equal to 30" ]); - - if (value < 0) - push(errors, [ location, "must be bigger than or equal to 0" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "tx-power")) { - obj.tx_power = parseTxPower(location + "/tx-power", value["tx-power"], errors); - } - - function parseLegacyRates(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "legacy-rates")) { - obj.legacy_rates = parseLegacyRates(location + "/legacy-rates", value["legacy-rates"], errors); - } - else { - obj.legacy_rates = false; - } - - function parseBeaconInterval(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 15) - push(errors, [ location, "must be bigger than or equal to 15" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "beacon-interval")) { - obj.beacon_interval = parseBeaconInterval(location + "/beacon-interval", value["beacon-interval"], errors); - } - else { - obj.beacon_interval = 100; - } - - function parseMaximumClients(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "maximum-clients")) { - obj.maximum_clients = parseMaximumClients(location + "/maximum-clients", value["maximum-clients"], errors); - } - - function parseMaximumClientsIgnoreProbe(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "maximum-clients-ignore-probe")) { - obj.maximum_clients_ignore_probe = parseMaximumClientsIgnoreProbe(location + "/maximum-clients-ignore-probe", value["maximum-clients-ignore-probe"], errors); - } - - if (exists(value, "rates")) { - obj.rates = instantiateRadioRates(location + "/rates", value["rates"], errors); - } - - if (exists(value, "he-settings")) { - obj.he_settings = instantiateRadioHe(location + "/he-settings", value["he-settings"], errors); - } - - function parseHostapdIfaceRaw(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); return value; } + if (exists(value, "dhcp-snoop-inf-opt-remoteid")) { + obj.dhcp_snoop_inf_opt_remoteid = parseDhcpSnoopInfOptRemoteid(location + "/dhcp-snoop-inf-opt-remoteid", value["dhcp-snoop-inf-opt-remoteid"], errors); + } + + function parseDhcpSnoopInfOptPolicy(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "drop", "keep", "replace" ])) + push(errors, [ location, "must be one of \"drop\", \"keep\" or \"replace\"" ]); + + return value; + } + + if (exists(value, "dhcp-snoop-inf-opt-policy")) { + obj.dhcp_snoop_inf_opt_policy = parseDhcpSnoopInfOptPolicy(location + "/dhcp-snoop-inf-opt-policy", value["dhcp-snoop-inf-opt-policy"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "dhcp-snooping")) { + obj.dhcp_snooping = parseDhcpSnooping(location + "/dhcp-snooping", value["dhcp-snooping"], errors); + } + + function parseAcl(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseAclType(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "ipv4", "ipv6", "ipv4Ext", "ipv6Ext", "mac", "arp" ])) + push(errors, [ location, "must be one of \"ipv4\", \"ipv6\", \"ipv4Ext\", \"ipv6Ext\", \"mac\" or \"arp\"" ]); + + return value; + } + + if (exists(value, "acl-type")) { + obj.acl_type = parseAclType(location + "/acl-type", value["acl-type"], errors); + } + + function parseAclName(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 32) + push(errors, [ location, "must be at most 32 characters long" ]); + + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-name")) { + obj.acl_name = parseAclName(location + "/acl-name", value["acl-name"], errors); + } + + function parseAclRuleAction(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "permit", "deny" ])) + push(errors, [ location, "must be one of \"permit\" or \"deny\"" ]); + + return value; + } + + if (exists(value, "acl-rule-action")) { + obj.acl_rule_action = parseAclRuleAction(location + "/acl-rule-action", value["acl-rule-action"], errors); + } + + function parseAclSourceMacaddress(location, value, errors) { + if (type(value) == "string") { + if (!matchUcMac(value)) + push(errors, [ location, "must be a valid MAC address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-source-macaddress")) { + obj.acl_source_macaddress = parseAclSourceMacaddress(location + "/acl-source-macaddress", value["acl-source-macaddress"], errors); + } + + function parseAclSourceMacbitmask(location, value, errors) { + if (type(value) == "string") { + if (!matchUcMac(value)) + push(errors, [ location, "must be a valid MAC address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-source-macbitmask")) { + obj.acl_source_macbitmask = parseAclSourceMacbitmask(location + "/acl-source-macbitmask", value["acl-source-macbitmask"], errors); + } + + function parseAclDestMacaddress(location, value, errors) { + if (type(value) == "string") { + if (!matchUcMac(value)) + push(errors, [ location, "must be a valid MAC address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-dest-macaddress")) { + obj.acl_dest_macaddress = parseAclDestMacaddress(location + "/acl-dest-macaddress", value["acl-dest-macaddress"], errors); + } + + function parseAclDestMacbitmask(location, value, errors) { + if (type(value) == "string") { + if (!matchUcMac(value)) + push(errors, [ location, "must be a valid MAC address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-dest-macbitmask")) { + obj.acl_dest_macbitmask = parseAclDestMacbitmask(location + "/acl-dest-macbitmask", value["acl-dest-macbitmask"], errors); + } + + function parseAclPacketFormat(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "any", "untagged-eth2", "untagged-802.3", "tagged-eth2", "tagged-802.3" ])) + push(errors, [ location, "must be one of \"any\", \"untagged-eth2\", \"untagged-802.3\", \"tagged-eth2\" or \"tagged-802.3\"" ]); + + return value; + } + + if (exists(value, "acl-packet-format")) { + obj.acl_packet_format = parseAclPacketFormat(location + "/acl-packet-format", value["acl-packet-format"], errors); + } + + function parseAclVlanid(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 4094) + push(errors, [ location, "must be lower than or equal to 4094" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-vlanid")) { + obj.acl_vlanid = parseAclVlanid(location + "/acl-vlanid", value["acl-vlanid"], errors); + } + + function parseAclVidBitmask(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 4095) + push(errors, [ location, "must be lower than or equal to 4095" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-vid-bitmask")) { + obj.acl_vid_bitmask = parseAclVidBitmask(location + "/acl-vid-bitmask", value["acl-vid-bitmask"], errors); + } + + function parseAclEthertype(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ethertype")) { + obj.acl_ethertype = parseAclEthertype(location + "/acl-ethertype", value["acl-ethertype"], errors); + } + else { + obj.acl_ethertype = "800"; + } + + function parseAclEthertypeBitmask(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ethertype-bitmask")) { + obj.acl_ethertype_bitmask = parseAclEthertypeBitmask(location + "/acl-ethertype-bitmask", value["acl-ethertype-bitmask"], errors); + } + else { + obj.acl_ethertype_bitmask = "FFFF"; + } + + function parseAclCos(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 7) + push(errors, [ location, "must be lower than or equal to 7" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-cos")) { + obj.acl_cos = parseAclCos(location + "/acl-cos", value["acl-cos"], errors); + } + + function parseAclCosBitmask(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 7) + push(errors, [ location, "must be lower than or equal to 7" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-cos-bitmask")) { + obj.acl_cos_bitmask = parseAclCosBitmask(location + "/acl-cos-bitmask", value["acl-cos-bitmask"], errors); + } + + function parseAclIpv4SourceAddress(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv4(value)) + push(errors, [ location, "must be a valid IPv4 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ipv4-source-address")) { + obj.acl_ipv4_source_address = parseAclIpv4SourceAddress(location + "/acl-ipv4-source-address", value["acl-ipv4-source-address"], errors); + } + + function parseAclIpv4SourceSubnetmask(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv4(value)) + push(errors, [ location, "must be a valid IPv4 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ipv4-source-subnetmask")) { + obj.acl_ipv4_source_subnetmask = parseAclIpv4SourceSubnetmask(location + "/acl-ipv4-source-subnetmask", value["acl-ipv4-source-subnetmask"], errors); + } + + function parseAclIpv4DestAddress(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv4(value)) + push(errors, [ location, "must be a valid IPv4 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ipv4-dest-address")) { + obj.acl_ipv4_dest_address = parseAclIpv4DestAddress(location + "/acl-ipv4-dest-address", value["acl-ipv4-dest-address"], errors); + } + + function parseAclIpv4DestSubnetmask(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv4(value)) + push(errors, [ location, "must be a valid IPv4 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ipv4-dest-subnetmask")) { + obj.acl_ipv4_dest_subnetmask = parseAclIpv4DestSubnetmask(location + "/acl-ipv4-dest-subnetmask", value["acl-ipv4-dest-subnetmask"], errors); + } + + function parseAclIpv6SourceAddress(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv6(value)) + push(errors, [ location, "must be a valid IPv6 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ipv6-source-address")) { + obj.acl_ipv6_source_address = parseAclIpv6SourceAddress(location + "/acl-ipv6-source-address", value["acl-ipv6-source-address"], errors); + } + + function parseAclIpv6SourcePrefixLength(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 128) + push(errors, [ location, "must be lower than or equal to 128" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ipv6-source-prefix-length")) { + obj.acl_ipv6_source_prefix_length = parseAclIpv6SourcePrefixLength(location + "/acl-ipv6-source-prefix-length", value["acl-ipv6-source-prefix-length"], errors); + } + + function parseAclIpv6DestAddress(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv6(value)) + push(errors, [ location, "must be a valid IPv6 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-ipv6-dest-address")) { + obj.acl_ipv6_dest_address = parseAclIpv6DestAddress(location + "/acl-ipv6-dest-address", value["acl-ipv6-dest-address"], errors); + } + + function parseAclIpv6DestPrefixLength(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 128) + push(errors, [ location, "must be lower than or equal to 128" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ipv6-dest-prefix-length")) { + obj.acl_ipv6_dest_prefix_length = parseAclIpv6DestPrefixLength(location + "/acl-ipv6-dest-prefix-length", value["acl-ipv6-dest-prefix-length"], errors); + } + + function parseAclIpProto(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 255) + push(errors, [ location, "must be lower than or equal to 255" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ip-proto")) { + obj.acl_ip_proto = parseAclIpProto(location + "/acl-ip-proto", value["acl-ip-proto"], errors); + } + + function parseAclIpv6NextHeader(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 255) + push(errors, [ location, "must be lower than or equal to 255" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ipv6-next-header")) { + obj.acl_ipv6_next_header = parseAclIpv6NextHeader(location + "/acl-ipv6-next-header", value["acl-ipv6-next-header"], errors); + } + + function parseAclIpv6FlowLabel(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 1048575) + push(errors, [ location, "must be lower than or equal to 1048575" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ipv6-flow-label")) { + obj.acl_ipv6_flow_label = parseAclIpv6FlowLabel(location + "/acl-ipv6-flow-label", value["acl-ipv6-flow-label"], errors); + } + + function parseAclIpSourcePort(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 65535) + push(errors, [ location, "must be lower than or equal to 65535" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ip-source-port")) { + obj.acl_ip_source_port = parseAclIpSourcePort(location + "/acl-ip-source-port", value["acl-ip-source-port"], errors); + } + + function parseAclIpSourcePortBitmask(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 65535) + push(errors, [ location, "must be lower than or equal to 65535" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ip-source-port-bitmask")) { + obj.acl_ip_source_port_bitmask = parseAclIpSourcePortBitmask(location + "/acl-ip-source-port-bitmask", value["acl-ip-source-port-bitmask"], errors); + } + + function parseAclIpDestPort(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 65535) + push(errors, [ location, "must be lower than or equal to 65535" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ip-dest-port")) { + obj.acl_ip_dest_port = parseAclIpDestPort(location + "/acl-ip-dest-port", value["acl-ip-dest-port"], errors); + } + + function parseAclIpDestPortBitmask(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 65535) + push(errors, [ location, "must be lower than or equal to 65535" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ip-dest-port-bitmask")) { + obj.acl_ip_dest_port_bitmask = parseAclIpDestPortBitmask(location + "/acl-ip-dest-port-bitmask", value["acl-ip-dest-port-bitmask"], errors); + } + + function parseAclIpv4FragmentOffset(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 8191) + push(errors, [ location, "must be lower than or equal to 8191" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ipv4-fragment-offset")) { + obj.acl_ipv4_fragment_offset = parseAclIpv4FragmentOffset(location + "/acl-ipv4-fragment-offset", value["acl-ipv4-fragment-offset"], errors); + } + + function parseAclIpv6FragmentOffset(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 8191) + push(errors, [ location, "must be lower than or equal to 8191" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ipv6-fragment-offset")) { + obj.acl_ipv6_fragment_offset = parseAclIpv6FragmentOffset(location + "/acl-ipv6-fragment-offset", value["acl-ipv6-fragment-offset"], errors); + } + + function parseAclIpTtl(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 255) + push(errors, [ location, "must be lower than or equal to 255" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ip-ttl")) { + obj.acl_ip_ttl = parseAclIpTtl(location + "/acl-ip-ttl", value["acl-ip-ttl"], errors); + } + + function parseAclIpv6HopLimit(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 255) + push(errors, [ location, "must be lower than or equal to 255" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-ipv6-hop-limit")) { + obj.acl_ipv6_hop_limit = parseAclIpv6HopLimit(location + "/acl-ipv6-hop-limit", value["acl-ipv6-hop-limit"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); } @@ -2349,8 +2886,906 @@ function instantiateRadio(location, value, errors) { return value; } - if (exists(value, "hostapd-iface-raw")) { - obj.hostapd_iface_raw = parseHostapdIfaceRaw(location + "/hostapd-iface-raw", value["hostapd-iface-raw"], errors); + if (exists(value, "acl")) { + obj.acl = parseAcl(location + "/acl", value["acl"], errors); + } + + function parseMvrConfig(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseMvrEnable(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "mvr-enable")) { + obj.mvr_enable = parseMvrEnable(location + "/mvr-enable", value["mvr-enable"], errors); + } + else { + obj.mvr_enable = false; + } + + function parseMvrProxyQueryIntvl(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 43200) + push(errors, [ location, "must be lower than or equal to 43200" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "mvr-proxy-query-intvl")) { + obj.mvr_proxy_query_intvl = parseMvrProxyQueryIntvl(location + "/mvr-proxy-query-intvl", value["mvr-proxy-query-intvl"], errors); + } + else { + obj.mvr_proxy_query_intvl = 125; + } + + function parseMvrProxySwitching(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "mvr-proxy-switching")) { + obj.mvr_proxy_switching = parseMvrProxySwitching(location + "/mvr-proxy-switching", value["mvr-proxy-switching"], errors); + } + else { + obj.mvr_proxy_switching = false; + } + + function parseMvrRobustnessVal(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 255) + push(errors, [ location, "must be lower than or equal to 255" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "mvr-robustness-val")) { + obj.mvr_robustness_val = parseMvrRobustnessVal(location + "/mvr-robustness-val", value["mvr-robustness-val"], errors); + } + else { + obj.mvr_robustness_val = 2; + } + + function parseMvrSourcePortMode(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "dynamic", "forward" ])) + push(errors, [ location, "must be one of \"dynamic\" or \"forward\"" ]); + + return value; + } + + if (exists(value, "mvr-source-port-mode")) { + obj.mvr_source_port_mode = parseMvrSourcePortMode(location + "/mvr-source-port-mode", value["mvr-source-port-mode"], errors); + } + else { + obj.mvr_source_port_mode = "forward"; + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "mvr-config")) { + obj.mvr_config = parseMvrConfig(location + "/mvr-config", value["mvr-config"], errors); + } + + function parseMvrDomainConfig(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseMvrDomainId(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 10) + push(errors, [ location, "must be lower than or equal to 10" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "mvr-domain-id")) { + obj.mvr_domain_id = parseMvrDomainId(location + "/mvr-domain-id", value["mvr-domain-id"], errors); + } + else { + obj.mvr_domain_id = 1; + } + + function parseMvrDomainEnable(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "mvr-domain-enable")) { + obj.mvr_domain_enable = parseMvrDomainEnable(location + "/mvr-domain-enable", value["mvr-domain-enable"], errors); + } + else { + obj.mvr_domain_enable = false; + } + + function parseMvrDomainVlanId(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 4094) + push(errors, [ location, "must be lower than or equal to 4094" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "mvr-domain-vlan-id")) { + obj.mvr_domain_vlan_id = parseMvrDomainVlanId(location + "/mvr-domain-vlan-id", value["mvr-domain-vlan-id"], errors); + } + else { + obj.mvr_domain_vlan_id = 1; + } + + function parseMvrDomainUpstreamSip(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv4(value)) + push(errors, [ location, "must be a valid IPv4 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "mvr-domain-upstream-sip")) { + obj.mvr_domain_upstream_sip = parseMvrDomainUpstreamSip(location + "/mvr-domain-upstream-sip", value["mvr-domain-upstream-sip"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "mvr-domain-config")) { + obj.mvr_domain_config = parseMvrDomainConfig(location + "/mvr-domain-config", value["mvr-domain-config"], errors); + } + + function parseMvrGroupConfig(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseMvrGroupName(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 16) + push(errors, [ location, "must be at most 16 characters long" ]); + + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "mvr-group-name")) { + obj.mvr_group_name = parseMvrGroupName(location + "/mvr-group-name", value["mvr-group-name"], errors); + } + else { + push(errors, [ location, "is required" ]); + } + + function parseMvrGroupRangeStart(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv4(value)) + push(errors, [ location, "must be a valid IPv4 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "mvr-group-range-start")) { + obj.mvr_group_range_start = parseMvrGroupRangeStart(location + "/mvr-group-range-start", value["mvr-group-range-start"], errors); + } + else { + push(errors, [ location, "is required" ]); + } + + function parseMvrGroupRangeEnd(location, value, errors) { + if (type(value) == "string") { + if (!matchIpv4(value)) + push(errors, [ location, "must be a valid IPv4 address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "mvr-group-range-end")) { + obj.mvr_group_range_end = parseMvrGroupRangeEnd(location + "/mvr-group-range-end", value["mvr-group-range-end"], errors); + } + else { + push(errors, [ location, "is required" ]); + } + + function parseMvrGroupAssocDomain(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 10) + push(errors, [ location, "must be lower than or equal to 10" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "mvr-group-assoc-domain")) { + obj.mvr_group_assoc_domain = parseMvrGroupAssocDomain(location + "/mvr-group-assoc-domain", value["mvr-group-assoc-domain"], errors); + } + else { + push(errors, [ location, "is required" ]); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "mvr-group-config")) { + obj.mvr_group_config = parseMvrGroupConfig(location + "/mvr-group-config", value["mvr-group-config"], errors); + } + + function parseLldpGlobalConfig(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseLldpEnable(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lldp-enable")) { + obj.lldp_enable = parseLldpEnable(location + "/lldp-enable", value["lldp-enable"], errors); + } + else { + obj.lldp_enable = true; + } + + function parseLldpHoldtimeMultiplier(location, value, errors) { + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-holdtime-multiplier")) { + obj.lldp_holdtime_multiplier = parseLldpHoldtimeMultiplier(location + "/lldp-holdtime-multiplier", value["lldp-holdtime-multiplier"], errors); + } + else { + obj.lldp_holdtime_multiplier = 4; + } + + function parseLldpMedFastStartCount(location, value, errors) { + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-med-fast-start-count")) { + obj.lldp_med_fast_start_count = parseLldpMedFastStartCount(location + "/lldp-med-fast-start-count", value["lldp-med-fast-start-count"], errors); + } + else { + obj.lldp_med_fast_start_count = 4; + } + + function parseLldpRefreshInterval(location, value, errors) { + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-refresh-interval")) { + obj.lldp_refresh_interval = parseLldpRefreshInterval(location + "/lldp-refresh-interval", value["lldp-refresh-interval"], errors); + } + else { + obj.lldp_refresh_interval = 30; + } + + function parseLldpReinitDelay(location, value, errors) { + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-reinit-delay")) { + obj.lldp_reinit_delay = parseLldpReinitDelay(location + "/lldp-reinit-delay", value["lldp-reinit-delay"], errors); + } + else { + obj.lldp_reinit_delay = 2; + } + + function parseLldpTxDelay(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 8192) + push(errors, [ location, "must be lower than or equal to 8192" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-tx-delay")) { + obj.lldp_tx_delay = parseLldpTxDelay(location + "/lldp-tx-delay", value["lldp-tx-delay"], errors); + } + + function parseLldpNotificationInterval(location, value, errors) { + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "lldp-notification-interval")) { + obj.lldp_notification_interval = parseLldpNotificationInterval(location + "/lldp-notification-interval", value["lldp-notification-interval"], errors); + } + else { + obj.lldp_notification_interval = 5; + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "lldp-global-config")) { + obj.lldp_global_config = parseLldpGlobalConfig(location + "/lldp-global-config", value["lldp-global-config"], errors); + } + + function parseMcLag(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "mc-lag")) { + obj.mc_lag = parseMcLag(location + "/mc-lag", value["mc-lag"], errors); + } + else { + obj.mc_lag = false; + } + + function parseMclagConfig(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseMclagDomains(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseMclagDomain(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 1024) + push(errors, [ location, "must be lower than or equal to 1024" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "mclag-domain")) { + obj.mclag_domain = parseMclagDomain(location + "/mclag-domain", value["mclag-domain"], errors); + } + else { + obj.mclag_domain = 1; + } + + function parsePeerLink(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseType(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "port", "trunk-group" ])) + push(errors, [ location, "must be one of \"port\" or \"trunk-group\"" ]); + + return value; + } + + if (exists(value, "type")) { + obj.type = parseType(location + "/type", value["type"], errors); + } + else { + obj.type = "trunk-group"; + } + + function parseValue(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 64) + push(errors, [ location, "must be lower than or equal to 64" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "value")) { + obj.value = parseValue(location + "/value", value["value"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "peer-link")) { + obj.peer_link = parsePeerLink(location + "/peer-link", value["peer-link"], errors); + } + + function parseMclagGroup(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseGroupId(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 128) + push(errors, [ location, "must be lower than or equal to 128" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "group-id")) { + obj.group_id = parseGroupId(location + "/group-id", value["group-id"], errors); + } + + function parseMembers(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "members")) { + obj.members = parseMembers(location + "/members", value["members"], errors); + } + + function parseLacpConfig(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseLacpEnable(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "lacp-enable")) { + obj.lacp_enable = parseLacpEnable(location + "/lacp-enable", value["lacp-enable"], errors); + } + else { + obj.lacp_enable = true; + } + + function parseLacpRole(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "actor", "partner" ])) + push(errors, [ location, "must be one of \"actor\" or \"partner\"" ]); + + return value; + } + + if (exists(value, "lacp-role")) { + obj.lacp_role = parseLacpRole(location + "/lacp-role", value["lacp-role"], errors); + } + else { + obj.lacp_role = "actor"; + } + + function parseLacpTimeout(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "short", "long" ])) + push(errors, [ location, "must be one of \"short\" or \"long\"" ]); + + return value; + } + + if (exists(value, "lacp-timeout")) { + obj.lacp_timeout = parseLacpTimeout(location + "/lacp-timeout", value["lacp-timeout"], errors); + } + else { + obj.lacp_timeout = "long"; + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "lacp-config")) { + obj.lacp_config = parseLacpConfig(location + "/lacp-config", value["lacp-config"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "mclag-group")) { + obj.mclag_group = parseMclagGroup(location + "/mclag-group", value["mclag-group"], errors); + } + + function parseSystemPriority(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 65535) + push(errors, [ location, "must be lower than or equal to 65535" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "system-priority")) { + obj.system_priority = parseSystemPriority(location + "/system-priority", value["system-priority"], errors); + } + else { + obj.system_priority = 32768; + } + + function parseDualActiveDetection(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "dual-active-detection")) { + obj.dual_active_detection = parseDualActiveDetection(location + "/dual-active-detection", value["dual-active-detection"], errors); + } + else { + obj.dual_active_detection = true; + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "mclag-domains")) { + obj.mclag_domains = parseMclagDomains(location + "/mclag-domains", value["mclag-domains"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "mclag-config")) { + obj.mclag_config = parseMclagConfig(location + "/mclag-config", value["mclag-config"], errors); + } + + function parseVoiceVlanConfig(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseVoiceVlanId(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 4094) + push(errors, [ location, "must be lower than or equal to 4094" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "voice-vlan-id")) { + obj.voice_vlan_id = parseVoiceVlanId(location + "/voice-vlan-id", value["voice-vlan-id"], errors); + } + + function parseVoiceVlanAgeingTime(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 43200) + push(errors, [ location, "must be lower than or equal to 43200" ]); + + if (value < 5) + push(errors, [ location, "must be bigger than or equal to 5" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "voice-vlan-ageing-time")) { + obj.voice_vlan_ageing_time = parseVoiceVlanAgeingTime(location + "/voice-vlan-ageing-time", value["voice-vlan-ageing-time"], errors); + } + else { + obj.voice_vlan_ageing_time = 1440; + } + + function parseVoiceVlanOuiConfig(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseVoiceVlanOuiMac(location, value, errors) { + if (type(value) == "string") { + if (!matchUcMac(value)) + push(errors, [ location, "must be a valid MAC address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "voice-vlan-oui-mac")) { + obj.voice_vlan_oui_mac = parseVoiceVlanOuiMac(location + "/voice-vlan-oui-mac", value["voice-vlan-oui-mac"], errors); + } + + function parseVoiceVlanOuiMask(location, value, errors) { + if (type(value) == "string") { + if (!matchUcMac(value)) + push(errors, [ location, "must be a valid MAC address" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "voice-vlan-oui-mask")) { + obj.voice_vlan_oui_mask = parseVoiceVlanOuiMask(location + "/voice-vlan-oui-mask", value["voice-vlan-oui-mask"], errors); + } + + function parseVoiceVlanOuiDescription(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 32) + push(errors, [ location, "must be at most 32 characters long" ]); + + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "voice-vlan-oui-description")) { + obj.voice_vlan_oui_description = parseVoiceVlanOuiDescription(location + "/voice-vlan-oui-description", value["voice-vlan-oui-description"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; + } + + if (exists(value, "voice-vlan-oui-config")) { + obj.voice_vlan_oui_config = parseVoiceVlanOuiConfig(location + "/voice-vlan-oui-config", value["voice-vlan-oui-config"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "voice-vlan-config")) { + obj.voice_vlan_config = parseVoiceVlanConfig(location + "/voice-vlan-config", value["voice-vlan-config"], errors); } return obj; @@ -3283,6 +4718,80 @@ function instantiateInterfaceIpv4(location, value, errors) { obj.igmp = parseIgmp(location + "/igmp", value["igmp"], errors); } + function parseMvr(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseMvrIntfMvrRole(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "none", "source", "receiver" ])) + push(errors, [ location, "must be one of \"none\", \"source\" or \"receiver\"" ]); + + return value; + } + + if (exists(value, "mvr-intf-mvr-role")) { + obj.mvr_intf_mvr_role = parseMvrIntfMvrRole(location + "/mvr-intf-mvr-role", value["mvr-intf-mvr-role"], errors); + } + else { + push(errors, [ location, "is required" ]); + } + + function parseMvrIntfImmedLeave(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "none", "by-host-ip", "by-group" ])) + push(errors, [ location, "must be one of \"none\", \"by-host-ip\" or \"by-group\"" ]); + + return value; + } + + if (exists(value, "mvr-intf-immed-leave")) { + obj.mvr_intf_immed_leave = parseMvrIntfImmedLeave(location + "/mvr-intf-immed-leave", value["mvr-intf-immed-leave"], errors); + } + else { + obj.mvr_intf_immed_leave = "by-group"; + } + + function parseMvrIntfAssocDomain(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 10) + push(errors, [ location, "must be lower than or equal to 10" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "mvr-intf-assoc-domain")) { + obj.mvr_intf_assoc_domain = parseMvrIntfAssocDomain(location + "/mvr-intf-assoc-domain", value["mvr-intf-assoc-domain"], errors); + } + else { + push(errors, [ location, "is required" ]); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "mvr")) { + obj.mvr = parseMvr(location + "/mvr", value["mvr"], errors); + } + return obj; } @@ -3338,6 +4847,108 @@ function instantiateInterfaceIpv4(location, value, errors) { obj.use_dns = parseUseDns(location + "/use-dns", value["use-dns"], errors); } + function parseDhcpSnoopVlanEnable(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "dhcp-snoop-vlan-enable")) { + obj.dhcp_snoop_vlan_enable = parseDhcpSnoopVlanEnable(location + "/dhcp-snoop-vlan-enable", value["dhcp-snoop-vlan-enable"], errors); + } + else { + obj.dhcp_snoop_vlan_enable = false; + } + + function parseVoiceVlanIntfConfig(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseVoiceVlanIntfMode(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "none", "manual", "auto" ])) + push(errors, [ location, "must be one of \"none\", \"manual\" or \"auto\"" ]); + + return value; + } + + if (exists(value, "voice-vlan-intf-mode")) { + obj.voice_vlan_intf_mode = parseVoiceVlanIntfMode(location + "/voice-vlan-intf-mode", value["voice-vlan-intf-mode"], errors); + } + else { + obj.voice_vlan_intf_mode = "auto"; + } + + function parseVoiceVlanIntfPriority(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 6) + push(errors, [ location, "must be lower than or equal to 6" ]); + + if (value < 0) + push(errors, [ location, "must be bigger than or equal to 0" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "voice-vlan-intf-priority")) { + obj.voice_vlan_intf_priority = parseVoiceVlanIntfPriority(location + "/voice-vlan-intf-priority", value["voice-vlan-intf-priority"], errors); + } + else { + obj.voice_vlan_intf_priority = 6; + } + + function parseVoiceVlanIntfDetectVoice(location, value, errors) { + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + if (!(value in [ "oui", "lldp" ])) + push(errors, [ location, "must be one of \"oui\" or \"lldp\"" ]); + + return value; + } + + if (exists(value, "voice-vlan-intf-detect-voice")) { + obj.voice_vlan_intf_detect_voice = parseVoiceVlanIntfDetectVoice(location + "/voice-vlan-intf-detect-voice", value["voice-vlan-intf-detect-voice"], errors); + } + else { + obj.voice_vlan_intf_detect_voice = "oui"; + } + + function parseVoiceVlanIntfSecurity(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "voice-vlan-intf-security")) { + obj.voice_vlan_intf_security = parseVoiceVlanIntfSecurity(location + "/voice-vlan-intf-security", value["voice-vlan-intf-security"], errors); + } + else { + obj.voice_vlan_intf_security = false; + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + if (exists(value, "voice-vlan-intf-config")) { + obj.voice_vlan_intf_config = parseVoiceVlanIntfConfig(location + "/voice-vlan-intf-config", value["voice-vlan-intf-config"], errors); + } + if (exists(value, "dhcp")) { obj.dhcp = instantiateInterfaceIpv4Dhcp(location + "/dhcp", value["dhcp"], errors); } @@ -3823,6 +5434,185 @@ function instantiateInterfaceIpv6(location, value, errors) { return value; } +function instantiateInterfaceAcl(location, value, errors) { + if (type(value) == "array") { + function parseItem(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseAclInfPolicyPreference(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value > 64) + push(errors, [ location, "must be lower than or equal to 64" ]); + + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "acl-inf-policy-preference")) { + obj.acl_inf_policy_preference = parseAclInfPolicyPreference(location + "/acl-inf-policy-preference", value["acl-inf-policy-preference"], errors); + } + else { + obj.acl_inf_policy_preference = 1; + } + + function parseAclInfPolicyIngress(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 32) + push(errors, [ location, "must be at most 32 characters long" ]); + + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-inf-policy-ingress")) { + obj.acl_inf_policy_ingress = parseAclInfPolicyIngress(location + "/acl-inf-policy-ingress", value["acl-inf-policy-ingress"], errors); + } + + function parseAclInfCountersIngress(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "acl-inf-counters-ingress")) { + obj.acl_inf_counters_ingress = parseAclInfCountersIngress(location + "/acl-inf-counters-ingress", value["acl-inf-counters-ingress"], errors); + } + else { + obj.acl_inf_counters_ingress = false; + } + + function parseAclInfPolicyEgress(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 32) + push(errors, [ location, "must be at most 32 characters long" ]); + + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "acl-inf-policy-egress")) { + obj.acl_inf_policy_egress = parseAclInfPolicyEgress(location + "/acl-inf-policy-egress", value["acl-inf-policy-egress"], errors); + } + + function parseAclInfCountersEgress(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "acl-inf-counters-egress")) { + obj.acl_inf_counters_egress = parseAclInfCountersEgress(location + "/acl-inf-counters-egress", value["acl-inf-counters-egress"], errors); + } + else { + obj.acl_inf_counters_egress = false; + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; + } + + return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); + } + + if (type(value) != "array") + push(errors, [ location, "must be of type array" ]); + + return value; +} + +function instantiateInterfaceDhcpSnoopPort(location, value, errors) { + if (type(value) == "object") { + let obj = {}; + + function parseDhcpSnoopPortTrust(location, value, errors) { + if (type(value) != "bool") + push(errors, [ location, "must be of type boolean" ]); + + return value; + } + + if (exists(value, "dhcp-snoop-port-trust")) { + obj.dhcp_snoop_port_trust = parseDhcpSnoopPortTrust(location + "/dhcp-snoop-port-trust", value["dhcp-snoop-port-trust"], errors); + } + else { + obj.dhcp_snoop_port_trust = false; + } + + function parseDhcpSnoopPortClientLimit(location, value, errors) { + if (type(value) in [ "int", "double" ]) { + if (value < 1) + push(errors, [ location, "must be bigger than or equal to 1" ]); + + } + + if (type(value) != "int") + push(errors, [ location, "must be of type integer" ]); + + return value; + } + + if (exists(value, "dhcp-snoop-port-client-limit")) { + obj.dhcp_snoop_port_client_limit = parseDhcpSnoopPortClientLimit(location + "/dhcp-snoop-port-client-limit", value["dhcp-snoop-port-client-limit"], errors); + } + + function parseDhcpSnoopPortCircuitId(location, value, errors) { + if (type(value) == "string") { + if (length(value) > 32) + push(errors, [ location, "must be at most 32 characters long" ]); + + if (length(value) < 1) + push(errors, [ location, "must be at least 1 characters long" ]); + + } + + if (type(value) != "string") + push(errors, [ location, "must be of type string" ]); + + return value; + } + + if (exists(value, "dhcp-snoop-port-circuit-id")) { + obj.dhcp_snoop_port_circuit_id = parseDhcpSnoopPortCircuitId(location + "/dhcp-snoop-port-circuit-id", value["dhcp-snoop-port-circuit-id"], errors); + } + + return obj; + } + + if (type(value) != "object") + push(errors, [ location, "must be of type object" ]); + + return value; +} + function instantiateInterfaceBroadBandWwan(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -4047,3188 +5837,6 @@ function instantiateInterfaceBroadBand(location, value, errors) { return value; } -function instantiateInterfaceSsidMultiPsk(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMac(location, value, errors) { - if (type(value) == "string") { - if (!matchUcMac(value)) - push(errors, [ location, "must be a valid MAC address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "mac")) { - obj.mac = parseMac(location + "/mac", value["mac"], errors); - } - - function parseKey(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 63) - push(errors, [ location, "must be at most 63 characters long" ]); - - if (length(value) < 8) - push(errors, [ location, "must be at least 8 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "key")) { - obj.key = parseKey(location + "/key", value["key"], errors); - } - - function parseVlanId(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 4096) - push(errors, [ location, "must be lower than or equal to 4096" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "vlan-id")) { - obj.vlan_id = parseVlanId(location + "/vlan-id", value["vlan-id"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRrm(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseNeighborReporting(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "neighbor-reporting")) { - obj.neighbor_reporting = parseNeighborReporting(location + "/neighbor-reporting", value["neighbor-reporting"], errors); - } - else { - obj.neighbor_reporting = false; - } - - function parseReducedNeighborReporting(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "reduced-neighbor-reporting")) { - obj.reduced_neighbor_reporting = parseReducedNeighborReporting(location + "/reduced-neighbor-reporting", value["reduced-neighbor-reporting"], errors); - } - else { - obj.reduced_neighbor_reporting = false; - } - - function parseLci(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "lci")) { - obj.lci = parseLci(location + "/lci", value["lci"], errors); - } - - function parseCivicLocation(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "civic-location")) { - obj.civic_location = parseCivicLocation(location + "/civic-location", value["civic-location"], errors); - } - - function parseFtmResponder(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "ftm-responder")) { - obj.ftm_responder = parseFtmResponder(location + "/ftm-responder", value["ftm-responder"], errors); - } - else { - obj.ftm_responder = false; - } - - function parseStationaryAp(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "stationary-ap")) { - obj.stationary_ap = parseStationaryAp(location + "/stationary-ap", value["stationary-ap"], errors); - } - else { - obj.stationary_ap = false; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRateLimit(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseIngressRate(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "ingress-rate")) { - obj.ingress_rate = parseIngressRate(location + "/ingress-rate", value["ingress-rate"], errors); - } - else { - obj.ingress_rate = 0; - } - - function parseEgressRate(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "egress-rate")) { - obj.egress_rate = parseEgressRate(location + "/egress-rate", value["egress-rate"], errors); - } - else { - obj.egress_rate = 0; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRoaming(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMessageExchange(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "air", "ds" ])) - push(errors, [ location, "must be one of \"air\" or \"ds\"" ]); - - return value; - } - - if (exists(value, "message-exchange")) { - obj.message_exchange = parseMessageExchange(location + "/message-exchange", value["message-exchange"], errors); - } - else { - obj.message_exchange = "ds"; - } - - function parseGeneratePsk(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "generate-psk")) { - obj.generate_psk = parseGeneratePsk(location + "/generate-psk", value["generate-psk"], errors); - } - else { - obj.generate_psk = false; - } - - function parseDomainIdentifier(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 4) - push(errors, [ location, "must be at most 4 characters long" ]); - - if (length(value) < 4) - push(errors, [ location, "must be at least 4 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "domain-identifier")) { - obj.domain_identifier = parseDomainIdentifier(location + "/domain-identifier", value["domain-identifier"], errors); - } - - function parsePmkR0KeyHolder(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "pmk-r0-key-holder")) { - obj.pmk_r0_key_holder = parsePmkR0KeyHolder(location + "/pmk-r0-key-holder", value["pmk-r0-key-holder"], errors); - } - - function parsePmkR1KeyHolder(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "pmk-r1-key-holder")) { - obj.pmk_r1_key_holder = parsePmkR1KeyHolder(location + "/pmk-r1-key-holder", value["pmk-r1-key-holder"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRadiusLocalUser(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMac(location, value, errors) { - if (type(value) == "string") { - if (!matchUcMac(value)) - push(errors, [ location, "must be a valid MAC address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "mac")) { - obj.mac = parseMac(location + "/mac", value["mac"], errors); - } - - function parseUserName(location, value, errors) { - if (type(value) == "string") { - if (length(value) < 1) - push(errors, [ location, "must be at least 1 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "user-name")) { - obj.user_name = parseUserName(location + "/user-name", value["user-name"], errors); - } - - function parsePassword(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 63) - push(errors, [ location, "must be at most 63 characters long" ]); - - if (length(value) < 8) - push(errors, [ location, "must be at least 8 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "password")) { - obj.password = parsePassword(location + "/password", value["password"], errors); - } - - function parseVlanId(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 4096) - push(errors, [ location, "must be lower than or equal to 4096" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "vlan-id")) { - obj.vlan_id = parseVlanId(location + "/vlan-id", value["vlan-id"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRadiusLocal(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseServerIdentity(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "server-identity")) { - obj.server_identity = parseServerIdentity(location + "/server-identity", value["server-identity"], errors); - } - else { - obj.server_identity = "uCentral"; - } - - function parseUsers(location, value, errors) { - if (type(value) == "array") { - return map(value, (item, i) => instantiateInterfaceSsidRadiusLocalUser(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "users")) { - obj.users = parseUsers(location + "/users", value["users"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRadiusServer(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseHost(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "host")) { - obj.host = parseHost(location + "/host", value["host"], errors); - } - - function parsePort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "port")) { - obj.port = parsePort(location + "/port", value["port"], errors); - } - - function parseSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "secret")) { - obj.secret = parseSecret(location + "/secret", value["secret"], errors); - } - - function parseSecondary(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseHost(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "host")) { - obj.host = parseHost(location + "/host", value["host"], errors); - } - - function parsePort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "port")) { - obj.port = parsePort(location + "/port", value["port"], errors); - } - - function parseSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "secret")) { - obj.secret = parseSecret(location + "/secret", value["secret"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - if (exists(value, "secondary")) { - obj.secondary = parseSecondary(location + "/secondary", value["secondary"], errors); - } - - function parseRequestAttribute(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - function parseVariant0(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseVendorId(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "vendor-id")) { - obj.vendor_id = parseVendorId(location + "/vendor-id", value["vendor-id"], errors); - } - - function parseVendorAttributes(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseId(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 255) - push(errors, [ location, "must be lower than or equal to 255" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "id")) { - obj.id = parseId(location + "/id", value["id"], errors); - } - - function parseValue(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "value")) { - obj.value = parseValue(location + "/value", value["value"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "vendor-attributes")) { - obj.vendor_attributes = parseVendorAttributes(location + "/vendor-attributes", value["vendor-attributes"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - function parseVariant1(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseId(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 255) - push(errors, [ location, "must be lower than or equal to 255" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "id")) { - obj.id = parseId(location + "/id", value["id"], errors); - } - - function parseValue(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 4294967295) - push(errors, [ location, "must be lower than or equal to 4294967295" ]); - - if (value < 0) - push(errors, [ location, "must be bigger than or equal to 0" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "value")) { - obj.value = parseValue(location + "/value", value["value"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - function parseVariant2(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseId(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 255) - push(errors, [ location, "must be lower than or equal to 255" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "id")) { - obj.id = parseId(location + "/id", value["id"], errors); - } - - function parseValue(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "value")) { - obj.value = parseValue(location + "/value", value["value"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - function parseVariant3(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseId(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 255) - push(errors, [ location, "must be lower than or equal to 255" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "id")) { - obj.id = parseId(location + "/id", value["id"], errors); - } - - function parseHexValue(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "hex-value")) { - obj.hex_value = parseHexValue(location + "/hex-value", value["hex-value"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant2(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant3(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success == 0) { - if (length(verrors)) - push(errors, [ location, "must match at least one of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "request-attribute")) { - obj.request_attribute = parseRequestAttribute(location + "/request-attribute", value["request-attribute"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRadiusHealth(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseUsername(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "username")) { - obj.username = parseUsername(location + "/username", value["username"], errors); - } - - function parsePassword(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "password")) { - obj.password = parsePassword(location + "/password", value["password"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidRadius(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseNasIdentifier(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "nas-identifier")) { - obj.nas_identifier = parseNasIdentifier(location + "/nas-identifier", value["nas-identifier"], errors); - } - - function parseChargeableUserId(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "chargeable-user-id")) { - obj.chargeable_user_id = parseChargeableUserId(location + "/chargeable-user-id", value["chargeable-user-id"], errors); - } - else { - obj.chargeable_user_id = false; - } - - if (exists(value, "local")) { - obj.local = instantiateInterfaceSsidRadiusLocal(location + "/local", value["local"], errors); - } - - function parseDynamicAuthorization(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseHost(location, value, errors) { - if (type(value) == "string") { - if (!matchUcIp(value)) - push(errors, [ location, "must be a valid IPv4 or IPv6 address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "host")) { - obj.host = parseHost(location + "/host", value["host"], errors); - } - - function parsePort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "port")) { - obj.port = parsePort(location + "/port", value["port"], errors); - } - - function parseSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "secret")) { - obj.secret = parseSecret(location + "/secret", value["secret"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - if (exists(value, "dynamic-authorization")) { - obj.dynamic_authorization = parseDynamicAuthorization(location + "/dynamic-authorization", value["dynamic-authorization"], errors); - } - - function parseAuthentication(location, value, errors) { - function parseVariant0(location, value, errors) { - value = instantiateInterfaceSsidRadiusServer(location, value, errors); - - return value; - } - - function parseVariant1(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMacFilter(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "mac-filter")) { - obj.mac_filter = parseMacFilter(location + "/mac-filter", value["mac-filter"], errors); - } - else { - obj.mac_filter = false; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success != 2) { - if (length(verrors)) - push(errors, [ location, "must match all of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; - } - - if (exists(value, "authentication")) { - obj.authentication = parseAuthentication(location + "/authentication", value["authentication"], errors); - } - - function parseAccounting(location, value, errors) { - function parseVariant0(location, value, errors) { - value = instantiateInterfaceSsidRadiusServer(location, value, errors); - - return value; - } - - function parseVariant1(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseInterval(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 600) - push(errors, [ location, "must be lower than or equal to 600" ]); - - if (value < 60) - push(errors, [ location, "must be bigger than or equal to 60" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "interval")) { - obj.interval = parseInterval(location + "/interval", value["interval"], errors); - } - else { - obj.interval = 60; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success != 2) { - if (length(verrors)) - push(errors, [ location, "must match all of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; - } - - if (exists(value, "accounting")) { - obj.accounting = parseAccounting(location + "/accounting", value["accounting"], errors); - } - - if (exists(value, "health")) { - obj.health = instantiateInterfaceSsidRadiusHealth(location + "/health", value["health"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidCertificates(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseUseLocalCertificates(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "use-local-certificates")) { - obj.use_local_certificates = parseUseLocalCertificates(location + "/use-local-certificates", value["use-local-certificates"], errors); - } - else { - obj.use_local_certificates = false; - } - - function parseCaCertificate(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "ca-certificate")) { - obj.ca_certificate = parseCaCertificate(location + "/ca-certificate", value["ca-certificate"], errors); - } - - function parseCertificate(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "certificate")) { - obj.certificate = parseCertificate(location + "/certificate", value["certificate"], errors); - } - - function parsePrivateKey(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "private-key")) { - obj.private_key = parsePrivateKey(location + "/private-key", value["private-key"], errors); - } - - function parsePrivateKeyPassword(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "private-key-password")) { - obj.private_key_password = parsePrivateKeyPassword(location + "/private-key-password", value["private-key-password"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidPassPoint(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseVenueName(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "venue-name")) { - obj.venue_name = parseVenueName(location + "/venue-name", value["venue-name"], errors); - } - - function parseVenueGroup(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 32) - push(errors, [ location, "must be lower than or equal to 32" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "venue-group")) { - obj.venue_group = parseVenueGroup(location + "/venue-group", value["venue-group"], errors); - } - - function parseVenueType(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 32) - push(errors, [ location, "must be lower than or equal to 32" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "venue-type")) { - obj.venue_type = parseVenueType(location + "/venue-type", value["venue-type"], errors); - } - - function parseVenueUrl(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "string") { - if (!matchUri(value)) - push(errors, [ location, "must be a valid URI" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "venue-url")) { - obj.venue_url = parseVenueUrl(location + "/venue-url", value["venue-url"], errors); - } - - function parseAuthType(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 2) - push(errors, [ location, "must be at most 2 characters long" ]); - - if (length(value) < 2) - push(errors, [ location, "must be at least 2 characters long" ]); - - } - - if (type(value) == "object") { - let obj = {}; - - function parseType(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "terms-and-conditions", "online-enrollment", "http-redirection", "dns-redirection" ])) - push(errors, [ location, "must be one of \"terms-and-conditions\", \"online-enrollment\", \"http-redirection\" or \"dns-redirection\"" ]); - - return value; - } - - if (exists(value, "type")) { - obj.type = parseType(location + "/type", value["type"], errors); - } - - function parseUri(location, value, errors) { - if (type(value) == "string") { - if (!matchUri(value)) - push(errors, [ location, "must be a valid URI" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "uri")) { - obj.uri = parseUri(location + "/uri", value["uri"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - if (exists(value, "auth-type")) { - obj.auth_type = parseAuthType(location + "/auth-type", value["auth-type"], errors); - } - - function parseDomainName(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "string") { - if (!matchHostname(value)) - push(errors, [ location, "must be a valid hostname" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "domain-name")) { - obj.domain_name = parseDomainName(location + "/domain-name", value["domain-name"], errors); - } - - function parseNaiRealm(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "nai-realm")) { - obj.nai_realm = parseNaiRealm(location + "/nai-realm", value["nai-realm"], errors); - } - - function parseOsen(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "osen")) { - obj.osen = parseOsen(location + "/osen", value["osen"], errors); - } - - function parseAnqpDomain(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 0) - push(errors, [ location, "must be bigger than or equal to 0" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "anqp-domain")) { - obj.anqp_domain = parseAnqpDomain(location + "/anqp-domain", value["anqp-domain"], errors); - } - - function parseAnqp3gppCellNet(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "anqp-3gpp-cell-net")) { - obj.anqp_3gpp_cell_net = parseAnqp3gppCellNet(location + "/anqp-3gpp-cell-net", value["anqp-3gpp-cell-net"], errors); - } - - function parseFriendlyName(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "friendly-name")) { - obj.friendly_name = parseFriendlyName(location + "/friendly-name", value["friendly-name"], errors); - } - - function parseAccessNetworkType(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 15) - push(errors, [ location, "must be lower than or equal to 15" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "access-network-type")) { - obj.access_network_type = parseAccessNetworkType(location + "/access-network-type", value["access-network-type"], errors); - } - else { - obj.access_network_type = 0; - } - - function parseInternet(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "internet")) { - obj.internet = parseInternet(location + "/internet", value["internet"], errors); - } - else { - obj.internet = true; - } - - function parseAsra(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "asra")) { - obj.asra = parseAsra(location + "/asra", value["asra"], errors); - } - else { - obj.asra = false; - } - - function parseEsr(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "esr")) { - obj.esr = parseEsr(location + "/esr", value["esr"], errors); - } - else { - obj.esr = false; - } - - function parseUesa(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "uesa")) { - obj.uesa = parseUesa(location + "/uesa", value["uesa"], errors); - } - else { - obj.uesa = false; - } - - function parseHessid(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "hessid")) { - obj.hessid = parseHessid(location + "/hessid", value["hessid"], errors); - } - - function parseRoamingConsortium(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "roaming-consortium")) { - obj.roaming_consortium = parseRoamingConsortium(location + "/roaming-consortium", value["roaming-consortium"], errors); - } - - function parseDisableDgaf(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "disable-dgaf")) { - obj.disable_dgaf = parseDisableDgaf(location + "/disable-dgaf", value["disable-dgaf"], errors); - } - else { - obj.disable_dgaf = false; - } - - function parseIpaddrTypeAvailable(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 255) - push(errors, [ location, "must be lower than or equal to 255" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "ipaddr-type-available")) { - obj.ipaddr_type_available = parseIpaddrTypeAvailable(location + "/ipaddr-type-available", value["ipaddr-type-available"], errors); - } - - function parseConnectionCapability(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "connection-capability")) { - obj.connection_capability = parseConnectionCapability(location + "/connection-capability", value["connection-capability"], errors); - } - - function parseIcons(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseWidth(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "width")) { - obj.width = parseWidth(location + "/width", value["width"], errors); - } - - function parseHeight(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "height")) { - obj.height = parseHeight(location + "/height", value["height"], errors); - } - - function parseType(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "type")) { - obj.type = parseType(location + "/type", value["type"], errors); - } - - function parseIcon(location, value, errors) { - if (type(value) == "string") { - if (!matchUcBase64(value)) - push(errors, [ location, "must be a valid base64 encoded data" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "icon")) { - obj.icon = parseIcon(location + "/icon", value["icon"], errors); - } - - function parseLanguage(location, value, errors) { - if (type(value) == "string") { - if (!match(value, regexp("^[a-z][a-z][a-z]$"))) - push(errors, [ location, "must match regular expression /^[a-z][a-z][a-z]$/" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "language")) { - obj.language = parseLanguage(location + "/language", value["language"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "icons")) { - obj.icons = parseIcons(location + "/icons", value["icons"], errors); - } - - function parseWanMetrics(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseInfo(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "up", "down", "testing" ])) - push(errors, [ location, "must be one of \"up\", \"down\" or \"testing\"" ]); - - return value; - } - - if (exists(value, "info")) { - obj.info = parseInfo(location + "/info", value["info"], errors); - } - - function parseDownlink(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "downlink")) { - obj.downlink = parseDownlink(location + "/downlink", value["downlink"], errors); - } - - function parseUplink(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "uplink")) { - obj.uplink = parseUplink(location + "/uplink", value["uplink"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - if (exists(value, "wan-metrics")) { - obj.wan_metrics = parseWanMetrics(location + "/wan-metrics", value["wan-metrics"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidQualityThresholds(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseProbeRequestRssi(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "probe-request-rssi")) { - obj.probe_request_rssi = parseProbeRequestRssi(location + "/probe-request-rssi", value["probe-request-rssi"], errors); - } - - function parseAssociationRequestRssi(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "association-request-rssi")) { - obj.association_request_rssi = parseAssociationRequestRssi(location + "/association-request-rssi", value["association-request-rssi"], errors); - } - - function parseClientKickRssi(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "client-kick-rssi")) { - obj.client_kick_rssi = parseClientKickRssi(location + "/client-kick-rssi", value["client-kick-rssi"], errors); - } - - function parseClientKickBanTime(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "client-kick-ban-time")) { - obj.client_kick_ban_time = parseClientKickBanTime(location + "/client-kick-ban-time", value["client-kick-ban-time"], errors); - } - else { - obj.client_kick_ban_time = 0; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateInterfaceSsidAcl(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "allow", "deny" ])) - push(errors, [ location, "must be one of \"allow\" or \"deny\"" ]); - - return value; - } - - if (exists(value, "mode")) { - obj.mode = parseMode(location + "/mode", value["mode"], errors); - } - - function parseMacAddress(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "string") { - if (!matchUcMac(value)) - push(errors, [ location, "must be a valid MAC address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "mac-address")) { - obj.mac_address = parseMacAddress(location + "/mac-address", value["mac-address"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceCaptiveClick(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseAuthMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (value != "click-to-continue") - push(errors, [ location, "must have value \"click-to-continue\"" ]); - - return value; - } - - if (exists(value, "auth-mode")) { - obj.auth_mode = parseAuthMode(location + "/auth-mode", value["auth-mode"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceCaptiveRadius(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseAuthMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (value != "radius") - push(errors, [ location, "must have value \"radius\"" ]); - - return value; - } - - if (exists(value, "auth-mode")) { - obj.auth_mode = parseAuthMode(location + "/auth-mode", value["auth-mode"], errors); - } - - function parseAuthServer(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "auth-server")) { - obj.auth_server = parseAuthServer(location + "/auth-server", value["auth-server"], errors); - } - - function parseAuthPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "auth-port")) { - obj.auth_port = parseAuthPort(location + "/auth-port", value["auth-port"], errors); - } - else { - obj.auth_port = 1812; - } - - function parseAuthSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "auth-secret")) { - obj.auth_secret = parseAuthSecret(location + "/auth-secret", value["auth-secret"], errors); - } - - function parseAcctServer(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "acct-server")) { - obj.acct_server = parseAcctServer(location + "/acct-server", value["acct-server"], errors); - } - - function parseAcctPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "acct-port")) { - obj.acct_port = parseAcctPort(location + "/acct-port", value["acct-port"], errors); - } - else { - obj.acct_port = 1812; - } - - function parseAcctSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "acct-secret")) { - obj.acct_secret = parseAcctSecret(location + "/acct-secret", value["acct-secret"], errors); - } - - function parseAcctInterval(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "acct-interval")) { - obj.acct_interval = parseAcctInterval(location + "/acct-interval", value["acct-interval"], errors); - } - else { - obj.acct_interval = 600; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceCaptiveCredentials(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseAuthMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (value != "credentials") - push(errors, [ location, "must have value \"credentials\"" ]); - - return value; - } - - if (exists(value, "auth-mode")) { - obj.auth_mode = parseAuthMode(location + "/auth-mode", value["auth-mode"], errors); - } - - function parseCredentials(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseUsername(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "username")) { - obj.username = parseUsername(location + "/username", value["username"], errors); - } - - function parsePassword(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "password")) { - obj.password = parsePassword(location + "/password", value["password"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "credentials")) { - obj.credentials = parseCredentials(location + "/credentials", value["credentials"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceCaptiveUam(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseAuthMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (value != "uam") - push(errors, [ location, "must have value \"uam\"" ]); - - return value; - } - - if (exists(value, "auth-mode")) { - obj.auth_mode = parseAuthMode(location + "/auth-mode", value["auth-mode"], errors); - } - - function parseUamPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "uam-port")) { - obj.uam_port = parseUamPort(location + "/uam-port", value["uam-port"], errors); - } - else { - obj.uam_port = 3990; - } - - function parseUamSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "uam-secret")) { - obj.uam_secret = parseUamSecret(location + "/uam-secret", value["uam-secret"], errors); - } - - function parseUamServer(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "uam-server")) { - obj.uam_server = parseUamServer(location + "/uam-server", value["uam-server"], errors); - } - - function parseNasid(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "nasid")) { - obj.nasid = parseNasid(location + "/nasid", value["nasid"], errors); - } - - function parseNasmac(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "nasmac")) { - obj.nasmac = parseNasmac(location + "/nasmac", value["nasmac"], errors); - } - - function parseAuthServer(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "auth-server")) { - obj.auth_server = parseAuthServer(location + "/auth-server", value["auth-server"], errors); - } - - function parseAuthPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "auth-port")) { - obj.auth_port = parseAuthPort(location + "/auth-port", value["auth-port"], errors); - } - else { - obj.auth_port = 1812; - } - - function parseAuthSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "auth-secret")) { - obj.auth_secret = parseAuthSecret(location + "/auth-secret", value["auth-secret"], errors); - } - - function parseAcctServer(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "acct-server")) { - obj.acct_server = parseAcctServer(location + "/acct-server", value["acct-server"], errors); - } - - function parseAcctPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "acct-port")) { - obj.acct_port = parseAcctPort(location + "/acct-port", value["acct-port"], errors); - } - else { - obj.acct_port = 1812; - } - - function parseAcctSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "acct-secret")) { - obj.acct_secret = parseAcctSecret(location + "/acct-secret", value["acct-secret"], errors); - } - - function parseAcctInterval(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "acct-interval")) { - obj.acct_interval = parseAcctInterval(location + "/acct-interval", value["acct-interval"], errors); - } - else { - obj.acct_interval = 600; - } - - function parseSsid(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "ssid")) { - obj.ssid = parseSsid(location + "/ssid", value["ssid"], errors); - } - - function parseMacFormat(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "aabbccddeeff", "aa-bb-cc-dd-ee-ff", "aa:bb:cc:dd:ee:ff", "AABBCCDDEEFF", "AA:BB:CC:DD:EE:FF", "AA-BB-CC-DD-EE-FF" ])) - push(errors, [ location, "must be one of \"aabbccddeeff\", \"aa-bb-cc-dd-ee-ff\", \"aa:bb:cc:dd:ee:ff\", \"AABBCCDDEEFF\", \"AA:BB:CC:DD:EE:FF\" or \"AA-BB-CC-DD-EE-FF\"" ]); - - return value; - } - - if (exists(value, "mac-format")) { - obj.mac_format = parseMacFormat(location + "/mac-format", value["mac-format"], errors); - } - - function parseFinalRedirectUrl(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "default", "uam" ])) - push(errors, [ location, "must be one of \"default\" or \"uam\"" ]); - - return value; - } - - if (exists(value, "final-redirect-url")) { - obj.final_redirect_url = parseFinalRedirectUrl(location + "/final-redirect-url", value["final-redirect-url"], errors); - } - - function parseMacAuth(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "mac-auth")) { - obj.mac_auth = parseMacAuth(location + "/mac-auth", value["mac-auth"], errors); - } - else { - obj.mac_auth = "default"; - } - - function parseRadiusGwProxy(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "radius-gw-proxy")) { - obj.radius_gw_proxy = parseRadiusGwProxy(location + "/radius-gw-proxy", value["radius-gw-proxy"], errors); - } - else { - obj.radius_gw_proxy = false; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceCaptive(location, value, errors) { - function parseVariant0(location, value, errors) { - function parseVariant0(location, value, errors) { - value = instantiateServiceCaptiveClick(location, value, errors); - - return value; - } - - function parseVariant1(location, value, errors) { - value = instantiateServiceCaptiveRadius(location, value, errors); - - return value; - } - - function parseVariant2(location, value, errors) { - value = instantiateServiceCaptiveCredentials(location, value, errors); - - return value; - } - - function parseVariant3(location, value, errors) { - value = instantiateServiceCaptiveUam(location, value, errors); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant2(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant3(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success != 1) { - if (length(verrors)) - push(errors, [ location, "must match exactly one of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; - } - - function parseVariant1(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseWalledGardenFqdn(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "walled-garden-fqdn")) { - obj.walled_garden_fqdn = parseWalledGardenFqdn(location + "/walled-garden-fqdn", value["walled-garden-fqdn"], errors); - } - - function parseWalledGardenIpaddr(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "string") { - if (!matchUcIp(value)) - push(errors, [ location, "must be a valid IPv4 or IPv6 address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "walled-garden-ipaddr")) { - obj.walled_garden_ipaddr = parseWalledGardenIpaddr(location + "/walled-garden-ipaddr", value["walled-garden-ipaddr"], errors); - } - - function parseWebRoot(location, value, errors) { - if (type(value) == "string") { - if (!matchUcBase64(value)) - push(errors, [ location, "must be a valid base64 encoded data" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "web-root")) { - obj.web_root = parseWebRoot(location + "/web-root", value["web-root"], errors); - } - - function parseIdleTimeout(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "idle-timeout")) { - obj.idle_timeout = parseIdleTimeout(location + "/idle-timeout", value["idle-timeout"], errors); - } - else { - obj.idle_timeout = 600; - } - - function parseSessionTimeout(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "session-timeout")) { - obj.session_timeout = parseSessionTimeout(location + "/session-timeout", value["session-timeout"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success != 2) { - if (length(verrors)) - push(errors, [ location, "must match all of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; -} - -function instantiateInterfaceSsid(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parsePurpose(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "user-defined", "onboarding-ap", "onboarding-sta" ])) - push(errors, [ location, "must be one of \"user-defined\", \"onboarding-ap\" or \"onboarding-sta\"" ]); - - return value; - } - - if (exists(value, "purpose")) { - obj.purpose = parsePurpose(location + "/purpose", value["purpose"], errors); - } - else { - obj.purpose = "user-defined"; - } - - function parseName(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 32) - push(errors, [ location, "must be at most 32 characters long" ]); - - if (length(value) < 1) - push(errors, [ location, "must be at least 1 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "name")) { - obj.name = parseName(location + "/name", value["name"], errors); - } - - function parseWifiBands(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "2G", "5G", "5G-lower", "5G-upper", "6G" ])) - push(errors, [ location, "must be one of \"2G\", \"5G\", \"5G-lower\", \"5G-upper\" or \"6G\"" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "wifi-bands")) { - obj.wifi_bands = parseWifiBands(location + "/wifi-bands", value["wifi-bands"], errors); - } - - function parseBssMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "ap", "sta", "mesh", "wds-ap", "wds-sta", "wds-repeater" ])) - push(errors, [ location, "must be one of \"ap\", \"sta\", \"mesh\", \"wds-ap\", \"wds-sta\" or \"wds-repeater\"" ]); - - return value; - } - - if (exists(value, "bss-mode")) { - obj.bss_mode = parseBssMode(location + "/bss-mode", value["bss-mode"], errors); - } - else { - obj.bss_mode = "ap"; - } - - function parseBssid(location, value, errors) { - if (type(value) == "string") { - if (!matchUcMac(value)) - push(errors, [ location, "must be a valid MAC address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "bssid")) { - obj.bssid = parseBssid(location + "/bssid", value["bssid"], errors); - } - - function parseHiddenSsid(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "hidden-ssid")) { - obj.hidden_ssid = parseHiddenSsid(location + "/hidden-ssid", value["hidden-ssid"], errors); - } - - function parseIsolateClients(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "isolate-clients")) { - obj.isolate_clients = parseIsolateClients(location + "/isolate-clients", value["isolate-clients"], errors); - } - - function parseStrictForwarding(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "strict-forwarding")) { - obj.strict_forwarding = parseStrictForwarding(location + "/strict-forwarding", value["strict-forwarding"], errors); - } - else { - obj.strict_forwarding = false; - } - - function parsePowerSave(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "power-save")) { - obj.power_save = parsePowerSave(location + "/power-save", value["power-save"], errors); - } - - function parseRtsThreshold(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "rts-threshold")) { - obj.rts_threshold = parseRtsThreshold(location + "/rts-threshold", value["rts-threshold"], errors); - } - - function parseBroadcastTime(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "broadcast-time")) { - obj.broadcast_time = parseBroadcastTime(location + "/broadcast-time", value["broadcast-time"], errors); - } - - function parseUnicastConversion(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "unicast-conversion")) { - obj.unicast_conversion = parseUnicastConversion(location + "/unicast-conversion", value["unicast-conversion"], errors); - } - else { - obj.unicast_conversion = true; - } - - function parseServices(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "services")) { - obj.services = parseServices(location + "/services", value["services"], errors); - } - - function parseDtimPeriod(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 255) - push(errors, [ location, "must be lower than or equal to 255" ]); - - if (value < 1) - push(errors, [ location, "must be bigger than or equal to 1" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "dtim-period")) { - obj.dtim_period = parseDtimPeriod(location + "/dtim-period", value["dtim-period"], errors); - } - else { - obj.dtim_period = 2; - } - - function parseMaximumClients(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "maximum-clients")) { - obj.maximum_clients = parseMaximumClients(location + "/maximum-clients", value["maximum-clients"], errors); - } - - function parseProxyArp(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "proxy-arp")) { - obj.proxy_arp = parseProxyArp(location + "/proxy-arp", value["proxy-arp"], errors); - } - else { - obj.proxy_arp = true; - } - - function parseDisassocLowAck(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "disassoc-low-ack")) { - obj.disassoc_low_ack = parseDisassocLowAck(location + "/disassoc-low-ack", value["disassoc-low-ack"], errors); - } - else { - obj.disassoc_low_ack = false; - } - - function parseVendorElements(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "vendor-elements")) { - obj.vendor_elements = parseVendorElements(location + "/vendor-elements", value["vendor-elements"], errors); - } - - function parseTipInformationElement(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "tip-information-element")) { - obj.tip_information_element = parseTipInformationElement(location + "/tip-information-element", value["tip-information-element"], errors); - } - else { - obj.tip_information_element = true; - } - - function parseFilsDiscoveryInterval(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 20) - push(errors, [ location, "must be lower than or equal to 20" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "fils-discovery-interval")) { - obj.fils_discovery_interval = parseFilsDiscoveryInterval(location + "/fils-discovery-interval", value["fils-discovery-interval"], errors); - } - else { - obj.fils_discovery_interval = 20; - } - - if (exists(value, "encryption")) { - obj.encryption = instantiateInterfaceSsidEncryption(location + "/encryption", value["encryption"], errors); - } - - function parseMultiPsk(location, value, errors) { - if (type(value) == "array") { - return map(value, (item, i) => instantiateInterfaceSsidMultiPsk(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "multi-psk")) { - obj.multi_psk = parseMultiPsk(location + "/multi-psk", value["multi-psk"], errors); - } - - if (exists(value, "rrm")) { - obj.rrm = instantiateInterfaceSsidRrm(location + "/rrm", value["rrm"], errors); - } - - if (exists(value, "rate-limit")) { - obj.rate_limit = instantiateInterfaceSsidRateLimit(location + "/rate-limit", value["rate-limit"], errors); - } - - function parseRoaming(location, value, errors) { - function parseVariant0(location, value, errors) { - value = instantiateInterfaceSsidRoaming(location, value, errors); - - return value; - } - - function parseVariant1(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - let success = 0, tryval, tryerr, vvalue = null, verrors = []; - - tryerr = []; - tryval = parseVariant0(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - tryerr = []; - tryval = parseVariant1(location, value, tryerr); - if (!length(tryerr)) { - if (type(vvalue) == "object" && type(tryval) == "object") - vvalue = { ...vvalue, ...tryval }; - else - vvalue = tryval; - - success++; - } - else { - push(verrors, join(" and\n", map(tryerr, err => "\t - " + err[1]))); - } - - if (success == 0) { - if (length(verrors)) - push(errors, [ location, "must match at least one of the following constraints:\n" + join("\n- or -\n", verrors) ]); - else - push(errors, [ location, "must match only one variant" ]); - return null; - } - - value = vvalue; - - return value; - } - - if (exists(value, "roaming")) { - obj.roaming = parseRoaming(location + "/roaming", value["roaming"], errors); - } - - if (exists(value, "radius")) { - obj.radius = instantiateInterfaceSsidRadius(location + "/radius", value["radius"], errors); - } - - if (exists(value, "certificates")) { - obj.certificates = instantiateInterfaceSsidCertificates(location + "/certificates", value["certificates"], errors); - } - - if (exists(value, "pass-point")) { - obj.pass_point = instantiateInterfaceSsidPassPoint(location + "/pass-point", value["pass-point"], errors); - } - - if (exists(value, "quality-thresholds")) { - obj.quality_thresholds = instantiateInterfaceSsidQualityThresholds(location + "/quality-thresholds", value["quality-thresholds"], errors); - } - - if (exists(value, "access-control-list")) { - obj.access_control_list = instantiateInterfaceSsidAcl(location + "/access-control-list", value["access-control-list"], errors); - } - - if (exists(value, "captive")) { - obj.captive = instantiateServiceCaptive(location + "/captive", value["captive"], errors); - } - - function parseHostapdBssRaw(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "hostapd-bss-raw")) { - obj.hostapd_bss_raw = parseHostapdBssRaw(location + "/hostapd-bss-raw", value["hostapd-bss-raw"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateInterfaceTunnelMesh(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -7804,25 +6412,18 @@ function instantiateInterface(location, value, errors) { obj.ipv6 = instantiateInterfaceIpv6(location + "/ipv6", value["ipv6"], errors); } + if (exists(value, "acl")) { + obj.acl = instantiateInterfaceAcl(location + "/acl", value["acl"], errors); + } + + if (exists(value, "dhcp-snoop-port")) { + obj.dhcp_snoop_port = instantiateInterfaceDhcpSnoopPort(location + "/dhcp-snoop-port", value["dhcp-snoop-port"], errors); + } + if (exists(value, "broad-band")) { obj.broad_band = instantiateInterfaceBroadBand(location + "/broad-band", value["broad-band"], errors); } - function parseSsids(location, value, errors) { - if (type(value) == "array") { - return map(value, (item, i) => instantiateInterfaceSsid(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "ssids")) { - obj.ssids = parseSsids(location + "/ssids", value["ssids"], errors); - } - if (exists(value, "tunnel")) { obj.tunnel = instantiateInterfaceTunnel(location + "/tunnel", value["tunnel"], errors); } @@ -8301,242 +6902,6 @@ function instantiateServiceIgmp(location, value, errors) { return value; } -function instantiateServiceIeee8021x(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "radius", "user" ])) - push(errors, [ location, "must be one of \"radius\" or \"user\"" ]); - - return value; - } - - if (exists(value, "mode")) { - obj.mode = parseMode(location + "/mode", value["mode"], errors); - } - - function parsePortFilter(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "port-filter")) { - obj.port_filter = parsePortFilter(location + "/port-filter", value["port-filter"], errors); - } - - function parseUsers(location, value, errors) { - if (type(value) == "array") { - return map(value, (item, i) => instantiateInterfaceSsidRadiusLocalUser(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "users")) { - obj.users = parseUsers(location + "/users", value["users"], errors); - } - - function parseRadius(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseNasIdentifier(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "nas-identifier")) { - obj.nas_identifier = parseNasIdentifier(location + "/nas-identifier", value["nas-identifier"], errors); - } - - function parseAuthServerAddr(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "auth-server-addr")) { - obj.auth_server_addr = parseAuthServerAddr(location + "/auth-server-addr", value["auth-server-addr"], errors); - } - - function parseAuthServerPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "auth-server-port")) { - obj.auth_server_port = parseAuthServerPort(location + "/auth-server-port", value["auth-server-port"], errors); - } - - function parseAuthServerSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "auth-server-secret")) { - obj.auth_server_secret = parseAuthServerSecret(location + "/auth-server-secret", value["auth-server-secret"], errors); - } - - function parseAcctServerAddr(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "acct-server-addr")) { - obj.acct_server_addr = parseAcctServerAddr(location + "/acct-server-addr", value["acct-server-addr"], errors); - } - - function parseAcctServerPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "acct-server-port")) { - obj.acct_server_port = parseAcctServerPort(location + "/acct-server-port", value["acct-server-port"], errors); - } - - function parseAcctServerSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "acct-server-secret")) { - obj.acct_server_secret = parseAcctServerSecret(location + "/acct-server-secret", value["acct-server-secret"], errors); - } - - function parseCoaServerAddr(location, value, errors) { - if (type(value) == "string") { - if (!matchUcHost(value)) - push(errors, [ location, "must be a valid hostname or IP address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "coa-server-addr")) { - obj.coa_server_addr = parseCoaServerAddr(location + "/coa-server-addr", value["coa-server-addr"], errors); - } - - function parseCoaServerPort(location, value, errors) { - if (type(value) in [ "int", "double" ]) { - if (value > 65535) - push(errors, [ location, "must be lower than or equal to 65535" ]); - - if (value < 1024) - push(errors, [ location, "must be bigger than or equal to 1024" ]); - - } - - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "coa-server-port")) { - obj.coa_server_port = parseCoaServerPort(location + "/coa-server-port", value["coa-server-port"], errors); - } - - function parseCoaServerSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "coa-server-secret")) { - obj.coa_server_secret = parseCoaServerSecret(location + "/coa-server-secret", value["coa-server-secret"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - if (exists(value, "radius")) { - obj.radius = parseRadius(location + "/radius", value["radius"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateServiceRadiusProxy(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -9113,8 +7478,8 @@ function instantiateServiceOnlineCheck(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); - if (!(value in [ "wifi", "leds" ])) - push(errors, [ location, "must be one of \"wifi\" or \"leds\"" ]); + if (!(value in [ "leds" ])) + push(errors, [ location, "must be one of \"leds\"" ]); return value; } @@ -9210,131 +7575,6 @@ function instantiateServiceDataPlane(location, value, errors) { return value; } -function instantiateServiceWifiSteering(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseMode(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "local", "none" ])) - push(errors, [ location, "must be one of \"local\" or \"none\"" ]); - - return value; - } - - if (exists(value, "mode")) { - obj.mode = parseMode(location + "/mode", value["mode"], errors); - } - - function parseAssocSteering(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "assoc-steering")) { - obj.assoc_steering = parseAssocSteering(location + "/assoc-steering", value["assoc-steering"], errors); - } - else { - obj.assoc_steering = false; - } - - function parseRequiredSnr(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "required-snr")) { - obj.required_snr = parseRequiredSnr(location + "/required-snr", value["required-snr"], errors); - } - else { - obj.required_snr = 0; - } - - function parseRequiredProbeSnr(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "required-probe-snr")) { - obj.required_probe_snr = parseRequiredProbeSnr(location + "/required-probe-snr", value["required-probe-snr"], errors); - } - else { - obj.required_probe_snr = 0; - } - - function parseRequiredRoamSnr(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "required-roam-snr")) { - obj.required_roam_snr = parseRequiredRoamSnr(location + "/required-roam-snr", value["required-roam-snr"], errors); - } - else { - obj.required_roam_snr = 0; - } - - function parseLoadKickThreshold(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "load-kick-threshold")) { - obj.load_kick_threshold = parseLoadKickThreshold(location + "/load-kick-threshold", value["load-kick-threshold"], errors); - } - else { - obj.load_kick_threshold = 0; - } - - function parseAutoChannel(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "auto-channel")) { - obj.auto_channel = parseAutoChannel(location + "/auto-channel", value["auto-channel"], errors); - } - else { - obj.auto_channel = false; - } - - function parseIpv6(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "ipv6")) { - obj.ipv6 = parseIpv6(location + "/ipv6", value["ipv6"], errors); - } - else { - obj.ipv6 = false; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateServiceQualityOfServiceClassSelector(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); @@ -9657,163 +7897,6 @@ function instantiateServiceQualityOfService(location, value, errors) { return value; } -function instantiateServiceFacebookWifi(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseVendorId(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "vendor-id")) { - obj.vendor_id = parseVendorId(location + "/vendor-id", value["vendor-id"], errors); - } - - function parseGatewayId(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "gateway-id")) { - obj.gateway_id = parseGatewayId(location + "/gateway-id", value["gateway-id"], errors); - } - - function parseSecret(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "secret")) { - obj.secret = parseSecret(location + "/secret", value["secret"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceAirtimeFairness(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseVoiceWeight(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "voice-weight")) { - obj.voice_weight = parseVoiceWeight(location + "/voice-weight", value["voice-weight"], errors); - } - else { - obj.voice_weight = 4; - } - - function parsePacketThreshold(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "packet-threshold")) { - obj.packet_threshold = parsePacketThreshold(location + "/packet-threshold", value["packet-threshold"], errors); - } - else { - obj.packet_threshold = 100; - } - - function parseBulkThreshold(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "bulk-threshold")) { - obj.bulk_threshold = parseBulkThreshold(location + "/bulk-threshold", value["bulk-threshold"], errors); - } - else { - obj.bulk_threshold = 50; - } - - function parsePriorityThreshold(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "priority-threshold")) { - obj.priority_threshold = parsePriorityThreshold(location + "/priority-threshold", value["priority-threshold"], errors); - } - else { - obj.priority_threshold = 30; - } - - function parseWeightNormal(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "weight-normal")) { - obj.weight_normal = parseWeightNormal(location + "/weight-normal", value["weight-normal"], errors); - } - else { - obj.weight_normal = 256; - } - - function parseWeightPriority(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "weight-priority")) { - obj.weight_priority = parseWeightPriority(location + "/weight-priority", value["weight-priority"], errors); - } - else { - obj.weight_priority = 394; - } - - function parseWeightBulk(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "weight-bulk")) { - obj.weight_bulk = parseWeightBulk(location + "/weight-bulk", value["weight-bulk"], errors); - } - else { - obj.weight_bulk = 128; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateServiceWireguardOverlay(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -10212,261 +8295,6 @@ function instantiateServiceGps(location, value, errors) { return value; } -function instantiateServiceDhcpRelay(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseSelectPorts(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "select-ports")) { - obj.select_ports = parseSelectPorts(location + "/select-ports", value["select-ports"], errors); - } - - function parseVlans(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseVlan(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "vlan")) { - obj.vlan = parseVlan(location + "/vlan", value["vlan"], errors); - } - - function parseRelayServer(location, value, errors) { - if (type(value) == "string") { - if (!matchUcIp(value)) - push(errors, [ location, "must be a valid IPv4 or IPv6 address" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "relay-server")) { - obj.relay_server = parseRelayServer(location + "/relay-server", value["relay-server"], errors); - } - - function parseCircuitIdFormat(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "vlan-id", "ap-mac", "ssid" ])) - push(errors, [ location, "must be one of \"vlan-id\", \"ap-mac\" or \"ssid\"" ]); - - return value; - } - - if (exists(value, "circuit-id-format")) { - obj.circuit_id_format = parseCircuitIdFormat(location + "/circuit-id-format", value["circuit-id-format"], errors); - } - else { - obj.circuit_id_format = "vlan-id"; - } - - function parseRemoteIdFormat(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "vlan-id", "ap-mac", "ssid" ])) - push(errors, [ location, "must be one of \"vlan-id\", \"ap-mac\" or \"ssid\"" ]); - - return value; - } - - if (exists(value, "remote-id-format")) { - obj.remote_id_format = parseRemoteIdFormat(location + "/remote-id-format", value["remote-id-format"], errors); - } - else { - obj.remote_id_format = "ap-mac"; - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "vlans")) { - obj.vlans = parseVlans(location + "/vlans", value["vlans"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceAdminUi(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseWifiSsid(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 32) - push(errors, [ location, "must be at most 32 characters long" ]); - - if (length(value) < 1) - push(errors, [ location, "must be at least 1 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "wifi-ssid")) { - obj.wifi_ssid = parseWifiSsid(location + "/wifi-ssid", value["wifi-ssid"], errors); - } - else { - obj.wifi_ssid = "Maverick"; - } - - function parseWifiKey(location, value, errors) { - if (type(value) == "string") { - if (length(value) > 63) - push(errors, [ location, "must be at most 63 characters long" ]); - - if (length(value) < 8) - push(errors, [ location, "must be at least 8 characters long" ]); - - } - - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - return value; - } - - if (exists(value, "wifi-key")) { - obj.wifi_key = parseWifiKey(location + "/wifi-key", value["wifi-key"], errors); - } - - function parseWifiBands(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "2G", "5G", "5G-lower", "5G-upper", "6G" ])) - push(errors, [ location, "must be one of \"2G\", \"5G\", \"5G-lower\", \"5G-upper\" or \"6G\"" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "wifi-bands")) { - obj.wifi_bands = parseWifiBands(location + "/wifi-bands", value["wifi-bands"], errors); - } - - function parseOfflineTrigger(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "offline-trigger")) { - obj.offline_trigger = parseOfflineTrigger(location + "/offline-trigger", value["offline-trigger"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - -function instantiateServiceRrm(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseBeaconRequestAssoc(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "beacon-request-assoc")) { - obj.beacon_request_assoc = parseBeaconRequestAssoc(location + "/beacon-request-assoc", value["beacon-request-assoc"], errors); - } - else { - obj.beacon_request_assoc = true; - } - - function parseStationStatsInterval(location, value, errors) { - if (!(type(value) in [ "int", "double" ])) - push(errors, [ location, "must be of type number" ]); - - return value; - } - - if (exists(value, "station-stats-interval")) { - obj.station_stats_interval = parseStationStatsInterval(location + "/station-stats-interval", value["station-stats-interval"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateServiceTelnet(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -10574,10 +8402,6 @@ function instantiateService(location, value, errors) { obj.igmp = instantiateServiceIgmp(location + "/igmp", value["igmp"], errors); } - if (exists(value, "ieee8021x")) { - obj.ieee8021x = instantiateServiceIeee8021x(location + "/ieee8021x", value["ieee8021x"], errors); - } - if (exists(value, "radius-proxy")) { obj.radius_proxy = instantiateServiceRadiusProxy(location + "/radius-proxy", value["radius-proxy"], errors); } @@ -10590,46 +8414,18 @@ function instantiateService(location, value, errors) { obj.data_plane = instantiateServiceDataPlane(location + "/data-plane", value["data-plane"], errors); } - if (exists(value, "wifi-steering")) { - obj.wifi_steering = instantiateServiceWifiSteering(location + "/wifi-steering", value["wifi-steering"], errors); - } - if (exists(value, "quality-of-service")) { obj.quality_of_service = instantiateServiceQualityOfService(location + "/quality-of-service", value["quality-of-service"], errors); } - if (exists(value, "facebook-wifi")) { - obj.facebook_wifi = instantiateServiceFacebookWifi(location + "/facebook-wifi", value["facebook-wifi"], errors); - } - - if (exists(value, "airtime-fairness")) { - obj.airtime_fairness = instantiateServiceAirtimeFairness(location + "/airtime-fairness", value["airtime-fairness"], errors); - } - if (exists(value, "wireguard-overlay")) { obj.wireguard_overlay = instantiateServiceWireguardOverlay(location + "/wireguard-overlay", value["wireguard-overlay"], errors); } - if (exists(value, "captive")) { - obj.captive = instantiateServiceCaptive(location + "/captive", value["captive"], errors); - } - if (exists(value, "gps")) { obj.gps = instantiateServiceGps(location + "/gps", value["gps"], errors); } - if (exists(value, "dhcp-relay")) { - obj.dhcp_relay = instantiateServiceDhcpRelay(location + "/dhcp-relay", value["dhcp-relay"], errors); - } - - if (exists(value, "admin-ui")) { - obj.admin_ui = instantiateServiceAdminUi(location + "/admin-ui", value["admin-ui"], errors); - } - - if (exists(value, "rrm")) { - obj.rrm = instantiateServiceRrm(location + "/rrm", value["rrm"], errors); - } - if (exists(value, "telnet")) { obj.telnet = instantiateServiceTelnet(location + "/telnet", value["telnet"], errors); } @@ -10674,8 +8470,8 @@ function instantiateMetricsStatistics(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); - if (!(value in [ "ssids", "lldp", "clients", "tid-stats" ])) - push(errors, [ location, "must be one of \"ssids\", \"lldp\", \"clients\" or \"tid-stats\"" ]); + if (!(value in [ "lldp", "clients" ])) + push(errors, [ location, "must be one of \"lldp\" or \"clients\"" ]); return value; } @@ -10799,44 +8595,6 @@ function instantiateMetricsHealth(location, value, errors) { return value; } -function instantiateMetricsWifiFrames(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseFilters(location, value, errors) { - if (type(value) == "array") { - function parseItem(location, value, errors) { - if (type(value) != "string") - push(errors, [ location, "must be of type string" ]); - - if (!(value in [ "probe", "auth", "assoc", "disassoc", "deauth", "local-deauth", "inactive-deauth", "key-mismatch", "beacon-report", "radar-detected" ])) - push(errors, [ location, "must be one of \"probe\", \"auth\", \"assoc\", \"disassoc\", \"deauth\", \"local-deauth\", \"inactive-deauth\", \"key-mismatch\", \"beacon-report\" or \"radar-detected\"" ]); - - return value; - } - - return map(value, (item, i) => parseItem(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "filters")) { - obj.filters = parseFilters(location + "/filters", value["filters"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateMetricsDhcpSnooping(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -10875,52 +8633,6 @@ function instantiateMetricsDhcpSnooping(location, value, errors) { return value; } -function instantiateMetricsWifiScan(location, value, errors) { - if (type(value) == "object") { - let obj = {}; - - function parseInterval(location, value, errors) { - if (type(value) != "int") - push(errors, [ location, "must be of type integer" ]); - - return value; - } - - if (exists(value, "interval")) { - obj.interval = parseInterval(location + "/interval", value["interval"], errors); - } - - function parseVerbose(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "verbose")) { - obj.verbose = parseVerbose(location + "/verbose", value["verbose"], errors); - } - - function parseInformationElements(location, value, errors) { - if (type(value) != "bool") - push(errors, [ location, "must be of type boolean" ]); - - return value; - } - - if (exists(value, "information-elements")) { - obj.information_elements = parseInformationElements(location + "/information-elements", value["information-elements"], errors); - } - - return obj; - } - - if (type(value) != "object") - push(errors, [ location, "must be of type object" ]); - - return value; -} - function instantiateMetricsTelemetry(location, value, errors) { if (type(value) == "object") { let obj = {}; @@ -10942,8 +8654,8 @@ function instantiateMetricsTelemetry(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); - if (!(value in [ "ssh", "health", "health.dns", "health.dhcp", "health.radius", "health.memory", "client", "client.join", "client.leave", "client.key-mismatch", "wifi", "wifi.start", "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", "unit", "unit.boot-up" ])) - push(errors, [ location, "must be one of \"ssh\", \"health\", \"health.dns\", \"health.dhcp\", \"health.radius\", \"health.memory\", \"client\", \"client.join\", \"client.leave\", \"client.key-mismatch\", \"wifi\", \"wifi.start\", \"wifi.stop\", \"wired\", \"wired.carrier-up\", \"wired.carrier-down\", \"unit\" or \"unit.boot-up\"" ]); + if (!(value in [ "ssh", "health", "health.dns", "health.dhcp", "health.radius", "health.memory", "client", "client.join", "client.leave", "client.key-mismatch", "wired", "wired.carrier-up", "wired.carrier-down", "unit", "unit.boot-up" ])) + push(errors, [ location, "must be one of \"ssh\", \"health\", \"health.dns\", \"health.dhcp\", \"health.radius\", \"health.memory\", \"client\", \"client.join\", \"client.leave\", \"client.key-mismatch\", \"wired\", \"wired.carrier-up\", \"wired.carrier-down\", \"unit\" or \"unit.boot-up\"" ]); return value; } @@ -10980,8 +8692,8 @@ function instantiateMetricsRealtime(location, value, errors) { if (type(value) != "string") push(errors, [ location, "must be of type string" ]); - if (!(value in [ "ssh", "health", "health.dns", "health.dhcp", "health.radius", "health.memory", "client", "client.join", "client.leave", "client.key-mismatch", "wifi", "wifi.start", "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", "unit", "unit.boot-up" ])) - push(errors, [ location, "must be one of \"ssh\", \"health\", \"health.dns\", \"health.dhcp\", \"health.radius\", \"health.memory\", \"client\", \"client.join\", \"client.leave\", \"client.key-mismatch\", \"wifi\", \"wifi.start\", \"wifi.stop\", \"wired\", \"wired.carrier-up\", \"wired.carrier-down\", \"unit\" or \"unit.boot-up\"" ]); + if (!(value in [ "ssh", "health", "health.dns", "health.dhcp", "health.radius", "health.memory", "client", "client.join", "client.leave", "client.key-mismatch", "wired", "wired.carrier-up", "wired.carrier-down", "unit", "unit.boot-up" ])) + push(errors, [ location, "must be one of \"ssh\", \"health\", \"health.dns\", \"health.dhcp\", \"health.radius\", \"health.memory\", \"client\", \"client.join\", \"client.leave\", \"client.key-mismatch\", \"wired\", \"wired.carrier-up\", \"wired.carrier-down\", \"unit\" or \"unit.boot-up\"" ]); return value; } @@ -11020,18 +8732,10 @@ function instantiateMetrics(location, value, errors) { obj.health = instantiateMetricsHealth(location + "/health", value["health"], errors); } - if (exists(value, "wifi-frames")) { - obj.wifi_frames = instantiateMetricsWifiFrames(location + "/wifi-frames", value["wifi-frames"], errors); - } - if (exists(value, "dhcp-snooping")) { obj.dhcp_snooping = instantiateMetricsDhcpSnooping(location + "/dhcp-snooping", value["dhcp-snooping"], errors); } - if (exists(value, "wifi-scan")) { - obj.wifi_scan = instantiateMetricsWifiScan(location + "/wifi-scan", value["wifi-scan"], errors); - } - if (exists(value, "telemetry")) { obj.telemetry = instantiateMetricsTelemetry(location + "/telemetry", value["telemetry"], errors); } @@ -11135,10 +8839,6 @@ function newUCentralState(location, value, errors) { obj.globals = instantiateGlobals(location + "/globals", value["globals"], errors); } - if (exists(value, "definitions")) { - obj.definitions = instantiateDefinitions(location + "/definitions", value["definitions"], errors); - } - function parseEthernet(location, value, errors) { if (type(value) == "array") { return map(value, (item, i) => instantiateEthernet(location + "/" + i, item, errors)); @@ -11158,21 +8858,6 @@ function newUCentralState(location, value, errors) { obj.switch = instantiateSwitch(location + "/switch", value["switch"], errors); } - function parseRadios(location, value, errors) { - if (type(value) == "array") { - return map(value, (item, i) => instantiateRadio(location + "/" + i, item, errors)); - } - - if (type(value) != "array") - push(errors, [ location, "must be of type array" ]); - - return value; - } - - if (exists(value, "radios")) { - obj.radios = parseRadios(location + "/radios", value["radios"], errors); - } - function parseInterfaces(location, value, errors) { if (type(value) == "array") { return map(value, (item, i) => instantiateInterface(location + "/" + i, item, errors)); diff --git a/state/interface.ssid.association.yml b/state/interface.ssid.association.yml deleted file mode 100644 index d969cb9..0000000 --- a/state/interface.ssid.association.yml +++ /dev/null @@ -1,126 +0,0 @@ -type: array -description: - A list all OBSS that this BSS is connected to. The contained entries - depend opertational mode. - * AP - all associated stationd - * STA - the remote AP - * mesh - all mesh peers that we can see - * wds - the remote wds peer -items: - description: An explanation about the purpose - of this instance. - properties: - bssid: - type: string - description: - The BSSID of the remote BSS. - station: - type: string - description: - The MAC address of the station. - dynamic_vlan: - type: number - description: - The VID in case the association is on a dynamic VLAN. - rssi: - type: number - description: - The RX signal strength of the remore BSS. (dBm) - connected: - type: number - description: - The time since connected - inactive: - type: number - description: - The time since inactive - tx_duration: - type: number - description: - The tx duration - rx_duration: - type: number - description: - The rx duration - rx_bytes: - type: number - description: - The number of bytes received. - ack_signal: - type: number - ack_signal_avg: - type: number - rx_packets: - type: number - description: - The number of packets received. - tx_packets: - type: number - description: - The number of packets received. - tx_bytes: - type: number - description: - The number of bytes transmitted. - tx_retries: - type: number - tx_failed: - type: number - description: - The number of packets transmitted. - rx_rate: - type: array - items: - properties: - bitrate: - type: number - sgi: - type: boolean - vht: - type: boolean - mcs: - type: number - nss: - type: number - chwidth: - type: number - he: - type: boolean - he_gi: - type: number - he_dcm: - type: number - tx_rate: - type: array - items: - properties: - bitrate: - type: number - sgi: - type: boolean - vht: - type: boolean - mcs: - type: number - nss: - type: number - chwidth: - type: number - he: - type: boolean - he_gi: - type: number - he_dcm: - type: number - tid_stats: - type: array - items: - properties: - rx_msdu: - type: number - tx_msdu: - type: number - tx_msdu_retries: - type: number - tx_msdu_failed: - type: number diff --git a/state/interface.ssid.yml b/state/interface.ssid.yml deleted file mode 100644 index 8fb37a2..0000000 --- a/state/interface.ssid.yml +++ /dev/null @@ -1,66 +0,0 @@ -type: array -items: - type: object - description: - This section contains the current state of a SSID being brodcasted or - connected to by this device. - properties: - bssid: - type: string - description: - The BSSID used by this BSS instance. - location: - type: string - description: - The json-schema path within the configuration where this logical interface is located. - phy: - type: string - description: - The unique path identifying the radio - band: - description: - The wireless band that the radio is currently operating on. - type: string - ssid: - type: string - description: - The SSID that we are broadcasting or connecting to. - iface: - type: string - description: - The physical network device used. - vlan_ifaces: - description: - The list of dynamic vlan interfaces. - type: object - patternProperties: - ^wlan-v: - type: object - $ref: "https://ucentral.io/state/v1/interface/counter/" - mode: - type: string - description: - The operational mode of the BSS. - enum: - - ap - - sta - - mesh - - wds - radio: - type: object - description: - A reference to the section describing the physical radio within this - document. (#/radios/X) - properties: - ref: - type: string - frequency: - description: - The list of all frequencies that the radio is currently operating on. - type: array - items: - type: number - associations: - $ref: "https://ucentral.io/state/v1/interface/ssid/association/" - counters: - $ref: "https://ucentral.io/state/v1/interface/counter/" diff --git a/state/interface.yml b/state/interface.yml index 83e38b8..7703dc6 100644 --- a/state/interface.yml +++ b/state/interface.yml @@ -42,8 +42,6 @@ items: $ref: "https://ucentral.io/state/v1/interface/counter/" mesh-path: $ref: "https://ucentral.io/state/v1/interface/mesh-path/" - ssids: - $ref: "https://ucentral.io/state/v1/interface/ssid/" multicast: type: object description: @@ -188,4 +186,4 @@ items: - "Ethernet1" - "Unit-1/Port-2" - "1-2" - - "Trunk 1" \ No newline at end of file + - "Trunk 1" diff --git a/state/radio.yml b/state/radio.yml deleted file mode 100644 index e029aac..0000000 --- a/state/radio.yml +++ /dev/null @@ -1,51 +0,0 @@ -type: array -description: - An array containing the current operational state of all wifi radios that the - unit has. -items: - type: object - properties: - channel: - description: - The primary channel that the radio is currently operating on. - type: number - channels: - description: - The list of all channels that the radio is currently operating on. - type: array - items: - type: number - frequency: - description: - The list of all frequencies that the radio is currently operating on. - type: array - items: - type: number - channel_width: - type: number - description: - The channel width currently being used. - enum: - - 20 - - 40 - - 80 - - 160 - - 320 - tx_power: - type: number - description: - The currently configure TX power of the radio in dBm. - phy: - type: string - description: - The unique path identifying the radio - band: - description: - The wireless band that the radio is currently operating on. - type: array - items: - type: string - temperature: - description: - The current temperature of the Wireless radio. - type: number diff --git a/state/state.yml b/state/state.yml index c8c16c1..e0b3b34 100644 --- a/state/state.yml +++ b/state/state.yml @@ -18,8 +18,6 @@ properties: $ref: "https://ucentral.io/state/v1/unit/" gps: $ref: "https://ucentral.io/state/v1/gps/" - radios: - $ref: "https://ucentral.io/state/v1/radio/" interfaces: $ref: "https://ucentral.io/state/v1/interface/" lldp-peers: diff --git a/system/capabilities.uc b/system/capabilities.uc deleted file mode 100755 index 2bb2bbf..0000000 --- a/system/capabilities.uc +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/ucode -push(REQUIRE_SEARCH_PATH, - "/usr/lib/ucode/*.so", - "/usr/share/ucentral/*.uc"); - -let ubus = require("ubus"); -let fs = require("fs"); - -let boardfile = fs.open("/etc/board.json", "r"); -let board = json(boardfile.read("all")); -boardfile.close(); -let restrictfile = fs.open("/etc/ucentral/restrictions.json", "r"); - -capa = { - 'secure-rtty': true -}; -if (restrictfile) { - capa.restrictions = json(restrictfile.read("all")) || {}; - let pipe = fs.popen('fw_printenv developer'); - let developer = replace(pipe.read("all"), '\n', ''); - pipe.close(); - if (developer == 'developer=1') - capa.developer = true; - else - capa.developer = false; -} -ctx = ubus.connect(); -let wifi = require("wifi.phy"); -capa.compatible = replace(board.model.id, ',', '_'); -capa.model = board.model.name; - -if (board.bridge && board.bridge.name == "switch") - capa.platform = "switch"; -else if (length(wifi)) - capa.platform = "ap"; -else - capa.platform = "unknown"; - -if (board.switch) { - capa.switch = []; - capa.switch_ports = {}; - for (let name, s in board.switch) { - let device = { name, lan: [], wan: [] }; - let netdev; - for (let p in s.ports) { - if (p.device) { - netdev = p.device; - device.port = p.num; - } else if (device[p.role]) { - push(device[p.role], p.num) - } - } - if (!length(device.lan)) - delete device.lan; - if (!length(device.wan)) - delete device.wan; - if (netdev) - capa.switch_ports[netdev] = device; - push(capa.switch, { name, enable: s.enable, reset: s.reset }); - } -} - -function swconfig_ports(device, role) { - let netdev = split(device, '.')[0]; - let switch_dev = capa.switch_ports ? capa.switch_ports[netdev] : null; - if (!switch_dev || !switch_dev[role]) - return [ device ]; - let rv = []; - for (let port in switch_dev[role]) - push(rv, netdev + ':' + port); - return rv; -} - -capa.network = {}; -macs = {}; -for (let k, v in board.network) { - if (v.ports) - capa.network[k] = v.ports; - if (v.device) - capa.network[k] = swconfig_ports(v.device, k); - if (v.ifname) - capa.network[k] = split(replace(v.ifname, /^ */, ''), " "); - if (v.macaddr) - macs[k] = v.macaddr; -} - -if (length(macs)) - capa.macaddr = macs; - -if (board.wifi?.country) - capa.country_code = split(board.wifi.country, ' '); - -if (board.system?.label_macaddr) - capa.label_macaddr = board.system?.label_macaddr; - -if (length(wifi)) - capa.wifi = wifi; - -capafile = fs.open("/etc/ucentral/capabilities.json", "w"); -capafile.write(capa); -capafile.close(); diff --git a/system/health.uc b/system/health.uc deleted file mode 100755 index b3faf14..0000000 --- a/system/health.uc +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/ucode -push(REQUIRE_SEARCH_PATH, '/usr/share/ucentral/*.uc'); -let fs = require('fs'); -let uci = require('uci'); -let ubus = require('ubus'); - -state = { - unit: {}, - interfaces: {} -}; - -let ctx = ubus.connect(); -let interfaces = ctx.call('network.interface', 'dump').interface; -let cursor = uci.cursor(); -cursor.load('health'); -cursor.load('dhcp'); -cursor.load('network'); -cursor.load('wireless'); -let config = cursor.get_all('state', 'health'); -let dhcp = cursor.get_all('dhcp'); -let wifi_config = cursor.get_all('wireless'); -let wifi_state = require('wifi.iface'); -let count = 0; - -function find_ssid(ssid) { - for (let name, iface in wifi_state) - if (ssid == iface.ssid) - return 0; - return 1; -} - -function radius_probe(server, port, secret, user, pass) -{ - let f = fs.open('/tmp/radius.conf', 'w'); - if (f) { - f.write(sprintf('authserver %s:%d\n', server, port)); - f.write('servers /tmp/radius.servers\n'); - f.write('dictionary /etc/radcli/dictionary\n'); - f.write('radius_timeout 3\n'); - f.write('radius_retries 1\n'); - f.write('bindaddr *\n'); - f.close(); - } - - let f = fs.open('/tmp/radius.servers', 'w'); - if (f) { - f.write(sprintf('%s %s\n', server, secret)); - f.close(); - } - return system(['/usr/sbin/radiusprobe', user, pass]); -} - -for (let iface in interfaces) { - let name = iface.interface; - if (name == 'loopback') - continue; - - count++; - - let health = {}; - let ssid = {}; - let radius = {}; - let device = iface.l3_device || iface.interface; - let warnings = []; - - let probe_dhcp = cursor.get('network', iface.interface, 'dhcp_healthcheck') || false; - - if (dhcp[name]?.leasetime && +config.dhcp_local) - probe_dhcp = true; - - if (iface?.data.leasetime && +config.dhcp_remote) - probe_dhcp = true; - - if (probe_dhcp) { - let rc = system(['/usr/sbin/dhcpdiscover', '-i', device, '-t', '5']); - if (rc) { - health.dhcp = false; - push(warnings, 'DHCP did not offer any leases'); - ctx.call('event', 'event', { object: 'health', verb: 'dhcp', payload: { iface: name, error: 'DHCP did not offer any leases' }}); - } - } - - let probe_dns = false; - - if (length(iface['dns-server']) && +config.dns_remote) - probe_dns = true; - - if (dhcp[name]?.dns_service && +config.dns_local) - probe_dns = true; - - if (probe_dns) { - let dns = iface['dns-server']; - if (!length(dns) && iface['ipv4-address'] && iface['ipv4-address'][0]) - dns = [ iface['ipv4-address'][0]['address'] ]; - - for (let ip in dns) { - let rc = system(['/usr/sbin/dnsprobe', '-s', ip]); - - if (rc) { - health.dns = false; - push(warnings, `DNS ${ip} is not reachable`); - ctx.call('event', 'event', { object: 'health', verb: 'dns', payload: { iface: name, error: `DNS ${ip} is not reachable.` }}); - } - } - } - - for (let k, iface in wifi_config) { - if (iface['.type'] != 'wifi-iface' || iface.network != name) - continue; - if (find_ssid(iface.ssid)) - ssid[iface.ssid] = false; - if (iface.auth_server && iface.auth_port && iface.auth_secret && iface.health_username && iface.health_password && !iface.radius_gw_proxy) - if (radius_probe(iface.auth_server, iface.auth_port, iface.auth_secret, iface.health_username, iface.health_password)) { - radius[iface.ssid] = false; - push(warnings, sprintf('Radius %s:%s is not reachable', iface.auth_server, iface.auth_port)); - ctx.call('event', 'event', { object: 'health', verb: 'radius', payload: { ssid: iface.ssid, error: `Radius ${iface.auth_server}:${iface.auth_port} is not reachable` }}); - } - } - - if (length(ssid)) - health.ssids = ssid; - - if (length(radius)) - health.radius = radius; - - if (length(health)) { - health.location= cursor.get('network', name, 'ucentral_path'); - if (length(warnings)) - health.warning = warnings; - state.interfaces[name] = health; - } -} - -try { - memory = ctx.call('system', 'info'); - memory = memory.memory; - state.unit.memory = 100 - (memory.available * 100 / memory.total); - if (state.unit.memory >= 90) - ctx.call('event', 'event', { object: 'health', verb: 'memory', payload: { used: state.unit.memory, error: 'Memory is almost exhausted.' }}); -} -catch(e) { - log('Failed to invoke memory probing: %s\n', e); -} - -let errors = length(state.interfaces); -if (!errors) - delete state.interfaces; - -let sanity = 100 - (errors * 100 / count); - -warn(printf('health check reports sanity of %d', sanity)); -ctx.call('ucentral', 'health', {sanity: sanity, data: state}); diff --git a/system/ip-collide.uc b/system/ip-collide.uc deleted file mode 100755 index 2eb512a..0000000 --- a/system/ip-collide.uc +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/ucode - -let uci = require("uci").cursor(); -let ubus = require("ubus").connect(); -let status = ubus.call("network.interface", "dump"); -let up = []; -let down = []; -let collision = false; - -let ipcalc = { - used_prefixes: [], - - convert_bits_to_mask: function(bits) { - let width = 32, - mask = []; - - bits = width - bits; - - for (let i = width / 8; i > 0; i--) { - let b = (bits < 8) ? bits : 8; - mask[i - 1] = ~((1 << b) - 1) & 0xff; - bits -= b; - } - - return mask; - }, - - apply_mask: function(addr, mask) { - return map(addr, (byte, i) => byte & mask[i]); - }, - - is_intersecting_prefix: function(addr1, bits1, addr2, bits2) { - let mask = this.convert_bits_to_mask((bits1 < bits2) ? bits1 : bits2, length(addr1) == 16); - - for (let i = 0; i < length(addr1); i++) - if ((addr1[i] & mask[i]) != (addr2[i] & mask[i])) - return false; - - return true; - }, - - add_amount: function(addr, amount) { - for (let i = length(addr); i > 0; i--) { - let t = addr[i - 1] + amount; - addr[i - 1] = t & 0xff; - amount = t >> 8; - } - - return addr; - }, - - reserve_prefix: function(addr, mask) { - addr = split(addr, "."); - for (let i = 0; i < length(this.used_prefixes); i += 2) { - let addr2 = this.used_prefixes[i + 0], - mask2 = this.used_prefixes[i + 1]; - - // printf('reserve_prefix %.J %J\n', addr2, addr); - if (length(addr2) != length(addr)) - continue; - - if (this.is_intersecting_prefix(addr, mask, addr2, mask2)) - return false; - } - - push(this.used_prefixes, addr, mask); - - return true; - }, - - generate_prefix: function(available, template) { - let prefix = match(template, /^(auto|[0-9a-fA-F:.]+)\/([0-9]+)$/); - - if (prefix && prefix[1] == 'auto') { - let pool = match(available, /^([0-9a-fA-F:.]+)\/([0-9]+)$/); - - if (prefix[2] < pool[2]) { - printf("Interface IPv4 prefix size exceeds available allocation pool size"); - return NULL; - } - - let available_prefixes = 1 << (prefix[2] - pool[2]), - prefix_mask = this.convert_bits_to_mask(prefix[2]), - address_base = iptoarr(pool[1]); - - // printf("generate %.J %.J\n", pool[1], address_base); - - for (let offset = 0; offset < available_prefixes; offset++) { - if (this.reserve_prefix(pool[1], prefix[2])) { - this.add_amount(address_base, 1); - - return arrtoip(address_base) + '/' + prefix[2]; - } - - for (let i = length(address_base), carry = 1; i > 0; i--) { - let t = address_base[i - 1] + (~prefix_mask[i - 1] & 0xff) + carry; - address_base[i - 1] = t & 0xff; - carry = t >> 8; - } - } - - return NULL; - } - - return template; - }, -}; - -uci.load("network"); - -for (let iface in status.interface) { - if (!iface.up || !length(iface['ipv4-address'])) - continue; - let role = split(iface.device, /[[:digit:]]/); - switch (role[0]) { - case 'up': - push(up, iface); - break; - case 'down': - push(down, iface); - break; - } -} - -for (let iface in up) - for (let addr in iface['ipv4-address']) - ipcalc.reserve_prefix(addr.address, addr.mask); - -for (let iface in down) - for (let addr in iface['ipv4-address']) - if (!ipcalc.reserve_prefix(addr.address, addr.mask)) { - let auto = ipcalc.generate_prefix('192.168.0.0/16', 'auto/' + addr.mask, false); - system(sprintf("logger ip-collide: collision detected on %s\n", iface.device)); - if (auto) { - system(sprintf('logger ip-collide: moving from %s/%d to %s\n', addr.address, addr.mask, auto)); - uci.set('network', iface.device, 'ipaddr', auto); - } else { - system(sprintf('logger ip-collide: no free address available, shutting down device\n')); - system(sprintf('ifconfig %s down', iface.device)); - } - uci.set('network', iface.device, 'collision', time()); - collision = true; - } - -if (collision) { - uci.commit(); - system('reload_config'); -} diff --git a/system/onlinecheck.uc b/system/onlinecheck.uc deleted file mode 100755 index 1064c02..0000000 --- a/system/onlinecheck.uc +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/ucode -let fs = require("fs"); -let uci = require("uci"); -let cursor = uci.cursor(); - -cursor.load("onlinecheck"); -let config = cursor.get_all("onlinecheck", "@config[-1]"); - -let state_file = fs.open("/tmp/onlinecheck.state", "r"); -let state = state_file ? json(state_file.read("all")) : { - online: true, - threshold: 0, -}; -if (state_file) - state_file.close(); - -let online_count = 0; -let online_success = 0; - -for (let host in config.ping_hosts) { - printf("ping %s\n", host); - online_count++; - - let result = system("ping -c 1 -w 3 " + host); - - if (!result) - online_success++; -} - -for (let host in config.download_hosts) { - printf("download %s\n", host); - online_count++; - - let result = system(sprintf("/usr/bin/curl -m 3 http://%s/online.txt -o /tmp/onlinecheck.tmp", host)); - - printf("result %d\n", result); - - if (result) - continue; - let online_file = fs.open("/tmp/onlinecheck.tmp"); - let ok = online_file.read("all") || ''; - online_file.close(); - if (split(ok, '\n')[0] == 'ok') - online_success++; -} - -printf("%d/%d checks have passed\n", online_success, online_count); - -if (!online_success) - state.threshold = state.threshold + 1; -else - state.threshold = 0; - -if (state.threshold >= config.check_threshold && state.online) { - if (index(config.action, "wifi") >= 0) - system("wifi down"); - if (index(config.action, "leds") >= 0) - system("/etc/init.d/led blink"); - - state.online = false; - system("logger onlinecheck: going offline\n") - -} else if (!state.threshold && !state.online) { - if (index(config.action, "wifi") >= 0) - system("wifi up"); - if (index(config.action, "leds") >= 0) - system("/etc/init.d/led turnon"); - state.online = true; - system("logger onlinecheck: going online\n") -} - -let state_file = fs.open("/tmp/onlinecheck.state", "w"); -state_file.write(state); -state_file.close(); - - diff --git a/system/probe_services.uc b/system/probe_services.uc deleted file mode 100755 index 95df822..0000000 --- a/system/probe_services.uc +++ /dev/null @@ -1,68 +0,0 @@ -let fail = {}, -dhcp = {}, -dns = { localhost: [ "127.0.0.1" ] }; - -function lookup_wan() { - let i = ctx.call("network.interface.wan", "status"); - - if (i["dns-server"]) - dns.wan = i["dns-server"]; - - if (i["l3_device"]) - dhcp.wan = i["l3_device"]; -} - -function lookup_ip(iface) { - let i = ctx.call("network.interface", "status", { interface: iface }); - - if (i["ipv4-address"] && i["ipv4-address"][0]) - dns[iface] = [ i["ipv4-address"][0]["address"] ]; - - if (i["l3_device"]) - dhcp[iface] = i["l3_device"]; -} - -function service_probe() { - lookup_wan(); - - let cursor = uci.cursor(); - - cursor.load("dhcp"); - cursor.foreach("dhcp", "dhcp", function(d) { - if (d.ignore != 1) - lookup_ip(d.interface); - }); - - for (let iface, ips in dns) { - let failed = true; - - for (let ip in ips) { - let rc = system(['/usr/sbin/dnsprobe', '-s', ip]); - - if (rc == 0) - failed = false; - } - - if (!failed) - continue; - - fail.dns = fail.dns || {}; - fail.dns[iface] = ips; - } - - for (let iface, dev in dhcp) { - let rc = system(['/usr/sbin/dhcpdiscover', '-i', dev, '-t', '5']); - - if (rc != 0) { - fail.dhcp = fail.dhcp || {}; - fail.dhcp[iface] = dev; - } - } - - return fail; -} - -let services = service_probe(); - -if (length(services)) - state.serviceprobe = services; diff --git a/system/reboot_cause.uc b/system/reboot_cause.uc deleted file mode 100644 index 84b3621..0000000 --- a/system/reboot_cause.uc +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/ucode -let fs = require('fs'); -let pmsg = fs.open('/dev/pmsg0', 'w'); -pmsg.write({ boot_cause: reason ? reason : ARGV[0] }); -pmsg.close(); diff --git a/system/state.uc b/system/state.uc deleted file mode 100755 index 8b5e6d1..0000000 --- a/system/state.uc +++ /dev/null @@ -1,766 +0,0 @@ -#!/usr/bin/ucode -push(REQUIRE_SEARCH_PATH, '/usr/share/ucentral/*.uc'); -let fs = require("fs"); - -let uci = require("uci"); -let ubus = require("ubus"); -let cfgfile = fs.open("/etc/ucentral/ucentral.active", "r"); -let cfg = json(cfgfile.read("all")); -let capabfile = fs.open("/etc/ucentral/capabilities.json", "r"); -let capab = json(capabfile.read("all")); - -/* set up basic functionality */ -if (!cursor) - cursor = uci.cursor(); -if (!ctx) - ctx = ubus.connect(); - -let state = { - unit: { memory: {} }, - radios: [], - interfaces: [] -}; - -function discover_ports() { - let roles = {}; - - /* Derive ethernet port names and roles from default config */ - for (let role, spec in capab.network) { - for (let i, ifname in spec) { - role = uc(role); - push(roles[role] = roles[role] || [], { - netdev: ifname, - index: i - }); - } - } - - /* Sort ports in each role group according to their index, then normalize - * names into uppercase role name with 1-based index suffix in case of multiple - * ports or just uppercase role name in case of single ports */ - let rv = {}; - - for (let role, ports in roles) { - switch (length(ports)) { - case 0: - break; - - case 1: - rv[role] = ports[0]; - break; - - default: - map(sort(ports, (a, b) => (a.index - b.index)), (port, i) => { - rv[role + (i + 1)] = port; - }); - } - } - - return rv; -} - -let select_ports = discover_ports(); - -function lookup_port(netdev) { - for (let k, v in select_ports) - if (v.netdev == netdev) - return k; - return 'unknown'; -} - -/* find out what telemetry we should gather */ -let stats; -if (!length(stats)) { - cursor.load("state"); - stats = cursor.get_all("state", "stats"); -} - -let delta = 1; -if (telemetry) - delta = 0; - -let public_ip_file = "/tmp/public_ip"; -let public_ip = ""; -if (cfg.public_ip_lookup) { - if (!fs.access(public_ip_file)) - system(sprintf("/usr/bin/curl -m 3 %s -o %s", cfg.public_ip_lookup, public_ip_file)); - let online_file = fs.open(public_ip_file); - public_ip = online_file.read("all") || ''; - online_file.close(); -} - -global.tid_stats = (index(stats.types, 'tid-stats') > 0); - -/* load state data */ -let ipv6leases = ctx.call("dhcp", "ipv6leases"); -let topology = ctx.call("topology", "mac"); -let wifistatus = ctx.call("network.wireless", "status"); -let wifiphy = require('wifi.phy'); -let wifiiface = require('wifi.iface'); -let stations = require('wifi.station'); -let survey = require('wifi.survey'); -let mesh = require('wifi.mesh'); -let ports = ctx.call("topology", "port", { delta }); -let poe = ctx.call("poe", "info"); -let gps = ctx.call("gps", "info"); -let devstats = ctx.call('udevstats', 'dump'); -let devices = ctx.call("network.device", "status"); - -let previous = json(fs.readfile('/tmp/' + (telemetry ? 'telemetry.json' : 'state.json')) || '{ "ports": {}, "devstats": {}, "stations": {}, "devstats": {} }'); - -let stations_lookup = {}; -for (let k, v in stations) { - stations_lookup[k] = {}; - for (let assoc in v) - stations_lookup[k][assoc.station] = assoc; -} -fs.writefile('/tmp/' + (telemetry ? 'telemetry.json' : 'state.json'), { ports, devstats, stations: stations_lookup, devstats }); - -//printf('%.J\n', previous); - -let lldp = []; -let wireless = cursor.get_all("wireless"); -let snoop = ctx.call("dhcpsnoop", "dump"); -let captive = ctx.call("spotfilter", "client_list", { "interface": "hotspot"}); - - -function ports_deltas(port) { - if (!ports[port]?.counters || !previous.ports[port]?.counters) - return {}; - - let ret = {}; - for (let k in ports[port].counters) - ret[k] = ports[port].counters[k] - (previous.ports[port]?.counters[k] || 0); - return ret; -} - -function stations_deltas(assoc, iface) { - let ret = {}; - if (!previous.stations[iface] || !previous.stations[iface][assoc.station]) - return ret; - for (let k in [ "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "tx_retries", "tx_failed" ]) - ret[k] = assoc[k] - (previous.stations[iface][assoc.station][k] || 0); - return ret; -} - -/* prepare dhcp leases cache */ -let ip4leases = {}; -try { - let fd = fs.open("/tmp/dhcp.leases"); - if (fd) { - let line; - while (line = fd.read("line")) { - let tokens = split(line, " "); - - if (length(tokens) < 4) - continue; - - ip4leases[tokens[1]] = { - assigned: tokens[0], - mac: tokens[1], - address: tokens[2], - hostname: tokens[3] - }; - } - fd.close(); - } -} -catch(e) { - printf("Failed to parse dhcp leases cache: %s\n%s\n", e, e.stacktrace[0].context); -} - -/* prepare lldp cache */ -try { - let stdout = fs.popen("lldpcli -f json show neighbors"); - let tmp; - if (stdout) { - tmp = json(stdout.read("all")).lldp[0].interface; - stdout.close(); - } else { - printf("LLDP cli command failed: %s", fs.error()); - } - - for (let key, iface in tmp) { - let peer = { }; - for (let host, chassis in iface.chassis) { - if (!length(chassis.id) || - !length(chassis.descr)) - continue; - peer.mac = chassis.id[0].value; - peer.ifname = iface.name; - peer.description = chassis.descr[0].value; - if (iface?.port[0]?.id[0]?.value && iface?.port[0]?.descr[0]?.value) { - peer.port_id = iface.port[0].id[0].value; - peer.port_descr = iface.port[0].descr[0].value; - } - if (length(chassis.name)) - peer.name = chassis.name[0].value; - - if (length(chassis['mgmt-ip'])) { - let ipaddr = []; - - for (let ip in chassis["mgmt-ip"]) - push(ipaddr, ip.value); - peer.management_ips = ipaddr; - } - - if (length(chassis.capability)) { - let cap = []; - - for (let c in chassis.capability) { - if (!c.enabled) - continue; - push(cap, c.type); - } - peer.capability = cap; - } - - } - - if (!length(peer)) - continue; - - push(lldp, peer); - } -} -catch(e) { - printf("Failed to parse LLDP cli output: %s\n%s\n", e, e.stacktrace[0].context); -} - -/* system state */ -let system = ctx.call("system", "info"); -state.unit.localtime = system.localtime; -state.unit.uptime = system.uptime; -state.unit.load = system.load; -state.unit.memory.total = system.memory.total; -state.unit.memory.free = system.memory.free; -state.unit.memory.cached = system.memory.cached; -state.unit.memory.buffered = system.memory.buffered; - -for (let l = 0; l < 3; l++) - state.unit.load[l] /= 65535.0; - -let thermal = fs.glob('/sys/class/thermal/thermal_zone*/temp'); -if (length(thermal) > 0) { - let temps = []; - for (let t in thermal) { - let file = fs.open(t, 'r'); - if (!file) - continue; - let temp = +file.read('all'); - if (temp > 1000) - temp /= 1000; - file.close(); - push(temps, temp); - } - if (length(temps) > 0) { - let avg = 0; - temps = sort(temps); - for (let t in temps) - avg += t; - avg /= length(temps); - state.unit.temperature = [ avg, temps[-1] ]; - } -} - -/* cpu load */ -let fs = require('fs'); - -function sum(arr) { - let rv = 0; - - for (let val in arr) - rv += +val; - return rv; -} - -function cpu_stats() { - let proc = fs.open('/proc/stat', 'r'); - let stats; - - if (proc) { - let line; - stats = []; - while (line = proc.read('line')) { - let cols = split(replace(trim(line), ' ', ' '), ' '); - if (cols[0] == 'btime') - state.unit.boottime = +cols[1]; - if (!wildcard(cols[0], 'cpu*')) - continue; - shift(cols); - push(stats, [ sum(cols), +cols[3] ]); - } - proc.close(); - } - return stats; -} - -let last; -let file = fs.open('/tmp/cpu_load', 'r'); -if (file) { - last = json(file.read('all')); - file.close(); -} - -let now = cpu_stats(); -if (now && last) { - state.unit.cpu_load = []; - for (let i = 0; i < length(now); i++) - //printf('CPU%s %3d\%\n', i ? i : ' ', 100 * (now[i][1] - last[i][1]) / (now[i][0] - last[i][0])); - push(state.unit.cpu_load, 100 - (100 * (now[i][1] - last[i][1]) / (now[i][0] - last[i][0]))); -} -file = fs.open('/tmp/cpu_load', 'w'); -if (file) { - file.write(now); - file.close(); -} - -/* wifi radios */ -for (let radio, data in wifistatus) { - if (!length(data.interfaces)) - continue; - let vap = wifiiface[data.interfaces[0].ifname]; - if (!length(vap)) - continue; - - let radio = {}; - radio.channel = vap.channel[0]; - radio.channels = uniq(vap.channel); - radio.frequency = uniq(vap.frequency); - radio.channel_width = +vap.ch_width; - radio.tx_power = vap.tx_power; - radio.survey = []; - for (let k, v in survey.survey) - if (v.frequency in radio.frequency) - push(radio.survey, v); - delete radio.in_use; - radio.phy = data.config.path; - if (wifiphy[data.config.path] && wifiphy[data.config.path].temperature) - radio.temperature = wifiphy[data.config.path].temperature; - radio.band = wifiphy[data.config.path].band; - push(state.radios, radio); -} -if (!length(state.radios)) - delete state.radios; - - -function iface_add_counters(iface, vlan, port) { - if (!devstats[port]) - return; - for (let k, vid in devstats[port]) { - if (vid.vid != vlan) - continue; - iface.counters.tx_bytes += vid.tx?.bytes || 0; - iface.counters.tx_packets += vid.tx?.packets || 0; - iface.counters.rx_bytes += vid.rx?.bytes || 0; - iface.counters.rx_packets += vid.rx?.packets || 0; - if (previous.devstats[port] && previous.devstats[port][k]) { - iface.delta_counters = {}; - for (let v in [ 'tx_bytes', 'tx_packets', 'rx_bytes', 'rx_packets' ]) - iface.delta_counters[v] = +iface.counters[v] - (+previous.devstats[port][k] || 0); - } - } -} - -function is_mesh(net, wif) { - if (!net.batman) - return false; - if (wif.mode != 'mesh') - return false; - return true; -} - -let idx = 0; -let dyn_vlans = {}; -let dyn_vids = []; -if (devices?.up) for (let k, vlan in devices.up['bridge-vlans']) { - if (vlan.id >= 4000) - continue; - let wlan = []; - for (let port in vlan.ports) { - let dev = split(port, '-v'); - if (+dev[1] != vlan.id) - continue; - if (!dyn_vlans[dev[0]]) - dyn_vlans[dev[0]] = []; - push(dyn_vlans[dev[0]], port); - } -} - -/* interfaces */ -cursor.load("network"); -cursor.foreach("network", "interface", function(d) { - let name = d[".name"]; - if (name == "loopback") - return; - if (index(name, "_") >= 0) - return; - if (!d.ucentral_path) - return; - let role = split(name, /[[:digit:]]/)[0]; - let vlan = split(name, 'v')[1]; - let iface_port; - - let iface = { name, location: d.ucentral_path, ipv4:{}, ipv6:{} }; - let ipv4leases = []; - - push(state.interfaces, iface); - - let status = ctx.call(sprintf("network.interface.%s", name) , "status"); - - if (!length(status)) - return; - - if (devices && length(devices[role]["bridge-members"])) - iface_ports = devices[role]["bridge-members"]; - iface.uptime = status.uptime || 0; - - if (length(status["ipv4-address"])) { - let ipv4 = []; - - for (let a in status["ipv4-address"]) - push(ipv4, sprintf("%s/%d", a.address, a.mask)); - - iface.ipv4.addresses = ipv4; - if( cfg.public_ip_lookup && length(public_ip)) - iface.ipv4.public_ip = public_ip; - } - - if (length(status["ipv6-address"])) { - iface.ipv6.addresses = status["ipv6-address"]; - for (let key, addr in iface.ipv6.addresses) { - if (!addr.mask) - continue; - addr.address = sprintf("%s/%s", addr.address, addr.mask); - delete addr.mask; - } - } - - if (length(status["dns-server"])) - iface.dns_servers = status["dns-server"]; - - if (length(status.data) && status.data.ntpserver) - iface.ntp_server = status.data.ntpserver; - - if (length(status.data) && status.data.leasetime && status.proto == "dhcp") { - iface.ipv4.leasetime = status.data.leasetime; - iface.ipv4.dhcp_server = status.data.dhcpserver; - } - - if (length(ipv6leases) && - length(ipv6leases.device) && - length(ipv6leases.device[status.device]) && - length(ipv6leases.device[status.device].leases)) { - let leases = []; - - for (let l in ipv6leases.device[status.device].leases) { - let lease = {}; - - lease.hostname = l.hostname; - lease.addresses = []; - for (let addr in l["ipv6-addr"]) - push(lease.addresses, addr.address); - push(leases, lease); - } - - if (length(leases)) - iface.ipv6.leases = leases - } - - let macs = []; - - if (length(topology)) { - let clients = []; - - for (let mac, topo in topology) { - if (topo.interface != d[".name"] || - !length(topo.fdb) || - (!length(topo["ipv4"]) && !length(topo["ipv6"]))) - continue; - - let client = {}; - - if (length(ip4leases[mac])) - push(ipv4leases, ip4leases[mac]); - - client.mac = mac; - if (length(topo["ipv4"])) - client.ipv4_addresses = topo["ipv4"]; - else if (snoop && snoop[mac]) - client.ipv4_addresses = [ snoop[mac] ]; - - if (length(topo["ipv6"])) - client.ipv6_addresses = topo["ipv6"]; - - client.ports = []; - for (let k in topo.fdb) - push(client.ports, lookup_port(k)); - client.last_seen = topo.last_seen; - if (index(stats.types, 'clients') >= 0) { - push(clients, client); - push(macs, mac); - } - } - - if (length(ipv4leases)) - iface.ipv4.leases = ipv4leases; - - - if (length(clients)) - iface.clients = clients; - } - - if (index(stats.types, 'ssids') >= 0 && length(wifistatus)) { - let ssids = []; - let counter = 0; - - for (let radio, data in wifistatus) { - for (let k, vap in data.interfaces) { - if (!length(vap.config) || - !length(vap.config.network) || - !wifiiface[vap.ifname]) - continue; - let wif = wifiiface[vap.ifname]; - if (!(name in vap.config.network) && !is_mesh(d, wif)) - continue; - let ssid = { - radio:{"$ref": sprintf("#/radios/%d", counter)}, - phy: data.config.path, - band: uc(data.config.band) - }; - ssid.location = wireless[vap.section]?.ucentral_path || ''; - ssid.ssid = wif.ssid || vap.config.mesh_id; - ssid.mode = wif.mode; - ssid.bssid = wif.bssid; - ssid.frequency = uniq(wif.frequency); - for (let k, v in stations) { - let vlan = split(k, '-v'); - if (vlan[0] != vap.ifname) - continue; - if (vlan[1]) - for (let k, assoc in v) - assoc.dynamic_vlan = +vlan[1]; - ssid.associations = [ ...(ssid.associations || []), ...v ]; - } - for (let assoc in ssid.associations) { - if (length(ip4leases[assoc.station])) - assoc.ipaddr_v4 = ip4leases[assoc.station]; - else if (snoop && snoop[assoc.station]) { - assoc.ipaddr_v4 = snoop[assoc.station]; - if (!(assoc.station in macs)) { - if (!iface.clients) - iface.clients = []; - let client = { - "mac": assoc.station, - "ipv4_addresses": [ snoop[assoc.station] ], - "ports": [ vap.ifname ] - }; - push(iface.clients, client); - } - } - - assoc.delta_counters = stations_deltas(assoc, vap.ifname); - } - - ssid.iface = vap.ifname; - if (ports[vap.ifname]?.counters) { - ssid.counters = ports[vap.ifname].counters || {}; - ssid.delta_counters = ports_deltas(vap.ifname); - } - if (is_mesh(d, wif)) { - ssid.counters = ports['batman_mesh'].counters; - ssid['mesh-path'] = mesh[vap.ifname]; - } - if (dyn_vlans[vap.ifname]) { - ssid.vlan_ifaces = []; - for (let vlan in dyn_vlans[vap.ifname]) { - let vid = +split(vlan, '-v')[1]; - push(dyn_vids, vid); - push(ssid.vlan_ifaces, { ...(ports[vlan]?.counters || {}), ...{ vid} }); - } - } - push(ssids, ssid); - } - counter++; - } - if (length(ssids)) - iface.ssids = ssids; - } - - iface.counters = ports[name]?.counters || {}; - if (role == 'up') { - iface.counters.rx_bytes = 0; - iface.counters.tx_bytes = 0; - iface.counters.rx_packets = 0; - iface.counters.tx_packets = 0; - for (let port in iface_ports) - iface_add_counters(iface, vlan, port); - - } else { - iface.delta_counters = ports_deltas(name); - } - - if (!length(iface.ipv4)) - delete iface.ipv4; - if (!length(iface.ipv6)) - delete iface.ipv6; -}); - -dyn_vids = uniq(dyn_vids); -if (length(dyn_vids)) { - state.dynamic_vlans = []; - for (let id in dyn_vids) { - let dyn = { - vid: id, - tx_bytes: 0, - tx_packets: 0, - rx_bytes: 0, - rx_packets: 0 - }; - for (let k, dev in devstats) { - for (let k, vid in dev) { - if (vid.vid != id) - continue; - dyn.tx_bytes += vid.tx.bytes; - dyn.tx_packets += vid.tx.packets; - dyn.rx_bytes += vid.rx.bytes; - dyn.rx_packets += vid.rx.packets; - } - } - push(state.dynamic_vlans, dyn); - } -} - -if (length(poe)) { - state.poe = {}; - state.poe.consumption = poe.consumption; - state.poe.ports = []; - for (let k, v in poe.ports) { - let port = { - id: replace(k, 'lan', ''), - status: v.status - }; - if (v.consumption) - port.consumption = v.consumption; - push(state.poe.ports, port); - } -} - -if (length(gps) && gps.latitude) - state.gps = { - latitude: gps.latitude, - longitude: gps.longitude, - elevation: gps.elevation - }; - -if (length(captive)) { - let res = {}; - let t = time(); - - for (let c, val in captive) { - res[c] = { - status: val.state ? 'Authenticated' : 'Garden', - idle: val.idle || 0, - time: val.data.connect ? t - val.data.connect : 0, - ip4addr: val.ip4addr || '', - ip6addr: val.ip6addr || '', - packets_ul: val.packets_ul || 0, - bytes_ul: val.bytes_ul || 0, - packets_dl: val.packets_dl || 0, - bytes_dl: val.bytes_dl || 0, - username: val?.data?.radius?.request?.username || '', - }; - } - state.captive = res; -} - -function sysfs_net(iface, prop) { - let f = fs.open(sprintf("/sys/class/net/%s/%s", iface, prop), "r"); - let val = 0; - - if (f) { - val = replace(f.read("all"), '\n', ''); - f.close(); - } - if (val === null) - val = 0; - return val; -} - -function link_state_name(name) { - let names = { - 'wan': 'upstream', - 'lan': 'downstream' - }; - return names[name] || name; -} - -if (length(capab.network)) { - let link = {}; - let lldp_peers = {}; - - for (let name, net in capab.network) { - let link_name = link_state_name(name); - link[link_name] = {}; - lldp_peers[link_name] = {}; - - for (let iface in capab.network[name]) { - let state = {}; - let lldp_state = {}; - let name = lookup_port(iface); - - state.carrier = +sysfs_net(iface, "carrier"); - if (state.carrier) { - state.speed = +sysfs_net(iface, "speed"); - state.duplex = sysfs_net(iface, "duplex"); - } - if (ports[iface]?.counters) { - state.counters = ports[iface].counters; - state.delta_counters = ports_deltas(iface); - } - link[link_name][name] = state; - - let lldp_neigh = []; - for (let l in lldp) - if (l.ifname == iface) { - delete l.ifname; - push(lldp_neigh, l); - } - if (length(lldp_neigh)) - lldp_peers[link_name][name] = lldp_neigh; - - } - } - state["link-state"] = link; - if (index(stats.types, 'lldp') >= 0) - state["lldp-peers"] = lldp_peers; -} - -state.version = 1; -printf("%.J\n", state); - -let msg = { - uuid: cfg.uuid || 1, - serial: cursor.get("ucentral", "config", "serial"), - state -}; - -if (telemetry) { - ctx.call("ucentral", "telemetry", { "event": "state", "payload": msg }); - let f = fs.open("/tmp/ucentral.telemetry", "w"); - if (f) { - f.write(msg); - f.close(); - } - return; -} - -ctx.call("ucentral", "stats", msg); -let f = fs.open("/tmp/ucentral.state", "w"); -if (f) { - f.write(msg); - f.close(); -} -else { - printf("Unable to open %s for writing: %s", statefile_path, fs.error()); -} diff --git a/system/sysinfo.uc b/system/sysinfo.uc deleted file mode 100644 index d6a9bf2..0000000 --- a/system/sysinfo.uc +++ /dev/null @@ -1,26 +0,0 @@ -let fs = require("fs"); -let ubus = require("ubus"); -let ctx = ubus.connect(); -let services = ctx.call("service", "list"); -let sysinfo = {}; - -for (let name, service in services) { - if (!service.instances) - continue; - let s = []; - for (let idx, instance in service.instances) { - if (!instance.running) - continue; - push(s, { - pid: instance.pid, - basename: fs.basename(instance.command[0]), - meminfo: instance.meminfo, - load: instance.load, - age: instance.age, - fds: instance.fds, - }); - } - if (length(s)) - sysinfo[name] = s; -} -printf("%.J\n", sysinfo); diff --git a/system/telemetry.uc b/system/telemetry.uc deleted file mode 100755 index 68e5e83..0000000 --- a/system/telemetry.uc +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/ucode -let fs = require("fs"); -let f = fs.open("/tmp/ucentral.telemetry", "w"); -if (f) - f.close(); - -telemetry = true; -include("state.uc"); diff --git a/ucentral.schema.full.json b/ucentral.schema.full.json index a977605..7238cd4 100644 --- a/ucentral.schema.full.json +++ b/ucentral.schema.full.json @@ -146,288 +146,6 @@ "fdca:1234:4567::/48" ] }, - "wireless-multimedia": { - "anyOf": [ - { - "description": "Define the default WMM behaviour of all SSIDs on the device. Each access category can be assigned a default class selector that gets used for packet matching.", - "type": "object", - "additionalProperties": false, - "properties": { - "UP0": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "UP1": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "UP2": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "UP3": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "UP4": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "UP5": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "UP6": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "UP7": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - } - } - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "profile": { - "description": "Define a default profile that shall be used for the WMM behaviour of all SSIDs on the device.", - "type": "string", - "enum": [ - "enterprise", - "rfc8325", - "3gpp" - ] - } - } - } - ] - }, "ipv4-blackhole": { "description": "Define a list of non-interface specific BLACKHOLE (to-nowhere) routes.", "type": "array", @@ -472,70 +190,6 @@ } } }, - "definitions": { - "description": "This section is used to define templates that can be referenced by a configuration. This avoids duplication of data. A RADIUS server can be defined here for example and then referenced by several SSIDs.", - "type": "object", - "properties": { - "wireless-encryption": { - "type": "object", - "description": "A dictionary of wireless encryption templates which can be referenced by the corresponding property name.", - "patternProperties": { - ".+": { - "description": "A device has certain properties that describe its identity and location. These properties are described inside this object.", - "type": "object", - "properties": { - "proto": { - "description": "The wireless encryption protocol that shall be used for this BSS", - "type": "string", - "enum": [ - "none", - "owe", - "owe-transition", - "psk", - "psk2", - "psk-mixed", - "psk2-radius", - "wpa", - "wpa2", - "wpa-mixed", - "sae", - "sae-mixed", - "wpa3", - "wpa3-192", - "wpa3-mixed" - ], - "examples": [ - "psk2" - ] - }, - "key": { - "description": "The Pre Shared Key (PSK) that is used for encryption on the BSS when using any of the WPA-PSK modes.", - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "ieee80211w": { - "description": "Enable 802.11w Management Frame Protection (MFP) for this BSS.", - "type": "string", - "enum": [ - "disabled", - "optional", - "required" - ], - "default": "disabled" - }, - "key-caching": { - "description": "PMKSA created through EAP authentication and RSN preauthentication can be cached.", - "type": "boolean", - "default": true - } - }, - "additionalProperties": false - } - } - } - } - }, "ethernet": { "type": "array", "items": { @@ -1770,230 +1424,6 @@ } } }, - "radios": { - "type": "array", - "items": { - "description": "Describe a physical radio on the AP. A radio is be parent to several VAPs. They all share the same physical properties.", - "type": "object", - "properties": { - "band": { - "description": "Specifies the wireless band to configure the radio for. Available radio device phys on the target system are matched by the wireless band given here. If multiple radio phys support the same band, the settings specified here will be applied to all of them.", - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - }, - "bandwidth": { - "description": "Specifies a narrow channel width in MHz, possible values are 5, 10, 20.", - "type": "integer", - "enum": [ - 5, - 10, - 20 - ] - }, - "channel": { - "description": "Specifies the wireless channel to use. A value of 'auto' starts the ACS algorithm.", - "oneOf": [ - { - "type": "integer", - "maximum": 196, - "minimum": 1 - }, - { - "type": "string", - "const": "auto" - } - ] - }, - "valid-channels": { - "description": "Pass a list of valid-channels that can be used during ACS.", - "type": "array", - "items": { - "type": "integer", - "maximum": 196, - "minimum": 1 - } - }, - "country": { - "description": "Specifies the country code, affects the available channels and transmission powers.", - "type": "string", - "maxLength": 2, - "minLength": 2, - "examples": [ - "US" - ] - }, - "allow-dfs": { - "description": "This property defines whether a radio may use DFS channels.", - "type": "boolean", - "default": true - }, - "channel-mode": { - "description": "Define the ideal channel mode that the radio shall use. This can be 802.11n, 802.11ac or 802.11ax. This is just a hint for the AP. If the requested value is not supported then the AP will use the highest common denominator.", - "type": "string", - "enum": [ - "HT", - "VHT", - "HE", - "EHT" - ], - "default": "HE" - }, - "channel-width": { - "description": "The channel width that the radio shall use. This is just a hint for the AP. If the requested value is not supported then the AP will use the highest common denominator.", - "type": "integer", - "enum": [ - 20, - 40, - 80, - 160, - 320, - 8080 - ], - "default": 80 - }, - "require-mode": { - "description": "Stations that do no fulfill these HT modes will be rejected.", - "type": "string", - "enum": [ - "HT", - "VHT", - "HE" - ] - }, - "mimo": { - "description": "This option allows configuring the antenna pairs that shall be used. This is just a hint for the AP. If the requested value is not supported then the AP will use the highest common denominator.", - "type": "string", - "enum": [ - "1x1", - "2x2", - "3x3", - "4x4", - "5x5", - "6x6", - "7x7", - "8x8" - ] - }, - "tx-power": { - "description": "This option specifies the transmission power in dBm", - "type": "integer", - "maximum": 30, - "minimum": 0 - }, - "legacy-rates": { - "description": "Allow legacy 802.11b data rates.", - "type": "boolean", - "default": false - }, - "beacon-interval": { - "description": "Beacon interval in kus (1.024 ms).", - "type": "integer", - "default": 100, - "maximum": 65535, - "minimum": 15 - }, - "maximum-clients": { - "description": "Set the maximum number of clients that may connect to this radio. This value is accumulative for all attached VAP interfaces.", - "type": "integer", - "example": 64 - }, - "maximum-clients-ignore-probe": { - "description": "Ignore probe requests if maximum-clients was reached.", - "type": "boolean" - }, - "rates": { - "description": "The rate configuration of this BSS.", - "type": "object", - "properties": { - "beacon": { - "description": "The beacon rate that shall be used by the BSS. Values are in Mbps.", - "type": "integer", - "default": 6000, - "enum": [ - 0, - 1000, - 2000, - 5500, - 6000, - 9000, - 11000, - 12000, - 18000, - 24000, - 36000, - 48000, - 54000 - ] - }, - "multicast": { - "description": "The multicast rate that shall be used by the BSS. Values are in Mbps.", - "type": "integer", - "default": 24000, - "enum": [ - 0, - 1000, - 2000, - 5500, - 6000, - 9000, - 11000, - 12000, - 18000, - 24000, - 36000, - 48000, - 54000 - ] - } - } - }, - "he-settings": { - "description": "This section describes the HE specific configuration options of the BSS.", - "type": "object", - "properties": { - "multiple-bssid": { - "description": "Enabling this option will make the PHY broadcast its BSSs using the multiple BSSID beacon IE.", - "type": "boolean", - "default": false - }, - "ema": { - "description": "Enableing this option will make the PHY broadcast its multiple BSSID beacons using EMA.", - "type": "boolean", - "default": false - }, - "bss-color": { - "description": "This enables BSS Coloring on the PHY. setting it to 0 disables the feature 1-63 sets the color and 64 will make hostapd pick a random color.", - "type": "integer", - "default": 64 - } - } - }, - "hostapd-iface-raw": { - "description": "This array allows passing raw hostapd.conf lines.", - "type": "array", - "items": { - "type": "string", - "examples": [ - "ap_table_expiration_time=3600", - "device_type=6-0050F204-1", - "ieee80211h=1", - "rssi_ignore_probe_request=-75", - "time_zone=EST5", - "uuid=12345678-9abc-def0-1234-56789abcdef0", - "venue_url=1:http://www.example.com/info-eng", - "wpa_deny_ptk0_rekey=0" - ] - } - } - } - } - }, "interfaces": { "type": "array", "items": { @@ -2880,1369 +2310,6 @@ } ] }, - "ssids": { - "type": "array", - "items": { - "description": "A device has certain properties that describe its identity and location. These properties are described inside this object.", - "type": "object", - "properties": { - "purpose": { - "description": "An SSID can have a special purpose such as the hidden on-boarding BSS. All purposes other than \"user-defined\" are static pre-defined configurations.", - "type": "string", - "enum": [ - "user-defined", - "onboarding-ap", - "onboarding-sta" - ], - "default": "user-defined" - }, - "name": { - "description": "The broadcasted SSID of the wireless network and for for managed mode the SSID of the network you\u2019re connecting to", - "type": "string", - "maxLength": 32, - "minLength": 1 - }, - "wifi-bands": { - "description": "The band that the SSID should be broadcasted on. The configuration layer will use the first matching band.", - "type": "array", - "items": { - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - } - }, - "bss-mode": { - "description": "Selects the operation mode of the wireless network interface controller.", - "type": "string", - "enum": [ - "ap", - "sta", - "mesh", - "wds-ap", - "wds-sta", - "wds-repeater" - ], - "default": "ap" - }, - "bssid": { - "description": "Override the BSSID of the network, only applicable in adhoc or sta mode.", - "type": "string", - "format": "uc-mac" - }, - "hidden-ssid": { - "description": "Disables the broadcasting of beacon frames if set to 1 and,in doing so, hides the ESSID.", - "type": "boolean" - }, - "isolate-clients": { - "description": "Isolates wireless clients from each other on this BSS.", - "type": "boolean" - }, - "strict-forwarding": { - "description": "Isolate the BSS from all other members on the bridge apart from the first wired port.", - "type": "boolean", - "default": false - }, - "power-save": { - "description": "Unscheduled Automatic Power Save Delivery.", - "type": "boolean" - }, - "rts-threshold": { - "description": "Set the RTS/CTS threshold of the BSS.", - "type": "integer", - "maximum": 65535, - "minimum": 1 - }, - "broadcast-time": { - "description": "This option will make the unit braodcast the time inside its beacons.", - "type": "boolean" - }, - "unicast-conversion": { - "description": "Convert multicast traffic to unicast on this BSS.", - "type": "boolean", - "default": true - }, - "services": { - "description": "The services that shall be offered on this logical interface. These are just strings such as \"wifi-steering\"", - "type": "array", - "items": { - "type": "string", - "examples": [ - "wifi-steering" - ] - } - }, - "dtim-period": { - "description": "Set the DTIM (delivery traffic information message) period. There will be one DTIM per this many beacon frames. This may be set between 1 and 255. This option only has an effect on ap wifi-ifaces.", - "type": "integer", - "default": 2, - "maximum": 255, - "minimum": 1 - }, - "maximum-clients": { - "description": "Set the maximum number of clients that may connect to this VAP.", - "type": "integer", - "example": 64 - }, - "proxy-arp": { - "description": "Proxy ARP is the technique in which the host router, answers ARP requests intended for another machine.", - "type": "boolean", - "default": true - }, - "disassoc-low-ack": { - "decription": "Disassociate stations based on excessive transmission failures or other indications of connection loss.", - "type": "boolean", - "default": false - }, - "vendor-elements": { - "decription": "This option allows embedding custom vendor specific IEs inside the beacons of a BSS in AP mode.", - "type": "string" - }, - "tip-information-element": { - "decription": "The device will broadcast the TIP vendor IE inside its beacons if this option is enabled.", - "type": "boolean", - "default": true - }, - "fils-discovery-interval": { - "description": "The maximum interval for FILS discovery announcement frames. This is a condensed beacon used in 6GHz channels for passive BSS discovery.", - "type": "integer", - "default": 20, - "maximum": 20 - }, - "encryption": { - "description": "A device has certain properties that describe its identity and location. These properties are described inside this object.", - "type": "object", - "properties": { - "proto": { - "description": "The wireless encryption protocol that shall be used for this BSS", - "type": "string", - "enum": [ - "none", - "owe", - "owe-transition", - "psk", - "psk2", - "psk-mixed", - "psk2-radius", - "wpa", - "wpa2", - "wpa-mixed", - "sae", - "sae-mixed", - "wpa3", - "wpa3-192", - "wpa3-mixed" - ], - "examples": [ - "psk2" - ] - }, - "key": { - "description": "The Pre Shared Key (PSK) that is used for encryption on the BSS when using any of the WPA-PSK modes.", - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "ieee80211w": { - "description": "Enable 802.11w Management Frame Protection (MFP) for this BSS.", - "type": "string", - "enum": [ - "disabled", - "optional", - "required" - ], - "default": "disabled" - }, - "key-caching": { - "description": "PMKSA created through EAP authentication and RSN preauthentication can be cached.", - "type": "boolean", - "default": true - } - } - }, - "multi-psk": { - "type": "array", - "items": { - "type": "object", - "description": "A SSID can have multiple PSK/VID mappings. Each one of them can be bound to a specific MAC or be a wildcard.", - "properties": { - "mac": { - "type": "string", - "format": "uc-mac" - }, - "key": { - "description": "The Pre Shared Key (PSK) that is used for encryption on the BSS when using any of the WPA-PSK modes.", - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "vlan-id": { - "type": "integer", - "maximum": 4096, - "examples": [ - 3, - 100, - 200, - 4094 - ] - } - } - } - }, - "rrm": { - "description": "Enable 802.11k Radio Resource Management (RRM) for this BSS.", - "type": "object", - "properties": { - "neighbor-reporting": { - "description": "Enable neighbor report via radio measurements (802.11k).", - "type": "boolean", - "default": false - }, - "reduced-neighbor-reporting": { - "description": "Enable reduced neighbor reports.", - "type": "boolean", - "default": false - }, - "lci": { - "description": "The content of a LCI measurement subelement", - "type": "string" - }, - "civic-location": { - "description": "The content of a location civic measurement subelement", - "type": "string" - }, - "ftm-responder": { - "description": "Publish fine timing measurement (FTM) responder functionality on this BSS.", - "type": "boolean", - "default": false - }, - "stationary-ap": { - "description": "Stationary AP config indicates that the AP doesn't move.", - "type": "boolean", - "default": false - } - } - }, - "rate-limit": { - "description": "The UE rate-limiting configuration of this BSS.", - "type": "object", - "properties": { - "ingress-rate": { - "description": "The ingress rate to which hosts will be shaped. Values are in Mbps", - "type": "integer", - "default": 0 - }, - "egress-rate": { - "description": "The egress rate to which hosts will be shaped. Values are in Mbps", - "type": "integer", - "default": 0 - } - } - }, - "roaming": { - "anyOf": [ - { - "description": "Enable 802.11r Fast Roaming for this BSS.", - "type": "object", - "properties": { - "message-exchange": { - "description": "Shall the pre authenticated message exchange happen over the air or distribution system.", - "type": "string", - "enum": [ - "air", - "ds" - ], - "default": "ds" - }, - "generate-psk": { - "description": "Whether to generate FT response locally for PSK networks. This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks.", - "type": "boolean", - "default": false - }, - "domain-identifier": { - "description": "Mobility Domain identifier (dot11FTMobilityDomainID, MDID).", - "type": "string", - "maxLength": 4, - "minLength": 4, - "examples": [ - "abcd" - ] - }, - "pmk-r0-key-holder": { - "description": "The pairwise master key R0. This is unique to the mobility domain and is required for fast roaming over the air. If the field is left empty a deterministic key is generated.", - "type": "string", - "example": "14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff" - }, - "pmk-r1-key-holder": { - "description": "The pairwise master key R1. This is unique to the mobility domain and is required for fast roaming over the air. If the field is left empty a deterministic key is generated.", - "type": "string", - "example": "14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff" - } - } - }, - { - "description": "Enable 802.11r Fast Roaming for this BSS. This will enable \"auto\" mode which will work for most scenarios.", - "type": "boolean" - } - ] - }, - "radius": { - "description": "When using EAP encryption we need to provide the required information allowing us to connect to the AAA servers.", - "type": "object", - "properties": { - "nas-identifier": { - "description": "NAS-Identifier string for RADIUS messages. When used, this should be unique to the NAS within the scope of the RADIUS server.", - "type": "string" - }, - "chargeable-user-id": { - "description": "This will enable support for Chargeable-User-Identity (RFC 4372).", - "type": "boolean", - "default": false - }, - "local": { - "description": "Describe the properties of the local Radius server inside hostapd.", - "type": "object", - "properties": { - "server-identity": { - "description": "EAP methods that provide mechanism for authenticated server identity delivery use this value.", - "type": "string", - "default": "uCentral" - }, - "users": { - "description": "Specifies a collection of local EAP user/psk/vid triplets.", - "type": "array", - "items": { - "type": "object", - "description": "Describes a local EAP user/psk/vid triplet.", - "properties": { - "mac": { - "type": "string", - "format": "uc-mac" - }, - "user-name": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "vlan-id": { - "type": "integer", - "maximum": 4096, - "examples": [ - 3, - 100, - 200, - 4094 - ] - } - } - } - } - } - }, - "dynamic-authorization": { - "description": "Dynamic Authorization Extensions (DAE) is an extension to Radius.", - "type": "object", - "properties": { - "host": { - "description": "The IP of the DAE client.", - "type": "string", - "format": "uc-ip", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port that the DAE client can connet on.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared DAE authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - } - } - }, - "authentication": { - "allOf": [ - { - "description": "Describe the properties of a Radius server.", - "type": "object", - "properties": { - "host": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "secondary": { - "description": "Definition of the secondary/failsafe radius server.", - "type": "object", - "properties": { - "host": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - } - } - }, - "request-attribute": { - "description": "The additional Access-Request attributes that gets sent to the server.", - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "vendor-id": { - "type": "integer", - "description": "The ID of the vendor specific RADIUS attribute", - "maximum": 65535, - "minimum": 1 - }, - "vendor-attributes": { - "type": "array", - "items": { - "type": "object", - "description": "The numeric RADIUS attribute value", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the vendor specific RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string", - "description": "The vendor specific RADIUS attribute value. This needs to be a hexadecimal string." - } - } - } - } - } - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "integer", - "description": "The numeric RADIUS attribute value", - "maximum": 4294967295, - "minimum": 0 - } - }, - "examples": [ - { - "id": 27, - "value": 900 - }, - { - "id": 56, - "value": 1004 - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string", - "description": "The RADIUS attribute value string" - } - }, - "examples": [ - { - "id": 32, - "value": "My NAS ID" - }, - { - "id": 126, - "value": "Example Operator" - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "hex-value": { - "type": "string", - "description": "The RADIUS attribute value string" - } - }, - "examples": [ - { - "id": 32, - "value": "0a0b0c0d" - } - ] - } - ] - } - } - } - }, - { - "type": "object", - "properties": { - "mac-filter": { - "description": "Should the radius server be used for MAC address ACL.", - "type": "boolean", - "default": false - } - } - } - ] - }, - "accounting": { - "allOf": [ - { - "description": "Describe the properties of a Radius server.", - "type": "object", - "properties": { - "host": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "secondary": { - "description": "Definition of the secondary/failsafe radius server.", - "type": "object", - "properties": { - "host": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - } - } - }, - "request-attribute": { - "description": "The additional Access-Request attributes that gets sent to the server.", - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "vendor-id": { - "type": "integer", - "description": "The ID of the vendor specific RADIUS attribute", - "maximum": 65535, - "minimum": 1 - }, - "vendor-attributes": { - "type": "array", - "items": { - "type": "object", - "description": "The numeric RADIUS attribute value", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the vendor specific RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string", - "description": "The vendor specific RADIUS attribute value. This needs to be a hexadecimal string." - } - } - } - } - } - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "integer", - "description": "The numeric RADIUS attribute value", - "maximum": 4294967295, - "minimum": 0 - } - }, - "examples": [ - { - "id": 27, - "value": 900 - }, - { - "id": 56, - "value": 1004 - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string", - "description": "The RADIUS attribute value string" - } - }, - "examples": [ - { - "id": 32, - "value": "My NAS ID" - }, - { - "id": 126, - "value": "Example Operator" - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "hex-value": { - "type": "string", - "description": "The RADIUS attribute value string" - } - }, - "examples": [ - { - "id": 32, - "value": "0a0b0c0d" - } - ] - } - ] - } - } - } - }, - { - "type": "object", - "properties": { - "interval": { - "description": "The interim accounting update interval. This value is defined in seconds.", - "type": "integer", - "maximum": 600, - "minimum": 60, - "default": 60 - } - } - } - ] - }, - "health": { - "description": "The credentials used when health check probes this radius server.", - "type": "object", - "properties": { - "username": { - "description": "The username that gets used when doing a healthcheck on this radius server.", - "type": "string" - }, - "password": { - "description": "The password that gets used when doing a healthcheck on this radius server.", - "type": "string" - } - } - } - } - }, - "certificates": { - "description": "When running a local EAP server or using STA/MESH to connect to another BSS a set of certificates is required.", - "type": "object", - "properties": { - "use-local-certificates": { - "description": "The device will use its local certificate bundle for the TLS setup and ignores all other certificate options in this section.", - "type": "boolean", - "default": false - }, - "ca-certificate": { - "description": "The local servers CA bundle.", - "type": "string" - }, - "certificate": { - "description": "The local servers certificate.", - "type": "string" - }, - "private-key": { - "description": "The local servers private key/", - "type": "string" - }, - "private-key-password": { - "description": "The password required to read the private key.", - "type": "string" - } - } - }, - "pass-point": { - "description": "Enable Hotspot 2.0 support.", - "type": "object", - "properties": { - "venue-name": { - "description": "This parameter can be used to configure one or more Venue Name Duples for Venue Name ANQP information.", - "type": "array", - "items": { - "type": "string" - } - }, - "venue-group": { - "description": "The available values are defined in 802.11u.", - "type": "integer", - "maximum": 32 - }, - "venue-type": { - "description": "The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34", - "type": "integer", - "maximum": 32 - }, - "venue-url": { - "description": "This parameter can be used to configure one or more Venue URL Duples to provide additional information corresponding to Venue Name information.", - "type": "array", - "items": { - "type": "string", - "format": "uri" - } - }, - "auth-type": { - "description": "This parameter indicates what type of network authentication is used in the network.", - "type": "object", - "properties": { - "type": { - "description": "Specifies the specific network authentication type in use.", - "type": "string", - "enum": [ - "terms-and-conditions", - "online-enrollment", - "http-redirection", - "dns-redirection" - ] - }, - "uri": { - "description": "Specifies the redirect URL applicable to the indicated authentication type.", - "type": "string", - "format": "uri", - "examples": [ - "https://operator.example.org/wireless-access/terms-and-conditions.html", - "http://www.example.com/redirect/me/here/" - ] - } - }, - "minLength": 2, - "maxLength": 2 - }, - "domain-name": { - "description": "The IEEE 802.11u Domain Name.", - "type": "array", - "items": { - "type": "string", - "format": "hostname" - } - }, - "nai-realm": { - "description": "NAI Realm information", - "type": "array", - "items": { - "type": "string" - } - }, - "osen": { - "description": "OSU Server-Only Authenticated L2 Encryption Network;", - "type": "boolean" - }, - "anqp-domain": { - "description": "ANQP Domain ID, An identifier for a set of APs in an ESS that share the same common ANQP information.", - "type": "integer", - "maximum": 65535, - "minimum": 0 - }, - "anqp-3gpp-cell-net": { - "description": "The ANQP 3GPP Cellular Network information.", - "type": "array", - "items": { - "type": "string" - } - }, - "friendly-name": { - "description": "This parameter can be used to configure one or more Operator Friendly Name Duples.", - "type": "array", - "items": { - "type": "string" - } - }, - "access-network-type": { - "description": "Indicate the type of network. This is part of the interworking IE.", - "type": "integer", - "maximum": 15, - "default": 0 - }, - "internet": { - "description": "Whether the network provides connectivity to the Internet", - "type": "boolean", - "default": true - }, - "asra": { - "description": "Additional Step Required for Access.", - "type": "boolean", - "default": false - }, - "esr": { - "description": "Emergency services reachable.", - "type": "boolean", - "default": false - }, - "uesa": { - "description": "Unauthenticated emergency service accessible.", - "type": "boolean", - "default": false - }, - "hessid": { - "description": "Homogeneous ESS identifier", - "type": "string", - "example": "00:11:22:33:44:55" - }, - "roaming-consortium": { - "description": "Roaming Consortium OIs can be configured here. Each OI is between 3 and 15 octets and is configured as a hexstring.", - "type": "array", - "items": { - "type": "string" - } - }, - "disable-dgaf": { - "description": "Disable Downstream Group-Addressed Forwarding. This can be used to configure a network where no group-addressed frames are allowed.", - "type": "boolean", - "default": false - }, - "ipaddr-type-available": { - "description": "IP Address Type Availability.", - "type": "integer", - "maximum": 255 - }, - "connection-capability": { - "description": "This can be used to advertise what type of IP traffic can be sent through the hotspot.", - "type": "array", - "items": { - "type": "string" - } - }, - "icons": { - "description": "The operator icons.", - "type": "array", - "items": { - "type": "object", - "properties": { - "width": { - "type": "integer", - "description": "The width of the operator icon in pixel", - "examples": [ - 64 - ] - }, - "height": { - "type": "integer", - "description": "The height of the operator icon in pixel", - "examples": [ - 64 - ] - }, - "type": { - "type": "string", - "description": "The mimetype of the operator icon", - "examples": [ - "image/png" - ] - }, - "icon": { - "type": "string", - "format": "uc-base64", - "description": "The base64 encoded image" - }, - "language": { - "type": "string", - "description": "ISO 639-2 language code of the icon", - "pattern": "^[a-z][a-z][a-z]$", - "examples": [ - "eng", - "fre", - "ger", - "ita" - ] - } - }, - "examples": [ - { - "width": 32, - "height": 32, - "type": "image/png", - "language": "eng", - "icon": "R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" - } - ] - } - }, - "wan-metrics": { - "description": "A description of the wan metric offered by this device.", - "type": "object", - "properties": { - "info": { - "description": "The state of the devices uplink", - "type": "string", - "enum": [ - "up", - "down", - "testing" - ] - }, - "downlink": { - "description": "Estimate of WAN backhaul link current downlink speed in kbps.", - "type": "integer" - }, - "uplink": { - "description": "Estimate of WAN backhaul link current uplink speed in kbps.", - "type": "integer" - } - } - } - } - }, - "quality-thresholds": { - "description": "The thresholds that need to be meet for a clien association to be allowed.", - "type": "object", - "properties": { - "probe-request-rssi": { - "description": "Probe requests will be ignored if the rssi is below this threshold.", - "type": "integer" - }, - "association-request-rssi": { - "description": "Association requests will be denied if the rssi is below this threshold.", - "type": "integer" - }, - "client-kick-rssi": { - "description": "Clients will get kicked if their SNR drops below this value.", - "type": "integer" - }, - "client-kick-ban-time": { - "description": "The duration that a client is banned from re-joining after it was kicked.", - "type": "integer", - "default": 0 - } - } - }, - "access-control-list": { - "description": "The MAC ACL that defines which clients are allowed or denied to associations.", - "type": "object", - "properties": { - "mode": { - "description": "Defines if this is an allow or deny list.", - "type": "string", - "enum": [ - "allow", - "deny" - ] - }, - "mac-address": { - "description": "Association requests will be denied if the rssi is below this threshold.", - "type": "array", - "items": { - "type": "string", - "format": "uc-mac" - } - } - } - }, - "captive": { - "allOf": [ - { - "oneOf": [ - { - "description": "This section can be used to setup a captive portal on the AP with a click-to-continue splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'click-to-continue'", - "type": "string", - "const": "click-to-continue" - } - } - }, - { - "description": "This section can be used to setup a captive portal on the AP with a click-to-continue splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'radius'", - "type": "string", - "const": "radius" - }, - "auth-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "description": "The timeout used for interim messages.", - "type": "integer", - "default": 600 - } - } - }, - { - "description": "This section can be used to setup a captive portal on the AP with a credentials splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'credentials'", - "type": "string", - "const": "credentials" - }, - "credentials": { - "description": "The list of local username/password pairs that can be used to login.", - "type": "array", - "items": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - } - }, - { - "description": "This section can be used to setup a captive portal on the AP with a remote UAM server.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'uam'", - "type": "string", - "const": "uam" - }, - "uam-port": { - "description": "The local UAM port.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 3990 - }, - "uam-secret": { - "description": "The pre-shared UAM secret.", - "type": "string" - }, - "uam-server": { - "description": "The fqdn of the UAM server.", - "type": "string" - }, - "nasid": { - "description": "The NASID that gets sent to the UAM server.", - "type": "string" - }, - "nasmac": { - "description": "The NAS MAC that gets send to the UAM server. The devices serial is used if this value is not provided.", - "type": "string" - }, - "auth-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "description": "The timeout used for interim messages.", - "type": "integer", - "default": 600 - }, - "ssid": { - "description": "The name of the SSID that shall be sent as part of the UAM redirect.", - "type": "string" - }, - "mac-format": { - "description": "Defines the format used to send the MAC address inside AAA frames.", - "type": "string", - "enum": [ - "aabbccddeeff", - "aa-bb-cc-dd-ee-ff", - "aa:bb:cc:dd:ee:ff", - "AABBCCDDEEFF", - "AA:BB:CC:DD:EE:FF", - "AA-BB-CC-DD-EE-FF" - ] - }, - "final-redirect-url": { - "description": "Define the behaviour off the final redirect. Default will honour \"userurl\" and fallback to \"local\". Alternatively it is possible to force a redirect to the \"UAM\" or \"local\" URL.", - "type": "string", - "enum": [ - "default", - "uam" - ] - }, - "mac-auth": { - "description": "Try to authenticate new clients using macauth.", - "type": "boolean", - "default": "default" - }, - "radius-gw-proxy": { - "description": "Tunnel all radius traffic via the radius-gw-proxy.", - "type": "boolean", - "default": false - } - } - } - ] - }, - { - "type": "object", - "properties": { - "walled-garden-fqdn": { - "description": "The list of FQDNs that a non-authenticated client is allowed to connect to.", - "type": "array", - "items": { - "type": "string" - } - }, - "walled-garden-ipaddr": { - "description": "The list of IP addresses that a non-authenticated client is allowed to connect to.", - "type": "array", - "items": { - "type": "string", - "format": "uc-ip" - } - }, - "web-root": { - "description": "A base64 encoded TAR file with the custom web-root.", - "type": "string", - "format": "uc-base64" - }, - "idle-timeout": { - "description": "How long may a client be idle before getting removed.", - "type": "integer", - "default": 600 - }, - "session-timeout": { - "description": "How long may a client be idle before getting removed.", - "type": "integer" - } - } - } - ] - }, - "hostapd-bss-raw": { - "description": "This array allows passing raw hostapd.conf lines.", - "type": "array", - "items": { - "type": "string", - "examples": [ - "ap_table_expiration_time=3600", - "device_type=6-0050F204-1", - "ieee80211h=1", - "rssi_ignore_probe_request=-75", - "time_zone=EST5", - "uuid=12345678-9abc-def0-1234-56789abcdef0", - "venue_url=1:http://www.example.com/info-eng", - "wpa_deny_ptk0_rekey=0" - ] - } - } - } - } - }, "tunnel": { "oneOf": [ { @@ -4362,7 +2429,7 @@ } }, "services": { - "description": "This section describes all of the services that may be present on the AP. Each service is then referenced via its name inside an interface, ssid, ...", + "description": "This section describes all of the services that may be present on the AP. Each service is then referenced via its name inside an interface, ...", "type": "object", "properties": { "lldp": { @@ -4554,147 +2621,6 @@ } } }, - "ieee8021x": { - "description": "This section allows enabling wired ieee802.1X", - "type": "object", - "properties": { - "mode": { - "description": "This field must be set to 'radius or user'", - "type": "string", - "enum": [ - "radius", - "user" - ] - }, - "port-filter": { - "description": "Specifies a list of ports that we want to filter.", - "type": "array", - "items": { - "type": "string", - "examples": [ - { - "LAN1": null - } - ] - } - }, - "users": { - "description": "Specifies a collection of local EAP user/psk/vid triplets.", - "type": "array", - "items": { - "type": "object", - "description": "Describes a local EAP user/psk/vid triplet.", - "properties": { - "mac": { - "type": "string", - "format": "uc-mac" - }, - "user-name": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "vlan-id": { - "type": "integer", - "maximum": 4096, - "examples": [ - 3, - 100, - 200, - 4094 - ] - } - } - } - }, - "radius": { - "description": "Specifies the information about radius account authentication and accounting", - "type": "object", - "properties": { - "nas-identifier": { - "description": "NAS-Identifier string for RADIUS messages. When used, this should be unique to the NAS within the scope of the RADIUS server.", - "type": "string" - }, - "auth-server-addr": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-server-port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "auth-server-secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server-addr": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-server-port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1813 - ] - }, - "acct-server-secret": { - "description": "The shared Radius accounting secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "coa-server-addr": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "coa-server-port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1814 - ] - }, - "coa-server-secret": { - "description": "The shared Radius accounting secret.", - "type": "string", - "examples": [ - "secret" - ] - } - } - } - } - }, "radius-proxy": { "description": "This section can be used to setup a radius security proxy instance (radsecproxy).", "type": "object", @@ -4916,7 +2842,6 @@ "items": { "type": "string", "enum": [ - "wifi", "leds" ] } @@ -4947,58 +2872,6 @@ } } }, - "wifi-steering": { - "description": "This section describes the band steering behaviour of the unit.", - "type": "object", - "properties": { - "mode": { - "description": "Wifi sterring can happen either locally or via the backend gateway.", - "type": "string", - "enum": [ - "local", - "none" - ], - "examples": [ - "local" - ] - }, - "assoc-steering": { - "description": "Allow rejecting assoc requests for steering purposes.", - "type": "boolean", - "default": false - }, - "required-snr": { - "description": "Minimum required signal level (dBm) for connected clients. If the client will be kicked if the SNR drops below this value.", - "type": "integer", - "default": 0 - }, - "required-probe-snr": { - "description": "Minimum required signal level (dBm) to allow connections. If the SNR is below this value, probe requests will not be replied to.", - "type": "integer", - "default": 0 - }, - "required-roam-snr": { - "description": "Minimum required signal level (dBm) before an attempt is made to roam the client to a better AP.", - "type": "integer", - "default": 0 - }, - "load-kick-threshold": { - "description": "Minimum channel load (%) before kicking clients", - "type": "integer", - "default": 0 - }, - "auto-channel": { - "description": "Allow multiple instances of the steering daemon to coordinate the best channel usage amongst eachother.", - "type": "boolean", - "default": false - }, - "ipv6": { - "description": "Use IPv6 multicast to communicate with remote usteerd instances, rather than IPv4 broadcast.", - "type": "boolean", - "default": false - } - } - }, "quality-of-service": { "description": "This section describes the QoS behaviour of the unit.", "type": "object", @@ -5167,65 +3040,6 @@ } } }, - "facebook-wifi": { - "description": "This section describes the FaceBook Wifi behaviour of the unit.", - "type": "object", - "properties": { - "vendor-id": { - "description": "The Vendors ID.", - "type": "string" - }, - "gateway-id": { - "description": "The Gateways ID.", - "type": "string" - }, - "secret": { - "description": "The Device specific secret", - "type": "string" - } - } - }, - "airtime-fairness": { - "description": "This section describes the vlan behaviour of a logical network interface.", - "type": "object", - "properties": { - "voice-weight": { - "description": "Voice traffic does not get aggregated. As voice and video are both considered priotity voice is considered to have a heavier weight when calculation priority average.", - "type": "number", - "default": 4 - }, - "packet-threshold": { - "description": "The amount of packets that need to be received for a specific type of traffic before new averageg is calculated.", - "type": "number", - "default": 100 - }, - "bulk-threshold": { - "description": "This option is a percentual value. If more the X% of the traffic is bulk, we assign the bulk weight.", - "type": "number", - "default": 50 - }, - "priority-threshold": { - "description": "This option is a percentual value. If more the X% of the traffic is priority, we assign the priority weight. Priority classification will take precedence over bulk.", - "type": "number", - "default": 30 - }, - "weight-normal": { - "description": "The default ATF weight that UEs get assigned.", - "type": "number", - "default": 256 - }, - "weight-priority": { - "description": "The default ATF weight that UEs get assigned when priority traffic above the configured percentage is detected.", - "type": "number", - "default": 394 - }, - "weight-bulk": { - "description": "The default ATF weight that UEs get assigned when bulk traffic above the configured percentage is detected.", - "type": "number", - "default": 128 - } - } - }, "wireguard-overlay": { "description": "This Object defines the properties of a wireguard-overlay.", "type": "object", @@ -5341,262 +3155,6 @@ } } }, - "captive": { - "allOf": [ - { - "oneOf": [ - { - "description": "This section can be used to setup a captive portal on the AP with a click-to-continue splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'click-to-continue'", - "type": "string", - "const": "click-to-continue" - } - } - }, - { - "description": "This section can be used to setup a captive portal on the AP with a click-to-continue splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'radius'", - "type": "string", - "const": "radius" - }, - "auth-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "description": "The timeout used for interim messages.", - "type": "integer", - "default": 600 - } - } - }, - { - "description": "This section can be used to setup a captive portal on the AP with a credentials splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'credentials'", - "type": "string", - "const": "credentials" - }, - "credentials": { - "description": "The list of local username/password pairs that can be used to login.", - "type": "array", - "items": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - } - }, - { - "description": "This section can be used to setup a captive portal on the AP with a remote UAM server.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'uam'", - "type": "string", - "const": "uam" - }, - "uam-port": { - "description": "The local UAM port.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 3990 - }, - "uam-secret": { - "description": "The pre-shared UAM secret.", - "type": "string" - }, - "uam-server": { - "description": "The fqdn of the UAM server.", - "type": "string" - }, - "nasid": { - "description": "The NASID that gets sent to the UAM server.", - "type": "string" - }, - "nasmac": { - "description": "The NAS MAC that gets send to the UAM server. The devices serial is used if this value is not provided.", - "type": "string" - }, - "auth-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "description": "The timeout used for interim messages.", - "type": "integer", - "default": 600 - }, - "ssid": { - "description": "The name of the SSID that shall be sent as part of the UAM redirect.", - "type": "string" - }, - "mac-format": { - "description": "Defines the format used to send the MAC address inside AAA frames.", - "type": "string", - "enum": [ - "aabbccddeeff", - "aa-bb-cc-dd-ee-ff", - "aa:bb:cc:dd:ee:ff", - "AABBCCDDEEFF", - "AA:BB:CC:DD:EE:FF", - "AA-BB-CC-DD-EE-FF" - ] - }, - "final-redirect-url": { - "description": "Define the behaviour off the final redirect. Default will honour \"userurl\" and fallback to \"local\". Alternatively it is possible to force a redirect to the \"UAM\" or \"local\" URL.", - "type": "string", - "enum": [ - "default", - "uam" - ] - }, - "mac-auth": { - "description": "Try to authenticate new clients using macauth.", - "type": "boolean", - "default": "default" - }, - "radius-gw-proxy": { - "description": "Tunnel all radius traffic via the radius-gw-proxy.", - "type": "boolean", - "default": false - } - } - } - ] - }, - { - "type": "object", - "properties": { - "walled-garden-fqdn": { - "description": "The list of FQDNs that a non-authenticated client is allowed to connect to.", - "type": "array", - "items": { - "type": "string" - } - }, - "walled-garden-ipaddr": { - "description": "The list of IP addresses that a non-authenticated client is allowed to connect to.", - "type": "array", - "items": { - "type": "string", - "format": "uc-ip" - } - }, - "web-root": { - "description": "A base64 encoded TAR file with the custom web-root.", - "type": "string", - "format": "uc-base64" - }, - "idle-timeout": { - "description": "How long may a client be idle before getting removed.", - "type": "integer", - "default": 600 - }, - "session-timeout": { - "description": "How long may a client be idle before getting removed.", - "type": "integer" - } - } - } - ] - }, "gps": { "description": "This section can be used to configure a GPS dongle", "type": "object", @@ -5618,108 +3176,6 @@ } } }, - "dhcp-relay": { - "description": "Define the vlans on which the dhcp shall be relayed.", - "type": "object", - "properties": { - "select-ports": { - "description": "The list of physical network devices that shall be used to fwd the DHCP frames.", - "type": "array", - "items": { - "type": "string" - } - }, - "vlans": { - "description": "The list of all vlans", - "type": "array", - "items": { - "type": "object", - "properties": { - "vlan": { - "description": "The vlan id.", - "type": "number" - }, - "relay-server": { - "description": "The unicast target DHCP pool server where frames get relayed to.", - "type": "string", - "format": "uc-ip" - }, - "circuit-id-format": { - "description": "This option selects what info shall be contained within a relayed frame's circuit ID.", - "type": "string", - "enum": [ - "vlan-id", - "ap-mac", - "ssid" - ], - "default": "vlan-id" - }, - "remote-id-format": { - "description": "This option selects what info shall be contained within a relayed frame's remote ID.", - "type": "string", - "enum": [ - "vlan-id", - "ap-mac", - "ssid" - ], - "default": "ap-mac" - } - } - } - } - } - }, - "admin-ui": { - "type": "object", - "properties": { - "wifi-ssid": { - "description": "The name of the admin ssid.", - "type": "string", - "default": "Maverick", - "maxLength": 32, - "minLength": 1 - }, - "wifi-key": { - "description": "The Pre Shared Key (PSK) that is used for encryption on the BSS.", - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "wifi-bands": { - "description": "The band that the SSID should be broadcasted on. The configuration layer will use the first matching band.", - "type": "array", - "items": { - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - } - }, - "offline-trigger": { - "description": "The admin-ui will be spawned when this offline threshold was exceeded.", - "type": "number" - } - } - }, - "rrm": { - "description": "This section describes the band steering behaviour of the unit.", - "type": "object", - "properties": { - "beacon-request-assoc": { - "description": "Tell stations to send a beacon request scan when they associate.", - "type": "boolean", - "default": true - }, - "station-stats-interval": { - "description": "Periodically send station statistics every N seconds.", - "type": "number" - } - } - }, "telnet": { "description": "This section can be used to setup a Telnet server on the device.", "type": "object", @@ -5768,10 +3224,8 @@ "items": { "type": "string", "enum": [ - "ssids", "lldp", - "clients", - "tid-stats" + "clients" ] } }, @@ -5812,31 +3266,6 @@ } } }, - "wifi-frames": { - "description": "Define which types of ieee802.11 management frames shall be sent up to the controller.", - "type": "object", - "properties": { - "filters": { - "description": "A list of the management frames types that shall be sent to the backend.", - "type": "array", - "items": { - "type": "string", - "enum": [ - "probe", - "auth", - "assoc", - "disassoc", - "deauth", - "local-deauth", - "inactive-deauth", - "key-mismatch", - "beacon-report", - "radar-detected" - ] - } - } - } - }, "dhcp-snooping": { "description": "DHCP snooping allows us to intercept DHCP packages on interface that are bridged, where DHCP is not offered as a service by the AP.", "type": "object", @@ -5859,24 +3288,6 @@ } } }, - "wifi-scan": { - "description": "Define the behaviour of the periodic wifi scanning interface.", - "type": "object", - "properties": { - "interval": { - "description": "The periodicity at which the scan shall be performed.", - "type": "integer" - }, - "verbose": { - "description": "Add capabilities, v/ht_oper, ... to the resulting scan info.", - "type": "boolean" - }, - "information-elements": { - "description": "Add all IEs to the resulting scan info.", - "type": "boolean" - } - } - }, "telemetry": { "description": "Configure the unsolicited telemetry stream.", "type": "object", @@ -5901,9 +3312,6 @@ "client.join", "client.leave", "client.key-mismatch", - "wifi", - "wifi.start", - "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", @@ -5934,9 +3342,6 @@ "client.join", "client.leave", "client.key-mismatch", - "wifi", - "wifi.start", - "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", diff --git a/ucentral.schema.json b/ucentral.schema.json index 838b5b2..f6d1fe7 100644 --- a/ucentral.schema.json +++ b/ucentral.schema.json @@ -20,9 +20,6 @@ "globals": { "$ref": "#/$defs/globals" }, - "definitions": { - "$ref": "#/$defs/definitions" - }, "ethernet": { "type": "array", "items": { @@ -32,12 +29,6 @@ "switch": { "$ref": "#/$defs/switch" }, - "radios": { - "type": "array", - "items": { - "$ref": "#/$defs/radio" - } - }, "interfaces": { "type": "array", "items": { @@ -147,82 +138,6 @@ } } }, - "globals.wireless-multimedia.class-selector": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "globals.wireless-multimedia.table": { - "type": "object", - "additionalProperties": false, - "properties": { - "UP0": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP1": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP2": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP3": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP4": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP5": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP6": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP7": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - } - } - }, - "globals.wireless-multimedia.profile": { - "type": "object", - "additionalProperties": false, - "properties": { - "profile": { - "type": "string", - "enum": [ - "enterprise", - "rfc8325", - "3gpp" - ] - } - } - }, "globals": { "type": "object", "properties": { @@ -240,16 +155,6 @@ "fdca:1234:4567::/48" ] }, - "wireless-multimedia": { - "anyOf": [ - { - "$ref": "#/$defs/globals.wireless-multimedia.table" - }, - { - "$ref": "#/$defs/globals.wireless-multimedia.profile" - } - ] - }, "ipv4-blackhole": { "type": "array", "items": { @@ -288,66 +193,6 @@ } } }, - "interface.ssid.encryption": { - "type": "object", - "properties": { - "proto": { - "type": "string", - "enum": [ - "none", - "owe", - "owe-transition", - "psk", - "psk2", - "psk-mixed", - "psk2-radius", - "wpa", - "wpa2", - "wpa-mixed", - "sae", - "sae-mixed", - "wpa3", - "wpa3-192", - "wpa3-mixed" - ], - "examples": [ - "psk2" - ] - }, - "key": { - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "ieee80211w": { - "type": "string", - "enum": [ - "disabled", - "optional", - "required" - ], - "default": "disabled" - }, - "key-caching": { - "type": "boolean", - "default": true - } - } - }, - "definitions": { - "type": "object", - "properties": { - "wireless-encryption": { - "type": "object", - "patternProperties": { - ".+": { - "$ref": "#/$defs/interface.ssid.encryption", - "additionalProperties": false - } - } - } - } - }, "ethernet": { "type": "object", "properties": { @@ -1389,209 +1234,6 @@ } } }, - "radio.rates": { - "type": "object", - "properties": { - "beacon": { - "type": "integer", - "default": 6000, - "enum": [ - 0, - 1000, - 2000, - 5500, - 6000, - 9000, - 11000, - 12000, - 18000, - 24000, - 36000, - 48000, - 54000 - ] - }, - "multicast": { - "type": "integer", - "default": 24000, - "enum": [ - 0, - 1000, - 2000, - 5500, - 6000, - 9000, - 11000, - 12000, - 18000, - 24000, - 36000, - 48000, - 54000 - ] - } - } - }, - "radio.he": { - "type": "object", - "properties": { - "multiple-bssid": { - "type": "boolean", - "default": false - }, - "ema": { - "type": "boolean", - "default": false - }, - "bss-color": { - "type": "integer", - "default": 64 - } - } - }, - "radio": { - "type": "object", - "properties": { - "band": { - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - }, - "bandwidth": { - "type": "integer", - "enum": [ - 5, - 10, - 20 - ] - }, - "channel": { - "oneOf": [ - { - "type": "integer", - "maximum": 196, - "minimum": 1 - }, - { - "type": "string", - "const": "auto" - } - ] - }, - "valid-channels": { - "type": "array", - "items": { - "type": "integer", - "maximum": 196, - "minimum": 1 - } - }, - "country": { - "type": "string", - "maxLength": 2, - "minLength": 2, - "examples": [ - "US" - ] - }, - "allow-dfs": { - "type": "boolean", - "default": true - }, - "channel-mode": { - "type": "string", - "enum": [ - "HT", - "VHT", - "HE", - "EHT" - ], - "default": "HE" - }, - "channel-width": { - "type": "integer", - "enum": [ - 20, - 40, - 80, - 160, - 320, - 8080 - ], - "default": 80 - }, - "require-mode": { - "type": "string", - "enum": [ - "HT", - "VHT", - "HE" - ] - }, - "mimo": { - "type": "string", - "enum": [ - "1x1", - "2x2", - "3x3", - "4x4", - "5x5", - "6x6", - "7x7", - "8x8" - ] - }, - "tx-power": { - "type": "integer", - "maximum": 30, - "minimum": 0 - }, - "legacy-rates": { - "type": "boolean", - "default": false - }, - "beacon-interval": { - "type": "integer", - "default": 100, - "maximum": 65535, - "minimum": 15 - }, - "maximum-clients": { - "type": "integer", - "example": 64 - }, - "maximum-clients-ignore-probe": { - "type": "boolean" - }, - "rates": { - "$ref": "#/$defs/radio.rates" - }, - "he-settings": { - "$ref": "#/$defs/radio.he" - }, - "hostapd-iface-raw": { - "type": "array", - "items": { - "type": "string", - "examples": [ - "ap_table_expiration_time=3600", - "device_type=6-0050F204-1", - "ieee80211h=1", - "rssi_ignore_probe_request=-75", - "time_zone=EST5", - "uuid=12345678-9abc-def0-1234-56789abcdef0", - "venue_url=1:http://www.example.com/info-eng", - "wpa_deny_ptk0_rekey=0" - ] - } - } - } - }, "interface.vlan": { "type": "object", "properties": { @@ -2330,1045 +1972,6 @@ } ] }, - "interface.ssid.multi-psk": { - "type": "object", - "properties": { - "mac": { - "type": "string", - "format": "uc-mac" - }, - "key": { - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "vlan-id": { - "type": "integer", - "maximum": 4096, - "examples": [ - 3, - 100, - 200, - 4094 - ] - } - } - }, - "interface.ssid.rrm": { - "type": "object", - "properties": { - "neighbor-reporting": { - "type": "boolean", - "default": false - }, - "reduced-neighbor-reporting": { - "type": "boolean", - "default": false - }, - "lci": { - "type": "string" - }, - "civic-location": { - "type": "string" - }, - "ftm-responder": { - "type": "boolean", - "default": false - }, - "stationary-ap": { - "type": "boolean", - "default": false - } - } - }, - "interface.ssid.rate-limit": { - "type": "object", - "properties": { - "ingress-rate": { - "type": "integer", - "default": 0 - }, - "egress-rate": { - "type": "integer", - "default": 0 - } - } - }, - "interface.ssid.roaming": { - "type": "object", - "properties": { - "message-exchange": { - "type": "string", - "enum": [ - "air", - "ds" - ], - "default": "ds" - }, - "generate-psk": { - "type": "boolean", - "default": false - }, - "domain-identifier": { - "type": "string", - "maxLength": 4, - "minLength": 4, - "examples": [ - "abcd" - ] - }, - "pmk-r0-key-holder": { - "type": "string", - "example": "14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff" - }, - "pmk-r1-key-holder": { - "type": "string", - "example": "14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff" - } - } - }, - "interface.ssid.radius.local-user": { - "type": "object", - "properties": { - "mac": { - "type": "string", - "format": "uc-mac" - }, - "user-name": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "vlan-id": { - "type": "integer", - "maximum": 4096, - "examples": [ - 3, - 100, - 200, - 4094 - ] - } - } - }, - "interface.ssid.radius.local": { - "type": "object", - "properties": { - "server-identity": { - "type": "string", - "default": "uCentral" - }, - "users": { - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid.radius.local-user" - } - } - } - }, - "interface.ssid.radius.server": { - "type": "object", - "properties": { - "host": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "type": "string", - "examples": [ - "secret" - ] - }, - "secondary": { - "type": "object", - "properties": { - "host": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "type": "string", - "examples": [ - "secret" - ] - } - } - }, - "request-attribute": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "vendor-id": { - "type": "integer", - "maximum": 65535, - "minimum": 1 - }, - "vendor-attributes": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string" - } - } - } - } - } - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "integer", - "maximum": 4294967295, - "minimum": 0 - } - }, - "examples": [ - { - "id": 27, - "value": 900 - }, - { - "id": 56, - "value": 1004 - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string" - } - }, - "examples": [ - { - "id": 32, - "value": "My NAS ID" - }, - { - "id": 126, - "value": "Example Operator" - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "maximum": 255, - "minimum": 1 - }, - "hex-value": { - "type": "string" - } - }, - "examples": [ - { - "id": 32, - "value": "0a0b0c0d" - } - ] - } - ] - } - } - } - }, - "interface.ssid.radius.health": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, - "interface.ssid.radius": { - "type": "object", - "properties": { - "nas-identifier": { - "type": "string" - }, - "chargeable-user-id": { - "type": "boolean", - "default": false - }, - "local": { - "$ref": "#/$defs/interface.ssid.radius.local" - }, - "dynamic-authorization": { - "type": "object", - "properties": { - "host": { - "type": "string", - "format": "uc-ip", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "type": "string", - "examples": [ - "secret" - ] - } - } - }, - "authentication": { - "allOf": [ - { - "$ref": "#/$defs/interface.ssid.radius.server" - }, - { - "type": "object", - "properties": { - "mac-filter": { - "type": "boolean", - "default": false - } - } - } - ] - }, - "accounting": { - "allOf": [ - { - "$ref": "#/$defs/interface.ssid.radius.server" - }, - { - "type": "object", - "properties": { - "interval": { - "type": "integer", - "maximum": 600, - "minimum": 60, - "default": 60 - } - } - } - ] - }, - "health": { - "$ref": "#/$defs/interface.ssid.radius.health" - } - } - }, - "interface.ssid.certificates": { - "type": "object", - "properties": { - "use-local-certificates": { - "type": "boolean", - "default": false - }, - "ca-certificate": { - "type": "string" - }, - "certificate": { - "type": "string" - }, - "private-key": { - "type": "string" - }, - "private-key-password": { - "type": "string" - } - } - }, - "interface.ssid.pass-point": { - "type": "object", - "properties": { - "venue-name": { - "type": "array", - "items": { - "type": "string" - } - }, - "venue-group": { - "type": "integer", - "maximum": 32 - }, - "venue-type": { - "type": "integer", - "maximum": 32 - }, - "venue-url": { - "type": "array", - "items": { - "type": "string", - "format": "uri" - } - }, - "auth-type": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "terms-and-conditions", - "online-enrollment", - "http-redirection", - "dns-redirection" - ] - }, - "uri": { - "type": "string", - "format": "uri", - "examples": [ - "https://operator.example.org/wireless-access/terms-and-conditions.html", - "http://www.example.com/redirect/me/here/" - ] - } - }, - "minLength": 2, - "maxLength": 2 - }, - "domain-name": { - "type": "array", - "items": { - "type": "string", - "format": "hostname" - } - }, - "nai-realm": { - "type": "array", - "items": { - "type": "string" - } - }, - "osen": { - "type": "boolean" - }, - "anqp-domain": { - "type": "integer", - "maximum": 65535, - "minimum": 0 - }, - "anqp-3gpp-cell-net": { - "type": "array", - "items": { - "type": "string" - } - }, - "friendly-name": { - "type": "array", - "items": { - "type": "string" - } - }, - "access-network-type": { - "type": "integer", - "maximum": 15, - "default": 0 - }, - "internet": { - "type": "boolean", - "default": true - }, - "asra": { - "type": "boolean", - "default": false - }, - "esr": { - "type": "boolean", - "default": false - }, - "uesa": { - "type": "boolean", - "default": false - }, - "hessid": { - "type": "string", - "example": "00:11:22:33:44:55" - }, - "roaming-consortium": { - "type": "array", - "items": { - "type": "string" - } - }, - "disable-dgaf": { - "type": "boolean", - "default": false - }, - "ipaddr-type-available": { - "type": "integer", - "maximum": 255 - }, - "connection-capability": { - "type": "array", - "items": { - "type": "string" - } - }, - "icons": { - "type": "array", - "items": { - "type": "object", - "properties": { - "width": { - "type": "integer", - "examples": [ - 64 - ] - }, - "height": { - "type": "integer", - "examples": [ - 64 - ] - }, - "type": { - "type": "string", - "examples": [ - "image/png" - ] - }, - "icon": { - "type": "string", - "format": "uc-base64" - }, - "language": { - "type": "string", - "pattern": "^[a-z][a-z][a-z]$", - "examples": [ - "eng", - "fre", - "ger", - "ita" - ] - } - }, - "examples": [ - { - "width": 32, - "height": 32, - "type": "image/png", - "language": "eng", - "icon": "R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" - } - ] - } - }, - "wan-metrics": { - "type": "object", - "properties": { - "info": { - "type": "string", - "enum": [ - "up", - "down", - "testing" - ] - }, - "downlink": { - "type": "integer" - }, - "uplink": { - "type": "integer" - } - } - } - } - }, - "interface.ssid.quality-thresholds": { - "type": "object", - "properties": { - "probe-request-rssi": { - "type": "integer" - }, - "association-request-rssi": { - "type": "integer" - }, - "client-kick-rssi": { - "type": "integer" - }, - "client-kick-ban-time": { - "type": "integer", - "default": 0 - } - } - }, - "interface.ssid.acl": { - "type": "object", - "properties": { - "mode": { - "type": "string", - "enum": [ - "allow", - "deny" - ] - }, - "mac-address": { - "type": "array", - "items": { - "type": "string", - "format": "uc-mac" - } - } - } - }, - "service.captive.click": { - "type": "object", - "properties": { - "auth-mode": { - "type": "string", - "const": "click-to-continue" - } - } - }, - "service.captive.radius": { - "type": "object", - "properties": { - "auth-mode": { - "type": "string", - "const": "radius" - }, - "auth-server": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "type": "integer", - "default": 600 - } - } - }, - "service.captive.credentials": { - "type": "object", - "properties": { - "auth-mode": { - "type": "string", - "const": "credentials" - }, - "credentials": { - "type": "array", - "items": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - } - }, - "service.captive.uam": { - "type": "object", - "properties": { - "auth-mode": { - "type": "string", - "const": "uam" - }, - "uam-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 3990 - }, - "uam-secret": { - "type": "string" - }, - "uam-server": { - "type": "string" - }, - "nasid": { - "type": "string" - }, - "nasmac": { - "type": "string" - }, - "auth-server": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "type": "integer", - "default": 600 - }, - "ssid": { - "type": "string" - }, - "mac-format": { - "type": "string", - "enum": [ - "aabbccddeeff", - "aa-bb-cc-dd-ee-ff", - "aa:bb:cc:dd:ee:ff", - "AABBCCDDEEFF", - "AA:BB:CC:DD:EE:FF", - "AA-BB-CC-DD-EE-FF" - ] - }, - "final-redirect-url": { - "type": "string", - "enum": [ - "default", - "uam" - ] - }, - "mac-auth": { - "type": "boolean", - "default": "default" - }, - "radius-gw-proxy": { - "type": "boolean", - "default": false - } - } - }, - "service.captive": { - "allOf": [ - { - "oneOf": [ - { - "$ref": "#/$defs/service.captive.click" - }, - { - "$ref": "#/$defs/service.captive.radius" - }, - { - "$ref": "#/$defs/service.captive.credentials" - }, - { - "$ref": "#/$defs/service.captive.uam" - } - ] - }, - { - "type": "object", - "properties": { - "walled-garden-fqdn": { - "type": "array", - "items": { - "type": "string" - } - }, - "walled-garden-ipaddr": { - "type": "array", - "items": { - "type": "string", - "format": "uc-ip" - } - }, - "web-root": { - "type": "string", - "format": "uc-base64" - }, - "idle-timeout": { - "type": "integer", - "default": 600 - }, - "session-timeout": { - "type": "integer" - } - } - } - ] - }, - "interface.ssid": { - "type": "object", - "properties": { - "purpose": { - "type": "string", - "enum": [ - "user-defined", - "onboarding-ap", - "onboarding-sta" - ], - "default": "user-defined" - }, - "name": { - "type": "string", - "maxLength": 32, - "minLength": 1 - }, - "wifi-bands": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - } - }, - "bss-mode": { - "type": "string", - "enum": [ - "ap", - "sta", - "mesh", - "wds-ap", - "wds-sta", - "wds-repeater" - ], - "default": "ap" - }, - "bssid": { - "type": "string", - "format": "uc-mac" - }, - "hidden-ssid": { - "type": "boolean" - }, - "isolate-clients": { - "type": "boolean" - }, - "strict-forwarding": { - "type": "boolean", - "default": false - }, - "power-save": { - "type": "boolean" - }, - "rts-threshold": { - "type": "integer", - "maximum": 65535, - "minimum": 1 - }, - "broadcast-time": { - "type": "boolean" - }, - "unicast-conversion": { - "type": "boolean", - "default": true - }, - "services": { - "type": "array", - "items": { - "type": "string", - "examples": [ - "wifi-steering" - ] - } - }, - "dtim-period": { - "type": "integer", - "default": 2, - "maximum": 255, - "minimum": 1 - }, - "maximum-clients": { - "type": "integer", - "example": 64 - }, - "proxy-arp": { - "type": "boolean", - "default": true - }, - "disassoc-low-ack": { - "decription": "Disassociate stations based on excessive transmission failures or other indications of connection loss.", - "type": "boolean", - "default": false - }, - "vendor-elements": { - "decription": "This option allows embedding custom vendor specific IEs inside the beacons of a BSS in AP mode.", - "type": "string" - }, - "tip-information-element": { - "decription": "The device will broadcast the TIP vendor IE inside its beacons if this option is enabled.", - "type": "boolean", - "default": true - }, - "fils-discovery-interval": { - "type": "integer", - "default": 20, - "maximum": 20 - }, - "encryption": { - "$ref": "#/$defs/interface.ssid.encryption" - }, - "multi-psk": { - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid.multi-psk" - } - }, - "rrm": { - "$ref": "#/$defs/interface.ssid.rrm" - }, - "rate-limit": { - "$ref": "#/$defs/interface.ssid.rate-limit" - }, - "roaming": { - "anyOf": [ - { - "$ref": "#/$defs/interface.ssid.roaming" - }, - { - "type": "boolean" - } - ] - }, - "radius": { - "$ref": "#/$defs/interface.ssid.radius" - }, - "certificates": { - "$ref": "#/$defs/interface.ssid.certificates" - }, - "pass-point": { - "$ref": "#/$defs/interface.ssid.pass-point" - }, - "quality-thresholds": { - "$ref": "#/$defs/interface.ssid.quality-thresholds" - }, - "access-control-list": { - "$ref": "#/$defs/interface.ssid.acl" - }, - "captive": { - "$ref": "#/$defs/service.captive" - }, - "hostapd-bss-raw": { - "type": "array", - "items": { - "type": "string", - "examples": [ - "ap_table_expiration_time=3600", - "device_type=6-0050F204-1", - "ieee80211h=1", - "rssi_ignore_probe_request=-75", - "time_zone=EST5", - "uuid=12345678-9abc-def0-1234-56789abcdef0", - "venue_url=1:http://www.example.com/info-eng", - "wpa_deny_ptk0_rekey=0" - ] - } - } - } - }, "interface.tunnel.mesh": { "type": "object", "properties": { @@ -3556,12 +2159,6 @@ "broad-band": { "$ref": "#/$defs/interface.broad-band" }, - "ssids": { - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid" - } - }, "tunnel": { "$ref": "#/$defs/interface.tunnel" } @@ -3728,106 +2325,6 @@ } } }, - "service.ieee8021x": { - "type": "object", - "properties": { - "mode": { - "type": "string", - "enum": [ - "radius", - "user" - ] - }, - "port-filter": { - "type": "array", - "items": { - "type": "string", - "examples": [ - { - "LAN1": null - } - ] - } - }, - "users": { - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid.radius.local-user" - } - }, - "radius": { - "type": "object", - "properties": { - "nas-identifier": { - "type": "string" - }, - "auth-server-addr": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-server-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "auth-server-secret": { - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server-addr": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-server-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1813 - ] - }, - "acct-server-secret": { - "type": "string", - "examples": [ - "secret" - ] - }, - "coa-server-addr": { - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "coa-server-port": { - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1814 - ] - }, - "coa-server-secret": { - "type": "string", - "examples": [ - "secret" - ] - } - } - } - } - }, "service.radius-proxy": { "type": "object", "properties": { @@ -4018,7 +2515,6 @@ "items": { "type": "string", "enum": [ - "wifi", "leds" ] } @@ -4045,49 +2541,6 @@ } } }, - "service.wifi-steering": { - "type": "object", - "properties": { - "mode": { - "type": "string", - "enum": [ - "local", - "none" - ], - "examples": [ - "local" - ] - }, - "assoc-steering": { - "type": "boolean", - "default": false - }, - "required-snr": { - "type": "integer", - "default": 0 - }, - "required-probe-snr": { - "type": "integer", - "default": 0 - }, - "required-roam-snr": { - "type": "integer", - "default": 0 - }, - "load-kick-threshold": { - "type": "integer", - "default": 0 - }, - "auto-channel": { - "type": "boolean", - "default": false - }, - "ipv6": { - "type": "boolean", - "default": false - } - } - }, "service.quality-of-service.class-selector": { "type": "string", "enum": [ @@ -4215,53 +2668,6 @@ } } }, - "service.facebook-wifi": { - "type": "object", - "properties": { - "vendor-id": { - "type": "string" - }, - "gateway-id": { - "type": "string" - }, - "secret": { - "type": "string" - } - } - }, - "service.airtime-fairness": { - "type": "object", - "properties": { - "voice-weight": { - "type": "number", - "default": 4 - }, - "packet-threshold": { - "type": "number", - "default": 100 - }, - "bulk-threshold": { - "type": "number", - "default": 50 - }, - "priority-threshold": { - "type": "number", - "default": 30 - }, - "weight-normal": { - "type": "number", - "default": 256 - }, - "weight-priority": { - "type": "number", - "default": 394 - }, - "weight-bulk": { - "type": "number", - "default": 128 - } - } - }, "service.wireguard-overlay": { "type": "object", "properties": { @@ -4376,94 +2782,6 @@ } } }, - "service.dhcp-relay": { - "type": "object", - "properties": { - "select-ports": { - "type": "array", - "items": { - "type": "string" - } - }, - "vlans": { - "type": "array", - "items": { - "type": "object", - "properties": { - "vlan": { - "type": "number" - }, - "relay-server": { - "type": "string", - "format": "uc-ip" - }, - "circuit-id-format": { - "type": "string", - "enum": [ - "vlan-id", - "ap-mac", - "ssid" - ], - "default": "vlan-id" - }, - "remote-id-format": { - "type": "string", - "enum": [ - "vlan-id", - "ap-mac", - "ssid" - ], - "default": "ap-mac" - } - } - } - } - } - }, - "service.admin-ui": { - "type": "object", - "properties": { - "wifi-ssid": { - "type": "string", - "default": "Maverick", - "maxLength": 32, - "minLength": 1 - }, - "wifi-key": { - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "wifi-bands": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - } - }, - "offline-trigger": { - "type": "number" - } - } - }, - "service.rrm": { - "type": "object", - "properties": { - "beacon-request-assoc": { - "type": "boolean", - "default": true - }, - "station-stats-interval": { - "type": "number" - } - } - }, "service.telnet": { "type": "object", "properties": { @@ -4513,9 +2831,6 @@ "igmp": { "$ref": "#/$defs/service.igmp" }, - "ieee8021x": { - "$ref": "#/$defs/service.ieee8021x" - }, "radius-proxy": { "$ref": "#/$defs/service.radius-proxy" }, @@ -4525,36 +2840,15 @@ "data-plane": { "$ref": "#/$defs/service.data-plane" }, - "wifi-steering": { - "$ref": "#/$defs/service.wifi-steering" - }, "quality-of-service": { "$ref": "#/$defs/service.quality-of-service" }, - "facebook-wifi": { - "$ref": "#/$defs/service.facebook-wifi" - }, - "airtime-fairness": { - "$ref": "#/$defs/service.airtime-fairness" - }, "wireguard-overlay": { "$ref": "#/$defs/service.wireguard-overlay" }, - "captive": { - "$ref": "#/$defs/service.captive" - }, "gps": { "$ref": "#/$defs/service.gps" }, - "dhcp-relay": { - "$ref": "#/$defs/service.dhcp-relay" - }, - "admin-ui": { - "$ref": "#/$defs/service.admin-ui" - }, - "rrm": { - "$ref": "#/$defs/service.rrm" - }, "telnet": { "$ref": "#/$defs/service.telnet" }, @@ -4575,10 +2869,8 @@ "items": { "type": "string", "enum": [ - "ssids", "lldp", - "clients", - "tid-stats" + "clients" ] } }, @@ -4612,29 +2904,6 @@ } } }, - "metrics.wifi-frames": { - "type": "object", - "properties": { - "filters": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "probe", - "auth", - "assoc", - "disassoc", - "deauth", - "local-deauth", - "inactive-deauth", - "key-mismatch", - "beacon-report", - "radar-detected" - ] - } - } - } - }, "metrics.dhcp-snooping": { "type": "object", "properties": { @@ -4655,20 +2924,6 @@ } } }, - "metrics.wifi-scan": { - "type": "object", - "properties": { - "interval": { - "type": "integer" - }, - "verbose": { - "type": "boolean" - }, - "information-elements": { - "type": "boolean" - } - } - }, "metrics.telemetry": { "type": "object", "properties": { @@ -4690,9 +2945,6 @@ "client.join", "client.leave", "client.key-mismatch", - "wifi", - "wifi.start", - "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", @@ -4721,9 +2973,6 @@ "client.join", "client.leave", "client.key-mismatch", - "wifi", - "wifi.start", - "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", @@ -4743,15 +2992,9 @@ "health": { "$ref": "#/$defs/metrics.health" }, - "wifi-frames": { - "$ref": "#/$defs/metrics.wifi-frames" - }, "dhcp-snooping": { "$ref": "#/$defs/metrics.dhcp-snooping" }, - "wifi-scan": { - "$ref": "#/$defs/metrics.wifi-scan" - }, "telemetry": { "$ref": "#/$defs/metrics.telemetry" }, diff --git a/ucentral.schema.pretty.json b/ucentral.schema.pretty.json index 88074d1..2cd29c9 100644 --- a/ucentral.schema.pretty.json +++ b/ucentral.schema.pretty.json @@ -24,9 +24,6 @@ "globals": { "$ref": "#/$defs/globals" }, - "definitions": { - "$ref": "#/$defs/definitions" - }, "ethernet": { "type": "array", "items": { @@ -36,12 +33,6 @@ "switch": { "$ref": "#/$defs/switch" }, - "radios": { - "type": "array", - "items": { - "$ref": "#/$defs/radio" - } - }, "interfaces": { "type": "array", "items": { @@ -171,84 +162,6 @@ } } }, - "globals.wireless-multimedia.class-selector": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "DF", - "EF", - "VA", - "LE" - ] - } - }, - "globals.wireless-multimedia.table": { - "description": "Define the default WMM behaviour of all SSIDs on the device. Each access category can be assigned a default class selector that gets used for packet matching.", - "type": "object", - "additionalProperties": false, - "properties": { - "UP0": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP1": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP2": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP3": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP4": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP5": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP6": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - }, - "UP7": { - "$ref": "#/$defs/globals.wireless-multimedia.class-selector" - } - } - }, - "globals.wireless-multimedia.profile": { - "type": "object", - "additionalProperties": false, - "properties": { - "profile": { - "description": "Define a default profile that shall be used for the WMM behaviour of all SSIDs on the device.", - "type": "string", - "enum": [ - "enterprise", - "rfc8325", - "3gpp" - ] - } - } - }, "globals": { "description": "A device has certain global properties that are used to derive parts of the final configuration that gets applied.", "type": "object", @@ -269,16 +182,6 @@ "fdca:1234:4567::/48" ] }, - "wireless-multimedia": { - "anyOf": [ - { - "$ref": "#/$defs/globals.wireless-multimedia.table" - }, - { - "$ref": "#/$defs/globals.wireless-multimedia.profile" - } - ] - }, "ipv4-blackhole": { "description": "Define a list of non-interface specific BLACKHOLE (to-nowhere) routes.", "type": "array", @@ -323,73 +226,6 @@ } } }, - "interface.ssid.encryption": { - "description": "A device has certain properties that describe its identity and location. These properties are described inside this object.", - "type": "object", - "properties": { - "proto": { - "description": "The wireless encryption protocol that shall be used for this BSS", - "type": "string", - "enum": [ - "none", - "owe", - "owe-transition", - "psk", - "psk2", - "psk-mixed", - "psk2-radius", - "wpa", - "wpa2", - "wpa-mixed", - "sae", - "sae-mixed", - "wpa3", - "wpa3-192", - "wpa3-mixed" - ], - "examples": [ - "psk2" - ] - }, - "key": { - "description": "The Pre Shared Key (PSK) that is used for encryption on the BSS when using any of the WPA-PSK modes.", - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "ieee80211w": { - "description": "Enable 802.11w Management Frame Protection (MFP) for this BSS.", - "type": "string", - "enum": [ - "disabled", - "optional", - "required" - ], - "default": "disabled" - }, - "key-caching": { - "description": "PMKSA created through EAP authentication and RSN preauthentication can be cached.", - "type": "boolean", - "default": true - } - } - }, - "definitions": { - "description": "This section is used to define templates that can be referenced by a configuration. This avoids duplication of data. A RADIUS server can be defined here for example and then referenced by several SSIDs.", - "type": "object", - "properties": { - "wireless-encryption": { - "type": "object", - "description": "A dictionary of wireless encryption templates which can be referenced by the corresponding property name.", - "patternProperties": { - ".+": { - "$ref": "#/$defs/interface.ssid.encryption", - "additionalProperties": false - } - } - } - } - }, "ethernet": { "description": "This section defines the linkk speed and duplex mode of the physical copper/fiber ports of the device.", "type": "object", @@ -1621,233 +1457,6 @@ } } }, - "radio.rates": { - "description": "The rate configuration of this BSS.", - "type": "object", - "properties": { - "beacon": { - "description": "The beacon rate that shall be used by the BSS. Values are in Mbps.", - "type": "integer", - "default": 6000, - "enum": [ - 0, - 1000, - 2000, - 5500, - 6000, - 9000, - 11000, - 12000, - 18000, - 24000, - 36000, - 48000, - 54000 - ] - }, - "multicast": { - "description": "The multicast rate that shall be used by the BSS. Values are in Mbps.", - "type": "integer", - "default": 24000, - "enum": [ - 0, - 1000, - 2000, - 5500, - 6000, - 9000, - 11000, - 12000, - 18000, - 24000, - 36000, - 48000, - 54000 - ] - } - } - }, - "radio.he": { - "description": "This section describes the HE specific configuration options of the BSS.", - "type": "object", - "properties": { - "multiple-bssid": { - "description": "Enabling this option will make the PHY broadcast its BSSs using the multiple BSSID beacon IE.", - "type": "boolean", - "default": false - }, - "ema": { - "description": "Enableing this option will make the PHY broadcast its multiple BSSID beacons using EMA.", - "type": "boolean", - "default": false - }, - "bss-color": { - "description": "This enables BSS Coloring on the PHY. setting it to 0 disables the feature 1-63 sets the color and 64 will make hostapd pick a random color.", - "type": "integer", - "default": 64 - } - } - }, - "radio": { - "description": "Describe a physical radio on the AP. A radio is be parent to several VAPs. They all share the same physical properties.", - "type": "object", - "properties": { - "band": { - "description": "Specifies the wireless band to configure the radio for. Available radio device phys on the target system are matched by the wireless band given here. If multiple radio phys support the same band, the settings specified here will be applied to all of them.", - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - }, - "bandwidth": { - "description": "Specifies a narrow channel width in MHz, possible values are 5, 10, 20.", - "type": "integer", - "enum": [ - 5, - 10, - 20 - ] - }, - "channel": { - "description": "Specifies the wireless channel to use. A value of 'auto' starts the ACS algorithm.", - "oneOf": [ - { - "type": "integer", - "maximum": 196, - "minimum": 1 - }, - { - "type": "string", - "const": "auto" - } - ] - }, - "valid-channels": { - "description": "Pass a list of valid-channels that can be used during ACS.", - "type": "array", - "items": { - "type": "integer", - "maximum": 196, - "minimum": 1 - } - }, - "country": { - "description": "Specifies the country code, affects the available channels and transmission powers.", - "type": "string", - "maxLength": 2, - "minLength": 2, - "examples": [ - "US" - ] - }, - "allow-dfs": { - "description": "This property defines whether a radio may use DFS channels.", - "type": "boolean", - "default": true - }, - "channel-mode": { - "description": "Define the ideal channel mode that the radio shall use. This can be 802.11n, 802.11ac or 802.11ax. This is just a hint for the AP. If the requested value is not supported then the AP will use the highest common denominator.", - "type": "string", - "enum": [ - "HT", - "VHT", - "HE", - "EHT" - ], - "default": "HE" - }, - "channel-width": { - "description": "The channel width that the radio shall use. This is just a hint for the AP. If the requested value is not supported then the AP will use the highest common denominator.", - "type": "integer", - "enum": [ - 20, - 40, - 80, - 160, - 320, - 8080 - ], - "default": 80 - }, - "require-mode": { - "description": "Stations that do no fulfill these HT modes will be rejected.", - "type": "string", - "enum": [ - "HT", - "VHT", - "HE" - ] - }, - "mimo": { - "description": "This option allows configuring the antenna pairs that shall be used. This is just a hint for the AP. If the requested value is not supported then the AP will use the highest common denominator.", - "type": "string", - "enum": [ - "1x1", - "2x2", - "3x3", - "4x4", - "5x5", - "6x6", - "7x7", - "8x8" - ] - }, - "tx-power": { - "description": "This option specifies the transmission power in dBm", - "type": "integer", - "maximum": 30, - "minimum": 0 - }, - "legacy-rates": { - "description": "Allow legacy 802.11b data rates.", - "type": "boolean", - "default": false - }, - "beacon-interval": { - "description": "Beacon interval in kus (1.024 ms).", - "type": "integer", - "default": 100, - "maximum": 65535, - "minimum": 15 - }, - "maximum-clients": { - "description": "Set the maximum number of clients that may connect to this radio. This value is accumulative for all attached VAP interfaces.", - "type": "integer", - "example": 64 - }, - "maximum-clients-ignore-probe": { - "description": "Ignore probe requests if maximum-clients was reached.", - "type": "boolean" - }, - "rates": { - "$ref": "#/$defs/radio.rates" - }, - "he-settings": { - "$ref": "#/$defs/radio.he" - }, - "hostapd-iface-raw": { - "description": "This array allows passing raw hostapd.conf lines.", - "type": "array", - "items": { - "type": "string", - "examples": [ - "ap_table_expiration_time=3600", - "device_type=6-0050F204-1", - "ieee80211h=1", - "rssi_ignore_probe_request=-75", - "time_zone=EST5", - "uuid=12345678-9abc-def0-1234-56789abcdef0", - "venue_url=1:http://www.example.com/info-eng", - "wpa_deny_ptk0_rekey=0" - ] - } - } - } - }, "interface.vlan": { "description": "This section describes the vlan behaviour of a logical network interface.", "type": "object", @@ -2695,1204 +2304,6 @@ } ] }, - "interface.ssid.multi-psk": { - "type": "object", - "description": "A SSID can have multiple PSK/VID mappings. Each one of them can be bound to a specific MAC or be a wildcard.", - "properties": { - "mac": { - "type": "string", - "format": "uc-mac" - }, - "key": { - "description": "The Pre Shared Key (PSK) that is used for encryption on the BSS when using any of the WPA-PSK modes.", - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "vlan-id": { - "type": "integer", - "maximum": 4096, - "examples": [ - 3, - 100, - 200, - 4094 - ] - } - } - }, - "interface.ssid.rrm": { - "description": "Enable 802.11k Radio Resource Management (RRM) for this BSS.", - "type": "object", - "properties": { - "neighbor-reporting": { - "description": "Enable neighbor report via radio measurements (802.11k).", - "type": "boolean", - "default": false - }, - "reduced-neighbor-reporting": { - "description": "Enable reduced neighbor reports.", - "type": "boolean", - "default": false - }, - "lci": { - "description": "The content of a LCI measurement subelement", - "type": "string" - }, - "civic-location": { - "description": "The content of a location civic measurement subelement", - "type": "string" - }, - "ftm-responder": { - "description": "Publish fine timing measurement (FTM) responder functionality on this BSS.", - "type": "boolean", - "default": false - }, - "stationary-ap": { - "description": "Stationary AP config indicates that the AP doesn't move.", - "type": "boolean", - "default": false - } - } - }, - "interface.ssid.rate-limit": { - "description": "The UE rate-limiting configuration of this BSS.", - "type": "object", - "properties": { - "ingress-rate": { - "description": "The ingress rate to which hosts will be shaped. Values are in Mbps", - "type": "integer", - "default": 0 - }, - "egress-rate": { - "description": "The egress rate to which hosts will be shaped. Values are in Mbps", - "type": "integer", - "default": 0 - } - } - }, - "interface.ssid.roaming": { - "description": "Enable 802.11r Fast Roaming for this BSS.", - "type": "object", - "properties": { - "message-exchange": { - "description": "Shall the pre authenticated message exchange happen over the air or distribution system.", - "type": "string", - "enum": [ - "air", - "ds" - ], - "default": "ds" - }, - "generate-psk": { - "description": "Whether to generate FT response locally for PSK networks. This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks.", - "type": "boolean", - "default": false - }, - "domain-identifier": { - "description": "Mobility Domain identifier (dot11FTMobilityDomainID, MDID).", - "type": "string", - "maxLength": 4, - "minLength": 4, - "examples": [ - "abcd" - ] - }, - "pmk-r0-key-holder": { - "description": "The pairwise master key R0. This is unique to the mobility domain and is required for fast roaming over the air. If the field is left empty a deterministic key is generated.", - "type": "string", - "example": "14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff" - }, - "pmk-r1-key-holder": { - "description": "The pairwise master key R1. This is unique to the mobility domain and is required for fast roaming over the air. If the field is left empty a deterministic key is generated.", - "type": "string", - "example": "14:DD:20:47:14:E4,14DD204714E4,00112233445566778899aabbccddeeff" - } - } - }, - "interface.ssid.radius.local-user": { - "type": "object", - "description": "Describes a local EAP user/psk/vid triplet.", - "properties": { - "mac": { - "type": "string", - "format": "uc-mac" - }, - "user-name": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "vlan-id": { - "type": "integer", - "maximum": 4096, - "examples": [ - 3, - 100, - 200, - 4094 - ] - } - } - }, - "interface.ssid.radius.local": { - "description": "Describe the properties of the local Radius server inside hostapd.", - "type": "object", - "properties": { - "server-identity": { - "description": "EAP methods that provide mechanism for authenticated server identity delivery use this value.", - "type": "string", - "default": "uCentral" - }, - "users": { - "description": "Specifies a collection of local EAP user/psk/vid triplets.", - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid.radius.local-user" - } - } - } - }, - "interface.ssid.radius.server": { - "description": "Describe the properties of a Radius server.", - "type": "object", - "properties": { - "host": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "secondary": { - "description": "Definition of the secondary/failsafe radius server.", - "type": "object", - "properties": { - "host": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - } - } - }, - "request-attribute": { - "description": "The additional Access-Request attributes that gets sent to the server.", - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "vendor-id": { - "type": "integer", - "description": "The ID of the vendor specific RADIUS attribute", - "maximum": 65535, - "minimum": 1 - }, - "vendor-attributes": { - "type": "array", - "items": { - "type": "object", - "description": "The numeric RADIUS attribute value", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the vendor specific RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string", - "description": "The vendor specific RADIUS attribute value. This needs to be a hexadecimal string." - } - } - } - } - } - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "integer", - "description": "The numeric RADIUS attribute value", - "maximum": 4294967295, - "minimum": 0 - } - }, - "examples": [ - { - "id": 27, - "value": 900 - }, - { - "id": 56, - "value": 1004 - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "value": { - "type": "string", - "description": "The RADIUS attribute value string" - } - }, - "examples": [ - { - "id": 32, - "value": "My NAS ID" - }, - { - "id": 126, - "value": "Example Operator" - } - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The ID of the RADIUS attribute", - "maximum": 255, - "minimum": 1 - }, - "hex-value": { - "type": "string", - "description": "The RADIUS attribute value string" - } - }, - "examples": [ - { - "id": 32, - "value": "0a0b0c0d" - } - ] - } - ] - } - } - } - }, - "interface.ssid.radius.health": { - "description": "The credentials used when health check probes this radius server.", - "type": "object", - "properties": { - "username": { - "description": "The username that gets used when doing a healthcheck on this radius server.", - "type": "string" - }, - "password": { - "description": "The password that gets used when doing a healthcheck on this radius server.", - "type": "string" - } - } - }, - "interface.ssid.radius": { - "description": "When using EAP encryption we need to provide the required information allowing us to connect to the AAA servers.", - "type": "object", - "properties": { - "nas-identifier": { - "description": "NAS-Identifier string for RADIUS messages. When used, this should be unique to the NAS within the scope of the RADIUS server.", - "type": "string" - }, - "chargeable-user-id": { - "description": "This will enable support for Chargeable-User-Identity (RFC 4372).", - "type": "boolean", - "default": false - }, - "local": { - "$ref": "#/$defs/interface.ssid.radius.local" - }, - "dynamic-authorization": { - "description": "Dynamic Authorization Extensions (DAE) is an extension to Radius.", - "type": "object", - "properties": { - "host": { - "description": "The IP of the DAE client.", - "type": "string", - "format": "uc-ip", - "examples": [ - "192.168.1.10" - ] - }, - "port": { - "description": "The network port that the DAE client can connet on.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "secret": { - "description": "The shared DAE authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - } - } - }, - "authentication": { - "allOf": [ - { - "$ref": "#/$defs/interface.ssid.radius.server" - }, - { - "type": "object", - "properties": { - "mac-filter": { - "description": "Should the radius server be used for MAC address ACL.", - "type": "boolean", - "default": false - } - } - } - ] - }, - "accounting": { - "allOf": [ - { - "$ref": "#/$defs/interface.ssid.radius.server" - }, - { - "type": "object", - "properties": { - "interval": { - "description": "The interim accounting update interval. This value is defined in seconds.", - "type": "integer", - "maximum": 600, - "minimum": 60, - "default": 60 - } - } - } - ] - }, - "health": { - "$ref": "#/$defs/interface.ssid.radius.health" - } - } - }, - "interface.ssid.certificates": { - "description": "When running a local EAP server or using STA/MESH to connect to another BSS a set of certificates is required.", - "type": "object", - "properties": { - "use-local-certificates": { - "description": "The device will use its local certificate bundle for the TLS setup and ignores all other certificate options in this section.", - "type": "boolean", - "default": false - }, - "ca-certificate": { - "description": "The local servers CA bundle.", - "type": "string" - }, - "certificate": { - "description": "The local servers certificate.", - "type": "string" - }, - "private-key": { - "description": "The local servers private key/", - "type": "string" - }, - "private-key-password": { - "description": "The password required to read the private key.", - "type": "string" - } - } - }, - "interface.ssid.pass-point": { - "description": "Enable Hotspot 2.0 support.", - "type": "object", - "properties": { - "venue-name": { - "description": "This parameter can be used to configure one or more Venue Name Duples for Venue Name ANQP information.", - "type": "array", - "items": { - "type": "string" - } - }, - "venue-group": { - "description": "The available values are defined in 802.11u.", - "type": "integer", - "maximum": 32 - }, - "venue-type": { - "description": "The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34", - "type": "integer", - "maximum": 32 - }, - "venue-url": { - "description": "This parameter can be used to configure one or more Venue URL Duples to provide additional information corresponding to Venue Name information.", - "type": "array", - "items": { - "type": "string", - "format": "uri" - } - }, - "auth-type": { - "description": "This parameter indicates what type of network authentication is used in the network.", - "type": "object", - "properties": { - "type": { - "description": "Specifies the specific network authentication type in use.", - "type": "string", - "enum": [ - "terms-and-conditions", - "online-enrollment", - "http-redirection", - "dns-redirection" - ] - }, - "uri": { - "description": "Specifies the redirect URL applicable to the indicated authentication type.", - "type": "string", - "format": "uri", - "examples": [ - "https://operator.example.org/wireless-access/terms-and-conditions.html", - "http://www.example.com/redirect/me/here/" - ] - } - }, - "minLength": 2, - "maxLength": 2 - }, - "domain-name": { - "description": "The IEEE 802.11u Domain Name.", - "type": "array", - "items": { - "type": "string", - "format": "hostname" - } - }, - "nai-realm": { - "description": "NAI Realm information", - "type": "array", - "items": { - "type": "string" - } - }, - "osen": { - "description": "OSU Server-Only Authenticated L2 Encryption Network;", - "type": "boolean" - }, - "anqp-domain": { - "description": "ANQP Domain ID, An identifier for a set of APs in an ESS that share the same common ANQP information.", - "type": "integer", - "maximum": 65535, - "minimum": 0 - }, - "anqp-3gpp-cell-net": { - "description": "The ANQP 3GPP Cellular Network information.", - "type": "array", - "items": { - "type": "string" - } - }, - "friendly-name": { - "description": "This parameter can be used to configure one or more Operator Friendly Name Duples.", - "type": "array", - "items": { - "type": "string" - } - }, - "access-network-type": { - "description": "Indicate the type of network. This is part of the interworking IE.", - "type": "integer", - "maximum": 15, - "default": 0 - }, - "internet": { - "description": "Whether the network provides connectivity to the Internet", - "type": "boolean", - "default": true - }, - "asra": { - "description": "Additional Step Required for Access.", - "type": "boolean", - "default": false - }, - "esr": { - "description": "Emergency services reachable.", - "type": "boolean", - "default": false - }, - "uesa": { - "description": "Unauthenticated emergency service accessible.", - "type": "boolean", - "default": false - }, - "hessid": { - "description": "Homogeneous ESS identifier", - "type": "string", - "example": "00:11:22:33:44:55" - }, - "roaming-consortium": { - "description": "Roaming Consortium OIs can be configured here. Each OI is between 3 and 15 octets and is configured as a hexstring.", - "type": "array", - "items": { - "type": "string" - } - }, - "disable-dgaf": { - "description": "Disable Downstream Group-Addressed Forwarding. This can be used to configure a network where no group-addressed frames are allowed.", - "type": "boolean", - "default": false - }, - "ipaddr-type-available": { - "description": "IP Address Type Availability.", - "type": "integer", - "maximum": 255 - }, - "connection-capability": { - "description": "This can be used to advertise what type of IP traffic can be sent through the hotspot.", - "type": "array", - "items": { - "type": "string" - } - }, - "icons": { - "description": "The operator icons.", - "type": "array", - "items": { - "type": "object", - "properties": { - "width": { - "type": "integer", - "description": "The width of the operator icon in pixel", - "examples": [ - 64 - ] - }, - "height": { - "type": "integer", - "description": "The height of the operator icon in pixel", - "examples": [ - 64 - ] - }, - "type": { - "type": "string", - "description": "The mimetype of the operator icon", - "examples": [ - "image/png" - ] - }, - "icon": { - "type": "string", - "format": "uc-base64", - "description": "The base64 encoded image" - }, - "language": { - "type": "string", - "description": "ISO 639-2 language code of the icon", - "pattern": "^[a-z][a-z][a-z]$", - "examples": [ - "eng", - "fre", - "ger", - "ita" - ] - } - }, - "examples": [ - { - "width": 32, - "height": 32, - "type": "image/png", - "language": "eng", - "icon": "R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" - } - ] - } - }, - "wan-metrics": { - "description": "A description of the wan metric offered by this device.", - "type": "object", - "properties": { - "info": { - "description": "The state of the devices uplink", - "type": "string", - "enum": [ - "up", - "down", - "testing" - ] - }, - "downlink": { - "description": "Estimate of WAN backhaul link current downlink speed in kbps.", - "type": "integer" - }, - "uplink": { - "description": "Estimate of WAN backhaul link current uplink speed in kbps.", - "type": "integer" - } - } - } - } - }, - "interface.ssid.quality-thresholds": { - "description": "The thresholds that need to be meet for a clien association to be allowed.", - "type": "object", - "properties": { - "probe-request-rssi": { - "description": "Probe requests will be ignored if the rssi is below this threshold.", - "type": "integer" - }, - "association-request-rssi": { - "description": "Association requests will be denied if the rssi is below this threshold.", - "type": "integer" - }, - "client-kick-rssi": { - "description": "Clients will get kicked if their SNR drops below this value.", - "type": "integer" - }, - "client-kick-ban-time": { - "description": "The duration that a client is banned from re-joining after it was kicked.", - "type": "integer", - "default": 0 - } - } - }, - "interface.ssid.acl": { - "description": "The MAC ACL that defines which clients are allowed or denied to associations.", - "type": "object", - "properties": { - "mode": { - "description": "Defines if this is an allow or deny list.", - "type": "string", - "enum": [ - "allow", - "deny" - ] - }, - "mac-address": { - "description": "Association requests will be denied if the rssi is below this threshold.", - "type": "array", - "items": { - "type": "string", - "format": "uc-mac" - } - } - } - }, - "service.captive.click": { - "description": "This section can be used to setup a captive portal on the AP with a click-to-continue splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'click-to-continue'", - "type": "string", - "const": "click-to-continue" - } - } - }, - "service.captive.radius": { - "description": "This section can be used to setup a captive portal on the AP with a click-to-continue splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'radius'", - "type": "string", - "const": "radius" - }, - "auth-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "description": "The timeout used for interim messages.", - "type": "integer", - "default": 600 - } - } - }, - "service.captive.credentials": { - "description": "This section can be used to setup a captive portal on the AP with a credentials splash page.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'credentials'", - "type": "string", - "const": "credentials" - }, - "credentials": { - "description": "The list of local username/password pairs that can be used to login.", - "type": "array", - "items": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - } - }, - "service.captive.uam": { - "description": "This section can be used to setup a captive portal on the AP with a remote UAM server.", - "type": "object", - "properties": { - "auth-mode": { - "description": "This field must be set to 'uam'", - "type": "string", - "const": "uam" - }, - "uam-port": { - "description": "The local UAM port.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 3990 - }, - "uam-secret": { - "description": "The pre-shared UAM secret.", - "type": "string" - }, - "uam-server": { - "description": "The fqdn of the UAM server.", - "type": "string" - }, - "nasid": { - "description": "The NASID that gets sent to the UAM server.", - "type": "string" - }, - "nasmac": { - "description": "The NAS MAC that gets send to the UAM server. The devices serial is used if this value is not provided.", - "type": "string" - }, - "auth-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "auth-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server": { - "description": "The URI of our Radius Authentication server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-port": { - "description": "The network port of our Radius Authentication server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "default": 1812 - }, - "acct-secret": { - "description": "The shared Radius authentication Authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-interval": { - "description": "The timeout used for interim messages.", - "type": "integer", - "default": 600 - }, - "ssid": { - "description": "The name of the SSID that shall be sent as part of the UAM redirect.", - "type": "string" - }, - "mac-format": { - "description": "Defines the format used to send the MAC address inside AAA frames.", - "type": "string", - "enum": [ - "aabbccddeeff", - "aa-bb-cc-dd-ee-ff", - "aa:bb:cc:dd:ee:ff", - "AABBCCDDEEFF", - "AA:BB:CC:DD:EE:FF", - "AA-BB-CC-DD-EE-FF" - ] - }, - "final-redirect-url": { - "description": "Define the behaviour off the final redirect. Default will honour \"userurl\" and fallback to \"local\". Alternatively it is possible to force a redirect to the \"UAM\" or \"local\" URL.", - "type": "string", - "enum": [ - "default", - "uam" - ] - }, - "mac-auth": { - "description": "Try to authenticate new clients using macauth.", - "type": "boolean", - "default": "default" - }, - "radius-gw-proxy": { - "description": "Tunnel all radius traffic via the radius-gw-proxy.", - "type": "boolean", - "default": false - } - } - }, - "service.captive": { - "allOf": [ - { - "oneOf": [ - { - "$ref": "#/$defs/service.captive.click" - }, - { - "$ref": "#/$defs/service.captive.radius" - }, - { - "$ref": "#/$defs/service.captive.credentials" - }, - { - "$ref": "#/$defs/service.captive.uam" - } - ] - }, - { - "type": "object", - "properties": { - "walled-garden-fqdn": { - "description": "The list of FQDNs that a non-authenticated client is allowed to connect to.", - "type": "array", - "items": { - "type": "string" - } - }, - "walled-garden-ipaddr": { - "description": "The list of IP addresses that a non-authenticated client is allowed to connect to.", - "type": "array", - "items": { - "type": "string", - "format": "uc-ip" - } - }, - "web-root": { - "description": "A base64 encoded TAR file with the custom web-root.", - "type": "string", - "format": "uc-base64" - }, - "idle-timeout": { - "description": "How long may a client be idle before getting removed.", - "type": "integer", - "default": 600 - }, - "session-timeout": { - "description": "How long may a client be idle before getting removed.", - "type": "integer" - } - } - } - ] - }, - "interface.ssid": { - "description": "A device has certain properties that describe its identity and location. These properties are described inside this object.", - "type": "object", - "properties": { - "purpose": { - "description": "An SSID can have a special purpose such as the hidden on-boarding BSS. All purposes other than \"user-defined\" are static pre-defined configurations.", - "type": "string", - "enum": [ - "user-defined", - "onboarding-ap", - "onboarding-sta" - ], - "default": "user-defined" - }, - "name": { - "description": "The broadcasted SSID of the wireless network and for for managed mode the SSID of the network you\u2019re connecting to", - "type": "string", - "maxLength": 32, - "minLength": 1 - }, - "wifi-bands": { - "description": "The band that the SSID should be broadcasted on. The configuration layer will use the first matching band.", - "type": "array", - "items": { - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - } - }, - "bss-mode": { - "description": "Selects the operation mode of the wireless network interface controller.", - "type": "string", - "enum": [ - "ap", - "sta", - "mesh", - "wds-ap", - "wds-sta", - "wds-repeater" - ], - "default": "ap" - }, - "bssid": { - "description": "Override the BSSID of the network, only applicable in adhoc or sta mode.", - "type": "string", - "format": "uc-mac" - }, - "hidden-ssid": { - "description": "Disables the broadcasting of beacon frames if set to 1 and,in doing so, hides the ESSID.", - "type": "boolean" - }, - "isolate-clients": { - "description": "Isolates wireless clients from each other on this BSS.", - "type": "boolean" - }, - "strict-forwarding": { - "description": "Isolate the BSS from all other members on the bridge apart from the first wired port.", - "type": "boolean", - "default": false - }, - "power-save": { - "description": "Unscheduled Automatic Power Save Delivery.", - "type": "boolean" - }, - "rts-threshold": { - "description": "Set the RTS/CTS threshold of the BSS.", - "type": "integer", - "maximum": 65535, - "minimum": 1 - }, - "broadcast-time": { - "description": "This option will make the unit braodcast the time inside its beacons.", - "type": "boolean" - }, - "unicast-conversion": { - "description": "Convert multicast traffic to unicast on this BSS.", - "type": "boolean", - "default": true - }, - "services": { - "description": "The services that shall be offered on this logical interface. These are just strings such as \"wifi-steering\"", - "type": "array", - "items": { - "type": "string", - "examples": [ - "wifi-steering" - ] - } - }, - "dtim-period": { - "description": "Set the DTIM (delivery traffic information message) period. There will be one DTIM per this many beacon frames. This may be set between 1 and 255. This option only has an effect on ap wifi-ifaces.", - "type": "integer", - "default": 2, - "maximum": 255, - "minimum": 1 - }, - "maximum-clients": { - "description": "Set the maximum number of clients that may connect to this VAP.", - "type": "integer", - "example": 64 - }, - "proxy-arp": { - "description": "Proxy ARP is the technique in which the host router, answers ARP requests intended for another machine.", - "type": "boolean", - "default": true - }, - "disassoc-low-ack": { - "decription": "Disassociate stations based on excessive transmission failures or other indications of connection loss.", - "type": "boolean", - "default": false - }, - "vendor-elements": { - "decription": "This option allows embedding custom vendor specific IEs inside the beacons of a BSS in AP mode.", - "type": "string" - }, - "tip-information-element": { - "decription": "The device will broadcast the TIP vendor IE inside its beacons if this option is enabled.", - "type": "boolean", - "default": true - }, - "fils-discovery-interval": { - "description": "The maximum interval for FILS discovery announcement frames. This is a condensed beacon used in 6GHz channels for passive BSS discovery.", - "type": "integer", - "default": 20, - "maximum": 20 - }, - "encryption": { - "$ref": "#/$defs/interface.ssid.encryption" - }, - "multi-psk": { - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid.multi-psk" - } - }, - "rrm": { - "$ref": "#/$defs/interface.ssid.rrm" - }, - "rate-limit": { - "$ref": "#/$defs/interface.ssid.rate-limit" - }, - "roaming": { - "anyOf": [ - { - "$ref": "#/$defs/interface.ssid.roaming" - }, - { - "description": "Enable 802.11r Fast Roaming for this BSS. This will enable \"auto\" mode which will work for most scenarios.", - "type": "boolean" - } - ] - }, - "radius": { - "$ref": "#/$defs/interface.ssid.radius" - }, - "certificates": { - "$ref": "#/$defs/interface.ssid.certificates" - }, - "pass-point": { - "$ref": "#/$defs/interface.ssid.pass-point" - }, - "quality-thresholds": { - "$ref": "#/$defs/interface.ssid.quality-thresholds" - }, - "access-control-list": { - "$ref": "#/$defs/interface.ssid.acl" - }, - "captive": { - "$ref": "#/$defs/service.captive" - }, - "hostapd-bss-raw": { - "description": "This array allows passing raw hostapd.conf lines.", - "type": "array", - "items": { - "type": "string", - "examples": [ - "ap_table_expiration_time=3600", - "device_type=6-0050F204-1", - "ieee80211h=1", - "rssi_ignore_probe_request=-75", - "time_zone=EST5", - "uuid=12345678-9abc-def0-1234-56789abcdef0", - "venue_url=1:http://www.example.com/info-eng", - "wpa_deny_ptk0_rekey=0" - ] - } - } - } - }, "interface.tunnel.mesh": { "description": "This Object defines the properties of a mesh interface overlay.", "type": "object", @@ -4108,12 +2519,6 @@ "broad-band": { "$ref": "#/$defs/interface.broad-band" }, - "ssids": { - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid" - } - }, "tunnel": { "$ref": "#/$defs/interface.tunnel" } @@ -4308,121 +2713,6 @@ } } }, - "service.ieee8021x": { - "description": "This section allows enabling wired ieee802.1X", - "type": "object", - "properties": { - "mode": { - "description": "This field must be set to 'radius or user'", - "type": "string", - "enum": [ - "radius", - "user" - ] - }, - "port-filter": { - "description": "Specifies a list of ports that we want to filter.", - "type": "array", - "items": { - "type": "string", - "examples": [ - { - "LAN1": null - } - ] - } - }, - "users": { - "description": "Specifies a collection of local EAP user/psk/vid triplets.", - "type": "array", - "items": { - "$ref": "#/$defs/interface.ssid.radius.local-user" - } - }, - "radius": { - "description": "Specifies the information about radius account authentication and accounting", - "type": "object", - "properties": { - "nas-identifier": { - "description": "NAS-Identifier string for RADIUS messages. When used, this should be unique to the NAS within the scope of the RADIUS server.", - "type": "string" - }, - "auth-server-addr": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "auth-server-port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1812 - ] - }, - "auth-server-secret": { - "description": "The shared Radius authentication secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "acct-server-addr": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "acct-server-port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1813 - ] - }, - "acct-server-secret": { - "description": "The shared Radius accounting secret.", - "type": "string", - "examples": [ - "secret" - ] - }, - "coa-server-addr": { - "description": "The URI of our Radius server.", - "type": "string", - "format": "uc-host", - "examples": [ - "192.168.1.10" - ] - }, - "coa-server-port": { - "description": "The network port of our Radius server.", - "type": "integer", - "maximum": 65535, - "minimum": 1024, - "examples": [ - 1814 - ] - }, - "coa-server-secret": { - "description": "The shared Radius accounting secret.", - "type": "string", - "examples": [ - "secret" - ] - } - } - } - } - }, "service.radius-proxy": { "description": "This section can be used to setup a radius security proxy instance (radsecproxy).", "type": "object", @@ -4644,7 +2934,6 @@ "items": { "type": "string", "enum": [ - "wifi", "leds" ] } @@ -4675,58 +2964,6 @@ } } }, - "service.wifi-steering": { - "description": "This section describes the band steering behaviour of the unit.", - "type": "object", - "properties": { - "mode": { - "description": "Wifi sterring can happen either locally or via the backend gateway.", - "type": "string", - "enum": [ - "local", - "none" - ], - "examples": [ - "local" - ] - }, - "assoc-steering": { - "description": "Allow rejecting assoc requests for steering purposes.", - "type": "boolean", - "default": false - }, - "required-snr": { - "description": "Minimum required signal level (dBm) for connected clients. If the client will be kicked if the SNR drops below this value.", - "type": "integer", - "default": 0 - }, - "required-probe-snr": { - "description": "Minimum required signal level (dBm) to allow connections. If the SNR is below this value, probe requests will not be replied to.", - "type": "integer", - "default": 0 - }, - "required-roam-snr": { - "description": "Minimum required signal level (dBm) before an attempt is made to roam the client to a better AP.", - "type": "integer", - "default": 0 - }, - "load-kick-threshold": { - "description": "Minimum channel load (%) before kicking clients", - "type": "integer", - "default": 0 - }, - "auto-channel": { - "description": "Allow multiple instances of the steering daemon to coordinate the best channel usage amongst eachother.", - "type": "boolean", - "default": false - }, - "ipv6": { - "description": "Use IPv6 multicast to communicate with remote usteerd instances, rather than IPv4 broadcast.", - "type": "boolean", - "default": false - } - } - }, "service.quality-of-service.class-selector": { "type": "string", "enum": [ @@ -4872,65 +3109,6 @@ } } }, - "service.facebook-wifi": { - "description": "This section describes the FaceBook Wifi behaviour of the unit.", - "type": "object", - "properties": { - "vendor-id": { - "description": "The Vendors ID.", - "type": "string" - }, - "gateway-id": { - "description": "The Gateways ID.", - "type": "string" - }, - "secret": { - "description": "The Device specific secret", - "type": "string" - } - } - }, - "service.airtime-fairness": { - "description": "This section describes the vlan behaviour of a logical network interface.", - "type": "object", - "properties": { - "voice-weight": { - "description": "Voice traffic does not get aggregated. As voice and video are both considered priotity voice is considered to have a heavier weight when calculation priority average.", - "type": "number", - "default": 4 - }, - "packet-threshold": { - "description": "The amount of packets that need to be received for a specific type of traffic before new averageg is calculated.", - "type": "number", - "default": 100 - }, - "bulk-threshold": { - "description": "This option is a percentual value. If more the X% of the traffic is bulk, we assign the bulk weight.", - "type": "number", - "default": 50 - }, - "priority-threshold": { - "description": "This option is a percentual value. If more the X% of the traffic is priority, we assign the priority weight. Priority classification will take precedence over bulk.", - "type": "number", - "default": 30 - }, - "weight-normal": { - "description": "The default ATF weight that UEs get assigned.", - "type": "number", - "default": 256 - }, - "weight-priority": { - "description": "The default ATF weight that UEs get assigned when priority traffic above the configured percentage is detected.", - "type": "number", - "default": 394 - }, - "weight-bulk": { - "description": "The default ATF weight that UEs get assigned when bulk traffic above the configured percentage is detected.", - "type": "number", - "default": 128 - } - } - }, "service.wireguard-overlay": { "description": "This Object defines the properties of a wireguard-overlay.", "type": "object", @@ -5067,108 +3245,6 @@ } } }, - "service.dhcp-relay": { - "description": "Define the vlans on which the dhcp shall be relayed.", - "type": "object", - "properties": { - "select-ports": { - "description": "The list of physical network devices that shall be used to fwd the DHCP frames.", - "type": "array", - "items": { - "type": "string" - } - }, - "vlans": { - "description": "The list of all vlans", - "type": "array", - "items": { - "type": "object", - "properties": { - "vlan": { - "description": "The vlan id.", - "type": "number" - }, - "relay-server": { - "description": "The unicast target DHCP pool server where frames get relayed to.", - "type": "string", - "format": "uc-ip" - }, - "circuit-id-format": { - "description": "This option selects what info shall be contained within a relayed frame's circuit ID.", - "type": "string", - "enum": [ - "vlan-id", - "ap-mac", - "ssid" - ], - "default": "vlan-id" - }, - "remote-id-format": { - "description": "This option selects what info shall be contained within a relayed frame's remote ID.", - "type": "string", - "enum": [ - "vlan-id", - "ap-mac", - "ssid" - ], - "default": "ap-mac" - } - } - } - } - } - }, - "service.admin-ui": { - "type": "object", - "properties": { - "wifi-ssid": { - "description": "The name of the admin ssid.", - "type": "string", - "default": "Maverick", - "maxLength": 32, - "minLength": 1 - }, - "wifi-key": { - "description": "The Pre Shared Key (PSK) that is used for encryption on the BSS.", - "type": "string", - "maxLength": 63, - "minLength": 8 - }, - "wifi-bands": { - "description": "The band that the SSID should be broadcasted on. The configuration layer will use the first matching band.", - "type": "array", - "items": { - "type": "string", - "enum": [ - "2G", - "5G", - "5G-lower", - "5G-upper", - "6G" - ] - } - }, - "offline-trigger": { - "description": "The admin-ui will be spawned when this offline threshold was exceeded.", - "type": "number" - } - } - }, - "service.rrm": { - "description": "This section describes the band steering behaviour of the unit.", - "type": "object", - "properties": { - "beacon-request-assoc": { - "description": "Tell stations to send a beacon request scan when they associate.", - "type": "boolean", - "default": true - }, - "station-stats-interval": { - "description": "Periodically send station statistics every N seconds.", - "type": "number" - } - } - }, "service.telnet": { "description": "This section can be used to setup a Telnet server on the device.", "type": "object", @@ -5197,7 +3273,7 @@ } }, "service": { - "description": "This section describes all of the services that may be present on the AP. Each service is then referenced via its name inside an interface, ssid, ...", + "description": "This section describes all of the services that may be present on the AP. Each service is then referenced via its name inside an interface, ...", "type": "object", "properties": { "lldp": { @@ -5224,9 +3300,6 @@ "igmp": { "$ref": "#/$defs/service.igmp" }, - "ieee8021x": { - "$ref": "#/$defs/service.ieee8021x" - }, "radius-proxy": { "$ref": "#/$defs/service.radius-proxy" }, @@ -5236,36 +3309,15 @@ "data-plane": { "$ref": "#/$defs/service.data-plane" }, - "wifi-steering": { - "$ref": "#/$defs/service.wifi-steering" - }, "quality-of-service": { "$ref": "#/$defs/service.quality-of-service" }, - "facebook-wifi": { - "$ref": "#/$defs/service.facebook-wifi" - }, - "airtime-fairness": { - "$ref": "#/$defs/service.airtime-fairness" - }, "wireguard-overlay": { "$ref": "#/$defs/service.wireguard-overlay" }, - "captive": { - "$ref": "#/$defs/service.captive" - }, "gps": { "$ref": "#/$defs/service.gps" }, - "dhcp-relay": { - "$ref": "#/$defs/service.dhcp-relay" - }, - "admin-ui": { - "$ref": "#/$defs/service.admin-ui" - }, - "rrm": { - "$ref": "#/$defs/service.rrm" - }, "telnet": { "$ref": "#/$defs/service.telnet" }, @@ -5289,10 +3341,8 @@ "items": { "type": "string", "enum": [ - "ssids", "lldp", - "clients", - "tid-stats" + "clients" ] } }, @@ -5333,31 +3383,6 @@ } } }, - "metrics.wifi-frames": { - "description": "Define which types of ieee802.11 management frames shall be sent up to the controller.", - "type": "object", - "properties": { - "filters": { - "description": "A list of the management frames types that shall be sent to the backend.", - "type": "array", - "items": { - "type": "string", - "enum": [ - "probe", - "auth", - "assoc", - "disassoc", - "deauth", - "local-deauth", - "inactive-deauth", - "key-mismatch", - "beacon-report", - "radar-detected" - ] - } - } - } - }, "metrics.dhcp-snooping": { "description": "DHCP snooping allows us to intercept DHCP packages on interface that are bridged, where DHCP is not offered as a service by the AP.", "type": "object", @@ -5380,24 +3405,6 @@ } } }, - "metrics.wifi-scan": { - "description": "Define the behaviour of the periodic wifi scanning interface.", - "type": "object", - "properties": { - "interval": { - "description": "The periodicity at which the scan shall be performed.", - "type": "integer" - }, - "verbose": { - "description": "Add capabilities, v/ht_oper, ... to the resulting scan info.", - "type": "boolean" - }, - "information-elements": { - "description": "Add all IEs to the resulting scan info.", - "type": "boolean" - } - } - }, "metrics.telemetry": { "description": "Configure the unsolicited telemetry stream.", "type": "object", @@ -5422,9 +3429,6 @@ "client.join", "client.leave", "client.key-mismatch", - "wifi", - "wifi.start", - "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", @@ -5455,9 +3459,6 @@ "client.join", "client.leave", "client.key-mismatch", - "wifi", - "wifi.start", - "wifi.stop", "wired", "wired.carrier-up", "wired.carrier-down", @@ -5478,15 +3479,9 @@ "health": { "$ref": "#/$defs/metrics.health" }, - "wifi-frames": { - "$ref": "#/$defs/metrics.wifi-frames" - }, "dhcp-snooping": { "$ref": "#/$defs/metrics.dhcp-snooping" }, - "wifi-scan": { - "$ref": "#/$defs/metrics.wifi-scan" - }, "telemetry": { "$ref": "#/$defs/metrics.telemetry" }, diff --git a/ucentral.state.pretty.json b/ucentral.state.pretty.json index b2b78f6..a7a99c0 100644 --- a/ucentral.state.pretty.json +++ b/ucentral.state.pretty.json @@ -22,9 +22,6 @@ "gps": { "$ref": "#/$defs/gps" }, - "radios": { - "$ref": "#/$defs/radio" - }, "interfaces": { "$ref": "#/$defs/interface" }, @@ -242,63 +239,6 @@ } } }, - "radio": { - "type": "array", - "description": "An array containing the current operational state of all wifi radios that the unit has.", - "items": { - "type": "object", - "properties": { - "channel": { - "description": "The primary channel that the radio is currently operating on.", - "type": "number" - }, - "channels": { - "description": "The list of all channels that the radio is currently operating on.", - "type": "array", - "items": { - "type": "number" - } - }, - "frequency": { - "description": "The list of all frequencies that the radio is currently operating on.", - "type": "array", - "items": { - "type": "number" - } - }, - "channel_width": { - "type": "number", - "description": "The channel width currently being used.", - "enum": [ - 20, - 40, - 80, - 160, - 320 - ] - }, - "tx_power": { - "type": "number", - "description": "The currently configure TX power of the radio in dBm." - }, - "phy": { - "type": "string", - "description": "The unique path identifying the radio" - }, - "band": { - "description": "The wireless band that the radio is currently operating on.", - "type": "array", - "items": { - "type": "string" - } - }, - "temperature": { - "description": "The current temperature of the Wireless radio.", - "type": "number" - } - } - } - }, "interface.ipv4-lease": { "type": "array", "items": { @@ -493,238 +433,6 @@ } } }, - "interface.ssid.association": { - "type": "array", - "description": "A list all OBSS that this BSS is connected to. The contained entries depend opertational mode. * AP - all associated stationd * STA - the remote AP * mesh - all mesh peers that we can see * wds - the remote wds peer", - "items": { - "description": "An explanation about the purpose of this instance.", - "properties": { - "bssid": { - "type": "string", - "description": "The BSSID of the remote BSS." - }, - "station": { - "type": "string", - "description": "The MAC address of the station." - }, - "dynamic_vlan": { - "type": "number", - "description": "The VID in case the association is on a dynamic VLAN." - }, - "rssi": { - "type": "number", - "description": "The RX signal strength of the remore BSS. (dBm)" - }, - "connected": { - "type": "number", - "description": "The time since connected" - }, - "inactive": { - "type": "number", - "description": "The time since inactive" - }, - "tx_duration": { - "type": "number", - "description": "The tx duration" - }, - "rx_duration": { - "type": "number", - "description": "The rx duration" - }, - "rx_bytes": { - "type": "number", - "description": "The number of bytes received." - }, - "ack_signal": { - "type": "number" - }, - "ack_signal_avg": { - "type": "number" - }, - "rx_packets": { - "type": "number", - "description": "The number of packets received." - }, - "tx_packets": { - "type": "number", - "description": "The number of packets received." - }, - "tx_bytes": { - "type": "number", - "description": "The number of bytes transmitted." - }, - "tx_retries": { - "type": "number" - }, - "tx_failed": { - "type": "number", - "description": "The number of packets transmitted." - }, - "rx_rate": { - "type": "array", - "items": { - "properties": { - "bitrate": { - "type": "number" - }, - "sgi": { - "type": "boolean" - }, - "vht": { - "type": "boolean" - }, - "mcs": { - "type": "number" - }, - "nss": { - "type": "number" - }, - "chwidth": { - "type": "number" - }, - "he": { - "type": "boolean" - }, - "he_gi": { - "type": "number" - }, - "he_dcm": { - "type": "number" - } - } - } - }, - "tx_rate": { - "type": "array", - "items": { - "properties": { - "bitrate": { - "type": "number" - }, - "sgi": { - "type": "boolean" - }, - "vht": { - "type": "boolean" - }, - "mcs": { - "type": "number" - }, - "nss": { - "type": "number" - }, - "chwidth": { - "type": "number" - }, - "he": { - "type": "boolean" - }, - "he_gi": { - "type": "number" - }, - "he_dcm": { - "type": "number" - } - } - } - }, - "tid_stats": { - "type": "array", - "items": { - "properties": { - "rx_msdu": { - "type": "number" - }, - "tx_msdu": { - "type": "number" - }, - "tx_msdu_retries": { - "type": "number" - }, - "tx_msdu_failed": { - "type": "number" - } - } - } - } - } - } - }, - "interface.ssid": { - "type": "array", - "items": { - "type": "object", - "description": "This section contains the current state of a SSID being brodcasted or connected to by this device.", - "properties": { - "bssid": { - "type": "string", - "description": "The BSSID used by this BSS instance." - }, - "location": { - "type": "string", - "description": "The json-schema path within the configuration where this logical interface is located." - }, - "phy": { - "type": "string", - "description": "The unique path identifying the radio" - }, - "band": { - "description": "The wireless band that the radio is currently operating on.", - "type": "string" - }, - "ssid": { - "type": "string", - "description": "The SSID that we are broadcasting or connecting to." - }, - "iface": { - "type": "string", - "description": "The physical network device used." - }, - "vlan_ifaces": { - "description": "The list of dynamic vlan interfaces.", - "type": "object", - "patternProperties": { - "^wlan-v": { - "type": "object", - "$ref": "#/$defs/interface.counter" - } - } - }, - "mode": { - "type": "string", - "description": "The operational mode of the BSS.", - "enum": [ - "ap", - "sta", - "mesh", - "wds" - ] - }, - "radio": { - "type": "object", - "description": "A reference to the section describing the physical radio within this document. (#/radios/X)", - "properties": { - "ref": { - "type": "string" - } - } - }, - "frequency": { - "description": "The list of all frequencies that the radio is currently operating on.", - "type": "array", - "items": { - "type": "number" - } - }, - "associations": { - "$ref": "#/$defs/interface.ssid.association" - }, - "counters": { - "$ref": "#/$defs/interface.counter" - } - } - } - }, "interface": { "type": "array", "description": "An array containing the runtime state of all logical interfaces.items", @@ -775,9 +483,6 @@ "mesh-path": { "$ref": "#/$defs/interface.mesh-path" }, - "ssids": { - "$ref": "#/$defs/interface.ssid" - }, "multicast": { "type": "object", "description": "Detailed information about all multicast-related data (groups joined, src address used etc)",