mirror of
https://github.com/Telecominfraproject/ols-ucentral-schema.git
synced 2025-10-28 16:52:20 +00:00
[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 <mike.hansen@netexperience.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
||||
/jsdoc.conf.json
|
||||
docs/
|
||||
node_modules
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
@@ -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)
|
||||
});
|
||||
@@ -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");
|
||||
@@ -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"
|
||||
});
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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"
|
||||
});
|
||||
@@ -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'});
|
||||
@@ -1 +0,0 @@
|
||||
include("./state.uc", { stats: args.stats });
|
||||
@@ -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
|
||||
});
|
||||
@@ -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"
|
||||
});
|
||||
@@ -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);
|
||||
@@ -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");
|
||||
@@ -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,
|
||||
});
|
||||
181
docs/schema_doc.css
Normal file
181
docs/schema_doc.css
Normal file
@@ -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 */
|
||||
1
docs/schema_doc.min.js
vendored
Normal file
1
docs/schema_doc.min.js
vendored
Normal file
@@ -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)}
|
||||
117
docs/ucentral-schema.html
Normal file
117
docs/ucentral-schema.html
Normal file
File diff suppressed because one or more lines are too long
28
docs/ucentral-state.html
Normal file
28
docs/ucentral-state.html
Normal file
File diff suppressed because one or more lines are too long
20
docs/ucentral.capabilities.html
Normal file
20
docs/ucentral.capabilities.html
Normal file
File diff suppressed because one or more lines are too long
@@ -1,650 +0,0 @@
|
||||
/*
|
||||
* ucode-transpiler.js - JSDoc plugin to naively transpile ucode into JS.
|
||||
*
|
||||
* Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
};
|
||||
@@ -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 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
1172
renderer/renderer.uc
1172
renderer/renderer.uc
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@@ -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 }}
|
||||
@@ -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 %}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
%}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
# Raw Configuration
|
||||
{% for (let config in config_raw): %}
|
||||
{{ config[0] }} {{ config[1] }}{{config[2] ? '=' + config[2] : ''}}
|
||||
{% endfor %}
|
||||
@@ -1,4 +0,0 @@
|
||||
{% for (let user in users): %}
|
||||
"{{ user.user_name }}" PWD "{{ user.password }}"
|
||||
{% endfor %}
|
||||
* TLS,TTLS
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 }); %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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
|
||||
});
|
||||
%}
|
||||
@@ -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
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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) }}'
|
||||
@@ -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) }}'
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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) }}"
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 }}
|
||||
@@ -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()) }}
|
||||
@@ -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 %}
|
||||
@@ -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) }}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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) }}
|
||||
@@ -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 %}
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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'
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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
|
||||
@@ -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) }}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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) }}
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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);
|
||||
%}
|
||||
@@ -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 %}
|
||||
@@ -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 }}
|
||||
@@ -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) }}
|
||||
@@ -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 %}
|
||||
@@ -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'
|
||||
@@ -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 %}
|
||||
@@ -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)
|
||||
}
|
||||
%}
|
||||
@@ -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 %}
|
||||
@@ -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);
|
||||
%}
|
||||
@@ -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);
|
||||
%}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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/'
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user