include $(TOPDIR)/rules.mk
PKG_NAME:=yggdrasil
-PKG_VERSION:=0.4.7
+PKG_VERSION:=0.5.2
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/yggdrasil-network/yggdrasil-go/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=47429f75b87d9b2450108471991e84c90d748606642e8778e9f578485b05a56f
+PKG_HASH:=ed908594ab687e141dd2202e1b360e5bd93f910de1fd1f737d210cc784cf2470
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-go-$(PKG_VERSION)
PKG_MAINTAINER:=William Fleurant <meshnet@protonmail.com>
SUBMENU:=Routing and Redirection
TITLE:=Yggdrasil supports end-to-end encrypted IPv6 networks
URL:=https://yggdrasil-network.github.io/
- DEPENDS:=$(GO_ARCH_DEPENDS) @IPV6 +kmod-tun +dkjson +libuci-lua
+ DEPENDS:=$(GO_ARCH_DEPENDS) @IPV6 +kmod-tun
endef
define Package/yggdrasil/description
interfaces simultaneously with much greater throughput.
endef
-define Package/yggdrasil/conffiles
-/etc/config/yggdrasil
-endef
-
define Package/yggdrasil/install
$(INSTALL_DIR) \
- $(1)/etc/init.d \
- $(1)/etc/uci-defaults \
+ $(1)/lib/netifd/proto \
$(1)/usr/sbin
$(INSTALL_BIN) \
$(1)/usr/sbin
$(INSTALL_BIN) \
- ./files/ygguci \
- $(1)/usr/sbin
-
- $(INSTALL_BIN) \
- ./files/yggdrasil.defaults \
- $(1)/etc/uci-defaults/yggdrasil
-
- $(INSTALL_BIN) \
- ./files/yggdrasil.init \
- $(1)/etc/init.d/yggdrasil
+ ./files/yggdrasil.sh \
+ $(1)/lib/netifd/proto
endef
$(eval $(call GoBinPackage,yggdrasil))
+++ /dev/null
-#!/bin/sh
-
-yggConfig="/etc/config/yggdrasil"
-
-first_boot_genConfig()
-{
- . /usr/share/libubox/jshn.sh
- boardcfg=$(ubus call system board)
- touch ${yggConfig}
- yggdrasil -genconf -json | ygguci set
-
- json_load "$boardcfg"
- json_get_var kernel kernel
- json_get_var system system
- json_get_var model model
- json_get_var board_name board_name
- nodeinfo='{"kernel": "'$kernel'", "hostname":"'OpenWrt'", "system": "'$system'", "model": "'$model'", "board_name": "'$board_name'"}'
-
- uci set yggdrasil.yggdrasil.IfName="ygg0"
- uci set yggdrasil.yggdrasil.NodeInfo="$nodeinfo"
- uci commit yggdrasil
-}
-
-if [ -e /etc/yggdrasil.conf ]; then
- echo "config: import config from /etc/yggdrasil.conf to /etc/config/yggdrasil" | logger -t yggdrasil
- touch ${yggConfig}
- cat /etc/yggdrasil.conf | ygguci set
- mv /etc/yggdrasil.conf /etc/yggdrasil.conf.bak
-elif [ ! -e ${yggConfig} ]; then
- echo "first_boot: adding system board details to NodeInfo[] in NEW config: ${yggConfig}" | logger -t yggdrasil
-
- first_boot_genConfig
-
- # create the network interface
- uci -q batch <<-EOF >/dev/null
- set network.yggdrasil=interface
- set network.yggdrasil.device=ygg0
- set network.yggdrasil.proto=none
-EOF
-
- # create the firewall zone
- uci -q batch <<-EOF >/dev/null
- set firewall.yggdrasil=zone
- set firewall.yggdrasil.name=yggdrasil
- add_list firewall.yggdrasil.network=yggdrasil
- set firewall.yggdrasil.input=REJECT
- set firewall.yggdrasil.output=ACCEPT
- set firewall.yggdrasil.forward=REJECT
- set firewall.yggdrasil.conntrack=1
-EOF
-
- # allow ICMP from yggdrasil zone, e.g. ping6
- uci -q batch <<-EOF >/dev/null
- add firewall rule
- set firewall.@rule[-1].name='Allow-ICMPv6-yggdrasil'
- set firewall.@rule[-1].src=yggdrasil
- set firewall.@rule[-1].proto=icmp
- add_list firewall.@rule[-1].icmp_type=echo-request
- add_list firewall.@rule[-1].icmp_type=echo-reply
- add_list firewall.@rule[-1].icmp_type=destination-unreachable
- add_list firewall.@rule[-1].icmp_type=packet-too-big
- add_list firewall.@rule[-1].icmp_type=time-exceeded
- add_list firewall.@rule[-1].icmp_type=bad-header
- add_list firewall.@rule[-1].icmp_type=unknown-header-type
- set firewall.@rule[-1].limit='1000/sec'
- set firewall.@rule[-1].family=ipv6
- set firewall.@rule[-1].target=ACCEPT
-EOF
-
- # allow SSH from yggdrasil zone, needs to be explicitly enabled
- uci -q batch <<-EOF >/dev/null
- add firewall rule
- set firewall.@rule[-1].enabled=0
- set firewall.@rule[-1].name='Allow-SSH-yggdrasil'
- set firewall.@rule[-1].src=yggdrasil
- set firewall.@rule[-1].proto=tcp
- set firewall.@rule[-1].dest_port=22
- set firewall.@rule[-1].target=ACCEPT
-EOF
-
- # allow LuCI access from yggdrasil zone, needs to be explicitly enabled
- uci -q batch <<-EOF >/dev/null
- add firewall rule
- set firewall.@rule[-1].enabled=0
- set firewall.@rule[-1].name='Allow-HTTP-yggdrasil'
- set firewall.@rule[-1].src=yggdrasil
- set firewall.@rule[-1].proto=tcp
- set firewall.@rule[-1].dest_port=80
- set firewall.@rule[-1].target=ACCEPT
-EOF
-
- # allow LuCI access with SSL from yggdrasil zone, needs to be explicitly enabled
- uci -q batch <<-EOF >/dev/null
- add firewall rule
- set firewall.@rule[-1].enabled=0
- set firewall.@rule[-1].name='Allow-HTTPS-yggdrasil'
- set firewall.@rule[-1].src=yggdrasil
- set firewall.@rule[-1].proto=tcp
- set firewall.@rule[-1].dest_port=443
- set firewall.@rule[-1].target=ACCEPT
-EOF
-
- uci commit firewall
- uci commit network
-
-else
- :
-fi
-
-exit 0
+++ /dev/null
-#!/bin/sh /etc/rc.common
-
-START=90
-STOP=85
-
-USE_PROCD=1
-BIN_FILE="/usr/sbin/yggdrasil"
-CONFIG_FILE="/tmp/yggdrasil.conf"
-DAEMON_OPTS="-useconffile $CONFIG_FILE"
-
-start_service()
-{
- [ -f /etc/uci-defaults/yggdrasil ] && ( . /etc/uci-defaults/yggdrasil )
-
- /usr/sbin/ygguci get | $BIN_FILE -useconf -normaliseconf -json > $CONFIG_FILE
-
- procd_open_instance
- procd_set_param respawn
- procd_set_param command $BIN_FILE $DAEMON_OPTS
- procd_set_param stdout 1
- procd_set_param stderr 1
- procd_close_instance
-}
-
-reload_service()
-{
- restart
-}
-
-service_triggers()
-{
- procd_add_reload_trigger yggdrasil
-}
--- /dev/null
+#!/bin/sh
+
+
+[ -n "$INCLUDE_ONLY" ] || {
+ . /lib/functions.sh
+ . ../netifd-proto.sh
+ init_proto "$@"
+}
+
+proto_yggdrasil_init_config() {
+ proto_config_add_string "private_key"
+ available=1
+}
+
+proto_yggdrasil_setup_peer_if_non_interface() {
+ local peer_config="$1"
+ local peer_address
+ local peer_interface
+ config_get peer_address "${peer_config}" "address"
+ config_get peer_interface "${peer_config}" "interface"
+ if [ -z ${peer_interface} ]; then
+ json_add_string "" ${peer_address}
+ fi;
+}
+
+proto_yggdrasil_dump_peer_interface() {
+ local peer_config="$1"
+ local peer_interface
+
+ config_get peer_interface "${peer_config}" "interface"
+
+ if [ ! -z ${peer_interface} ]; then
+ peer_interfaces="${peer_interfaces}\n${peer_interface}"
+ fi;
+}
+
+proto_yggdrasil_setup_peer_if_interface() {
+ local peer_config="$1"
+ local peer_address
+ local peer_interface
+ config_get peer_interface "${peer_config}" "interface"
+ if [ "${peer_interface}" = "${peer_interface_filter}" ]; then
+ config_get peer_address "${peer_config}" "address"
+ json_add_string "" ${peer_address}
+ fi;
+}
+
+proto_yggdrasil_append_to_interface_regex() {
+ if [ -z "${regex}" ]; then
+ regex="$1"
+ else
+ regex="${regex}|$1";
+ fi;
+}
+
+proto_yggdrasil_setup_multicast_interface() {
+ local interface_config="$1"
+ local beacon
+ local listen
+ local port=0
+ local password
+ local regex=""
+
+ config_get beacon "${interface_config}" "beacon"
+ config_get listen "${interface_config}" "listen"
+ config_get port "${interface_config}" "port"
+ config_get password "${interface_config}" "password"
+
+ json_add_object ""
+ json_add_boolean "Beacon" $beacon
+ json_add_boolean "Listen" $listen
+ if [ ! -z ${port} ]; then
+ json_add_int "Port" $port
+ else
+ json_add_int "Port" 0
+ fi;
+ if [ ! -z ${password} ]; then
+ json_add_string "Password" $password
+ fi;
+
+ config_list_foreach "${interface_config}" interface proto_yggdrasil_append_to_interface_regex
+
+ json_add_string "Regex" "^(${regex})\$"
+
+ json_close_object
+}
+
+proto_yggdrasil_add_string() {
+ json_add_string "" $1
+}
+
+proto_yggdrasil_generate_keypair() {
+ json_load "$(yggdrasil -genconf -json)"
+ json_get_vars PublicKey PrivateKey
+ json_cleanup
+ private_key=$PrivateKey
+ public_key=$PublicKey
+}
+
+proto_yggdrasil_setup() {
+ local config="$1"
+ local device="$2"
+ local ygg_dir="/tmp/yggdrasil"
+ local ygg_cfg="${ygg_dir}/${config}.conf"
+ local ygg_sock="unix://${ygg_dir}/${config}.sock"
+
+
+ local private_key
+ local public_key
+ local mtu
+ local listen_addresses
+ local whitelisted_keys
+ local node_info
+ local node_info_privacy
+
+ config_load network
+ config_get private_key "${config}" "private_key"
+ config_get public_key "${config}" "public_key"
+ config_get mtu "${config}" "mtu"
+ config_get node_info "${config}" "node_info"
+ config_get node_info_privacy "${config}" "node_info_privacy"
+
+ if [ -z $private_key ]; then
+ proto_yggdrasil_generate_keypair
+ fi;
+
+ umask 077
+ mkdir -p "${ygg_dir}"
+
+ if [ $private_key = "auto" ]; then
+ proto_yggdrasil_generate_keypair
+ uci -t ${ygg_dir}/.uci.${config} batch <<EOF
+ set network.${config}.private_key='${private_key}'
+ set network.${config}.public_key='${public_key}'
+EOF
+ uci -t ${ygg_dir}/.uci.${config} commit;
+ fi;
+
+ # Generate config file
+ json_init
+ json_add_string "IfName" ${config}
+ json_add_string "AdminListen" ${ygg_sock}
+
+ json_add_string "PrivateKey" ${private_key}
+ json_add_string "PublicKey" ${public_key}
+
+ if [ ! -z $mtu ]; then
+ json_add_int "IfMTU" ${mtu}
+ fi;
+
+ if [ ! -z $node_info ]; then
+ json_add_string "NodeInfo" "%%_YGGDRASIL_NODEINFO_TEMPLATE_%%"
+ fi;
+
+ json_add_boolean "NodeInfoPrivacy" ${node_info_privacy}
+
+ # Peers
+ json_add_array "Peers"
+ config_foreach proto_yggdrasil_setup_peer_if_non_interface "yggdrasil_${config}_peer"
+ json_close_array
+
+ local peer_interfaces
+ peer_interfaces=""
+ config_foreach proto_yggdrasil_dump_peer_interface "yggdrasil_${config}_peer"
+ peer_interfaces=$(echo -e ${peer_interfaces} | sort | uniq)
+
+ json_add_object "InterfacePeers"
+ for peer_interface_filter in ${peer_interfaces}; do
+ json_add_array "${peer_interface_filter}"
+ config_foreach proto_yggdrasil_setup_peer_if_interface "yggdrasil_${config}_peer"
+ json_close_array
+ done
+ json_close_object
+
+ json_add_array "AllowedPublicKeys"
+ config_list_foreach "$config" allowed_public_key proto_yggdrasil_add_string
+ json_close_array
+
+ json_add_array "Listen"
+ config_list_foreach "$config" listen_address proto_yggdrasil_add_string
+ json_close_array
+
+ json_add_array "MulticastInterfaces"
+ config_foreach proto_yggdrasil_setup_multicast_interface "yggdrasil_${config}_interface"
+ json_close_array
+
+ json_dump > "${ygg_cfg}.1"
+ awk -v s='"%%_YGGDRASIL_NODEINFO_TEMPLATE_%%"' -v r="${node_info}" '{gsub(s, r)} 1' "${ygg_cfg}.1" > ${ygg_cfg}
+ rm "${ygg_cfg}.1"
+
+ proto_run_command "$config" /usr/sbin/yggdrasil -useconffile "${ygg_cfg}"
+ proto_init_update "$config" 1
+ proto_add_ipv6_address "$(yggdrasil -useconffile "${ygg_cfg}" -address)" "7"
+ proto_add_ipv6_prefix "$(yggdrasil -useconffile "${ygg_cfg}" -subnet)"
+ proto_send_update "$config"
+}
+
+proto_yggdrasil_teardown() {
+ local interface="$1"
+ proto_kill_command "$interface"
+}
+
+[ -n "$INCLUDE_ONLY" ] || {
+ add_protocol yggdrasil
+}
+++ /dev/null
-#!/usr/bin/env lua
-
-dkjson = require("dkjson")
-uci = require("uci")
-
-UCI = {}
-
---- Return the configuration defaults as a table suitable for JSON output
---
--- Mostly taken from yggdrasil -genconf -json
--- @return table with configuration defaults
-function UCI.defaults()
- return {
- AdminListen = "unix:///var/run/yggdrasil.sock", IfName = "ygg0",
- NodeInfoPrivacy = false,
- IfMTU = 65535,
-
- Peers = { }, Listen = { }, MulticastInterfaces = { }, AllowedPublicKeys = { },
- InterfacePeers = setmetatable({ }, {__jsontype = "object"}),
- NodeInfo = setmetatable({ }, {__jsontype = "object"})
- }
-end
-
---- Return the yggdrasil configuration as a table suitable for JSON output
---
--- @return table with yggdrasil configuration
-function UCI.get()
- local obj = UCI.defaults()
-
- local cursor = uci.cursor()
- local config = cursor:get_all("yggdrasil", "yggdrasil")
- if not config then return obj end
-
- obj.PublicKey = config.PublicKey
- obj.PrivateKey = config.PrivateKey
- obj.AdminListen = config.AdminListen or obj.AdminListen
- obj.IfName = config.IfName or obj.IfName
- obj.NodeInfo = dkjson.decode(config.NodeInfo) or obj.NodeInfo
- for _, v in pairs({ "NodeInfoPrivacy" }) do
- if config[v] ~= nil then obj[v] = to_bool(config[v]) end
- end
- if config["IfMTU"] ~= nil then obj["IfMTU"] = tonumber(config["IfMTU"]) end
-
- cursor:foreach("yggdrasil", "peer", function (s)
- table.insert(obj.Peers, s.uri)
- end)
- cursor:foreach("yggdrasil", "listen_address", function (s)
- table.insert(obj.Listen, s.uri)
- end)
- cursor:foreach("yggdrasil", "multicast_interface", function (s)
- table.insert(obj.MulticastInterfaces, {
- Beacon = to_bool(s.beacon), Listen = to_bool(s.listen),
- Port = tonumber(s.port), Regex = s.regex
- })
- end)
- cursor:foreach("yggdrasil", "allowed_public_key", function (s)
- table.insert(obj.AllowedPublicKeys, s.key)
- end)
-
- cursor:foreach("yggdrasil", "interface_peer", function (s)
- if obj.InterfacePeers[s.interface] == nil then
- obj.InterfacePeers[s.interface] = {}
- end
- table.insert(obj.InterfacePeers[s["interface"]], s.uri)
- end)
-
- return obj
-end
-
---- Parse and save updated configuration from JSON input
---
--- Transforms general settings into UCI sections, and replaces the UCI config's
--- contents with them.
--- @param table JSON input
--- @return Boolean whether saving succeeded
-function UCI.set(obj)
- local cursor = uci.cursor()
-
- for i, section in pairs(cursor:get_all("yggdrasil")) do
- cursor:delete("yggdrasil", section[".name"])
- end
-
-
- cursor:set("yggdrasil", "yggdrasil", "yggdrasil")
- cursor:set("yggdrasil", "yggdrasil", "PublicKey", obj.PublicKey)
- cursor:set("yggdrasil", "yggdrasil", "PrivateKey", obj.PrivateKey)
- cursor:set("yggdrasil", "yggdrasil", "AdminListen", obj.AdminListen)
- cursor:set("yggdrasil", "yggdrasil", "IfName", obj.IfName)
- cursor:set("yggdrasil", "yggdrasil", "NodeInfoPrivacy", to_int(obj.NodeInfoPrivacy))
- cursor:set("yggdrasil", "yggdrasil", "NodeInfo", dkjson.encode(obj.NodeInfo))
- cursor:set("yggdrasil", "yggdrasil", "IfMTU", obj.IfMTU)
-
- set_values(cursor, "peer", "uri", obj.Peers)
- set_values(cursor, "listen_address", "uri", obj.Listen)
-
- for _, interface in pairs(obj.MulticastInterfaces) do
- local name = cursor:add("yggdrasil", "multicast_interface")
- cursor:set("yggdrasil", name, "beacon", to_int(interface.Beacon))
- cursor:set("yggdrasil", name, "listen", to_int(interface.Listen))
- cursor:set("yggdrasil", name, "port", interface.Port)
- cursor:set("yggdrasil", name, "regex", interface.Regex)
- end
-
- set_values(cursor, "allowed_public_key", "key", obj.AllowedPublicKeys)
-
- for interface, peers in pairs(obj.InterfacePeers) do
- for _, v in pairs(peers) do
- local name = cursor:add("yggdrasil", "interface_peer")
- cursor:set("yggdrasil", name, "interface", interface)
- cursor:set("yggdrasil", name, "uri", v)
- end
- end
-
- return cursor:commit("yggdrasil")
-end
-
-function set_values(cursor, section_name, parameter, values)
- if values == nil then return false end
-
- for k, v in pairs(values) do
- local name = cursor:add("yggdrasil", section_name)
- cursor:set("yggdrasil", name, parameter, v)
- end
-end
-
-function to_int(bool) return bool and '1' or '0' end
-
-function to_bool(int) return int ~= '0' end
-
-function help()
- print("JSON interface to /etc/config/yggdrasil\n\nExamples: \
- ygguci get > /tmp/etc/yggdrasil.conf \
- cat /tmp/etc/yggdrasil.conf | ygguci set \
- uci changes \
- ygguci get | yggdrasil -useconf")
-end
-
--- main
-
-if arg[1] == "get" then
- local json = dkjson.encode(UCI.get(), { indent = true })
- print(json)
-elseif arg[1] == "set" then
- local json = io.stdin:read("*a")
- local obj, pos, err = dkjson.decode(json, 1, nil)
-
- if obj then
- UCI.set(obj)
- else
- print("dkjson: " .. err)
- os.exit(1)
- end
-else
- help()
-end