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