1 common = require("cjdns/common")
7 --- Return the configuration defaults as a table suitable for JSON output
9 -- Mostly taken from cjdroute --genconf
10 -- @return table with configuration defaults
11 function UCI.defaults()
14 { setuser = "nobody", keepNetAdmin = 1 },
15 { chroot = "/var/run/" },
23 ipTunnel = { outgoingConnections = {}, allowedConnections = {} },
24 interface = { type = "TUNInterface" }
26 interfaces = { UDPInterface = {}, ETHInterface = {} },
27 authorizedPasswords = {},
28 logging = { logTo = "stdout" }
32 --- Return the cjdns configuration as a table suitable for JSON output
34 -- Iterates over cjdns, eth_interface, udp_interface, eth_peer, udp_peer,
35 -- and password sections. Doesn't include IPTunnel related options yet.
36 -- @return table with cjdns configuration
38 local obj = UCI.defaults()
40 local cursor = uci.cursor()
42 local config = cursor:get_all("cjdns", "cjdns")
43 if not config then return obj end
45 obj.ipv6 = config.ipv6
46 obj.publicKey = config.public_key
47 obj.privateKey = config.private_key
49 bind = config.admin_address .. ":" .. config.admin_port,
50 password = config.admin_password }
52 if config.tun_device and string.len(config.tun_device) > 0 then
53 obj.router.interface.tunDevice = config.tun_device
56 for i,section in pairs(obj.security) do
57 if type(section.seccomp) == "number" then
58 obj.security[i].seccomp = tonumber(config.seccomp)
62 cursor:foreach("cjdns", "iptunnel_outgoing", function(outgoing)
63 table.insert(obj.router.ipTunnel.outgoingConnections, outgoing.public_key)
66 cursor:foreach("cjdns", "iptunnel_allowed", function(allowed)
67 entry = { publicKey = allowed.public_key }
69 entry["ip4Address"] = allowed.ipv4
72 entry["ip6Address"] = allowed.ipv6
74 table.insert(obj.router.ipTunnel.allowedConnections, entry)
77 cursor:foreach("cjdns", "eth_interface", function(eth_interface)
78 table.insert(obj.interfaces.ETHInterface, {
79 bind = eth_interface.bind,
80 beacon = tonumber(eth_interface.beacon),
85 cursor:foreach("cjdns", "udp_interface", function(udp_interface)
86 table.insert(obj.interfaces.UDPInterface, {
87 bind = udp_interface.address .. ":" .. udp_interface.port,
92 cursor:foreach("cjdns", "eth_peer", function(eth_peer)
93 if not eth_peer.address == "" then
94 local i = tonumber(eth_peer.interface)
95 obj.interfaces.ETHInterface[i].connectTo[eth_peer.address] = {
96 publicKey = eth_peer.public_key,
97 password = eth_peer.password
102 cursor:foreach("cjdns", "udp_peer", function(udp_peer)
103 local bind = udp_peer.address .. ":" .. udp_peer.port
104 local i = tonumber(udp_peer.interface)
105 obj.interfaces.UDPInterface[i].connectTo[bind] = {
106 user = udp_peer.user,
107 publicKey = udp_peer.public_key,
108 password = udp_peer.password
112 cursor:foreach("cjdns", "password", function(password)
113 table.insert(obj.authorizedPasswords, {
114 password = password.password,
115 user = password.user,
116 contact = password.contact
123 --- Parse and save updated configuration from JSON input
125 -- Transforms general settings, ETHInterface, UDPInterface, connectTo, and
126 -- authorizedPasswords fields into UCI sections, and replaces the UCI config's
127 -- contents with them.
128 -- @param table JSON input
129 -- @return Boolean whether saving succeeded
130 function UCI.set(obj)
131 local cursor = uci.cursor()
133 for i, section in pairs(cursor:get_all("cjdns")) do
134 cursor:delete("cjdns", section[".name"])
137 local admin_address, admin_port = string.match(obj.admin.bind, "^(.*):(.*)$")
138 UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
140 public_key = obj.publicKey,
141 private_key = obj.privateKey,
142 admin_password = obj.admin.password,
143 admin_address = admin_address,
144 admin_port = admin_port
147 if obj.router.interface.tunDevice then
148 UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
149 tun_device = tostring(obj.router.interface.tunDevice)
154 for i,section in pairs(obj.security) do
155 for key,value in pairs(section) do
156 if key == "seccomp" then
157 UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
158 seccomp = tonumber(value)
165 if obj.router.ipTunnel.outgoingConnections then
166 for i,public_key in pairs(obj.router.ipTunnel.outgoingConnections) do
167 UCI.cursor_section(cursor, "cjdns", "iptunnel_outgoing", nil, {
168 public_key = public_key
173 if obj.router.ipTunnel.allowedConnections then
174 for i,allowed in pairs(obj.router.ipTunnel.allowedConnections) do
175 entry = { public_key = allowed.publicKey }
176 if allowed.ip4Address then
177 entry["ipv4"] = allowed.ip4Address
179 if allowed.ip6Address then
180 entry["ipv6"] = allowed.ip6Address
183 UCI.cursor_section(cursor, "cjdns", "iptunnel_allowed", nil, entry)
187 if obj.interfaces.ETHInterface then
188 for i,interface in pairs(obj.interfaces.ETHInterface) do
189 UCI.cursor_section(cursor, "cjdns", "eth_interface", nil, {
190 bind = interface.bind,
191 beacon = tostring(interface.beacon)
194 if interface.connectTo then
195 for peer_address,peer in pairs(interface.connectTo) do
196 UCI.cursor_section(cursor, "cjdns", "eth_peer", nil, {
198 address = peer_address,
199 public_key = peer.publicKey,
200 password = peer.password
207 if obj.interfaces.UDPInterface then
208 for i,interface in pairs(obj.interfaces.UDPInterface) do
209 local address, port = string.match(interface.bind, "^(.*):(.*)$")
210 UCI.cursor_section(cursor, "cjdns", "udp_interface", nil, {
215 if interface.connectTo then
216 for peer_bind,peer in pairs(interface.connectTo) do
217 local peer_address, peer_port = string.match(peer_bind, "^(.*):(.*)$")
218 UCI.cursor_section(cursor, "cjdns", "udp_peer", nil, {
220 address = peer_address,
223 public_key = peer.publicKey,
224 password = peer.password
231 if obj.authorizedPasswords then
232 for i,password in pairs(obj.authorizedPasswords) do
233 local user = password.user
234 if not user or string.len(user) == 0 then
235 user = "user-" .. UCI.random_string(6)
238 UCI.cursor_section(cursor, "cjdns", "password", nil, {
239 password = password.password,
241 contact = password.contact
246 return cursor:save("cjdns")
249 --- Simple backport of Cursor:section from luci.model.uci
251 -- Backport reason: we don't wanna depend on LuCI.
252 -- @param Cursor the UCI cursor to operate on
253 -- @param string name of the config
254 -- @param string type of the section
255 -- @param string name of the section (optional)
256 -- @param table config values
257 function UCI.cursor_section(cursor, config, type, section, values)
259 cursor:set(config, section, type)
261 section = cursor:add("cjdns", type)
264 for k,v in pairs(values) do
265 cursor:set(config, section, k, v)
269 function UCI.makeInterface()
270 local cursor = uci.cursor()
272 local config = cursor:get_all("cjdns", "cjdns")
273 if not config then return nil end
275 return common.AdminInterface.new({
276 host = config.admin_address,
277 port = config.admin_port,
278 password = config.admin_password,
284 function UCI.random_string(length)
285 -- tr -cd 'A-Za-z0-9' < /dev/urandom
286 local urandom = io.popen("tr -cd 'A-Za-z0-9' 2> /dev/null < /dev/urandom", "r")
287 local string = urandom:read(length)