Skip to content

Instantly share code, notes, and snippets.

@afresh1
Last active December 7, 2019 02:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afresh1/24d7099e6e27ae92b6aa9a207b34defc to your computer and use it in GitHub Desktop.
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
#!/bin/ksh
set -e
set -f -u -C
# Copyright (c) 2019 Andrew Hewus Fresh <andrew@afresh1.com>
#
# 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