aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/apipa.sh94
1 files changed, 67 insertions, 27 deletions
diff --git a/net/apipa.sh b/net/apipa.sh
index 849728b..f3ec534 100644
--- a/net/apipa.sh
+++ b/net/apipa.sh
@@ -1,49 +1,89 @@
# Copyright (c) 2007-2008 Roy Marples <roy@marples.name>
# Released under the 2-clause BSD license.
-# shellcheck shell=sh disable=SC1008
apipa_depend()
{
program /sbin/arping /bin/arping
}
-_random()
+_random_bytes_as_int()
{
- local r=${RANDOM} # checkbashisms: false positive, we handle it AFTERWARDS
- if [ -n "${r}" ]; then
- echo "${r}"
- else
- uuidgen | sed -n -e 's/[^[:digit:]]//g' -e 's/\(^.\{1,7\}\).*/\1/p'
- fi
+ local hex num_bytes="$1"
+
+ # While POSIX does not require that /dev/urandom exist, it is a
+ # de-facto standard. Therefore, the following approach should be
+ # highly portable in practice. In the case of Linux, and unlike BSD
+ # this interface does not block in the event that the CSRNG has not
+ # yet been seeded. Still, this is acceptable because we do not
+ # require a guarantee that the entropy be cryptographically secure.
+ # It's also worth noting that Linux >=5.4 is faster at seeding in
+ # the absence of RDRAND/RDSEED than previous versions were.
+ test -e /dev/urandom &&
+ hex=$(
+ LC_ALL=C tr -dc '[:xdigit:]' < /dev/urandom |
+ dd bs="$(( num_bytes * 2 ))" count=1 2>/dev/null) &&
+ test "${#hex}" = "$(( num_bytes * 2 ))" &&
+ printf '%d\n' "0x${hex}"
+}
+
+_random_apipa_octets()
+{
+ local seed
+
+ # Obtain a highly random 16-bit seed for use by awk's RNG. In the
+ # unlikely event that the seed ends up being empty, awk will seed
+ # based on the time of day, with a granularity of one second.
+ seed=$(_random_bytes_as_int 2)
+
+ # For APIPA (RFC 3927), the 169.254.0.0/16 address block is
+ # reserved. This provides 65534 addresses, having accounted for the
+ # network and broadcast address. Note that we must count from 1.
+ awk "BEGIN {
+ srand($seed)
+ for (i=1; i<65535; i++) print rand() \" \" i
+ }" |
+ sort -k 1,1 -n |
+ POSIXLY_CORRECT=1 awk '{
+ hex = sprintf("%04x",$2)
+ printf("%d %d\n", "0x" substr(hex,1,2), "0x" substr(hex,3,2))
+ }'
}
apipa_start()
{
- local iface="$1" i1= i2= addr= i=0
+ local addr rc
- _exists true || return 1
+ _exists || return
einfo "Searching for free addresses in 169.254.0.0/16"
eindent
- while [ ${i} -lt 64516 ]; do
- : $(( i1 = (_random % 255) + 1 ))
- : $(( i2 = (_random % 255) + 1 ))
-
- addr="169.254.${i1}.${i2}"
- vebegin "${addr}/16"
- if ! arping_address "${addr}"; then
- eval config_${config_index}="\"${addr}/16 broadcast 169.254.255.255\""
- : $(( config_index -= 1 ))
- veend 0
- eoutdent
- return 0
- fi
+ exec 3>&1
+ addr=$(
+ _random_apipa_octets |
+ {
+ while read -r i1 i2; do
+ addr="169.254.${i1}.${i2}"
+ vebegin "${addr}/16" >&3
+ if ! arping_address "${addr}" >&3; then
+ printf '%s\n' "${addr}"
+ exit 0
+ fi
+ done
+ exit 1
+ }
+ )
+ rc=$?
+ exec 3>&-
- : $(( i += 1 ))
- done
+ if [ "$rc" = 0 ]; then
+ eval "config_${config_index}=\"\${addr}/16 broadcast 169.254.255.255\""
+ : $(( config_index -= 1 ))
+ veend 0
+ else
+ eerror "No free address found!"
+ fi
- eerror "No free address found!"
eoutdent
- return 1
+ return "$rc"
}