aboutsummaryrefslogtreecommitdiff
blob: 82649b9ac37e7e0a6c4a1062f96d141122aadfd8 (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
# Copyright (c) 2016 Emeric Verschuur <emeric@mbedsys.org>
# All rights reserved. Released under the 2-clause BSD license.
# shellcheck shell=sh disable=SC1008

l2tp_depend()
{
	program ip
	before bridge interface macchanger
}

# Extract parameter list to shell vars
#   1. variable prefix
#   2. string to parse
_l2tp_eval_props() {
	local prop_pref=$1
	local prop_list=$2
	eval set -- "$3"
	while [ -n "$1" ]; do
		eval "case $1 in
			$prop_list)
				$prop_pref$1=\"$2\"
				shift
				shift
				;;
			*)
				l2tp_err=\"invalid property $1\"
				return 1
				;;
			
		esac" || return 1
	done
	return 0
}

_is_l2tp() {
	# Check for L2TP support in kernel
	ip l2tp show session 2>/dev/null 1>/dev/null || return 1

	eval "$(ip l2tp show session | \
		awk "match(\$0, /^Session ([0-9]+) in tunnel ([0-9]+)\$/, ret) {sid=ret[1]; tid=ret[2]} 
		match(\$0, /^[ ]*interface name: ${IFACE}\$/) {print \"session_id=\"sid\";tunnel_id=\"tid; exit}")"
	test -n "$session_id"
}

# Get tunnel info
#    1. Output variable prefix
#    2. Tunnel ID to find
_l2tp_get_tunnel_info() {
	local found
	eval "$(ip l2tp show tunnel | \
		awk -v id=$2 -v prefix=$1 '
		match($0, /^Tunnel ([0-9]+), encap (IP|UDP)$/, ret) {
			if (found == "1") exit;
			if (ret[1] == id) {
				print "found=1;"
				print prefix "tunnel_id=" ret[1] ";"
				print prefix "encap=" ret[2] ";";
				found="1"
			}
		} 
		match($0, /^[ ]*From ([^ ]+) to ([^ ]+)$/, ret) {
			if (found == "1") {
				print prefix "local=" ret[1] ";"; 
				print prefix "remote=" ret[2] ";"; 
			}
		}
		match($0, /^[ ]*Peer tunnel ([0-9]+)$/, ret) {
			if (found == "1") {
				print prefix "peer_tunnel_id=" ret[1] ";"; 
			}
		}
		match($0, /^[ ]*UDP source \/ dest ports: ([0-9]+)\/([0-9]+)$/, ret) {
			if (found == "1") {
				print prefix "udp_sport=" ret[1] ";"; 
				print prefix "udp_dport=" ret[2] ";"; 
			}
		}')"
	test -n "$found"
}

_ip_l2tp_add() {
	local e
	e="$(LC_ALL=C ip l2tp add "$@" 2>&1 1>/dev/null)"
	case $e in
		"")
			return 0
			;;
		"RTNETLINK answers: No such process")
			# seems to not be a fatal error but I don't know why I have this error... hmmm
			ewarn "ip l2tp add $2 error: $e"
			return 0
			;;
		*)
			eend 1 "ip l2tp add $2 error: $e"
			return 1
			;;
	esac
	
}

l2tp_pre_start()
{
	local l2tpsession=
	eval l2tpsession=\$l2tpsession_${IFVAR}
	test -n "${l2tpsession}" || return 0
	
	ebegin "Creating L2TPv3 link ${IFVAR}"
	local l2tp_err s_name s_tunnel_id s_session_id s_peer_session_id s_cookie s_peer_cookie s_offset s_peer_offset s_l2spec_type
	if ! _l2tp_eval_props s_ "name|tunnel_id|session_id|peer_session_id|cookie|peer_cookie|offset|peer_offset|l2spec_type" "${l2tpsession}"; then
		eend 1 "l2tpsession_${IFVAR} syntax error: $l2tp_err"
		return 1
	fi
	if [ -n "$s_name" ]; then
		eend 1 "l2tpsession_${IFVAR} error: please remove the \"name\" parameter (this parameter is managed by the system)"
		return 1
	fi
	# Try to load mendatory l2tp_eth kernel module
	if ! modprobe l2tp_eth; then
		eend 1 "l2tp_eth module not present in your kernel (please enable CONFIG_L2TP_ETH option in your kernel config)"
		return 1
	fi
	local l2tptunnel=
	eval l2tptunnel=\$l2tptunnel_${IFVAR}
	if [ -n "${l2tptunnel}" ]; then
		local t_tunnel_id t_encap t_local t_remote t_peer_tunnel_id t_udp_sport t_udp_dport
		_l2tp_eval_props t_ "remote|local|encap|tunnel_id|peer_tunnel_id|encap|udp_sport|udp_dport" "${l2tptunnel}"
		# if encap=ip we need l2tp_ip kernel module
		if [ "${t_encap^^}" = "IP" ] && ! modprobe l2tp_ip; then
			eend 1 "l2tp_ip module not present in your kernel (please enable CONFIG_L2TP_IP option in your kernel config)"
			return 1
		fi
		# Search for an existing tunnel with the same ID
		local f_tunnel_id f_encap f_local f_remote f_peer_tunnel_id f_udp_sport f_udp_dport
		if _l2tp_get_tunnel_info f_ $t_tunnel_id; then
			# check if the existing tunnel has the same property than expected
			if [ "tunnel_id:$f_tunnel_id;encap:$f_encap;local:$f_local;remote:$f_remote;
			peer_tunnel_id:$f_peer_tunnel_id;udp_sport:$f_udp_sport;udp_dport:$f_udp_dport" \
			!= "tunnel_id:$t_tunnel_id;encap:${t_encap^^};local:$t_local;remote:$t_remote;
			peer_tunnel_id:$t_peer_tunnel_id;udp_sport:$t_udp_sport;udp_dport:$t_udp_dport" ]; then
				eend 1 "There are an existing tunnel with id=$s_tunnel_id, but the properties mismatch with the one you want to create"
				return 1
			fi
		else
			veinfo ip l2tp add tunnel ${l2tptunnel}
			_ip_l2tp_add tunnel ${l2tptunnel} || return 1
		fi
	elif ! ip l2tp show tunnel | grep -Eq "^Tunnel $s_tunnel_id,"; then
		# no l2tptunnel_<INTF> declaration, assume that the tunnel is already present
		# checking if tunnel_id exists otherwise raise an error
		eend 1 "Tunnel id=$s_tunnel_id no found (you may have to set l2tptunnel_${IFVAR})"
		return 1
	fi
	veinfo ip l2tp add session ${l2tpsession} name "${IFACE}"
	_ip_l2tp_add session ${l2tpsession} name "${IFACE}" || return 1
	_up
}


l2tp_post_stop()
{
	local session_id tunnel_id
	_is_l2tp || return 0
	
	ebegin "Destroying L2TPv3 link ${IFACE}"
	veinfo ip l2tp del session tunnel_id $tunnel_id session_id $session_id
	ip l2tp del session tunnel_id $tunnel_id session_id $session_id
	if ! ip l2tp show session | grep -Eq "^Session [0-9]+ in tunnel $tunnel_id\$"; then
		#tunnel $tunnel_id no longer used, destoying it...
		veinfo ip l2tp del tunnel tunnel_id $tunnel_id
		ip l2tp del tunnel tunnel_id $tunnel_id
	fi
	eend $?
}