Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 890238 | Differences between
and this patch

Collapse All | Expand All

(-)a/net/l2tp.sh (-144 / +206 lines)
Lines 1-6 Link Here
1
# Copyright (c) 2016 Emeric Verschuur <emeric@mbedsys.org>
1
# Copyright (c) 2016 Emeric Verschuur <emeric@mbedsys.org>
2
# Copyright (c) 2023 Kerin Millar <kfm@plushkava.net>
2
# All rights reserved. Released under the 2-clause BSD license.
3
# All rights reserved. Released under the 2-clause BSD license.
3
# shellcheck shell=sh disable=SC1008
4
5
# Don't complain about local, even though POSIX does not define its behaviour.
6
# This is unwise but, as things stand, it is being used extensively by netifrc.
7
# Also, SC2034 and SC2316 are muted because they produce false-positives.
8
# shellcheck shell=sh disable=SC3043,SC2034,SC2316
4
9
5
l2tp_depend()
10
l2tp_depend()
6
{
11
{
Lines 8-173 l2tp_depend() Link Here
8
	before bridge interface macchanger
13
	before bridge interface macchanger
9
}
14
}
10
15
11
# Extract parameter list to shell vars
16
_l2tp_parse_opts()
12
#   1. variable prefix
17
{
13
#   2. string to parse
18
	# Parses lt2psession or l2tptunnel options using xargs(1), conveying
14
_l2tp_eval_props() {
19
	# them as arguments to awk(1). The awk program interprets the arguments
15
	local prop_pref=$1
20
	# as a series of key/value pairs and safely prints those specified as
16
	local prop_list=$2
21
	# being required as variable declarations for evaluation by sh(1).
17
	eval set -- "$3"
22
	# Other keys are handled similarly, only in a way that renders them a
18
	while [ -n "$1" ]; do
23
	# no-op. For the program to exit successfully, all key names must be
19
		eval "case $1 in
24
	# well-formed, all required keys must be seen, and all values must be
20
			$prop_list)
25
	# non-blank. Note that assigning 1 to ARGC prevents awk from treating
21
				$prop_pref$1=\"$2\"
26
	# its arguments as the names of files to be opened.
22
				shift
27
	printf %s "$1" \
23
				shift
28
	| LC_CTYPE=C xargs -E '' awk -v q="'" -v required_keys="$2" -v other_keys="$3" '
24
				;;
29
		function shquote(str) {
25
			*)
30
			gsub(q, q "\\" q q, str)
26
				l2tp_err=\"invalid property $1\"
31
			return q str q
27
				return 1
32
		}
28
				;;
33
		BEGIN {
29
			
34
			argc = ARGC
30
		esac" || return 1
35
			ARGC = 1
31
	done
36
			gsub(" ", "|", required_keys)
32
	return 0
37
			gsub(" ", "|", other_keys)
38
			re = "^(" required_keys "|" other_keys ")$"
39
			sorter = "sort"
40
			for (i = 1; i < argc; i += 2) {
41
				key = ARGV[i]
42
				val = ARGV[i + 1]
43
				if (key !~ /^[[:alpha:]][_[:alnum:]]+$/) {
44
					system("ewarn " shquote("Skipping malformed parameter: " key))
45
				} else if (key ~ re) {
46
					print key "=" shquote(val) | sorter
47
					val_by[key] = val
48
				} else {
49
					print ": " key "=" shquote(val) | sorter
50
				}
51
			}
52
			close(sorter)
53
			split(required_keys, keys, "|")
54
			missing = 0
55
			for (i in keys) {
56
				key = keys[i]
57
				if (! (key in val_by)) {
58
					system("eerror " shquote("The \"" key "\" parameter is missing"))
59
					missing += 1
60
				} else if (val_by[key] ~ /^[[:blank:]]*$/) {
61
					system("eerror " shquote("The \"" key "\" parameter has a blank value"))
62
					missing += 1
63
				}
64
			}
65
			exit(!!missing)
66
		}
67
	'
