#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:usr/local/bin:/usr/local/sbin:/usr/lib/ksvd/bin
export PATH

NEW_KSVD_RULE_TMP='/etc/firewalld/.direct.xml.tmp'
NEW_KSVD_RULE='/etc/firewalld/direct.xml'

OLD_KSVD_RULE='/etc/firewalld/services/KSVD.xml'
OLD_KSVD_RULE_BAK='/etc/firewalld/services/.KSVD.bak'

DEFAULT_RULE='/etc/firewalld/zones/public.xml'
OLD_KSVD_DEFAULT_RULE_1='/etc/firewalld/zones/.public.xml.bak1'
SETTINGS_GLOBAL_MC='/home/kylin-ksvd/.ksvd/settings.global.mc'

LOGFILE='/var/log/ksvd/1/port_security_node.log'

IPSET_RULE_TMP='/etc/firewalld/ipsets/.ksvdset.xml.tmp'
IPSET_RULE='/etc/firewalld/ipsets/ksvdset.xml'

TMP_IPSET='/tmp/.ksvdset.xml.tmp'
TMP_KSVD='/tmp/.direct.xml.tmp'

# 特权ip文件
PRIVILEGE_FILE="/home/kylin-ksvd/.ksvd-local/privilege_ips"

. /usr/lib/ksvd/etc/sec_port.conf

direct_xml_items=''

log(){
    local prefix=`date "+%Y-%m-%d %H:%M:%S"`" : "
    echo "${prefix}" $@ >> ${LOGFILE}
}

warn(){
    local prefix=`date "+%Y-%m-%d %H:%M:%S"`" : WARN : "
    echo "${prefix}" $@ >> ${LOGFILE}
}

fatal(){
    local prefix=`date "+%Y-%m-%d %H:%M:%S"`" : ERROR : "
    echo "${prefix}" $@ >> ${LOGFILE}
}

generate_direct_xml_item(){

    for element in "${OUTER_TCP_PORTS[@]}" ; do
        if [ "x$element" != "x" ]; then
            direct_xml_items=`echo -e "  <rule priority=\"0\" table=\"filter\" ipv=\"ipv4\" chain=\"IN_public_allow\">-p tcp -m multiport --dports ${element} -j ACCEPT</rule>\n$direct_xml_items"`
        fi
    done

    for element in "${OUTER_UDP_PORTS[@]}" ; do 
        if [ "x$element" != "x" ]; then
            direct_xml_items=`echo -e "  <rule priority=\"0\" table=\"filter\" ipv=\"ipv4\" chain=\"IN_public_allow\">-p udp -m multiport --dports ${element} -j ACCEPT</rule>\n$direct_xml_items"`
        fi
    done

    for element in "${INNER_TCP_PORTS[@]}" ; do 
        if [ "x$element" != "x" ]; then
            direct_xml_items=`echo -e "  <rule priority=\"0\" table=\"filter\" ipv=\"ipv4\" chain=\"IN_public_allow\">-p tcp -m set --match-set ksvdset src -m multiport --dports ${element} -j ACCEPT</rule>\n$direct_xml_items"`
        fi
    done

    for element in "${INNER_UDP_PORTS[@]}" ; do 
        if [ "x$element" != "x" ]; then
            direct_xml_items=`echo -e "  <rule priority=\"0\" table=\"filter\" ipv=\"ipv4\" chain=\"IN_public_allow\">-p udp -m set --match-set ksvdset src -m multiport --dports ${element} -j ACCEPT</rule>\n$direct_xml_items"`
        fi
    done

}

