#!/bin/sh # defaults SWANCTL="/var/etc/ipsec/swanctl.conf" POOLNAME="mobile-pool-v6" # BEGIN "library" isempty() { # ph34r m4h m4d 5k1llz [ "${1}x" == "x" ] && return 0 return 1 } prerr() { echo $PROGNAME: "$@" 1>&2 } errexit() { prerr "error: $@" exit 1 } # hello PROGNAME=`basename $0` VERSION="0.99" COPY="(c) 2026: brokeaf" PROGDESC="a FreeBSD NDP proxy static entry generator" PROGNOTE="Is this ugly? Yes. Do we need this? Also yes." showhelp() { { echo echo "$PROGNAME v$VERSION $COPY: $PROGDESC" isempty $PROGNOTE || echo isempty $PROGNOTE || echo "$PROGNOTE" echo echo "usage: $PROGNAME <-h|-a|-d|-c> [-x] [-s] [-S ] [-P ] [-p ] [-i ]" echo echo " -h : this help" echo " -a : insert NDP proxy entries" echo " -d : delete NDP proxy entries" echo " -x : execute insertion / deletion, otherwise only output" echo " a list of commands to insert or delete NDP proxy" echo " entries (can be saved to a file)" echo " -c : check only" echo " -s : get mobile IPv6 pool prefix from swanctl.conf (default)" echo " -S : specify swanctl.conf path, default is $SWANCTL" echo " -P : specify swanctl.conf IPv6 pool name, default is mobile-pool-v6" echo " -p : specify IPv6 prefix to insert NDP proxy entries for" echo " -i : interface to get the NDP entry MAC address from" echo " (default is to use the interface with IPv6 default route" echo echo "note: $PROGNAME will only process the last hextet in the prefix," echo " and does not validate IPv6 addresses. need to get good." echo } 1>&2 exit 1 } isdigits() { # this is because... isempty $1 && return 0 # 'expr' does not honour PCRE, no +, only * expr "$1" : "^[0-9]*$" >/dev/null 2>&1 && return 0 return 1 } ishex() { isempty $1 && return 0 expr "$1" : "^[0-9a-fA-F]*$" >/dev/null 2>&1 && return 0 return 1 } ifcexists() { ifconfig "$1" >/dev/null 2>&1 && return 0 return 1 } default6() { netstat -rn6 | grep ^default | awk '{print $NF;}' } getmac() { ifconfig "$1" ether | grep ether | awk '{print $2;}' } swanprefix() { grep -A9999 "$2.*:" "$1" | grep addrs | head -1 | awk '{print $3;}' } # END "library" # early exit [ $# -eq 0 ] && errexit "no arguments given, see -h" # parse options arguments MODE="" DOSWAN="true" while getopts :hadxcsS:P:p:i: OPTNAME; do case $OPTNAME in h) showhelp;; a) MODE="insert";; d) MODE="delete";; x) EXECUTE="true";; c) MODE="check";; s) DOSWAN="true";; S) SWANCTL="$OPTARG";; P) POOLNAME="$OPTARG";; p) PREFIX="$OPTARG"; DOSWAN="";; i) INTFC="$OPTARG";; :) errexit "option '-$OPTARG' requires an argument, see -h";; \?) errexit "unknown option '-$OPTARG', see -h";; esac done isempty $MODE && errexit "no mode specified, see -h" # check everything # get / check interface isempty $INTFC && { prerr "no interface specified, trying default IPv6 egress" INTFC=$(default6) isempty $INTFC && errexit "no default IPv6 interface found" prerr "using $INTFC" } ifcexists $INTFC || errexit "interface '$INTFC' not found" # get / check MAC MACADDR=$(getmac $INTFC) isempty $MACADDR && errexit "could not get MAC address for $INTFC" prerr "MAC: $MACADDR" # prefix specified isempty $PREFIX || prerr "prefix: $PREFIX" # no prefix specified isempty $PREFIX && prerr "no prefix specified" # prefix from StrongSWAN explicitly wanted isempty $DOSWAN || PREFIX="" # no prefix specified or StrongSWAN wanted isempty $PREFIX && { prerr "trying pool $POOLNAME from $SWANCTL" [ -f $SWANCTL ] || errexit "swanctl.conf ($SWANCTL) not found" PREFIX=$(swanprefix $SWANCTL $POOLNAME) isempty $PREFIX && errexit "could not extract IPv6 pool, could be this tool's fault, try -p" prerr "prefix found: $PREFIX" } # extract and check subnet width MAXWIDTH=122 # it is what it is SUBNETWIDTH=`echo $PREFIX | awk -F/ '{print $2;}'` isempty $SUBNETWIDTH && errexit "could not extract prefix width from prefix" isdigits $SUBNETWIDTH || errexit "prefix width '$SUBNETWIDTH' does not look like a (positive) number" [ $SUBNETWIDTH -lt $MAXWIDTH ] && errexit "prefix width $SUBNETWIDTH too large (max $MAXWIDTH)" [ $SUBNETWIDTH -gt 128 ] && errexit "prefix width $SUBNETWIDTH above 128, come on now..." prerr "prefix width $SUBNETWIDTH" # extract subnet SUBNET=`echo $PREFIX | awk -F/ '{print $1;}'` isempty $SUBNET && errexit "subnet seems to be empty" # extract and check last hextet START=`echo $SUBNET | sed 's/.*://g'` isempty $START && errexit "the last hextet seems to be empty, use :0 instead" ishex $START || errexit "the last hextet '$START' does not look like hex to me" # remove last hextet from prefix SUBNET=`echo $SUBNET | sed s/:[^:]*$//g` # do the work... # /128 = 1 address WDTH=128 CNT=1 # calculate address count from width while [ "$WDTH" -gt "$SUBNETWIDTH" ]; do WDTH=$(( $WDTH - 1 )) CNT=$(( $CNT * 2)) done # first address, decimal FIRST=`printf %d 0x$START` # last address LAST=$(( $FIRST + $CNT - 1)) # only do the work if within one hextet if [ "$LAST" -lt 65536 ]; then # check only [ "$MODE" == "check" ] && prerr "all checks OK, ready to use -a|-d" && exit 0 isempty $EXECUTE && LISTDESC=" (listing only)" prerr "$MODE $CNT NDP entries$LISTDESC: `printf %04x $FIRST`...`printf %04x $LAST`" SUCC=0 # add, delete or list ND proxy entries for all IPs in range for a in `seq $FIRST $LAST`; do case $MODE in insert) NDPCMD="ndp -ns `printf $SUBNET:%x $a` $MACADDR proxy";; delete) NDPCMD="ndp -nd `printf $SUBNET:%x $a`";; esac if [ "$EXECUTE" == "true" ]; then $NDPCMD # increment success count [ $? -eq 0 ] && SUCC=$(( $SUCC + 1)) else echo $NDPCMD fi done isempty $EXECUTE || prerr "$SUCC out of $CNT operations successful" else prerr "wanted $CNT NDP entries: `printf %04x $FIRST`...`printf %04x $LAST`" errexit "wanted more than 16 bits' worth" fi