#!/bin/bash
#############################################################################
# 脚本名称: ppp_rule_check.sh
# 脚本功能: 检查并维护PPPoE连接的网络配置、路由规则和网络接口
# 主要功能:
#   1. 检查macvlan网卡配置文件完整性
#   2. 检查并恢复默认路由
#   3. 检查并配置VLAN和macvlan接口
#   4. 检查并维护PPP连接的路由表和路由规则
#############################################################################


#############################################################################
# 函数名称: macv_file_check
# 函数功能: 检测macvlan网卡配置文件是否完整，删除异常文件
# 参数: 无
# 返回值: 无
#############################################################################
function macv_file_check(){
	# 检测网卡文件是否有缺失，缺失的macv文件删除
	for i in $(ls /etc/sysconfig/network-scripts/ifcfg-macv*| sort -uV)
		do 
			# 检查文件行数是否为10行(正常配置文件应有10行)
			if [[ $(cat $i | wc -l) == 10 ]];then
				echo $i 文件正常
			else 
				echo $i 文件异常
				# 移除文件不可变属性
				chattr -i ${i}
				# 删除异常文件
				rm -rf $i
			fi
		done
}


#############################################################################
# 函数名称: defroute_check
# 函数功能: 检查并恢复默认路由和管理网络路由
# 参数: 无
# 返回值: 无
#############################################################################
function defroute_check()
{
	# 检查是否存在默认路由
	def_route=`/usr/sbin/ip route | grep default|wc -l`
	# 如果没有默认路由，尝试从配置文件恢复
	if  [[ $def_route -lt 1 ]];then
		if [[ -s "/opt/pppoe/route_mgt.txt" ]];then
			route_mgt_1=`cat /opt/pppoe/route_mgt.txt`
			# 添加默认路由
			/usr/sbin/ip route add $route_mgt_1
		fi
	fi
	
	# 检查管理网络路由配置文件是否存在
	if [[ -s "/opt/pppoe/route.txt" ]];then
		# 读取管理网络配置信息
		mgt_gateway=`cat /opt/pppoe/route.txt|sed -n 1p|awk '{print $1}'`  # 管理网关
		mgt_dev=`cat /opt/pppoe/route.txt|sed -n 2p|awk '{print $1}'`      # 管理网络设备
		mgt_ip=`cat /opt/pppoe/route.txt|sed -n 3p|awk '{print $1}'`       # 管理IP地址
		mgt_net=`cat /opt/pppoe/route.txt|sed -n 4p|awk '{print $1}'`      # 管理网络地址

		# 检查路由表中是否已存在管理路由表
		tables_num=`cat /etc/iproute2/rt_tables |grep "250[ ]*mgt"|wc -l`
		if [[ $tables_num -lt 1 ]];then
			# 添加管理路由表
			echo "250   mgt" >> /etc/iproute2/rt_tables
		fi

		# 检查管理路由表中是否存在默认路由
		route_num_1=`/usr/sbin/ip route show table mgt 2>>/dev/null|grep "default via $mgt_gateway dev $mgt_dev" |wc -l`
		# 检查管理路由表中是否存在管理网络路由
		route_num_2=`/usr/sbin/ip route show table mgt 2>>/dev/null|grep "$mgt_net dev $mgt_dev" |wc -l`
		
		# 如果管理路由表中没有默认路由，添加默认路由
		if [[ $route_num_1 -lt 1 ]];then
			/usr/sbin/ip route add default via $mgt_gateway dev $mgt_dev table mgt
		fi
		# 如果管理路由表中没有管理网络路由，添加管理网络路由
		if [[ $route_num_2 -lt 1 ]];then
			/usr/sbin/ip route add $mgt_net dev $mgt_dev table mgt
		fi
		
		# 检查是否存在从管理IP地址发出的流量使用管理路由表的规则
		rule_num=`/usr/sbin/ip rule | grep "from $mgt_ip lookup mgt"|wc -l`
		if [[ $rule_num -lt 1 ]];then
			# 添加路由规则：从管理IP地址发出的流量使用管理路由表
			/usr/sbin/ip rule add from $mgt_ip lookup mgt
		fi
	fi
}