33
}
68
}
34
69
35
_is_l2tp() {
70
_l2tp_parse_existing_session() {
36
	# Check for L2TP support in kernel
71
	ip l2tp show session \
37
	ip l2tp show session 2>/dev/null 1>/dev/null || return 1
72
	| LC_CTYPE=C awk -v iface="${IFACE:?}" '
38
73
		BEGIN { found = 0 }
39
	eval "$(ip l2tp show session | \
74
		/^Session [0-9]+ in tunnel [0-9]+$/ {
40
		awk "match(\$0, /^Session ([0-9]+) in tunnel ([0-9]+)\$/, ret) {sid=ret[1]; tid=ret[2]} 
75
			session_id = $2
41
		match(\$0, /^[ ]*interface name: ${IFACE}\$/) {print \"session_id=\"sid\";tunnel_id=\"tid; exit}")"
76
			tunnel_id = $5
42
	test -n "$session_id"
77
		}
78
		/^[[:blank:]]*interface name:/ && "" $NF == "" iface {
79
			print "session_id=" session_id
80
			print "tunnel_id=" tunnel_id
81
			found = 1
82
			exit
83
		}
84
		END { exit(!found) }
85
	'
43
}
86
}
44
87
45
# Get tunnel info
88
46
#    1. Output variable prefix
89
_l2tp_parse_existing_tunnel() {
47
#    2. Tunnel ID to find
90
	ip l2tp show tunnel \
48
_l2tp_get_tunnel_info() {
91
	| LC_CTYPE=C awk -v q="'" -v id="$1" '
49
	local found
92
		function shquote(str) {
50
	eval "$(ip l2tp show tunnel | \
93
			gsub(q, q "\\" q q, str)
51
		awk -v id=$2 -v prefix=$1 '
94
			return q str q
52
		match($0, /^Tunnel ([0-9]+), encap (IP|UDP)$/, ret) {
53
			if (found == "1") exit;
54
			if (ret[1] == id) {
55
				print "found=1;"
56
				print prefix "tunnel_id=" ret[1] ";"
57
				print prefix "encap=" ret[2] ";";
58
				found="1"
59
			}
60
		} 
61
		match($0, /^[ ]*From ([^ ]+) to ([^ ]+)$/, ret) {
62
			if (found == "1") {
63
				print prefix "local=" ret[1] ";"; 
64
				print prefix "remote=" ret[2] ";"; 
65
			}
66
		}
95
		}
67
		match($0, /^[ ]*Peer tunnel ([0-9]+)$/, ret) {
96
		BEGIN {
68
			if (found == "1") {
97
			found = 0
69
				print prefix "peer_tunnel_id=" ret[1] ";"; 
98
			sorter = "sort"
70
			}
71
		}
99
		}
72
		match($0, /^[ ]*UDP source \/ dest ports: ([0-9]+)\/([0-9]+)$/, ret) {
100
		/^Tunnel [0-9]+, encap (IP|UDP)$/ {
73
			if (found == "1") {
101
			if (found) exit
74
				print prefix "udp_sport=" ret[1] ";"; 
102
			tunnel_id = substr($2, 0, length($2) - 1)
75
				print prefix "udp_dport=" ret[2] ";"; 
103
			if (tunnel_id == id) {
104
				found = 1
105
				print "tunnel_id=" shquote(tunnel_id) | sorter
106
				print "encap=" shquote(tolower($4)) | sorter
76
			}
107
			}
77
		}')"
108
		}
78
	test -n "$found"
109
		found && /^[[:blank:]]*From [^[:blank:]]+ to [^[:blank:]]+$/ {
110
			print "local=" shquote($2) | sorter
111
			print "remote=" shquote($4) | sorter
112
		}
113
		found && /^[[:blank:]]*Peer tunnel [0-9]+$/ {
114
			print "peer_tunnel_id=" shquote($NF) | sorter
115
		}
116
		found && /^[[:blank:]]*UDP source \/ dest ports: [0-9]+\/[0-9]+$/ {
117
			split($NF, ports, "/")
118
			print ": udp_sport=" shquote(ports[1]) | sorter
119
			print ": udp_dport=" shquote(ports[2]) | sorter
120
		}
121
		END {
122
			close(sorter)
123
			exit(!found)
124
		}
125
	'
79
}
126
}
80
127
81
_ip_l2tp_add() {
128
_l2tp_should_add_tunnel() {
82
	local e
129
	local existing_tunnel
83
	e="$(LC_ALL=C ip l2tp add "$@" 2>&1 1>/dev/null)"
130
84
	case $e in
131
	if ! existing_tunnel=$(_l2tp_parse_existing_tunnel "$1"); then
85
		"")
132
		return 0
86
			return 0
133
	elif [ "$2" = "${existing_tunnel}" ]; then
87
			;;
134
		return 1
88
		"RTNETLINK answers: No such process")
135
	else
89
			# seems to not be a fatal error but I don't know why I have this error... hmmm
136
		return 2
90
			ewarn "ip l2tp add $2 error: $e"
137
	fi
91
			return 0
92
			;;
93
		*)
94
			eend 1 "ip l2tp add $2 error: $e"
95
			return 1
96
			;;
97
	esac
98
	
