--- /dev/null
+FROM openwrt-1806-4-base
+
+#EXPOSE 80
+ENV http_proxy={docker_proxy}
+ENV https_proxy={docker_proxy}
+ENV no_proxy=localhost,120.0.0.1,192.168.*
+
+RUN mkdir /var/lock && \
+ opkg update && \
+ opkg install uhttpd-mod-lua && \
+ uci set uhttpd.main.interpreter='.lua=/usr/bin/lua' && \
+ uci commit uhttpd && \
+ opkg install mwan3 && \
+ opkg install luci-app-mwan3; exit 0
+
+COPY system /etc/config/system
+COPY rest_v1 /usr/lib/lua/luci/controller/rest_v1
+
+ENV http_proxy=
+ENV https_proxy=
+ENV no_proxy=
+
+USER root
+
+# using exec format so that /sbin/init is proc 1 (see procd docs)
+CMD ["/sbin/init"]
--- /dev/null
+FROM openwrt-1806-4-base
+
+#EXPOSE 80
+
+RUN mkdir /var/lock && \
+ opkg update && \
+ opkg install uhttpd-mod-lua && \
+ uci set uhttpd.main.interpreter='.lua=/usr/bin/lua' && \
+ uci commit uhttpd && \
+ opkg install mwan3 && \
+ opkg install luci-app-mwan3; exit 0
+
+COPY system /etc/config/system
+COPY rest_v1 /usr/lib/lua/luci/controller/rest_v1
+
+USER root
+
+# using exec format so that /sbin/init is proc 1 (see procd docs)
+CMD ["/sbin/init"]
--- /dev/null
+#!/bin/bash
+
+# usage: build_images.sh
+
+set -ex
+base_image_tag=openwrt-1806-4-base
+docker_file=Dockerfile_1806_mwan3
+image_tag=openwrt-1806-mwan3
+package=openwrt-18.06.4-x86-64-generic-rootfs
+
+# build openwrt base docker images
+base_image=`docker images | grep $base_image_tag | awk '{print $1}'`
+if [ -z "$base_image" ]; then
+ # download driver source package
+ if [ ! -e /tmp/$package.tar.gz ]; then
+ wget -P /tmp https://downloads.openwrt.org/releases/18.06.4/targets/x86/64/$package.tar.gz
+ fi
+ cp /tmp/$package.tar.gz .
+
+ docker import $package.tar.gz $base_image_tag
+fi
+
+# generate Dockerfile
+test -f ./set_proxy && . set_proxy
+docker_proxy=${docker_proxy-""}
+if [ -z "$docker_proxy" ]; then
+ cp ${docker_file}_noproxy.tpl $docker_file
+else
+ cp $docker_file.tpl $docker_file
+ sed -i "s,{docker_proxy},$docker_proxy,g" $docker_file
+fi
+
+# build docker images for openwrt with wman3
+docker build --network=host -f $docker_file -t $image_tag .
+
+# clear
+docker image rm $base_image_tag
+rm -rf $docker_file
+rm -rf $package.tar.gz
--- /dev/null
+-- Licensed to the public under the GNU General Public License v2.
+
+module("luci.controller.rest_v1.firewall_rest", package.seeall)
+
+local uci = require "luci.model.uci"
+
+json = require "luci.jsonc"
+io = require "io"
+sys = require "luci.sys"
+utils = require "luci.controller.rest_v1.utils"
+
+uci_conf = "firewall"
+
+zone_validator = {
+ create_section_name=false,
+ {name="name"},
+ {name="network", item_validator=function(value) return is_network_interface_available(value) end, message="invalid network"},
+ {name="masq", validator=function(value) return utils.in_array(value, {"0", "1"}) end, message="invalid masq"},
+ {name="masq_src", item_validator=function(value) return is_valid_masq_subset(value) end, message="invalid masq_src"},
+ {name="masq_dest", item_validator=function(value) return is_valid_masq_subset(value) end, message="invalid masq_dest"},
+ {name="masq_allow_invalid", validator=function(value) return utils.in_array(value, {"0", "1"}) end, message="invalid masq_allow_invalid"},
+ {name="mtu_fix", validator=function(value) return utils.in_array(value, {"0", "1"}) end, message="invalid mtu_fix"},
+ {name="input", validator=function(value) return utils.in_array(value, {"ACCEPT", "REJECT", "DROP"}) end, message="invalid input"},
+ {name="forward", validator=function(value) return utils.in_array(value, {"ACCEPT", "REJECT", "DROP"}) end, message="invalid forward"},
+ {name="output", validator=function(value) return utils.in_array(value, {"ACCEPT", "REJECT", "DROP"}) end, message="invalid output"},
+ {name="family", validator=function(value) return utils.in_array(value, {"ipv4", "ipv6", "any"}) end, message="invalid family"},
+ {name="subnet", item_validator=function(value) return utils.is_valid_ip(value) end, message="invalid subnet"},
+ {name="extra_src"},
+ {name="etra_dest"},
+}
+
+redirect_validator = {
+ create_section_name=false,
+ object_validator=function(value) return check_redirect(value) end,
+ {name="name"},
+ {name="src", validator=function(value) return is_zone_available(value) end, message="invalid src"},
+ {name="src_ip", validator=function(value) return utils.is_valid_ip(value) end, message="invalid src_ip"},
+ {name="src_dip", validator=function(value) return utils.is_valid_ip(value) end, message="invalid src_dip"},
+ {name="src_mac", validator=function(value) return utils.is_valid_mac(value) end, message="invalid src_mac"},
+ {name="src_port", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid src_port"},
+ {name="src_dport", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid src_port"},
+ {name="proto", validator=function(value) return utils.in_array(value, {"tcp", "udp", "tcpudp", "udplite", "icmp", "esp", "ah", "sctp", "all"}) end, message="invalid proto"},
+ {name="dest", validator=function(value) return is_zone_available(value) end, message="invalid dest"},
+ {name="dest_ip", validator=function(value) return utils.is_valid_ip(value) end, message="invalid dest_ip"},
+ {name="dest_port", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid dest_port"},
+ {name="mark"},
+ {name="target", validator=function(value) return utils.in_array(value, {"DNAT", "SNAT"}) end, message="invalid target"},
+ {name="family", validator=function(value) return utils.in_array(value, {"ipv4", "ipv6", "any"}) end, message="invalid family"},
+}
+
+rule_validator = {
+ create_section_name=false,
+ object_validator=function(value) return check_rule(value) end,
+ {name="name"},
+ {name="src", validator=function(value) return is_zone_available(value) end, message="invalid src"},
+ {name="src_ip", validator=function(value) return utils.is_valid_ip(value) end, message="invalid src_ip"},
+ {name="src_mac", validator=function(value) return utils.is_valid_mac(value) end, message="invalid src_mac"},
+ {name="src_port", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid src_port"},
+ {name="proto", validator=function(value) return utils.in_array(value, {"tcp", "udp", "tcpudp", "udplite", "icmp", "esp", "ah", "sctp", "all"}) end, message="invalid proto"},
+ {name="icmp_type", is_list=true, item_validator=function(value) return check_icmp_type(value) end, message="invalid icmp_type"},
+ {name="dest", validator=function(value) return is_zone_available(value) end, message="invalid dest"},
+ {name="dest_ip", validator=function(value) return utils.is_valid_ip(value) end, message="invalid dest_ip"},
+ {name="dest_port", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid dest_port"},
+ {name="mark"},
+ {name="target", validator=function(value) return utils.in_array(value, {"ACCEPT", "REJECT", "DROP", "MARK", "NOTRACK"}) end, message="invalid target"},
+ {name="set_mark"},
+ {name="set_xmark"},
+ {name="family", validator=function(value) return utils.in_array(value, {"ipv4", "ipv6", "any"}) end, message="invalid family"},
+ {name="extra"},
+}
+
+forwarding_validator = {
+ create_section_name=false,
+ {name="name"},
+ {name="src", required=true, validator=function(value) return is_zone_available(value) end, message="invalid src"},
+ {name="dest", required=true, validator=function(value) return is_zone_available(value) end, message="invalid dest"},
+ {name="family", validator=function(value) return utils.in_array(value, {"ipv4", "ipv6", "any"}) end, message="invalid family"},
+}
+
+firewall_processor = {
+ zone={update="update_zone", delete="delete_zone", validator=zone_validator},
+ redirect={validator=redirect_validator},
+ rule={validator=rule_validator},
+ forwarding={validator=forwarding_validator},
+ configuration=uci_conf
+}
+
+zone_checker = {
+ {name="rule", checker={"src", "dest"}},
+ {name="forwarding", checker={"src", "dest"}},
+ {name="redirect", checker={"src", "dest"}},
+}
+
+function index()
+ ver = "v1"
+ configuration = "firewall"
+ entry({"sdewan", configuration, ver, "zones"}, call("get_zones"))
+ entry({"sdewan", configuration, ver, "redirects"}, call("get_redirects"))
+ entry({"sdewan", configuration, ver, "rules"}, call("get_rules"))
+ entry({"sdewan", configuration, ver, "forwardings"}, call("get_forwardings"))
+ entry({"sdewan", configuration, ver, "zone"}, call("handle_request")).leaf = true
+ entry({"sdewan", configuration, ver, "redirect"}, call("handle_request")).leaf = true
+ entry({"sdewan", configuration, ver, "rule"}, call("handle_request")).leaf = true
+ entry({"sdewan", configuration, ver, "forwarding"}, call("handle_request")).leaf = true
+end
+
+function is_network_interface_available(interface)
+ local interfaces = uci:get_all("network", interface)
+ if interfaces == nil then
+ return false, "Interface[" .. interface .. "] is not definied"
+ end
+
+ return true, interface
+end
+
+function is_valid_masq_subset(s)
+ local ip = s
+ if utils.start_with(ip, "!") then
+ ip = string.sub(ip, 2, string.len(ip))
+ end
+
+ local res, _ = utils.is_valid_ip(ip)
+ if res then
+ return true, s
+ else
+ return false
+ end
+end
+
+function check_redirect(value)
+ local target = value["target"]
+ if target == "SNAT" then
+ if value["src_dip"] == nil then
+ return false, "src_dip is required for SNAT"
+ end
+ if value["dest"] == nil then
+ return false, "dest is required for SNAT"
+ end
+ end
+
+ if target == "DNAT" then
+ if value["src"] == nil then
+ return false, "src is required for DNAT"
+ end
+ end
+
+ return true, value
+end
+
+function check_rule(value)
+ local target = value["target"]
+ if target == "MARK" then
+ if value["set_mark"] == nil and value["set_xmark"] == nil then
+ return false, "set_mark or set_xmark is required for MARK"
+ end
+ end
+
+ return true, value
+end
+
+function check_icmp_type(value)
+ return utils.in_array(value, {"address-mask-reply", "address-mask-request ", "any", "communication-prohibited",
+ "destination-unreachable", "echo-reply", "echo-request", "fragmentation-needed", "host-precedence-violation",
+ "host-prohibited", "host-redirect", "host-unknown", "host-unreachable", "ip-header-bad", "network-prohibited",
+ "network-redirect", "network-unknown", "network-unreachable", "parameter-problem", "ping", "pong",
+ "port-unreachable", "precedence-cutoff", "protocol-unreachable", "redirect", "required-option-missing",
+ "router-advertisement", "router-solicitation", "source-quench", "source-route-failed", "time-exceeded",
+ "timestamp-reply", "timestamp-request", "TOS-host-redirect", "TOS-host-unreachable", "TOS-network-redirect",
+ "TOS-network-unreachable", "ttl-exceeded", "ttl-zero-during-reassembly", "ttl-zero-during-transit"})
+end
+
+-- Request Handler
+function handle_request()
+ local handler = utils.handles_table[utils.get_req_method()]
+ if handler == nil then
+ utils.response_error(405, "Method Not Allowed")
+ else
+ return utils[handler](_M, firewall_processor)
+ end
+end
+
+-- Zone APIs
+-- check if zone is used by rule, forwarding or redirect
+function is_zone_used(name)
+ local is_used = false
+
+ for i,v in pairs(zone_checker) do
+ local section_name = v["name"]
+ local checker = v["checker"]
+ uci:foreach(uci_conf, section_name,
+ function(section)
+ for j=1, #checker do
+ if name == section[checker[j]] then
+ is_used = true
+ return false
+ end
+ end
+ end
+ )
+
+ if is_used then
+ break
+ end
+ end
+
+ return is_used
+end
+
+function is_zone_available(name)
+ local zone = utils.get_object(_M, firewall_processor, "zone", name)
+ if zone == nil then
+ return false, "Zone[" .. name .. "] is not definied"
+ end
+
+ return true, name
+end
+
+-- get /zones
+function get_zones()
+ utils.handle_get_objects("zones", uci_conf, "zone", zone_validator)
+end
+
+-- delete a zone
+function delete_zone(name, check_used)
+ -- check whether zone is defined
+ local zone = utils.get_object(_M, firewall_processor, "zone", name)
+ if zone == nil then
+ return false, 404, "zone " .. name .. " is not defined"
+ end
+
+ if check_used == nil then
+ check_used = true
+ else
+ check_used = false
+ end
+
+ -- Todo: check whether the zone is used by a rule
+ if check_used == true and is_zone_used(name) then
+ return false, 400, "zone " .. name .. " is used"
+ end
+
+ -- delete zone
+ uci:foreach(uci_conf, "zone",
+ function (section)
+ if name == section[".name"] or name == section["name"] then
+ uci:delete(uci_conf, section[".name"])
+ end
+ end
+ )
+
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
+
+ return true
+end
+
+-- update a zone
+function update_zone(zone)
+ local name = zone.name
+ res, code, msg = delete_zone(name, false)
+ if res == true then
+ return utils.create_object(_M, firewall_processor, "zone", zone)
+ end
+
+ return false, code, msg
+end
+
+-- Redirect APIs
+-- get /redirects
+function get_redirects()
+ utils.handle_get_objects("redirects", uci_conf, "redirect", redirect_validator)
+end
+
+-- Rule APIs
+-- get /rules
+function get_rules()
+ utils.handle_get_objects("rules", uci_conf, "rule", rule_validator)
+end
+
+-- Forwarding APIs
+-- get /forwardings
+function get_forwardings()
+ utils.handle_get_objects("forwardings", uci_conf, "forwarding", forwarding_validator)
+end
--- /dev/null
+-- Licensed to the public under the GNU General Public License v2.
+
+module("luci.controller.rest_v1.index", package.seeall)
+
+function index()
+ ver = "v1"
+ entry({"sdewan", ver}, call("help")).dependent = false
+ entry({"sdewan", "mwan3", ver}, call("help")).dependent = false
+ entry({"sdewan", "firewall", ver}, call("help")).dependent = false
+ entry({"sdewan", "ipsec", ver}, call("help")).dependent = false
+end
+
+function help()
+ luci.http.prepare_content("application/json")
+ luci.http.write('{"message":"sdewan restful API service v1"}')
+end
--- /dev/null
+-- Licensed to the public under the GNU General Public License v2.
+
+module("luci.controller.rest_v1.ipsec_rest", package.seeall)
+
+local uci = require "luci.model.uci"
+
+json = require "luci.jsonc"
+io = require "io"
+sys = require "luci.sys"
+utils = require "luci.controller.rest_v1.utils"
+
+uci_conf = "ipsec"
+
+proposal_validator = {
+ {name="name"},
+ {name="encryption_algorithm", validator=function(value) return true, value end, message="invalid encryption_algorithm"},
+ {name="hash_algorithm", validator=function(value) return true, value end, message="invalid hash_algorithm"},
+ {name="dh_group", validator=function(value) return true, value end, message="invalid dh_group"},
+}
+
+connection_validator = {
+ config_type=function(value) return value["type"] end,
+ {name="name"},
+ {name="type", required=true, validator=function(value) return utils.in_array(value, {"tunnel", "transport"}) end, load_func=function(value) return value[".type"] end, save_func=function(value) return true, "" end, message="invalid type"},
+ {name="mode"},
+ {name="local_subnet"},
+ {name="local_nat"},
+ {name="local_sourceip"},
+ {name="local_updown"},
+ {name="local_firewall"},
+ {name="remote_subnet"},
+ {name="remote_sourceip"},
+ {name="remote_updown"},
+ {name="remote_firewall"},
+ {name="crypto_proposal", is_list=true, item_validator=function(value) return is_proposal_available(value) end, message="invalid crypto_proposal"},
+}
+
+site_validator = {
+ config_type="remote",
+ {name="name"},
+ {name="gateway"},
+ {name="pre_shared_key"},
+ {name="authentication_method"},
+ {name="local_identifier"},
+ {name="remote_identifier"},
+ {name="crypto_proposal", is_list=true, item_validator=function(value) return is_proposal_available(value) end, message="invalid crypto_proposal"},
+ {name="force_crypto_proposal"},
+ {name="local_public_cert",
+ load_func=function(value) return load_cert(value["local_public_cert"]) end,
+ save_func=function(value) return save_cert(value["local_public_cert"], "/tmp/" .. value["name"] .. "_public.cert") end,
+ delete_func=function(value) return delete_cert(value["local_public_cert"]) end},
+ {name="local_private_cert",
+ load_func=function(value) return load_cert(value["local_private_cert"]) end,
+ save_func=function(value) return save_cert(value["local_private_cert"], "/tmp/" .. value["name"] .. "_private.cert") end,
+ delete_func=function(value) return delete_cert(value["local_private_cert"]) end},
+ {name="shared_ca"},
+ {name="connections", item_validator=connection_validator, message="invalid connection",
+ load_func=function(value) return load_connection(value) end,
+ save_func=function(value) return save_connection(value) end,},
+}
+
+ipsec_processor = {
+ proposal={update="update_proposal", delete="delete_proposal", validator=proposal_validator},
+ site={validator=site_validator},
+ configuration=uci_conf
+}
+
+proposal_checker = {
+ {name="remote", checker={"crypto_proposal"}},
+ {name="tunnel", checker={"crypto_proposal"}},
+ {name="transport", checker={"crypto_proposal"}},
+}
+
+function index()
+ ver = "v1"
+ configuration = "ipsec"
+ entry({"sdewan", configuration, ver, "proposals"}, call("get_proposals"))
+ entry({"sdewan", configuration, ver, "sites"}, call("get_sites"))
+ entry({"sdewan", configuration, ver, "proposal"}, call("handle_request")).leaf = true
+ entry({"sdewan", configuration, ver, "site"}, call("handle_request")).leaf = true
+end
+
+-- Request Handler
+function handle_request()
+ local handler = utils.handles_table[utils.get_req_method()]
+ if handler == nil then
+ utils.response_error(405, "Method Not Allowed")
+ else
+ return utils[handler](_M, ipsec_processor)
+ end
+end
+
+function save_cert(content, path)
+ local file = io.open(path, "w")
+ if file == nil then
+ return false, "Can not generate cert at: " .. path
+ end
+
+ file:write(content)
+ file:close()
+
+ return true, path
+end
+
+function load_cert(path)
+ if path == nil then
+ return nil
+ end
+ content = path
+ local file = io.open(path, "rb")
+ if file ~= nil then
+ content = file:read "*a"
+ file:close()
+ end
+ return content
+end
+
+function delete_cert(path)
+ if path ~= nil then
+ os.remove(path)
+ end
+end
+
+function add_to_key_list(arr, key, value)
+ local sub_arr = nil
+ for i=1, #arr do
+ if key == arr[i].option then
+ sub_arr = arr[i].values
+ end
+ end
+
+ if sub_arr == nil then
+ sub_arr = {}
+ arr[#arr+1] = {option=key, values=sub_arr}
+ end
+ sub_arr[#sub_arr+1] = value
+end
+
+-- load uci configuration as format: {{section="tunnel/transport", name="section_name"},}
+function load_connection(value)
+ local ret_value={}
+ local tunnels = value["tunnel"]
+ if tunnels ~= nil and #tunnels > 0 then
+ for i=1, #tunnels do
+ ret_value[#ret_value+1]={section="tunnel", name=tunnels[i]}
+ end
+ end
+ local transports = value["transport"]
+ if transports ~= nil and #transports > 0 then
+ for i=1, #transports do
+ ret_value[#ret_value+1]={section="transport", name=transports[i]}
+ end
+ end
+ if #ret_value == 0 then
+ return nil
+ end
+ return ret_value
+end
+
+-- save connections as standard format:
+-- {_standard_format=true, {option="tunnel", values={...}}, {option="transport", values={...}}}
+function save_connection(value)
+ local connections = value["connections"]
+ local ret_value = {_standard_format=true}
+ for i=1, #connections do
+ add_to_key_list(ret_value, connections[i]["type"], connections[i])
+ end
+
+ return true, ret_value
+end
+
+-- Site APIs
+-- get /sites
+function get_sites()
+ utils.handle_get_objects("sites", uci_conf, "remote", site_validator)
+end
+
+-- Proposal APIs
+-- check if proposal is used by connection, site
+function is_proposal_used(name)
+ local is_used = false
+
+ for i,v in pairs(proposal_checker) do
+ local section_name = v["name"]
+ local checker = v["checker"]
+ uci:foreach(uci_conf, section_name,
+ function(section)
+ for j=1, #checker do
+ for k=1, #section[checker[j]] do
+ if name == section[checker[j]][k] then
+ is_used = true
+ return false
+ end
+ end
+ end
+ end
+ )
+
+ if is_used then
+ break
+ end
+ end
+
+ return is_used
+end
+
+function is_proposal_available(name)
+ local proposal = utils.get_object(_M, ipsec_processor, "proposal", name)
+ if proposal == nil then
+ return false, "Proposal[" .. name .. "] is not definied"
+ end
+
+ return true, name
+end
+
+-- get /proposals
+function get_proposals()
+ utils.handle_get_objects("proposals", uci_conf, "proposal", proposal_validator)
+end
+
+-- delete a proposal
+function delete_proposal(name, check_used)
+ -- check whether proposal is defined
+ local proposal = utils.get_object(_M, ipsec_processor, "proposal", name)
+ if proposal == nil then
+ return false, 404, "proposal " .. name .. " is not defined"
+ end
+
+ if check_used == nil then
+ check_used = true
+ else
+ check_used = false
+ end
+
+ -- Todo: check whether the proposal is used
+ if check_used == true and is_proposal_used(name) then
+ return false, 400, "proposal " .. name .. " is used"
+ end
+
+ -- delete proposal
+ uci:foreach(uci_conf, "proposal",
+ function (section)
+ if name == section[".name"] or name == section["name"] then
+ uci:delete(uci_conf, section[".name"])
+ end
+ end
+ )
+
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
+
+ return true
+end
+
+-- update a proposal
+function update_proposal(proposal)
+ local name = proposal.name
+ res, code, msg = delete_proposal(name, false)
+ if res == true then
+ return utils.create_object(_M, ipsec_processor, "proposal", proposal)
+ end
+
+ return false, code, msg
+end
--- /dev/null
+-- Licensed to the public under the GNU General Public License v2.
+
+module("luci.controller.rest_v1.mwan3_rest", package.seeall)
+
+local uci = require "luci.model.uci"
+
+json = require "luci.jsonc"
+io = require "io"
+sys = require "luci.sys"
+utils = require "luci.controller.rest_v1.utils"
+
+policy_member_validator = {
+ {name="interface", required=true, validator=function(value) return is_interface_available(value) end, message="invalid interface"},
+ {name="metric", required=true, validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="metric should be greater than 0"},
+ {name="weight", required=true, validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="weight should be greater than 0"}
+}
+
+policy_validator = {
+ {name="name"},
+ {name="members", required=true, item_validator=policy_member_validator, message="Incorrect policy member"}
+}
+
+rule_validator = {
+ {name="name"},
+ {name="policy", required=true, target="use_policy", validator=function(value) return is_policy_available(value) end, message="invalid policy"},
+ {name="src_ip", validator=function(value) return utils.is_valid_ip(value) end, message="invalid ip address"},
+ {name="src_port", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid src_port"},
+ {name="dest_ip", validator=function(value) return utils.is_valid_ip(value) end, message="invalid ip address"},
+ {name="dest_port", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid dest_port"},
+ {name="proto", validator=function(value) return utils.in_array(value, {"tcp", "udp", "icmp", "all"}) end, message="wrong proto"},
+ {name="family", validator=function(value) return utils.in_array(value, {"ipv4", "ipv6", "all"}) end, message="wrong family"},
+ {name="sticky", validator=function(value) return utils.in_array(value, {"0", "1"}) end, message="wrong sticky"},
+ {name="timeout", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="invalid timeout"}
+}
+
+mwan3_processor = {
+ policy={get="get_policy", update="update_policy", create="create_policy", delete="delete_policy", validator=policy_validator},
+ rule={validator=rule_validator},
+ configuration="mwan3"
+}
+
+function index()
+ ver = "v1"
+ entry({"sdewan", "mwan3", ver, "policies"}, call("get_policies"))
+ entry({"sdewan", "mwan3", ver, "rules"}, call("get_rules"))
+ entry({"sdewan", "mwan3", ver, "policy"}, call("handle_request")).leaf = true
+ entry({"sdewan", "mwan3", ver, "rule"}, call("handle_request")).leaf = true
+end
+
+-- Request Handler
+function handle_request()
+ local handler = utils.handles_table[utils.get_req_method()]
+ if handler == nil then
+ utils.response_error(405, "Method Not Allowed")
+ else
+ return utils[handler](_M, mwan3_processor)
+ end
+end
+
+-- check interface
+function is_interface_available(name)
+ local interfaces = uci:get_all("mwan3", name)
+ if interfaces == nil then
+ return false, "Interface[" .. name .. "] is not definied"
+ end
+
+ return true
+end
+
+-- check policy
+function is_policy_available(name)
+ local policy = uci:get_all("mwan3", name)
+ if policy == nil then
+ return false, "Policy[" .. name .. "] is not definied"
+ end
+
+ return true
+end
+
+-- policy APIs
+-- get a policy
+function get_policy(name)
+ local members = uci:get_all("mwan3", name)
+ if members == nil then
+ return nil
+ end
+ members = members["use_member"]
+
+ local policy = {}
+ policy["name"] = name
+ policy["members"] = {}
+
+ for i=1, #members do
+ local member_name = members[i]
+ local member_data = uci:get_all("mwan3", member_name)
+ if not (member_data == nil) then
+ local member = {}
+ member["name"] = member_name
+ utils.set_data("mwan3", policy_member_validator, member_data, member)
+ policy["members"][i] = member
+ end
+ end
+
+ return policy
+end
+
+-- get /policies
+function get_policies()
+ if not (utils.validate_req_method("GET")) then
+ return
+ end
+
+ local res = {}
+ res["policies"] = {}
+
+ local index = 1
+ uci:foreach("mwan3", "policy",
+ function (section)
+ policy = get_policy(section[".name"])
+ if not (policy == nil) then
+ res["policies"][index] = policy
+ index = index + 1
+ end
+ end
+ )
+
+ utils.response_object(res)
+end
+
+-- create a policy
+function create_policy(policy)
+ -- check whether policy is exist
+ local policy_name = policy.name
+ local exist_policy_obj = uci:get_all("mwan3", policy_name)
+ if exist_policy_obj ~= nil then
+ return false, 409, "Conflict"
+ end
+
+ -- check member interfaces
+ local member_interface_names = {}
+ local members = policy.members
+ for i=1, #members do
+ if utils.in_array(policy_name .. "_" .. members[i].interface, member_interface_names) then
+ return false, 400, "Same interface is used in different members"
+ end
+ member_interface_names[i] = policy_name .. "_" .. members[i].interface
+ end
+
+ -- create member
+ for i=1, #members do
+ uci:section("mwan3", "member", member_interface_names[i], {
+ interface = members[i].interface,
+ metric = members[i].metric,
+ weight = members[i].weight})
+ end
+
+ -- create policy
+ uci:section("mwan3", "policy", policy_name)
+ uci:set_list("mwan3", policy_name, "use_member", member_interface_names)
+
+ -- commit change
+ uci:save("mwan3")
+ uci:commit("mwan3")
+
+ return true
+end
+
+-- check if policy is used by rule
+function is_policy_used(name)
+ local is_used = false
+
+ uci:foreach("mwan3", "rule",
+ function (section)
+ rule = utils.get_object(_M, mwan3_processor, "rule", section[".name"])
+ if not (rule == nil) then
+ if rule.policy == name then
+ is_used = true
+ return false
+ end
+ end
+ end
+ )
+
+ return is_used
+end
+
+-- delete a policy
+function delete_policy(name, check_used)
+ -- check whether policy is defined
+ local policy = get_policy(name)
+ if policy == nil then
+ return false, 404, "policy " .. name .. " is not defined"
+ end
+
+ if check_used == nil then
+ check_used = true
+ else
+ check_used = false
+ end
+
+ -- Todo: check whether the policy is used by a rule
+ if check_used == true and is_policy_used(name) then
+ return false, 400, "policy " .. name .. " is used"
+ end
+
+ -- delete policy
+ uci:delete("mwan3", name)
+
+ -- delete members
+ local members = policy.members
+ for i=1, #members do
+ uci:delete("mwan3", members[i].name)
+ end
+
+ -- commit change
+ uci:save("mwan3")
+ uci:commit("mwan3")
+
+ return true
+end
+
+-- update a policy
+function update_policy(policy)
+ local name = policy.name
+ res, code, msg = delete_policy(name, false)
+ if res == true then
+ return create_policy(policy)
+ end
+
+ return false, code, msg
+end
+
+-- Rule APIs
+-- get /rules
+function get_rules()
+ utils.handle_get_objects("rules", "mwan3", "rule", rule_validator)
+end
--- /dev/null
+-- Licensed to the public under the GNU General Public License v2.
+
+module("luci.controller.rest_v1.service", package.seeall)
+
+json = require "luci.jsonc"
+io = require "io"
+sys = require "luci.sys"
+utils = require "luci.controller.rest_v1.utils"
+
+available_services = {"mwan3", "firewall", "ipsec"}
+action_tables = {
+ mwan3 = {command="/etc/init.d/mwan3", reload="restart"},
+ firewall = {command="/etc/init.d/firewall", reload_command="fw3"},
+ ipsec = {command="/etc/init.d/ipsec", reload="restart"}
+}
+
+executeservice_validator = {
+ {name="action", required=true, validator=function(value) return utils.in_array(value, {"start", "stop", "restart", "reload"}) end, message="wrong action"}
+}
+
+function index()
+ ver = "v1"
+ entry({"sdewan", ver, "services"}, call("getServices"))
+ entry({"sdewan", ver, "service"}, call("executeService")).leaf = true
+end
+
+function getServices()
+ if not (utils.validate_req_method("GET")) then
+ return
+ end
+
+ local res = {}
+ res["services"] = available_services
+
+ utils.response_object(res)
+end
+
+function getservicename()
+ local uri_list = utils.get_URI_list(6)
+ if uri_list == nil then
+ return nil
+ end
+
+ local service = uri_list[#uri_list]
+ if not utils.in_array(service, available_services) then
+ utils.response_error(400, "Bad request URI")
+ return nil
+ end
+
+ return service
+end
+
+function executeService()
+ -- check request method
+ if not (utils.validate_req_method("PUT")) then
+ return
+ end
+
+ -- get service name
+ local service = getservicename()
+ if service == nil then
+ return
+ end
+
+ -- check content
+ local body_obj = utils.get_and_validate_body_object(executeservice_validator)
+ if body_obj == nil then
+ return
+ end
+
+ local action = body_obj["action"]
+
+ local exec_command = action_tables[service][action .. "_command"]
+ if exec_command == nil then
+ exec_command = action_tables[service]["command"]
+ end
+ local exec_action = action_tables[service][action]
+ if exec_action == nil then
+ exec_action = action
+ end
+
+ exec_command = exec_command .. " " .. exec_action
+
+ -- execute command
+ utils.log("Execute Command: %s" % exec_command)
+ sys.exec(exec_command)
+
+ utils.response_success()
+end
--- /dev/null
+-- Licensed to the public under the GNU General Public License v2.
+
+module("luci.controller.rest_v1.utils", package.seeall)
+
+local json = require "luci.jsonc"
+local uci = require "luci.model.uci"
+REQUEST_METHOD = "REQUEST_METHOD"
+
+function index()
+end
+
+function log(obj)
+ return io.stderr:write(tostring(obj) .. "\n")
+end
+
+function printTableKeys(t, prefix)
+ for key, value in pairs(t) do
+ if string.sub(key,1,string.len(prefix)) == prefix then
+ log(key)
+ end
+ end
+end
+
+-- check whether s starts with prefix
+function start_with(s, prefix)
+ return (string.find(s, "^" .. prefix) ~= nil)
+end
+
+-- check ip
+function is_match(s, reg)
+ local ret = string.match(s, reg)
+ if s == ret then
+ return true
+ end
+ return false
+end
+
+function is_valid_ip_address(s)
+ local array = {}
+ local reg = string.format("([^%s]+)", ".")
+ for item in string.gmatch(s, reg) do
+ if is_match(item, "(25[0-5])") or is_match(item, "(2[0-4]%d)") or is_match(item, "([01]?%d?%d)") then
+ table.insert(array, item)
+ else
+ return false
+ end
+ end
+
+ if #array ~= 4 then
+ return false
+ end
+
+ return true, s
+end
+
+function is_valid_ip(s)
+ local array = {}
+ local reg = string.format("([^%s]+)", "/")
+ for item in string.gmatch(s, reg) do
+ table.insert(array, item)
+ end
+
+ if #array == 1 then
+ -- check ip address
+ return is_valid_ip_address(array[1])
+ else
+ if #array == 2 then
+ if is_valid_ip_address(array[1]) then
+ -- check mask
+ if is_integer_and_in_range(array[2], -1, 33) then
+ return true, s
+ end
+ end
+ end
+ end
+
+ return false
+end
+
+function is_valid_mac(s)
+ local array = {}
+ local reg = string.format("([^%s]+)", ":")
+ for item in string.gmatch(s, reg) do
+ if is_match(item, "([a-fA-F0-9][a-fA-F0-9])") then
+ table.insert(array, item)
+ else
+ return false
+ end
+ end
+
+ if #array == 6 then
+ -- check mac
+ return true, s
+ end
+
+ return false
+end
+
+-- trim a string
+function trim(s)
+ return s:match("^%s*(.-)%s*$")
+end
+
+-- split a string based on sep
+function split_and_trim(str, sep)
+ local array = {}
+ local reg = string.format("([^%s]+)", sep)
+ for item in string.gmatch(str, reg) do
+ item_trimed = trim(item)
+ if string.len(item_trimed) > 0 then
+ table.insert(array, item_trimed)
+ end
+ end
+ return array
+end
+
+-- Check whether value1 is equal to value2
+function equal(value1, value2)
+ return value1 == value2
+end
+
+-- Check whether value is in values array
+function in_array(value, values)
+ for i=1, #values do
+ if (value == values[i]) then
+ return true, value
+ end
+ end
+ return false
+end
+
+-- Check whether value is an integer and in special range
+function is_integer_and_in_range(value, min_value, max_value)
+ if(type(value) == "string") then
+ local num_value = tonumber(value)
+ if not (num_value == nil) then
+ local int_value = math.floor(num_value)
+ if int_value == num_value then
+ if (min_value ~= nil) then
+ if (int_value <= min_value) then
+ return false
+ end
+ end
+ if (max_value ~= nil) then
+ if (int_value >= max_value) then
+ return false
+ end
+ end
+
+ return true
+ end
+ end
+ end
+
+ return false
+end
+
+-- Rest API handler function
+handles_table = {
+ PUT = "handle_put",
+ GET = "handle_get",
+ POST = "handle_post",
+ DELETE = "handle_delete"
+}
+
+function get_validator_type(validator)
+ config_type = validator["config_type"]
+ if config_type ~= nil and type(config_type) == "function" then
+ config_type = nil
+ end
+
+ return config_type
+end
+
+-- set target from src based on data validator definition
+function get_uci_section(configuration, validator, object_type, name)
+ local object_data = uci:get_all(configuration, name)
+ if object_data == nil then
+ -- check if name defined as option
+ local obj = {}
+ local found = false
+ uci:foreach(configuration, object_type,
+ function (section)
+ if name == section["name"] then
+ set_data(configuration, validator, section, obj)
+ found = true
+ return false
+ end
+ end
+ )
+
+ if found == true then
+ return obj
+ else
+ return nil
+ end
+ else
+ -- name is defined as section name
+ local obj = {}
+ obj["name"] = name
+ set_data(configuration, validator, object_data, obj)
+
+ return obj
+ end
+end
+
+function set_data(configuration, data_validator, src, target)
+ for i,v in pairs(data_validator) do
+ if(type(v) == "table") then
+ local name = v["name"]
+ local src_name = v["target"]
+ local is_list = v["is_list"]
+ if is_list == nil then
+ is_list = false
+ end
+ if src_name == nil then
+ src_name = name
+ end
+ local value = src[src_name]
+ if v["load_func"] ~= nil and type(v["load_func"]) == "function" then
+ value = v["load_func"](src)
+ end
+ if value ~= nil then
+ if is_list and type(value) ~= "table" then
+ value = { value }
+ end
+
+ if v["item_validator"] ~= nil and type(v["item_validator"]) == "table" then
+ array_value = {}
+ local index = 1
+ for j=1, #value do
+ local item_obj = nil
+ if value[j].section ~= nil then
+ item_obj = get_uci_section(configuration, v["item_validator"], value[j].section, value[j].name)
+ else
+ item_obj = get_uci_section(configuration, v["item_validator"], get_validator_type(v["item_validator"]), value[j])
+ end
+ if item_obj ~= nil then
+ array_value[index] = item_obj
+ index = index + 1
+ end
+ end
+
+ target[name] = array_value
+ else
+ if v["validator"] ~= nil and type(v["validator"]) == "table" then
+ if value.section ~= nil then
+ target[name] = get_uci_section(configuration, value.section, value.name)
+ else
+ target[name] = get_uci_section(configuration, v["validator"], get_validator_type(v["validator"]), value)
+ end
+ else
+ target[name] = value
+ end
+ end
+ end
+ end
+ end
+end
+
+-- get
+function get_objects(type_names, configuration, type_name, validator)
+ local res = {}
+ res[type_names] = {}
+
+ local index = 1
+ uci:foreach(configuration, type_name,
+ function (section)
+ local obj = {}
+ obj["name"] = section[".name"]
+ set_data(configuration, validator, section, obj)
+ res[type_names][index] = obj
+ index = index + 1
+ end
+ )
+
+ return res
+end
+
+function handle_get_objects(type_names, configuration, type_name, validator)
+ if not (validate_req_method("GET")) then
+ return
+ end
+
+ response_object(get_objects(type_names, configuration, type_name, validator))
+end
+
+function get_object(module_table, processors, object_type, name)
+ if processors ~= nil and processors[object_type] ~= nil
+ and processors[object_type]["get"] ~= nil
+ and module_table[processors[object_type]["get"]] ~= nil then
+ return module_table[processors[object_type]["get"]](name)
+ else
+ return get_uci_section(processors["configuration"], processors[object_type].validator, object_type, name)
+ end
+end
+
+function handle_get(module_table, processors)
+ local uri_list = get_URI_list(7)
+ if uri_list == nil then
+ return
+ end
+
+ local object_type = uri_list[#uri_list-1]
+ local name = uri_list[#uri_list]
+ local obj = get_object(module_table, processors, object_type, name)
+
+ if obj == nil then
+ response_error(404, "Cannot find " .. object_type .. "[" .. name .. "]" )
+ return
+ end
+
+ response_object(obj)
+end
+
+-- put
+function update_object(module_table, processors, object_type, obj)
+ if processors ~= nil and processors[object_type] ~= nil
+ and processors[object_type]["update"] ~= nil
+ and module_table[processors[object_type]["update"]] ~= nil then
+ return module_table[processors[object_type]["update"]](obj)
+ else
+ local name = obj.name
+ res, code, msg = delete_object(module_table, processors, object_type, name)
+ if res == true then
+ return create_object(module_table, processors, object_type, obj)
+ end
+
+ return false, code, msg
+ end
+end
+
+function handle_put(module_table, processors)
+ local uri_list = get_URI_list(7)
+ if uri_list == nil then
+ return
+ end
+
+ local object_type = uri_list[#uri_list-1]
+ local name = uri_list[#uri_list]
+ -- check content and get object
+ local obj = get_and_validate_body_object(processors[object_type].validator)
+ if obj == nil then
+ return
+ end
+ obj.name = name
+
+ res, code, msg = update_object(module_table, processors, object_type, obj)
+ if res == false then
+ response_error(code, msg)
+ else
+ response_object(get_object(module_table, processors, object_type, name))
+ end
+end
+
+-- post
+function create_uci_section(configuration, validator, object_type, obj)
+ -- check whether object is exist
+ local obj_name = obj.name
+ local exist_obj = uci:get_all(configuration, obj_name)
+ if exist_obj ~= nil then
+ return false, 409, "Conflict"
+ end
+
+ -- create object
+ local create_section_name = validator["create_section_name"]
+ if create_section_name == nil then
+ create_section_name = true
+ end
+
+ local obj_section = nil
+ local obj_section_type = object_type
+ local obj_section_config_type = validator["config_type"]
+ if obj_section_config_type ~= nil then
+ if type(obj_section_config_type) == "function" then
+ obj_section_type = obj_section_config_type(obj)
+ else
+ obj_section_type = obj_section_config_type
+ end
+ end
+ if create_section_name == true then
+ obj_section = uci:section(configuration, obj_section_type, obj_name)
+ else
+ obj_section = uci:section(configuration, obj_section_type)
+ end
+
+ for i,v in pairs(validator) do
+ if(type(v) == "table") then
+ local name = v["name"]
+ if create_section_name == false or name ~= "name" then
+ if(obj[name] ~= nil) then
+ local target_name = v["target"]
+ local use_target_name = false
+ if(target_name == nil) then
+ target_name = name
+ else
+ use_target_name = true
+ end
+ local obj_value = obj[name]
+ if v["save_func"] ~= nil and type(v["save_func"]) == "function" then
+ res, obj_value = v["save_func"](obj)
+ if res == false then
+ return res, obj_value
+ end
+ end
+ if type(obj_value) == "table" then
+ -- Support creating multiple options/sections:
+ -- change obj_value to standard format: {{option="option_name", values={...}},}
+ if obj_value._standard_format == nil then
+ obj_value = {{option=target_name, values=obj_value},}
+ end
+ for j=1, #obj_value do
+ local option_name = obj_value[j].option
+ local section_obj = obj_value[j].values
+ if v["item_validator"] ~= nil and type(v["item_validator"]) == "table" then
+ local sub_obj_names = {}
+ for k=1, #section_obj do
+ sub_obj_names[k] = section_obj[k].name
+ local res, msg = create_uci_section(configuration, v["item_validator"], option_name, section_obj[k])
+ if res == false then
+ return res, msg
+ end
+ end
+ uci:set_list(configuration, obj_section, option_name, sub_obj_names)
+ else
+ uci:set_list(configuration, obj_section, option_name, section_obj)
+ end
+ end
+ else
+ if v["validator"] ~= nil and type(v["validator"]) == "table" then
+ local res, msg = create_uci_section(configuration, v["validator"], target_name, obj_value)
+ if res == false then
+ return res, msg
+ end
+ uci:set(configuration, obj_section, target_name, obj_value.name)
+ else
+ if obj_value ~= "" then
+ uci:set(configuration, obj_section, target_name, obj_value)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return true
+end
+
+function create_object(module_table, processors, object_type, obj)
+ if processors ~= nil and processors[object_type] ~= nil
+ and processors[object_type]["create"] ~= nil
+ and module_table[processors[object_type]["create"]] ~= nil then
+ return module_table[processors[object_type]["create"]](obj)
+ else
+ local res, msg = create_uci_section(processors["configuration"], processors[object_type].validator, object_type, obj)
+
+ if res == false then
+ uci:revert(processors["configuration"])
+ return res, msg
+ end
+
+ -- commit change
+ uci:save(processors["configuration"])
+ uci:commit(processors["configuration"])
+
+ return true
+ end
+end
+
+function handle_post(module_table, processors)
+ local uri_list = get_URI_list(6)
+ if uri_list == nil then
+ return
+ end
+
+ local object_type = uri_list[#uri_list]
+ -- check content and get policy object
+ local obj = get_and_validate_body_object(processors[object_type].validator)
+ if obj == nil then
+ return
+ end
+
+ -- check name is not empty
+ local name = obj.name
+ if name ~= nil and type(name) == "string" then
+ name = trim(name)
+ else
+ name = nil
+ end
+ if name == nil or name == "" then
+ response_error(400, "Field[name] checked failed: required")
+ return
+ end
+
+ res, code, msg = create_object(module_table, processors, object_type, obj)
+ if res == false then
+ response_error(code, msg)
+ else
+ response_object(get_object(module_table, processors, object_type, name), 201)
+ end
+end
+
+-- delete
+function delete_uci_section(configuration, validator, obj, object_type)
+ local name = obj["name"]
+ local obj_section_config_type = validator["config_type"]
+ if obj_section_config_type ~= nil then
+ if type(obj_section_config_type) == "function" then
+ object_type = obj_section_config_type(obj)
+ else
+ object_type = obj_section_config_type
+ end
+ end
+
+ -- delete depedency uci section
+ for i,v in pairs(validator) do
+ if(type(v) == "table") then
+ if v["item_validator"] ~= nil and type(v["item_validator"]) == "table" then
+ for j=1, #obj[v["name"]] do
+ local sub_obj = obj[v["name"]][j]
+ delete_uci_section(configuration, v["item_validator"], obj[v["name"]][j], v["name"])
+ end
+ else
+ if v["validator"] ~= nil and type(v["validator"]) == "table" then
+ delete_uci_section(configuration, v["validator"], obj[v["name"]], v["name"])
+ end
+ end
+ end
+ end
+
+ -- delete uci section
+ uci:foreach(configuration, object_type,
+ function (section)
+ if name == section[".name"] or name == section["name"] then
+ for i,v in pairs(validator) do
+ if(type(v) == "table") then
+ if v["delete_func"] ~= nil and type(v["delete_func"]) == "function" then
+ v["delete_func"](section)
+ end
+ end
+ end
+ uci:delete(configuration, section[".name"])
+ end
+ end
+ )
+end
+
+function delete_object(module_table, processors, object_type, name)
+ if processors ~= nil and processors[object_type] ~= nil
+ and processors[object_type]["delete"] ~= nil
+ and module_table[processors[object_type]["delete"]] ~= nil then
+ return module_table[processors[object_type]["delete"]](name)
+ else
+ local obj = get_object(module_table, processors, object_type, name)
+ if obj == nil then
+ return false, 404, object_type .. " " .. name .. " is not defined"
+ end
+
+ delete_uci_section(processors["configuration"], processors[object_type].validator, obj, object_type)
+
+ -- commit change
+ uci:save(processors["configuration"])
+ uci:commit(processors["configuration"])
+
+ return true
+ end
+end
+
+function handle_delete(module_table, processors)
+ local uri_list = get_URI_list(7)
+ if uri_list == nil then
+ return
+ end
+
+ local object_type = uri_list[#uri_list-1]
+ local name = uri_list[#uri_list]
+
+ res, code, msg = delete_object(module_table, processors, object_type, name)
+ if res == false then
+ response_error(code, msg)
+ else
+ response_success()
+ end
+end
+
+-- Validate src with validator then set target (array)
+function validate_and_set_data_array(validator, src)
+ if #src == 0 then
+ return false, "Not an array"
+ end
+
+ local ret_obj = {}
+ for i=1, #src do
+ local res = false
+ local ret_obj_item = nil
+ if type(validator) == "function" then
+ res, ret_obj_item = validator(src[i])
+ else
+ res, ret_obj_item = validate_and_set_data(validator, src[i])
+ end
+
+ if not res then
+ return false, ret_obj_item
+ end
+ ret_obj[i] = ret_obj_item
+ end
+
+ return true, ret_obj
+end
+
+-- Validate src with validator then set target (set data field with default value if applied)
+-- return:
+-- res: validate result
+-- ret_obj: copy of src based on validator definition(res=true) or error message(res=false)
+function validate_and_set_data(validator, src)
+ local target = {}
+ for i,v in pairs(validator) do
+ if(type(v) == "table") then
+ local name = v["name"]
+ local val_func = v["validator"]
+ local item_val_func = v["item_validator"]
+ local error_message = v["message"]
+ local required = v["required"]
+ local default = v["default"]
+ local target_name = name
+
+ if required == nil then
+ required = false
+ end
+
+ if error_message == nil then
+ error_message = ""
+ end
+
+ local value = src[name]
+ if value ~= nil and type(value) == "string" then
+ value = trim(value)
+ end
+
+ if value ~= nil and value ~= "" then
+ local res = true
+ local ret_obj = nil
+ if val_func ~= nil then
+ if type(val_func) == "function" then
+ res, ret_obj = val_func(value)
+ else
+ res, ret_obj = validate_and_set_data(val_func, value)
+ end
+ else
+ if item_val_func ~= nil then
+ res, ret_obj = validate_and_set_data_array(item_val_func, value)
+ end
+ end
+
+ if res == false then
+ if ret_obj ~= nil and ret_obj ~= "" then
+ return false, "Field[" .. name .. "] checked failed: " .. error_message .. " [" .. ret_obj .. "]"
+ else
+ return false, "Field[" .. name .. "] checked failed: " .. error_message
+ end
+ else
+ if ret_obj ~= nil then
+ value = ret_obj
+ end
+ end
+
+ target[target_name] = value
+ else
+ if required then
+ return false, "Field[" .. name .. "] checked failed: required"
+ else
+ -- set default value
+ if default ~= nil then
+ target[target_name] = default
+ end
+ end
+ end
+ end
+ end
+
+ -- check with object_validator
+ local object_validator = validator["object_validator"]
+ if object_validator ~= nil then
+ return object_validator(target)
+ end
+
+ return true, target
+end
+
+-- parse URI
+function get_URI_list(required_lenth)
+ local uri = luci.http.getenv("REQUEST_URI")
+ local uri_list = split_and_trim(uri, "/")
+ if not (#uri_list == required_lenth) then
+ response_error(400, "Bad request URI")
+ return nil
+ end
+
+ return uri_list
+end
+
+-- get request body
+function get_request_body_object()
+ local content, size = luci.http.content()
+ local content_obj, err = json.parse(content)
+ if not (err == nil) then
+ response_error(400, "Body parse error: " .. err)
+ end
+
+ return content_obj
+end
+
+-- get and validate body object
+function get_and_validate_body_object(validator)
+ local body_obj = get_request_body_object()
+ if body_obj == nil then
+ return nil
+ end
+
+ local res, res_obj = validate_and_set_data(validator, body_obj)
+ if not res then
+ response_error(400, res_obj)
+ return nil
+ end
+
+ return res_obj
+end
+
+-- get request method
+function get_req_method()
+ return luci.http.getenv("REQUEST_METHOD")
+end
+
+-- check request method
+function validate_req_method(method)
+ local res = equal(get_req_method(), method)
+ if not res then
+ response_error(405, "Method Not Allowed")
+ end
+
+ return res
+end
+
+-- response with error
+function response_error(code, message)
+ if message == nil then
+ message = ""
+ end
+ luci.http.status(code, message)
+ luci.http.prepare_content("text/plain")
+ luci.http.write('{"message":"' .. message .. '"}')
+end
+
+-- response with json object
+function response_object(obj, code)
+ if code ~= nil then
+ luci.http.status(code, "")
+ end
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(obj)
+end
+
+-- response success message
+function response_success(code)
+ local message = '{"message":"success"}'
+ if code ~= nil then
+ luci.http.status(code, message)
+ end
+ response_message(message)
+end
+
+-- response with message
+function response_message(message)
+ luci.http.prepare_content("text/plain")
+ luci.http.write(message)
+end
\ No newline at end of file
--- /dev/null
+# set docker proxy with below line
+#docker_proxy=
--- /dev/null
+config system
+ option log_file '/var/log/mylog'
+ option timezone 'UTC'
+ option ttylogin '0'
+ option log_size '64'
+ option urandom_seed '0'
+EOF
--- /dev/null
+package openwrt
+
+import (
+ "openwrt"
+ "reflect"
+ "testing"
+ "flag"
+ "encoding/json"
+ "fmt"
+ "os"
+)
+
+var fw openwrt.FirewallClient
+var available_zone_1 string
+var available_zone_2 string
+
+func TestMain(m *testing.M) {
+ servIp := flag.String("ip", "10.244.0.18", "SDEWAN CNF Management IP Address")
+ flag.Parse()
+
+ client := openwrt.NewOpenwrtClient(*servIp, "root", "")
+ fw = openwrt.FirewallClient{client}
+ available_zone_1 = "wan"
+ available_zone_2 = "lan"
+
+ os.Exit(m.Run())
+}
+
+// Error handler
+func handleError(t *testing.T, err error, name string, expectedErr bool, errorCode int) {
+ if (err != nil) {
+ if (expectedErr) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ if(errorCode != err.(*openwrt.OpenwrtError).Code) {
+ t.Errorf("Test case '%s': expected '%d', but got '%d'", name, errorCode, err.(*openwrt.OpenwrtError).Code)
+ } else {
+ fmt.Printf("%s\n", err.(*openwrt.OpenwrtError).Message)
+ }
+ default:
+ t.Errorf("Test case '%s': expected openwrt.OpenwrtError, but got '%s'", name, reflect.TypeOf(err).String())
+ }
+ } else {
+ t.Errorf("Test case '%s': expected success, but got '%s'", name, reflect.TypeOf(err).String())
+ }
+ } else {
+ if (expectedErr) {
+ t.Errorf("Test case '%s': expected error code '%d', but success", name, errorCode)
+ }
+ }
+}
+
+func printError(err error) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("%s\n", err.(*openwrt.OpenwrtError).Message)
+ default:
+ fmt.Printf("%s\n", reflect.TypeOf(err).String())
+ }
+}
+
+// Firewall Zone API Test
+func TestGetZones(t *testing.T) {
+ res, err := fw.GetZones()
+ if res == nil {
+ printError(err)
+ t.Errorf("Test case GetZones: can not get firewall zones")
+ return
+ }
+
+ if len(res.Zones) == 0 {
+ fmt.Printf("Test case GetZones: no zone defined")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetZone(t *testing.T) {
+ tcases := []struct {
+ name string
+ zone string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetAvailableZone",
+ zone: available_zone_1,
+ },
+ {
+ name: "GetFoolRule",
+ zone: "foo_zone",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.GetZone(tcase.zone)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolZone(t *testing.T) {
+ tcases := []struct {
+ name string
+ zone openwrt.SdewanFirewallZone
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ zone: openwrt.SdewanFirewallZone{Name:" "},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidNetwork",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", Network:[]string{"fool_name"}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidMasq",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", Masq:"2"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidMasqSrc",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", MasqSrc:[]string{"256.0.0.0/0"}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidMasqDest",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", MasqDest:[]string{"256.0.0.0/0"}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidMasqAllowInvalid",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", MasqAllowInvalid:"2"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidMtuFix",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", MtuFix:"2"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidInput",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", Input:"FOOL"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidForward",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", Forward:"FOOL"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidOutput",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", Output:"FOOL"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidFamily",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", Family:"FOOL"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidSubnet",
+ zone: openwrt.SdewanFirewallZone{Name:"test_zone", Subnet:[]string{"256.0.0.0/0"}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.CreateZone(tcase.zone)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolZone(t *testing.T) {
+ tcases := []struct {
+ name string
+ zone string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ zone: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ zone: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := fw.DeleteZone(tcase.zone)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteAvailableZone(t *testing.T) {
+ var err error
+ var zone_name = "test_zone_1"
+ var redirect_name = "test_redirect_1"
+ var zone = openwrt.SdewanFirewallZone{Name:zone_name, Network:[]string{"wan", "lan"}, Input:"REJECT", Masq:"1"}
+ var redirect = openwrt.SdewanFirewallRedirect{Name:redirect_name, Src:zone_name, SrcDPort:"22000", Dest:"lan", DestPort:"22", Proto:"tcp", Target:"DNAT"}
+
+ // create zone
+ _, err = fw.GetZone(zone_name)
+ if (err == nil) {
+ err = fw.DeleteZone(zone_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestDeleteAvailableZone: failed to delete zone '%s'", zone_name)
+ return
+ }
+ }
+
+ _, err = fw.CreateZone(zone)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestDeleteAvailableZone: failed to create zone '%s'", zone_name)
+ return
+ }
+
+ // create redirect
+ _, err = fw.GetRedirect(redirect_name)
+ if (err == nil) {
+ err = fw.DeleteRedirect(redirect_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestDeleteAvailableZone: failed to delete redirect '%s'", redirect_name)
+ return
+ }
+ }
+
+ _, err = fw.CreateRedirect(redirect)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestDeleteAvailableZone: failed to create redirect '%s'", redirect_name)
+ return
+ }
+
+ // try to delete a used zone
+ err = fw.DeleteZone(zone_name)
+ if (err != nil) {
+ printError(err)
+ } else {
+ t.Errorf("Test case TestDeleteAvailableZone: error to delete zone '%s'", zone_name)
+ return
+ }
+
+ // clean
+ err = fw.DeleteRedirect(redirect_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestDeleteAvailableZone: failed to delete redirect '%s'", redirect_name)
+ return
+ }
+
+ err = fw.DeleteZone(zone_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestDeleteAvailableZone: failed to delete zone '%s'", zone_name)
+ return
+ }
+}
+
+func TestUpdateFoolZone(t *testing.T) {
+ tcases := []struct {
+ name string
+ zone openwrt.SdewanFirewallZone
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ zone: openwrt.SdewanFirewallZone{Name:"fool_name"},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.UpdateZone(tcase.zone)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestZone(t *testing.T) {
+ var err error
+ var ret_zone *openwrt.SdewanFirewallZone
+
+ var zone_name = "test_zone"
+ var zone = openwrt.SdewanFirewallZone{Name:zone_name, Network:[]string{"wan", "lan"}, Input:"REJECT", Masq:"1"}
+ var update_zone = openwrt.SdewanFirewallZone{Name:zone_name, Network:[]string{"lan"}, Input:"ACCEPT", MasqSrc:[]string{"!0.0.0.0/0", "172.16.1.1"}, Subnet:[]string{"172.16.0.1/24"}}
+
+ _, err = fw.GetZone(zone_name)
+ if (err == nil) {
+ err = fw.DeleteZone(zone_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestZone: failed to delete zone '%s'", zone_name)
+ return
+ }
+ }
+
+ // Create zone
+ ret_zone, err = fw.CreateZone(zone)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestZone: failed to create zone '%s'", zone_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_zone)
+ fmt.Printf("Created Zone: %s\n", string(p_data))
+ }
+
+ ret_zone, err = fw.GetZone(zone_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestZone: failed to get created zone")
+ return
+ } else {
+ if( ret_zone.Input != "REJECT" ) {
+ t.Errorf("Test case TestZone: failed to create zone")
+ return
+ }
+ }
+
+ // Update zone
+ ret_zone, err = fw.UpdateZone(update_zone)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestZone: failed to update zone '%s'", zone_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_zone)
+ fmt.Printf("Updated Zone: %s\n", string(p_data))
+ }
+
+ ret_zone, err = fw.GetZone(zone_name)
+ if (err != nil) {
+ t.Errorf("Test case TestZone: failed to get updated zone")
+ return
+ } else {
+ if( ret_zone.Input != "ACCEPT" ) {
+ t.Errorf("Test case TestZone: failed to update zone")
+ return
+ }
+ }
+
+ // Delete zone
+ err = fw.DeleteZone(zone_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestZone: failed to delete zone '%s'", zone_name)
+ return
+ }
+
+ ret_zone, err = fw.GetZone(zone_name)
+ if (err == nil) {
+ t.Errorf("Test case TestZone: failed to delete zone")
+ return
+ }
+}
+
+// Firewall Forwarding API Test
+func TestGetForwardings(t *testing.T) {
+ res, err := fw.GetForwardings()
+ if res == nil {
+ printError(err)
+ t.Errorf("Test case GetForwardings: can not get firewall forwardings")
+ return
+ }
+
+ if len(res.Forwardings) == 0 {
+ fmt.Printf("Test case GetForwardings: no forwarding defined")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetForwarding(t *testing.T) {
+ tcases := []struct {
+ name string
+ forwarding string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetFoolForwarding",
+ forwarding: "foo_forwarding",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.GetForwarding(tcase.forwarding)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolForwarding(t *testing.T) {
+ tcases := []struct {
+ name string
+ forwarding openwrt.SdewanFirewallForwarding
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ forwarding: openwrt.SdewanFirewallForwarding{Name:" "},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "NoSrc",
+ forwarding: openwrt.SdewanFirewallForwarding{Name:"test_forwarding", Dest: available_zone_1},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidSrc",
+ forwarding: openwrt.SdewanFirewallForwarding{Name:"test_forwarding", Src:"fool_zone", Dest: available_zone_1},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "NoDest",
+ forwarding: openwrt.SdewanFirewallForwarding{Name:"test_forwarding", Src: available_zone_1},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidDest",
+ forwarding: openwrt.SdewanFirewallForwarding{Name:"test_forwarding", Src:available_zone_1, Dest:"fool_zone"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidFamily",
+ forwarding: openwrt.SdewanFirewallForwarding{Name:"test_forwarding", Family:"fool_family", Src:available_zone_1, Dest:available_zone_2},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.CreateForwarding(tcase.forwarding)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolForwarding(t *testing.T) {
+ tcases := []struct {
+ name string
+ forwarding string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ forwarding: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ forwarding: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := fw.DeleteForwarding(tcase.forwarding)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestUpdateFoolForwarding(t *testing.T) {
+ tcases := []struct {
+ name string
+ forwarding openwrt.SdewanFirewallForwarding
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ forwarding: openwrt.SdewanFirewallForwarding{Name:"fool_name", Src:available_zone_1, Dest:available_zone_2},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.UpdateForwarding(tcase.forwarding)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestForwarding(t *testing.T) {
+ var err error
+ var ret_forwarding *openwrt.SdewanFirewallForwarding
+
+ var forwarding_name = "test_forwarding"
+ var forwarding = openwrt.SdewanFirewallForwarding{Name:forwarding_name, Src:available_zone_1, Dest:available_zone_2}
+ var update_forwarding = openwrt.SdewanFirewallForwarding{Name:forwarding_name, Src:available_zone_2, Dest:available_zone_1}
+
+ _, err = fw.GetForwarding(forwarding_name)
+ if (err == nil) {
+ err = fw.DeleteForwarding(forwarding_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestForwarding: failed to delete forwarding '%s'", forwarding_name)
+ return
+ }
+ }
+
+ // Create forwarding
+ ret_forwarding, err = fw.CreateForwarding(forwarding)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestForwarding: failed to create forwarding '%s'", forwarding_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_forwarding)
+ fmt.Printf("Created Forwarding: %s\n", string(p_data))
+ }
+
+ ret_forwarding, err = fw.GetForwarding(forwarding_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestForwarding: failed to get created forwarding")
+ return
+ } else {
+ if( ret_forwarding.Dest != available_zone_2 ) {
+ t.Errorf("Test case TestForwarding: failed to create forwarding")
+ return
+ }
+ }
+
+ // Update forwarding
+ ret_forwarding, err = fw.UpdateForwarding(update_forwarding)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestForwarding: failed to update forwarding '%s'", forwarding_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_forwarding)
+ fmt.Printf("Updated Forwarding: %s\n", string(p_data))
+ }
+
+ ret_forwarding, err = fw.GetForwarding(forwarding_name)
+ if (err != nil) {
+ t.Errorf("Test case TestForwarding: failed to get updated forwarding")
+ return
+ } else {
+ if( ret_forwarding.Dest != available_zone_1 ) {
+ t.Errorf("Test case TestForwarding: failed to update forwarding")
+ return
+ }
+ }
+
+ // Delete forwarding
+ err = fw.DeleteForwarding(forwarding_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestForwarding: failed to delete forwarding '%s'", forwarding_name)
+ return
+ }
+
+ ret_forwarding, err = fw.GetForwarding(forwarding_name)
+ if (err == nil) {
+ t.Errorf("Test case TestForwarding: failed to delete forwarding")
+ return
+ }
+}
+
+// Firewall Redirect API Test
+func TestGetRedirects(t *testing.T) {
+ res, err := fw.GetRedirects()
+ if res == nil {
+ printError(err)
+ t.Errorf("Test case GetRedirects: can not get firewall redirects")
+ return
+ }
+
+ if len(res.Redirects) == 0 {
+ fmt.Printf("Test case GetRedirects: no redirect defined")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetRedirect(t *testing.T) {
+ tcases := []struct {
+ name string
+ redirect string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetFoolRedirect",
+ redirect: "foo_redirect",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.GetRedirect(tcase.redirect)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolRedirect(t *testing.T) {
+ tcases := []struct {
+ name string
+ redirect openwrt.SdewanFirewallRedirect
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ redirect: openwrt.SdewanFirewallRedirect{Name:" "},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "No Src for DNAT",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", Dest:available_zone_2, DestPort:"22", Proto:"tcp", Target:"DNAT"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "No SrcDIp for SNAT",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", Dest:available_zone_2, DestPort:"22", Proto:"tcp", Target:"SNAT"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "No Dest for SNAT",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", SrcDIp:"192.168.1.1", DestPort:"22", Proto:"tcp", Target:"SNAT"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Src",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", Src:"fool_zone"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SrcIp",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", SrcIp:"192.168.1.300"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SrcDIp",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", SrcDIp:"192.168.1.300"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SrcMac",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", SrcMac:"00:00"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SrcPort",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", SrcPort:"1a"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SrcDPort",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", SrcDPort:"1a"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Proto",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", Proto:"fool_protocol"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Dest",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", Dest:"fool_zone"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid DestIp",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", DestIp:"192.168.1a.1"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid DestPort",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", DestPort:"0"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Target",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", Target:"fool_NAT"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Family",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"test_redirect", Family:"fool_family"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.CreateRedirect(tcase.redirect)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolRedirect(t *testing.T) {
+ tcases := []struct {
+ name string
+ redirect string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ redirect: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ redirect: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := fw.DeleteRedirect(tcase.redirect)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestUpdateFoolRedirect(t *testing.T) {
+ tcases := []struct {
+ name string
+ redirect openwrt.SdewanFirewallRedirect
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ redirect: openwrt.SdewanFirewallRedirect{Name:"fool_name", Src:available_zone_1, SrcDPort:"22000", Dest:available_zone_2, DestPort:"22", Proto:"tcp", Target:"DNAT"},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.UpdateRedirect(tcase.redirect)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestRedirect(t *testing.T) {
+ var err error
+ var ret_redirect *openwrt.SdewanFirewallRedirect
+
+ var redirect_name = "test_redirect"
+ var redirect = openwrt.SdewanFirewallRedirect{Name:redirect_name, Src:available_zone_1, SrcDPort:"22000", Dest:available_zone_2, DestPort:"22", Proto:"tcp", Target:"DNAT"}
+ var update_redirect = openwrt.SdewanFirewallRedirect{Name:redirect_name, Src:available_zone_1, SrcDPort:"22001", Dest:available_zone_2, DestPort:"22", Proto:"tcp", Target:"DNAT"}
+
+ _, err = fw.GetRedirect(redirect_name)
+ if (err == nil) {
+ err = fw.DeleteRedirect(redirect_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRedirect: failed to delete redirect '%s'", redirect_name)
+ return
+ }
+ }
+
+ // Create redirect
+ ret_redirect, err = fw.CreateRedirect(redirect)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRedirect: failed to create redirect '%s'", redirect_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_redirect)
+ fmt.Printf("Created Redirect: %s\n", string(p_data))
+ }
+
+ ret_redirect, err = fw.GetRedirect(redirect_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRedirect: failed to get created redirect")
+ return
+ } else {
+ if( ret_redirect.SrcDPort != "22000" ) {
+ t.Errorf("Test case TestRedirect: failed to create redirect")
+ return
+ }
+ }
+
+ // Update redirect
+ ret_redirect, err = fw.UpdateRedirect(update_redirect)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRedirect: failed to update redirect '%s'", redirect_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_redirect)
+ fmt.Printf("Updated Redirect: %s\n", string(p_data))
+ }
+
+ ret_redirect, err = fw.GetRedirect(redirect_name)
+ if (err != nil) {
+ t.Errorf("Test case TestRedirect: failed to get updated redirect")
+ return
+ } else {
+ if( ret_redirect.SrcDPort != "22001" ) {
+ t.Errorf("Test case TestRedirect: failed to update redirect")
+ return
+ }
+ }
+
+ // Delete redirect
+ err = fw.DeleteRedirect(redirect_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRedirect: failed to delete redirect '%s'", redirect_name)
+ return
+ }
+
+ ret_redirect, err = fw.GetRedirect(redirect_name)
+ if (err == nil) {
+ t.Errorf("Test case TestRedirect: failed to delete redirect")
+ return
+ }
+}
+
+// Firewall Rule API Test
+func TestGetRules(t *testing.T) {
+ res, err := fw.GetRules()
+ if res == nil {
+ printError(err)
+ t.Errorf("Test case GetRules: can not get firewall rules")
+ return
+ }
+
+ if len(res.Rules) == 0 {
+ fmt.Printf("Test case GetRules: no rule defined")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetFoolRule",
+ rule: "foo_rule",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.GetRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule openwrt.SdewanFirewallRule
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ rule: openwrt.SdewanFirewallRule{Name:" "},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "No SetMark and SetXmark for DNAT",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", Target:"MARK"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SRC",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", Src:"fool_zone"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SrcIp",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", SrcIp:"191.11.aa.10"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Valid SrcMac",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", SrcMac:"01:02:a0:0F:aC:EE"},
+ },
+ {
+ name: "Invalid SrcMac",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", SrcMac:"11:rt:00:00:00:00"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid SrcPort",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", SrcPort:"0"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Proto",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", Proto:"fool_proto"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid IcmpType",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", IcmpType:[]string{"network-unreachable", "address-mask-reply", "fool_icmp_type"}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Dest",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", Dest:"fool_zone"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid DestIp",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", DestIp:"192.168.1.2.1"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid DestPort",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", DestPort:"aa"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Target",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", Target:"fool_target"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Invalid Family",
+ rule: openwrt.SdewanFirewallRule{Name:"test_rule", Family:"fool_family"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.CreateRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ rule: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ rule: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := fw.DeleteRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestUpdateFoolRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule openwrt.SdewanFirewallRule
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ rule: openwrt.SdewanFirewallRule{Name:"fool_name", Src:available_zone_1, Proto:"udp", DestPort:"68", Target:"REJECT", Family:"ipv4"},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := fw.UpdateRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestRule(t *testing.T) {
+ var err error
+ var ret_rule *openwrt.SdewanFirewallRule
+
+ var rule_name = "test_rule"
+ var rule = openwrt.SdewanFirewallRule{Name:rule_name, Src:available_zone_1, Proto:"udp", DestPort:"68", Target:"REJECT", Family:"ipv4"}
+ var update_rule = openwrt.SdewanFirewallRule{Name:rule_name, Src:available_zone_1, IcmpType:[]string{"host-redirect", "echo-request"}, Proto:"udp", DestPort:"68", Target:"ACCEPT", Family:"ipv4"}
+
+ _, err = fw.GetRule(rule_name)
+ if (err == nil) {
+ err = fw.DeleteRule(rule_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to delete rule '%s'", rule_name)
+ return
+ }
+ }
+
+ // Create rule
+ ret_rule, err = fw.CreateRule(rule)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to create rule '%s'", rule_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_rule)
+ fmt.Printf("Created Rule: %s\n", string(p_data))
+ }
+
+ ret_rule, err = fw.GetRule(rule_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to get created rule")
+ return
+ } else {
+ if( ret_rule.Target != "REJECT" ) {
+ t.Errorf("Test case TestRule: failed to create rule")
+ return
+ }
+ }
+
+ // Update rule
+ ret_rule, err = fw.UpdateRule(update_rule)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to update rule '%s'", rule_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_rule)
+ fmt.Printf("Updated Rule: %s\n", string(p_data))
+ }
+
+ ret_rule, err = fw.GetRule(rule_name)
+ if (err != nil) {
+ t.Errorf("Test case TestRule: failed to get updated rule")
+ return
+ } else {
+ if( ret_rule.Target != "ACCEPT" ) {
+ t.Errorf("Test case TestRule: failed to update rule")
+ return
+ }
+ }
+
+ // Delete rule
+ err = fw.DeleteRule(rule_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to delete rule '%s'", rule_name)
+ return
+ }
+
+ ret_rule, err = fw.GetRule(rule_name)
+ if (err == nil) {
+ t.Errorf("Test case TestRule: failed to delete rule")
+ return
+ }
+}
--- /dev/null
+package openwrt
+
+import (
+ "openwrt"
+ "reflect"
+ "testing"
+ "flag"
+ "encoding/json"
+ "fmt"
+ "os"
+)
+
+var ic openwrt.IpsecClient
+var available_proposal_1 string
+var available_proposal_2 string
+var available_site string
+
+func TestMain(m *testing.M) {
+ servIp := flag.String("ip", "10.244.0.18", "SDEWAN CNF Management IP Address")
+ flag.Parse()
+
+ var err error
+ client := openwrt.NewOpenwrtClient(*servIp, "root", "")
+ ic = openwrt.IpsecClient{client}
+ available_proposal_1 = "test_proposal1"
+ available_proposal_2 = "test_proposal2"
+ available_site = "test_default_site"
+
+ // create default proposals
+ var proposall = openwrt.SdewanIpsecProposal{Name:available_proposal_1, EncryptionAlgorithm:"aes128", HashAlgorithm:"sha256", DhGroup:"modp3072",}
+ var proposal2 = openwrt.SdewanIpsecProposal{Name:available_proposal_2, EncryptionAlgorithm:"aes256", HashAlgorithm:"sha128", DhGroup:"modp4096",}
+
+ _, err = ic.GetProposal(available_proposal_1)
+ if (err != nil) {
+ // Create proposal
+ _, err = ic.CreateProposal(proposall)
+ if (err != nil) {
+ printError(err)
+ return
+ }
+ }
+
+ _, err = ic.GetProposal(available_proposal_2)
+ if (err != nil) {
+ // Create proposal
+ _, err = ic.CreateProposal(proposal2)
+ if (err != nil) {
+ printError(err)
+ return
+ }
+ }
+
+ // create default site
+ var site = openwrt.SdewanIpsecSite{
+ Name:available_site,
+ Gateway:"10.0.1.2",
+ PreSharedKey:"test_key",
+ AuthenticationMethod:"psk",
+ LocalIdentifier:"C=CH, O=strongSwan, CN=peer",
+ RemoteIdentifier:"C=CH, O=strongSwan, CN=peerB",
+ ForceCryptoProposal:"true",
+ LocalPublicCert:"public cert\npublic cert value",
+ CryptoProposal:[]string{available_proposal_1},
+ Connections:[]openwrt.SdewanIpsecConnection{{
+ Name:available_site+"_conn",
+ Type:"tunnel",
+ Mode:"start",
+ LocalSubnet:"192.168.1.1/24",
+ LocalSourceip:"10.0.1.1",
+ RemoteSubnet:"192.168.0.1/24",
+ RemoteSourceip:"10.0.1.2",
+ CryptoProposal:[]string{available_proposal_1, available_proposal_2},
+ },
+ },
+ }
+
+ _, err = ic.GetSite(available_site)
+ if (err == nil) {
+ // Update site
+ _, err = ic.UpdateSite(site)
+ if (err != nil) {
+ printError(err)
+ return
+ }
+ } else {
+ // Create site
+ _, err = ic.CreateSite(site)
+ if (err != nil) {
+ printError(err)
+ return
+ }
+ }
+
+ var ret = m.Run()
+
+ // clean
+ ic.DeleteSite(available_site)
+ ic.DeleteProposal(available_proposal_1)
+ ic.DeleteProposal(available_proposal_2)
+
+ os.Exit(ret)
+}
+
+// Error handler
+func handleError(t *testing.T, err error, name string, expectedErr bool, errorCode int) {
+ if (err != nil) {
+ if (expectedErr) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ if(errorCode != err.(*openwrt.OpenwrtError).Code) {
+ t.Errorf("Test case '%s': expected '%d', but got '%d'", name, errorCode, err.(*openwrt.OpenwrtError).Code)
+ } else {
+ fmt.Printf("%s\n", err.(*openwrt.OpenwrtError).Message)
+ }
+ default:
+ t.Errorf("Test case '%s': expected openwrt.OpenwrtError, but got '%s'", name, reflect.TypeOf(err).String())
+ }
+ } else {
+ t.Errorf("Test case '%s': expected success, but got '%s'", name, reflect.TypeOf(err).String())
+ }
+ } else {
+ if (expectedErr) {
+ t.Errorf("Test case '%s': expected error code '%d', but success", name, errorCode)
+ }
+ }
+}
+
+func printError(err error) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("%s\n", err.(*openwrt.OpenwrtError).Message)
+ default:
+ fmt.Printf("%s\n", reflect.TypeOf(err).String())
+ }
+}
+
+// IpSec Site API Test
+func TestGetSites(t *testing.T) {
+ res, err := ic.GetSites()
+ if res == nil {
+ printError(err)
+ t.Errorf("Test case GetSites: can not get IpSec sites")
+ return
+ }
+
+ if len(res.Sites) == 0 {
+ fmt.Printf("Test case GetSites: no site found")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetSite(t *testing.T) {
+ tcases := []struct {
+ name string
+ site string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetAvailableSite",
+ site: available_site,
+ },
+ {
+ name: "GetFoolSite",
+ site: "foo_site",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := ic.GetSite(tcase.site)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolSite(t *testing.T) {
+ tcases := []struct {
+ name string
+ site openwrt.SdewanIpsecSite
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ site: openwrt.SdewanIpsecSite{Name:" "},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidConnectionType",
+ site: openwrt.SdewanIpsecSite{Name:"test_site",
+ Connections:[]openwrt.SdewanIpsecConnection{{Name:"test_connection_name", Type:"fool_type",},},},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidCryptoProtocolInConnection",
+ site: openwrt.SdewanIpsecSite{Name:"test_site",
+ Connections:[]openwrt.SdewanIpsecConnection{{Name:"test_connection_name", Type:"tunnel", CryptoProposal:[]string{available_proposal_1, "fool_protocol"}},},},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "InvalidCryptoProtocolInSite",
+ site: openwrt.SdewanIpsecSite{Name:"test_site", CryptoProposal:[]string{available_proposal_1, "fool_protocol"},},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := ic.CreateSite(tcase.site)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolSite(t *testing.T) {
+ tcases := []struct {
+ name string
+ site string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ site: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ site: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := ic.DeleteSite(tcase.site)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestUpdateFoolSite(t *testing.T) {
+ tcases := []struct {
+ name string
+ site openwrt.SdewanIpsecSite
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ site: openwrt.SdewanIpsecSite{Name:"fool_name"},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := ic.UpdateSite(tcase.site)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestSite(t *testing.T) {
+ var err error
+ var ret_site *openwrt.SdewanIpsecSite
+
+ var site_name = "test_site"
+ var conn_name1 = "test_name1"
+ var conn_name2 = "test_name2"
+ var conn_name3 = "test_name3"
+ var site = openwrt.SdewanIpsecSite{
+ Name:site_name,
+ Gateway:"10.0.1.2",
+ PreSharedKey:"test_key",
+ AuthenticationMethod:"psk",
+ LocalIdentifier:"C=CH, O=strongSwan, CN=peer",
+ RemoteIdentifier:"C=CH, O=strongSwan, CN=peerB",
+ ForceCryptoProposal:"true",
+ CryptoProposal:[]string{available_proposal_1},
+ Connections:[]openwrt.SdewanIpsecConnection{{
+ Name:conn_name1,
+ Type:"tunnel",
+ Mode:"start",
+ LocalSubnet:"192.168.1.1/24",
+ LocalSourceip:"10.0.1.1",
+ RemoteSubnet:"192.168.0.1/24",
+ RemoteSourceip:"10.0.1.2",
+ CryptoProposal:[]string{available_proposal_1, available_proposal_2},
+ },
+ },
+ }
+
+ var update_site = openwrt.SdewanIpsecSite{
+ Name:site_name,
+ Gateway:"10.0.21.2",
+ PreSharedKey:"test_key_2",
+ AuthenticationMethod:"psk",
+ LocalIdentifier:"C=CH, O=strongSwan, CN=peer",
+ RemoteIdentifier:"C=CH, O=strongSwan, CN=peerB",
+ ForceCryptoProposal:"true",
+ CryptoProposal:[]string{available_proposal_1, available_proposal_2},
+ Connections:[]openwrt.SdewanIpsecConnection{{
+ Name:conn_name1,
+ Type:"tunnel",
+ Mode:"start",
+ LocalSubnet:"192.168.21.1/24",
+ LocalSourceip:"10.0.1.1",
+ RemoteSubnet:"192.168.0.1/24",
+ RemoteSourceip:"10.0.21.2",
+ CryptoProposal:[]string{available_proposal_2},
+ },
+ {
+ Name:conn_name2,
+ Type:"transport",
+ Mode:"start",
+ LocalSubnet:"192.168.31.1/24",
+ LocalSourceip:"10.0.11.1",
+ RemoteSubnet:"192.168.10.1/24",
+ RemoteSourceip:"10.0.31.2",
+ CryptoProposal:[]string{available_proposal_1, available_proposal_2},
+ },
+ {
+ Name:conn_name3,
+ Type:"tunnel",
+ Mode:"start",
+ LocalSubnet:"192.168.41.1/24",
+ LocalSourceip:"10.0.11.1",
+ RemoteSubnet:"192.168.10.1/24",
+ RemoteSourceip:"10.0.41.2",
+ CryptoProposal:[]string{available_proposal_1},
+ },
+ },
+ }
+
+ _, err = ic.GetSite(site_name)
+ if (err == nil) {
+ err = ic.DeleteSite(site_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestSite: failed to delete site '%s'", site_name)
+ return
+ }
+ }
+
+ // Create site
+ ret_site, err = ic.CreateSite(site)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestSite: failed to create site '%s'", site_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_site)
+ fmt.Printf("Created Site: %s\n", string(p_data))
+ }
+
+ ret_site, err = ic.GetSite(site_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestSite: failed to get created site")
+ return
+ } else {
+ if( len(ret_site.Connections) != 1 || ret_site.Connections[0].LocalSubnet != "192.168.1.1/24" ) {
+ t.Errorf("Test case TestSite: failed to create site")
+ return
+ }
+ }
+
+ // Update site
+ ret_site, err = ic.UpdateSite(update_site)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestSite: failed to update site '%s'", site_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_site)
+ fmt.Printf("Updated Site: %s\n", string(p_data))
+ }
+
+ ret_site, err = ic.GetSite(site_name)
+ if (err != nil) {
+ t.Errorf("Test case TestSite: failed to get updated site")
+ return
+ } else {
+ if( len(ret_site.Connections) != 3 || ret_site.Connections[0].LocalSubnet != "192.168.21.1/24" ) {
+ t.Errorf("Test case TestSite: failed to update site")
+ return
+ }
+ }
+
+ // Delete site
+ err = ic.DeleteSite(site_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestSite: failed to delete site '%s'", site_name)
+ return
+ }
+
+ ret_site, err = ic.GetSite(site_name)
+ if (err == nil) {
+ t.Errorf("Test case TestSite: failed to delete site")
+ return
+ }
+}
+
+// IpSec Proposal API Test
+func TestGetProposals(t *testing.T) {
+ res, err := ic.GetProposals()
+ if res == nil {
+ printError(err)
+ t.Errorf("Test case TestGetProposals: can not get IpSec proposals")
+ return
+ }
+
+ if len(res.Proposals) == 0 {
+ fmt.Printf("Test case TestGetProposals: no proposal found")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetProposal(t *testing.T) {
+ tcases := []struct {
+ name string
+ proposal string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetAvailableProposal",
+ proposal: available_proposal_1,
+ },
+ {
+ name: "GetFoolProposal",
+ proposal: "foo_proposal",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := ic.GetProposal(tcase.proposal)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolProposal(t *testing.T) {
+ tcases := []struct {
+ name string
+ proposal openwrt.SdewanIpsecProposal
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ proposal: openwrt.SdewanIpsecProposal{Name:" "},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := ic.CreateProposal(tcase.proposal)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolProposal(t *testing.T) {
+ tcases := []struct {
+ name string
+ proposal string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ proposal: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "DeleteInUsedProposal",
+ proposal: available_proposal_1,
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ proposal: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := ic.DeleteProposal(tcase.proposal)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestUpdateFoolProposal(t *testing.T) {
+ tcases := []struct {
+ name string
+ proposal openwrt.SdewanIpsecProposal
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ proposal: openwrt.SdewanIpsecProposal{Name:"fool_name"},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := ic.UpdateProposal(tcase.proposal)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestProposal(t *testing.T) {
+ var err error
+ var ret_proposal *openwrt.SdewanIpsecProposal
+
+ var proposal_name = "test_proposal"
+ var proposal = openwrt.SdewanIpsecProposal{Name:proposal_name,EncryptionAlgorithm:"aes128", HashAlgorithm:"sha256", DhGroup:"modp3072",}
+ var update_proposal = openwrt.SdewanIpsecProposal{Name:proposal_name,EncryptionAlgorithm:"aes256", HashAlgorithm:"sha128", DhGroup:"modp3072",}
+
+ _, err = ic.GetProposal(proposal_name)
+ if (err == nil) {
+ err = ic.DeleteProposal(proposal_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestProposal: failed to delete proposal '%s'", proposal_name)
+ return
+ }
+ }
+
+ // Create proposal
+ ret_proposal, err = ic.CreateProposal(proposal)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestProposal: failed to create proposal '%s'", proposal_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_proposal)
+ fmt.Printf("Created Proposal: %s\n", string(p_data))
+ }
+
+ ret_proposal, err = ic.GetProposal(proposal_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestProposal: failed to get created proposal")
+ return
+ } else {
+ if( ret_proposal.EncryptionAlgorithm != "aes128" ) {
+ t.Errorf("Test case TestProposal: failed to create proposal")
+ return
+ }
+ }
+
+ // Update proposal
+ ret_proposal, err = ic.UpdateProposal(update_proposal)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestProposal: failed to update proposal '%s'", proposal_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_proposal)
+ fmt.Printf("Updated Proposal: %s\n", string(p_data))
+ }
+
+ ret_proposal, err = ic.GetProposal(proposal_name)
+ if (err != nil) {
+ t.Errorf("Test case TestProposal: failed to get updated proposal")
+ return
+ } else {
+ if( ret_proposal.EncryptionAlgorithm != "aes256" ) {
+ t.Errorf("Test case TestProposal: failed to update proposal")
+ return
+ }
+ }
+
+ // Delete proposal
+ err = ic.DeleteProposal(proposal_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestProposal: failed to delete proposal '%s'", proposal_name)
+ return
+ }
+
+ ret_proposal, err = ic.GetProposal(proposal_name)
+ if (err == nil) {
+ t.Errorf("Test case TestProposal: failed to delete proposal")
+ return
+ }
+}
--- /dev/null
+package openwrt
+
+import (
+ "openwrt"
+ "reflect"
+ "testing"
+ "flag"
+ "encoding/json"
+ "fmt"
+ "os"
+)
+
+var mwan3 openwrt.Mwan3Client
+var service openwrt.ServiceClient
+var available_policy string
+var available_interface string
+var available_interfaceb string
+
+func TestMain(m *testing.M) {
+ servIp := flag.String("ip", "10.244.0.18", "SDEWAN CNF Management IP Address")
+ flag.Parse()
+
+ client := openwrt.NewOpenwrtClient(*servIp, "root", "")
+ mwan3 = openwrt.Mwan3Client{client}
+ service = openwrt.ServiceClient{client}
+ available_policy = "balanced"
+ available_interface = "wan"
+ available_interfaceb = "wanb"
+
+ os.Exit(m.Run())
+}
+
+// Error handler
+func handleError(t *testing.T, err error, name string, expectedErr bool, errorCode int) {
+ if (err != nil) {
+ if (expectedErr) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ if(errorCode != err.(*openwrt.OpenwrtError).Code) {
+ t.Errorf("Test case '%s': expected '%d', but got '%d'", name, errorCode, err.(*openwrt.OpenwrtError).Code)
+ } else {
+ fmt.Printf("%s\n", err.(*openwrt.OpenwrtError).Message)
+ }
+ default:
+ t.Errorf("Test case '%s': expected openwrt.OpenwrtError, but got '%s'", name, reflect.TypeOf(err).String())
+ }
+ } else {
+ t.Errorf("Test case '%s': expected success, but got '%s'", name, reflect.TypeOf(err).String())
+ }
+ } else {
+ if (expectedErr) {
+ t.Errorf("Test case '%s': expected error code '%d', but success", name, errorCode)
+ }
+ }
+}
+
+func printError(err error) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("%s\n", err.(*openwrt.OpenwrtError).Message)
+ default:
+ fmt.Printf("%s\n", reflect.TypeOf(err).String())
+ }
+}
+
+// Service API Test
+func TestGetServices(t *testing.T) {
+ res, _ := service.GetAvailableServices()
+
+ if res == nil {
+ t.Errorf("Test case GetServices: no available services")
+ return
+ }
+
+ if !reflect.DeepEqual(res.Services, []string{"mwan3", "firewall", "ipsec"}) {
+ t.Errorf("Test case GetServices: error available services returned")
+ }
+}
+
+func TestExecuteService(t *testing.T) {
+ tcases := []struct {
+ name string
+ service string
+ action string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "Foo_Service",
+ service: "foo_service",
+ action: "restart",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Foo_action",
+ service: "mwan3",
+ action: "foo_action",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+ for _, tcase := range tcases {
+ _, err := service.ExecuteService(tcase.service, tcase.action)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+// MWAN3 Rule API Test
+func TestGetInterfaceStatus(t *testing.T) {
+ res, _ := mwan3.GetInterfaceStatus()
+ if res == nil {
+ t.Errorf("Test case GetInterfaceStatus: can not get interfaces status")
+ }
+}
+
+func TestGetRules(t *testing.T) {
+ res, _ := mwan3.GetRules()
+ if res == nil {
+ t.Errorf("Test case GetRules: can not get mwan3 rules")
+ return
+ }
+
+ if len(res.Rules) == 0 {
+ t.Errorf("Test case GetRules: no rule defined")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetAvailableRule",
+ rule: "default_rule",
+ },
+ {
+ name: "GetFoolRule",
+ rule: "foo_rule",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := mwan3.GetRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule openwrt.SdewanRule
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolPolicy",
+ rule: openwrt.SdewanRule{Name:" ", Policy: "fool_policy"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_src_ip",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, SrcIp: "10.200.200.500"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_src_port",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, SrcPort: "-1"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_dest_ip",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, DestIp: "10.200"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_dest_port",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, DestPort: "0"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_proto",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, Proto: "fool_protocol"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_family",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, Family: "fool_family"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_sticky",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, Sticky: "2"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Wrong_timeout",
+ rule: openwrt.SdewanRule{Name:" ", Policy: available_policy, Timeout: "-1"},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := mwan3.CreateRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ rule: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ rule: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := mwan3.DeleteRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestUpdateFoolRule(t *testing.T) {
+ tcases := []struct {
+ name string
+ rule openwrt.SdewanRule
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ rule: openwrt.SdewanRule{Name:"fool_name", Policy: available_policy},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := mwan3.UpdateRule(tcase.rule)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestRule(t *testing.T) {
+ var err error
+ var ret_rule *openwrt.SdewanRule
+
+ var rule_name = "test_rule"
+ var rule = openwrt.SdewanRule{Name:rule_name, Policy: available_policy, SrcIp: "10.10.10.10/24", SrcPort: "80"}
+ var update_rule = openwrt.SdewanRule{Name:rule_name, Policy: available_policy, SrcIp: "100.100.100.100/24", SrcPort: "8000", DestIp: "172.172.172.172/16", DestPort: "8080"}
+
+ _, err = mwan3.GetRule(rule_name)
+ if (err == nil) {
+ err = mwan3.DeleteRule(rule_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to delete rule '%s'", rule_name)
+ return
+ }
+ }
+
+ // Create rule
+ ret_rule, err = mwan3.CreateRule(rule)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to create rule '%s'", rule_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_rule)
+ fmt.Printf("Created Rule: %s\n", string(p_data))
+ }
+
+ ret_rule, err = mwan3.GetRule(rule_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to get created rule")
+ return
+ } else {
+ if( ret_rule.SrcPort != "80" ) {
+ t.Errorf("Test case TestRule: failed to create rule")
+ return
+ }
+ }
+
+ // Update rule
+ ret_rule, err = mwan3.UpdateRule(update_rule)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to update rule '%s'", rule_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_rule)
+ fmt.Printf("Updated Rule: %s\n", string(p_data))
+ }
+
+ ret_rule, err = mwan3.GetRule(rule_name)
+ if (err != nil) {
+ t.Errorf("Test case TestRule: failed to get updated rule")
+ return
+ } else {
+ if( ret_rule.SrcIp != "100.100.100.100/24" ) {
+ t.Errorf("Test case TestRule: failed to update rule")
+ return
+ }
+ }
+
+ // Delete rule
+ err = mwan3.DeleteRule(rule_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestRule: failed to delete rule '%s'", rule_name)
+ return
+ }
+
+ ret_rule, err = mwan3.GetRule(rule_name)
+ if (err == nil) {
+ t.Errorf("Test case TestRule: failed to delete rule")
+ return
+ }
+}
+
+// MWAN3 Policy API Tests
+func TestGetPolicies(t *testing.T) {
+ res, _ := mwan3.GetPolicies()
+ if res == nil {
+ t.Errorf("Test case GetPolicies: can not get mwan3 policies")
+ return
+ }
+
+ if len(res.Policies) == 0 {
+ t.Errorf("Test case GetPolicies: no policy defined")
+ return
+ }
+
+ p_data, _ := json.Marshal(res)
+ fmt.Printf("%s\n", string(p_data))
+}
+
+func TestGetPolicy(t *testing.T) {
+ tcases := []struct {
+ name string
+ policy string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "GetAvailablePolicy",
+ policy: available_policy,
+ },
+ {
+ name: "GetFoolPolicy",
+ policy: "foo_policy",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := mwan3.GetPolicy(tcase.policy)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestCreateFoolPolicy(t *testing.T) {
+ tcases := []struct {
+ name string
+ policy openwrt.SdewanPolicy
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ policy: openwrt.SdewanPolicy{Name:" ", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"1", Weight:"2"}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "EmptyMember",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "Policy-Conflict",
+ policy: openwrt.SdewanPolicy{Name:"wan_only", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"1", Weight:"2"}}},
+ expectedErr: true,
+ expectedErrCode: 409,
+ },
+ {
+ name: "WrongMember-interface",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{{Interface:""}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "WrongMember-interface-name",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{{Interface:"fool-name", Metric:"1", Weight:"2"}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "WrongMember-duplicate-interface",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"1", Weight:"2"}, {Interface:available_interface, Metric:"2", Weight:"3"}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "WrongMember-Metric",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:""}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "WrongMember-Weight",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"2"}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "WrongMember-Metric",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"-1", Weight:"2"}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "WrongMember-Weight",
+ policy: openwrt.SdewanPolicy{Name:"policy1", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"2", Weight:"0"}}},
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := mwan3.CreatePolicy(tcase.policy)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestDeleteFoolPolicy(t *testing.T) {
+ tcases := []struct {
+ name string
+ policy string
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "EmptyName",
+ policy: "",
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ {
+ name: "FoolName",
+ policy: "fool_name",
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ {
+ name: "Delete-InUsePolicy",
+ policy: available_policy,
+ expectedErr: true,
+ expectedErrCode: 400,
+ },
+ }
+
+ for _, tcase := range tcases {
+ err := mwan3.DeletePolicy(tcase.policy)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestUpdateFoolPolicy(t *testing.T) {
+ tcases := []struct {
+ name string
+ policy openwrt.SdewanPolicy
+ expectedErr bool
+ expectedErrCode int
+ }{
+ {
+ name: "FoolName",
+ policy: openwrt.SdewanPolicy{Name:"fool_name", Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"1", Weight:"2"}}},
+ expectedErr: true,
+ expectedErrCode: 404,
+ },
+ }
+
+ for _, tcase := range tcases {
+ _, err := mwan3.UpdatePolicy(tcase.policy)
+ handleError(t, err, tcase.name, tcase.expectedErr, tcase.expectedErrCode)
+ }
+}
+
+func TestPolicy(t *testing.T) {
+ var err error
+ var ret_policy *openwrt.SdewanPolicy
+
+ var policy_name = "test_policy"
+ var policy = openwrt.SdewanPolicy{Name:policy_name, Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"1", Weight:"2"}}}
+ var update_policy = openwrt.SdewanPolicy{Name:policy_name, Members:[]openwrt.SdewanMember{{Interface:available_interface, Metric:"2", Weight:"3"}, {Interface:available_interfaceb, Metric:"2", Weight:"3"}}}
+
+ _, err = mwan3.GetPolicy(policy_name)
+ if (err == nil) {
+ err = mwan3.DeletePolicy(policy_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestPolicy: failed to delete policy '%s'", policy_name)
+ return
+ }
+ }
+
+ // Create policy
+ ret_policy, err = mwan3.CreatePolicy(policy)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestPolicy: failed to create policy '%s'", policy_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_policy)
+ fmt.Printf("Created Policy: %s\n", string(p_data))
+ }
+
+ ret_policy, err = mwan3.GetPolicy(policy_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestPolicy: failed to get created policy")
+ return
+ } else {
+ if( len(ret_policy.Members) != 1 || ret_policy.Members[0].Metric != "1" ) {
+ t.Errorf("Test case TestPolicy: failed to create policy")
+ return
+ }
+ }
+
+ // Update policy
+ ret_policy, err = mwan3.UpdatePolicy(update_policy)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestPolicy: failed to update policy '%s'", policy_name)
+ return
+ } else {
+ p_data, _ := json.Marshal(ret_policy)
+ fmt.Printf("Updated Policy: %s\n", string(p_data))
+ }
+
+ ret_policy, err = mwan3.GetPolicy(policy_name)
+ if (err != nil) {
+ t.Errorf("Test case TestPolicy: failed to get updated policy")
+ return
+ } else {
+ if( len(ret_policy.Members) != 2 || ret_policy.Members[1].Metric != "2" ) {
+ t.Errorf("Test case TestPolicy: failed to update policy")
+ return
+ }
+ }
+
+ // Delete policy
+ err = mwan3.DeletePolicy(policy_name)
+ if (err != nil) {
+ printError(err)
+ t.Errorf("Test case TestPolicy: failed to delete policy '%s'", policy_name)
+ return
+ }
+
+ ret_policy, err = mwan3.GetPolicy(policy_name)
+ if (err == nil) {
+ t.Errorf("Test case TestPolicy: failed to delete policy")
+ return
+ }
+}
--- /dev/null
+package main
+
+import (
+ "openwrt"
+ "fmt"
+ "encoding/json"
+ "strconv"
+)
+
+func testGetServices(serv *openwrt.ServiceClient) {
+ res, err := serv.GetAvailableServices()
+ if(err != nil) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("Openwrt Error: %s\n", err.Error())
+ fmt.Printf("Openwrt Error: %d\n", err.(*openwrt.OpenwrtError).Code)
+ default:
+ fmt.Printf("%s\n", err.Error())
+ }
+ } else {
+ servs, err := json.Marshal(res)
+ if(err != nil) {
+ fmt.Printf("%s\n", err.Error())
+ } else {
+ fmt.Printf("%s\n", string(servs))
+ }
+ }
+}
+
+func testPutService(serv *openwrt.ServiceClient) {
+ res, err := serv.ExecuteService("mwan3", "start")
+ if(err != nil) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("Openwrt Error: %s\n", err.Error())
+ fmt.Printf("Openwrt Error: %d\n", err.(*openwrt.OpenwrtError).Code)
+ default:
+ fmt.Printf("%s\n", err.Error())
+ }
+ } else {
+ fmt.Printf("Result: %s\n", strconv.FormatBool(res))
+ }
+}
+
+func testGetInterfaceStatus(mwan3 *openwrt.Mwan3Client) {
+ is, err := mwan3.GetInterfaceStatus()
+ if(err != nil) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("Openwrt Error: %s\n", err.Error())
+ fmt.Printf("Openwrt Error: %d\n", err.(*openwrt.OpenwrtError).Code)
+ default:
+ fmt.Printf("%s\n", err.Error())
+ }
+ } else {
+ fmt.Printf("Wan interface status: %s\n", is.Interfaces["wan"].Status)
+ is_data, err := json.Marshal(is)
+ if(err != nil) {
+ fmt.Printf("%s\n", err.Error())
+ } else {
+ fmt.Printf("%s\n", string(is_data))
+ }
+ }
+
+ is = nil
+}
+
+func testGetPolicies(mwan3 *openwrt.Mwan3Client) {
+ res, err := mwan3.GetPolicies()
+ if(err != nil) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("Openwrt Error: %s\n", err.Error())
+ fmt.Printf("Openwrt Error: %d\n", err.(*openwrt.OpenwrtError).Code)
+ default:
+ fmt.Printf("%s\n", err.Error())
+ }
+ } else {
+ p_data, err := json.Marshal(res)
+ if(err != nil) {
+ fmt.Printf("%s\n", err.Error())
+ } else {
+ fmt.Printf("%s\n", string(p_data))
+ }
+ }
+}
+
+func testGetPolicy(mwan3 *openwrt.Mwan3Client, policy string) {
+ res, err := mwan3.GetPolicy(policy)
+ if(err != nil) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("Openwrt Error: %s\n", err.Error())
+ fmt.Printf("Openwrt Error: %d\n", err.(*openwrt.OpenwrtError).Code)
+ default:
+ fmt.Printf("%s\n", err.Error())
+ }
+ } else {
+ p_data, err := json.Marshal(res)
+ if(err != nil) {
+ fmt.Printf("%s\n", err.Error())
+ } else {
+ fmt.Printf("%s\n", string(p_data))
+ }
+ }
+}
+
+func testGetRules(mwan3 *openwrt.Mwan3Client) {
+ res, err := mwan3.GetRules()
+ if(err != nil) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("Openwrt Error: %s\n", err.Error())
+ fmt.Printf("Openwrt Error: %d\n", err.(*openwrt.OpenwrtError).Code)
+ default:
+ fmt.Printf("%s\n", err.Error())
+ }
+ } else {
+ p_data, err := json.Marshal(res)
+ if(err != nil) {
+ fmt.Printf("%s\n", err.Error())
+ } else {
+ fmt.Printf("%s\n", string(p_data))
+ }
+ }
+}
+
+func testGetRule(mwan3 *openwrt.Mwan3Client, rule string) {
+ res, err := mwan3.GetRule(rule)
+ if(err != nil) {
+ switch err.(type) {
+ case *openwrt.OpenwrtError:
+ fmt.Printf("Openwrt Error: %s\n", err.Error())
+ fmt.Printf("Openwrt Error: %d\n", err.(*openwrt.OpenwrtError).Code)
+ default:
+ fmt.Printf("%s\n", err.Error())
+ }
+ } else {
+ p_data, err := json.Marshal(res)
+ if(err != nil) {
+ fmt.Printf("%s\n", err.Error())
+ } else {
+ fmt.Printf("%s\n", string(p_data))
+ }
+ }
+}
+
+func main() {
+ client := openwrt.NewOpenwrtClient("10.244.0.18", "root", "")
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: client}
+ service := openwrt.ServiceClient{client}
+
+ testGetServices(&service)
+
+ testGetInterfaceStatus(&mwan3)
+ testGetPolicies(&mwan3)
+ testGetPolicy(&mwan3, "balanced")
+ testGetPolicy(&mwan3, "foo_policy")
+
+ testGetRules(&mwan3)
+ testGetRule(&mwan3, "https")
+ testGetRule(&mwan3, "foo_rule")
+}
--- /dev/null
+package openwrt
+
+import (
+ "encoding/json"
+)
+
+const (
+ firewallBaseURL = "sdewan/firewall/v1/"
+)
+
+type FirewallClient struct {
+ OpenwrtClient *openwrtClient
+}
+
+// Firewall Zones
+type SdewanFirewallZone struct {
+ Name string `json:"name"`
+ Network []string `json:"network"`
+ Masq string `json:"masq"`
+ MasqSrc []string `json:"masq_src"`
+ MasqDest []string `json:"masq_dest"`
+ MasqAllowInvalid string `json:"masq_allow_invalid"`
+ MtuFix string `json:"mtu_fix"`
+ Input string `json:"input"`
+ Forward string `json:"forward"`
+ Output string `json:"output"`
+ Family string `json:"family"`
+ Subnet []string `json:"subnet"`
+ ExtraSrc string `json:"extra_src"`
+ ExtraDest string `json:"etra_dest"`
+}
+
+type SdewanFirewallZones struct {
+ Zones []SdewanFirewallZone `json:"zones"`
+}
+
+// Firewall Forwarding
+type SdewanFirewallForwarding struct {
+ Name string `json:"name"`
+ Src string `json:"src"`
+ Dest string `json:"dest"`
+ Family string `json:"family"`
+}
+
+type SdewanFirewallForwardings struct {
+ Forwardings []SdewanFirewallForwarding `json:"forwardings"`
+}
+
+// Firewall Rule
+type SdewanFirewallRule struct {
+ Name string `json:"name"`
+ Src string `json:"src"`
+ SrcIp string `json:"src_ip"`
+ SrcMac string `json:"src_mac"`
+ SrcPort string `json:"src_port"`
+ Proto string `json:"proto"`
+ IcmpType []string `json:"icmp_type"`
+ Dest string `json:"dest"`
+ DestIp string `json:"dest_ip"`
+ DestPort string `json:"dest_port"`
+ Mark string `json:"mark"`
+ Target string `json:"target"`
+ SetMark string `json:"set_mark"`
+ SetXmark string `json:"set_xmark"`
+ Family string `json:"family"`
+ Extra string `json:"extra"`
+}
+
+type SdewanFirewallRules struct {
+ Rules []SdewanFirewallRule `json:"rules"`
+}
+
+// Firewall Redirect
+type SdewanFirewallRedirect struct {
+ Name string `json:"name"`
+ Src string `json:"src"`
+ SrcIp string `json:"src_ip"`
+ SrcDIp string `json:"src_dip"`
+ SrcMac string `json:"src_mac"`
+ SrcPort string `json:"src_port"`
+ SrcDPort string `json:"src_dport"`
+ Proto string `json:"proto"`
+ Dest string `json:"dest"`
+ DestIp string `json:"dest_ip"`
+ DestPort string `json:"dest_port"`
+ Mark string `json:"mark"`
+ Target string `json:"target"`
+ Family string `json:"family"`
+}
+
+type SdewanFirewallRedirects struct {
+ Redirects []SdewanFirewallRedirect `json:"redirects"`
+}
+
+// Zone APIs
+// get zones
+func (f *FirewallClient) GetZones() (*SdewanFirewallZones, error) {
+ var response string
+ var err error
+ response, err = f.OpenwrtClient.Get(firewallBaseURL + "zones")
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallZones SdewanFirewallZones
+ err = json.Unmarshal([]byte(response), &sdewanFirewallZones)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallZones, nil
+}
+
+// get zone
+func (m *FirewallClient) GetZone(zone string) (*SdewanFirewallZone, error) {
+ var response string
+ var err error
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "zone/" + zone)
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallZone SdewanFirewallZone
+ err = json.Unmarshal([]byte(response), &sdewanFirewallZone)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallZone, nil
+}
+
+// create zone
+func (m *FirewallClient) CreateZone(zone SdewanFirewallZone) (*SdewanFirewallZone, error) {
+ var response string
+ var err error
+ zone_obj, _ := json.Marshal(zone)
+ response, err = m.OpenwrtClient.Post(firewallBaseURL + "zone", string(zone_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallZone SdewanFirewallZone
+ err = json.Unmarshal([]byte(response), &sdewanFirewallZone)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallZone, nil
+}
+
+// delete zone
+func (m *FirewallClient) DeleteZone(zone_name string) (error) {
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "zone/" + zone_name)
+ if (err != nil) {
+ return err
+ }
+
+ return nil
+}
+
+// update zone
+func (m *FirewallClient) UpdateZone(zone SdewanFirewallZone) (*SdewanFirewallZone, error) {
+ var response string
+ var err error
+ zone_obj, _ := json.Marshal(zone)
+ zone_name := zone.Name
+ response, err = m.OpenwrtClient.Put(firewallBaseURL + "zone/" + zone_name, string(zone_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallZone SdewanFirewallZone
+ err = json.Unmarshal([]byte(response), &sdewanFirewallZone)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallZone, nil
+}
+
+// Rule APIs
+// get rules
+func (f *FirewallClient) GetRules() (*SdewanFirewallRules, error) {
+ var response string
+ var err error
+ response, err = f.OpenwrtClient.Get(firewallBaseURL + "rules")
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRules SdewanFirewallRules
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRules)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRules, nil
+}
+
+// get rule
+func (m *FirewallClient) GetRule(rule string) (*SdewanFirewallRule, error) {
+ var response string
+ var err error
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "rule/" + rule)
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRule SdewanFirewallRule
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRule)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRule, nil
+}
+
+// create rule
+func (m *FirewallClient) CreateRule(rule SdewanFirewallRule) (*SdewanFirewallRule, error) {
+ var response string
+ var err error
+ rule_obj, _ := json.Marshal(rule)
+ response, err = m.OpenwrtClient.Post(firewallBaseURL + "rule", string(rule_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRule SdewanFirewallRule
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRule)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRule, nil
+}
+
+// delete rule
+func (m *FirewallClient) DeleteRule(rule_name string) (error) {
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "rule/" + rule_name)
+ if (err != nil) {
+ return err
+ }
+
+ return nil
+}
+
+// update rule
+func (m *FirewallClient) UpdateRule(rule SdewanFirewallRule) (*SdewanFirewallRule, error) {
+ var response string
+ var err error
+ rule_obj, _ := json.Marshal(rule)
+ rule_name := rule.Name
+ response, err = m.OpenwrtClient.Put(firewallBaseURL + "rule/" + rule_name, string(rule_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRule SdewanFirewallRule
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRule)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRule, nil
+}
+
+// Forwarding APIs
+// get forwardings
+func (f *FirewallClient) GetForwardings() (*SdewanFirewallForwardings, error) {
+ var response string
+ var err error
+ response, err = f.OpenwrtClient.Get(firewallBaseURL + "forwardings")
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallForwardings SdewanFirewallForwardings
+ err = json.Unmarshal([]byte(response), &sdewanFirewallForwardings)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallForwardings, nil
+}
+
+// get forwarding
+func (m *FirewallClient) GetForwarding(forwarding string) (*SdewanFirewallForwarding, error) {
+ var response string
+ var err error
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "forwarding/" + forwarding)
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallForwarding SdewanFirewallForwarding
+ err = json.Unmarshal([]byte(response), &sdewanFirewallForwarding)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallForwarding, nil
+}
+
+// create forwarding
+func (m *FirewallClient) CreateForwarding(forwarding SdewanFirewallForwarding) (*SdewanFirewallForwarding, error) {
+ var response string
+ var err error
+ forwarding_obj, _ := json.Marshal(forwarding)
+ response, err = m.OpenwrtClient.Post(firewallBaseURL + "forwarding", string(forwarding_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallForwarding SdewanFirewallForwarding
+ err = json.Unmarshal([]byte(response), &sdewanFirewallForwarding)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallForwarding, nil
+}
+
+// delete forwarding
+func (m *FirewallClient) DeleteForwarding(forwarding_name string) (error) {
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "forwarding/" + forwarding_name)
+ if (err != nil) {
+ return err
+ }
+
+ return nil
+}
+
+// update forwarding
+func (m *FirewallClient) UpdateForwarding(forwarding SdewanFirewallForwarding) (*SdewanFirewallForwarding, error) {
+ var response string
+ var err error
+ forwarding_obj, _ := json.Marshal(forwarding)
+ forwarding_name := forwarding.Name
+ response, err = m.OpenwrtClient.Put(firewallBaseURL + "forwarding/" + forwarding_name, string(forwarding_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallForwarding SdewanFirewallForwarding
+ err = json.Unmarshal([]byte(response), &sdewanFirewallForwarding)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallForwarding, nil
+}
+
+// Redirect APIs
+// get redirects
+func (f *FirewallClient) GetRedirects() (*SdewanFirewallRedirects, error) {
+ var response string
+ var err error
+ response, err = f.OpenwrtClient.Get(firewallBaseURL + "redirects")
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRedirects SdewanFirewallRedirects
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRedirects)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRedirects, nil
+}
+
+// get redirect
+func (m *FirewallClient) GetRedirect(redirect string) (*SdewanFirewallRedirect, error) {
+ var response string
+ var err error
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "redirect/" + redirect)
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRedirect SdewanFirewallRedirect
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRedirect)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRedirect, nil
+}
+
+// create redirect
+func (m *FirewallClient) CreateRedirect(redirect SdewanFirewallRedirect) (*SdewanFirewallRedirect, error) {
+ var response string
+ var err error
+ redirect_obj, _ := json.Marshal(redirect)
+ response, err = m.OpenwrtClient.Post(firewallBaseURL + "redirect", string(redirect_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRedirect SdewanFirewallRedirect
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRedirect)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRedirect, nil
+}
+
+// delete redirect
+func (m *FirewallClient) DeleteRedirect(redirect_name string) (error) {
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "redirect/" + redirect_name)
+ if (err != nil) {
+ return err
+ }
+
+ return nil
+}
+
+// update redirect
+func (m *FirewallClient) UpdateRedirect(redirect SdewanFirewallRedirect) (*SdewanFirewallRedirect, error) {
+ var response string
+ var err error
+ redirect_obj, _ := json.Marshal(redirect)
+ redirect_name := redirect.Name
+ response, err = m.OpenwrtClient.Put(firewallBaseURL + "redirect/" + redirect_name, string(redirect_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanFirewallRedirect SdewanFirewallRedirect
+ err = json.Unmarshal([]byte(response), &sdewanFirewallRedirect)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanFirewallRedirect, nil
+}
--- /dev/null
+package openwrt
+
+import (
+ "encoding/json"
+)
+
+const (
+ ipsecBaseURL = "sdewan/ipsec/v1/"
+)
+
+type IpsecClient struct {
+ OpenwrtClient *openwrtClient
+}
+
+// Proposals
+type SdewanIpsecProposal struct {
+ Name string `json:"name"`
+ EncryptionAlgorithm string `json:"encryption_algorithm"`
+ HashAlgorithm string `json:"hash_algorithm"`
+ DhGroup string `json:"dh_group"`
+}
+
+type SdewanIpsecProposals struct {
+ Proposals []SdewanIpsecProposal `json:"proposals"`
+}
+
+// Sites
+type SdewanIpsecConnection struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Mode string `json:"mode"`
+ LocalSubnet string `json:"local_subnet"`
+ LocalNat string `json:"local_nat"`
+ LocalSourceip string `json:"local_sourceip"`
+ LocalUpdown string `json:"local_updown"`
+ LocalFirewall string `json:"local_firewall"`
+ RemoteSubnet string `json:"remote_subnet"`
+ RemoteSourceip string `json:"remote_sourceip"`
+ RemoteUpdown string `json:"remote_updown"`
+ RemoteFirewall string `json:"remote_firewall"`
+ CryptoProposal []string `json:"crypto_proposal"`
+}
+
+type SdewanIpsecSite struct {
+ Name string `json:"name"`
+ Gateway string `json:"gateway"`
+ PreSharedKey string `json:"pre_shared_key"`
+ AuthenticationMethod string `json:"authentication_method"`
+ LocalIdentifier string `json:"local_identifier"`
+ RemoteIdentifier string `json:"remote_identifier"`
+ CryptoProposal []string `json:"crypto_proposal"`
+ ForceCryptoProposal string `json:"force_crypto_proposal"`
+ LocalPublicCert string `json:"local_public_cert"`
+ LocalPrivateCert string `json:"local_private_cert"`
+ SharedCa string `json:"shared_ca"`
+ Connections []SdewanIpsecConnection `json:"connections"`
+}
+
+type SdewanIpsecSites struct {
+ Sites []SdewanIpsecSite `json:"sites"`
+}
+
+// Proposal APIs
+// get proposals
+func (f *IpsecClient) GetProposals() (*SdewanIpsecProposals, error) {
+ var response string
+ var err error
+ response, err = f.OpenwrtClient.Get(ipsecBaseURL + "proposals")
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecProposals SdewanIpsecProposals
+ err = json.Unmarshal([]byte(response), &sdewanIpsecProposals)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecProposals, nil
+}
+
+// get proposal
+func (m *IpsecClient) GetProposal(proposal string) (*SdewanIpsecProposal, error) {
+ var response string
+ var err error
+ response, err = m.OpenwrtClient.Get(ipsecBaseURL + "proposal/" + proposal)
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecProposal SdewanIpsecProposal
+ err = json.Unmarshal([]byte(response), &sdewanIpsecProposal)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecProposal, nil
+}
+
+// create proposal
+func (m *IpsecClient) CreateProposal(proposal SdewanIpsecProposal) (*SdewanIpsecProposal, error) {
+ var response string
+ var err error
+ proposal_obj, _ := json.Marshal(proposal)
+ response, err = m.OpenwrtClient.Post(ipsecBaseURL + "proposal", string(proposal_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecProposal SdewanIpsecProposal
+ err = json.Unmarshal([]byte(response), &sdewanIpsecProposal)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecProposal, nil
+}
+
+// delete proposal
+func (m *IpsecClient) DeleteProposal(proposal_name string) (error) {
+ _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "proposal/" + proposal_name)
+ if (err != nil) {
+ return err
+ }
+
+ return nil
+}
+
+// update proposal
+func (m *IpsecClient) UpdateProposal(proposal SdewanIpsecProposal) (*SdewanIpsecProposal, error) {
+ var response string
+ var err error
+ proposal_obj, _ := json.Marshal(proposal)
+ proposal_name := proposal.Name
+ response, err = m.OpenwrtClient.Put(ipsecBaseURL + "proposal/" + proposal_name, string(proposal_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecProposal SdewanIpsecProposal
+ err = json.Unmarshal([]byte(response), &sdewanIpsecProposal)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecProposal, nil
+}
+
+// Site APIs
+// get sites
+func (f *IpsecClient) GetSites() (*SdewanIpsecSites, error) {
+ var response string
+ var err error
+ response, err = f.OpenwrtClient.Get(ipsecBaseURL + "sites")
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecSites SdewanIpsecSites
+ err = json.Unmarshal([]byte(response), &sdewanIpsecSites)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecSites, nil
+}
+
+// get site
+func (m *IpsecClient) GetSite(site string) (*SdewanIpsecSite, error) {
+ var response string
+ var err error
+ response, err = m.OpenwrtClient.Get(ipsecBaseURL + "site/" + site)
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecSite SdewanIpsecSite
+ err = json.Unmarshal([]byte(response), &sdewanIpsecSite)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecSite, nil
+}
+
+// create site
+func (m *IpsecClient) CreateSite(site SdewanIpsecSite) (*SdewanIpsecSite, error) {
+ var response string
+ var err error
+ site_obj, _ := json.Marshal(site)
+ response, err = m.OpenwrtClient.Post(ipsecBaseURL + "site", string(site_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecSite SdewanIpsecSite
+ err = json.Unmarshal([]byte(response), &sdewanIpsecSite)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecSite, nil
+}
+
+// delete site
+func (m *IpsecClient) DeleteSite(site_name string) (error) {
+ _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "site/" + site_name)
+ if (err != nil) {
+ return err
+ }
+
+ return nil
+}
+
+// update site
+func (m *IpsecClient) UpdateSite(site SdewanIpsecSite) (*SdewanIpsecSite, error) {
+ var response string
+ var err error
+ site_obj, _ := json.Marshal(site)
+ site_name := site.Name
+ response, err = m.OpenwrtClient.Put(ipsecBaseURL + "site/" + site_name, string(site_obj))
+ if (err != nil) {
+ return nil, err
+ }
+
+ var sdewanIpsecSite SdewanIpsecSite
+ err = json.Unmarshal([]byte(response), &sdewanIpsecSite)
+ if (err != nil) {
+ return nil, err
+ }
+
+ return &sdewanIpsecSite, nil
+}
api admission webhook.
For each created Sdewan instance, the controller creates a pod, a configmap
-and a service for the instance. The pod runs openswrt which provides network
+and a service for the instance. The pod runs openwrt which provides network
services, i.e. sdwan, firewall, ipsec etc.
The configmap stores the network interface information and the entrypoint.sh.
1. kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.11.0/cert-manager.yaml
2. kubectl apply -f sdewan-deploy.yaml
+## Create Sdewan CNF docker image
+1. update build/set_proxy file with required proxy for docker build
+2. execute below commands to generate Sdewan CNF docker image which tagged with 'openwrt-1806-mwan3'
+```
+cd build
+sudo bash build_image.sh
+```
+
## References
- https://book.kubebuilder.io/