remove_ksvd_rule(){
    if [ -f ${OLD_KSVD_RULE} ]; then
        change3=1
        log "${OLD_KSVD_RULE} is still exist!"
        mv ${OLD_KSVD_RULE} ${OLD_KSVD_RULE_BAK}
        if [ -f ${DEFAULT_RULE} ]; then
            if grep -q '<service name="KSVD"/>' "${DEFAULT_RULE}"; then
                # 文件中存在该行，使用 sed 命令移除该行
                cp ${DEFAULT_RULE} ${OLD_KSVD_DEFAULT_RULE_1}
                sed -i '/<service name="KSVD"\/>/d' "${DEFAULT_RULE}"
                log "ksvd is removed from public.xml !"
            fi
        fi
    fi
}

remove_default_rule(){
    if grep -q "CLOUD_DISK_AVAILABILITY=\"yes\"" ${SETTINGS_GLOBAL_MC} > /dev/null 2>&1;then
        if ! grep -q "samba" ${DEFAULT_RULE} > /dev/null 2>&1; then
            change4=1
            sed -i "/description/a\  <service name=\"samba\"\/>" ${DEFAULT_RULE}
            log "samba is not in  ${DEFAULT_RULE}"
        fi
    else
        if  grep -q "samba" ${DEFAULT_RULE} > /dev/null 2>&1; then
            change4=1
            sed -i '/samba/d' ${DEFAULT_RULE}
            log "samba is still in  ${DEFAULT_RULE}"
        fi
    fi
}

restore_rule(){
    if [ -f ${IPSET_RULE} ]; then
        mv ${IPSET_RULE} ${TMP_IPSET}
    fi

    if [ -f ${NEW_KSVD_RULE} ]; then
        mv ${NEW_KSVD_RULE} ${TMP_KSVD}
    fi

}

rollback_rule(){
    reload=$1

    log "Call rollbackc_rule reload=${reload}"

    if [ -f ${TMP_IPSET} ]; then
        mv ${TMP_IPSET} ${IPSET_RULE}
    fi

    if [ -f ${TMP_KSVD} ]; then
        mv ${TMP_KSVD}  ${NEW_KSVD_RULE}
    fi

    if [ ${reload} -eq 1 ]; then
        firewall-cmd --reload >>${LOGFILE} 2>&1
    fi
}

