aboutsummaryrefslogtreecommitdiffstats
path: root/setup-dmvpn
blob: 0e7d9a22b8f661027630412230673e9e7cbd7f0d (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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#!/bin/sh -e

# Dynamic Multipoint VPN setup script for Alpine Linux
# Copyright (c) 2017-2020 Kaarle Ritvanen
# See LICENSE file for license details


. /lib/libalpine.sh

if [ -z "$1" ]; then
	echo "Usage: $0 <pfx_file>" >&2
	exit 1
fi


ATTRS=$(/usr/libexec/dmvpn-pfx-decode "$1")
eval $ATTRS

for attr in GRE_IPV4_ADDRESS HUBS VPNC_TYPE; do
	eval "[ \"\$$attr\" ]" || die "attribute not defined: $attr"
done


ask "NHRP network ID" 1
NHRP_ID=$resp

NFLOG_GROUP=
if [ $VPNC_TYPE = hub ]; then
	ask "NFLOG group" 1
	NFLOG_GROUP=$resp

	ask "DMVPN site IPv4 prefix length" 16
	SITE_PREFIX_LEN_IPV4=$resp

	if [ "$GRE_IPV6_ADDRESS" ]; then
		ask "DMVPN site IPv6 prefix length" 48
		SITE_PREFIX_LEN_IPV6=$resp
	fi
fi


GRE_MODULE=nf_conntrack_proto_gre
PMTU_SYSCTL=net.ipv4.ip_forward_use_pmtu


get_dev() {
	sed -E "s/^$* (.+ )?dev ([^ ]+)( .+)?\$/\\2/;ta;d;:a"
}

get_local_dev() {
	ip route list table local | get_dev local $1
}

is_active() {
	rc-service $1 status > /dev/null
}

enable_service() {
	if is_active $1; then
		rc-service $1 restart
	else
		rc-update add $1
		rc-service $1 start
	fi
}

enable_firewall() {
	augtool -s <<EOF
set /files/etc/conf.d/$1/IPFORWARD yes
set /files/etc/conf.d/$1/SAVE_ON_STOP no
EOF
	enable_service $1
}

get_config_cmds() {
	local p=$1
	shift
	while [ $# -gt 0 ]; do
		[ "$1" ] && echo $p $1
		shift
	done
}

get_nhrp_config() {
	(
		IFS=$'\n'
		get_config_cmds "$1 nhrp" \
			"network-id $NHRP_ID" \
			shortcut \
			"registration no-unique" \
			"${NFLOG_GROUP:+redirect}" \
			$(get_config_cmds "nhs dynamic nbma" $HUBS)
	)
}

get_route_map_config() {
	cat <<EOF
	route-map RTT-$1 permit 10
		set metric $2rtt
	exit
EOF
}

get_peer_config() {
	local group=$1
	local map=RTT-$2
	shift 2
	get_config_cmds "neighbor $group" \
		peer-group \
		"ebgp-multihop 1" \
		disable-connected-check \
		"timers 10 30" \
		"next-hop-self all" \
		"soft-reconfiguration inbound" \
		"route-map $map in" \
		"$@"
}

get_spoke_config() {
	local group=spoke-$1
	shift
	get_peer_config $group SET "$@" \
		passive \
		"advertisement-interval 1" \
		"prefix-list no-hosts out"
}

get_quagga_config() {
	cat <<EOF
	configure terminal
		nhrp event socket /var/run/nhrp-events.sock
		${NFLOG_GROUP:+nhrp nflog-group $NFLOG_GROUP}
		interface $GRE_IFACE
			tunnel protection vici profile dmvpn
EOF
	get_nhrp_config ip
	[ "$GRE_IPV6_ADDRESS" ] && get_nhrp_config ipv6
	cat <<EOF
		exit
		no router bgp $AS_NUMBER
		router bgp $AS_NUMBER
			bgp router-id $GRE_IPV4_ADDRESS
EOF
	get_config_cmds network $IPV4_PREFIXES
	if [ "$IPV6_PREFIXES" ]; then
		echo address-family ipv6
		get_config_cmds network $IPV6_PREFIXES
		echo exit
	fi
	echo exit
	if [ $VPNC_TYPE = hub ]; then
		get_route_map_config ADD +
		get_route_map_config SET
		cat <<EOF
		no ip prefix-list dmvpn
		no ip prefix-list no-hosts
EOF
		for p in $IPV4_PREFIXES; do
			echo "ip prefix-list dmvpn permit $p le 32"
			echo "ip prefix-list no-hosts permit $p le 30"
		done
		cat <<EOF
		router bgp $AS_NUMBER
			redistribute nhrp
			redistribute kernel
EOF
		get_peer_config hubs ADD \
			"remote-as $AS_NUMBER" \
			"timers connect 10" \
			"prefix-list dmvpn out"
		get_spoke_config ebgp "attribute-unchanged med"
		get_spoke_config ibgp \
			"remote-as $AS_NUMBER" \
			route-reflector-client
		echo exit
	fi
	cat <<EOF
	exit
	write memory
EOF
}


GRE_IFACE=$(get_local_dev $GRE_IPV4_ADDRESS)

if [ -z "$GRE_IFACE" ]; then
	ask_which interface "shall be used for GRE transport" \
		"$(ls /sys/class/net)" \
		$(for h in $HUBS; do
			if expr ${h##*.} : '[a-zA-Z][a-zA-Z0-9]*$' \
				> /dev/null; then

				host $h | sed -E 's/^.+ has address //;ta;d;:a'
			else
				echo $h
			fi
		done | while read addr; do

			if [ -z "$(get_local_dev $addr)" ]; then
				ip route get $addr | get_dev $addr
				break
			fi
		done)
	TRANSPORT_IFACE=$resp

	i=1
	while [ -d /sys/class/net/gre$i ]; do
		: $(( i++ ))
	done

	ask "GRE tunnel interface" gre$i
	GRE_IFACE=$resp

	echo "$PMTU_SYSCTL = 1" > /etc/sysctl.d/dmvpn.conf
	sysctl -w $PMTU_SYSCTL=1

	cat >> /etc/network/interfaces <<EOF

auto $GRE_IFACE
iface $GRE_IFACE inet static
	address $GRE_IPV4_ADDRESS
	netmask 255.255.255.255
	tunnel-mode gre
	tunnel-dev $TRANSPORT_IFACE
	tunnel-ttl 64
EOF

	if [ "$GRE_IPV6_ADDRESS" ]; then
		cat >> /etc/network/interfaces <<EOF
iface $GRE_IFACE inet6 static
	address $GRE_IPV6_ADDRESS
	netmask 128
EOF
	fi

	ifup $GRE_IFACE
fi


augtool -s "set /files/etc/conf.d/nhrpd/rc_need '\"charon nhrp-events\"'"

for serv in charon zebra; do
	is_active $serv && rc-service $serv stop
done

for serv in bgpd nhrpd zebra; do
	file=/etc/quagga/$serv.conf
	touch $file
	chown quagga $file
done

enable_service nhrpd

vtysh -c "$(get_quagga_config)"


if [ -f /etc/iptables/awall-save -o "$NFLOG_GROUP" ]; then
	apk add awall
	echo "{ \"variable\": { \"dmvpn_gre_iface\": \"$GRE_IFACE\" } }" > \
		/etc/awall/dmvpn-config.json

	if [ "$NFLOG_GROUP" ]; then
		cat > /etc/nhrp-events.conf <<EOF
max-prefix-length:
  ip: $SITE_PREFIX_LEN_IPV4
EOF
		[ "$SITE_PREFIX_LEN_IPV6" ] && \
			cat >> /etc/nhrp-events.conf <<EOF
  ipv6: $SITE_PREFIX_LEN_IPV6
EOF

		(
			PREFIX="set /files/etc/awall/dmvpn-config.json/dict/entry/dict/entry"
			cat <<EOF
$PREFIX[2] dmvpn_nflog_group
$PREFIX[2]/number $NFLOG_GROUP
$PREFIX[3] dmvpn_site_mask
$PREFIX[3]/dict/entry inet
$PREFIX[3]/dict/entry/number $SITE_PREFIX_LEN_IPV4
EOF
			[ "$SITE_PREFIX_LEN_IPV6" ] && cat <<EOF
$PREFIX[3]/dict/entry[2] inet6
$PREFIX[3]/dict/entry[2]/number $SITE_PREFIX_LEN_IPV6
EOF
		) | augtool -s
		awall enable dmvpn-hub
	else
		awall enable dmvpn
	fi

	awall translate

	if modprobe $GRE_MODULE; then
		echo $GRE_MODULE > /etc/modules-load.d/dmvpn.conf
	fi

	enable_firewall iptables
	[ -f /etc/iptables/rules6-save -o "$SITE_PREFIX_LEN_IPV6" ] && \
		enable_firewall ip6tables
fi