#!/bin/bash if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then echo 'usage: ./natfwd.sh ' exit 1 fi if [ "$UID" -ne 0 ] && [ -z "$SKIP_ROOT_CHECK" ]; then echo 'must be root!' exit 1 fi # Check if we should add an exemption for UFW. ufw=no if [ "$1" = "--ufw" ]; then ufw=yes shift fi action=$1 extip=$2 ports=$3 netnsname=$4 pipeouter=$netnsname"_veth0" pipeinner=$netnsname"_veth1" # use /31 here to minimize chance of something fucking up later # CONFIG: change pouterip and pinnerip to something in case there's a conflict, # but there probably won't be pouterip=192.168.69.42 pinnerip=192.168.69.43 pouteripmasked=$pouterip/31 pinneripmasked=$pinnerip/31 # Used in testing: # -A PREROUTING -d 192.168.1.9/32 -p tcp -m tcp --dport 1337 -j DNAT --to-destination 10.13.37.69 # -A POSTROUTING -d 10.13.37.69/32 -p tcp -m tcp --dport 1337 -j SNAT --to-source 10.13.37.42 dnatrule="PREROUTING -d $extip -p tcp --dport $port -j DNAT --to-destination $pinnerip" snatrule="POSTROUTING -d $pinnerip -p tcp --dport $port -j SNAT --to-source $pouterip" function get_dnat_rule { port=$1 echo "PREROUTING -d $extip -p tcp --dport $port -j DNAT --to-destination $pinnerip" } function get_snat_rule { port=$1 echo "POSTROUTING -d $pinnerip -p tcp --dport $port -j SNAT --to-source $pouterip" } function get_ufw_rule { port=$1 echo "proto tcp from any to $pinnerip port $port" } # CONFIG: make sure this is set right ufwcomment="natfwd_"$netnsname if [ $action = "enable" ]; then echo 'enabling' set -x # Make sure we have IP forwarding enabled. We don't bother disabling # this later. sysctl -w net.ipv4.ip_forward=1 > /dev/null # Create the interface and move to the network namespace. ip link add $pipeouter type veth peer name $pipeinner ip link set $pipeinner netns $netnsname # Assign IP addresses to the new devices. ip addr add $pouteripmasked dev $pipeouter ip netns exec $netnsname ip addr add $pinneripmasked dev $pipeinner # Create the routing rules to allow the packets. for p in $ports; do iptables -t nat -A $(get_dnat_rule $p) iptables -t nat -A $(get_snat_rule $p) done [ "$ufw" = "yes" ] && [ -z "$(ufw status | grep '# $ufwcomment')" ] && \ for p in $ports; do ufw route allow $(get_ufw_rule $p) comment $ufwcomment done # Enable the interfaces. ip link set dev $pipeouter up ip netns exec $netnsname ip link set dev $pipeinner up elif [ $action = "disable" ]; then echo 'disabling' set -x # Delete the routing rules. Deleting the interfaces doesn't # automatically remove these. for p in $ports; do iptables -t nat -D $(get_dnat_rule $p) iptables -t nat -D $(get_snat_rule $p) done if [ "$ufw" = "yes" ]; then rulenum=$(ufw status numbered | grep "# $ufwcomment" | awk '{ print substr($1, 2, length($1) - 2) }') if [ -n "$rulenum" ]; then yes | ufw delete $rulenum fi fi # Deleting this interface deletes everything else associated with it. ip link delete $pipeouter else echo 'actions are "enable" or "disable"' exit 1 fi set +x echo 'OK'