Last active
December 7, 2019 02:38
-
-
Save afresh1/24d7099e6e27ae92b6aa9a207b34defc to your computer and use it in GitHub Desktop.
Calculates a 6rd IP and default gateway and outputs them in an OpenBSD hostname.gif0 format from an IPv4 address, with optional mask, the v4 destination, a 6rd prefix with length. See also this gist: https://gist.github.com/afresh1/791343380b4410687d51fdd94f20bd42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/ksh | |
set -e | |
set -f -u -C | |
# Copyright (c) 2019 Andrew Hewus Fresh <[email protected]> | |
# | |
# Permission to use, copy, modify, and distribute this software for any | |
# purpose with or without fee is hereby granted, provided that the above | |
# copyright notice and this permission notice appear in all copies. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
usage() { | |
cat <<EOL | |
$0 v4ip[/mask] v4dest v6_prefix/prefix_length # -h for more help | |
EOL | |
exit $1 | |
} | |
full_usage() { | |
cat <<EOL | |
$0 v4ip[/mask] v4dest v6_prefix:/prefix_length | |
$0 192.0.2.93/14 198.51.100.3 2001:db8:/32 | |
v4 ip - The public IPv4 address used in the 6rd configuration | |
v4 mask - An optional mask | |
If your 6rd provider only uses part of the v4 address | |
Also applies to the v4 dest | |
v4 dest - The IPv4 address of the remote side of 6rd tunnel | |
v6 prefix - The prefix used to calculate the 6rd address | |
prefix length - The number of bits used for the prefix | |
EOL | |
exit 0 | |
} | |
# Convert the IP to an integer so do calculations on it | |
v4ip2int() { | |
local IFS=. | |
local ip | |
set -A ip $* | |
echo $(( (${ip[0]}<<24) + (${ip[1]}<<16) + (${ip[2]}<<8) + ${ip[3]} )) | |
} | |
# Apply the v4 mask so to only use the bits we're supposed to | |
# Arguments are an integer IP and number of bits to mask | |
apply_v4_mask() { | |
echo $(( $1 & ( 0xFFFFFFFF >> $2 ) )) | |
} | |
# This takes a dotted-quad v4 IP, an optional number of bits in a mask, | |
# and the length of the v6 prefix. It takes those things and returns | |
# 8 groups of integers, each with a max of 16 bits, as in an IPv6 address. | |
v4ip2ints() { | |
local v4_int=$( v4ip2int "$1" ) # make the v4 IP an int | |
local v4_mask="${2:-}" | |
local prefix_length="$3" | |
local ints | |
set -A ints | |
# If we got a v4 mask, we need to zero out the bits we aren't using | |
[ "${v4_mask:-}" ] && v4_int=$( apply_v4_mask $v4_int $v4_mask ) | |
# We're going to loop over the v4 address and pull out the bits | |
# that go in each group, starting at the bit where the prefix ends. | |
# That means we can skip the first $i groups of 16 bits. | |
local i=$(( $prefix_length / 16 )) | |
# and then we need to start shifting the v4 address by | |
# half, minus whatever is left in the possible mask, | |
# plus the length left in the prefix. | |
local b=$(( 16 - ${v4_mask:-0} + ( $prefix_length % 16 ) )) | |
# Then, while we haven't shifted too far off the end of the v4 IP | |
# We filter out each 16 bit group we need. | |
while [ $b -gt -16 ]; do | |
if [ $b -ge 0 ]; then | |
eval ints[$i]=$(( $v4_int >> $b & 0xFFFF )) | |
else | |
eval ints[$i]=$(( $v4_int << ( 0 - $b ) & 0xFFFF )) | |
fi | |
b=$(( $b - 16 )) | |
i=$(( $i + 1 )) | |
done | |
# Then iterate over the 8 groups, returning whatever is in them or 0 | |
for i in 0 1 2 3 4 5 6 7; do echo "${ints[$i]:-0}"; done | |
} | |
# Takes a colon separated IPv6 prefix and splits it into up to | |
# 8 groups of integers, each with a max of 16 bits, as in an IPv6 address. | |
v6prefix2ints() { | |
local IFS=: | |
local x | |
for x in $*; do echo $(( 0x${x:-0} )); done | |
} | |
# Takes a colon separated IPv6 prefix, the number of bits at which the v4 | |
# address will be combined, a dotted-quad IPv4 address, and an optional | |
# number of bits to mask off from the beginning of the IPv4 address. | |
combine() { | |
local prefix=$1 | |
local length=$2 | |
local v4_ip=$3 | |
local v4_mask=${4:-} | |
# A place to put the combined v6 representation | |
local v6_ip="" | |
# A couple of temporary variables we're going to need | |
# The one is in base 16 so we can get the values we need for v6 | |
local i= | |
typeset -i16 x | |
local v4 | |
local v6 | |
local zeros | |
# Split the v4 and v6 IPs we're combining into 8 groups of 16 bits | |
set -A v4 $( v4ip2ints "$v4_ip" "$v4_mask" "$length" ) | |
set -A v6 $( v6prefix2ints "$prefix" ) | |
set -A zeros 0 0 0 0 0 0 0 0 0 0 | |
# Now we loop over all the groups in reverse order combining them | |
for i in 7 6 5 4 3 2 1 0; do | |
x=$(( ${v6[$i]:-0} + ${v4[$i]:-0} )) | |
# We turn the last group into 1, because that's what we need | |
[ $i -eq 7 -a $x -eq 0 ] && x=1 | |
# If when combined, the group grew over 16 bits, | |
# then move those extra bits into the next group. | |
if [ $x -gt $((0xFFFF)) ]; then | |
v6[$i-1]=$(( ${v6[$i-1]:-0} + ( $x >> 16 ) )) | |
x=$(( $x & 0xFFFF )) | |
fi | |
v6[$i]=${x#*#} | |
# Now we loop back up, looking for all the zeros | |
# putting the max we find in [8] and the index of that in [9] | |
while [ $i -le 7 -a ${v6[$i]} = 0 ]; do | |
zeros[$i]=$(( ${zeros[$i]} + 1 )) | |
if [ ${zeros[$i]} -gt ${zeros[8]} ]; then | |
zeros[8]=${zeros[$i]} | |
zeros[9]=$i | |
fi | |
i=$(( $i + 1 )) | |
done | |
done | |
# Now we loop again, to assemble our groups into a v6 address | |
for i in 0 1 2 3 4 5 6 7; do | |
# If we are inside the longest run of zero groups | |
# we want to replace them all with an extra : | |
if [ $i -le ${zeros[9]} \ | |
-a $i -gt $(( ${zeros[9]} - ${zeros[8]} )) ]; then | |
[ $i -eq ${zeros[9]} ] && v6_ip="$v6_ip:" | |
continue; | |
fi | |
# Once we have a v6_ip, need to separate groups with a : | |
[ "$v6_ip" ] && v6_ip="$v6_ip:" | |
v6_ip="$v6_ip${v6[$i]}" | |
done | |
echo $v6_ip | |
} | |
for o in "$@"; do [ "$o" = "-h" ] && full_usage; done | |
[ $# -eq 3 ] || usage 1 | |
v4_ip=$1; shift | |
v4_dest=$1; shift | |
v6_prefix=$1; shift | |
v4_mask= | |
v6_length= | |
# If the v4 IP includes a mask, split it out | |
if [ "${v4_ip}" != "${v4_ip%/*}" ]; then | |
v4_mask=${v4_ip#*/} | |
v4_ip=${v4_ip%/*} | |
fi | |
v6_length=${v6_prefix#*/} | |
v6_prefix=${v6_prefix%/*} | |
# Validate the v6 prefix has a length | |
[ "${v6_prefix}" = "${v6_length}" ] && usage 2 | |
v6_ip=$( combine $v6_prefix $v6_length $v4_ip $v4_mask ) | |
v6_dest=$( combine $v6_prefix $v6_length $v4_dest $v4_mask ) | |
cat <<EOL | |
# /etc/hostname.gif0 | |
mtu 1480 | |
tunnel $v4_ip $v4_dest | |
inet6 alias $v6_ip 128 | |
dest $v6_dest | |
!/sbin/route -qn add -inet6 default $v6_dest | |
EOL |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment