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
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
|