You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
8.5 KiB

  1. #!/usr/bin/env lua
  2. dkjson = require("dkjson")
  3. uci = require("uci")
  4. UCI = {}
  5. --- Return the configuration defaults as a table suitable for JSON output
  6. --
  7. -- Mostly taken from yggdrasil -genconf -json
  8. -- @return table with configuration defaults
  9. function UCI.defaults()
  10. return {
  11. AdminListen = "unix:///var/run/yggdrasil.sock", IfName = "ygg0",
  12. NodeInfoPrivacy = false, IfTAPMode = false,
  13. LinkLocalTCPPort = 0, IfMTU = 65535,
  14. Peers = { }, Listen = { }, MulticastInterfaces = { }, AllowedEncryptionPublicKeys = { },
  15. InterfacePeers = setmetatable({ }, {__jsontype = "object"}),
  16. NodeInfo = setmetatable({ }, {__jsontype = "object"}),
  17. SessionFirewall = {
  18. Enable = false,
  19. AllowFromDirect = true,
  20. AllowFromRemote = true,
  21. AlwaysAllowOutbound = true,
  22. WhitelistEncryptionPublicKeys = { },
  23. BlacklistEncryptionPublicKeys = { }
  24. },
  25. TunnelRouting = {
  26. Enable = false,
  27. IPv6RemoteSubnets = setmetatable({ }, {__jsontype = "object"}),
  28. IPv6LocalSubnets = { },
  29. IPv4RemoteSubnets = setmetatable({ }, {__jsontype = "object"}),
  30. IPv4LocalSubnets = { }
  31. },
  32. SwitchOptions = { MaxTotalQueueSize = 4194304 }
  33. }
  34. end
  35. --- Return the yggdrasil configuration as a table suitable for JSON output
  36. --
  37. -- @return table with yggdrasil configuration
  38. function UCI.get()
  39. local obj = UCI.defaults()
  40. local cursor = uci.cursor()
  41. local config = cursor:get_all("yggdrasil", "yggdrasil")
  42. if not config then return obj end
  43. obj.EncryptionPublicKey = config.EncryptionPublicKey
  44. obj.EncryptionPrivateKey = config.EncryptionPrivateKey
  45. obj.SigningPublicKey = config.SigningPublicKey
  46. obj.SigningPrivateKey = config.SigningPrivateKey
  47. obj.AdminListen = config.AdminListen or obj.AdminListen
  48. obj.IfName = config.IfName or obj.IfName
  49. obj.NodeInfo = dkjson.decode(config.NodeInfo) or obj.NodeInfo
  50. for _, v in pairs({ "NodeInfoPrivacy", "IfTAPMode" }) do
  51. if config[v] ~= nil then obj[v] = to_bool(config[v]) end
  52. end
  53. for _, v in pairs({ "LinkLocalTCPPort", "IfMTU" }) do
  54. if config[v] ~= nil then obj[v] = tonumber(config[v]) end
  55. end
  56. cursor:foreach("yggdrasil", "peer", function (s)
  57. table.insert(obj.Peers, s.uri)
  58. end)
  59. cursor:foreach("yggdrasil", "listen_address", function (s)
  60. table.insert(obj.Listen, s.uri)
  61. end)
  62. cursor:foreach("yggdrasil", "multicast_interface", function (s)
  63. table.insert(obj.MulticastInterfaces, s.name)
  64. end)
  65. cursor:foreach("yggdrasil", "allowed_encryption_public_key", function (s)
  66. table.insert(obj.AllowedEncryptionPublicKeys, s.key)
  67. end)
  68. cursor:foreach("yggdrasil", "interface_peer", function (s)
  69. if obj.InterfacePeers[s.interface] == nil then
  70. obj.InterfacePeers[s.interface] = {}
  71. end
  72. table.insert(obj.InterfacePeers[s["interface"]], s.uri)
  73. end)
  74. -- session firewall config
  75. local session_firewall_config = { "Enable", "AllowFromDirect", "AllowFromRemote", "AlwaysAllowOutbound" }
  76. for _, v in pairs(session_firewall_config) do
  77. if config["SessionFirewall_"..v] ~= nil then
  78. obj.SessionFirewall[v] = to_bool(config["SessionFirewall_"..v])
  79. end
  80. end
  81. cursor:foreach("yggdrasil", "whitelisted_encryption_public_key", function (s)
  82. table.insert(obj.SessionFirewall.WhitelistEncryptionPublicKeys, s.key)
  83. end)
  84. cursor:foreach("yggdrasil", "blacklisted_encryption_public_key", function (s)
  85. table.insert(obj.SessionFirewall.BlacklistEncryptionPublicKeys, s.key)
  86. end)
  87. -- /session firewall config
  88. -- tunnel routing config
  89. if config.TunnelRouting_Enable ~= nil then
  90. obj.TunnelRouting.Enable = to_bool(config.TunnelRouting_Enable)
  91. end
  92. cursor:foreach("yggdrasil", "ipv6_remote_subnet", function (s)
  93. obj.TunnelRouting.IPv6RemoteSubnets[s.subnet] = s.key
  94. end)
  95. cursor:foreach("yggdrasil", "ipv6_local_subnet", function (s)
  96. table.insert(obj.TunnelRouting.IPv6LocalSubnets, s.subnet)
  97. end)
  98. cursor:foreach("yggdrasil", "ipv4_remote_subnet", function (s)
  99. obj.TunnelRouting.IPv4RemoteSubnets[s.subnet] = s.key
  100. end)
  101. cursor:foreach("yggdrasil", "ipv4_local_subnet", function (s)
  102. table.insert(obj.TunnelRouting.IPv4LocalSubnets, s.subnet)
  103. end)
  104. -- /tunnel routing config
  105. if config.SwitchOptions_MaxTotalQueueSize ~= nil then
  106. obj.SwitchOptions.MaxTotalQueueSize = tonumber(config.SwitchOptions_MaxTotalQueueSize)
  107. end
  108. return obj
  109. end
  110. --- Parse and save updated configuration from JSON input
  111. --
  112. -- Transforms general settings into UCI sections, and replaces the UCI config's
  113. -- contents with them.
  114. -- @param table JSON input
  115. -- @return Boolean whether saving succeeded
  116. function UCI.set(obj)
  117. local cursor = uci.cursor()
  118. for i, section in pairs(cursor:get_all("yggdrasil")) do
  119. cursor:delete("yggdrasil", section[".name"])
  120. end
  121. cursor:set("yggdrasil", "yggdrasil", "yggdrasil")
  122. cursor:set("yggdrasil", "yggdrasil", "EncryptionPublicKey", obj.EncryptionPublicKey)
  123. cursor:set("yggdrasil", "yggdrasil", "EncryptionPrivateKey", obj.EncryptionPrivateKey)
  124. cursor:set("yggdrasil", "yggdrasil", "SigningPublicKey", obj.SigningPublicKey)
  125. cursor:set("yggdrasil", "yggdrasil", "SigningPrivateKey", obj.SigningPrivateKey)
  126. cursor:set("yggdrasil", "yggdrasil", "AdminListen", obj.AdminListen)
  127. cursor:set("yggdrasil", "yggdrasil", "IfName", obj.IfName)
  128. cursor:set("yggdrasil", "yggdrasil", "NodeInfoPrivacy", to_int(obj.NodeInfoPrivacy))
  129. cursor:set("yggdrasil", "yggdrasil", "NodeInfo", dkjson.encode(obj.NodeInfo))
  130. cursor:set("yggdrasil", "yggdrasil", "IfTAPMode", to_int(obj.IfTAPMode))
  131. cursor:set("yggdrasil", "yggdrasil", "LinkLocalTCPPort", obj.LinkLocalTCPPort)
  132. cursor:set("yggdrasil", "yggdrasil", "IfMTU", obj.IfMTU)
  133. set_values(cursor, "peer", "uri", obj.Peers)
  134. set_values(cursor, "listen_address", "uri", obj.Listen)
  135. set_values(cursor, "multicast_interface", "name", obj.MulticastInterfaces)
  136. set_values(cursor, "allowed_encryption_public_key", "key", obj.AllowedEncryptionPublicKeys)
  137. for interface, peers in pairs(obj.InterfacePeers) do
  138. for _, v in pairs(peers) do
  139. local name = cursor:add("yggdrasil", "interface_peer")
  140. cursor:set("yggdrasil", name, "interface", interface)
  141. cursor:set("yggdrasil", name, "uri", v)
  142. end
  143. end
  144. -- session firewall config
  145. cursor:set("yggdrasil", "yggdrasil", "SessionFirewall_Enable", to_int(obj.SessionFirewall.Enable))
  146. cursor:set("yggdrasil", "yggdrasil", "SessionFirewall_AllowFromDirect", to_int(obj.SessionFirewall.AllowFromDirect))
  147. cursor:set("yggdrasil", "yggdrasil", "SessionFirewall_AllowFromRemote", to_int(obj.SessionFirewall.AllowFromRemote))
  148. cursor:set("yggdrasil", "yggdrasil", "SessionFirewall_AlwaysAllowOutbound", to_int(obj.SessionFirewall.AlwaysAllowOutbound))
  149. set_values(cursor, "whitelisted_encryption_public_key", "key", obj.SessionFirewall.WhitelistEncryptionPublicKeys)
  150. set_values(cursor, "blacklisted_encryption_public_key", "key", obj.SessionFirewall.BlacklistEncryptionPublicKeys)
  151. -- /session firewall config
  152. -- tunnel routing config
  153. cursor:set("yggdrasil", "yggdrasil", "TunnelRouting_Enable", to_int(obj.TunnelRouting.Enable))
  154. if obj.TunnelRouting.IPv6RemoteSubnets ~= nil then
  155. for subnet, key in pairs(obj.TunnelRouting.IPv6RemoteSubnets) do
  156. local name = cursor:add("yggdrasil", "ipv6_remote_subnet")
  157. cursor:set("yggdrasil", name, "subnet", subnet)
  158. cursor:set("yggdrasil", name, "key", key)
  159. end
  160. end
  161. set_values(cursor, "ipv6_local_subnet", "subnet", obj.TunnelRouting.IPv6LocalSubnets)
  162. if obj.TunnelRouting.IPv4RemoteSubnets ~= nil then
  163. for subnet, key in pairs(obj.TunnelRouting.IPv4RemoteSubnets) do
  164. local name = cursor:add("yggdrasil", "ipv4_remote_subnet")
  165. cursor:set("yggdrasil", name, "subnet", subnet)
  166. cursor:set("yggdrasil", name, "key", key)
  167. end
  168. end
  169. set_values(cursor, "ipv4_local_subnet", "subnet", obj.TunnelRouting.IPv4LocalSubnets)
  170. -- /tunnel routing config
  171. cursor:set("yggdrasil", "yggdrasil", "SwitchOptions_MaxTotalQueueSize", obj.SwitchOptions.MaxTotalQueueSize)
  172. return cursor:commit("yggdrasil")
  173. end
  174. function set_values(cursor, section_name, parameter, values)
  175. if values == nil then return false end
  176. for k, v in pairs(values) do
  177. local name = cursor:add("yggdrasil", section_name)
  178. cursor:set("yggdrasil", name, parameter, v)
  179. end
  180. end
  181. function to_int(bool) return bool and '1' or '0' end
  182. function to_bool(int) return int ~= '0' end
  183. function help()
  184. print("JSON interface to /etc/config/yggdrasil\n\nExamples: \
  185. ygguci get > /tmp/etc/yggdrasil.conf \
  186. cat /tmp/etc/yggdrasil.conf | ygguci set \
  187. uci changes \
  188. ygguci get | yggdrasil -useconf")
  189. end
  190. -- main
  191. if arg[1] == "get" then
  192. local json = dkjson.encode(UCI.get(), { indent = true })
  193. print(json)
  194. elseif arg[1] == "set" then
  195. local json = io.stdin:read("*a")
  196. local obj, pos, err = dkjson.decode(json, 1, nil)
  197. if obj then
  198. UCI.set(obj)
  199. else
  200. print("dkjson: " .. err)
  201. os.exit(1)
  202. end
  203. else
  204. help()
  205. end