aboutsummaryrefslogblamecommitdiff
path: root/dist/netifd/wgconfd.sh
blob: af3452583224c9c4d9dcc7643102ca1457d16f18 (plain) (tree)
































































                                                                 


                                                                         











































                                                                                   


                                                                   


                                                              


                                                                    




































































































                                                                                                                                    
#!/bin/sh

WG=/usr/bin/wg
if [ ! -x "$WG" ]; then
        logger -t "wgconfd" "error: missing wgconfd (${WG})"
        exit 1
fi

CURL=/usr/bin/curl
if [ ! -x "$CURL" ]; then
        logger -t "wgconfd" "error: missing curl (${CURL})"
        exit 1
fi

WGCONFD=/usr/bin/wgconfd
if [ ! -x "$WGCONFD" ]; then
        logger -t "wgconfd" "error: missing wgconfd (${WGCONFD})"
        exit 1
fi

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

proto_wgconfd_init_config() {
	proto_config_add_array 'ipaddr:ipaddr'
	proto_config_add_array 'ip6addr:ip6addr'
	proto_config_add_int 'mtu'

	proto_config_add_string 'private_key'
	proto_config_add_int 'listen_port'
	proto_config_add_string 'fwmark'

	proto_config_add_int 'refresh_sec'
	proto_config_add_int 'min_keepalive'
	proto_config_add_int 'max_keepalive'

	available=1
}

proto_wgconfd_setup__print() {
	local i
	for i; do
		# TODO: escape
		echo -n "$i "
	done
}

proto_wgconfd_setup__source() {
	local name val

	config_get name "$1" name
	[ -z "$name" ] && return
	config_get val "$1" url
	[ -z "$val" ] && return
	proto_wgconfd_setup__print source "$name" "$val"

	config_get val "$1" psk
	[ -n "$val" ] && proto_wgconfd_setup__print psk "$val"

	config_get_bool val "$1" required 0
	[ "$val" -eq 1 ] && proto_wgconfd_setup__print required

	config_get_bool val "$1" allow_road_warriors 1
	[ "$val" -eq 0 ] && proto_wgconfd_setup__print deny_road_warriors

	config_list_foreach "$1" ipv4 proto_wgconfd_setup__source_route ipv4 32

	config_list_foreach "$1" ipv6 proto_wgconfd_setup__source_route ipv6 128
}

proto_wgconfd_setup__source_route() {
	local p="$2"
	local maxlen="$3"
	local route=1
	set -- $1
	local r="$1"
	shift 1
	local i
	for i; do case "$i" in
		no-route)
			route=0
			;;
		*)
			true
			;;
	esac; done
	proto_wgconfd_setup__print "$p" "$r"
	if [ "$route" -eq 1 ]; then
		case "$r" in
			'')
				true
				;;
			*/*)
				echo "${p}_route ${r%/*} ${r##*/}" >> "$dir/update"
				;;
			*)
				echo "${p}_route $r $maxlen" >> "$dir/update"
				;;
		esac
	fi
}

proto_wgconfd_setup__peer() {
	local val

	config_get val "$1" key
	[ -z "$val" ] && return
	proto_wgconfd_setup__print public_key "$val"

	config_get val "$1" endpoint
	[ -n "$val" ] && proto_wgconfd_setup__print endpoint "$val"

	config_get val "$1" psk
	[ -n "$val" ] && proto_wgconfd_setup__print psk "$val"

	config_get val "$1" keepalive
	[ -n "$val" ] && proto_wgconfd_setup__print keepalive "$val"

	config_get val "$1" source
	[ -n "$val" ] && proto_wgconfd_setup__print source "$val"
}

proto_wgconfd__echo_addr() {
	case "$1" in
		'')
			true
			;;
		*/*)
			echo "${3}_address ${1%/*} ${1##*/}" >> "$dir/update"
			;;
		*)
			echo "${3}_address $1 $4" >> "$dir/update"
			;;
	esac
}

proto_wgconfd_setup() {
	local interface="$1" ifname="$2" i r
	if [ -z "$ifname" ]; then
		ifname="$interface"
	fi

	local mtu
	local private_key listen_port fwmark
	local refresh_sec min_keepalive max_keepalive
	json_get_vars mtu private_key listen_port fwmark refresh_sec min_keepalive max_keepalive

	if [ -z "$private_key" ]; then
		proto_notify_error "$interface" NO_PRIVATE_KEY
		proto_block_restart "$interface"
		exit
	fi

	[ -n "$fwmark" ] && fwmark="fwmark $fwmark"

	dir="/tmp/wgconfd/$interface"
	if [ -d "$dir" ]; then
		rm -rf "$dir"
	fi
	mkdir -p /tmp/wgconfd
	if ! mkdir -m 0700 "$dir" || ! mkdir "$dir/cache" || ! echo "$private_key" > "$dir/private" || ! true > "$dir/update" ; then
		proto_notify_error "$interface" FS_ERROR
		return 1
	fi

	json_for_each_item proto_wgconfd__echo_addr ipaddr ipv4 32
	json_for_each_item proto_wgconfd__echo_addr ip6addr ipv6 128

	wgconfd_command="$(
		proto_wgconfd_setup__print "$WGCONFD" --cmdline "$ifname"
		config_load network
		config_foreach proto_wgconfd_setup__source wgconfd_source_"$interface"
		config_foreach proto_wgconfd_setup__peer wgconfd_peer_"$interface"
	)"

	ip link del dev "$ifname" 2>/dev/null
	if ! ip link add dev "$ifname" mtu "${mtu:-1420}" type wireguard; then
		proto_notify_error "$interface" IFACE_ERROR
		exit
	fi

	"$WG" set "$ifname" private-key "$dir/private" listen-port "${listen_port:-656}" $fwmark
	r="$?"
	rm -f "$dir/private"
	if [ "$r" != 0 ]; then
		ip link del dev "$ifname" 2>/dev/null
		proto_notify_error "$interface" WG_ERROR
		exit
	fi

	proto_init_update "$ifname" 1 0
	proto_set_keep 0
	while read i r; do
		proto_add_"$i" $r
	done < "$dir/update"
	# rm -f "$dir/update"
	proto_send_update "$interface"


	proto_export "WG=$WG"
	proto_export "CURL=$CURL"
	proto_export "RUNTIME_DIRECTORY=$dir"
	proto_export "CACHE_DIRECTORY=$dir/cache"
	proto_run_command "$interface" $wgconfd_command
}

proto_wgconfd_teardown() {
	local interface="$1" ifname="$2" i r
	if [ -z "$ifname" ]; then
		ifname="$interface"
	fi

	proto_kill_command "$interface"
	ip link del dev "$ifname" 2>/dev/null
}

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