aboutsummaryrefslogtreecommitdiff
path: root/net/yggdrasil/files/yggdrasil.sh
blob: 3185f204d8f42c19c774d8dc7a784a7b6861a053 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#!/bin/sh


[ -n "$INCLUDE_ONLY" ] || {
	. /lib/functions.sh
	. ../netifd-proto.sh
	init_proto "$@"
}

proto_yggdrasil_init_config() {
	available=1

	# Yggdrasil
	proto_config_add_string "private_key"
	proto_config_add_boolean "allocate_listen_addresses"

	# Jumper
	proto_config_add_boolean "jumper_enable"
	proto_config_add_string "jumper_loglevel"
	proto_config_add_boolean "jumper_autofill_listen_addresses"
	proto_config_add_string "jumper_config"
}

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 PrivateKey
	json_cleanup
	private_key=$PrivateKey
	public_key=${PrivateKey:64}
}

proto_yggdrasil_allocate_listen_addresses() {
	local config="$1"

	# Collect already defined protocols
	protocols=""
	_add_address_protocol() {
		protocols="${protocols}$(echo $1 | cut -d "://" -f1) "
	}
	config_list_foreach "$config" listen_address _add_address_protocol

	# Add new address for each previously unspecified protocol
	for protocol in "tls" "quic"; do
		if ! echo "$protocols" | grep "$protocol" &>/dev/null; then
			# By default linux dynamically alocates ports in the range 32768..60999
			# `sysctl net.ipv4.ip_local_port_range`
			random_port=$(( ($RANDOM + $RANDOM) % 22767 + 10000 ))
			proto_yggdrasil_add_string "${protocol}://127.0.0.1:${random_port}"
		fi
	done
}

proto_yggdrasil_generate_jumper_config() {
	local config="$1"
	local ygg_sock="$2"
	local ygg_cfg="$3"

	# Autofill Yggdrasil listeners
	config_get is_autofill_listeners "$config" "jumper_autofill_listen_addresses"
	if [ "$is_autofill_listeners" == "1" ]; then
		echo "yggdrasil_listen = ["
		_print_address() {
			echo "\"${1}\","
		}
		json_load_file "${ygg_cfg}"
		json_for_each_item _print_address "Listen"
		echo "]"
	fi

	# Print admin api socket
	echo "yggdrasil_admin_listen = [ \"${ygg_sock}\" ]"

	# Print extra config
	config_get jumper_config "$config" "jumper_config"
	echo "${jumper_config}"
}

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

	# If needed, add new address for each previously unspecified protocol
	config_get is_jumper_enabled "$config" "jumper_enable"
	config_get allocate_listen_addresses "$config" "allocate_listen_addresses"
	if [ "$is_jumper_enabled" == "1" ] && [ "$allocate_listen_addresses" == "1" ]; then
		proto_yggdrasil_allocate_listen_addresses "$config"
	fi

	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"

	# Start jumper if needed
	config_get is_jumper_enabled "$config" "jumper_enable"
	if [ "$is_jumper_enabled" == "1" ] && [ -f /usr/sbin/yggdrasil-jumper ]; then
		jumper_cfg="${ygg_dir}/${config}-jumper.conf"
		proto_yggdrasil_generate_jumper_config "$config" "$ygg_sock" "$ygg_cfg" > "$jumper_cfg"

		config_get jumper_loglevel "$config" "jumper_loglevel"
		sh -c "sleep 2 && exec /usr/sbin/yggdrasil-jumper --loglevel \"${jumper_loglevel:-info}\" --config \"$jumper_cfg\" 2&>1 | logger -t \"${config}-jumper\"" &
	fi
}

proto_yggdrasil_teardown() {
	local interface="$1"
	proto_kill_command "$interface"
}

[ -n "$INCLUDE_ONLY" ] || {
	add_protocol yggdrasil
}