summaryrefslogtreecommitdiff
blob: acedeabf3a7bccd4b1c2e7c6393891279976c321 (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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#!/bin/bash
# Copyright (c) 2004-2005 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

# Contributed by Roy Marples (uberlord@gentoo.org)

# Fix any potential localisation problems
# Note that LC_ALL trumps LC_anything_else according to locale(7)
wpa_supplicant() {
	LC_ALL=C /sbin/wpa_supplicant "$@"
}

wpa_cli() {
	LC_ALL=C /bin/wpa_cli "$@"
}

# char* wpa_supplicant_provides(void)
#
# Returns a string to change module definition for starting up
wpa_supplicant_provides() {
	echo "wireless"
}

# void wpa_supplicant_depend(void)
#
# Sets up the dependancies for the module
wpa_supplicant_depend() {
	after macnet
	before interface
}

# bool wpa_supplicant_check_installed(void)
#
# Returns 0 if wpa_supplicant is installed, otherwise 1
wpa_supplicant_check_installed() {
	local report="${1:-false}" installed="0"
	if [[ ! -x /sbin/wpa_supplicant ]]; then
		installed="1"
		${report} && eerror "For WPA support (wpa_supplicant) support, emerge net-wireless/wpa_supplicant"
	fi
	if [[ ! -e /proc/net/wireless ]]; then
		installed="1"
		if ${report} ; then
			eerror "wpa_supplicant requires wireless support"
			eerror "(CONFIG_NET_WIRELESS=y) enabled in the kernel"
		fi
	fi
	if [[ ! -e /proc/net/packet ]]; then
		installed="1"
		if ${report} ; then
			eerror "wpa_supplicant requires Packet Socket"
			eerror "(CONFIG_PACKET=y) enabled in the kernel"
		fi
	fi
	return "${installed}"
}

# bool wpa_supplicant_check_depends(void)
#
# Checks to see if we have the needed functions
wpa_supplicant_check_depends() {
	local f

	for f in interface_exists; do
		[[ $( type -t "${f}" ) == "function" ]] && continue
		eerror "wpa_supplicant: missing required function ${f}\n"
		return 1
	done

	return 0
}

# bool wpa_supplicant_check_extensions(char *interface)
#
# Checks to see if wireless extensions are enabled on the interface
wpa_supplicant_check_extensions() {
	[[ ! -e /proc/net/wireless ]] && return 1
	grep -q "^[ \t]*$1:[ \t]" /proc/net/wireless
}

# char* wpa_supplicant_get_essid(char *interface)
#
# Gets the current ESSID of iface
wpa_supplicant_get_essid() {
	local i essid

	for (( i=0; i<5; i++ )); do
		essid=$( wpa_cli -i"$1" status | sed -n -e 's/^ssid=//p' )
		if [[ -n ${essid} ]]; then
			echo "${essid}"
			return 0
		fi
		sleep 1
	done

	return 1
}

# char* wpa_supplicant_get_ap_mac_address(char *interface)
#
# Returns the MAC address of the Access Point
# the interface is connected to
wpa_supplicant_get_ap_mac_address() {
	wpa_cli -i"$1" status | sed -n -e 's/^bssid=\([^=]\+\).*/\U\1/p'
}

# bool wpa_supplicant_associated(char *interface)
#
# Returns 0 if we're associated correctly or 1 if not
# Note that just because we are associated does not mean we are using the
# correct encryption keys
# We only need this for wpa_supplicant-0.3.x
wpa_supplicant_associated() {
	local -a status=( "$( wpa_cli -i"$1" status | sed -n -e 's/^\(key_mgmt\|wpa_state\|EAP state\)=\([^=]\+\).*/\U\2/p' )" )

	case "${status[0]}" in
		"NONE")			[[ ${status[1]} == "ASSOCIATED" ]] ;;
		"IEEE 802.1X (no WPA)")	[[ ${status[2]} == "SUCCESS" ]] ;;
		*)			[[ ${status[1]} == "COMPLETED" ]] ;;
	esac

	return $?
}