99
}
138
}
100
139
140
_l2tp_has_tunnel() {
141
	_l2tp_parse_existing_tunnel "$1" >/dev/null
142
}
143
144
_l2tp_in_session() {
145
	ip l2tp show session | {
146
		LC_CTYPE=C
147
		while read -r line; do
148
			case ${line} in
149
				"Session "*" in tunnel $1") return 0
150
			esac
151
		done
152
	}
153
	return 1
154
}
155
156
_is_blank() (
157
	LC_CTYPE=C
158
	case $1 in
159
		*[![:blank:]]*) return 1
160
	esac
161
)
162
101
l2tp_pre_start()
163
l2tp_pre_start()
102
{
164
{
103
	local l2tpsession=
165
	local declared_session declared_tunnel l2tpsession l2tptunnel
104
	eval l2tpsession=\$l2tpsession_${IFVAR}
166
	local name peer_session_id session_id tunnel_id
105
	test -n "${l2tpsession}" || return 0
167
	local encap local peer_tunnel_id remote
106
	
168
	local key
107
	ebegin "Creating L2TPv3 link ${IFVAR}"
169
108
	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
170
	if key="l2tpsession_${IFVAR:?}"; ! eval "[ \${${key}+set} ]"; then
109
	if ! _l2tp_eval_props s_ "name|tunnel_id|session_id|peer_session_id|cookie|peer_cookie|offset|peer_offset|l2spec_type" "${l2tpsession}"; then
171
		return
110
		eend 1 "l2tpsession_${IFVAR} syntax error: $l2tp_err"
172
	elif eval "l2tpsession=\$${key}"; _is_blank "${l2tpsession}"; then
111
		return 1
173
		eend 1 "${key} is defined but its value is blank"
112
	fi
174
	elif ! declared_session=$(_l2tp_parse_opts "${l2tpsession}" "peer_session_id session_id tunnel_id" "name"); then
113
	if [ -n "$s_name" ]; then
175
		eend 1 "${key} is missing at least one required parameter"
114
		eend 1 "l2tpsession_${IFVAR} error: please remove the \"name\" parameter (this parameter is managed by the system)"
176
	elif eval "${declared_session}"; [ "${name+set}" ]; then
115
		return 1
177
		eend 1 "${key} defines a \"name\" parameter, which is forbidden by netifrc"
116
	fi
178
	elif ! modprobe l2tp_eth; then
117
	# Try to load mendatory l2tp_eth kernel module
179
		eend 1 "Couldn't load the l2tp_eth module (perhaps the CONFIG_L2TP_ETH kernel option is disabled)"
118
	if ! modprobe l2tp_eth; then
180
	elif key="l2tptunnel_${IFVAR}"; eval "[ \${${key}+set} ]"; then
119
		eend 1 "l2tp_eth module not present in your kernel (please enable CONFIG_L2TP_ETH option in your kernel config)"
181
		if eval "l2tptunnel=\$${key}"; _is_blank "${l2tptunnel}"; then
120
		return 1
182
			eend 1 "${key} is defined but its value is blank"
121
	fi
183
		elif ! declared_tunnel=$(_l2tp_parse_opts "${l2tptunnel}" "local peer_tunnel_id remote tunnel_id" "encap"); then
122
	local l2tptunnel=
184
			eend 1 "${key} is missing at least one required parameter"
123
	eval l2tptunnel=\$l2tptunnel_${IFVAR}
185
		elif set -- "${tunnel_id}"; eval "${declared_tunnel}"; [ "$1" != "${tunnel_id}" ]; then
124
	if [ -n "${l2tptunnel}" ]; then
186
			eend 1 "${key} defines a \"tunnel_id\" parameter that contradicts l2tpsession_${IFVAR}"
125
		local t_tunnel_id t_encap t_local t_remote t_peer_tunnel_id t_udp_sport t_udp_dport
187
		elif _l2tp_should_add_tunnel "${tunnel_id}" "${declared_tunnel}"; set -- $?; [ "$1" -eq 2 ]; then
126
		_l2tp_eval_props t_ "remote|local|encap|tunnel_id|peer_tunnel_id|encap|udp_sport|udp_dport" "${l2tptunnel}"
188
			eend 1 "Tunnel #${tunnel_id} exists but its properties mismatch those defined by ${key}"
127
		# if encap=ip we need l2tp_ip kernel module
189
		elif [ "$1" -eq 1 ]; then
128
		if [ "${t_encap^^}" = "IP" ] && ! modprobe l2tp_ip; then
190
			# The config matches an existing tunnel.
129
			eend 1 "l2tp_ip module not present in your kernel (please enable CONFIG_L2TP_IP option in your kernel config)"
191
			true
130
			return 1
192
		elif [ "${encap}" = ip ] && ! modprobe l2tp_ip; then
131
		fi
193
			eend 1 "Couldn't load the l2tp_ip module (perhaps the CONFIG_L2TP_IP kernel option is disabled)"
132
		# Search for an existing tunnel with the same ID
133
		local f_tunnel_id f_encap f_local f_remote f_peer_tunnel_id f_udp_sport f_udp_dport
134
		if _l2tp_get_tunnel_info f_ $t_tunnel_id; then
135
			# check if the existing tunnel has the same property than expected
136
			if [ "tunnel_id:$f_tunnel_id;encap:$f_encap;local:$f_local;remote:$f_remote;
137
			peer_tunnel_id:$f_peer_tunnel_id;udp_sport:$f_udp_sport;udp_dport:$f_udp_dport" \
138
			!= "tunnel_id:$t_tunnel_id;encap:${t_encap^^};local:$t_local;remote:$t_remote;
139
			peer_tunnel_id:$t_peer_tunnel_id;udp_sport:$t_udp_sport;udp_dport:$t_udp_dport" ]; then
140
				eend 1 "There are an existing tunnel with id=$s_tunnel_id, but the properties mismatch with the one you want to create"
141
				return 1
142
			fi
143
		else
194
		else
144
			veinfo ip l2tp add tunnel ${l2tptunnel}
195
			ebegin "Creating L2TPv3 tunnel (tunnel_id ${tunnel_id})"
145
			_ip_l2tp_add tunnel ${l2tptunnel} || return 1
196
			printf %s "l2tp add tunnel ${l2tptunnel}" \
197
			| xargs -E '' ip
198
			eend $?
146
		fi
199
		fi
147
	elif ! ip l2tp show tunnel | grep -Eq "^Tunnel $s_tunnel_id,"; then
200
	elif ! _l2tp_has_tunnel "${tunnel_id}"; then
148
		# no l2tptunnel_<INTF> declaration, assume that the tunnel is already present
201
		# A tunnel may incorporate more than one session (link). This
149
		# checking if tunnel_id exists otherwise raise an error
202
		# module allows for the user not to define a tunnel for a given
150
		eend 1 "Tunnel id=$s_tunnel_id no found (you may have to set l2tptunnel_${IFVAR})"
203
		# session. In that case, it will be expected that the required
151
		return 1
204
		# tunnel has already been created to satisfy some other session.
152
	fi
205
		eend 1 "Tunnel #${tunnel_id} not found (defining ${key} may be required)"
153
	veinfo ip l2tp add session ${l2tpsession} name "${IFACE}"
206
	fi || return
154
	_ip_l2tp_add session ${l2tpsession} name "${IFACE}" || return 1
155
	_up
156
}
157
207
208
	ebegin "Creating L2TPv3 session (session_id ${session_id} tunnel_id ${tunnel_id})"
209
	printf %s "l2tp add session ${l2tpsession} name ${IFACE:?}" \
210
	| xargs -E '' ip && _up
211
	eend $?
212
}
158
213
159
l2tp_post_stop()
214
l2tp_post_stop()
160
{
215
{
161
	local session_id tunnel_id
216
	local existing_session session_id tunnel_id
162
	_is_l2tp || return 0
217
163
	
218
	# This function may be invoked for every interface. If not a virtual
164
	ebegin "Destroying L2TPv3 link ${IFACE}"
219
	# interface, it can't possibly be one that's managed by this module, in
165
	veinfo ip l2tp del session tunnel_id $tunnel_id session_id $session_id
220
	# which case running ip(8) and awk(1) would be a needless expense.
166
	ip l2tp del session tunnel_id $tunnel_id session_id $session_id
221
	[ -e /sys/devices/virtual/net/"${IFACE:?}" ] \
167
	if ! ip l2tp show session | grep -Eq "^Session [0-9]+ in tunnel $tunnel_id\$"; then
222
	&& existing_session=$(_l2tp_parse_existing_session 2>/dev/null) \
168
		#tunnel $tunnel_id no longer used, destoying it...
223
	|| return 0
169
		veinfo ip l2tp del tunnel tunnel_id $tunnel_id
224
170
		ip l2tp del tunnel tunnel_id $tunnel_id
225
	eval "${existing_session}"
226
	set -- session_id "${session_id}" tunnel_id "${tunnel_id}"
227
	ebegin "Destroying L2TPv3 session ($*)"
228
	ip l2tp del session "$@"
229
	eend $? &&
230
	if ! _l2tp_in_session "${tunnel_id}"; then
231
		shift 2
232
		ebegin "Destroying L2TPv3 tunnel ($*)"
233
		ip l2tp del tunnel "$@"
234
		eend $?
171
	fi
235
	fi
172
	eend $?
173
}
236
}
174
- 

Return to bug 890238