You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
11 KiB
Plaintext

#
# Copyright 2018 devolo AG
#
SCRIPT=/usr/sbin/wifi_guest.sh
_rm_cron_script(){
crontab -l | grep -v "$1" | sort -u | crontab -
}
_rm_and_add_cron_script(){
(crontab -l | grep -v "$1"; [ -n "$2" ] && echo "$2") | sort -u | crontab -
}
set_delos_guestwifi() {
#local EBLOG=--log
# determine new guest interfaces (from tmpfile written by a wifi hook)
[ -f /tmp/guestwifi-ifs ] && . /tmp/guestwifi-ifs || GUEST_IFS=
# determine new GW addresses (from tmpfile written by a dhcp hook)
[ -f /tmp/guestwifi-ipv4 ] && . /tmp/guestwifi-ipv4 || GW4_IP= GW4_MAC=
[ -f /tmp/guestwifi-ipv6 ] && . /tmp/guestwifi-ipv6 || GW6_IP= GW6_MAC=
# read saved interfaces and GW addresses of the installed rules
[ -f /var/run/guest-wifi-restricted ] && . /var/run/guest-wifi-restricted ||
CUR_GUEST_IFS= CUR_GW4_IP= CUR_GW4_MAC= CUR_GW6_IP= CUR_GW6_MAC=
# check if new parameters are same as last used
[ "$GUEST_IFS/$GW4_IP/$GW4_MAC/$GW6_IP/$GW6_MAC" = "$CUR_GUEST_IFS/$CUR_GW4_IP/$CUR_GW4_MAC/$CUR_GW6_IP/$CUR_GW6_MAC" ] &&
return
# reinstall rules only if changed
# throw away current user-defined chains
for iface in $CUR_GUEST_IFS; do
ebtables -D INPUT -i $iface -j i-guestwifi $EBLOG 2>/dev/null
ebtables -D OUTPUT -o $iface -j o-guestwifi $EBLOG 2>/dev/null
ebtables -D FORWARD -i $iface -j fi-guestwifi $EBLOG 2>/dev/null
ebtables -D FORWARD -o $iface -j fo-guestwifi $EBLOG 2>/dev/null
done
ebtables -X i-guestwifi 2>/dev/null
ebtables -X o-guestwifi 2>/dev/null
ebtables -X fi-guestwifi 2>/dev/null
ebtables -X fo-guestwifi 2>/dev/null
# but don't install new rules when there are no restricted interfaces
[ -z "$GUEST_IFS" ] && {
rm -f /var/run/guest-wifi-restricted
return
}
# use user-defined chains to group rules for easier deletion
ebtables -N i-guestwifi -P RETURN
ebtables -N o-guestwifi -P RETURN
ebtables -N fi-guestwifi -P RETURN
ebtables -N fo-guestwifi -P RETURN
for iface in $GUEST_IFS; do
ebtables -A INPUT -i $iface -j i-guestwifi $EBLOG 2>/dev/null
ebtables -A OUTPUT -o $iface -j o-guestwifi $EBLOG 2>/dev/null
ebtables -A FORWARD -i $iface -j fi-guestwifi $EBLOG 2>/dev/null
ebtables -A FORWARD -o $iface -j fo-guestwifi $EBLOG 2>/dev/null
done
# save current rule parameters for later
cat >/var/run/guest-wifi-restricted <<-EOF
CUR_GUEST_IFS="$GUEST_IFS"
CUR_GW4_IP="$GW4_IP"
CUR_GW4_MAC="$GW4_MAC"
CUR_GW6_IP="$GW6_IP"
CUR_GW6_MAC="$GW6_MAC"
EOF
# actual rules
# We return but don't accept so that more chains after ours are considered
#allow EAPOL frames destined to the adapter itself (needed for wifi authentication)
ebtables -A i-guestwifi -p 0x888e -j RETURN $EBLOG
#allow ARP frames destinated to the adapter itself
ebtables -A i-guestwifi -p ARP -j RETURN $EBLOG
#deny any other frame destined to the adapter itself
# TODO use DROP policy instead of using this rule as the last one everywhere!?
ebtables -A i-guestwifi -j DROP $EBLOG
#allow EAPOL frames originating from the adapter itself (needed for wifi authentication)
ebtables -A o-guestwifi -p 0x888e -j RETURN $EBLOG
#allow ARP frames originationg from the adapter itself
ebtables -A o-guestwifi -p ARP -j RETURN $EBLOG
#deny any frame originating from the adapter itself being destined to a guest account station
ebtables -A o-guestwifi -j DROP $EBLOG
# some IP-based restrictions to strengthen security
# FIXME what if no MAC? leave it out and use IP only?
[ -n "$GW4_IP" ] && {
#allow DHCP, DNS and ICMP unicast packets destined to the current default gateway
ebtables -A fi-guestwifi -d $GW4_MAC -p IPv4 --ip-dst $GW4_IP --ip-proto tcp --ip-dport 53 -j RETURN $EBLOG
ebtables -A fi-guestwifi -d $GW4_MAC -p IPv4 --ip-dst $GW4_IP --ip-proto udp --ip-dport 53 -j RETURN $EBLOG
ebtables -A fi-guestwifi -d $GW4_MAC -p IPv4 --ip-dst $GW4_IP --ip-proto udp --ip-dport 67 -j RETURN $EBLOG
ebtables -A fi-guestwifi -d $GW4_MAC -p IPv4 --ip-dst $GW4_IP --ip-proto icmp -j RETURN $EBLOG
#deny any other IP unicast packets destined to the current default gateway
ebtables -A fi-guestwifi -d $GW4_MAC -p IPv4 --ip-dst $GW4_IP -j DROP $EBLOG
}
#allow DHCP broadcast packets originating from a guest account station
ebtables -A fi-guestwifi -p IPv4 --ip-dst 255.255.255.255 --ip-proto udp --ip-dport 67 -j RETURN $EBLOG
#deny any other IP broadcast packets originating from a guest account station
ebtables -A fi-guestwifi -p IPv4 --ip-dst 255.255.255.255 -j DROP $EBLOG
[ -n "$GW4_IP" ] && {
#allow DHCP, DNS and ICMP packets originating from the current default gateway
ebtables -A fo-guestwifi -s $GW4_MAC -p IPv4 --ip-src $GW4_IP --ip-proto tcp --ip-sport 53 -j RETURN $EBLOG
ebtables -A fo-guestwifi -s $GW4_MAC -p IPv4 --ip-src $GW4_IP --ip-proto udp --ip-sport 53 -j RETURN $EBLOG
ebtables -A fo-guestwifi -s $GW4_MAC -p IPv4 --ip-src $GW4_IP --ip-proto udp --ip-sport 67 -j RETURN $EBLOG
ebtables -A fo-guestwifi -s $GW4_MAC -p IPv4 --ip-src $GW4_IP --ip-proto icmp -j RETURN $EBLOG
#deny any other IP packets originating from the current default gateway
ebtables -A fo-guestwifi -s $GW4_MAC -p IPv4 --ip-src $GW4_IP -j DROP $EBLOG
}
#if GW6_IP isn't known then deny all IPv6 frames originating from or destined to a guest account station
[ -z "$GW6_IP" ] && {
ebtables -A fo-guestwifi -p IPv6 -j DROP $EBLOG
ebtables -A fi-guestwifi -p IPv6 -j DROP $EBLOG
}
# some IPv6-based restrictions to strengthen security
[ -n "$GW6_IP" ] && {
#allow DNS packets destined to the current default gateway
ebtables -A fi-guestwifi -d $GW6_MAC -p IPv6 --ip6-dst $GW6_IP --ip6-proto tcp --ip6-dport 53 -j RETURN $EBLOG
ebtables -A fi-guestwifi -d $GW6_MAC -p IPv6 --ip6-dst $GW6_IP --ip6-proto udp --ip6-dport 53 -j RETURN $EBLOG
#allow DHCPv6 packets (using multicast)
ebtables -A fi-guestwifi -p IPv6 --ip6-proto udp --ip6-sport 546 --ip6-dport 547 -j RETURN $EBLOG
#allow ipv6-icmp type router-solicitation, router-advertisement, neighbour-solicitation, neighbour-advertisement
ebtables -A fi-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type router-solicitation -j RETURN $EBLOG
ebtables -A fi-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type router-advertisement -j RETURN $EBLOG
ebtables -A fi-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type neighbour-solicitation -j RETURN $EBLOG
ebtables -A fi-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type neighbour-advertisement -j RETURN $EBLOG
#deny any other IPv6 packets destined to the current default gateway
ebtables -A fi-guestwifi -d $GW6_MAC -p IPv6 --ip6-dst $GW6_IP -j DROP $EBLOG
}
[ -n "$GW6_IP" ] && {
#allow DNS packets originating from the current default gateway
ebtables -A fo-guestwifi -s $GW6_MAC -p IPv6 --ip6-src $GW6_IP --ip6-proto tcp --ip6-sport 53 -j RETURN $EBLOG
ebtables -A fo-guestwifi -s $GW6_MAC -p IPv6 --ip6-src $GW6_IP --ip6-proto udp --ip6-sport 53 -j RETURN $EBLOG
#allow DHCPv6 reply packets
ebtables -A fo-guestwifi -p IPv6 --ip6-proto udp --ip6-sport 547 --ip6-dport 546 -j RETURN $EBLOG
#allow ipv6-icmp type router-solicitation, router-advertisement, neighbour-solicitation, neighbour-advertisement
ebtables -A fo-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type router-solicitation -j RETURN $EBLOG
ebtables -A fo-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type router-advertisement -j RETURN $EBLOG
ebtables -A fo-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type neighbour-solicitation -j RETURN $EBLOG
ebtables -A fo-guestwifi -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type neighbour-advertisement -j RETURN $EBLOG
#deny any other IP packets originating from the current default gateway
ebtables -A fo-guestwifi -s $GW6_MAC -p IPv6 --ip6-src $GW6_IP -j DROP $EBLOG
}
#allow all broadcast frames transporting plausible protocols for Internet usage (e.g. no MMEs)
ebtables -A fi-guestwifi -p ARP -d FF:FF:FF:FF:FF:FF -j RETURN $EBLOG
ebtables -A fi-guestwifi -p IPv4 -d FF:FF:FF:FF:FF:FF -j RETURN $EBLOG
#allow all frames destined to the current default gateway (and thus, most likely to the Internet)
[ -n "$GW4_MAC" ] && {
ebtables -A fi-guestwifi -d $GW4_MAC -j RETURN $EBLOG
}
[ -n "$GW6_MAC" ] && {
ebtables -A fi-guestwifi -d $GW6_MAC -j RETURN $EBLOG
}
#deny all frames originating from a guest account station, that have not been allowed already
ebtables -A fi-guestwifi -j DROP $EBLOG
#allow all frames originating from the current default gateway destined to a guest account station
[ -n "$GW4_MAC" ] && {
ebtables -A fo-guestwifi -s $GW4_MAC -j RETURN $EBLOG
}
[ -n "$GW6_MAC" ] && {
ebtables -A fo-guestwifi -s $GW6_MAC -j RETURN $EBLOG
}
#deny all still unallowed frames destined to a guest account station
ebtables -A fo-guestwifi -j DROP $EBLOG
}
delos_wifi_guest_timer() {
local auto_switch_off=
config_get_bool auto_switch_off guest_wifi auto_switch_off 0
if [ "$auto_switch_off" = "1" ]; then
local starttime=
local stoptime=
local interval=
config_get interval guest_wifi interval '00:30'
config_get starttime guest_wifi starttime
if [ -n "$starttime" -a ${interval//:} -gt 0 ]; then
stoptime=$(($starttime + $(echo $interval | awk -F: '{ print ($1 * 3600) + ($2 * 60) + 60 }')))
_rm_and_add_cron_script "${SCRIPT}" "$(date -D %s -d ${stoptime} "+%M %H %d %m %a ${SCRIPT} stop")"
fi
else
_rm_cron_script "${SCRIPT}"
fi
}
delos_wifi_guest_post() {
local action=$1; shift # enable/disable
local iface_list
local ifname
case "$action" in
enable)
check_access() {
# check guest wifi restriction option
[ "$(config_get $1 disabled)" != "1" ] &&
[ "$(config_get $(config_get $1 device) disabled)" != "1" ] &&
[ "$(config_get $1 dvl_guest)" = "1" ] || return
local dvl_unrestricted_access
config_get dvl_unrestricted_access $1 dvl_unrestricted_access 0
[ "$dvl_unrestricted_access" = "0" ] &&
ifname=$(uci_get_state wireless $1 ifname 2>/dev/null) &&
echo $ifname
}
iface_list=$(config_foreach check_access wifi-iface | sort)
iface_list="$(echo $iface_list)"
delos_wifi_guest_timer
;;
disable)
iface_list=
;;
*)
return
;;
esac
if [ -n "$iface_list" ]; then (
# subshell for new trap context
TMP=
trap '[ -n "$TMP" ] && rm -f "$TMP"; TMP=' EXIT HUP INT QUIT TERM
TMP=$(mktemp)
cat >$TMP <<-EOF
GUEST_IFS="$iface_list"
EOF
# if changed:
cmp -s /tmp/guestwifi-ifs $TMP || {
mv $TMP /tmp/guestwifi-ifs
TMP=
( set_delos_guestwifi )
}
) else
# if removed:
rm /tmp/guestwifi-ifs 2>/dev/null && {
( set_delos_guestwifi )
}
fi
}
# is DELOS_WIFI_DRIVERS defined at all (empty or non-empty)? (force exit code 0 if empty,
# don't exit current script if not defined)
if (: ${DELOS_WIFI_DRIVERS?}) 2>/dev/null; then
# we were sourced to register as a plugin
delos_wifi_driver_guest() {
type=$1; shift
type delos_wifi_guest_$type >/dev/null 2>/dev/null && delos_wifi_guest_$type "$@"
}
append DELOS_WIFI_DRIVERS delos_wifi_driver_guest
fi