# void wpa_supplicant_kill(char *interface, bool report)
#
# Kills any existing wpa_supplicant process on the interface
wpa_supplicant_kill() {
	local iface="$1" report="${2:-false}" pidfile

	# Shutdown wpa_cli first, if it's running
	# This is important as future versions of wpa_supplicant
	# may send a disconnect message to wpa_cli when it shutsdown
	pidfile="/var/run/wpa_cli-${iface}.pid"
	if ! clean_pidfile "${pidfile}" ; then
		${report} && ebegin "Stopping wpa_cli on ${iface}"
		start-stop-daemon --stop --exec /bin/wpa_cli \
		--pidfile "${pidfile}"
		${report} && eend "$?"
	fi

	# Now shutdown wpa_supplicant
	pidfile="/var/run/wpa_supplicant-${iface}.pid"
	if ! clean_pidfile "${pidfile}" ; then
		${report} && ebegin "Stopping wpa_supplicant on ${iface}"
		start-stop-daemon --stop --exec /sbin/wpa_supplicant \
		--pidfile "${pidfile}"
		${report} && eend "$?"
	else
		# Support wpa_supplicant-0.3.x
		local pid=$( pgrep -f '^/sbin/wpa_supplicant .* -i'"${iface}"'[ ]*$' )
		if [[ -n ${pid} ]]; then
			${report} && ebegin "Stopping wpa_supplicant on ${iface}"
			kill -s TERM "${pid}"
			${report} && eend 0
		fi
	fi

	# If wpa_supplicant exits uncleanly, we need to remove the stale dir
	[[ -S "/var/run/wpa_supplicant/${iface}" ]] \
	&& rm -f "/var/run/wpa_supplicant/${iface}"
}

# bool wpa_supplicant_associate(char *interface)
#
# Returns 0 if wpa_supplicant associates and authenticates to an AP
# otherwise, 1
wpa_supplicant_associate() {
	local iface="$1" ifvar=$( bash_variable "$1" ) timeout i
	eval timeout=\"\$\{associate_timeout_${ifvar}\}\"
	[[ -z ${timeout} ]] && eval timeout=\"\$\{wpa_timeout_${ifvar}:-60\}\"

	if [[ ${timeout} == "0" ]]; then
		ewarn "WARNING: infinite timeout set for association on ${iface}"
	elif [[ ${timeout} -lt 0 ]]; then
		ewarn "WARNING: negative timeout set for ${iface}"
		exit 0
	fi

	while true ; do
		if ${action} ; then
			service_started "net.${iface}" && return 0
		else
			if ! wpa_cli -i"${iface}" status &>/dev/null ; then
				eend 1 "wpa_supplicant has exited unexpectedly"
				return 1
			fi
			wpa_supplicant_associated "${iface}" && return 0
		fi
		sleep 1
		(( i++ ))
		[[ ${i} == "${timeout}" || ${i} -gt "${timeout}" ]] && break
	done

	# Spit out an appropriate error
	if [[ ${background} != "yes" ]]; then
		if ${action} ; then
			eend 1 "Failed to configure ${iface} in the background"
		else
			eend 1 "Timed out"
		fi
	fi

	# exit without error with wpa_supplicant-0.4.x as we may get kickstarted
	# when an AP comes in range
	${action} && exit 0

	# Kill wpa_supplicant for 0.3.x
	wpa_supplicant_kill "${iface}"
	return 1
}

