badvpn/ncd/examples/router/port_forwarding.ncdi
2013-10-13 21:02:10 +00:00

171 lines
4.9 KiB
Plaintext

include_guard "port_forwarding"
template port_forwarding {
alias("_arg0") forwardings_file;
alias("_arg1") template_forward;
# Map which holds the set of current port forwardings.
# Enties are: {protocol, port_start, port_end, dest_addr}:""
value([]) map;
# Blocker which is initially down and is toggled down-up
# whenever the forwarding change.
blocker() update_blocker;
# Process manager, each forwarding has a port_forwarding__instance process.
# The process identifiers are the same as the keys in the map.
process_manager() mgr;
# Spawn a process for dealing with storage of port forwardings on disk.
spawn("port_forwarding__stored", {});
}
template port_forwarding__instance {
alias("_caller") pf;
alias("_arg0") protocol;
alias("_arg1") port_start;
alias("_arg2") port_end;
alias("_arg3") dest_addr;
log("notice", "adding port forwarding ", protocol, ":", port_start, ":", port_end, " to ", dest_addr);
log_r("notice", "removed port forwarding ", protocol, ":", port_start, ":", port_end, " to ", dest_addr);
# Do the forwarding.
call_with_caller_target(pf.template_forward, {protocol, port_start, port_end, dest_addr}, "pf._caller");
}
template port_forwarding_add {
alias(_arg0) pf;
alias("_arg1") protocol;
alias("_arg2") port_start;
alias("_arg3") port_end;
alias("_arg4") dest_addr;
var("false") succeeded;
var("") error_text;
var("true") not_finished;
backtrack_point() finished_point;
If (not_finished) {
# Check for conflicts with existing forwardings.
Foreach (pf.map.keys As entry) {
value(entry) entry;
entry->get("0") e_protocol;
entry->get("1") e_port_start;
entry->get("2") e_port_end;
val_different(protocol, e_protocol) different_protocol;
num_lesser(port_end, e_port_start) before;
num_greater(port_start, e_port_end) after;
or(different_protocol, before, after) no_conflict;
not(no_conflict) conflict;
If (conflict) {
error_text->set("Port forwarding conflicts with an existing forwarding.");
not_finished->set("false");
finished_point->go();
};
};
# Build entry key.
var({protocol, port_start, port_end, dest_addr}) key;
# Insert to map and toggle blocker.
pf.map->insert(key, "");
pf.update_blocker->downup();
# Start process.
pf.mgr->start(key, "port_forwarding__instance", {protocol, port_start, port_end, dest_addr});
succeeded->set("true");
not_finished->set("false");
finished_point->go();
};
}
template port_forwarding_remove {
alias(_arg0) pf;
alias("_arg1") protocol;
alias("_arg2") port_start;
alias("_arg3") port_end;
alias("_arg4") dest_addr;
var("false") succeeded;
var("") error_text;
var("true") not_finished;
backtrack_point() finished_point;
If (not_finished) {
# Build entry key.
var({protocol, port_start, port_end, dest_addr}) key;
# Check if the forwarding exists.
pf.map->try_get(key) entry;
not(entry.exists) does_not_exist;
If (does_not_exist) {
error_text->set("Port forwarding does not exist.");
not_finished->set("false");
finished_point->go();
};
# Stop process.
pf.mgr->stop(key);
# Remove from map and toggle blocker.
pf.map->remove(key);
pf.update_blocker->downup();
succeeded->set("true");
not_finished->set("false");
finished_point->go();
};
}
template port_forwarding__stored {
alias("_caller") pf;
# Create file if it doesn't exist.
file_stat(pf.forwardings_file) stat;
If (stat.succeeded) { print(); } Else {
file_write(pf.forwardings_file, "{}\n");
};
# Read port forwardings from file.
file_read(pf.forwardings_file) data;
from_string(data) forwardings;
# Add them.
Foreach (forwardings As fwd) {
value(fwd) fwd;
fwd->get("0") protocol;
fwd->get("1") port_start;
fwd->get("2") port_end;
fwd->get("3") dest_addr;
call("port_forwarding_add", {"_caller.pf", protocol, port_start, port_end, dest_addr});
};
# Write forwardings to file on exit.
imperative("<none>", {}, "port_forwarding__write", {}, "6000");
# Also write forwardings whenever they are changed.
pf.update_blocker->use();
call("port_forwarding__write", {});
}
template port_forwarding__write {
alias("_caller.pf") pf;
# Convert forwardings to string.
to_string(pf.map.keys) data;
concat(data, "\n") data;
# Build name of temporary file.
concat(pf.forwardings_file, ".new") temp_file;
# Write temporary file.
file_write(temp_file, data);
# Move to live file.
runonce({"/bin/mv", temp_file, pf.forwardings_file});
}