Great stuff, David ! Thanks for sharing.
Lonnie > On Jun 5, 2018, at 7:09 PM, David Kerr <da...@kerr.net> wrote: > > If using a WAN failover that uses cellular wireless connection you may need > to carefully control how much traffic you send over that link to avoid very > high data overage costs. To this end I have implemented the following > controls on my Astlinux box... > > 1) Block any given local device by MAC address from using the link > For example... block my Apple TV so no video streaming to that device. > > 2) Block any given TCP/IP port for inbound/outbound traffic. > For example... block ports used by an online backup service > > 3) Rate limit any given device by MAC address to e.g. 256kbps > For example... discourage video streaming to kids iPhones while still > allowing messaging. > > The above can all be implemented in firewall custom rules. Below are the > functions I implemented... > > # > ============================================================================= > ## Function to block devices so that they cannot access network through a > given > ## interface. And/or block traffic to/from a specific tcp/udp port number. > ## >>>Call this function only once per interface > ## Parameters: > ## interface (e.g. eth2) > ## MacAddrs (list of mac addresses) > ## Ports (list of ports) > ## The interface local IP addresses are whitelisted > block_ports_macaddrs() > { > local IFS=' ' > local mac > local interface="$1" > local macs="${2//,/ }" # if comma delimited convert to space delimited > local ports="$(echo $3 | tr -s ' ' ',')" # make sure comma delimited > local chain=$(echo "FORWARD_$interface" | tr [a-z] [A-Z]) # uppercase > interface name > local ipv4=$(ip -4 addr show $interface | grep 'inet ' | awk -F' ' '{ print > $2 }') > local ipv6ula=$(ip -6 addr show $interface | grep 'inet6 fd' | awk -F' ' '{ > print $2 }') > > iptables -N $chain 2>/dev/null > iptables -F $chain > iptables -A FORWARD_CHAIN -o $interface -j $chain > if [ -n "$ipv4" ]; then > ip4tables -A $chain -d "$ipv4" -j ACCEPT > fi > if [ -n "$ipv6ula" ]; then > ip6tables -A $chain -d "$ipv6ula" -j ACCEPT > fi > for mac in $macs; do > echo "[CUSTOM RULE] Block MAC address $mac on interface $interface" > iptables -A $chain -m mac --mac-source $mac -j DROP > done > if [ -n "$ports" ]; then > echo "[CUSTOM RULE] Block ports $ports on interface $interface" > iptables -A $chain -p tcp -m multiport --dports $ports -j DROP > iptables -A $chain -p tcp -m multiport --sports $ports -j DROP > iptables -A $chain -p udp -m multiport --dports $ports -j DROP > iptables -A $chain -p udp -m multiport --sports $ports -j DROP > fi > } > > # > ============================================================================= > ## Function to prepare for rate limiting for traffic between local net and > ## the WAN failover wireguard net. Actual packet selection for rate > ## limiting will take place in iptables. This function limits only > ## internal interfaces or WAN failover, not default EXTIF. > prepare_rate_limits() > { > local interface > local IFS=' ' > for interface in $INT_IF $EXT2IF; do > echo "[CUSTOM RULE] Prepare $interface for rate limiting" > tc qdisc del dev $interface root 2>/dev/null > tc qdisc add dev $interface root handle 1: htb > tc class add dev $interface parent 1: classid 1:1 htb rate 256Kbit > tc class add dev $interface parent 1: classid 1:2 htb rate 512Kbit > tc class add dev $interface parent 1: classid 1:3 htb rate 1024Kbit > tc qdisc add dev $interface parent 1:1 handle 2: sfq perturb 10 > tc qdisc add dev $interface parent 1:2 handle 3: sfq perturb 10 > tc qdisc add dev $interface parent 1:3 handle 4: sfq perturb 10 > tc filter add dev $interface protocol ip parent 1: prio 1 handle 1 fw > flowid 1:1 > tc filter add dev $interface protocol ip parent 1: prio 1 handle 2 fw > flowid 1:2 > tc filter add dev $interface protocol ip parent 1: prio 1 handle 3 fw > flowid 1:3 > done > } > > > ## Function to Rate limit some devices so that they cannot drive up huge data > use. > ## >>>Call this function only once per interface > ## This uses kernel traffic control (tc) rules set on the net interface > ## Parameters: > ## interface (e.g. eth2) > ## MacAddrs (list of mac addresses) > ## LimitMarks (list of limit-marks corresponding to each mac address) > ## Mark 1: 256 Kbps, 2: 512 Kbps, 3: 1 Mbps > ## Inbound packets have the packet mark restored > ## Outbound packets from selected devices are marked and the packet saved > ## The interface local IP addresses are whitelisted > rate_limit_macaddrs() > { > local IFS=' ' > local mac > local interface="$1" > local macs="${2//,/ }" # if comma delimited convert to space delimited > local rate_marks="${3//,/ }" # if comma delimited convert to space delimited > local chain=$(echo "FORWARD_$interface" | tr [a-z] [A-Z]) # uppercase > interface name > local ipv4=$(ip -4 addr show $interface | grep 'inet ' | awk -F' ' '{ print > $2 }') > local ipv6ula=$(ip -6 addr show $interface | grep 'inet6 fd' | awk -F' ' '{ > print $2 }') > > iptables -N $chain -t mangle 2>/dev/null > iptables -F $chain -t mangle > iptables -A FORWARD -t mangle -i $interface -j CONNMARK --restore-mark > iptables -A FORWARD -t mangle -o $interface -j $chain > if [ -n "$ipv4" ]; then > ip4tables -A $chain -t mangle -d "$ipv4" -j ACCEPT > fi > if [ -n "$ipv6ula" ]; then > ip6tables -A $chain -t mangle -d "$ipv6ula" -j ACCEPT > fi > iptables -A $chain -t mangle -j CONNMARK --restore-mark > iptables -A $chain -t mangle -m mark ! --mark 0 -j ACCEPT > for mac in $macs; do > echo "[CUSTOM RULE] Rate limit MAC address $mac on interface $interface" > mark=${rate_marks%% *} > rate_marks=${rate_marks#* } > iptables -A $chain -t mangle -m mac --mac-source $mac -j MARK --set-mark > ${mark:-1} > done > iptables -A $chain -t mangle -j CONNMARK --save-mark > iptables -A $chain -t mangle -j ACCEPT > } > > # > ============================================================================= > ## Make the calls to block / rate limit > > prepare_rate_limits > > rate_limit_macaddrs \ > "$EXT2IF" \ > "52:54:00:43:1c:6e 3c:2e:ff:xx:xx:xx 8c:85:90:xx:xx:xx b8:e8:56:xx:xx:xx" \ > "3 1 1 2" > ##^ TestVM, iPhone, MacBook, iPad > > block_ports_macaddrs \ > "$EXT2IF" \ > "00:08:9B:xx:xx:xx 00:08:9B:xx:xx:xx 00:08:9B:xx:xx:xx 52:54:00:xx:xx:xx > 08:66:98:xx:xx:xx" \ > "4242" > ##^ QNAP, QNAP, QNAP, UbuntuVM, AppleTV > ##^ 4242 = crashplan ports > > > Now if you are using wireguard to create a VPN link to a failover host you > also want to repeat the above settings with "wg0" in place of "$EXT2IF" and > because the wg0 interface is taken down during a wireguard VPN restart you > also need to reset the rate limiting traffic controls "tc" commands in the > up/down script for that. For example... > > #!/bin/bash > ## Action: POST_UP PRE_DOWN > action="$1" > ## WireGuard Interface: (ex. wg0) > interface="$2" > > if [ "$action" = "POST_UP" ]; then > logger -t wireguard -p kern.info "WireGuard VPN is started on '$interface' > interface." > echo "Prepare $interface for rate limiting" > tc qdisc del dev $interface root 2>/dev/null > tc qdisc add dev $interface root handle 1: htb > tc class add dev $interface parent 1: classid 1:1 htb rate 256Kbit > tc class add dev $interface parent 1: classid 1:2 htb rate 512Kbit > tc class add dev $interface parent 1: classid 1:3 htb rate 1024Kbit > tc qdisc add dev $interface parent 1:1 handle 2: sfq perturb 10 > tc qdisc add dev $interface parent 1:2 handle 3: sfq perturb 10 > tc qdisc add dev $interface parent 1:3 handle 4: sfq perturb 10 > tc filter add dev $interface protocol ip parent 1: prio 1 handle 1 fw > flowid 1:1 > tc filter add dev $interface protocol ip parent 1: prio 1 handle 2 fw > flowid 1:2 > tc filter add dev $interface protocol ip parent 1: prio 1 handle 3 fw > flowid 1:3 > elif [ "$action" = "PRE_DOWN" ]; then > logger -t wireguard -p kern.info "WireGuard VPN is stopping '$interface' > interface." > fi > ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Astlinux-users mailing list Astlinux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/astlinux-users Donations to support AstLinux are graciously accepted via PayPal to pay...@krisk.org.