diff --git a/lib/cidr.bash b/lib/cidr.bash new file mode 100644 index 0000000..27945c0 --- /dev/null +++ b/lib/cidr.bash @@ -0,0 +1,20 @@ +mask2cidr() { + local netmask=$1 + local nbits=0 + IFS=. + for dec in "$netmask"; do + case $dec in + 255) let nbits+=8;; + 254) let nbits+=7;; + 252) let nbits+=6;; + 248) let nbits+=5;; + 240) let nbits+=4;; + 224) let nbits+=3;; + 192) let nbits+=2;; + 128) let nbits+=1;; + 0);; + *) die "Error: $dec is not recognised" + esac + done + echo "$nbits" +} diff --git a/lib/core.bash b/lib/core.bash new file mode 100644 index 0000000..ffc884e --- /dev/null +++ b/lib/core.bash @@ -0,0 +1,12 @@ +has() { + command -v "$1" >/dev/null 2>&1 +} + +die() { + echo $1 &>2 + exit 1 +} + +warn() { + echo $1 &>2 +} diff --git a/lib/dhcp.bash b/lib/dhcp.bash new file mode 100644 index 0000000..53d1ae3 --- /dev/null +++ b/lib/dhcp.bash @@ -0,0 +1,30 @@ +start_dhclient() { + local interface=$1 + if [[ "$OSTYPE" == "linux-gnu" ]]; then + dhclient -x + dhclient -nw "$interface" + elif [[ "$OSTYPE" == "freebsd"* ]]; then + # prefer isc-dhcp-client as it supports dhcpv6 + if [[ -x /usr/local/sbin/dhclient ]]; then + /usr/local/sbin/dhclient -x + /usr/local/sbin/dhclient -nw "$interface" + else + dhclient -b "$interface" + fi + else + nohup setsid dhclient "$interface" >/dev/null 2>&1 + fi +} + +start_dhcp() { + local interface=$1 + if has dhcpcd; then + dhcpcd -b -n "$interface" + elif has dhclient; then + start_dhclient "$interface" + elif [[ "$OSTYPE" == "darwin"* ]]; then # TODO untested + ipconfig set "$interface" DHCP + else + die "no suitable dhcp program found, need dhcpcd or dhclient" + fi +} diff --git a/lib/ip.bash b/lib/ip.bash new file mode 100644 index 0000000..9efd600 --- /dev/null +++ b/lib/ip.bash @@ -0,0 +1,39 @@ +format_ip6_from_mac(){ + local template=$1 + IFS=':'; set $2; unset IFS + printf "$template" 0x${3} 0x${4} 0x${5} 0x${6} +} + +add_ip6(){ + local interface=$1 + local ip=$2 + local prefixlen=$3 + if has ifconfig; then + if [[ "$OSTYPE" == "linux-gnu" ]]; then + ifconfig "$interface" inet6 del "$ip/$prefixlen" || true + ifconfig "$interface" inet6 add "$ip/$prefixlen" + else + ifconfig "$interface" inet6 del "$ip" prefixlen "$prefixlen" || true + ifconfig "$interface" inet6 add "$ip" prefixlen "$prefixlen" + fi + elif has ip; then + ip addr del "$ip/$prefixlen" dev "$interface" || true + ip addr add "$ip/$prefixlen" dev "$interface" + else + die "no suitable program found to configure the network interface, need iproute2 or ifconfig" + fi +} + +add_ip4() { + local interface=$1 + local ip=$2 + local netmask=$3 + if has ifconfig; then + ifconfig "$interface" "$ip" netmask "$netmask" + elif has ip; then + local cidr="$(mask2cidr $netmask)" + ip addr add "$ip/$cidr" dev "$interface" + else + die "no suitable program found to set ip address, need iproute2 or ifconfig" + fi +} diff --git a/lib/mac.bash b/lib/mac.bash new file mode 100644 index 0000000..a4a668c --- /dev/null +++ b/lib/mac.bash @@ -0,0 +1,24 @@ +set_mac() { + local interface=$1 + local mac_address=$2 + if has ifconfig; then + current_mac=$(ifconfig "$interface" | awk '/ether/ {print $2}') + if [[ "${current_mac,,}" == "${mac_address,,}" ]]; then + return + fi + + ifconfig "$interface" down + if [[ "$OSTYPE" == "linux-gnu" ]]; then + ifconfig "$interface" hw ether "$mac_address" + else + ifconfig "$interface" ether "$mac_address" + fi + ifconfig "$interface" up + elif has ip; then + ip link set dev "$interface" down + ip link set dev "$interface" address "$mac_address" + ip link set dev "$interface" up + else + die "no suitable program found to configure the network interface, need iproute2 or ifconfig" + fi +} diff --git a/lib/route.bash b/lib/route.bash new file mode 100644 index 0000000..103874e --- /dev/null +++ b/lib/route.bash @@ -0,0 +1,57 @@ +add_route4() { + local interface="$1" + local subnet="$2" + local gateway="$3" + local netmask="$4" + local cidr="$(mask2cidr $netmask)" + + if has route; then + if [[ "$OSTYPE" == "linux-gnu" ]]; then + route del -net "$subnet" "$gateway" netmask "$netmask" "$interface" || true + route add -net "$subnet" "$gateway" netmask "$netmask" "$interface" + else + route -n del -net "$subnet/$prefixlen" "$gateway" || true + route -n add -net "$subnet/$prefixlen" "$gateway" + fi + elif has ip; then + ip route del "$subnet/$cidr" via "$gateway" dev "$interface" || true + ip route add "$subnet/$cidr" via "$gateway" dev "$interface" + else + die "no suitable program found to set routes, need iproute2 or ifconfig" + fi +} + +add_route6() { + local interface="$1" + local subnet="$2" + local gateway="$3" + local prefixlen="$4" + + if has route; then + if [[ "$OSTYPE" == "linux-gnu" ]]; then + route del -A inet6 "$subnet/$prefixlen" gw "$gateway" "$interface" || true + route add -A inet6 "$subnet/$prefixlen" gw "$gateway" "$interface" + else + if [[ "$gateway" == fe80* ]]; then + gateway="$gateway%$interface" + fi + route -n add -inet6 "$network" "$gateway" -prefixlen "$prefixlen" || true + route -n add -inet6 "$network" "$gateway" -prefixlen "$prefixlen" + fi + elif has ip; then + ip route del "$subnet/$prefixlen" via "$gateway" dev "$interface" || true + ip route add "$subnet/$prefixlen" via "$gateway" dev "$interface" + else + die "no suitable program found to set routes, need iproute2 or ifconfig" + fi +} + +set_route_policy(){ + # TODO: other operating systems + if [[ "$OSTYPE" != "linux-gnu" ]]; then + return + fi + if !has ip; then + warn "Could not set source " + fi +} diff --git a/tinc-up b/tinc-up index a1c78bb..e9008ca 100755 --- a/tinc-up +++ b/tinc-up @@ -1,127 +1,42 @@ #!/usr/bin/env bash set -eu -has() { - command -v "$1" >/dev/null 2>&1 -} +if [[ -n "${TINC_DEBUG+1}" ]]; then + set -x +fi -die() { - echo $1 &>2 - exit 1 -} +SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) -set_mac() { - local interface=$1 - local mac_address=$2 - if has ifconfig; then - current_mac=$(ifconfig "$interface" | awk '/ether/ {print $2}') - if [[ "${current_mac,,}" == "${mac_address,,}" ]]; then - return - fi +source "$SCRIPT_DIR/lib/core.bash" +source "$SCRIPT_DIR/lib/cidr.bash" +source "$SCRIPT_DIR/lib/dhcp.bash" +source "$SCRIPT_DIR/lib/ip.bash" +source "$SCRIPT_DIR/lib/mac.bash" +source "$SCRIPT_DIR/lib/route.bash" - ifconfig "$interface" down - if [[ "$OSTYPE" == "linux-gnu" ]]; then - ifconfig "$interface" hw ether "$mac_address" - else - ifconfig "$interface" ether "$mac_address" - fi - ifconfig "$interface" up - elif has ip; then - ip link set dev "$interface" down - ip link set dev "$interface" address "$mac_address" - ip link set dev "$interface" up - else - die "no suitable program found to configure the network interface, need iproute2 or ifconfig" - fi -} - -mask2cidr() { - local netmask=$1 - local nbits=0 - IFS=. - for dec in "$netmask"; do - case $dec in - 255) let nbits+=8;; - 254) let nbits+=7;; - 252) let nbits+=6;; - 248) let nbits+=5;; - 240) let nbits+=4;; - 224) let nbits+=3;; - 192) let nbits+=2;; - 128) let nbits+=1;; - 0);; - *) die "Error: $dec is not recognised" - esac - done - echo "$nbits" -} - -set_ip() { - local interface=$1 - local ip=$2 - local netmask=$3 - if has ifconfig; then - ifconfig "$interface" "$ip" netmask "$netmask" - elif has ip; then - ip addr add "$ip" dev "$interface" - else - die "no suitable program found to set ip address, need iproute2 or ifconfig" - fi -} - -start_dhclient() { - local interface=$1 - if [[ "$OSTYPE" == "linux-gnu" ]]; then - dhclient -x - dhclient -nw "$interface" - elif [[ "$OSTYPE" == "freebsd"* ]]; then - # prefer isc-dhcp-client as it supports dhcpv6 - if [[ -x /usr/local/sbin/dhclient ]]; then - /usr/local/sbin/dhclient -x - /usr/local/sbin/dhclient -nw "$interface" - else - dhclient -b "$interface" - fi - else - nohup setsid dhclient "$interface" >/dev/null 2>&1 - fi -} - -start_dhcp() { - local interface=$1 - if has dhcpcd; then - dhcpcd -b -n "$interface" - elif has dhclient; then - start_dhclient "$interface" - elif [[ "$OSTYPE" == "darwin"* ]]; then # TODO untested - ipconfig set "$interface" DHCP - else - die "no suitable dhcp program found, need dhcpcd or dhclient" - fi -} - -disable_ipv6_privacy() { - local interface=$1 - if [[ "$OSTYPE" == "linux-gnu" ]]; then - sysctl -w "net.ipv6.conf.${interface}.use_tempaddr=0" - elif [[ "$OSTYPE" == "openbsd"* ]]; then - ifconfig "${interface}" -autoconfprivacy - elif has sysctl; then - sysctl -w net.inet6.ip6.use_tempaddr=0 - fi -} +#disable_ipv6_privacy() { +# local interface=$1 +# if [[ "$OSTYPE" == "linux-gnu" ]]; then +# sysctl -w "net.ipv6.conf.${interface}.use_tempaddr=0" +# elif [[ "$OSTYPE" == "openbsd"* ]]; then +# ifconfig "${interface}" -autoconfprivacy +# elif has sysctl; then +# sysctl -w net.inet6.ip6.use_tempaddr=0 +# fi +#} [ -z "${INTERFACE:-}" ] && die "no INTERFACE environment variable set" -DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) -MAC_ADDRESS_FILE="${DIR}/tinc-macaddr" -LOCAL_HOOK_FILE="${DIR}/tinc-up.local" -TINCRC="${DIR}/tincrc" +MAC_ADDRESS_FILE="${SCRIPT_DIR}/tinc-macaddr" +LOCAL_HOOK_FILE="${SCRIPT_DIR}/tinc-up.local" +TINCRC="${SCRIPT_DIR}/tincrc" MAC_ADDRESS="" SUBNET="" DHCP="yes" -IPV6_PRIVACY="yes" +ULA_IP6_TEMPLATE="fdc0:4992:6a6d:a:%x%x:%x%x::1" +PUBLIC_IP6_PREFIX="2a01:4f8:210:31fd" +PUBLIC_IP6_TEMPLATE="$PUBLIC_IP6_PREFIX:1:%x%x:%x%x:1" source "${TINCRC}" || true @@ -132,20 +47,23 @@ if [ -z "$MAC_ADDRESS" ]; then fi set_mac "$INTERFACE" "$MAC_ADDRESS" +public_ip6="$(format_ip6_from_mac $PUBLIC_IP6_TEMPLATE $MAC_ADDRESS)" +ula_ip6="$(format_ip6_from_mac $ULA_IP6_TEMPLATE $MAC_ADDRESS)" +add_ip6 "$INTERFACE" "$public_ip6" "80" +add_ip6 "$INTERFACE" "$ula_ip6" "64" + +add_route6 "$INTERFACE" "fc00::" "fe80::1" "7" +add_route6 "$INTERFACE" "fc00::" "fe80::1" "7" if [ -n "$SUBNET" ]; then IFS=' ' read ip netmask <<< "$SUBNET" [ -z "$ip" ] && die "no ip set in SUBNET" [ -z "$netmask" ] && die "no netmask set in SUBNET" - set_ip "$INTERFACE" "$ip" "$netmask" + add_ip4 "$INTERFACE" "$ip" "$netmask" fi if [ "${DHCP,,}" = "yes" ]; then start_dhcp "$INTERFACE" fi -if [[ "${IPV6_PRIVACY,,}" != "yes" ]]; then - disable_ipv6_privacy "$INTERFACE" -fi - -[ -x "${DIR}/tinc-up.local" ] && "${DIR}/tinc-up.local" || true +[ -x "${SCRIPT_DIR}/tinc-up.local" ] && "${SCRIPT_DIR}/tinc-up.local" || true