Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/sh
- VER="v1.03b1"
- #======================================================================================== © 2018 Martineau, v1.03b1
- #
- # Track/Block unsolicited outbound traffic from the IoT devices such as Alexa-capable IPs
- #
- # IoTBlock [-h|help] { 'iot_group' {[ init | del | status} ] } [ track ] [ noapps] [ report ] [ onerule ] [ forcewan ]
- # [all status] [blockntp] [localntp='ntp_server']
- #
- # IoTBlock alexa init
- # IPSET 'Alexa' will be populated with devices from group 'ALEXA' in '/jffs/configs/IPGroups', and Chain 'MyAlexa' will be created
- # User defined apps firewall rules are added if defined in '/jffs/configs/IoT_Alexa.apps'
- # IoTBlock alexa del
- # IPSET 'Alexa' will be flushed, and Chain 'MyAlexa' will be deleted
- # IoTBlock alexa status
- # IPSET 'Alexa' will be listed together with Chain statistics
- # IoTBlock alexa init block
- # Block ALL i.e. no known ACCEPT rules will be used
- # IoTBlock alexa report
- # Scan Syslog for 'BADAlexa' messages and report basic Port/DST/SRC/Protocol statistics.
- # Then these ports can be added to '/jffs/configs/IoT_Alexa.apps'
- # IoTBlock all status
- # All IPSETs will be listed together with Chain statistics
- # IoTBlock alexa init noapps
- # No apps rules are added from '/jffs/configs/IoT_Alexa.apps'
- # IoTBlock alexa init onerule
- # User defined apps firewall rules are added if defined in '/jffs/configs/IoT_Alexa.apps' only once in the chain; not per device
- # IoTBlock alexa init forcewan
- # Replace '-o xxx' -> '-o $WAN_IF' or insert it if not specified.
- # IoTBlock alexa init localntp=192.168.0.1
- # As per example one, but it is assumed that NTPD service is running on the router so Alexa group will use it.
- #
- # /jffs/configs/IPGroups
- # ALEXA Echo-Show,Echo-Dot-KT,Echo-Dot-Guest
- #
- # /jffs/scripts/firewall-start
- # /jffs/scripts/IoTBlock.sh alexa init
- # /jffs/scripts/IoTBlock.sh lifx init track noapps
- # /jffs/scripts/IoTBlock.sh google init track forcewan
- # [URL="https://www.snbforums.com/threads/help-please-need-assistance-stopping-outbound-connections.38086/page-5#post-377324"]IoT VLAN Netdata graph[/URL]
- #*************************************FUNCTIONS***************************************************************
- # Print between line beginning with'#==' to first blank line inclusive
- ShowHelp() {
- awk '/^#==/{f=1} f{print; if (!NF) exit}' $0
- }
- Say(){
- echo -e $$ $@ | logger -st "($(basename $0))"
- }
- SayT(){
- echo -e $$ $@ | logger -t "($(basename $0))"
- }
- # shellcheck disable=SC2034
- ANSIColours() {
- cRESET="\e[0m";cBLA="\e[30m";cRED="\e[31m";cGRE="\e[32m";cYEL="\e[33m";cBLU="\e[34m";cMAG="\e[35m";cCYA="\e[36m";cGRA="\e[37m"
- cBGRA="\e[90m";cBRED="\e[91m";cBGRE="\e[92m";cBYEL="\e[93m";cBBLU="\e[94m";cBMAG="\e[95m";cBCYA="\e[96m";cBWHT="\e[97m"
- aBOLD="\e[1m";aDIM="\e[2m";aUNDER="\e[4m";aBLINK="\e[5m";aREVERSE="\e[7m"
- cRED_="\e[41m";cGRE_="\e[42m"
- }
- # 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 -- "$@" <<EOF
- $TEXT
- EOF
- }
- Check_Router_Mode() {
- local OK=1 # Assume not Router mode
- case "$(nvram get sw_mode)" in
- 0) SW_MODE="Unconfigured";;
- 1) SW_MODE="Router";OK=0;;
- 2) SW_MODE="Repeater";;
- 3) SW_MODE="AP";;
- 4) SW_MODE="Hotspot";;
- *) SW_MODE="Unknown nvram sw_mode value="$(nvram get sw_mode);;
- esac
- echo $SW_MODE
- return $OK
- }
- Get_WAN_IF_Name () {
- local IF_NAME=$(nvram get wan0_ifname) # DHCP/Static ?
- # Usually this is probably valid for both eth0/ppp0e ?
- if [ "$(nvram get wan0_gw_ifname)" != "$IF_NAME" ];then
- local IF_NAME=$(nvram get wan0_gw_ifname)
- fi
- if [ ! -z "$(nvram get wan0_pppoe_ifname)" ];then
- local IF_NAME="$(nvram get wan0_pppoe_ifname)" # PPPoE
- fi
- echo $IF_NAME
- }
- Firewall(){
- DUMMY="################################################################################## 102"
- local JUNK=$(echo -e $@ | sed 's/"/""/g')
- iptables $@ 2>/dev/null # Suppress error messages
- local FIREWALL_ERROR=$?
- if [ "$FIREWALL_ERROR" -gt 0 ];then # Report error for "-I" insert and '-A' append actions
- if [ "$1" == "-I" ] || [ "$1" == "-A" ];then
- # Hack using I/O to file!
- iptables $@ 2>/tmp/iptables.error$$; echo -e $cBRED"\t" $(grep "" -m1 /tmp/iptables.error$$);rm /tmp/iptables.error$$ # Hack using I/O to file!
- echo -e "\t\t==> $@"
- return 1
- fi
- fi
- return 0
- }
- ExpandIPRange() {
- # '192.168.1.30 192.168.1.50-192.168.1.54' -> '192.168.1.30 192.168.1.50 192.168.1.51 192.168.1.52 192.168.1.53 192.168.1.54'
- local HOST_NAME=0 # Hostname found/returned
- local IP_LIST=
- local START_RANGE=
- local END_RANGE=
- local NUM=
- local MAX=
- local LANIPADDR=`nvram get lan_ipaddr`
- local LAN_PREFIX=${LANIPADDR%.*} # 1.2.3.99 -> 1.2.3
- for THIS in $@
- do
- if [ -n "$(echo "$THIS" | grep -E "^#")" ];then
- break # Ignore comment
- fi
- # If any alphabetic characters then assume it is a name e.g. LIFX-Table_light
- if [ -z "$(echo $THIS | grep "[A-Za-z]")" ];then
- if [ -n "$(echo $THIS | grep "-")" ];then
- Parse $THIS "-" START_RANGE END_RANGE # 1.2.3.90-1.2.3.99 -> 1.2.3.90 1.2.3.99
- local START_PREFIX=${START_RANGE%.*} # 1.2.3.90 -> 1.2.3
- local END_PREFIX=${END_RANGE%.*} # 1.2.3.99 -> 1.2.3
- if [ "$START_PREFIX" != "$END_PREFIX" ];then # Restrict range of devices to 254
- Say "***ERROR*** invalid IP range" $THIS
- echo ""
- return 100
- fi
- NUM=${START_RANGE##*.} # Extract 4th octet 1.2.3.90 -> 90
- MAX=${END_RANGE##*.} # Extract 4th octet 1.2.3.99 -> 99
- while [ $NUM -le $MAX ]
- do
- IP_LIST=$IP_LIST" "$START_PREFIX"."$NUM
- NUM=$(($NUM+1))
- done
- else
- local THIS_PREFIX=${THIS%.*}
- if [ "$THIS_PREFIX" != "$LAN_PREFIX" ];then
- Say "***ERROR '"$THIS"' is not on this LAN '"$LAN_PREFIX".0/24'"
- echo ""
- return 200
- else
- IP_LIST=$IP_LIST" "$THIS # Add to list
- fi
- fi
- else
- # Let the caller ultimately decide if non-IP is valid!!!
- #Say "**Warning non-IP" $THIS
- IP_LIST=$IP_LIST" "$THIS # Add to list
- HOST_NAME=1
- fi
- shift 1
- done
- echo $IP_LIST
- if [ $HOST_NAME -eq 1 ];then
- return 300
- else
- return 0
- fi
- }
- Convert_TO_IP () {
- # Perform a lookup if a hostname (or I/P address) is supplied and is not known to PING
- # NOTE: etc/host.dnsmasq is in format
- #
- # I/P address hostname
- #
- local USEPATH="/jffs/configs"
- if [ -n "$1" ];then
- if [ -z $2 ];then # Name to IP Address
- local IP_NAME=$(echo $1 | tr '[a-z]' '[A-Z]')
- local IP_RANGE=$(ping -c1 -t1 -w1 $IP_NAME 2>&1 | tr -d '():' | awk '/^PING/{print $3}')
- # 127.0.53.53 for ANDROID? https://github.com/laravel/valet/issues/115
- if [ -n "$(echo $IP_RANGE | grep -E "^127")" ];then
- local IP_RANGE=
- fi
- if [ -z "$IP_RANGE" ];then # Not PINGable so lookup static
- IP_RANGE=$(grep -i "$IP_NAME" /etc/hosts.dnsmasq | awk '{print $1}')
- #Say "Lookup '$IP_NAME' in DNSMASQ returned:>$IP_RANGE<"
- # If entry not matched in /etc /hosts.dnsmasq see if it exists in our IPGroups lookup file
- #
- # KEY I/P address[ {,|-} I/P address]
- #
- if [ -z "$IP_RANGE" ] && [ -f $USEPATH/IPGroups ];then
- #IP_RANGE=$(grep -i "^$IP_NAME" $USEPATH/IPGroups | awk '{print $2}')
- IP_RANGE=$(grep -i "^$IP_NAME" $USEPATH/IPGroups | awk '{$1=""; print $0}' | tr ',' ' ' | tr ':' '-')
- Say "Lookup '$IP_NAME' in '$USEPATH/IPGroups' returned:>$IP_RANGE<"
- fi
- fi
- else # IP Address to name
- IP_RANGE=$(nslookup $1 | grep "Address" | grep -v localhost | cut -d" " -f4)
- fi
- else
- local IP_RANGE= # Return a default WiFi Client????
- #IP_NAME="Nexus-7"
- #IP_RANGE=`grep -i $IP_NAME /etc/hosts.dnsmasq | awk '{print $1}'`
- #Say "DEFAULT '$IP_NAME' lookup returned:>$IP_RANGE<"
- fi
- echo $IP_RANGE
- }
- Hostname_from_IP () {
- local HOSTNAMES=
- for IP in $@
- do
- local HOSTNAME=$(Convert_TO_IP "$IP" "Reverse")
- HOSTNAMES=$HOSTNAMES" "$HOSTNAME
- done
- echo $HOSTNAMES
- }
- Chain_exists() {
- # Args: {chain_name} [table_name]
- local CHAIN="$1"
- shift
- [ $# -eq 1 ] && local TABLE="-t $1"
- iptables $TABLE -n -L $CHAIN >/dev/null 2>&1
- local RC=$?
- if [ $RC -ne 0 ];then
- echo "N"
- return 1
- else
- echo "Y"
- return 0
- fi
- }
- Print_IPSET () {
- echo -en "\n"
- #ipset $LIST $1 | head | grep -v "^[0-9]" # Sadly 'ipset -t xxxxxx' to list only the IPSET header doesn't work on Asus
- ipset $LIST $1 -t # IPSET v6 does!!!!
- if [ "$2" == "list" ] && [ -z "$3" ] ;then # Verbose if $2=list'
- ipset $LIST $1 | \
- grep -E "^[0-9]" | \
- sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 | \
- awk ' {printf "%15s\t", $1;}'
- echo -e "\n"
- echo -e "\tTotal="$(ipset $LIST $1 | grep -E "^[0-9]" | wc -l)"\n"
- #else
- # ipset $LIST $1 | \
- # grep -E "^[0-9]" | grep -E "timeout 0$" | \
- # sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 | \
- # awk ' {printf "%15s\t", $1;}'
- # echo -e "\n"
- # echo -e "\tTotal="$(ipset $LIST $1 | grep -E "^[0-9]" | grep -E "timeout 0$" | wc -l)"\n"
- fi
- }
- Show_Status () {
- local IOT_GROUPS=$1
- if [ "$1" == "All" ];then
- local IOT_GROUPS=$(iptables -nvL --line -t filter | awk '/match-set/ {print $(NF-1)}')
- fi
- for IOT_GROUP in $IOT_GROUPS
- do
- if [ "$2" == "IPSET" ];then
- # Print devices
- echo -e $cBMAG >&2
- ipset $LIST $IOT_GROUP >/dev/null 2>&1;if [ $? -eq 0 ]; then
- if [ -z "$(echo "$1" | grep -oiE "$IOT_PRODUCTS")" ];then
- Print_IPSET $IOT_GROUP "list"
- else
- ipset $LIST $IOT_GROUP
- fi
- else
- echo -e $cBRED"\a" >&2
- Say "***ERROR '"$IOT_GROUP"' IPSET does not exist!"
- fi
- echo -e
- Print_IPSET ${IOT_GROUP}TRK "list"
- fi
- # Display rules
- echo -e $cBYEL"\n\tIoT rules:\n" >&2
- if [ "$(Chain_exists "My"$IOT_GROUP)" == "Y" ];then
- #$IPT -nvL $CHAIN --line -t filter | grep -E "$CHAIN|pkts bytes|!tun2+|udp dpt:123"$REGEXP # Display I/P Addresses
- $IPT -nvL "My"$IOT_GROUP --line -t filter
- fi
- # Were any "localntp="
- if [ -n "$($IPT -t nat -nvL PREROUTING | grep -E ":123.*$IOT_GROUP")" ];then
- echo -e $cBCYA"\n\tNTP redirect rules:\n" >&2
- $IPT -t nat --line -nvL PREROUTING | grep -E "Chain|pkts|:123.*$IOT_GROUP"
- fi
- if [ $SCANLOG -eq 1 ];then
- echo -e $cBCYA >&2
- Scan_Syslog
- fi
- done
- }
- Scan_Syslog () {
- if [ -z "$(ps | grep -v grep | grep -m 1 syslog-ng)" ];then # v1.02
- local SYSLOG="/tmp/syslog.log"
- else
- local SYSLOG="/opt/var/log/messages"
- fi
- # Useful scan log function for port traffic
- #grep "XXXXX" $SYSLOG | grep -vE "443|80|123|53|ICMP" | sed 's/^.*SRC=//;s/LEN=.*PROTO=//;s/.SEQ=.*$//;s/DST=//;s/LEN=.*$//' | sort -r | uniq -c | awk ' {printf "%5d %-16s-> %-15s %3s %-9s %-9s http://www.speedguide.net/port.php?port=%s\n", $1,$2,$3,$4,$5,$6, substr($6,5)}'
- grep "BAD"$IOT_GROUP $SYSLOG | sed 's/^.*SRC=//;s/LEN=.*PROTO=//;s/.SEQ=.*$//;s/DST=//;s/LEN=.*$//' | \
- sort -r | uniq -c | \
- awk ' {printf "%5d %-16s-> %-15s %3s %-9s %-9s http://www.speedguide.net/port.php?port=%s\n", $1,$2,$3,$4,$5,$6, substr($6,5)}'
- echo -e $cBYEL"\n\nUnknown TCP/UDP Ports" >&2
- # Ignore known TCP and UDP ports
- ipset list ${IOT_GROUP}TRK | grep -vE "tcp:[443|80|53]" | grep -vE "udp:[53]"
- grep "BAD"$IOT_GROUP $SYSLOG | grep -vE "DPT=[443|80|53]" | sed 's/^.*SRC=//;s/LEN=.*PROTO=//;s/.SEQ=.*$//;s/DST=//;s/LEN=.*$//' | \
- sort -r | uniq -c | \
- awk ' {printf "%5d %-16s-> %-15s %3s %-9s %-9s http://www.speedguide.net/port.php?port=%s\n", $1,$2,$3,$4,$5,$6, substr($6,5)}'
- }
- IPv4_nslookup () {
- local IP_LIST=$(nslookup $1| grep -woE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v 127.0.0.1)
- echo "$IP_LIST"
- if [ -n "$IP_LIST" ];then
- return 0
- else
- return 1
- fi
- }
- #======================================Main==================================================================================
- Main() { true; } # Syntax that is Atom Shellchecker compatible!
- ANSIColours
- # v384.13+ NVRAM variable 'lan_hostname' supersedes 'computer_name'
- [ -n "$(nvram get computer_name)" ] && MYROUTER=$(nvram get computer_name) || MYROUTER=$(nvram get lan_hostname)
- FIRMWARE=$(echo $(nvram get buildno) | awk 'BEGIN { FS = "." } {printf("%03d%02d",$1,$2)}')
- WAN_IF=$(Get_WAN_IF_Name)
- # Can only run in Router Mode;
- if [ "$(Check_Router_Mode)" != "Router" ];then
- echo -e $cBRED"\a\n\n\n\n\t\t\t\t** "$(Check_Router_Mode)" mode is not supported **\t\t\t\t\t\n\n\n"$cRESET
- exit 999
- fi
- # Need assistance!???
- if [ "$1" == "help" ] || [ "$1" == "-h" ]; then
- echo -e $cBWHT
- ShowHelp
- echo -e $cRESET
- exit 0
- fi
- if [ $(echo $@ | grep -cw "debug") -gt 0 ];then # 'debug' requested?
- DEBUG="debug"
- if [ "$1" == "debug" ];then
- shift # Remove from arg list!
- fi
- echo -e "\n\n\t\t"$cBWHT$aBLINK"DEBUG mode enabled"\t"$cBWHT$aBLINK"DEBUG mode enabled"\n\n"$cRESET
- set -x # Enable trace
- fi
- echo -e $cBWHT
- Say $VER "IoT Firewall blocking...." $@
- IPT="/usr/sbin/iptables"
- modprobe -sv xt_comment.ko # v1.10
- [ $? -gt 0 ] && { echo -e $cBRED"\a\n\t***ERROR modprobe unable to load iptables 'comment' module 'xt_comment.ko'\n"$cRESET; exit 90; }
- # 380.63+ for ARM routers, IPSET v6 is available...Load appropriate IPSET modules
- case $(ipset -v | grep -o "v[4,6]") in
- v6) MODULES="ARM"; MATCHSET='--match-set'; LIST='list'; CREATE='create'; SAVE='save'; RESTORE='restore'; FLUSH='flush'; DESTROY='destroy'; ADD='add'; SWAP='swap'; TEST='test'; DELETE="del"
- IPHASH='hash:ip'; NETHASH='hash:net'; PORTBITMAP="bitmap:port range 1-65535"; IPPORT=; SETNOTFOUND='name does not exist'; TIMEOUT='timeout'
- lsmod | grep -q "xt_set" || for module in ip_set ip_set_hash_net ip_set_hash_ip xt_set
- do modprobe $module; done;;
- v4) MODULES="MIPS";MATCHSET='--set'; LIST='--list'; CREATE='--create'; SAVE='--save'; RESTORE='--restore'; FLUSH='--flush'; DESTROY='--destroy'; ADD='--add'; SWAP='--swap'; TEST='--test'; DELETE="--del"
- IPHASH='iphash'; NETHASH='nethash'; PORTBITMAP="portmap --from 1 --to 65535"; IPPORT="ipporthash"; SETNOTFOUND='Unknown set'; TIMEOUT=; RETAIN_SECS=
- lsmod | grep -q "ipt_set" || for module in ip_set ip_set_nethash ip_set_iphash ipt_set
- do modprobe $module; done;;
- *) Say "**ERROR** Unknown ipset version: $(ipset -v). Exiting." && (echo -e "\a";exit 99);;
- esac
- # USB Location of IPSET save/restore configuration directory specified?
- if [ "$(echo $@ | grep -o "dir=" | wc -w )" -eq 1 ];then
- #Parse "$(echo $@ | grep -oE "dir=.*")" "=" v1 DIR
- #DIR=$(echo $DIR | sed 's/ //g') # Tacky! regexp should be used properly!
- DIR=$(echo $@ | sed -n "s/^.*dir=//p" | awk '{print $1}')
- if [ ! -d $DIR ];then # Must be only digits and not blank or '0'
- SayT $VER "***ERROR 'dir=$DIR' directory invalid! - ABORTing"
- echo -e $cBRED"\a\n\t***ERROR $aBLINK'dir=$DIR'\e[25m directory invalid! - ABORTing\n"$cRESET
- exit 94
- fi
- else
- if [ -d "/tmp/mnt/"$MYROUTER ];then
- DIR="/tmp/mnt/"$MYROUTER # <== USB Location of IPSET save/restore configuration
- else
- DIR="/tmp" # NOTE: /TMP isn't permanent! ;-) but allows testing of save/restore
- fi
- fi
- echo -en $cBRED
- IOT_PRODUCTS="Alexa|Hive|Tplink|Lifx|Nest|Hue|Ring|WeMo|Google|Roku"
- IOT_GROUP="all"
- if [ -n "$1" ] && [ "$1" != "status" ] && [ "$1" != "report" ] && [ "$1" != "init" ];then
- IOT_GROUP=$1
- fi
- # Change 'alexa' -> Alexa and 'iot' -> 'Iot'
- IOT_GROUP=$(echo $IOT_GROUP | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}')
- # Don't clutter the FORWARD chain
- CHAIN="My"$IOT_GROUP
- APP_RULES= # Add Known Application port rules
- if [ "$( echo $@ | grep -cw "noapps" )" -eq 1 ];then
- APP_RULES="noapps"
- fi
- TRACK_PORTS=1 # Use 'logaccept' rather than ACCEPT
- ALLOW_ALL=0
- if [ "$( echo $@ | grep -cw "track" )" -eq 1 ];then
- ALLOW_ALL=1
- fi
- SYSLOGMSG= # Write Syslog [BADxxxx] messages
- if [ "$( echo $@ | grep -cw "logmsg" )" -eq 1 ];then
- SYSLOGMSG="logmsg"
- fi
- SCANLOG=0
- if [ "$( echo $@ | grep -cw "report" )" -eq 1 ];then
- SCANLOG=1
- fi
- ONE_RULE= # Create apps rules for each device
- if [ "$( echo $@ | grep -cw "onerule" )" -eq 1 ];then
- ONE_RULE="onerule" # Create one apps rule for the group chain
- fi
- FORCEWAN= # Ensure '-o $WAN_IF' is in apps firewall rule
- if [ "$( echo $@ | grep -cw "forcewan" )" -eq 1 ];then
- FORCEWAN="forcewan"
- fi
- TRACK_NTP=0
- if [ "$( echo $@ | grep -cw "trackntp" )" -eq 1 ];then
- TRACK_NTP=1
- fi
- BLOCKNTP=0
- if [ "$( echo $@ | grep -cw "blockntp=" )" -eq 1 ];then
- BLOCKNTP=1
- fi
- LOCALNTP=
- if [ "$( echo $@ | grep -c "localntp=" )" -eq 1 ];then
- LOCALNTP=$(echo $@ | sed -n "s/^.*localntp=//p" | awk '{print $1}')
- fi
- if [ "$2" == "status" ] || [ "$2" == "report" ] || [ "$1" == "status" ] || [ "$1" == "report" ];then
- echo -en $cBCYA
- Show_Status $IOT_GROUP "IPSET"
- echo -e $cRESET
- exit 0
- fi
- # Check for group names, and expand as necessary
- # e.g. '192.168.1.30,192.168.1.50-192.168.1.54' -> '192.168.1.30 192.168.1.50 192.168.1.51 192.168.1.52 192.168.1.53 192.168.1.54'
- if [ -f "/jffs/configs/IPGroups" ];then # '/jffs/configs/IPGroups' two columns
- # ID xxx.xxx.xxx.xxx[[,xxx.xxx.xxx.xxx][-xxx.xxx.xxx.xxx]
- #IOT_DEVICES=$(grep -iwE "^$IOT_GROUP" /jffs/configs/IPGroups | awk '{print $2}')
- IOT_DEVICES=$(grep -iwE "^$IOT_GROUP" /jffs/configs/IPGroups | awk '{$1=""; print $0}')
- else
- #IOT_DEVICES="10.88.8.120,10.88.8.122-10.88.8.123,10.88.8.124:10.88.8.125" # Silly example to illustrate how multiple ranges can be specified!
- IOT_DEVICES=
- fi
- # Expand the list of IoT IPs as necessary
- # e.g. '192.168.1.30,192.168.1.50-192.168.1.54' -> '192.168.1.30 192.168.1.50 192.168.1.51 192.168.1.52 192.168.1.53 192.168.1.54'
- IOT_DEVICES=$(echo $IOT_DEVICES | tr ',' ' ') # CSVs ?
- IOT_DEVICES=$(echo $IOT_DEVICES | tr ':' '-') # Alternative range spec xxx.xxx.xxx.xxx:xxx.xxx.xxx.xxx
- # Expand any ranges - allow Hostnames e.g. LIFX-Table_light to pass through
- if [ -n "$(echo "$IOT_DEVICES" | grep "-")" ];then # xxx-yyy range ?
- IOT_DEVICES="$(ExpandIPRange "$IOT_DEVICES")"
- RC=$? # Should really check
- fi
- if [ -z "$IOT_DEVICES" ];then
- echo -e $cBRED
- Say "***ERROR No IoT '"$IOT_GROUP"' group devices defined - see /jffs/configs/IPGroups RC="$RC
- echo -e "\a"$cRESET
- exit 1
- fi
- # Create or Delete rules for the specified IoT group of devices
- if [ "$2" == "init" ] || [ "$2" == "del" ];then
- case $2 in
- init)
- # If IoT IPSET doesn't exist then create it, else wipe its contents
- ipset $LIST $IOT_GROUP >/dev/null 2>&1;if [ $? -ne 0 ]; then
- ipset $CREATE $IOT_GROUP $IPHASH comment
- else
- ipset $FLUSH $IOT_GROUP
- fi
- # If IoT Tracking IPSET doesn't exist then create it
- ipset $LIST ${IOT_GROUP}TRK >/dev/null 2>&1;if [ $? -ne 0 ]; then
- ipset $CREATE ${IOT_GROUP}TRK $IPHASH,port comment
- fi
- #for DEVICE in Echo_Show Echo_Bedroom Echo_Guest HS110-Lounge1 HS110-Lounge2 \
- # LIFX-Table LIFX-Reading Hive-Hub Nest1 Ring-Doorbell Chromecast-Main
- for DEVICE in $IOT_DEVICES
- do
- if [ -n "$(echo "$DEVICE" | grep -E "^#")" ];then
- break # Comment denotes end of devices
- fi
- ADD_COMMENT=
- REVERSE_IP=0
- # Cosmetic...add description for device if not already in IP format
- if [ -n "$(echo "$IOT_GROUP" | grep -oiE "$IOT_PRODUCTS")" ] && [ -z "$(echo $DEVICE | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$')" ];then
- ADD_COMMENT=$DEVICE
- fi
- # Since we are dealing with possibly IoT groups within IoT groups...
- IP_LIST=$(Convert_TO_IP "$DEVICE")
- if [ "$(echo $IP_LIST | wc -w)" -gt 1 ];then
- ADD_COMMENT="IoT Group: ${DEVICE}"
- REVERSE_IP=1 # Reverse lookup this group IP?
- else
- ADD_COMMENT=$DEVICE
- fi
- for IP in $IP_LIST
- do
- HOSTNAMETXT=
- if [ $REVERSE_IP -eq 1 ];then
- HOSTNAMETXT="-->("$(Convert_TO_IP $IP "reverse")")"
- fi
- ipset add $IOT_GROUP $IP comment "${ADD_COMMENT}$HOSTNAMETXT"
- done
- done
- ;;
- del)
- # Trash the IPSET?
- # Hmmm can only flush contents at this point....
- NOW=$(date +"%Y%m%d-%H%M%S") # current date and time
- if [ -f $DIR/$IOT_GROUP.config ];then
- cp $DIR/$IOT_GROUP.config $DIR/$IOT_GROUP.config-$NOW # Create restore backup
- rm $DIR/$IOT_GROUP.config
- fi
- ipset $LIST $IOT_GROUP >/dev/null 2>&1;if [ $? -eq 0 ]; then
- ipset $FLUSH $IOT_GROUP
- fi
- ;;
- esac
- # Firewall modification
- if [ "$2" == "init" ];then # Create rules (but delete them if they already exist to prevent duplicates)
- ACTIONS="-D -I"
- else
- ACTIONS="-D" # Delete rules
- fi
- FWRULENO=
- for ACTION in $ACTIONS
- do
- if [ "$ACTION" == "-I" ];then
- if [ "$(Chain_exists "$CHAIN")" == "N" ];then
- iptables -N $CHAIN
- else
- iptables -F $CHAIN 2> /dev/null
- fi
- Firewall -D FORWARD -m set --match-set $IOT_GROUP src,dst -j $CHAIN
- if [ "$(Chain_exists "other2wan")" == "Y" ];then
- Firewall -I FORWARD "$(iptables -nvL FORWARD --line | grep -E -m 1 "other2wan" | awk '{print $1}')" -m set --match-set $IOT_GROUP src,dst -j $CHAIN
- else
- Firewall -I FORWARD "$(iptables -nvL FORWARD --line | grep -E "ACCEPT all.*state RELATED,ESTABLISHED" | awk '{print $1+1}')" -m set --match-set $IOT_GROUP src,dst -j $CHAIN
- fi
- fi
- END_CHAIN="ACCEPT"
- DROP_ACCEPT="DROP" # Default for chain
- if [ "$TRACK_PORTS" -eq 1 ];then
- END_CHAIN="logaccept"
- fi
- if [ "$ALLOW_ALL" -eq 1 ];then
- DROP_ACCEPT="ACCEPT"
- fi
- if [ "$2" == "init" ] || [ "$2" == "del" ];then
- if [ "$ACTION" == "-I" ];then
- if [ -f /jffs/configs/IoT_$IOT_GROUP.apps ] && [ "$APP_RULES" != "noapps" ];then
- echo -e $cBRED
- APPSCNT=0
- APPSCNTBAD=0
- APPSCNTGOOD=0
- #while read RULE # This old-skool method doesn't read the last line!!! if no LF/CRLF
- while IFS= read -r RULE || [ -n "$RULE" ] # v1.03 This POSIX method does correctly read the last line with or without trailing LF/CRLF
- do
- DEBUG="=======================================================================641 $RULE "
- if [ -z "$(echo $RULE | grep -vE "^#" | grep .)" ];then
- continue
- DEBUG="=======================================================================644 "
- fi
- APPSCNT=$((APPSCNT+1))
- # Add an apps rule for each IP; remove comment| remove leading/trailing spaces | separate IPs with ','
- SRC="-s "$(echo $IOT_DEVICES | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/ /,/g')
- # If 'onerule' is specified then only add a rule for the group
- if [ "$ONE_RULE" == "onerule" ];then
- SRC=
- fi
- # Alter the WAN interface to match the local WAN if it was imported from another system
- # This should happen regardless of the 'forcewan' directive??? i.e. eth0-->vlan2
- if [ "$FORCEWAN" == "forcewan" ] || [ -n "$(echo $RULE | grep -E "\-o eth0")" ];then
- RULE=$(echo $RULE | sed 's/\-o \([a-z]*[0|1|2|3]\) /-o '"$WAN_IF"' /')
- if [ -z "$(echo $RULE | grep -E "\-o")" ];then
- # Explicity add the local outbound WAN interface to the rule
- RULE=$RULE" -o "$WAN_IF
- fi
- fi
- # Check the chain...in case it has been cloned incorrectly
- FCHAIN=$(echo $RULE | sed 's/^.*-A //' | awk '{print $1}')
- if [ "$FCHAIN" != "$CHAIN" ];then
- SayT "***ERROR Expected Chain '-A "$CHAIN"' mismatch? '"$FCHAIN"' with imported apps rule '"$RULE"'"
- echo -e "\a\t***ERROR Expected Chain '-A "$CHAIN"' mismatch? '"$FCHAIN"' with imported apps rule '"$RULE"'"
- APPSCNTBAD=$((APPSCNTBAD+1))
- continue
- fi
- RESULT=$(Firewall $RULE $SRC) # Add the apps rule
- RC=$?
- if [ $RC -ne 0 ];then
- echo -e $RESULT
- $IPT $RULE
- APPSCNTBAD=$((APPSCNTBAD+1))
- else
- APPSCNTGOOD=$((APPSCNTGOOD+1))
- fi
- done < /jffs/configs/IoT_$IOT_GROUP.apps
- TXT=
- if [ $APPSCNTBAD -gt 0 ];then
- TXT=${cBRED}$aBLINK"\aFAILED="${APPSCNTBAD}$cRESET
- fi
- SayT "Apps firewall rules ("$APPSCNT") read from '/jffs/configs/IoT_$IOT_GROUP.apps' Success="$APPSCNTGOOD $TXT
- echo -e $cBGRE"\n\tApps firewall rules ("$APPSCNT") read from '/jffs/configs/IoT_$IOT_GROUP.apps' Success="$APPSCNTGOOD $TXT
- echo -en $cBRED
- else
- #Say "*Warning '/jffs/configs/IoT_$IOT_GROUP.apps'"
- DUMMY=
- fi
- # Default LOG EVERYTHING?
- Firewall -A $CHAIN -j LOG --log-prefix "[BAD$IOT_GROUP] " --log-tcp-sequence --log-tcp-options --log-ip-options
- # Either Block or Track rule
- if [ "$FORCEWAN" == "forcewan" ];then
- Firewall -A $CHAIN -o $WAN_IF -j $DROP_ACCEPT
- else
- Firewall -A $CHAIN -j $DROP_ACCEPT
- fi
- fi
- fi
- # All IoT need to allow NTP (port 123) outbound? - Might be prudent to use router as internal NTPD?
- # NOTE: Allow NTP for ALL LAN devices - reduces number of rules!
- for ACTION in $ACTIONS # Create rules (but delete them if they already exist to prevent duplicates) or simply Delete if requested!
- do
- if [ -n "$LOCALNTP" ] || [ "$BLOCKNTP" -eq 1 ] ;then # v1.02
- for DEVICE in $IOT_DEVICES
- do
- if [ -n "$LOCALNTP" ];then # v1.02
- if [ "$ACTION" == "-I" ];then # Redirect NTP to local NTP server
- iptables -C PREROUTING -t nat -i br0 -s $DEVICE -p udp -m udp --dport 123 -j DNAT --to-destination $LOCALNTP -m comment --comment "$IOT_GROUP" 2> /dev/null
- if [ $? -eq 1 ];then
- iptables $ACTION PREROUTING -t nat -i br0 -s $DEVICE -p udp -m udp --dport 123 -j DNAT --to-destination $LOCALNTP -m comment --comment "$IOT_GROUP"
- fi
- fi
- else
- if [ "$BLOCKNTP" -eq 1 ];then
- #if [ "$ACTION" == "-I" ] && [ "$2" != "init" ];then # Don't allow NTP if 'init blockntp' requested
- Firewall $ACTION $CHAIN $FWRULENO -i br0 -o $WAN_IF -p udp -m udp --dport 123 -j ACCEPT
- #fi
- fi
- fi
- done
- else
- # Track who is referencing the external NTP server the most
- if [ "$TRACK_NTP" -eq 0 ];then
- if [ "$(Chain_exists "$CHAIN")" == "N" ];then
- iptables -N $CHAIN
- fi
- # Allow OUT=* otherwise if we have Policy Route=ALL then it will fail
- Firewall $ACTION $CHAIN $FWRULENO -i br0 -p udp -m udp --dport 123 -j ACCEPT
- else
- Firewall $ACTION $CHAIN $FWRULENO -i br0 -p udp -m udp --dport 123 -j logaccept
- fi
- fi
- done
- if [ "$2" == "del" ];then
- Firewall -D FORWARD -m set --match-set $IOT_GROUP src,dst -j $CHAIN
- Firewall -F $CHAIN 2> /dev/null
- Firewall -X $CHAIN 2> /dev/null
- if [ -n "$(iptables -t nat -nvL PREROUTING | grep -E ":123.*$DEVICE.*$IOT_GROUP")" ];then
- DEVICE_LIST=$(iptables -t nat -nvL PREROUTING | grep -E ":123.*$DEVICE.*$IOT_GROUP" | awk '{print $8}')
- LOCALNTP=$(iptables -t nat -nvL PREROUTING | grep -E -m 1 "$IOT_GROUP" | awk '{print $NF}' | cut -d':' -f2)
- for DEVICE in $DEVICE_LIST
- do
- Firewall -D PREROUTING -t nat -i br0 -s $DEVICE -p udp -m udp --dport 123 -j DNAT --to-destination $LOCALNTP -m comment --comment "$IOT_GROUP" # v1.02
- done
- fi
- fi
- done
- if [ "$2" == "init" ];then
- echo -e $cBCYA
- Show_Status $IOT_GROUP "IPSET"
- else
- echo -e $cBGRE
- Say "IoT (Group "$IOT_GROUP") Blocking DELETED"
- fi
- else
- if [ "$2" == "save" ];then
- echo -e $cBGRE
- Say "Saving IoT (Group "$IOT_GROUP") Blocking config to '"$DIR"/$IOT_GROUP.config'....."
- echo -en $cBRED
- ipset $SAVE $IOT_GROUP > $DIR/$IOT_GROUP.config
- echo -e $cRESET
- exit 0
- else
- echo -e $cBWHT
- ShowHelp
- echo -e $cRESET
- exit 0
- fi
- fi
- echo -e $cBWHT
- TXT="Blocking"
- if [ $ALLOW_ALL -eq 1 ];then
- TXT="Tracking"
- fi
- Say "IoT (Group "$IOT_GROUP") Firewall" $TXT "complete."
- echo -e $cRESET
- #grep -F "10.88.8.126" /opt/var/log/messages | grep -v "BLOCK" | grep -E "ACCEPT|DROP" | sed 's/SEQ=.*$//;s/LEN=.*DF//;s/LEN=[0-9].*$//'
- exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement