#!/bin/sh
# Turn a hostapd event into a dataplane authorisation change.
#
# Run by "hostapd_cli -B -i <if> -a <this> -r", which is hostapd's own
# integration point for this. It is invoked as:
#
#     dot1x-action <ifname> <event> [<arg>...]
#
# The event set is measured, not assumed. On a wired 802.1X link between two
# DANOS routers:
#
#   authentication succeeds   CTRL-EVENT-EAP-SUCCESS <mac>
#                             AP-STA-CONNECTED <mac>
#   clean EAPOL-Logoff        AP-STA-DISCONNECTED <mac>
#   supplicant disappears     nothing at all
#
# That last row is the one to understand before trusting this. A supplicant
# that is killed, unplugged or crashes sends no EAPOL-Logoff, and hostapd
# emits nothing -- checked by killing wpa_supplicant and waiting 40s, with no
# event in the action script and no AP-STA-DISCONNECTED in hostapd's own log.
# The port then stays authorised until reauthentication fails, so
# reauthentication-period is what actually bounds how long a departed
# supplicant leaves a port open. It is not a knob to leave at a large value
# and forget.
#
# AP-STA-CONNECTED is used for authorise rather than CTRL-EVENT-EAP-SUCCESS,
# even though both fire: CONNECTED/DISCONNECTED are a matched pair around the
# station's session, and pairing an authorise with a disconnect that has a
# different name invites one being handled without the other.

set -u

VPLSH=/opt/vyatta/bin/vplsh
IFNAME=${1:-}
EVENT=${2:-}
MAC=${3:-}

[ -n "$IFNAME" ] && [ -n "$EVENT" ] || exit 0

log() {
	logger -t vyatta-dot1x -p daemon.info -- "$IFNAME $EVENT ${MAC:-} $*"
}

case "$EVENT" in
AP-STA-CONNECTED)
	# The station address goes with the authorisation. hostapd is the only
	# thing that knows which station authenticated, and the dataplane is
	# where the authorisation lives, so this call is the one place the two
	# meet. Passing it is optional as far as the dataplane is concerned: a
	# missing or malformed address still authorises the port, it just is not
	# reported.
	if "$VPLSH" -l -c "dot1x authorize $IFNAME ${MAC:-}" >/dev/null 2>&1; then
		log "authorised"
	else
		# Refused when 802.1X is not enabled on the port in the
		# dataplane, which means hostapd is running on an interface the
		# component did not configure. Worth a log line: silently doing
		# nothing here would leave a port that authenticates and never
		# forwards.
		log "authorise REFUSED by the dataplane"
	fi
	;;
AP-STA-DISCONNECTED)
	if "$VPLSH" -l -c "dot1x unauthorize $IFNAME" >/dev/null 2>&1; then
		log "unauthorised"
	else
		log "unauthorise REFUSED by the dataplane"
	fi
	;;
*)
	# Everything else -- EAP-STARTED, EAP-PROPOSED-METHOD, EAP-SUCCESS --
	# is progress within an exchange whose outcome arrives as one of the
	# two above.
	;;
esac

exit 0
