#!/usr/bin/env ucode
+'use strict';
+
import { access, basename, dirname, mkstemp, open, writefile } from 'fs';
function assert(cond, message) {
return true;
}
+let unet_tool = "unet-tool";
let script_dir = sourcepath(0, true);
+
if (basename(script_dir) == "scripts") {
unet_tool = `${dirname(script_dir)}/unet-tool`;
assert(access(unet_tool, "x"), "unet-tool missing");
-} else {
- unet_tool = "unet-tool";
}
-args = {};
+let args = {};
-defaults = {
+const defaults = {
port: 51830,
pex_port: 51831,
keepalive: 10,
Commands:
- create: Create a new network file
- - set-config: Change network config parameters
- - add-host <name>: Add a host
- - add-ssh-host <name> <host>: Add a remote OpenWrt host via SSH
- (<host> can contain SSH options as well)
- - set-host <name>: Change host settings
- - set-ssh-host <name> <host>: Update local and remote host settings
- - add-service <name>: Add a service
- - set-service <name>: Change service settings
- - sign Sign network data
+ - set-config: Change network config parameters
+ - add-host <name>: Add a host
+ - add-ssh-host <name> <host>: Add a remote OpenWrt host via SSH
+ (<host> can contain SSH options as well)
+ - set-host <name>: Change host settings
+ - set-ssh-host <name> <host>: Update local and remote host settings
+ - add-service <name>: Add a service
+ - set-service <name>: Change service settings
+ - sign Sign network data
Flags:
- -p: Print modified JSON instead of updating file
+ -p: Print modified JSON instead of updating file
Options:
- config options (create, set-config):
- port=<val> set tunnel port (default: ${defaults.port})
- pex_port=<val> set peer-exchange port (default: ${defaults.pex_port})
- keepalive=<val> set keepalive interval (seconds, 0: off, default: ${defaults.keepalive})
- host options (add-host, add-ssh-host, set-host):
- key=<val> set host public key (required for add-host)
- port=<val> set host tunnel port number
- groups=[+|-]<val>[,<val>...] set/add/remove groups that the host is a member of
- ipaddr=[+|-]<val>[,<val>...] set/add/remove host ip addresses
- subnet=[+|-]<val>[,<val>...] set/add/remove host announced subnets
- endpoint=<val> set host endpoint address
- gateway=<name> set host gateway (using name of other host)
- ssh host options (add-ssh-host, set-ssh-host)
- auth_key=<key> use <key> as public auth key on the remote host
- priv_key=<key> use <key> as private host key on the remote host (default: generate a new key)
- interface=<name> use <name> as interface in /etc/config/network on the remote host
- domain=<name> use <name> as hosts file domain on the remote host (default: unet)
- connect=<val>[,<val>...] set IP addresses that the host will contact for network updates
- tunnels=<ifname>:<service>[,...] set active tunnel devices
- service options (add-service, set-service):
- type=<val> set service type (required for add-service)
- members=[+|-]<val>[,<val>...] set/add/remove service member hosts/groups
- vxlan service options (add-service, set-service):
- id=<val> set VXLAN ID
- port=<val> set VXLAN port
- mtu=<val> set VXLAN device MTU
- forward_ports=[+|-]<val>[,<val>...] set members allowed to receive broadcast/multicast/unknown-unicast
- sign options:
- upload=<ip>[,<ip>...] upload signed file to hosts
+ port=<val> set tunnel port (default: ${defaults.port})
+ pex_port=<val> set peer-exchange port (default: ${defaults.pex_port})
+ keepalive=<val> set keepalive interval (seconds, 0: off, default: ${defaults.keepalive})
+ - host options (add-host, add-ssh-host, set-host):
+ key=<val> set host public key (required for add-host)
+ port=<val> set host tunnel port number
+ groups=[+|-]<val>[,<val>...] set/add/remove groups that the host is a member of
+ ipaddr=[+|-]<val>[,<val>...] set/add/remove host ip addresses
+ subnet=[+|-]<val>[,<val>...] set/add/remove host announced subnets
+ endpoint=<val> set host endpoint address
+ gateway=<name> set host gateway (using name of other host)
+ - ssh host options (add-ssh-host, set-ssh-host)
+ auth_key=<key> use <key> as public auth key on the remote host
+ priv_key=<key> use <key> as private host key on the remote host (default: generate a new key)
+ interface=<name> use <name> as interface in /etc/config/network on the remote host
+ domain=<name> use <name> as hosts file domain on the remote host (default: unet)
+ connect=<val>[,<val>...] set IP addresses that the host will contact for network updates
+ tunnels=<ifname>:<service>[,...] set active tunnel devices
+ - service options (add-service, set-service):
+ type=<val> set service type (required for add-service)
+ members=[+|-]<val>[,<val>...] set/add/remove service member hosts/groups
+ - vxlan service options (add-service, set-service):
+ id=<val> set VXLAN ID
+ port=<val> set VXLAN port
+ mtu=<val> set VXLAN device MTU
+ forward_ports=[+|-]<val>[,<val>...] set members allowed to receive broadcast/multicast/unknown-unicast
+ - sign options:
+ upload=<ip>[,<ip>...] upload signed file to hosts
`;
if (length(ARGV) < 2)
exit(usage());
-file = shift(ARGV);
-command = shift(ARGV);
+let file = shift(ARGV);
+let command = shift(ARGV);
-field_types = {
+const field_types = {
int: function(object, name, val) {
object[name] = int(val);
},
},
};
-service_field_types = {
+const service_field_types = {
vxlan: {
id: "int",
port: "int",
},
};
-ssh_script = `
+const ssh_script = `
set_list() {
local field="$1"
ifup $INTERFACE
`;
-args = {};
-print_only = false;
+let print_only = false;
function fetch_args() {
- for (arg in ARGV) {
- vals = match(arg, /^(.[[:alnum:]_-]*)=(.*)$/);
+ for (let arg in ARGV) {
+ let vals = match(arg, /^(.[[:alnum:]_-]*)=(.*)$/);
assert(vals, `Invalid argument: ${arg}`);
args[vals[1]] = vals[2]
}
}
function set_fields(object, list) {
- for (f in list)
+ for (let f in list)
set_field(list[f], object, f, args[f]);
}
-function set_host(name) {
- let host = net_data.hosts[name];
-
+function set_host(host) {
set_fields(host, {
key: "string",
endpoint: "string",
});
}
-function set_service(name) {
- let service = net_data.services[name];
-
+function set_service(service) {
set_fields(service, {
type: "string",
members: "array",
fh.flush();
fh.seek();
- fh2 = mkstemp();
+ let fh2 = mkstemp();
system(`ssh ${host} sh <&${fh.fileno()} >&${fh2.fileno()}`);
fh.close();
- data = {};
+ let data = {}, line;
fh2.seek();
while (line = fh2.read("line")) {
let vals = match(line, /^(.[[:alnum:]_-]*)=(.*)\n$/);
- assert(vals, `Invalid argument: ${arg}`);
+ assert(vals, `Invalid argument: ${line}`);
data[vals[1]] = vals[2]
}
fh2.close();
}
while (substr(ARGV[0], 0, 1) == "-") {
- opt = shift(ARGV);
+ let opt = shift(ARGV);
if (opt == "--")
break;
else if (opt == "-p")
exit(usage());
}
+let hostname, ssh_host, servicename;
+
if (command in [ "add-host", "set-host", "add-ssh-host", "set-ssh-host" ]) {
hostname = shift(ARGV);
assert(hostname, "Missing host name argument");
command = replace(command, "ssh-", "");
}
+let net_data;
+
if (command == "create") {
net_data = {
config: {},
services: {}
};
} else {
- fh = open(file);
+ let fh = open(file);
assert(fh, `Could not open input file ${file}`);
try {
}
if (command == "create") {
- for (key, val in defaults)
+ for (let key, val in defaults)
args[key] ??= `${val}`;
if (!access(`${file}.key`))
system(`${unet_tool} -G > ${file}.key`);
}
if (command == "sign") {
- ret = system(`${unet_tool} -S -K ${file}.key -o ${file}.bin ${file}`);
+ let ret = system(`${unet_tool} -S -K ${file}.key -o ${file}.bin ${file}`);
if (ret != 0)
exit(ret);
if (args.upload) {
- hosts = split(args.upload, ",");
- for (host in hosts) {
+ for (let host in split(args.upload, ",")) {
warn(`Uploading ${file}.bin to ${host}\n`);
ret = system(`${unet_tool} -U ${host} -K ${file}.key ${file}.bin`);
if (ret)
case 'add-host':
net_data.hosts[hostname] = {};
assert(args.key, "Missing host key");
- set_host(hostname);
+ set_host(net_data.hosts[hostname]);
break;
case 'set-host':
assert(net_data.hosts[hostname], `Host '${hostname}' does not exist`);
- set_host(hostname);
+ set_host(net_data.hosts[hostname]);
break;
case 'add-service':
members: [],
};
assert(args.type, "Missing service type");
- set_service(servicename);
+ set_service(net_data.services[servicename]);
break;
case 'set-service':
assert(net_data.services[servicename], `Service '${servicename}' does not exist`);
- set_service(servicename);
+ set_service(net_data.services[servicename]);
break;
default:
assert(false, "Unknown command");
}
-net_data_json = sprintf("%.J\n", net_data);
+const net_data_json = sprintf("%.J\n", net_data);
+
if (print_only)
print(net_data_json);
else