#!/bin/sh

## Add exceptions to IPsec SPD
# Accept IKE and IKEv2
for ipaddr in ::/0 0.0.0.0/0; do
    for port in 500 4500; do
	for dir in in out; do
	    ip xfrm policy update src $ipaddr dst $ipaddr proto udp \
		sport $port dport $port dir $dir ptype main
	done
    done
done
# Accept ICMPv6 neighbour-*
for dir in in out; do
    for type in 135 136; do
	ip xfrm policy update src ::/0 dst ::/0 proto ipv6-icmp type \
	    $type code 0 dir $dir ptype main
    done
done

## Add exceptions to firewall
for chain in INPUT OUTPUT; do
    for iptables in iptables ip6tables; do
	$iptables --flush $chain
	# Accept encrypted packets
	$iptables --append $chain --protocol esp --jump ACCEPT
	# Accept IKE and IKEv2
	for port in 500 4500; do
	    $iptables --append $chain --proto udp \
		--source-port $port --destination-port $port \
		--jump ACCEPT
	done
    done
    if [ "$chain" = INPUT ]; then
	ip6tables --append INPUT --protocol udp --match frag \
	    --fragid 0 --fraglast --jump ACCEPT
    fi
    # Accept ICMPv6 neighbour-*
    ip6tables --append $chain --protocol icmpv6 --match icmp6 \
	--icmpv6-type neighbour-solicitation --jump ACCEPT
    ip6tables --append $chain --protocol icmpv6 --match icmp6 \
	--icmpv6-type neighbour-advertisement --jump ACCEPT
done

for iptables in iptables ip6tables; do
    if ! $iptables --new-chain reject_ipsec_hosts 2>/dev/null; then
	$iptables --flush reject_ipsec_hosts
    fi
done

for iptables in iptables ip6tables; do
    # Accept packets which will match an IPsec policy
    $iptables --append INPUT --match policy --dir in --pol ipsec \
	--mode transport --jump ACCEPT
    # Reject packets from IPsec-only hosts
    $iptables --append INPUT --jump reject_ipsec_hosts
    
    # Accept packets which will be encrypted
    $iptables --append OUTPUT --match policy --dir out --pol ipsec \
	--mode transport --jump ACCEPT
    # Reject packets for IPsec-only hosts
    $iptables --append OUTPUT --jump reject_ipsec_hosts
done

ipsec_transport(){
    : "ipsec_transport 2001:470:1f15:1a45::4/128 88.80.26.40/32"
    my_ipv6="$1"
    my_ipv4="$2"
    peer_ip="$3"
    case "$peer_ip" in
	*:*)
	    peer_ipv6="${peer_ip}/128"
	    ip6tables --append reject_ipsec_hosts --source \
		"${my_ipv6}"/128 --destination "${peer_ipv6}" --jump \
		REJECT --reject-with icmp6-adm-prohibited
	    ip6tables --append reject_ipsec_hosts --source \
		"${peer_ipv6}" --destination "${my_ipv6}"/128 --jump \
		REJECT --reject-with icmp6-adm-prohibited
	    ;;
	*.*)
	    peer_ipv4="${peer_ip}/32"
	    iptables --append reject_ipsec_hosts --source \
		"${my_ipv4}"/32 --destination "${peer_ipv4}" --jump \
		REJECT --reject-with icmp-admin-prohibited
	    iptables --append reject_ipsec_hosts --source \
		"${peer_ipv4}" --destination "${my_ipv4}"/32 --jump \
		REJECT --reject-with icmp-admin-prohibited
	    ;;
    esac
}

my_ipv6=$(sed --quiet --expression='s/^[^#]*[[:space:]]left=\([^#]*:[^#]*\)#\?.*$/\1/p' /etc/ipsec.conf)
export my_ipv6
my_ipv4=$(sed --quiet --expression='s/^[^#]*[[:space:]]left=\([^#]*\.[^#]*\)#\?.*$/\1/p' /etc/ipsec.conf)
export my_ipv4

sed --quiet --expression='s/^[^#]*[[:space:]]right=\(.*[.:]\)/\1/p' \
    /etc/ipsec.conf | while read peer_ip; do
    ipsec_transport "${my_ipv6}" "${my_ipv4}" "${peer_ip}"
done