#############################################################################
# 函数名称: checkvlanEth
# 函数功能: 检查并配置VLAN和macvlan接口
# 参数: $1 - PPP连接编号
# 返回值: 无
#############################################################################
function checkvlanEth()
{
	num=$1
	# 获取配置文件中该行的列数
	colsN=`cat /opt/pppoe/pppoe_user.txt | awk '{print NF}'|sed -n $(($num+1))p`
	
	# 检查配置信息是否完整(至少需要5列)
	if [[ $colsN -lt 5 ]];then
		ttime=`date +"%Y-%m-%d %H:%M:%S"`
		echo "${ttime}, $num line information error" >> /opt/pppoe/pppoe.log
		exit
	fi
	
	# 检查macvlan相关脚本是否存在，不存在则下载
	if [ ! -s /usr/sbin/ifdown-macvlan ];then 
		curl -o /usr/sbin/ifdown-macvlan http://down.9494.org:9999/ppp/ifdown-macvlan && chmod +x /usr/sbin/ifdown-macvlan
	fi
	if [ ! -s /usr/sbin/ifup-macvlan ];then
		curl -o /usr/sbin/ifup-macvlan http://down.9494.org:9999/ppp/ifup-macvlan && chmod +x /usr/sbin/ifup-macvlan
	fi
	
	# 获取VLAN ID和物理网卡名称
	vlanid=`cat /opt/pppoe/pppoe_user.txt|sed -n $(($num+1))p|awk '{print $1}'`
	p_eth=`cat /opt/pppoe/pppoe_user.txt|sed -n $(($num+1))p|awk '{print $4}'`
	
	# 处理无VLAN情况
	if [[ $vlanid == "novlan" ]] ;then
		# 生成macvlan接口名称
		eth_name=`printf "macv%.2d" $num`
		
		# 如果macvlan接口配置文件不存在，创建配置文件
		if  [[ ! -f  "/etc/sysconfig/network-scripts/ifcfg-${eth_name}" ]];then
			# 生成随机MAC地址
			mac=`dd if=/dev/urandom bs=1024 count=1 2>/dev/null | md5sum | sed -e 's/^\(..\)\(..\)\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4:\5:\6/' -e 's/^\(.\)[13579bdf]/\10/'`
			
			# 创建macvlan接口配置文件
			echo "DEVICE=${eth_name}" > /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "DEVICETYPE=macvlan" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "TYPE=macvlan" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "BOOTPROTO=none" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "ONBOOT=yes" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "NM_CONTROLLED=no" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "MACVLAN_PARENT=${p_eth}" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "MACVLAN_MODE=bridge" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "MACADDR=$mac" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			echo "DEFROUTE=no" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
			
			# 启动macvlan接口
			/usr/sbin/ifup-macvlan ${eth_name}
			sleep 1
		else
			# 检查macvlan接口是否已启动，如果未启动则启动
			macv_num=`/usr/sbin/ip addr|grep ${eth_name}@|wc -l`
			if [[ $macv_num -le 0 ]];then
				/usr/sbin/ifup-macvlan ${eth_name}
				sleep 1
			fi
		fi
	else
		# 处理有VLAN情况
		# 检查VLAN接口配置文件是否存在
		if  [[ ! -f  "/etc/sysconfig/network-scripts/ifcfg-${p_eth}.${vlanid}" ]];then
			# 生成随机MAC地址
			mac=`dd if=/dev/urandom bs=1024 count=1 2>/dev/null | md5sum | sed -e 's/^\(..\)\(..\)\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4:\5:\6/' -e 's/^\(.\)[13579bdf]/\10/'`
			
			# 创建VLAN接口配置文件
			echo "DEVICE=${p_eth}.${vlanid}" > /etc/sysconfig/network-scripts/ifcfg-${p_eth}.${vlanid}
			echo "BOOTPROTO=none" >> /etc/sysconfig/network-scripts/ifcfg-${p_eth}.${vlanid}
			echo "ONBOOT=yes" >> /etc/sysconfig/network-scripts/ifcfg-${p_eth}.${vlanid}
			echo "VLAN=yes" >> /etc/sysconfig/network-scripts/ifcfg-${p_eth}.${vlanid}
			echo "MACADDR=$mac" >> /etc/sysconfig/network-scripts/ifcfg-${p_eth}.${vlanid}
			
			# 启动VLAN接口
			/usr/sbin/ifup ${p_eth}.${vlanid}
			sleep 1
			
			# 生成macvlan接口名称
			eth_name=`printf "macv%.2d" $num`
			
			# 如果macvlan接口配置文件不存在，创建配置文件
			if  [[ ! -f  "/etc/sysconfig/network-scripts/ifcfg-${eth_name}" ]];then
				# 生成随机MAC地址
				mac1=`dd if=/dev/urandom bs=1024 count=1 2>/dev/null | md5sum | sed -e 's/^\(..\)\(..\)\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4:\5:\6/' -e 's/^\(.\)[13579bdf]/\10/'`
				
				# 创建macvlan接口配置文件
				echo "DEVICE=${eth_name}" > /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "DEVICETYPE=macvlan" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "TYPE=macvlan" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "BOOTPROTO=none" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "ONBOOT=yes" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "NM_CONTROLLED=no" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "MACVLAN_PARENT=${p_eth}.${vlanid}" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "MACVLAN_MODE=bridge" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "MACADDR=$mac1" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "DEFROUTE=no" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				
				# 启动macvlan接口
				/usr/sbin/ifup-macvlan ${eth_name}
				sleep 1
			else
				# 检查macvlan接口是否已启动，如果未启动则启动
				macv_num=`/usr/sbin/ip addr|grep ${eth_name}@|wc -l`
				if [[ $macv_num -le 0 ]];then
					/usr/sbin/ifup-macvlan ${eth_name}
					sleep 1
				fi
			fi
		else
			# VLAN接口配置文件存在，检查VLAN接口是否已启动
			vlan_num=`/usr/sbin/ip addr|grep ${p_eth}.${vlanid}@|wc -l`
			if [[ $vlan_num -le 0 ]];then
				# 启动VLAN接口
				/usr/sbin/ifup ${p_eth}.${vlanid}
				sleep 1
			fi
			
			# 生成macvlan接口名称
			eth_name=`printf "macv%.2d" $num`
			
			# 如果macvlan接口配置文件不存在，创建配置文件
			if  [[ ! -f  "/etc/sysconfig/network-scripts/ifcfg-${eth_name}" ]];then
				# 生成随机MAC地址
				mac1=`dd if=/dev/urandom bs=1024 count=1 2>/dev/null | md5sum | sed -e 's/^\(..\)\(..\)\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4:\5:\6/' -e 's/^\(.\)[13579bdf]/\10/'`
				
				# 创建macvlan接口配置文件
				echo "DEVICE=${eth_name}" > /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "DEVICETYPE=macvlan" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "TYPE=macvlan" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "BOOTPROTO=none" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "ONBOOT=yes" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "NM_CONTROLLED=no" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "MACVLAN_PARENT=${p_eth}.${vlanid}" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "MACVLAN_MODE=bridge" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "MACADDR=$mac1" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				echo "DEFROUTE=no" >> /etc/sysconfig/network-scripts/ifcfg-${eth_name}
				
				# 启动macvlan接口
				/usr/sbin/ifup-macvlan ${eth_name}
				sleep 1
			else
				# 检查macvlan接口是否已启动，如果未启动则启动
				macv_num=`/usr/sbin/ip addr|grep ${eth_name}@|wc -l`
				if [[ $macv_num -le 0 ]];then
					/usr/sbin/ifup-macvlan ${eth_name}
					sleep 1
				fi
			fi
		fi
	fi	
}

