jablonka.czprosek.czf

freenet-router

Subversion Repositories:
[/] [trunk/] [freenet-router/] [etc/] [firewall/] [nat] - Rev 2

Compare with Previous - Blame - Download


#! /bin/bash
# Firewall nove generace pro Czela Debian 3.0
# Autor: Mirek Slugen
# Spoluatori: Michal Perlik, Michal Vondracek, Jan Chmelensky
# Vytvoreno: 06.11.2006
# Naposledy zmeneno: 11.1.2009
# Tento skript muzete volne sirit a upravovat.
#
# TODO: Dodelat nat typu tree, ktery potrebuje zjednoduseni, vycisteni kodu a testovani!!!
#

# prevede /X na xxx.xxx.xxx.xxx
convert_net_to_mask() {
    if let $(((32-$1) > 0)); then
        M4=$(((255 << (32-$1)) & 255))
    else
        M4=255
    fi
    if let $(((24-$1) > 0)); then
        M3=$(((255 << (24-$1)) & 255))
    else
        M3=255
    fi
    if let $(((16-$1) > 0)); then
        M2=$(((255 << (16-$1)) & 255))
    else
        M2=255
    fi
    if let $(((8-$1) > 0)); then
        M1=$(((255 << (8-$1)) & 255))
    else
        M1=255
    fi
}

# prevede napriklad 10.93.55.125/24 na 10.93.55.0/24
convert_ip_to_network() {
    IP_POM=($(tr '/' ' ' <<< "$1"))
    BC=${IP_POM[1]}
    IP_POM=($(tr '.' ' ' <<< "${IP_POM[0]}"))
    BCN="$(($BC - 1))"
    convert_net_to_mask "$BCN"
}

