--- /dev/null
+--- SPDX-License-Identifier: Apache-2.0
+--- Copyright (c) 2021 Intel Corporation
+
+module("luci.controller.rest_v1.ifutil", package.seeall)
+
+NX = require("nixio")
+io = require "io"
+json = require "luci.jsonc"
+sys = require "luci.sys"
+util = require "luci.util"
+utils = require "luci.controller.rest_v1.utils"
+
+fields_table = {
+ {field="ip_address", key="inet", type="array", format=function(data) return format_ip(data) end},
+ {field="mac_address", key="link/ether"},
+ {field="ip6_address", key="inet6", format=function(data) return format_ip(data) end},
+}
+
+function index()
+end
+
+function is_interface_available(interface)
+ local f = io.open("/sys/class/net/" .. interface .. "/operstate", "r")
+ if f == nil then
+ return false
+ end
+ f:close()
+ return true
+end
+
+function format_ip(data)
+ local i, j = string.find(data, "/")
+ if i ~= nil then
+ return string.sub(data, 1, i-1)
+ end
+ return data
+end
+
+function get_field(data, key, field_type, format)
+ if type(key) == "function" then
+ return key(data)
+ end
+
+ local reg = {
+ key .. " [^%s]+[%s]",
+ }
+
+ local ret = nil
+ for index=1, #reg do
+ for item in string.gmatch(data, reg[index]) do
+ local value = nil
+ local i,j = string.find(item, key .. ": ")
+ if i ~= nil then
+ value = string.sub(item, j+1, string.len(item)-1)
+ else
+ i,j = string.find(item, key .. ":")
+ if i ~= nil then
+ value = string.sub(item, j+1, string.len(item)-1)
+ else
+ i,j = string.find(item, key .. " ")
+ if i ~= nil then
+ value = string.sub(item, j+1, string.len(item)-1)
+ end
+ end
+ end
+ if value ~= nil then
+ if format ~= nil and type(format) == "function" then
+ value = format(value)
+ end
+
+ if field_type == "array" then
+ if ret == nil then
+ ret = {value}
+ else
+ ret[#ret+1] = value
+ end
+ else
+ ret = value
+ break
+ end
+ end
+ end
+ end
+ return ret
+end
+
+function get_interface(interface)
+ local ret = {}
+ local data = util.exec("ip a show dev " .. interface)
+ if data == nil then
+ for j=1, 3 do
+ utils.log("ip command failed, retrying ... ")
+ NX.nanosleep(1)
+ data = util.exec("ip a show dev " .. interface)
+ if data ~= nil then
+ break
+ end
+ end
+ end
+ ret["name"] = interface
+ for i,v in pairs(fields_table) do
+ local value = get_field(data, v["key"], v["type"], v["format"])
+ if value ~= nil then
+ ret[v["field"]] = value
+ end
+ end
+ return ret
+end
+
+function get_interface_info()
+ local ret = {}
+ local index = 1
+ for interface in util.execi("ifconfig | awk '/^[^ \t]+/{print $1}'") do
+ if interface ~= "lo" then
+ ret[index] = get_interface(interface)
+ index = index + 1
+ end
+ end
+ return ret
+end
+
+function get_name_by_ip(ip_addr)
+ local ifs = get_interface_info()
+ for i, interface in pairs(ifs) do
+ if interface["ip_address"] ~= nil then
+ for j, ipa in pairs(interface["ip_address"]) do
+ if ipa == ip_addr then
+ return interface["name"]
+ end
+ end
+ end
+ end
+ return nil
+end
+
+function get_default_ifname()
+ local data = util.exec("ip route | grep '^default' 2>/dev/null")
+ if data ~= nil then
+ reg = "dev [^%s]+[%s]"
+ for item in string.gmatch(data, reg) do
+ local value = nil
+ local i,j = string.find(item, "dev ")
+ if i ~= nil then
+ value = string.sub(item, j+1, string.len(item)-1)
+ return value
+ end
+ end
+ end
+ return nil
+end
entry({"sdewan", "application", ver}, call("help")).dependent = false
entry({"sdewan", "route", ver}, call("help")).dependent = false
entry({"sdewan", "rule", ver}, call("help")).dependent = false
+ entry({"sdewan", "nat", ver}, call("help")).dependent = false
end
{name="remote_sourceip"},
{name="remote_updown"},
{name="remote_firewall", validator=function(value) return utils.in_array(value, {"yes", "no"}) end},
- {name="crypto_proposal", is_list=true, item_validator=function(value) return is_proposal_available(value) end, message="invalid crypto_proposal"},
+ {name="crypto_proposal", is_list=true, item_validator=function(value) return is_proposal_available(value) end, message="invalid crypto_proposal", code="428"},
{name="mark"},
}
{name="pre_shared_key"},
{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="crypto_proposal", is_list=true, item_validator=function(value) return is_proposal_available(value) end, message="invalid crypto_proposal", code="428"},
{name="force_crypto_proposal", validator=function(value) return utils.in_array(value, {"0", "1"}) end, message="invalid input for ForceCryptoProposal"},
{name="local_public_cert",
load_func=function(value) return load_cert(value["local_public_cert"]) end,
--- /dev/null
+--- SPDX-License-Identifier: Apache-2.0
+--- Copyright (c) 2021 Intel Corporation
+
+module("luci.controller.rest_v1.nat_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"
+ifutil = require "luci.controller.rest_v1.ifutil"
+
+uci_conf = "firewall-nat"
+
+nat_validator = {
+ create_section_name=false,
+ object_validator=function(value) return check_nat(value) end,
+ {name="name"},
+ {name="src", validator=function(value) return utils.in_array(value, {"#default", "#source"}) or ifutil.is_interface_available(value) end, message="invalid src", code="428"},
+ {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_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 utils.in_array(value, {"#default", "#source"}) or ifutil.is_interface_available(value) end, message="invalid dest", code="428"},
+ {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="target", validator=function(value) return utils.in_array(value, {"DNAT", "SNAT", "MASQUERADE"}) end, message="invalid target"},
+ {name="index", validator=function(value) return utils.is_integer_and_in_range(value, -1) end, message="invalid index"},
+}
+
+nat_processor = {
+ nat={create="create_nat", delete="delete_nat", validator=nat_validator},
+ configuration=uci_conf
+}
+
+function index()
+ ver = "v1"
+ configuration = "nat"
+ entry({"sdewan", configuration, ver, "nats"}, call("handle_request")).leaf = true
+end
+
+function check_nat(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 == "MASQUERADE" then
+ if value["dest"] == nil then
+ return false, "dest is required for SNAT MASQUERADE"
+ end
+ end
+
+ if target == "DNAT" then
+-- if value["src"] == nil then
+-- return false, "src is required for DNAT"
+-- end
+ if value["dest_ip"] == nil and value["dest_port"] == nil then
+ return false, "dest_ip or dest_port are required for DNAT"
+ end
+ end
+
+ return true, value
+end
+
+-- Request Handler
+function handle_request()
+ local conf = io.open("/etc/config/" .. uci_conf, "r")
+ if conf == nil then
+ conf = io.open("/etc/config/" .. uci_conf, "w")
+ end
+ conf:close()
+
+ 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, nat_processor)
+ end
+end
+
+-- generate iptables command for nat
+function nat_command(nat, op)
+ local target = nat["target"]
+ local proto = nat["proto"]
+ local src = nat["src"]
+ local src_ip = nat["src_ip"]
+ if src == "#default" then
+ src = ifutil.get_default_ifname()
+ end
+ local src_dip = nat["src_dip"]
+ local src_port = nat["src_port"]
+ local src_dport = nat["src_dport"]
+ local dest = nat["dest"]
+ local dest_ip = nat["dest_ip"]
+ if dest == "#default" then
+ dest = ifutil.get_default_ifname()
+ end
+ local dest_port = nat["dest_port"]
+ local index = nat["index"]
+ if index == nil or index == "" then
+ index = "0"
+ end
+
+ local comm = "iptables -t nat"
+ if op == "create" then
+ if index == "0" then
+ comm = comm .. " -A"
+ else
+ comm = comm .. " -I"
+ end
+ else
+ comm = comm .. " -D"
+ end
+ if target == "SNAT" or target == "MASQUERADE" then
+ comm = comm .. " POSTROUTING"
+ if index ~= "0" and op == "create" then
+ comm = comm .. " " .. index
+ end
+ if dest == "#source" then
+ dest = ifutil.get_name_by_ip(src_dip)
+ end
+ if dest ~= nil and dest ~= "" then
+ comm = comm .. " -o " .. dest
+ end
+ else
+ comm = comm .. " PREROUTING"
+ if index ~= "0" and op == "create" then
+ comm = comm .. " " .. index
+ end
+ if src ~= nil and src ~= "" then
+ comm = comm .. " -i " .. src
+ end
+ end
+
+ if proto ~= nil and proto ~= "" then
+ comm = comm .. " -p " .. proto
+ end
+ if src_ip ~= nil and src_ip ~= "" then
+ comm = comm .. " -s " .. src_ip
+ end
+ if src_port ~= nil and src_port ~= "" then
+ comm = comm .. " --sport " .. src_port
+ end
+
+ if target == "SNAT" then
+ if dest_ip ~= nil and dest_ip ~= "" then
+ comm = comm .. " -d " .. dest_ip
+ end
+ if dest_port ~= nil and dest_port ~= "" then
+ comm = comm .. " --dport " .. dest_port
+ end
+ local new_src = src_dip
+ if src_dport ~= nil and src_dport ~= "" then
+ new_src = new_src .. ":" .. src_dport
+ end
+ comm = comm .. " -j SNAT --to-source " .. new_src
+ elseif target == "DNAT" then
+ if src_dip ~= nil and src_dip ~= "" then
+ comm = comm .. " -d " .. src_dip
+ end
+ if src_dport ~= nil and src_dport ~= "" then
+ comm = comm .. " --dport " .. src_dport
+ end
+ local new_des = dest_ip
+ if new_des ~= nil and new_des ~= "" then
+ if dest_port ~= nil and dest_port ~= "" then
+ new_des = new_des .. ":" .. dest_port
+ end
+ comm = comm .. " -j DNAT --to-destination " .. new_des
+ else
+ if dest_port ~= nil and dest_port ~= "" then
+ new_des = dest_port
+ comm = comm .. " -j REDIRECT --to-port " .. new_des
+ end
+ end
+ else
+ if dest_ip ~= nil and dest_ip ~= "" then
+ comm = comm .. " -d " .. dest_ip
+ end
+ if dest_port ~= nil and dest_port ~= "" then
+ comm = comm .. " --dport " .. dest_port
+ end
+ comm = comm .. " -j MASQUERADE"
+ end
+ utils.log(comm)
+ return comm
+end
+
+-- create a nat
+function create_nat(nat)
+ local name = nat.name
+ local res, code, msg = utils.create_uci_section(uci_conf, nat_validator, "nat", nat)
+
+ if res == false then
+ uci:revert(uci_conf)
+ return res, code, msg
+ end
+
+ -- create nat rule
+ local comm = nat_command(nat, "create")
+ os.execute(comm)
+
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
+
+ return true
+end
+
+-- delete a nat
+function delete_nat(name)
+ -- check whether nat is defined
+ local nat = utils.get_object(_M, nat_processor, "nat", name)
+ if nat == nil then
+ return false, 404, "nat " .. name .. " is not defined"
+ end
+
+ -- delete nat rule in iptable
+ local comm = nat_command(nat, "delete")
+ os.execute(comm)
+
+ utils.delete_uci_section(uci_conf, nat_validator, nat, "nat")
+
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
+
+ return true
+end
---- SPDX-License-Identifier: Apache-2.0
+--- SPDX-License-Identifier: Apache-2.0
--- Copyright (c) 2021 Intel Corporation
module("luci.controller.rest_v1.route_rest", package.seeall)
io = require "io"
sys = require "luci.sys"
utils = require "luci.controller.rest_v1.utils"
+ifutil = require "luci.controller.rest_v1.ifutil"
+
+uci_conf = "route-cnf"
+
+route_validator = {
+ create_section_name=false,
+ {name="name"},
+ {name="dst", required=true, validator=function(value) return (value == "default") or utils.is_valid_ip(value) end, message="Invalid Destination IP Address"},
+ {name="src", validator=function(value) return utils.is_valid_ip_address(value) end, message="Invalid Source IP Address"},
+ {name="gw", validator=function(value) return utils.is_valid_ip_address(value) end, message="Invalid Gateway IP Address"},
+ {name="dev", required=true, validator=function(value) return (value == "#default") or ifutil.is_interface_available(value) end, message="Invalid interface", code="428"},
+ {name="table", validator=function(value) return utils.in_array(value, {"default", "cnf"}) end, message="Bad route table"},
+}
+
+route_processor = {
+ route={create="create_route", delete="delete_route", validator=route_validator},
+ configuration=uci_conf
+}
function index()
ver = "v1"
-- Request Handler
function handle_request()
- local method = utils.get_req_method()
- if method == "PUT" then
- return update_route()
- elseif method == "POST" then
- return create_route()
- elseif method == "DELETE" then
- return delete_route()
- elseif method == "GET" then
- return get_route()
- else
+ local conf = io.open("/etc/config/" .. uci_conf, "r")
+ if conf == nil then
+ conf = io.open("/etc/config/" .. uci_conf, "w")
+ end
+ conf:close()
+
+ 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, route_processor)
end
end
--- Post
-function create_route()
- local obj = utils.get_request_body_object()
- if obj == nil then
- utils.response_error(400, "No Route Data")
- return
- end
- if is_duplicated(obj.name, obj.dst) then
- utils.response_error(409, "Duplicated Route Configuration")
- return
- end
- if not utils.is_valid_ip(obj.dst) then
- utils.response_error(400, "Invalid Destination IP Address")
- return
- end
- if not utils.is_valid_ip_address(obj.gw) then
- utils.response_error(400, "Invalid gateway IP Address")
- return
+-- generate command for route
+function route_command(route, op)
+ local dst = route["dst"]
+ local src = route["src"]
+ local gw = route["gw"]
+ local dev = route["dev"]
+ local t = route["table"]
+ if dev == "#default" then
+ dev = ifutil.get_default_ifname()
end
- local iface = get_dev_name(obj.dev)
- if obj.table == "default" then
- local comm = "ip route add "..obj.dst.." via "..obj.gw.." dev "..iface
- os.execute(comm)
- elseif obj.table == "cnf" then
- local comm = "ip route add table 40 "..obj.dst.." via "..obj.gw.." dev "..iface
- os.execute(comm)
+ local comm = "ip route"
+ if op == "create" then
+ comm = comm .. " add"
else
- utils.response_error(400, "Bad route table")
- return
+ comm = comm .. " del"
end
- local file = io.open("/etc/route_cr.info", "a+")
- file:write(obj.name, " ", obj.dst, " ", obj.gw, " ", obj.dev, " ", obj.table, "\n")
- file:close()
- luci.http.prepare_content("application/json")
- luci.http.write_json(obj)
-end
--- Delete
-function delete_route()
- local uri_list = utils.get_URI_list(7)
- if uri_list == nil then
- return
+ if t == "cnf" then
+ comm = comm .. " table 40"
end
- local name = uri_list[#uri_list]
- local file = io.open("/etc/route_cr.info", "r")
- content = {}
- for line in file:lines() do
- local message = split(line, ' ')
- if name ~= message[1] then
- content[#content+1] = line
- else
- local iface = get_dev_name(message[4])
- if message[5] == "cnf" then
- local comm = "ip route del table 40 "..message[2].." via "..message[3].." dev "..iface
- os.execute(comm)
- else
- local comm = "ip route del "..message[2].." via "..message[3].." dev "..iface
- os.execute(comm)
- end
- end
+ comm = comm .. " " .. dst
+ if gw ~= nil and gw ~= "" then
+ comm = comm .. " via " .. gw
end
- file:close()
- local file = io.open("/etc/route_cr.info", "w+")
- for i = 1, #content do
- file:write(content[i])
+ comm = comm .. " dev " .. dev
+ if src ~= nil and src ~= "" then
+ comm = comm .. " src " .. src
end
- file:close()
+
+ utils.log(comm)
+ return comm
end
--- Update
-function update_route()
- local uri_list = utils.get_URI_list(7)
- if uri_list == nil then
- return
- end
- local name = uri_list[#uri_list]
- local obj = utils.get_request_body_object()
- if obj == nil then
- utils.response_error(400, "Route CR not found")
- return
- end
- if obj.name ~= name then
- utils.response_error(400, "Route CR name mismatch")
- return
- end
- if not utils.is_valid_ip(obj.dst) then
- utils.response_error(400, "Invalid Destination IP Address")
- return
- end
- if not utils.is_valid_ip_address(obj.gw) then
- utils.response_error(400, "Invalid gateway IP Address")
- return
- end
+-- create a route
+function create_route(route)
+ local name = route.name
+ local res, code, msg = utils.create_uci_section(uci_conf, route_validator, "route", route)
- local file = io.open("/etc/route_cr.info", "r")
- content = {}
- for line in file:lines() do
- local message = split(line, ' ')
- if name ~= message[1] then
- content[#content+1] = line
- else
- if obj.dst ~= message[2] or obj.table ~= message[5] then
- utils.response_error(400, "Route CR mismatch")
- file:close()
- return
- end
- local iface = get_dev_name(obj.dev)
- if obj.table == "default" then
- local comm = "ip route replace "..obj.dst.." via "..obj.gw.." dev "..iface
- os.execute(comm)
- elseif obj.table == "cnf" then
- local comm = "ip route replace table 40 "..obj.dst.." via "..obj.gw.." dev "..iface
- os.execute(comm)
- else
- utils.response_error(400, "Bad route table")
- return
- end
- content[#content+1] = obj.name.." "..obj.dst.." "..obj.gw.." "..obj.dev.." "..obj.table.."\n"
- end
- end
- file:close()
- local file = io.open("/etc/route_cr.info", "w+")
- for i = 1, #content do
- file:write(content[i])
+ if res == false then
+ uci:revert(uci_conf)
+ return res, code, msg
end
- file:close()
- luci.http.prepare_content("application/json")
- luci.http.write_json(obj)
-end
--- Get
-function get_route()
- local uri_list = utils.get_URI_list()
- local file = io.open("/etc/route_cr.info", "r")
- if #uri_list == 6 then
- local objs = {}
- objs["routes"] = {}
- for line in file:lines() do
- local message = split(line, ' ')
- local obj = {}
- obj["name"] = message[1]
- obj["dst"] = message[2]
- obj["gw"] = message[3]
- obj["dev"] = message[4]
- obj["table"] = message[5]
- table.insert(objs["routes"], obj)
- end
- luci.http.prepare_content("application/json")
- luci.http.write_json(objs)
- elseif #uri_list == 7 then
- local name = uri_list[#uri_list]
- local no = true
- for line in file:lines() do
- local message = split(line, ' ')
- if name == message[1] then
- no = false
- local obj = {}
- obj["name"] = message[1]
- obj["dst"] = message[2]
- obj["gw"] = message[3]
- obj["dev"] = message[4]
- obj["table"] = message[5]
- luci.http.prepare_content("application/json")
- luci.http.write_json(obj)
- break
- end
- end
- if no then
- utils.response_error(404, "Cannot find ".."Route CR ".."[".. name.."]" )
- end
- else
- utils.response_error(400, "Bad request URI")
- end
- file:close()
-end
+ -- create route rule
+ local comm = route_command(route, "create")
+ os.execute(comm)
+
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
--- Sync and validate
-function split(str,reps)
- local arr = {}
- string.gsub(str,'[^'..reps..']+',function(w)
- table.insert(arr, w)
- end)
- return arr
+ return true
end
-function is_duplicated(name, dst)
- local file = io.open("/etc/route_cr.info", "r")
- local judge = false
- for line in file:lines() do
- local message = split(line, ' ')
- if name == message[1] then
- judge = true
- break
- end
- if dst == message[2] then
- judge = true
- break
- end
+-- delete a route
+function delete_route(name)
+ -- check whether route is defined
+ local route = utils.get_object(_M, route_processor, "route", name)
+ if route == nil then
+ return false, 404, "route " .. name .. " is not defined"
end
- file:close()
- return judge
-end
-function get_dev_name(name)
- --TODO
- return name
-end
+ -- delete route rule
+ local comm = route_command(route, "delete")
+ os.execute(comm)
+
+ utils.delete_uci_section(uci_conf, route_validator, route, "route")
+
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
-function strict_subnet(ip)
- --TODO
return true
end
---- SPDX-License-Identifier: Apache-2.0
+--- SPDX-License-Identifier: Apache-2.0
--- Copyright (c) 2021 Intel Corporation
module("luci.controller.rest_v1.rule_rest", package.seeall)
io = require "io"
sys = require "luci.sys"
utils = require "luci.controller.rest_v1.utils"
+ifutil = require "luci.controller.rest_v1.ifutil"
+
+uci_conf = "rule-cnf"
+
+rule_validator = {
+ create_section_name=false,
+ object_validator=function(value) return check_rule(value) end,
+ {name="name"},
+ {name="src", validator=function(value) return utils.is_valid_ip(value) end, message="Invalid Source IP Address"},
+ {name="dst", validator=function(value) return utils.is_valid_ip(value) end, message="Invalid Destination IP Address"},
+ {name="prio", validator=function(value) return utils.is_integer_and_in_range(value, 0) end, message="Invalid Prioroty"},
+ {name="table", required=true, validator=function(value) return utils.in_array(value, {"main", "local", "default"}) or utils.is_integer_and_in_range(value, 0) end, message="Invalid Table"},
+ {name="fwmark", validator=function(value) return check_fwmark(value) end, message="Invalid fwmark"},
+ {name="flag",
+ load_func=function(value) if value["flag"] == "true" then return true else return false end end,
+ save_func=function(value) if value["flag"] == true then return true, "true" else return true, "false" end end},
+}
+
+rule_processor = {
+ rule={create="create_rule", delete="delete_rule", validator=rule_validator},
+ configuration=uci_conf
+}
function index()
ver = "v1"
entry({"sdewan", configuration, ver, "rules"}, call("handle_request")).leaf = true
end
+function check_rule(value)
+ local src = value["src"]
+ local dst = value["dst"]
+ if src == "" and dst == "" then
+ return false, "src or dst are required for rule"
+ end
+
+ return true, value
+end
+
-- Request Handler
function handle_request()
- local method = utils.get_req_method()
- if method == "PUT" then
- return update_rule()
- elseif method == "POST" then
- return create_rule()
- elseif method == "DELETE" then
- return delete_rule()
- elseif method == "GET" then
- return get_rule()
- else
+ local conf = io.open("/etc/config/" .. uci_conf, "r")
+ if conf == nil then
+ conf = io.open("/etc/config/" .. uci_conf, "w")
+ end
+ conf:close()
+
+ 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, rule_processor)
end
end
--- Post
-function create_rule()
- local obj = utils.get_request_body_object()
- if obj == nil then
- utils.response_error(400, "No Rule Data")
- return
- end
- if is_duplicated(obj.name, obj.src, obj.dst) then
- utils.response_error(409, "Duplicated Rule Configuration")
- return
- end
- if not is_valid_format(obj.src, obj.dst, obj.prio, obj.table, obj.fwmark) then
- utils.response_error(400, "Invalid rule format")
- return
+function check_fwmark(value)
+ local num = tonumber(value, 16)
+ if not num then
+ return false, "not a number"
+ elseif string.len(value) > 10 then
+ return false, "too large"
end
- local comm = "ip rule add "
- comm = rule_gen(comm, obj.src, obj.dst, obj.prio, obj.table, obj.fwmark, obj.flag)
- os.execute(comm)
-
- local file = io.open("/etc/rule_cr.info", "a+")
- local rule_str = input_format(obj.name, obj.src, obj.dst, obj.prio, obj.table, obj.fwmark, obj.flag)
- file:write(rule_str, "\n")
- file:close()
- luci.http.prepare_content("application/json")
- luci.http.write_json(obj)
+ return true, value
end
--- Delete
-function delete_rule()
- local uri_list = utils.get_URI_list(7)
- if uri_list == nil then
- return
- end
- local name = uri_list[#uri_list]
- local file = io.open("/etc/rule_cr.info", "r")
- local content = {}
- for line in file:lines() do
- local message = split(line, ',')
- if name ~= message[1] then
- content[#content+1] = line
- else
- local comm = "ip rule del "
- comm = rule_gen(comm, message[2], message[3], message[4], message[5], message[6], message[7])
- os.execute(comm)
- end
- end
- file:close()
- local file = io.open("/etc/rule_cr.info", "w+")
- for i = 1, #content do
- file:write(content[i], "\n")
+-- generate command for rule
+function rule_command(rule, op)
+ local src = rule["src"]
+ local dst = rule["dst"]
+ local prio = rule["prio"]
+ local t = rule["table"]
+ local fwmark = rule["fwmark"]
+ local flag = rule["flag"]
+
+ local comm = "ip rule"
+ if op == "create" then
+ comm = comm .. " add"
+ else
+ comm = comm .. " del"
end
- file:close()
-end
--- Update
-function update_rule()
- local uri_list = utils.get_URI_list(7)
- if uri_list == nil then
- return
- end
- local name = uri_list[#uri_list]
- local obj = utils.get_request_body_object()
- if obj == nil then
- utils.response_error(400, "Rule CR not found")
- return
+ if tostring(flag) == "true" then
+ comm = comm .. " not"
end
- if obj.name ~= name then
- utils.response_error(400, "Rule CR name mismatch")
- return
+ if prio ~= nil and prio ~= "" then
+ comm = comm .." prio " .. prio
end
- if not is_valid_format(obj.src, obj.dst, obj.prio, obj.table, obj.fwmark) then
- utils.response_error(400, "Invalid rule format")
- return
+ if src ~= nil and src ~= "" then
+ comm = comm .." from " .. src
end
-
- local file = io.open("/etc/rule_cr.info", "r")
- local content = {}
- local is_found = false
- for line in file:lines() do
- local message = split(line, ',')
- if name ~= message[1] then
- content[#content+1] = line
- else
- is_found = true
- local pre_comm = "ip rule del "
- pre_comm = rule_gen(pre_comm, message[2], message[3], message[4], message[5], message[6], message[7])
- os.execute(pre_comm)
- local post_comm = "ip rule add "
- post_comm = rule_gen(post_comm, obj.src, obj.dst, obj.prio, obj.table, obj.fwmark, obj.flag)
- os.execute(post_comm)
- content[#content+1] = input_format(obj.name, obj.src, obj.dst, obj.prio, obj.table, obj.fwmark, obj.flag)
- end
+ if dst ~= nil and dst ~= "" then
+ comm = comm .. " to " .. dst
end
- file:close()
- if not is_found then
- utils.response_error(404, "Cannot find ".."Rule ".."[".. name.."]".." to update." )
- return
+ if t == nil or t == "" then
+ t = "main"
end
+ comm = comm .. " lookup " .. t
- local file = io.open("/etc/rule_cr.info", "w+")
- for i = 1, #content do
- file:write(content[i], "\n")
+ if fwmark ~= nil and fwmark ~= "" then
+ comm = comm .. " fwmark " .. fwmark
end
- file:close()
- luci.http.prepare_content("application/json")
- luci.http.write_json(obj)
-end
--- Get
-function get_rule()
- local uri_list = utils.get_URI_list()
- local file = io.open("/etc/rule_cr.info", "r")
- if #uri_list == 6 then
- local objs = {}
- objs["rules"] = {}
- for line in file:lines() do
- local message = split(line, ',')
- local obj = {}
- obj["name"] = message[1]
- obj["src"] = message[2]
- obj["dst"] = message[3]
- obj["prio"] = message[4]
- obj["table"] = message[5]
- obj["fwmark"] = message[6]
- if message[7] == "false" then
- obj["flag"] = false
- else
- obj["flag"] = true
- end
- table.insert(objs["rules"], obj)
- end
- luci.http.prepare_content("application/json")
- luci.http.write_json(objs)
- elseif #uri_list == 7 then
- local name = uri_list[#uri_list]
- local no = true
- for line in file:lines() do
- local message = split(line, ',')
- if name == message[1] then
- no = false
- local obj = {}
- obj["name"] = message[1]
- obj["src"] = message[2]
- obj["dst"] = message[3]
- obj["prio"] = message[4]
- obj["table"] = message[5]
- obj["fwmark"] = message[6]
- if message[7] == "false" then
- obj["flag"] = false
- else
- obj["flag"] = true
- end
- luci.http.prepare_content("application/json")
- luci.http.write_json(obj)
- break
- end
- end
- if no then
- utils.response_error(404, "Cannot find ".."Rule CR ".."[".. name.."]" )
- end
- else
- utils.response_error(400, "Bad request URI")
- end
- file:close()
+ utils.log(comm)
+ return comm
end
--- Sync and validate
-function split(str,reps)
- local arr = {}
- string.gsub(str,'[^'..reps..']+',function(w)
- table.insert(arr, w)
- end)
- return arr
-end
+-- create a rule
+function create_rule(rule)
+ local name = rule.name
+ local res, code, msg = utils.create_uci_section(uci_conf, rule_validator, "rule", rule)
-function is_duplicated(name, src, dst)
- local file = io.open("/etc/rule_cr.info", "r")
- local judge = false
- for line in file:lines() do
- local message = split(line, ',')
- if name == message[1] then
- judge = true
- break
- end
- if src == "" then
- src = "NULL"
- end
- if dst == "" then
- dst = "NULL"
- end
- if src == message[2] and dst == message[3] then
- judge = true
- break
- end
+ if res == false then
+ uci:revert(uci_conf)
+ return res, code, msg
end
- file:close()
- return judge
-end
-function is_valid_format(src, dst, prio, table, fwmark)
- local judge = true
- if src == "" and dst == "" then
- judge = false
- elseif src == "" then
- judge = utils.is_valid_ip(dst)
- elseif dst == "" then
- judge = utils.is_valid_ip(src)
- else
- judge = utils.is_valid_ip(dst) and utils.is_valid_ip(src)
- end
+ -- create rule
+ local comm = rule_command(rule, "create")
+ os.execute(comm)
- if prio ~= "" then
- judge = judge and utils.is_integer_and_in_range(prio, 0)
- end
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
- if fwmark ~= "" then
- local num = tonumber(fwmark, 16)
- if not num then
- judge = false
- elseif string.len(fwmark) > 10 then
- judge = false
- end
- end
-
- if table == "main" or table == "local" or table == "default" or table == "" then
- return judge
- else
- table_id = get_table_id(table)
- judge = judge and utils.is_integer_and_in_range(table_id, 0)
- return judge
- end
+ return true
end
-function rule_gen(comm, src, dst, prio, table, fwmark, flag)
- if tostring(flag) == "true" then
- comm = comm.."not "
- end
- if prio ~= "" and prio ~= "NULL" then
- comm = comm.."prio "..prio.." "
- end
- if src == "" or src == "NULL" then
- comm = comm.."to "..dst.." "
- elseif dst == "" or dst == "NULL" then
- comm = comm.."from "..src.." "
- else
- comm = comm.."from "..src.." to "..dst.." "
+-- delete a rule
+function delete_rule(name)
+ -- check whether rule is defined
+ local rule = utils.get_object(_M, rule_processor, "rule", name)
+ if rule == nil then
+ return false, 404, "rule " .. name .. " is not defined"
end
- local table_id = get_table_id(table)
- comm = comm.."lookup "..table_id
- if fwmark ~= "" and fwmark ~= "NULL" then
- comm = comm.." fwmark "..fwmark
- end
- return comm
-end
-function get_table_id(table)
- --TODO
- local table_id = table
- if table == "" then
- table_id = "main"
- end
- return table_id
-end
+ -- delete rule
+ local comm = rule_command(rule, "delete")
+ os.execute(comm)
-function input_format(name, src, dst, prio, table, fwmark, flag)
- local str = name
- if src == "" then
- str = str..",".."NULL"
- else
- str = str..","..src
- end
- if dst == "" then
- str = str..",".."NULL"
- else
- str = str..","..dst
- end
- if prio == "" then
- str = str..",".."NULL"
- else
- str = str..","..prio
- end
- str = str..","..get_table_id(table)
- if fwmark == "" then
- str = str..",".."NULL"
- else
- str = str..","..fwmark
- end
- str = str..","..tostring(flag)
- return str
+ utils.delete_uci_section(uci_conf, rule_validator, rule, "rule")
+
+ -- commit change
+ uci:save(uci_conf)
+ uci:commit(uci_conf)
+
+ return true
end
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)
+ res, obj_value, code = v["save_func"](obj)
+ if code == nil then
+ code = 400
+ end
if res == false then
- return res, obj_value
+ return res, code, obj_value
end
end
if type(obj_value) == "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])
+ local res, code, msg = create_uci_section(configuration, v["item_validator"], option_name, section_obj[k])
if res == false then
- return res, msg
+ return res, code, msg
end
end
uci:set_list(configuration, obj_section, option_name, sub_obj_names)
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)
+ local res, code, msg = create_uci_section(configuration, v["validator"], target_name, obj_value)
if res == false then
- return res, msg
+ return res, code, msg
end
uci:set(configuration, obj_section, target_name, obj_value.name)
else
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)
+ local res, code, msg = create_uci_section(processors["configuration"], processors[object_type].validator, object_type, obj)
if res == false then
uci:revert(processors["configuration"])
- return res, msg
+ return res, code, msg
end
-- commit change
local val_func = v["validator"]
local item_val_func = v["item_validator"]
local error_message = v["message"]
+ local error_code = v["code"]
local required = v["required"]
local default = v["default"]
local target_name = name
error_message = ""
end
+ if error_code == nil then
+ error_code = ""
+ else
+ error_code = error_code .. ":"
+ end
+
local value = src[name]
if value ~= nil and type(value) == "string" then
value = trim(value)
if res == false then
if ret_obj ~= nil and ret_obj ~= "" then
- return false, "Field[" .. name .. "] checked failed: " .. error_message .. " [" .. ret_obj .. "]"
+ return false, error_code .. "Field[" .. name .. "] checked failed: " .. error_message .. " [" .. ret_obj .. "]"
else
- return false, "Field[" .. name .. "] checked failed: " .. error_message
+ return false, error_code .. "Field[" .. name .. "] checked failed: " .. error_message
end
else
if ret_obj ~= nil then
local res, res_obj = validate_and_set_data(validator, body_obj)
if not res then
- response_error(400, res_obj)
+ local code = 400
+ local message = res_obj
+ local i,j = string.find(res_obj, ":")
+ if i ~= nil then
+ local co = string.sub(res_obj, 1, i-1)
+ if tonumber(co) ~= nil then
+ code = co
+ message = string.sub(res_obj, j+1, string.len(res_obj))
+ end
+ end
+ response_error(code, message)
return nil
end
return true
}
-// +kubebuilder:webhook:path=/validate-sdewan-bucket-permission,mutating=false,failurePolicy=fail,groups="batch.sdewan.akraino.org",resources=mwan3policies;mwan3rules;firewallzones;firewallforwardings;firewallrules;firewallsnats;firewalldnats;cnfservice;cnfstatuses;sdewanapplication;ipsecproposals;ipsechosts;ipsecsites,verbs=create;update;delete,versions=v1alpha1,name=validate-sdewan-bucket.akraino.org
+// +kubebuilder:webhook:path=/validate-sdewan-bucket-permission,mutating=false,failurePolicy=fail,groups="batch.sdewan.akraino.org",resources=mwan3policies;mwan3rules;firewallzones;firewallforwardings;firewallrules;firewallsnats;firewalldnats;cnfnats;cnfroutes;cnfrouterules;cnfservices;cnflocalservices;cnfstatuses;sdewanapplication;ipsecproposals;ipsechosts;ipsecsites,verbs=create;update;delete,versions=v1alpha1,name=validate-sdewan-bucket.akraino.org
// bucketPermissionValidator validates Pods
type bucketPermissionValidator struct {
obj = &FirewallZone{}
case "FirewallRule":
obj = &FirewallRule{}
+ case "CNFNAT":
+ obj = &CNFNAT{}
+ case "CNFRoute":
+ obj = &CNFRoute{}
+ case "CNFRouteRule":
+ obj = &CNFRouteRule{}
case "FirewallDNAT":
obj = &FirewallDNAT{}
case "FirewallSNAT":
obj = &CNFService{}
case "CNFStatus":
obj = &CNFStatus{}
+ case "CNFLocalService":
+ obj = &CNFLocalService{}
case "SdewanApplication":
obj = &SdewanApplication{}
default:
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0\r
+// Copyright (c) 2021 Intel Corporation\r
+package v1alpha1\r
+\r
+import (\r
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"\r
+)\r
+\r
+// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!\r
+// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.\r
+\r
+// CNFLocalServiceStatus defines the observed state of CNFLocalServiceStatus\r
+type CNFLocalServiceStatus struct {\r
+ // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster\r
+ // Important: Run "make" to regenerate code after modifying this file\r
+ // +optional\r
+ LocalIP string `json:"localip,omitempty"`\r
+ // +optional\r
+ LocalPort string `json:"localport,omitempty"`\r
+ // +optional\r
+ RemoteIPs []string `json:"remoteips,omitempty"`\r
+ // +optional\r
+ RemotePort string `json:"remoteport,omitempty"`\r
+ // +optional\r
+ Message string `json:"message,omitempty"`\r
+}\r
+\r
+func (c *CNFLocalServiceStatus) IsEqual(s *CNFLocalServiceStatus) bool {\r
+ if c.LocalIP != s.LocalIP ||\r
+ c.LocalPort != s.LocalPort ||\r
+ c.RemotePort != s.RemotePort {\r
+ return false\r
+ }\r
+ if len(c.RemoteIPs) != len(s.RemoteIPs) {\r
+ return false\r
+ }\r
+\r
+ for i:=0; i<len(c.RemoteIPs); i++ {\r
+ if c.RemoteIPs[i] != s.RemoteIPs[i] {\r
+ return false\r
+ }\r
+ }\r
+\r
+ return true\r
+}\r
+\r
+// CNFLocalServiceSpec defines the desired state of CNFService\r
+type CNFLocalServiceSpec struct {\r
+ LocalService string `json:"localservice,omitempty"`\r
+ LocalPort string `json:"localport,omitempty"`\r
+ RemoteService string `json:"remoteservice,omitempty"`\r
+ RemotePort string `json:"remoteport,omitempty"`\r
+}\r
+\r
+// +kubebuilder:object:root=true\r
+// +kubebuilder:subresource:status\r
+\r
+// CNFLocalService is the Schema for the cnflocalservices API\r
+type CNFLocalService struct {\r
+ metav1.TypeMeta `json:",inline"`\r
+ metav1.ObjectMeta `json:"metadata,omitempty"`\r
+\r
+ Spec CNFLocalServiceSpec `json:"spec,omitempty"`\r
+ Status CNFLocalServiceStatus `json:"status,omitempty"`\r
+}\r
+\r
+// +kubebuilder:object:root=true\r
+\r
+// CNFLocalServiceList contains a list of CNFLocalServiceList\r
+type CNFLocalServiceList struct {\r
+ metav1.TypeMeta `json:",inline"`\r
+ metav1.ListMeta `json:"metadata,omitempty"`\r
+ Items []CNFLocalService `json:"items"`\r
+}\r
+\r
+func init() {\r
+ SchemeBuilder.Register(&CNFLocalService{}, &CNFLocalServiceList{})\r
+}\r
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0\r
+// Copyright (c) 2021 Intel Corporation\r
+package v1alpha1\r
+\r
+import (\r
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"\r
+)\r
+\r
+// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!\r
+// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.\r
+\r
+// CNFNATSpec defines the desired state of CNFNAT\r
+type CNFNATSpec struct {\r
+ // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster\r
+ // Important: Run "make" to regenerate code after modifying this file\r
+ Name string `json:"name,omitempty"`\r
+ Src string `json:"src,omitempty"`\r
+ SrcIp string `json:"src_ip,omitempty"`\r
+ SrcDIp string `json:"src_dip,omitempty"`\r
+ SrcPort string `json:"src_port,omitempty"`\r
+ SrcDPort string `json:"src_dport,omitempty"`\r
+ Proto string `json:"proto,omitempty"`\r
+ Dest string `json:"dest,omitempty"`\r
+ DestIp string `json:"dest_ip,omitempty"`\r
+ DestPort string `json:"dest_port,omitempty"`\r
+ Target string `json:"target,omitempty"`\r
+ Index string `json:"index,omitempty"`\r
+}\r
+\r
+// +kubebuilder:object:root=true\r
+// +kubebuilder:subresource:status\r
+\r
+// CNFNAT is the Schema for the cnfnats API\r
+type CNFNAT struct {\r
+ metav1.TypeMeta `json:",inline"`\r
+ metav1.ObjectMeta `json:"metadata,omitempty"`\r
+\r
+ Spec CNFNATSpec `json:"spec,omitempty"`\r
+ Status SdewanStatus `json:"status,omitempty"`\r
+}\r
+\r
+// +kubebuilder:object:root=true\r
+\r
+// CNFNATList contains a list of CNFNAT\r
+type CNFNATList struct {\r
+ metav1.TypeMeta `json:",inline"`\r
+ metav1.ListMeta `json:"metadata,omitempty"`\r
+ Items []CNFNAT `json:"items"`\r
+}\r
+\r
+func init() {\r
+ SchemeBuilder.Register(&CNFNAT{}, &CNFNATList{})\r
+}\r
return nil
}
-// +kubebuilder:webhook:path=/validate-label,mutating=false,failurePolicy=fail,groups=apps;batch.sdewan.akraino.org,resources=deployments;mwan3policies;mwan3rules;firewallzones;firewallforwardings;firewallrules;firewallsnats;firewalldnats;cnfservice;cnfstatuses;sdewanapplication;ipsecproposals;ipsechosts;ipsecsites,verbs=update,versions=v1;v1alpha1,name=validate-label.akraino.org
+// +kubebuilder:webhook:path=/validate-label,mutating=false,failurePolicy=fail,groups=apps;batch.sdewan.akraino.org,resources=deployments;mwan3policies;mwan3rules;firewallzones;firewallforwardings;firewallrules;firewallsnats;firewalldnats;cnfnats;cnfservices;cnfroutes;cnfrouterules;cnflocalservices;cnfstatuses;sdewanapplication;ipsecproposals;ipsechosts;ipsecsites,verbs=update,versions=v1;v1alpha1,name=validate-label.akraino.org
type labelValidator struct {
Client client.Client
obj = &FirewallZone{}
case "FirewallRule":
obj = &FirewallRule{}
+ case "CNFNAT":
+ obj = &CNFNAT{}
+ case "CNFRoute":
+ obj = &CNFRoute{}
+ case "CNFRouteRule":
+ obj = &CNFRouteRule{}
case "FirewallDNAT":
obj = &FirewallDNAT{}
case "FirewallSNAT":
obj = &IpsecSite{}
case "CNFService":
obj = &CNFService{}
+ case "CNFLocalService":
+ obj = &CNFLocalService{}
case "CNFStatus":
obj = &CNFStatus{}
case "SdewanApplication":
- bases/batch.sdewan.akraino.org_ipsechosts.yaml
- bases/batch.sdewan.akraino.org_ipsecsites.yaml
- bases/batch.sdewan.akraino.org_cnfservices.yaml
+- bases/batch.sdewan.akraino.org_cnflocalservices.yaml
- bases/batch.sdewan.akraino.org_sdewanapplications.yaml
- bases/batch.sdewan.akraino.org_cnfstatuses.yaml
- bases/batch.sdewan.akraino.org_cnfroutes.yaml
- bases/batch.sdewan.akraino.org_cnfrouterules.yaml
+- bases/batch.sdewan.akraino.org_cnfnats.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:
#- patches/webhook_in_ipsechosts.yaml
#- patches/webhook_in_ipsecsites.yaml
#- patches/webhook_in_cnfservices.yaml
+#- patches/webhook_in_cnflocalservices.yaml
#- patches/webhook_in_sdewanapplications.yaml
#- patches/webhook_in_cnfstatuses.yaml
#- patches/webhook_in_cnfroutes.yaml
#- patches/webhook_in_cnfrouterules.yaml
+#- patches/webhook_in_cnfnats.yaml
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
#- patches/cainjection_in_ipsechosts.yaml
#- patches/cainjection_in_ipsecsites.yaml
#- patches/cainjection_in_cnfservices.yaml
+#- patches/cainjection_in_cnflocalservices.yaml
#- patches/cainjection_in_sdewanapplications.yaml
#- patches/cainjection_in_cnfstatuses.yaml
#- patches/cainjection_in_cnfroutes.yaml
#- patches/cainjection_in_cnfrouterules.yaml
+#- patches/cainjection_in_cnfnats.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.
--- /dev/null
+# SPDX-License-Identifier: Apache-2.0\r
+# Copyright (c) 2021 Intel Corporation\r
+# The following patch adds a directive for certmanager to inject CA into the CRD\r
+# CRD conversion requires k8s 1.13 or later.\r
+apiVersion: apiextensions.k8s.io/v1beta1\r
+kind: CustomResourceDefinition\r
+metadata:\r
+ annotations:\r
+ cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)\r
+ name: cnfnats.batch.sdewan.akraino.org\r
--- /dev/null
+# SPDX-License-Identifier: Apache-2.0\r
+# Copyright (c) 2021 Intel Corporation\r
+# The following patch enables conversion webhook for CRD\r
+# CRD conversion requires k8s 1.13 or later.\r
+apiVersion: apiextensions.k8s.io/v1beta1\r
+kind: CustomResourceDefinition\r
+metadata:\r
+ name: cnfnats.batch.sdewan.akraino.org\r
+spec:\r
+ conversion:\r
+ strategy: Webhook\r
+ webhookClientConfig:\r
+ # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,\r
+ # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)\r
+ caBundle: Cg==\r
+ service:\r
+ namespace: system\r
+ name: webhook-service\r
+ path: /convert\r
resources:
limits:
cpu: 100m
- memory: 30Mi
+ memory: 100Mi
requests:
cpu: 100m
- memory: 20Mi
+ memory: 60Mi
terminationGracePeriodSeconds: 10
--- /dev/null
+# SPDX-License-Identifier: Apache-2.0\r
+# Copyright (c) 2021 Intel Corporation\r
+---\r
+apiVersion: batch.sdewan.akraino.org/v1alpha1\r
+kind: CNFNAT\r
+metadata:\r
+ name: nat-sample\r
+ namespace: default\r
+ labels:\r
+ sdewanPurpose: cnf1\r
+spec:\r
+ src: iface1\r
+ src_ip: 192.168.1.2\r
+ src_dip: 1.2.3.4\r
+ dest: iface2\r
+ proto: tcp\r
+ target: DNAT\r
+...
if err != nil {
log.Error(err, "Failed to add/update "+handler.GetType())
setStatus(instance, batchv1alpha1.SdewanStatus{State: batchv1alpha1.Applying, Message: err.Error()})
- _, ok := err.(*openwrt.OpenwrtError)
+ err2, ok := err.(*openwrt.OpenwrtError)
err = r.Status().Update(ctx, instance)
if err != nil {
log.Error(err, "Failed to update status for "+handler.GetType())
return ctrl.Result{}, err
}
- if ok {
+ if ok && err2.Code != 428 {
return ctrl.Result{}, err
} else {
+ // retry if network error or operation failed due to pre-condition is not satisfied (e.g. Code == 428)
return ctrl.Result{RequeueAfter: during}, nil
}
}
}
return ctrl.Result{}, nil
-}
+}
\ No newline at end of file
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0\r
+// Copyright (c) 2021 Intel Corporation\r
+package controllers\r
+\r
+import (\r
+ "context"\r
+ "errors"\r
+ "net"\r
+ "strconv"\r
+ "strings"\r
+ "sync"\r
+ "time"\r
+\r
+ "github.com/go-logr/logr"\r
+ "k8s.io/apimachinery/pkg/runtime"\r
+ errs "k8s.io/apimachinery/pkg/api/errors"\r
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"\r
+ "k8s.io/apimachinery/pkg/util/wait"\r
+ ctrl "sigs.k8s.io/controller-runtime"\r
+ "sigs.k8s.io/controller-runtime/pkg/builder"\r
+ "sigs.k8s.io/controller-runtime/pkg/client"\r
+ "sigs.k8s.io/controller-runtime/pkg/predicate"\r
+\r
+ batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"\r
+)\r
+\r
+var inLSQueryStatus = false\r
+\r
+// CNFLocalServiceReconciler reconciles a CNFLocalService object\r
+type CNFLocalServiceReconciler struct {\r
+ client.Client\r
+ Log logr.Logger\r
+ CheckInterval time.Duration\r
+ Scheme *runtime.Scheme\r
+ mux sync.Mutex\r
+}\r
+\r
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=cnflocalservices,verbs=get;list;watch;create;update;patch;delete\r
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=cnflocalservices/status,verbs=get;update;patch\r
+\r
+func (r *CNFLocalServiceReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {\r
+ ctx := context.Background()\r
+ log := r.Log.WithValues("CNFLocalService", req.NamespacedName)\r
+ during, _ := time.ParseDuration("5s")\r
+\r
+ instance, err := r.getInstance(req)\r
+ if err != nil {\r
+ if errs.IsNotFound(err) {\r
+ // No instance\r
+ return ctrl.Result{}, nil\r
+ }\r
+ // Error reading the object - requeue the request.\r
+ return ctrl.Result{RequeueAfter: during}, nil\r
+ }\r
+\r
+ finalizerName := "cnflocalservice.finalizers.sdewan.akraino.org"\r
+ delete_timestamp := getDeletionTempstamp(instance)\r
+\r
+ if delete_timestamp.IsZero() {\r
+ // Creating or updating CR\r
+ // Process instance\r
+ err = r.processInstance(instance)\r
+ if err != nil {\r
+ log.Error(err, "Adding/Updating CR")\r
+ instance.Status.Message = err.Error()\r
+ r.Status().Update(ctx, instance)\r
+\r
+ return ctrl.Result{}, err\r
+ }\r
+\r
+ finalizers := getFinalizers(instance)\r
+ if !containsString(finalizers, finalizerName) {\r
+ appendFinalizer(instance, finalizerName)\r
+ if err := r.Update(ctx, instance); err != nil {\r
+ return ctrl.Result{}, err\r
+ }\r
+ log.Info("Added finalizer for CNFLocalService")\r
+ }\r
+ } else {\r
+ // Deleting CR\r
+ // Remove instance\r
+ err = r.removeInstance(instance)\r
+ if err != nil {\r
+ log.Error(err, "Deleting CR")\r
+ return ctrl.Result{RequeueAfter: during}, nil\r
+ }\r
+\r
+ finalizers := getFinalizers(instance)\r
+ if containsString(finalizers, finalizerName) {\r
+ removeFinalizer(instance, finalizerName)\r
+ if err := r.Update(ctx, instance); err != nil {\r
+ return ctrl.Result{}, err\r
+ }\r
+ }\r
+ }\r
+\r
+ return ctrl.Result{}, nil\r
+}\r
+\r
+func (r *CNFLocalServiceReconciler) getInstance(req ctrl.Request) (*batchv1alpha1.CNFLocalService, error) {\r
+ instance := &batchv1alpha1.CNFLocalService{}\r
+ err := r.Get(context.Background(), req.NamespacedName, instance)\r
+ return instance, err\r
+}\r
+\r
+func (r *CNFLocalServiceReconciler) getIP4s(dns string) ([]string, error) {\r
+ ips, err := net.LookupIP(dns)\r
+ var ip4s []string\r
+\r
+ if err == nil {\r
+ for _, ip := range ips {\r
+ if strings.Contains(ip.String(), ".") {\r
+ ip4s = append(ip4s, ip.String())\r
+ }\r
+ }\r
+ }\r
+\r
+ return ip4s, err\r
+}\r
+\r
+func (r *CNFLocalServiceReconciler) processInstance(instance *batchv1alpha1.CNFLocalService) error {\r
+ r.mux.Lock()\r
+ defer r.mux.Unlock()\r
+\r
+ // check local service\r
+ ls := instance.Spec.LocalService\r
+ lips, err := r.getIP4s(ls)\r
+ if err != nil || len(lips) == 0 {\r
+ if err != nil {\r
+ r.Log.Error(err, "Local Service")\r
+ }\r
+ return errors.New("Cannot reterive LocalService ip")\r
+ }\r
+\r
+ // check remote service\r
+ rs := instance.Spec.RemoteService\r
+ rips, err := r.getIP4s(rs)\r
+ if err != nil || len(rips) == 0 {\r
+ if err != nil {\r
+ r.Log.Error(err, "Remote Service")\r
+ }\r
+ return errors.New("Cannot reterive RemoteService ip")\r
+ }\r
+\r
+ // check local port\r
+ lp := instance.Spec.LocalPort\r
+ if lp != "" {\r
+ _, err = strconv.Atoi(lp)\r
+ if err != nil {\r
+ return errors.New("LocalPort: " + err.Error())\r
+ }\r
+ }\r
+\r
+ // check remote port\r
+ rp := instance.Spec.RemotePort\r
+ if rp != "" {\r
+ _, err = strconv.Atoi(rp)\r
+ if err != nil {\r
+ return errors.New("RemotePort: " + err.Error())\r
+ }\r
+ }\r
+\r
+ var curStatus = batchv1alpha1.CNFLocalServiceStatus {\r
+ LocalIP: lips[0],\r
+ LocalPort: lp,\r
+ RemoteIPs: rips,\r
+ RemotePort: rp,\r
+ Message: "",\r
+ }\r
+\r
+ if !curStatus.IsEqual(&instance.Status) {\r
+ r.removeNats(instance)\r
+ r.addNats(instance, &curStatus)\r
+ instance.Status = curStatus\r
+ r.Status().Update(context.Background(), instance)\r
+ }\r
+\r
+ return nil\r
+}\r
+\r
+func (r *CNFLocalServiceReconciler) addNats(instance *batchv1alpha1.CNFLocalService, status *batchv1alpha1.CNFLocalServiceStatus) error {\r
+ r.Log.Info("Creating New CNFNAT CR for Local Service : " + instance.Name)\r
+ nat_base_name := instance.Name + "nat"\r
+ for i, rip := range status.RemoteIPs {\r
+ nat_name := nat_base_name + strconv.Itoa(i)\r
+ nat_instance := &batchv1alpha1.CNFNAT{\r
+ ObjectMeta: metav1.ObjectMeta{\r
+ Name: nat_name,\r
+ Namespace: instance.Namespace,\r
+ Labels: instance.Labels,\r
+ },\r
+ Spec: batchv1alpha1.CNFNATSpec{\r
+ SrcDIp: rip,\r
+ SrcDPort: status.RemotePort,\r
+ DestIp: status.LocalIP,\r
+ DestPort: status.LocalPort,\r
+ Proto: "tcp",\r
+ Target: "DNAT",\r
+ },\r
+ }\r
+\r
+ err := r.Create(context.Background(), nat_instance)\r
+ if err != nil {\r
+ r.Log.Error(err, "Creating NAT CR : " + nat_name)\r
+ }\r
+ }\r
+ return nil\r
+}\r
+\r
+func (r *CNFLocalServiceReconciler) removeInstance(instance *batchv1alpha1.CNFLocalService) error {\r
+ r.mux.Lock()\r
+ defer r.mux.Unlock()\r
+ return r.removeNats(instance)\r
+}\r
+\r
+func (r *CNFLocalServiceReconciler) removeNats(instance *batchv1alpha1.CNFLocalService) error {\r
+ r.Log.Info("Deleting CNFNAT CR for Local Service : " + instance.Name)\r
+ nat_base_name := instance.Name + "nat"\r
+ for i, _ := range instance.Status.RemoteIPs {\r
+ nat_name := nat_base_name + strconv.Itoa(i)\r
+ nat_instance := &batchv1alpha1.CNFNAT{\r
+ ObjectMeta: metav1.ObjectMeta{\r
+ Name: nat_name,\r
+ Namespace: instance.Namespace,\r
+ Labels: instance.Labels,\r
+ },\r
+ Spec: batchv1alpha1.CNFNATSpec{},\r
+ }\r
+\r
+ err := r.Delete(context.Background(), nat_instance)\r
+ if err != nil {\r
+ r.Log.Error(err, "Deleting NAT CR : " + nat_name)\r
+ }\r
+\r
+ // check resource\r
+ err = wait.PollImmediate(time.Second, time.Second*10,\r
+ func() (bool, error) {\r
+ nat_instance_temp := &batchv1alpha1.CNFNAT{}\r
+ err_get := r.Get(context.Background(), client.ObjectKey{\r
+ Namespace: instance.Namespace,\r
+ Name: nat_name,\r
+ }, nat_instance_temp)\r
+\r
+ if errs.IsNotFound(err_get) {\r
+ return true, nil\r
+ }\r
+ r.Log.Info("Waiting for Deleting CR : " + nat_name)\r
+ return false, nil\r
+ },\r
+ )\r
+\r
+ if err != nil {\r
+ r.Log.Error(err, "Failed to delete CR : " + nat_name)\r
+ }\r
+ }\r
+\r
+ return nil\r
+}\r
+\r
+// Query CNFStatus information\r
+func (r *CNFLocalServiceReconciler) check() {\r
+ ls_list := &batchv1alpha1.CNFLocalServiceList{}\r
+ err := r.List(context.Background(), ls_list)\r
+ if err != nil {\r
+ r.Log.Error(err, "Failed to list CNFLocalService CRs")\r
+ } else {\r
+ if len(ls_list.Items) > 0 {\r
+ for _, inst := range ls_list.Items {\r
+ r.Log.Info("Checking CNFLocalService: " + inst.Name)\r
+ r.processInstance(&inst)\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+// Query CNFStatus information\r
+func (r *CNFLocalServiceReconciler) SafeCheck() {\r
+ doCheck := true\r
+ r.mux.Lock()\r
+ if !inLSQueryStatus {\r
+ inLSQueryStatus = true\r
+ } else {\r
+ doCheck = false\r
+ }\r
+ r.mux.Unlock()\r
+\r
+ if doCheck {\r
+ r.check()\r
+\r
+ r.mux.Lock()\r
+ inLSQueryStatus = false\r
+ r.mux.Unlock()\r
+ }\r
+}\r
+\r
+func (r *CNFLocalServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {\r
+ // Start the loop to check ip address change of local/remote services\r
+ go func() {\r
+ interval := time.After(r.CheckInterval)\r
+ for {\r
+ select {\r
+ case <-interval:\r
+ r.SafeCheck()\r
+ interval = time.After(r.CheckInterval)\r
+ case <-context.Background().Done():\r
+ return\r
+ }\r
+ }\r
+ }()\r
+\r
+ ps := builder.WithPredicates(predicate.GenerationChangedPredicate{})\r
+ return ctrl.NewControllerManagedBy(mgr).\r
+ For(&batchv1alpha1.CNFLocalService{}, ps).\r
+ Complete(r)\r
+}\r
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0\r
+// Copyright (c) 2021 Intel Corporation\r
+package controllers\r
+\r
+import (\r
+ "context"\r
+ "reflect"\r
+\r
+ "github.com/go-logr/logr"\r
+ appsv1 "k8s.io/api/apps/v1"\r
+ "k8s.io/apimachinery/pkg/runtime"\r
+ ctrl "sigs.k8s.io/controller-runtime"\r
+ "sigs.k8s.io/controller-runtime/pkg/builder"\r
+ "sigs.k8s.io/controller-runtime/pkg/client"\r
+ "sigs.k8s.io/controller-runtime/pkg/handler"\r
+ "sigs.k8s.io/controller-runtime/pkg/predicate"\r
+ "sigs.k8s.io/controller-runtime/pkg/source"\r
+\r
+ batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"\r
+ "sdewan.akraino.org/sdewan/openwrt"\r
+)\r
+\r
+var cnfnatHandler = new(CNFNatHandler)\r
+\r
+type CNFNatHandler struct {\r
+}\r
+\r
+func (m *CNFNatHandler) GetType() string {\r
+ return "CNFNAT"\r
+}\r
+\r
+func (m *CNFNatHandler) GetName(instance runtime.Object) string {\r
+ nat := instance.(*batchv1alpha1.CNFNAT)\r
+ return nat.Name\r
+}\r
+\r
+func (m *CNFNatHandler) GetFinalizer() string {\r
+ return "cnfnat.finalizers.sdewan.akraino.org"\r
+}\r
+\r
+func (m *CNFNatHandler) GetInstance(r client.Client, ctx context.Context, req ctrl.Request) (runtime.Object, error) {\r
+ instance := &batchv1alpha1.CNFNAT{}\r
+ err := r.Get(ctx, req.NamespacedName, instance)\r
+ return instance, err\r
+}\r
+\r
+//pupulate "nat" to target field as default value\r
+func (m *CNFNatHandler) Convert(instance runtime.Object, deployment appsv1.Deployment) (openwrt.IOpenWrtObject, error) {\r
+ cnfnat := instance.(*batchv1alpha1.CNFNAT)\r
+ cnfnat.Spec.Name = cnfnat.ObjectMeta.Name\r
+ cnfnatObject := openwrt.SdewanNat(cnfnat.Spec)\r
+ return &cnfnatObject, nil\r
+}\r
+\r
+func (m *CNFNatHandler) IsEqual(instance1 openwrt.IOpenWrtObject, instance2 openwrt.IOpenWrtObject) bool {\r
+ nat1 := instance1.(*openwrt.SdewanNat)\r
+ nat2 := instance2.(*openwrt.SdewanNat)\r
+ return reflect.DeepEqual(*nat1, *nat2)\r
+}\r
+\r
+func (m *CNFNatHandler) GetObject(clientInfo *openwrt.OpenwrtClientInfo, name string) (openwrt.IOpenWrtObject, error) {\r
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)\r
+ natClient := openwrt.NatClient{OpenwrtClient: openwrtClient}\r
+ ret, err := natClient.GetNat(name)\r
+ return ret, err\r
+}\r
+\r
+func (m *CNFNatHandler) CreateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {\r
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)\r
+ natClient := openwrt.NatClient{OpenwrtClient: openwrtClient}\r
+ nat := instance.(*openwrt.SdewanNat)\r
+ return natClient.CreateNat(*nat)\r
+}\r
+\r
+func (m *CNFNatHandler) UpdateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {\r
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)\r
+ natClient := openwrt.NatClient{OpenwrtClient: openwrtClient}\r
+ nat := instance.(*openwrt.SdewanNat)\r
+ return natClient.UpdateNat(*nat)\r
+}\r
+\r
+func (m *CNFNatHandler) DeleteObject(clientInfo *openwrt.OpenwrtClientInfo, name string) error {\r
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)\r
+ natClient := openwrt.NatClient{OpenwrtClient: openwrtClient}\r
+ return natClient.DeleteNat(name)\r
+}\r
+\r
+func (m *CNFNatHandler) Restart(clientInfo *openwrt.OpenwrtClientInfo) (bool, error) {\r
+ return true, nil\r
+}\r
+\r
+// CNFNATReconciler reconciles a CNFNAT object\r
+type CNFNATReconciler struct {\r
+ client.Client\r
+ Log logr.Logger\r
+ Scheme *runtime.Scheme\r
+}\r
+\r
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=cnfnats,verbs=get;list;watch;create;update;patch;delete\r
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=cnfnats/status,verbs=get;update;patch\r
+\r
+func (r *CNFNATReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {\r
+ return ProcessReconcile(r, r.Log, req, cnfnatHandler)\r
+}\r
+\r
+func (r *CNFNATReconciler) SetupWithManager(mgr ctrl.Manager) error {\r
+ ps := builder.WithPredicates(predicate.GenerationChangedPredicate{})\r
+ return ctrl.NewControllerManagedBy(mgr).\r
+ For(&batchv1alpha1.CNFNAT{}, ps).\r
+ Watches(\r
+ &source.Kind{Type: &appsv1.Deployment{}},\r
+ &handler.EnqueueRequestsFromMapFunc{\r
+ ToRequests: handler.ToRequestsFunc(GetToRequestsFunc(r, &batchv1alpha1.CNFNATList{})),\r
+ },\r
+ Filter).\r
+ Complete(r)\r
+}\r
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
flag.IntVar(&checkInterval, "check-interval", 30,
- "The check interval for query of CNF Status (seconds)")
+ "The check interval of CRD Controller (seconds)")
flag.Parse()
ctrl.SetLogger(zap.New(func(o *zap.Options) {
setupLog.Error(err, "unable to create controller", "controller", "Mwan3Rule")
os.Exit(1)
}
+ if err = (&controllers.CNFNATReconciler{
+ Client: mgr.GetClient(),
+ Log: ctrl.Log.WithName("controllers").WithName("CNFNAT"),
+ Scheme: mgr.GetScheme(),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "CNFNAT")
+ os.Exit(1)
+ }
if err = (&controllers.FirewallZoneReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("FirewallZone"),
setupLog.Error(err, "unable to create controller", "controller", "CNFRouteRule")
os.Exit(1)
}
+ if err = (&controllers.CNFLocalServiceReconciler{
+ Client: mgr.GetClient(),
+ Log: ctrl.Log.WithName("controllers").WithName("CNFLocalService"),
+ CheckInterval: time.Duration(checkInterval) * time.Second,
+ Scheme: mgr.GetScheme(),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "CNFLocalService")
+ os.Exit(1)
+ }
// +kubebuilder:scaffold:builder
setupLog.Info("start CNFStatusController to query CNF status periodicly")
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0\r
+// Copyright (c) 2021 Intel Corporation\r
+\r
+package openwrt\r
+\r
+import (\r
+ "encoding/json"\r
+)\r
+\r
+const (\r
+ natBaseURL = "sdewan/nat/v1/"\r
+)\r
+\r
+type NatClient struct {\r
+ OpenwrtClient *openwrtClient\r
+}\r
+\r
+// Nat\r
+type SdewanNat struct {\r
+ Name string `json:"name"`\r
+ Src string `json:"src"`\r
+ SrcIp string `json:"src_ip"`\r
+ SrcDIp string `json:"src_dip"`\r
+ SrcPort string `json:"src_port"`\r
+ SrcDPort string `json:"src_dport"`\r
+ Proto string `json:"proto"`\r
+ Dest string `json:"dest"`\r
+ DestIp string `json:"dest_ip"`\r
+ DestPort string `json:"dest_port"`\r
+ Target string `json:"target"`\r
+ Index string `json:"index"`\r
+}\r
+\r
+func (o *SdewanNat) GetName() string {\r
+ return o.Name\r
+}\r
+\r
+type SdewanNats struct {\r
+ Nats []SdewanNat `json:"nats"`\r
+}\r
+\r
+// Nat APIs\r
+// get nats\r
+func (f *NatClient) GetNats() (*SdewanNats, error) {\r
+ var response string\r
+ var err error\r
+ response, err = f.OpenwrtClient.Get(natBaseURL + "nats")\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ var sdewanNats SdewanNats\r
+ err = json.Unmarshal([]byte(response), &sdewanNats)\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ return &sdewanNats, nil\r
+}\r
+\r
+// get nat\r
+func (m *NatClient) GetNat(nat string) (*SdewanNat, error) {\r
+ var response string\r
+ var err error\r
+ response, err = m.OpenwrtClient.Get(natBaseURL + "nats/" + nat)\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ var sdewanNat SdewanNat\r
+ err = json.Unmarshal([]byte(response), &sdewanNat)\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ return &sdewanNat, nil\r
+}\r
+\r
+// create nat\r
+func (m *NatClient) CreateNat(nat SdewanNat) (*SdewanNat, error) {\r
+ var response string\r
+ var err error\r
+ nat_obj, _ := json.Marshal(nat)\r
+ response, err = m.OpenwrtClient.Post(natBaseURL+"nats", string(nat_obj))\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ var sdewanNat SdewanNat\r
+ err = json.Unmarshal([]byte(response), &sdewanNat)\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ return &sdewanNat, nil\r
+}\r
+\r
+// delete nat\r
+func (m *NatClient) DeleteNat(nat_name string) error {\r
+ _, err := m.OpenwrtClient.Delete(natBaseURL + "nats/" + nat_name)\r
+ if err != nil {\r
+ return err\r
+ }\r
+\r
+ return nil\r
+}\r
+\r
+// update nat\r
+func (m *NatClient) UpdateNat(nat SdewanNat) (*SdewanNat, error) {\r
+ var response string\r
+ var err error\r
+ nat_obj, _ := json.Marshal(nat)\r
+ nat_name := nat.Name\r
+ response, err = m.OpenwrtClient.Put(natBaseURL+"nats/"+nat_name, string(nat_obj))\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ var sdewanNat SdewanNat\r
+ err = json.Unmarshal([]byte(response), &sdewanNat)\r
+ if err != nil {\r
+ return nil, err\r
+ }\r
+\r
+ return &sdewanNat, nil\r
+}\r