#############################################################################
# 函数名称: rule_check
# 函数功能: 检查所有PPP连接的路由规则
# 参数: 无
# 返回值: 无
#############################################################################
function rule_check()
{
	# 获取所有PPP配置文件列表
	config_list_r=`ls /etc/sysconfig/network-scripts/ifcfg-ppp*|grep "/etc/sysconfig/network-scripts/ifcfg-ppp[1-9]*[0-9]*$"|sort -Vk 1`
	# 获取PPP配置文件数量
	config_num_r=`ls /etc/sysconfig/network-scripts/ifcfg-ppp*|grep "/etc/sysconfig/network-scripts/ifcfg-ppp[1-9]*[0-9]*$"|sort -Vk 1|wc -l`
	
	# 遍历所有PPP配置文件
	for config_r in $config_list_r
	do
		# 提取PPP连接编号
		clo_r=`echo $config_r | sed "s/\/etc\/sysconfig\/network-scripts\/ifcfg-ppp//g"`
		
		# 检查并配置VLAN和macvlan接口
		checkvlanEth $clo_r
		
		# 检查PPP连接状态
		pppoe_status=`/usr/sbin/pppoe-status $config_r|grep "Link is up"|wc -l`
		echo $pppoe_status
		
		# 如果PPP连接已建立
		if [[ $pppoe_status -gt 0 ]];then
			# 检查该PPP连接的路由表是否存在路由
			routenum=`/usr/sbin/ip route show table ppp${clo_r} 2> /dev/null|wc -l`
			if [[ $routenum -lt 1 ]]; then
				# 如果路由表为空，记录日志并重新配置路由
				ttime=`date +"%Y-%m-%d %H:%M:%S"`
				echo "${ttime} $config routenum 0" >> /opt/pppoe/pppoe.log
				checkroute $clo_r $config_num_r
			else
				# 如果路由表不为空，检查路由规则
				checkrule $clo_r $config_num_r
			fi
		fi
	done
}

#############################################################################
# 函数名称: checkroute
# 函数功能: 配置PPP连接的路由表和路由规则
# 参数: $1 - PPP连接编号, $2 - PPP连接总数
# 返回值: 无
#############################################################################
function checkroute()
{
	num=$1
	
	# 获取PPP设备名称
	dev_name=`/usr/sbin/pppoe-status /etc/sysconfig/network-scripts/ifcfg-ppp$num|awk '$1~"^[0-9]" && $2~"ppp"{print $2}'|sed 's/://g'`
	
	# 获取当前路由规则数量
	rulenum=`/usr/sbin/ip rule | awk '$NF~"^ppp'$num'$"'|wc -l`
	
	# 获取PPP连接的IPv4地址
	ipaddr=`/usr/sbin/pppoe-status /etc/sysconfig/network-scripts/ifcfg-ppp$num|awk '$1~"inet$" && $2~"^[0-9]"{print $2 }'`
	
	# 获取PPP连接的IPv6地址
	ipaddr6=`/usr/sbin/pppoe-status /etc/sysconfig/network-scripts/ifcfg-ppp$num|awk '$1~"inet6$" && $2~"^[0-9]"{print $2 }'`
	
	# 检查路由表是否已定义
	check_tables_num=`cat /etc/iproute2/rt_tables |grep "ppp$num$"|wc -l`
	if [[ $check_tables_num -lt 1 ]];then
		# 如果路由表未定义，添加路由表定义
		echo "$(($num+10))  ppp${num}" >> /etc/iproute2/rt_tables
	fi
	
	# 清空IPv4路由表
	ip route flush table ppp${num}
	# 清空IPv6路由表
	ip -6 route flush table ppp${num}
	
	# 删除所有与该PPP连接相关的路由规则
	while [ $rulenum -gt 0 ] ;do 
		/usr/sbin/ip rule del lookup ppp${num}
		/usr/sbin/ip -6 rule del lookup ppp${num}
		let rulenum--
	done
	
	# 添加IPv4默认路由到路由表
	/usr/sbin/ip route add 0/0 dev $dev_name table ppp$num
	
	# 添加IPv6默认路由到路由表
	/usr/sbin/ip -6 route add 0/0 dev $dev_name table ppp$num  
	
	# 添加IPv4路由规则：从该PPP连接IP地址发出的流量使用该PPP连接的路由表
	/usr/sbin/ip rule add from $ipaddr table ppp$num pref 200
	
	# 如果获取到了IPv6地址，添加IPv6路由规则
	if [[ -n "$ipaddr6" ]]; then
		# 添加IPv6路由规则：从该PPP连接IPv6地址发出的流量使用该PPP连接的路由表
		/usr/sbin/ip -6 rule add from $ipaddr6 table ppp$num pref 200
	fi
}

#############################################################################
# 函数名称: checkrule
# 函数功能: 检查并修复PPP连接的路由规则
# 参数: $1 - PPP连接编号, $2 - PPP连接总数
# 返回值: 无
#############################################################################
function checkrule()
{
	num=$1
	
	# 获取PPP设备名称
	dev_name_1=`/usr/sbin/pppoe-status /etc/sysconfig/network-scripts/ifcfg-ppp$num|awk '$1~"^[0-9]" && $2~"ppp"{print $2}'|sed 's/://g'`
	
	# 获取PPP连接的IPv4地址
	ipaddr=`/usr/sbin/pppoe-status /etc/sysconfig/network-scripts/ifcfg-ppp$num|awk '$1~"inet$" && $2~"^[0-9]"{print $2 }'`
	
	# 获取PPP连接的IPv6地址
	ipaddr6=`/usr/sbin/pppoe-status /etc/sysconfig/network-scripts/ifcfg-ppp$num|awk '$1~"inet6$" && $2~"^[0-9]"{print $2 }'`
	
	# 获取当前路由规则数量
	rulenum=`/usr/sbin/ip rule | awk '$NF~"^ppp'$num'$"'|wc -l`   
	
	# 检查是否存在从该PPP连接IP地址发出的流量使用该PPP连接路由表的规则
	rulenum2=`/usr/sbin/ip rule | grep "from $ipaddr lookup ppp$num"|wc -l`
	
	# 如果不存在该规则，重新配置路由规则
	if [[ rulenum2 -eq 0 ]];then
		# 删除所有与该PPP连接相关的路由规则
		while [ $rulenum -gt 0 ]
		do 
			/usr/sbin/ip rule del lookup ppp$num
			/usr/sbin/ip -6 rule del lookup ppp$num
			let rulenum--
		done
		
		# 添加IPv4路由规则：从该PPP连接IP地址发出的流量使用该PPP连接的路由表
		/usr/sbin/ip rule add from $ipaddr table ppp$num pref 200
		
		# 如果获取到了IPv6地址，添加IPv6路由规则
		if [[ -n "$ipaddr6" ]]; then
			# 添加IPv6路由规则：从该PPP连接IPv6地址发出的流量使用该PPP连接的路由表
			/usr/sbin/ip -6 rule add from $ipaddr6 table ppp$num pref 200
		fi
	fi
}


#############################################################################
# 主程序开始
#############################################################################

# 检测网卡文件是否有缺失
macv_file_check

# 检查并恢复默认路由和管理网络路由
defroute_check

# 检查所有PPP连接的路由规则
rule_check
