#!/bin/sh VER="v1.02" #======================================================================================================= © 2016-2018 Martineau, v1.02 # # Configure Guest WiFi with new DHCP subnet and DNS using '/etc/dnsmasq.conf' and optionally allow LAN access to specified IPs # # Usage: GuestSubnet ['help' | '-h'] # {wifi_interface | ssid } | ['del[subnet]'] ['autodnsmasq'] [ 'ssid='name] ['ip='ipaddress[,ipaddress]] # # GuestSubnet wl0.1 # Guest WiFI 2.4GHz interface wl0.1 will have the new DHCP subnet and DNS applied (default) # GuestSubnet wl0.1 del # Guest WiFI 2.4GHz interface wl0.1 firewall rules will be removed but subnet remains. # GuestSubnet wl0.1 delsubnet # Guest WiFI 2.4GHz interface wl0.1 firewall rules will be removed and subnet will be reset to Asus default. # GuestSubnet wl0.2 autodnsmasq # Guest WiFi 2.4GHz interface wl0.2 will have new DHCP and DNS directives inserted into /jffs/configs/dnsmasq.conf.add # if they don't already exist. # NOTE: dnsmasq will be bounced. # GuestSubnet wl1.2 ip=192.168.1.99,192.168.1.100 # Guest WiFi 5GHz interface wl1.2 will allow access to LAN devices 192.168.1.99,192.168.1.100 # GuestSubnet Guest242 # Guest WiFI SSID 'Guest242' (possibly wl0.2?) will have the new DHCP subnet and DNS applied # # /jffs/configs/dnsmasq.conf.add: # # e.g. # # 2.4GHz Guest #1 uses DHCP pool 10.88.241.2 - 10.88.241.20 and OpenDNS/Google DNS # interface=wl0.1 # dhcp-range=wl0.1,10.88.241.2,10.88.241.20,255.255.255.0,21600s # dhcp-option=wl0.1,3,10.88.241.1 # dhcp-option=wl0.1,6,208.67.220.220,8.8.8.8 # # and will be included in /'etc/dnsmasq.conf' by command 'service restart_dnsmasq' and @boot time etc. # Print between line beginning with'#==' to first blank line inclusive ShowHelp() { /usr/bin/awk '/^#==/{f=1} f{print; if (!NF) exit}' $0 } Say(){ echo -e $$ $@ | logger -st "($(basename $0))" } SayT(){ echo -e $$ $@ | logger -t "($(basename $0))" } # Function Parse(String delimiter(s) variable_names) Parse() { # # Parse "Word1,Word2|Word3" ",|" VAR1 VAR2 REST # (Effectivley executes VAR1="Word1";VAR2="Word2";REST="Word3") local string IFS TEXT="$1" IFS="$2" shift 2 read -r -- "$@" <&2 $FW "$@" 2>/dev/null # Suppress error messages local RC=$? if [ "$RC" -gt 0 ] && [ ! -z "$(echo "$@" | grep -o "\-I|\-A")" ];then # Report errors for "-I" or "-A" actions i.e. ignore delete failures! echo -e "\a"$FW $@ $FW "$@" # Hopefully re-running the bad command for the second time reports the original error! fi echo -en $cRESET >&2 } Config_dnsmasqWIFI() { if [ -z $2 ];then local FN="/jffs/configs/dnsmasq.conf.add" else local FN="$2" fi local WIFI="$1" if [ -f "$FN" ];then local NOW=$(date +"%Y%m%d-%H%M%S") # current date and time cp $FN ${FN}-$NOW # Create 'dnsmasq.conf.add' backup sed -i "/$WIFI/d" $FN 2> /dev/null # Remove existing WIFI definition if it exists fi local DNS_LIST=$DNS_TO_USE if [ -z "$DNS_LIST" ];then DNS_LIST=$LAN_SUBNET".1" fi cat >> $FN << EOF # Guest WiFi=$WIFI uses DHCP pool ${LAN_SUBNET_PREFIX}.2 - ${LAN_SUBNET_PREFIX}.20 DNS=$DNS_TO_USE interface=$WIFI dhcp-range=$WIFI,${LAN_SUBNET_PREFIX}.2,${LAN_SUBNET_PREFIX}.20,255.255.255.0,14400s dhcp-option=$WIFI,3,${LAN_SUBNET_PREFIX}.1 dhcp-option=$WIFI,6,$DNS_LIST EOF service restart_dnsmasq 2>&1 >/dev/null #cat /etc/dnsmasq.conf return 0 } WIFI_FW() { local INTERFACE=$WIFI_IF # Delete Firewall rules and reinsert if required for ACTION in $ACTIONS do # Allow use of IPTABLES..... Firewall $EBT -t broute $ACTION BROUTING -p ipv6 -i $INTERFACE -j DROP Firewall $EBT -t broute $ACTION BROUTING -p ipv4 -i $INTERFACE -j DROP Firewall $EBT -t broute $ACTION BROUTING -p arp -i $INTERFACE -j DROP # Ebtables logging if [ "$ACTION" == "-D" ] || [ "$EBT_LOGGING" -eq 1 ];then Firewall $EBT -t filter $ACTION INPUT -p IPv4 -j CONTINUE --log-prefix "EBT filter INPUT" Firewall $EBT -t filter $ACTION FORWARD -p IPv4 -j CONTINUE --log-prefix "EBT filter FORWARD" Firewall $EBT -t filter $ACTION OUTPUT -p IPv4 -j CONTINUE --log-prefix "EBT filter OUTPUT" Firewall $EBT -t broute $ACTION BROUTING -p IPv4 -j CONTINUE --log-prefix "EBT broute BROUTING" #Firewall $EBT -t filter $ACTION INPUT -p IPv4 --ip-src $LAN_RESOURCE -j CONTINUE --log-prefix '"EBT filter INPUT"' fi # Router access Firewall $IPT $ACTION INPUT -i $INTERFACE -m state --state NEW -j $LOGDROP # Protect Router Block EVERYTHING! Firewall $IPT $ACTION INPUT -i $INTERFACE -p tcp --dport 53 -j ACCEPT # Allow VLAN to access DNSSEC? Firewall $IPT $ACTION INPUT -i $INTERFACE -p udp -m multiport --dport 53,67 -j ACCEPT # Allow VLAN to access DNS,DHCP #Firewall $IPT $ACTION INPUT -i $INTERFACE -s $VLAN_SUBNET_PREFIX.0/24 -p tcp -m multiport --dport 22,23,80,443,51893 -j DROP # Paranoid! Firewall $IPT $ACTION FORWARD -i $INTERFACE -o $(Get_WAN_IF_Name) -j ACCEPT # Access T'interWeb! - here be monsters! ;-) # Applications / local LAN if [ ! -z "$LAN_RESOURCES" ];then # My local LAN? for LAN_RESOURCE in $LAN_RESOURCES do #Firewall $ACTION FORWARD -s $WIFI_SUBNET_PREFIX.0/24 -d 239.255.255.250 -j ACCEPT # Chromecast Port 1900 specific? Firewall $IPT $ACTION FORWARD -s $WIFI_SUBNET_PREFIX.0/24 -d $LAN_RESOURCE -j ACCEPT Firewall $IPT $ACTION FORWARD -d $WIFI_SUBNET_PREFIX.0/24 -s $LAN_RESOURCE -j ACCEPT # These only work if the Guest Wifi and LAN subnets are the same? #Firewall $EBT -t filter $ACTION FORWARD -p ARP --arp-opcode 2 --arp-ip-src $LAN_RESOURCE #Firewall $EBT -t filter $ACTION FORWARD -p ARP --arp-opcode 1 --arp-ip-dst $LAN_RESOURCE done fi # If we need to access a Guest WiFi device (say an IoT IP CAM) from the LAN then these two ARP rules should suffice if subnet is the same? # ff:ff:ff:ff:ff:ff is used for broadcasting to all devices on an interface local ADMIN_DEVICE="10.88.8.114" # HP-Envy14 local IOT_MAC="xx:xx:xx:xx:xx:xx" # Allow ARP broadcast from a specific IP address on the 2.4GHz Primary Wi-Fi interface to all devices on the 2.4GHz Guest Wi-Fi interface # and ARP reply from a specific MAC address on the 2.4GHz Guest Wi-Fi interface to a specific IP address on the 2.4GHz Primary Wi-Fi interface #Firewall $EBT -t filter $ACTION FORWARD -i eth1 -o $INTERFACE -p ARP --arp-ip-src $ADMIN_DEVICE -d ff:ff:ff:ff:ff:ff -j ACCEPT #Firewall $EBT -t filter $ACTION FORWARD -i $INTERFACE -o eth1 -p ARP -s $IOT_MAC --arp-ip-dst $ADMIN_DEVICE-j ACCEPT # or from a specific IP address on the 5GHz Primary Wi-Fi interface #Firewall $EBT -t filter $ACTION FORWARD -i eth2 -o $INTERFACE -p ARP --arp-ip-src $ADMIN_DEVICE -d ff:ff:ff:ff:ff:ff -j ACCEPT #Firewall $EBT -t filter $ACTION FORWARD -i $INTERFACE -o eth2 -p ARP -s $IOT_MAC --arp-ip-dst $ADMIN_DEVICE-j ACCEPT done } Show_Status() { local WIFI_IF_DESC=$WIFI_IF if [ -z "$WIFI_IF" ];then local WIFI_IF_DESC="All" WIFI_IF="wl" LAN_SUBNET_PREFIX="10.88.5" # Hack assumes my numbering scheme is implemented! fi local BASE=${#WIFI_IF} if [ "$WIFI_IF_DESC" == "All" ];then BASE=$((BASE+1)) else BASE=$((BASE+${#SSID}+1)) fi local LENGTH=$((18+$BASE)) local EQUALS="$(printf %${LENGTH}s |tr " " "=")" echo -e "\n\n\t\tGuest WiFi" $SSID $WIFI_IF_DESC "Status";echo -e "\t\t"$EQUALS if [ "$WIFI_IF" != "wl" ];then ifconfig $WIFI_IF 2>/dev/null else for INDEX in 0.1 0.2 0.3 1.1 1.2 1.3 #Dump any configured wl0.x/wl1.x interface do ifconfig $WIFI_IF""$INDEX 2>/dev/null done fi local LENGTH=$((22+$BASE)) local EQUALS="$(printf %${LENGTH}s |tr " " "=")" echo -e "\n\tGuest WiFi" $SSID $WIFI_IF_DESC "Statistics";echo -e "\t"$EQUALS ip -s link | grep -i $WIFI_IF -A 5 local LENGTH=$((33+$BASE)) local EQUALS="$(printf %${LENGTH}s |tr " " "=")" echo -e "\n\tGuest WiFi" $SSID $WIFI_IF_DESC "-t filter INPUT rules";echo -e "\t"$EQUALS iptables -nvL INPUT | grep -iE "$WIFI_IF|$LAN_SUBNET_PREFIX" local LENGTH=$((35+$BASE)) local EQUALS="$(printf %${LENGTH}s |tr " " "=")" echo -e "\n\tGuest WiFi" $SSID $WIFI_IF_DESC "-t filter FORWARD rules";echo -e "\t"$EQUALS iptables -nvL FORWARD | grep -iE "$WIFI_IF|$LAN_SUBNET_PREFIX" if [ "$1" = "verbose" ];then echo -e "\n" ebtables -t broute -L --Lmac2 --Lc --Ln echo -e "\n" ebtables -L --Lmac2 --Lc --Ln else local LENGTH=$((30+$BASE)) local EQUALS="$(printf %${LENGTH}s |tr " " "=")" echo -e "\n\tGuest WiFi" $SSID $WIFI_IF "ebtables -t broute";echo -e "\t"$EQUALS ebtables -t broute -L --Lmac2 --Lc --Ln | grep -i $WIFI_IF local LENGTH=$((38+$BASE)) local EQUALS="$(printf %${LENGTH}s |tr " " "=")" echo -e "\n\tGuest WiFi" $SSID $WIFI_IF "ebtables -t filter FORWARD";echo -e "\t"$EQUALS ebtables -L --Lmac2 --Lc --Ln | grep -i $WIFI_IF fi echo -e } Get_Subnet_Prefix (){ local WIFI=$1 local LANIPADDR=$(nvram get lan_ipaddr) local LAN_SUBNET=${LANIPADDR%.*} local LAN_TWO_OCTETS=$(echo "$LAN_SUBNET" | awk 'BEGIN { FS = "." } {print $1"."$2}') # My numbering scheme for third OCTET: # # 10.88.8x.0 LAN # 10.88.10x.0 Bridge i.e. 101,102,103,104 and 105 # 10.88.24x.0 Wifi 2.4GHz i.e. 241,242 and 243 # 10.88.5x.0 Wifi 5GHz i.e. 51,52 and 53 # 10.88.x0.0 VLAN keep 'x' as multiple of 10 e.g. 50 won't clash with 51 aka Guest 5GHz #1 # but skip 60 as it is reserved by ASUS? # Since we use the 3rd octet as the subnet.... case "${WIFI:0:3}" in wl0) local DIGITS="24"${WIFI:4:1} # 2.4GHz 241-243 ;; wl1) local DIGITS="5"${WIFI:4:1} # 5Ghz 51-53 ;; esac local LAN_SUBNET_PREFIX=$LAN_TWO_OCTETS".$DIGITS" # Globally exposed variable echo $LAN_SUBNET_PREFIX $LAN_SUBNET } #=====================================Main=========================================================== Main() {} ANSIColours # Can only run in Router Mode; if [ "$(Check_Router_Mode)" != "Router" ];then echo -e "\e[41m\a\n\n\n\n\t\t\t\t** "$(Check_Router_Mode)" mode is not supported **\t\t\t\t\t\n\n\n\e[0m" exit 999 fi IPT="/usr/sbin/iptables" EBT="/usr/sbin/ebtables" USE_DNSMASQ="Y" # Require dnsmasq.conf directives for WIFI AUTODNSMASQ="N" # Auto create dnsmasq.conf directives for WIFI #WIFI_IF="wl0.1" # Guest WiFi interface (default) WIFI_IF= DNS_TO_USE="208.67.220.220,8.8.8.8" # OpenDNS/Google (default) #DNS_TO_USE= SSID_ID= # If SSID=xxxxxx specified then it will be set #LAN_RESOURCES="10.88.8.90 10.88.8.91" # LAN resources e.g. Roku,Chromecast-Bed1 #LAN_RESOURCES="10.88.8.90" # LAN resources e.g. Roku LAN_RESOURCES= # Can be overridden by cmd arg DELMODE=0 # Delete mode? 1-Firewall rules;2-DNSMASQ subnet LOGDROP="logdrop" # Normally 'DROP' EBT_LOGGING=0 # Write EBT trace logging messages to Syslog if [ "$1" == "-h" ] || [ "$1" == "help" ];then echo -e $cBWHT ShowHelp echo -e $cRESET exit 0 fi # Validate args if supplied if [ ! -z $1 ];then if [ "$1" == "status" ];then # Show the details of the ALL rules rather than the details for a specific WiFi Guest interface OPT="verbose" Show_Status "$OPT" echo -e $cRESET exit 0 fi if [ "$(echo $@ | grep -cw 'autodnsmasq')" -gt 0 ];then AUTODNSMASQ="Y" # Insert VLAN config into dnsmasq.conf if it doesn't exist fi if [ "$(echo $@ | grep -cw 'del')" -gt 0 ];then DELMODE=1 fi if [ "$(echo $@ | grep -cw 'delsubnet')" -gt 0 ];then DELMODE=2 fi if [ "$(echo $@ | grep -cw 'ebtlog')" -gt 0 ];then EBT_LOGGING=1 fi if [ "$(echo $@ | grep -ic 'ssid=')" -gt 0 ];then SSID_NAME=$(echo "$@" | sed -n "s/^.*ssid=//p" | awk '{print $1}') # Apply new SSID fi if [ "$(echo $@ | grep -ic 'ip=')" -gt 0 ];then LAN_RESOURCES=$(echo "$@" | sed -n "s/^.*ip=//p" | awk '{print $1}' ) # LAN resources to allow Guest WiFi access to LAN_RESOURCES=$(echo "$LAN_RESOURCES" | cut -d" " -f1 | tr "," " ") fi # Valid WiFi interface? if [ $DELMODE -eq 0 ];then if [ ! -z $( echo $1 | grep -E "^eth[1|2]|wl[0|1]\.[1-3]$") ];then SSID=$(nvram get $1"_ssid") WIFI_DEFINED=`ifconfig | grep $1` if [ -z $SSID ]; then echo -e "\a"$cBRED Say "**ERROR** Guest WiFi" $1 "NOT enabled - interface doesn't have a SSID?" $SSID echo -e $cRESET exit 99 else WIFI_IF=$1 WIFI_PREFIX=${WIFI_IF:0:4} WIFI_DEFINED=`ifconfig | grep $WIFI_IF` if [ "$WIFI_DEFINED" == "" ] && [ "$CONFIG_WIFI" != "FORCE" ]; then echo -e "\a"$cBRED Say "**ERROR** Guest WiFi SSID:" $SSID "("$1") not ENABLED!!" echo -e $cRESET exit 98 else # Check if 'Access Intranet' is currently blocked i.e there are usually 2 rules if LAN (intranet) is blocked! RULE_CNT=`ebtables -t filter -L FORWARD | grep -c "$WIFI_IF -j DROP"` #Say "**DEBUG** intranet ebtables rule count:" $RULE_CNT #if [ "$RULE_CNT" != 2 ]; then #Say "**ERROR** Guest WiFi SSID:" $SSID "("$WIFI_IF") already has intranet access!" #exit 97 #fi fi fi else # Check if a Guest WiFi SSID was specified (rather than the actual Guest WiFi interface) WIFI_VAR=`nvram show 2> /dev/null | grep "_ssid" | grep -e "wl[0-1]\." | grep -i $1` #Say "**DEBUG**" $WIFI_VAR if [ -z $WIFI_VAR ]; then echo -e "\a"$cBRED Say "**ERROR** Guest WiFi SSID:" $1 " not found" echo -e $cRESET exit 95 else WIFI_IF=${WIFI_VAR:0:5} SSID=$(nvram get $WIFI_IF"_ssid") #Say "**DEBUG**" $WIFI_VAR fi fi fi fi Parse "$(Get_Subnet_Prefix "$WIFI_IF")" " " LAN_SUBNET_PREFIX LAN_SUBNET # Predefined dnmasq directives?....if not create them if 'autodnsmasq' command arg supplied if [ -z "$(grep -iE "^dhcp-option=$WIFI_IF,3" /etc/dnsmasq.conf | awk 'BEGIN { FS = "," } {print $3}')" ] && [ $USE_DNSMASQ == "Y" ];then if [ "$AUTODNSMASQ" == "Y" ];then RC=$(Config_dnsmasqWIFI "$WIFI_IF") if [ "$?" -eq 1 ];then echo -e "\a"$cBRED Say "***ERROR Guest Wifi" $SSID "("$WIFI_IF") not defined in '/etc/dnsmasq.conf'" $RC echo -e $cRESET exit 99 fi else if [ "$DELMODE" -eq 0 ];then echo -e "\a"$cBRED Say "***ERROR Guest Wifi SSID:" $SSID "("$WIFI_IF") not defined in '/etc/dnsmasq.conf'" $RC "- use 'autodnsmasq' command arg" echo -e $cRESET exit 99 fi fi fi if [ -z "$WIFI_IF" ];then WIFI_IF=$1 # tacky buggy if arg1 is an SSID!!! fi # Get dnsmasq config WIFI_IP=`grep -iE "^dhcp-option=$WIFI_IF,3" /etc/dnsmasq.conf | awk 'BEGIN { FS = "," } {print $3}'` # Extract I/P from 'dhcp-option=$GUEST_IF,3,10.88.241.1' WIFI_SUBNET_PREFIX=${WIFI_IP%.*} # Extract first three octets of I/P WIFI_MASK=`cat /etc/dnsmasq.conf | grep "255." | grep "$WIFI_IF," | awk 'BEGIN { FS = "," } {print $4}'` if [ "${WIFI_IF:0:3}" == "wl0" ];then WIFI_DESC=$WIFI_DESC"2.4GHz Client "${WIFI_IF:4:1} fi if [ "${WIFI_IF:0:3}" == "wl1" ];then WIFI_DESC=$WIFI_DESC"5GHz Client "${WIFI_IF:4:1} fi if [ "$WIFI_IF" == "eth1" ];then WIFI_DESC=$WIFI_DESC"2.4GHz network" fi if [ "$WIFI_IF" == "eth2" ];then WIFI_DESC=$WIFI_DESC"5GHz network" fi # Show the details of the selected WiFi Guest if [ "$(echo $@ | grep -c "status")" -gt 0 ];then OPT= if [ "$(echo $@ | grep -c "status full")" -gt 0 ];then OPT="verbose" fi Show_Status "$OPT" echo -e $cRESET exit 0 fi # Delete request? if [ "$DELMODE" -gt 0 ];then if [ ! -z "$WIFI_IF" ];then /sbin/ifconfig $WIFI_IF down 2> /dev/null # Bounce rather than destroy the standard WiFi!!! /sbin/ifconfig $WIFI_IF up ACTIONS="-D" WIFI_FW "delete" # Reset Firewall and 'ebtables' rules TXT="rules deleted" if [ ! -z "$LAN_RESOURCES" ];then TXT=$TXT" for LAN resources "$LAN_RESOURCES fi if [ "$DELMODE" -eq 2 ];then LAN_IP=$(nvram get lan_ipaddr) FN="/jffs/configs/dnsmasq.conf.add" if [ -f "$FN" ] && [ ! -z "$WIFI_IF" ];then NOW=$(date +"%Y%m%d-%H%M%S") # current date and time cp $FN ${FN}-$NOW # Create 'dnsmasq.conf.add' backup sed -i "/$WIFI_IF/d" $FN 2> /dev/null # Remove existing WIFI definition if it exists fi service restart_dnsmasq 2>&1 >/dev/null TXT="$WIFI_SUBNET_PREFIX.0/24 subnet deleted, reset to Asus default. ${LAN_IP%.*}.0/24" fi echo -e $cBGRE Say "Guest WiFi" $WIFI_DESC "SSID:" $SSID "("$WIFI_IF")" $TXT echo -e $cRESET exit 0 fi else if [ ! -z "$WIFI_IF" ] && [ ! -z "$WIFI_IP" ] && [ ! -z "$WIFI_MASK" ] ;then /sbin/ifconfig $WIFI_IF $WIFI_IP netmask $WIFI_MASK up # Configure the WiFi interface ACTIONS="-D -I" WIFI_FW # Set Firewall and 'ebtables' rules PRE_SSID= if [ ! -z "$SSID_NAME" ];then SSID=$SSID_NAME nvram set ${WIFI_IF}_ssid="$SSID_NAME" # Assign new SSID - it isn't broadcast until 'service restart_wireless' :-( PRE_SSID="New" fi echo -e $cBGRE Say "Guest WiFi" $WIFI_DESC $PRE_SSID "SSID:" $SSID "("$WIFI_IF")" $WIFI_SUBNET_PREFIX.0/24 " subnet created, using DNS" $DNS_TO_USE if [ ! -z "$LAN_RESOURCES" ];then echo -e $cBCYA for LAN_RESOURCE in $LAN_RESOURCES do LAN_RESOURCE_NAME=`grep -i -w "$LAN_RESOURCE" /etc/hosts.dnsmasq | awk '{print $2}'` Say "Guest WiFi" $WIFI_DESC $PRE_SSID "SSID:" $SSID "("$WIFI_IF")" "Access to LAN resource" $LAN_RESOURCE "("$LAN_RESOURCE_NAME") now allowed" done fi else echo -e $cBRED Say "***ERROR*** Guest WiFi" $WIFI_DESC "SSID='"$SSID"' WIFI_IF='"$WIFI_IF"' WIFI_IP='"$WIFI_IP"' WIFI_MASK='"$WIFI_MASK"' WIFI_SUBNET_PREFIX='"$WIFI_SUBNET_PREFIX".0/24'" echo -e $cRESET fi fi echo -e $cRESET exit 0