# implementace natu
nat() {
    # Tady zacina nat 1:1
    echo -n "Starting NAT 1:1..."

    # Kontrola na neexistujici konfiguracni soubor
    if [ ! -e $NAT_CONFIG ] && [ "$NAT_TYPE" != "tree" ]; then
        echo "error, $NAT_CONFIG does not exist!"
        return 0
    fi

    [ "$DUMMY_IFACE" == "" ] && DUMMY_IFACE="dummy0"
    [ "$DUMMY_IP" == "" ] && DUMMY_IP="`$IP addr show $DUMMY_IFACE | grep inet | grep -v inet6 | awk '{print \$2}' | cut -d \"/\" -f1`"

    if [ "$NAT_TYPE" == "tree" ]; then
        # Zakladni cast celkem peti stromu
        rm -rf $TMP
        mkdir -p $TMP
        # raw
        echo "*raw" >> "$TMP/table_raw"
        echo ":PREROUTING ACCEPT" >> "$TMP/table_raw"
        echo ":OUTPUT ACCEPT" >> "$TMP/table_raw"
        # nat
        echo "*nat" >> "$TMP/table_nat2"
        echo ":PREROUTING ACCEPT" >> "$TMP/table_nat2"
        echo ":POSTROUTING ACCEPT" >> "$TMP/table_nat2"
        echo ":OUTPUT ACCEPT" >> "$TMP/table_nat2"
        # mangle
        echo "*mangle" >> "$TMP/table_mangle"
        echo ":PREROUTING ACCEPT" >> "$TMP/table_mangle"
        echo ":INPUT ACCEPT" >> "$TMP/table_mangle"
        echo ":FORWARD ACCEPT" >> "$TMP/table_mangle"
        echo ":OUTPUT ACCEPT" >> "$TMP/table_mangle"
        echo ":POSTROUTING ACCEPT" >> "$TMP/table_mangle"
        # filter
        echo "*filter" >> "$TMP/table_fwd2"
        echo ":INPUT ACCEPT" >> "$TMP/table_fwd2"
        echo ":FORWARD ACCEPT" >> "$TMP/table_fwd2"
        echo ":OUTPUT ACCEPT" >> "$TMP/table_fwd2"
        IP_POM=""
        M1=""
        M2=""
        M3=""
        M4=""
        BC=""
        BCN=""
        # vytvorime zakladni chainy
        convert_net_to_mask "`echo $INTERNAL_IP | cut -d/ -f2`"
        INTERNAL_IP2="`echo $INTERNAL_IP | cut -d/ -f1`/$M1.$M2.$M3.$M4"
        convert_net_to_mask "`echo $EXTERNAL_IP | cut -d/ -f2`"
        EXTERNAL_IP2="`echo $EXTERNAL_IP | cut -d/ -f1`/$M1.$M2.$M3.$M4"
        echo ":chain_fwd - [0:0]" >> "$TMP/table_fwd"
        echo "-A FORWARD -s $INTERNAL_IP2 -i $NAT_DEV -j chain_fwd" >> "$TMP/table_fwd"
        echo ":chain_pre - [0:0]" >> "$TMP/table_nat"
        echo "-A PREROUTING -d $EXTERNAL_IP2 -i $NAT_DEV -j chain_pre" >> "$TMP/table_nat"
        echo ":chain_pre_2 - [0:0]" >> "$TMP/table_nat"
        echo "-A PREROUTING -s $INTERNAL_IP2 -d $EXTERNAL_IP2 ! -i $NAT_DEV -j chain_pre_2" >> "$TMP/table_nat"
        echo ":chain_post - [0:0]" >> "$TMP/table_nat"
        echo "-A POSTROUTING -s $INTERNAL_IP2 -o $NAT_DEV -j chain_post" >> "$TMP/table_nat"
        echo ":chain_post_2 - [0:0]" >> "$TMP/table_nat"
        echo "-A POSTROUTING -s $INTERNAL_IP2 -d $INTERNAL_IP2 ! -o $NAT_DEV -j chain_post_2" >> "$TMP/table_nat"
    fi

    echo ""
    # Ne vsude pouzivame jednotnou syntaxi
    while read UserName PublicIP PrivateIP; do # standardni v Czela Debianu, podle me nejlepsi! :)
        if [ "`echo $UserName | cut -c1`" == "#" ]; then
            # Prazdne radky preskocime, jen neprazdne radky povazujeme za uzivatele, tj. oznamime je jako zakomentovane
            if [ "`echo $UserName | cut -c2`" != " " ] && [ "`echo $UserName | cut -c2`" != "" ] && [ "`echo $UserName | cut -c2`" != "#" ]; then
                echo "    $UserName commented"
            fi
        else
            echo "    $UserName ($PrivateIP -> $PublicIP)"
            for LOCAL_IP in $PrivateIP; do
                PORT_TYPE=""
                PUBLIC_PORT=""
                PRIVATE_PORT=""
                # rozdeleni na porty
                if [ "`echo $LOCAL_IP | grep :`" != "" ]; then
                    PORT_TYPE="`echo $LOCAL_IP | cut -d: -f2`"
                    PUBLIC_PORT="`echo $LOCAL_IP | cut -d: -f3`"
                    PRIVATE_PORT="`echo $LOCAL_IP | cut -d: -f4`"
                fi
                # local ip musi byt jako posledni!
                LOCAL_IP="`echo $LOCAL_IP | cut -d: -f1`"
                if [ "`echo $PUBLIC_PORT | grep -`" != "" ]; then
                    PUBLIC_PORT="`echo $PUBLIC_PORT | cut -d- -f1`:`echo $PUBLIC_PORT | cut -d- -f2`"
                fi

                if [ "$NAT_TYPE" == "tree" ]; then
                    LOCAL_IP3=""
                    if [ "`echo $LOCAL_IP | grep /`" == "" ]; then
                        LOCAL_IP2="$LOCAL_IP"
                        LOCAL_IP=$LOCAL_IP/32
                    else
                        LOCAL_POM3="`echo $LOCAL_IP | cut -d/ -f2`"
                        if [ "$LOCAL_POM3" != "32" ]; then
                            convert_net_to_mask "$LOCAL_POM3"
                            LOCAL_IP2="`echo $LOCAL_IP | cut -d/ -f1`/$M1.$M2.$M3.$M4"
                            # pokud dostaneme zadany rozsah, pak natujeme na prvni adresu z daneho rozsahu
                            #LOCAL_IP3="`echo $LOCAL_IP | cut -d. -f1`.`echo $LOCAL_IP | cut -d. -f2`.`echo $LOCAL_IP | cut -d. -f3`.$((`echo $LOCAL_IP | cut -d. -f4 | cut -d/ -f1` + 1))"
                            LOCAL_IP3="`echo $LOCAL_IP | cut -d/ -f1`"
                        else
                            LOCAL_IP2="`echo $LOCAL_IP | cut -d/ -f1`"
                        fi
                    fi
                    if [ "`echo $PublicIP | grep /`" == "" ]; then
                        PublicIP2="$PublicIP"
                        PublicIP="${PublicIP}/32"
                    else
                        if [ "`echo $PublicIP | cut -d/ -f2`" != "32" ]; then
                            echo "Verejna adresa pro $UserName je zadana spatne!"
                        else
                            PublicIP2="`echo $PublicIP | cut -d/ -f1`"
                        fi
                    fi
                    # Tato cast je opet pro generovani stromu
                    # tam kde je rozdeleni podle INTERNAL_IP
                    # chain_post
                    I="0"
                    J="$((`echo $LOCAL_IP | cut -d/ -f2` - `echo $INTERNAL_IP | cut -d/ -f2`))"
                    IP_POM="$LOCAL_IP"
                    CHAINS_POST=""
                    CHAINS_POST_2=""
                    CHAINS_FWD=""
                    IP_POMS=""
                    while [ $I -lt $J ]; do
                        convert_ip_to_network "$IP_POM"
                        CHAIN="${IP_POM[0]}${IP_POM[1]}${IP_POM[2]}${IP_POM[3]}_$BC"
                        if [ "$I" == "0" ]; then
                            LOCAL_POM="$CHAIN"
                        fi
                        CHAIN_POST="chain_post_$CHAIN"
                        CHAIN_POST_2="chain_post_2_$CHAIN"
                        CHAIN_FWD="chain_fwd_$CHAIN"
                        echo ":$CHAIN_POST - [0:0]" >> "$TMP/table_nat"
                        echo ":$CHAIN_POST_2 - [0:0]" >> "$TMP/table_nat"
                        echo ":$CHAIN_FWD - [0:0]" >> "$TMP/table_fwd"
                        IP_POMS="${IP_POM[0]}.${IP_POM[1]}.${IP_POM[2]}.${IP_POM[3]}/$BC $IP_POMS"
                        CHAINS_POST="$CHAIN_POST $CHAINS_POST"
                        CHAINS_POST_2="$CHAIN_POST_2 $CHAINS_POST_2"
                        CHAINS_FWD="$CHAIN_FWD $CHAINS_FWD"
                        IP_POM="$((${IP_POM[0]} & $M1)).$((${IP_POM[1]} & $M2)).$((${IP_POM[2]} & $M3)).$((${IP_POM[3]} & $M4))/$BCN"
                        ((I++))
                    done
                    CHAINS_POST="chain_post $CHAINS_POST"
                    CHAINS_POST_2="chain_post_2 $CHAINS_POST_2"
                    CHAINS_FWD="chain_fwd $CHAINS_FWD"
                    I="1"
                    for IP_POM in $IP_POMS; do
                        CHAIN_POST="`echo $CHAINS_POST | cut -d \" \" -f $I`"
                        CHAIN_POST_2="`echo $CHAINS_POST_2 | cut -d \" \" -f $I`"
                        CHAIN_FWD="`echo $CHAINS_FWD | cut -d \" \" -f $I`"
                        CHAIND_POST="`echo $CHAINS_POST | cut -d \" \" -f $(($I + 1))`"
                        CHAIND_POST_2="`echo $CHAINS_POST_2 | cut -d \" \" -f $(($I + 1))`"
                        CHAIND_FWD="`echo $CHAINS_FWD | cut -d \" \" -f $(($I + 1))`"
                        if [ "`echo $IP_POM | cut -d/ -f2`" != "32" ]; then
                            convert_net_to_mask "`echo $IP_POM | cut -d/ -f2`"
                            IP_POM="`echo $IP_POM | cut -d/ -f1`/$M1.$M2.$M3.$M4"
                        else
                            IP_POM="`echo $IP_POM | cut -d/ -f1`"
                        fi
                        echo "-A $CHAIN_POST -s $IP_POM -j $CHAIND_POST" >> "$TMP/table_nat"
                        echo "-A $CHAIN_POST_2 -d $IP_POM -j $CHAIND_POST_2" >> "$TMP/table_nat"
                        echo "-A $CHAIN_FWD -d $IP_POM -j $CHAIND_FWD" >> "$TMP/table_fwd"
                        ((I++))
                    done
                    # pro ty kde je rozdeleni podle EXTERNAL_IP
                    # chain_pre
                    I="0"
                    J="$((`echo $PublicIP | cut -d/ -f2` - `echo $EXTERNAL_IP | cut -d/ -f2`))"
                    IP_POM="$PublicIP"
                    CHAINS_PRE=""
                    CHAINS_PRE_2=""
                    IP_POMS=""
                    while [ $I -lt $J ]; do
                        convert_ip_to_network "$IP_POM"
                        CHAIN="${IP_POM[0]}${IP_POM[1]}${IP_POM[2]}${IP_POM[3]}_$BC"
                        if [ "$I" == "0" ]; then
                            PUBLIC_POM="$CHAIN"
                        fi
                        CHAIN_PRE="chain_pre_$CHAIN"
                        CHAIN_PRE_2="chain_pre_2_$CHAIN"
                        echo ":$CHAIN_PRE - [0:0]" >> "$TMP/table_nat"
                        echo ":$CHAIN_PRE_2 - [0:0]" >> "$TMP/table_nat"
                        IP_POMS="${IP_POM[0]}.${IP_POM[1]}.${IP_POM[2]}.${IP_POM[3]}/$BC $IP_POMS"
                        CHAINS_PRE="$CHAIN_PRE $CHAINS_PRE"
                        CHAINS_PRE_2="$CHAIN_PRE_2 $CHAINS_PRE_2"
                        IP_POM="$((${IP_POM[0]} & $M1)).$((${IP_POM[1]} & $M2)).$((${IP_POM[2]} & $M3)).$((${IP_POM[3]} & $M4))/$BCN"
                        ((I++))
                    done
                    CHAINS_PRE="chain_pre $CHAINS_PRE"
                    CHAINS_PRE_2="chain_pre_2 $CHAINS_PRE_2"
                    I="1"
                    for IP_POM in $IP_POMS; do
                        CHAIN_PRE="`echo $CHAINS_PRE | cut -d \" \" -f $I`"
                        CHAIN_PRE_2="`echo $CHAINS_PRE_2 | cut -d \" \" -f $I`"
                        CHAIND_PRE="`echo $CHAINS_PRE | cut -d \" \" -f $(($I + 1))`"
                        CHAIND_PRE_2="`echo $CHAINS_PRE_2 | cut -d \" \" -f $(($I + 1))`"
                        if [ "`echo $IP_POM | cut -d/ -f2`" != "32" ]; then
                            convert_net_to_mask "`echo $IP_POM | cut -d/ -f2`"
                            IP_POM="`echo $IP_POM | cut -d/ -f1`/$M1.$M2.$M3.$M4"
                        else
                            IP_POM="`echo $IP_POM | cut -d/ -f1`"
                        fi
                        echo "-A $CHAIN_PRE -d $IP_POM -j $CHAIND_PRE" >> "$TMP/table_nat"
                        echo "-A $CHAIN_PRE_2 -d $IP_POM -j $CHAIND_PRE_2" >> "$TMP/table_nat"
                        ((I++))
                    done
                    # no a strom je za nami
                    # nat 1:1
                    if [ "$PUBLIC_PORT" == "" ]; then
                        # Pokud je vice zaznamu na jednu verejnou ip adresu, pak bude plnohodnotna verejna jen ta prvni (jeste se to da zoptimalizovat o cca 15 vterin pro 1500 zaznamu)
                        if [ "`cat $TMP/table_nat | grep \"chain_pre_$PUBLIC_POM\" | grep DNAT | grep -v \"dport\" | cut -d \" \" -f 4`" != "$PublicIP2" ]; then
                            if [ "$LOCAL_IP3" != "" ]; then
                                echo "-A chain_pre_$PUBLIC_POM -d $PublicIP2 -j DNAT --to-destination $LOCAL_IP3" >> "$TMP/table_nat"
                                echo "-A chain_pre_2_$PUBLIC_POM -d $PublicIP2 -j DNAT --to-destination $LOCAL_IP3" >> "$TMP/table_nat"
                                echo "-A chain_post_2_$LOCAL_POM -d $LOCAL_IP3 -j SNAT --to-source $DUMMY_IP" >> "$TMP/table_nat"
                            else
                                echo "-A chain_pre_$PUBLIC_POM -d $PublicIP2 -j DNAT --to-destination $LOCAL_IP2" >> "$TMP/table_nat"
                                echo "-A chain_pre_2_$PUBLIC_POM -d $PublicIP2 -j DNAT --to-destination $LOCAL_IP2" >> "$TMP/table_nat"
                                echo "-A chain_post_2_$LOCAL_POM -d $LOCAL_IP2 -j SNAT --to-source $DUMMY_IP" >> "$TMP/table_nat"
                            fi
                        fi
                        # Pokud je vice zaznamu na jednu vnitrni ip adresu, pak zahlasime chybu, radeji otevirame fwd, ktery je kratsi! (totot jeste taky jde zoptimalizovat o cca 10 vterin pro 1500 zaznamu)
                        if [ "`cat $TMP/table_fwd | grep \"chain_fwd_$LOCAL_POM\" | grep ACCEPT | grep -v \"dport\" | cut -d \" \" -f 4`" != "$LOCAL_IP2" ]; then
                            echo "-A chain_post_"$LOCAL_POM" -s $LOCAL_IP2 -j SNAT --to-source $PublicIP2" >> "$TMP/table_nat"
                            echo "-A chain_fwd_$LOCAL_POM -d $LOCAL_IP2 -j ACCEPT" >> "$TMP/table_fwd"
                        else
                            echo "Chyba, zaznam pro lokalni adresu uz existuje!"
                        fi
                    elif [ "$PRIVATE_PORT" == "" ]; then
                        # Zatim neni jasne jestli porty funguji spravne!!!
                        # Ohlidani spravnosti je tu dost narocne, to proste musi byt spravne!
                        echo "-A chain_pre_$PUBLIC_POM -d $PublicIP2 -p $PORT_TYPE -m $PORT_TYPE --dport $PUBLIC_PORT -j DNAT --to-destination $LOCAL_IP2" >> "$TMP/table_nat"
                        echo "-A chain_post_"$LOCAL_POM" -s $LOCAL_IP2 -j SNAT --to-source $PublicIP2" >> "$TMP/table_nat"
                        echo "-A chain_fwd_$LOCAL_POM -d $LOCAL_IP2 -p $PORT_TYPE -m $PORT_TYPE --dport $PUBLIC_PORT -j ACCEPT" >> "$TMP/table_fwd"
                        echo "-A chain_pre_2_$PUBLIC_POM -d $PublicIP2 -j DNAT --to-destination $LOCAL_IP2" >> "$TMP/table_nat"
                        echo "-A chain_post_2_$LOCAL_POM -d $LOCAL_IP2 -j SNAT --to-source $DUMMY_IP" >> "$TMP/table_nat"
                    else
                        # Ohlidani spravnosti je tu dost narocne, to proste musi byt spravne!
                        echo "-A chain_pre_$PUBLIC_POM -d $PublicIP2 -p $PORT_TYPE -m $PORT_TYPE --dport $PUBLIC_PORT -j DNAT --to-destination $LOCAL_IP2:$PRIVATE_PORT" >> "$TMP/table_nat"
                        echo "-A chain_post_"$LOCAL_POM" -s $LOCAL_IP2 -j SNAT --to-source $PublicIP2" >> "$TMP/table_nat"
                        echo "-A chain_fwd_$LOCAL_POM -d $LOCAL_IP2 -p $PORT_TYPE -m $PORT_TYPE --dport $PRIVATE_PORT -j ACCEPT" >> "$TMP/table_fwd"
                        echo "-A chain_pre_2_$PUBLIC_POM -d $PublicIP2 -p $PORT_TYPE -m $PORT_TYPE --dport $PUBLIC_PORT -j DNAT --to-destination $LOCAL_IP2:$PRIVATE_PORT" >> "$TMP/table_nat"
                        echo "-A chain_post_2_$LOCAL_POM -d $LOCAL_IP2 -p $PORT_TYPE -m $PORT_TYPE --dport $PRIVATE_PORT -j SNAT --to-source $DUMMY_IP" >> "$TMP/table_nat"
                    fi
                else
#echo $PublicIP $LOCAL_IP
                    # zakladni nat 1:1 - originalni verze
                    if [ "$PUBLIC_PORT" == "" ]; then
                        $IPTABLES -t nat -I PREROUTING -d $PublicIP -j DNAT --to $LOCAL_IP
                        $IPTABLES -t nat -I POSTROUTING -o $NAT_DEV -s $LOCAL_IP -j SNAT --to $PublicIP
                        $IPTABLES -I FORWARD -i $NAT_DEV -d $LOCAL_IP -j ACCEPT
                    elif [ "$PRIVATE_PORT" == "" ]; then
                        $IPTABLES -t nat -I PREROUTING -d $PublicIP -p $PORT_TYPE --dport $PUBLIC_PORT -j DNAT --to $LOCAL_IP
                        $IPTABLES -t nat -I POSTROUTING -o $NAT_DEV -s $LOCAL_IP -j SNAT --to $PublicIP
                        $IPTABLES -I FORWARD -i $NAT_DEV -d $LOCAL_IP -p $PORT_TYPE --dport $PUBLIC_PORT -j ACCEPT
                    else
                        $IPTABLES -t nat -I PREROUTING -d $PublicIP -p $PORT_TYPE --dport $PUBLIC_PORT -j DNAT --to $LOCAL_IP:$PRIVATE_PORT
                        $IPTABLES -t nat -I POSTROUTING -o $NAT_DEV -s $LOCAL_IP -j SNAT --to $PublicIP
                        $IPTABLES -I FORWARD -i $NAT_DEV -d $LOCAL_IP -p $PORT_TYPE --dport $PRIVATE_PORT -j ACCEPT
                    fi
                    # Zaroven by bylo pekne zevnitr site na svou verejnou ip pingnout,
                    # pro tohle je idealne nutne mit dummy, dalsi moznost je nastavit
                    # DUMMY_IFACE na jedno z rozhrani LAN, kde se neprovadi nat.
                    if [ "$DUMMY_IP" != "" ]; then
                        if [ "$PUBLIC_PORT" == "" ]; then
                            $IPTABLES -t nat -I PREROUTING ! -i $NAT_DEV -s $INTERNAL_IP -d $PublicIP -j DNAT --to $LOCAL_IP
                            $IPTABLES -t nat -I POSTROUTING ! -o $NAT_DEV -s $INTERNAL_IP -d $LOCAL_IP -j SNAT --to $DUMMY_IP
                        elif [ "$PRIVATE_PORT" == "" ]; then
                            $IPTABLES -t nat -I PREROUTING ! -i $NAT_DEV -s $INTERNAL_IP -d $PublicIP -p $PORT_TYPE --dport $PUBLIC_PORT -j DNAT --to $LOCAL_IP
                            $IPTABLES -t nat -I POSTROUTING ! -o $NAT_DEV -s $INTERNAL_IP -d $LOCAL_IP -p $PORT_TYPE --dport $PUBLIC_PORT -j SNAT --to $DUMMY_IP
                        else
                            $IPTABLES -t nat -I PREROUTING ! -i $NAT_DEV -s $INTERNAL_IP -d $PublicIP -p $PORT_TYPE --dport $PUBLIC_PORT -j DNAT --to $LOCAL_IP:$PRIVATE_PORT
                            $IPTABLES -t nat -I POSTROUTING ! -o $NAT_DEV -s $INTERNAL_IP -d $LOCAL_IP -p $PORT_TYPE --dport $PRIVATE_PORT -j SNAT --to $DUMMY_IP
                        fi
                    fi
                fi
            done
        fi
    done < $NAT_CONFIG

    # V pripade typu natu "tree", musime spojit soubory do jedine tabulky
    if [ "$NAT_TYPE" == "tree" ]; then
        cat $TMP/table_nat | sort -k 2.1 | uniq >> $TMP/table_nat2
        cat $TMP/table_fwd | sort -k 2.1 | uniq >> $TMP/table_fwd2
        cat "$TMP/table_raw" >> "$TMP/table"
        echo "COMMIT" >> "$TMP/table"
        cat "$TMP/table_nat2" >> "$TMP/table"
        echo "COMMIT" >> "$TMP/table"
        cat "$TMP/table_mangle" >> "$TMP/table"
        echo "COMMIT" >> "$TMP/table"
        cat "$TMP/table_fwd2" >> "$TMP/table"
        echo "COMMIT" >> "$TMP/table"
        echo "done."
    fi
}

Powered by WebSVN 2.2.1