# bool wpa_supplicant_pre_start(char *interface)
#
# Start wpa_supplicant on an interface and wait for association
# Returns 0 (true) when successful, non-zero otherwise
wpa_supplicant_pre_start() {
	local iface="$1" opts timeout action=false
	local cfgfile="/etc/wpa_supplicant.conf"
	local actfile="/sbin/wpa_cli.action"

	# We don't configure wireless if we're being called from
	# the background
	if ${IN_BACKGROUND} ; then
		ESSID=$( wpa_supplicant_get_essid "${iface}" )
		ESSIDVAR=$( bash_variable "${ESSID}" )
		save_options "ESSID" "${ESSID}"
		return 0
	fi

	save_options "ESSID" ""

	# We only work on wirelesss interfaces
	wpa_supplicant_check_extensions "${iface}" || return 0

	# Kill off any existing wpa_supplicant on this interface
	# This is so we can re-read the configuration file and clean any stale
	# directories
	wpa_supplicant_kill "${iface}" true

	# Check for rf_kill - only ipw supports this at present, but other
	# cards may in the future
	if [[ -e "/sys/class/net/${iface}/device/rf_kill" ]]; then
		if [[ $( < "/sys/class/net/${iface}/device/rf_kill" ) != 0 ]]; then
			eerror "Wireless radio has been killed for interface ${iface}"
			return 1
		fi
	fi

	# If wireless-tools is installed, try and apply our user config
	# This is needed for some drivers - such as hostap because they start
	# the card in Master mode which causes problems with wpa_supplicant.
	if [[ $( type -t iwconfig_defaults ) == "function" ]]; then
		iwconfig_defaults "${iface}"
		iwconfig_user_config "${iface}"
	fi	

	ebegin "Starting wpa_supplicant on ${iface}"

	if [[ ! -f ${cfgfile} ]]; then
		eend 1 "configuration file ${cfgfile} not found!"
		return 1
	fi

	local ctrl_dir=$( sed -n -e 's/[ \t]*#.*//g;s/[ \t]*$//g;s/^ctrl_interface=//p' "${cfgfile}" )
	if [[ ${ctrl_dir} != "/var/run/wpa_supplicant" ]]; then
		eerror "${cfgfile} must set"
		eerror "  ctrl_interface=/var/run/wpa_supplicant"
		eend 1
		return 1
	fi

	local ifvar=$( bash_variable "${iface}" )
	eval opts=\" \$\{wpa_supplicant_${ifvar}\}\"
	[[ ${opts} != *" -D"* ]] \
	&& ewarn "wpa_supplicant_${ifvar} does not define a driver"

	# Some drivers require the interface to be up
	interface_up "${iface}"

	version=$( wpa_cli -v | sed -n -e 's/wpa_cli v//p' )
	version=( ${version//./ } )
	(( version = version[0] * 1000 + version[1] * 100 + version[2] ))

	# wpa_supplicant 0.4.0 and greater supports wpa_cli actions
	# This is very handy as if and when different association mechanisms are
	# introduced to wpa_supplicant we don't have to recode for them as
	# wpa_cli is now responsible for informing us of success/failure.
	# The downside of this is that we don't see the interface being configured
	# for DHCP/static.
	if [[ ${version} -gt 399 && -x ${actfile} ]]; then
		opts="${opts} -W -P/var/run/wpa_supplicant-${iface}.pid"
		action=true
		[[ ${RC_PARALLEL_STARTUP} == "yes" ]] && background=no
	fi

	start-stop-daemon --start --exec /sbin/wpa_supplicant \
	-- ${opts} -B -c/etc/wpa_supplicant.conf -i"${iface}"
	eend "$?" || return 1

	# Starting wpa_supplication-0.4.0, we can get wpa_cli to
	# start/stop our scripts from wpa_supplicant messages
	if ${action} ; then
		mark_service_inactive "net.${iface}"
		ebegin "Starting wpa_cli on ${iface}"
		start-stop-daemon --start --exec /bin/wpa_cli \
		-- -a"${actfile}" -i"${iface}" \
		-P"/var/run/wpa_cli-${iface}.pid" -B
		eend "$?" || return 1
	fi

	# Background wpa_supplication if required
	if [[ ${background} == "yes" ]]; then
		if ! ${action} ; then
			wpa_supplicant_associate "${iface}" \
			&& export IN_BACKGROUND=true \
			&& /etc/init.d/net.${iface} start >/dev/null &
		fi
		go_background
	fi

	eindent
	veinfo "Waiting for association"
	eend 0

	wpa_supplicant_associate "${iface}" || return 1

	# Set ESSID for essidnet and report
	ESSID=$( wpa_supplicant_get_essid "${iface}" )
	ESSIDVAR=$( bash_variable "${ESSID}" )
	save_options "ESSID" "${ESSID}"

	local -a status=( "$( wpa_cli -i${iface} status | sed -n -e 's/^\(bssid\|pairwise_cipher\|key_mgmt\)=\([^=]\+\).*/\U\2/p' | tr '[:lower:]' '[:upper:]' )" )
	einfo "${iface} connected to \"${ESSID//\\\\/\\\\}\" at ${status[0]}"

	if [[ ${status[2]} == "NONE" ]]; then
		if [[ ${status[1]} == "NONE" ]]; then
			ewarn "not using any encryption"
		else
			veinfo "using ${status[1]}"
		fi
	else
		veinfo "using ${status[2]}/${status[1]}"
	fi
	eoutdent

	if ${action} ; then
		local addr=$( interface_get_address "${iface}" )
		einfo "${iface} configured with address ${addr}"
		exit 0 
	fi

	return 0
}

# bool wpa_supplicant_post_stop(char *iface)
#
# Stops wpa_supplicant on an interface
# Returns 0 (true) when successful, non-zero otherwise
wpa_supplicant_post_stop() {
	! ${IN_BACKGROUND} && wpa_supplicant_kill "$1" true
	return 0
}

# vim:ts=4