check_ipv4(){   
    IP=$1   
    if [[ $IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then   
        FIELD1=$(echo $IP|cut -d. -f1)   
        FIELD2=$(echo $IP|cut -d. -f2)   
        FIELD3=$(echo $IP|cut -d. -f3)   
        FIELD4=$(echo $IP|cut -d. -f4)   
        if [ $FIELD1 -le 255 -a $FIELD2 -le 255 -a $FIELD3 -le 255 -a $FIELD4 -le 255 ]; then   
            return 0
        else   
            fatal "IP: $IP not available!"   
            return 1
        fi   
    else   
        fatal "IP($IP) format error!"   
        return 1
    fi
}
  
generate_portsec_config(){
cat > ${IPSET_RULE_TMP}  <<EOF
<?xml version="1.0" encoding="utf-8"?>
<ipset type="hash:ip">
EOF

    for ip in ${ipset}
    do
        check_ipv4 $ip
        if [ $? -ne 0 ]; then
            exit 1
        fi
        
        cat >> ${IPSET_RULE_TMP}  <<EOF
  <entry>${ip}</entry>
EOF
    done

cat >> ${IPSET_RULE_TMP}  <<EOF
</ipset>
EOF

    generate_direct_xml_item

    . /var/lib/ksvd/settings.node
    if [ -z "$UNIQB_NAT_SUBNET" ]; then
        UNIQB_NAT_SUBNET="192.168.84"
    fi

cat > ${NEW_KSVD_RULE_TMP}  <<EOF
<?xml version="1.0" encoding="utf-8"?>
<direct>
${direct_xml_items}
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-i ksvdinat0 -j ACCEPT</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="OUTPUT">-o ksvdinat0 -j ACCEPT</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="FORWARD">-i ksvdinat0 -j ACCEPT</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="FORWARD">-o ksvdinat0 -j ACCEPT</rule>
  <rule priority="0" table="nat" ipv="ipv4" chain="POSTROUTING">-s ${UNIQB_NAT_SUBNET}.0/24 -j MASQUERADE</rule>
</direct>
EOF

    change1=1
    cur_ipsets=`cat ${IPSET_RULE_TMP}`
    if [ -f ${IPSET_RULE} ]; then
        old_ipsets=`cat ${IPSET_RULE}`
        if [ "$old_ipsets"  == "$cur_ipsets" ];then
            log "${IPSET_RULE} ipsets not change!"
            change1=0
        fi
    fi

    change2=1
    cur_rules=`cat ${NEW_KSVD_RULE_TMP}`
    if [ -f ${NEW_KSVD_RULE} ]; then
        old_rules=`cat ${NEW_KSVD_RULE}`
        if [ "$old_rules"  == "$cur_rules" ];then
            log "${NEW_KSVD_RULE} rules not change!"
            change2=0
        fi
    fi

    change3=0
    remove_ksvd_rule

    change4=0
    remove_default_rule

    if [ $change1 -eq 0 -a $change2 -eq 0 -a $change3 -eq 0 -a $change4 -eq 0 ];then
        rm -rf ${IPSET_RULE_TMP}
        rm -rf ${NEW_KSVD_RULE_TMP}
        log " do nothing!"
        exit 0
    fi

    restore_rule

    mv ${IPSET_RULE_TMP} ${IPSET_RULE}
    if [ $? -eq 0 ]; then
        log "mv ${IPSET_RULE_TMP} ${IPSET_RULE} success!"
    else
        fatal "mv ${IPSET_RULE_TMP} ${IPSET_RULE} failed!"
        rollback_rule 0
        exit 1
    fi

    mv ${NEW_KSVD_RULE_TMP} ${NEW_KSVD_RULE}
    if [ $? -eq 0 ]; then
        log "mv ${NEW_KSVD_RULE_TMP} ${NEW_KSVD_RULE} success!"
    else
        fatal "mv ${NEW_KSVD_RULE_TMP} ${NEW_KSVD_RULE} failed!"
        rollback_rule 0
        exit 1
    fi

    systemctl status firewalld | grep running
    if [ $? -ne 0 ]; then
        systemctl start firewalld
    fi

    firewall-cmd --reload
    if [ $? -eq 0 ]; then
        log "firewall-cmd --reload success!"
    else
        fatal "firewall-cmd --reload failed!"
        rollback_rule 1
        exit 1
    fi

    iptables -nvL IN_public_allow >> ${LOGFILE} 2>&1
    ipset list >> ${LOGFILE} 2>&1
}

usage(){
    cat <<EOT

        usage: $1 <ipset> 
EOT
}

if [ $# -ne 1 ]; then
    usage $0
    exit 1
fi
ipset="${1}"
log "-------------------------------------------------------------------"
log "orig ipset=${ipset}"
ipset=`echo "$ipset" | sed 's/ //g'`
ipset=`echo "$ipset" | sed 's/,/ /g'`
ipset_tmp=${ipset}
if [ -f ${PRIVILEGE_FILE} ];then
    privilege_ips=$(cat ${PRIVILEGE_FILE})
    privilege_ips=`echo "$privilege_ips" | sed 's/ //g'`
    privilege_ips=`echo "$privilege_ips" | sed 's/,/ /g'`
    for privilege_ip in ${privilege_ips};do
        new_ip=1
        for ip in ${ipset_tmp};do
            if [[ ${ip} = ${privilege_ip} ]];then
                let new_ip=0
                break
            fi
        done
        if [ ${new_ip} -eq 1 ];then
            ipset=${ipset}" ${privilege_ip}"
        fi
    done 
fi
log "ipset=${ipset}"

if [ "x$ipset" == "x" ]; then
    log "ipset is null, do nothing!"
    exit 0
fi

generate_portsec_config
