Advertisement
MartineauPASTEBIN

Xentrk_Martineau_IPSET_Domains.sh

Oct 23rd, 2018
391
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 12.86 KB | None | 0 0
  1. #!/bin/sh
  2. ####################################################################################################
  3. # Script: IPSET_Domains.sh
  4. # Version 1.3
  5. # Author: Xentrk
  6. # Date: 7-September-2018
  7. #       22-October-2018        v1.1 Modified by Martineau to be generic rather than Netflix specific.
  8. #       24-October-2018        v1.2 Modified by Martineau Added 'dir=' to allow using custom directory rather than Entware's '/opt/tmp' directory for ipset save/rstore
  9. #       10-December-2018       v1.3 Modified by Martineau Added 'autoscan[{=}[logfile]]' option to extract domains from logfile or '/opt/var/log/dnsmasq.log' if 'autoscan=' used
  10. #
  11. # Description:
  12. #    Selective Routing Script for domains using Asuswrt-Merlin firmware.  This version uses the ipset method
  13. #    built into dnsmasq.
  14. #
  15. #
  16. # Usage:     IPSET_Domains.sh   {[0|1|2|3|4|5]  ipset_name  domains[,...]} ['autoscan'] [del]  [dir='directory']
  17. #                              
  18. # Usage:     IPSET_Domains.sh   2   BBC   bbc.co.uk
  19. #                               Create IPSET BBC via VPN Client 2 and autopopulate IPs for domain 'bbc.co.uk'
  20. # Usage:     IPSET_Domains.sh   2   BBC   bbc.co.uk   del
  21. #                               Delete IPSET BBC and remove from VPN Client 2
  22. # Usage:     IPSET_Domains.sh   2   BBC   bbc.co.uk   dir=/mnt/sda1/Backups
  23. #                               As per example one, but use '/mnt/sda1/Backups' rather than Entware's 'opt/tmp' for ipset save/restore
  24. # Usage:     IPSET_Domains.sh   0   x3mRouting_NETFLIX_DNSMASQ   amazonaws.com,netflix.com,nflxext.com,nflximg.net,nflxso.net,nflxvideo.net
  25. #                               Create IPSET x3mRouting_NETFLIX_DNSMASQ via WAN and autopopulate IPs for multiple Netflix domains
  26. # Usage:     IPSET_Domains.sh   2 SKY sky.com autoscan
  27. #                               Create IPSET SKY and extract all matching Top-Level domains containing 'sky.com' from '/opt/var/log/dnsmasq.log'
  28. #                               e.g. ipset=/akadns.net/edgekey.net/edgesuite.net/epgsky.com/sky.com/SKY
  29. #                                    from 'a674.hsar.cdn.sky.com.edgesuite.net/adm.sky.com/assets.sky.com/assets.sky.com-secure.edgekey.net/awk.epgsky.com' etc.
  30.  
  31.  
  32. # Print the names of IPSETs defined in dnsmasq
  33. #for IPSET in $(grep -E "^ipset=" /etc/dnsmasq.conf | awk -F "/" '{print $NF}');do echo $IPSET;ipset list $IPSET -t;done
  34.  
  35. # Loop through all of the domains and perform 'nslookup'
  36. #for DOMAIN in $(grep -E "^ipset=" /etc/dnsmasq.conf | sed 's~/~ ~g; s/ipset=//' | awk '$NF=" " {print $0}');do echo -e "\n\t\tDomain="$DOMAIN"\n";nslookup $DOMAIN;done
  37.  
  38. #
  39. # Grateful:
  40. #   Thank you to @Martineau on snbforums.com for sharing his Selective Routing expertise
  41. #   and on-going support!
  42. #
  43. ####################################################################################################
  44. logger -st "($(basename "$0"))" $$ Starting Script Execution
  45.  
  46. # Uncomment the line below for debugging
  47. #set -x
  48.  
  49. PROGNAME=$(basename "$0")
  50. LOCKFILE_DIR=/tmp
  51. LOCK_FD=200
  52.  
  53. lock() {
  54.     local prefix=$1
  55.     local fd=${2:-$LOCK_FD}
  56.     local lock_file=$LOCKFILE_DIR/$prefix.lock
  57.  
  58.     # create lock file
  59.     eval "exec $fd>$lock_file"
  60.  
  61.     # acquier the lock
  62.     flock -n "$fd" \
  63.         && return 0 \
  64.         || return 1
  65. }
  66.  
  67. error_exit() {
  68.     error_str="$@"
  69.     logger -t "($(basename "$0"))" $$ "$error_str"
  70.     exit 1
  71. }
  72.  
  73. main() {
  74.     lock "$PROGNAME" || error_exit "Exiting $PROGNAME. Only one instance of $PROGNAME can run at one time."
  75.  
  76. ### Define interface/bitmask to route traffic to below
  77. set_fwmark_parms () {
  78.     FWMARK_WAN="0x8000/0x8000"
  79.     FWMARK_OVPNC1="0x1000/0x1000"
  80.     FWMARK_OVPNC2="0x2000/0x2000"
  81.     FWMARK_OVPNC3="0x4000/0x4000"
  82.     FWMARK_OVPNC4="0x7000/0x7000"
  83.     FWMARK_OVPNC5="0x3000/0x3000"
  84. }
  85.  
  86. create_fwmarks () {
  87. # WAN
  88.     ip rule del fwmark "$FWMARK_WAN" > /dev/null 2>&1
  89.     ip rule add from 0/0 fwmark "$FWMARK_WAN" table 254 prio 9990
  90.    
  91. #VPN Client 1
  92.     ip rule del fwmark "$FWMARK_OVPNC1" > /dev/null 2>&1
  93.     ip rule add from 0/0 fwmark "$FWMARK_OVPNC1" table 111 prio 9995
  94.  
  95. #VPN Client 2
  96.     ip rule del fwmark "$FWMARK_OVPNC2" > /dev/null 2>&1
  97.     ip rule add from 0/0 fwmark "$FWMARK_OVPNC2" table 112 prio 9994
  98.  
  99. #VPN Client 3
  100.     ip rule del fwmark "$FWMARK_OVPNC3" > /dev/null 2>&1
  101.     ip rule add from 0/0 fwmark "$FWMARK_OVPNC3" table 113 prio 9993
  102.  
  103. #VPN Client 4
  104.     ip rule del fwmark "$FWMARK_OVPNC4" > /dev/null 2>&1
  105.     ip rule add from 0/0 fwmark "$FWMARK_OVPNC4" table 114 prio 9992
  106.  
  107. #VPN Client 5
  108.     ip rule del fwmark "$FWMARK_OVPNC5" > /dev/null 2>&1
  109.     ip rule add from 0/0 fwmark "$FWMARK_OVPNC5" table 115 prio 9991
  110.  
  111.     ip route flush cache
  112. }
  113.  
  114.  
  115. # Chk_Entware function provided by @Martineau at snbforums.com
  116.  
  117. Chk_Entware () {
  118.  
  119.     # ARGS [wait attempts] [specific_entware_utility]
  120.  
  121.     local READY=1                  # Assume Entware Utilities are NOT available
  122.     local ENTWARE="opkg"
  123.     ENTWARE_UTILITY=                # Specific Entware utility to search for (Tacky GLOBAL variable returned!)
  124.  
  125.     local MAX_TRIES=30
  126.     if [ -n "$2" ] && [ -n "$(echo $2 | grep -E '^[0-9]+$')" ];then
  127.         local MAX_TRIES=$2
  128.     fi
  129.  
  130.     if [ -n "$1" ] && [ -z "$(echo $1 | grep -E '^[0-9]+$')" ];then
  131.         ENTWARE_UTILITY=$1
  132.     else
  133.         if [ -z "$2" ] && [ -n "$(echo $1 | grep -E '^[0-9]+$')" ];then
  134.             MAX_TRIES=$1
  135.         fi
  136.     fi
  137.  
  138.    # Wait up to (default) 30 seconds to see if Entware utilities available.....
  139.    local TRIES=0
  140.    while [ $TRIES -lt $MAX_TRIES ];do
  141.       if [ -n "$(which $ENTWARE)" ] && [ "$($ENTWARE -v | grep -o "version")" == "version" ];then       # Check Entware exists and it executes OK
  142.          if [ -n "$ENTWARE_UTILITY" ];then                                      # Specific Entware utility installed?
  143.             if [ -n "$($ENTWARE list-installed $ENTWARE_UTILITY)" ];then
  144.                 READY=0                                                         # Specific Entware utility found
  145.             else
  146.                 # Not all Entware utilities exist as a stand-alone package e.g. 'find' is in package 'findutils'
  147.                 #   opkg files findutils
  148.                 #
  149.                 #   Package findutils (4.6.0-1) is installed on root and has the following files:
  150.                 #   /opt/bin/xargs
  151.                 #   /opt/bin/find
  152.                 # Add 'executable' as 'stubby' leaves behind two directories containing the string 'stubby'
  153.                 if [ -d /opt ] && [ -n "$(find /opt/ -type f -executable -name $ENTWARE_UTILITY)" ];then
  154.                     READY=0                                                     # Specific Entware utility found
  155.                 fi
  156.             fi
  157.          else
  158.             READY=0                                                             # Entware utilities ready
  159.          fi
  160.          break
  161.       fi
  162.       sleep 1
  163.       logger -st "($(basename $0))" $$ "Entware" $ENTWARE_UTILITY "not available - wait time" $((MAX_TRIES - TRIES-1))" secs left"
  164.       local TRIES=$((TRIES + 1))
  165.    done
  166.  
  167.    return $READY
  168. }
  169.  
  170. # check if /jffs/configs/dnsmasq.conf.add contains 'ipset=' entry for the domains
  171. check_dnsmasq () {
  172.     if [ -s /jffs/configs/dnsmasq.conf.add ]; then  # dnsmasq.conf.add file exists
  173.         if [ "$(grep -c "$1" "/jffs/configs/dnsmasq.conf.add")" -eq "1" ]; then  # see if line exists for $IPSET_NAME
  174.             if [ "$2" == "del" ];then
  175.                 sed -i "/^ipset.*${IPSET_NAME}$/d" /jffs/configs/dnsmasq.conf.add
  176.                 logger -st "($(basename "$0"))" $$ "'"ipset=$1"'" deleted from "'/jffs/configs/dnsmasq.conf.add'"
  177.             fi
  178.         else
  179.             printf "ipset=$1\n" >> /jffs/configs/dnsmasq.conf.add # add 'ipset=' domains entry to dnsmasq.conf.add
  180.         fi
  181.         service restart_dnsmasq > /dev/null 2>&1
  182.     else
  183.         if [ "$2" != "del" ];then
  184.             printf "ipset=$1\n" > /jffs/configs/dnsmasq.conf.add # dnsmasq.conf.add does not exist, create dnsmasq.conf.add
  185.             logger -st "($(basename "$0"))" $$ "'"ipset=$1"'" added to "'/jffs/configs/dnsmasq.conf.add'"
  186.             service restart_dnsmasq > /dev/null 2>&1
  187.         fi
  188.     fi
  189. }
  190.  
  191. check_ipset_list () {
  192.     if [ "$2" != "del" ];then
  193.         if [ "$(ipset list -n $1 2>/dev/null)" != "$1" ]; then #does ipset list exist?
  194.             if [ -s "$DIR/$1" ]; then # does $1 ipset restore file exist?
  195.                 ipset restore -! < "$DIR/$1"   # Restore ipset list if restore file exists at $DIR/$1
  196.                 logger -st "($(basename "$0"))" $$ IPSET restored: $1 from "'$DIR/$1'"
  197.             else
  198.                 ipset create $1 hash:net family inet hashsize 1024 maxelem 65536  # No restore file, so create $1 ipset list from scratch
  199.                 logger -st "($(basename "$0"))" $$ IPSET created: $1 hash:net family inet hashsize 1024 maxelem 65536
  200.             fi
  201.         fi
  202.     else
  203.         if [ "$(ipset list -n $1 2>/dev/null)" == "$1" ]; then
  204.             ipset destroy $1
  205.             logger -st "($(basename "$0"))" $$ IPSET $1 deleted!
  206.         fi
  207.     fi
  208. }
  209.  
  210. # if IPSET is older than 24 hours, save the current IPSET list to disk
  211. check_restore_file_age () {
  212.     if [ -s "$DIR" ]; then
  213.         if [ "$(find $DIR -name $1 -mtime +1 -print 2&>/dev/null )" = "$DIR/$1" ] ; then
  214.             ipset save $1 > "$DIR/$1"
  215.         fi
  216.     fi
  217. }
  218.  
  219. # If cronjob to back up the DOMAINS ipset list every 24 hours @ 2:00 AM does not exist, then create it
  220. check_cron_job () {
  221.     #cru l | grep $1_ipset_list
  222.     cru l | grep $1 2&>/dev/null                        # Martineau Fix
  223.     if [ "$?" = "1" ]; then  # no cronjob entry found, create it
  224.         if [ "$2" != "del" ];then
  225.             cru a $1 "0 2 * * * ipset save $1 > $DIR/$1"
  226.             logger -st "($(basename "$0"))" $$ CRON schedule created: "#"$1"#" "'0 2 * * * ipset save $1'"
  227.         fi
  228.     else
  229.         if [ "$2" == "del" ];then
  230.             cru d $1 "0 2 * * * ipset save $1"
  231.             logger -st "($(basename "$0"))" $$ CRON schedule deleted: "#"$1"#" "'0 2 * * * ipset save $1'"
  232.         fi
  233.     fi
  234. }
  235.  
  236. # Route IPSET to target WAN or VPN
  237. create_routing_rules () {
  238.     iptables -t mangle -D PREROUTING -i br0 -m set --match-set $1 dst -j MARK --set-mark "$TAG_MARK" > /dev/null 2>&1
  239.     if [ "$2" != "del" ];then
  240.         iptables -t mangle -A PREROUTING -i br0 -m set --match-set $1 dst -j MARK --set-mark "$TAG_MARK"
  241.         logger -st "($(basename "$0"))" $$ Selective Routing Rule via $TARGET_DESC created "("TAG fwmark $TAG_MARK")"
  242.     else
  243.         logger -st "($(basename "$0"))" $$ Selective Routing Rule via $TARGET_DESC deleted "("TAG fwmark $TAG_MARK")"
  244.     fi
  245. }
  246.  
  247. set_fwmark_parms
  248. create_fwmarks
  249.  
  250. #==================================================================================================Martineau Hack
  251. VPNID=0
  252. IPSET_NAME=
  253. DOMAINS_LIST=
  254. DIR="/opt"
  255.  
  256.  
  257. AUTOSCAN=                                                           # v1.3
  258. if [ "$(echo "$@" | grep -c 'autoscan')" -gt 0 ];then
  259.     AUTOSCAN=$(echo "$@" | sed -n "s/^.*autoscan=//p" | awk '{print $1}')
  260.     [ -z "$AUTOSCAN" ] && AUTOSCAN="/opt/var/log/dnsmasq.log"
  261.     if [ -n "$AUTOSCAN" ];then
  262.         if [ ! -f "$AUTOSCAN" ];then
  263.             logger -st "($(basename "$0"))" $$ "ERROR 'autoscan=$AUTOSCAN' file NOT found!"
  264.             exit 99
  265.         fi
  266.     fi
  267. fi
  268.  
  269. if [ ! -z "$1" ];then
  270.     VPNID=$1
  271. else
  272.     logger -st "($(basename "$0"))" $$ Warning missing arg1 "'destination_target' 0-WAN or 1-5=VPN," WAN assumed!
  273. fi
  274. if [ ! -z "$2" ];then
  275.     IPSET_NAME=$2
  276. else
  277.     logger -st "($(basename "$0"))" $$ ERROR missing arg2 "'ipset_name'"
  278.     exit 97
  279. fi
  280. if [ ! -z "$3" ] && [ -z "$AUTOSCAN" ];then                             # v1.3
  281.     DOMAINS_LIST="$3"
  282. else
  283.     if [ -z "$AUTOSCAN" ];then                              # v1.3
  284.         logger -st "($(basename "$0"))" $$ ERROR missing arg3 "'domain_list'"
  285.         exit 98
  286.     else
  287.         DOMAIN=$3
  288.         # So having extracted the matching domains                      # v1.3
  289.         # Extract only the two-part TL domain i.e. disregard the sub-domains
  290.         DOMAINS_LIST=$(grep $DOMAIN $AUTOSCAN | grep reply | awk '{print $(NF-2)}' | awk -F\. '{print $(NF-1) FS $NF}' | sort | uniq | tr '\n' ',')
  291.     fi
  292. fi
  293.  
  294. if [ "$(echo "$@" | grep -c 'dir=')" -gt 0 ];then
  295.     DIR=$(echo "$@" | sed -n "s/^.*dir=//p" | awk '{print $1}') # v1.2 Mount point/directory for backups
  296. fi
  297.  
  298. if [ -z "$IPSET_NAME" ] || [ -z "$DOMAINS_LIST" ];then
  299.     logger -st "($(basename "$0"))" $$ ERROR missing args "'target destination' 'ipset_name' 'domain_list'"
  300.     exit 98
  301. fi
  302.  
  303. DOMAINS_LIST=$(echo "$DOMAINS_LIST" | sed 's/,$//' | tr ',' '/')    # v1.3
  304. DNSMASQ_ENTRY="/"$DOMAINS_LIST"/"$IPSET_NAME
  305.  
  306. case $VPNID in
  307.     0)  TAG_MARK=$FWMARK_WAN                                        # Which Target WAN or VPN? Martineau Hack
  308.         TARGET_DESC="WAN"
  309.         ;;
  310.     1|2|3|4|5)  eval "TAG_MARK=\$FWMARK_OVPNC"${VPNID}
  311.                 TARGET_DESC="VPN Client "$VPNID
  312.         ;;
  313.     *)  logger -st "($(basename "$0"))" $$ ERROR "'"$1"'" should be "'0-WAN or 1-5=VPN'"
  314.         exit 99
  315.         ;;
  316. esac
  317.  
  318. # Delete mode?
  319. if [ "$(echo $@ | grep -cw 'del')" -gt 0 ];then
  320.     check_dnsmasq           "$DNSMASQ_ENTRY"    "del"
  321.     check_cron_job          "$IPSET_NAME"       "del"
  322.     create_routing_rules    "$IPSET_NAME"       "del"
  323.     check_ipset_list        "$IPSET_NAME"       "del"
  324. else
  325. #==================================================================================================
  326.     Chk_Entware
  327.     check_dnsmasq           "$DNSMASQ_ENTRY"            # Martineau Hack
  328.     check_ipset_list        "$IPSET_NAME"               # Martineau Hack
  329.     check_restore_file_age  "$IPSET_NAME"               # Martineau Hack
  330.     check_cron_job          "$IPSET_NAME"               # Martineau Hack
  331.     create_routing_rules    "$IPSET_NAME"               # Martineau Hack
  332. fi
  333. logger -st "($(basename "$0"))" $$ Completed Script Execution
  334.  
  335. }
  336. main "$@"                                           # Martineau Hack
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement