#!/bin/bash CONF_FILE=/root/config_svc.conf case $1 in echo|log_run|echo_log_run) export DRY=$1 ;; *) export DRY=echo_log_run ;; # Real mode esac # Copyright 2016 Ludovic Pouzenc # # This file is part of CHD Gestion. # # CHD Gestion is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # CHD Gestion is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with CHD Gestion. If not, see . function main() { # Init globals and work files PROGNAME=$(basename $0) BOOTTIME_TS=$(date +%s -d "$(uptime -s)") > /etc/hosts.svc > /etc/ethers.adt # Apply each config line from CONF_FILE while read line; do do_conf $line; done <$CONF_FILE # Finalize setup cat /etc/hosts.system /etc/hosts.svc > /etc/hosts cat /etc/ethers.system /etc/ethers.adt > /etc/ethers $DRY ip route flush cache $DRY arp -f /etc/ethers # Make to win melody [ -x /usr/bin/beep ] && beep -f 130 -l 100 -n -f 262 -l 100 -n -f 330 -l 100 -n -f 392 -l 100 -n -f 523 -l 100 -n -f 660 -l 100 -n -f 784 -l 300 -n -f 660 -l 300 -n -f 146 -l 100 -n -f 262 -l 100 -n -f 311 -l 100 -n -f 415 -l 100 -n -f 523 -l 100 -n -f 622 -l 100 -n -f 831 -l 300 -n -f 622 -l 300 -n -f 155 -l 100 -n -f 294 -l 100 -n -f 349 -l 100 -n -f 466 -l 100 -n -f 588 -l 100 -n -f 699 -l 100 -n -f 933 -l 300 -n -f 933 -l 100 -n -f 933 -l 100 -n -f 933 -l 100 -n -f 1047 -l 400 } # do_conf is called for each internet access service to (un)route function do_conf() { svc_etat=$1 # Mandatory svc_id=$2 # Mandatory ip4_nexthop=$3 # Optionnal (if set, NAT will be used, else direct routing will be used to ip4_public) ip4_public=$4 # Mandatory ip6_nexthop=$5 # Optionnal ip6_prefix=$6 # Optionnal mac_nexthop=${7:--} # Optionnal modified_ts=${8:--} # Optionnal (but important for cancel|suspend) # FIXME sanity checks, code injection here shift dev=br1 case $svc_etat in active) do_conf_enable $dev "$@" ;; cancel|suspend) do_conf_disable $dev "$@" ;; esac } function do_conf_enable() { dev=$1 # Mandatory (interface to join the user's router) svc_id=$2 # Mandatory ip4_nexthop=$3 # Optionnal (if set, NAT will be used, else direct routing will be used to ip4_public) ip4_public=$4 # Mandatory ip6_nexthop=$5 # Optionnal ip6_prefix=$6 # Optionnal mac_nexthop=${7:--} # Optionnal modified_ts=${8:--} # Unused # Keep track of all configured IP in hosts file for local DNS reverse resolutions ( [ "$ip4_nexthop" != "-" ] && printf '%-22s %s\n' ${ip4_nexthop} ${svc_id}.nh4 [ "$ip6_nexthop" != "-" ] && printf '%-22s %s\n' ${ip6_nexthop} ${svc_id}.nh6 [ "$ip4_public" != "-" ] && printf '%-22s %s\n' ${ip4_public} ${svc_id}.ip4 [ "$ip6_prefix" != "-" ] && printf '%-22s %s\n' ${ip6_prefix}1 ${svc_id}.ip6 ) >> /etc/hosts.svc # Keep track of all routers MAC in ethers file to add static ARP entries on them # This save broadcasts and helps about stolen or misconfigured WAN IPv4 if [ "$mac_nexthop" != "-" ] then if [ "$ip4_nexthop" != "-" ] then echo $mac_nexthop $ip4_nexthop >> /etc/ethers.adt else if [ "$ip4_public" != "-" ] then echo $mac_nexthop $ip4_public >> /etc/ethers.adt fi fi fi # IPv4 Internet -> adherent's router if [ ${ip4_nexthop} = "-" ] then # No NAT case # Clean up nat in case we change from "with NAT" to "without NAT" situation nat_cleanup $ip4_public #IPv4 route setup exists=$(ip route show $ip4_public/32 dev $dev | wc -l) if [ "$exists" -ne 1 ] then $DRY ip -4 route add $ip4_public/32 dev $dev fi else # With NAT case nat_setup $ip4_nexthop $ip4_public fi # IPv6 Internet -> adherent's router if [ $ip6_prefix != "-" -a $ip6_nexthop != "-" ] then exists=$(ip -6 route show $ip6_prefix/56 dev $dev | wc -l) if [ "$exists" -ne 1 ] then $DRY ip -6 route replace $ip6_prefix/56 via $ip6_nexthop dev $dev fi fi } function do_conf_disable() { dev=$1 # Mandatory (interface to join the user's router) svc_id=$2 # Unused ip4_nexthop=$3 # Unused ip4_public=$4 # Mandatory ip6_nexthop=$5 # Unused ip6_prefix=$6 # Optionnal mac_nexthop=${7:--} # Unused modified_ts=${8:--} # Mandatory # Defensive code even on madatory fields here, because of legacy data if [ "$modified_ts" != "-" ] then # system reboot flush all live routing config. Skip cleanup of older config entries if [ $modified_ts -ge $BOOTTIME_TS ] then if [ "$ip4_public" != "-" ] then nat_cleanup $ip4_public exists=$(ip route show $ip4_public/32 dev $dev | wc -l) if [ "$exists" -eq 1 ] then $DRY ip -4 route delete $ip4_public/32 dev $dev fi fi if [ $ip6_prefix != "-" ] then exists=$(ip -6 route show $ip6_prefix/56 dev $dev | wc -l) if [ "$exists" -eq 1 ] then $DRY ip -6 route delete $ip6_prefix/56 dev $dev fi fi fi fi } function nat_setup() { ip4_nexthop=$1 ip4_public=$2 ip addr show dev lo | grep -q "$ip4_public/32" || $DRY ip addr add $ip4_public/32 dev lo # Remove PRE/POST old rules for the same ip4_public but wrong ip4_nexthop # (happens if active service is updated, new equipement) ruleno_pre=$(iptables -t nat -L PREROUTING -n --line-numbers | grep " $ip4_public " | grep -vE "to:$ip4_nexthop$" | cut -d' ' -f1 | head -n1) [ -n "$ruleno_pre" ] && $DRY iptables -t nat -D PREROUTING $ruleno_pre ruleno_post=$(iptables -t nat -L POSTROUTING -n --line-numbers | grep -E "to:$ip4_public$" | grep -v " $ip4_nexthop " | cut -d' ' -f1 | head -n1) [ -n "$ruleno_post" ] && $DRY iptables -t nat -D POSTROUTING $ruleno_post # Insert PRE/POST rules if not already there iptables -t nat -L PREROUTING -n --line-numbers | grep -E "to:$ip4_nexthop$" -q \ || $DRY iptables -t nat -A PREROUTING -d $ip4_public -j DNAT --to-destination $ip4_nexthop iptables -t nat -L POSTROUTING -n --line-numbers | grep " $ip4_nexthop " -q \ || $DRY iptables -t nat -A POSTROUTING -s $ip4_nexthop -j SNAT --to-source $ip4_public } function nat_cleanup() { ip4_public=$1 ip addr show dev lo | grep -q "$ip4_public/32" && $DRY ip addr del $ip4_public/32 dev lo deleted=0 ruleno_pre=$(iptables -t nat -L PREROUTING -n --line-numbers | grep " $ip4_public " | cut -d' ' -f1 | head -n1) if [ -n "$ruleno_pre" ] then $DRY iptables -t nat -D PREROUTING $ruleno_pre deleted=1 fi ruleno_post=$(iptables -t nat -L POSTROUTING -n --line-numbers | grep -E "to:$ip4_public$" | cut -d' ' -f1 | head -n1) if [ -n "$ruleno_post" ] then $DRY iptables -t nat -D POSTROUTING $ruleno_post deleted=1 fi if [ $deleted -eq 1 ] then $DRY conntrack -D -q $ip4_public 2>&1 || true fi } function echo_log_run() { echo $@ "# ${FUNCNAME[1]}():${BASH_LINENO[0]}" logger -p user.debug -t "$PROGNAME" -- $@ "# ${FUNCNAME[1]}():${BASH_LINENO[0]}" $@ } function log_run() { logger -p user.debug -t "$PROGNAME" -- $@ "# ${FUNCNAME[1]}():${BASH_LINENO[0]}" $@ } main