aboutsummaryrefslogtreecommitdiff
blob: a124b847c3a5d39b129a7c325d19ce926fd167cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# Copyright (c) 2007-2008 Roy Marples <roy@marples.name>
# Released under the 2-clause BSD license.

apipa_depend()
{
	program /sbin/arping /bin/arping
}

_random_uint16()
{
	# While POSIX does not require that /dev/urandom exist, it is a de-facto
	# standard. 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.
	test -e /dev/urandom &&
	printf %d 0x"$(LC_ALL=C od -vAn -N2 -tx1 /dev/urandom | tr -d '[:space:]')"
}

_random_apipa_octets()
{
	local seed

	# Attempt to generate a random uint16 to seed awk's RNG. The maximum
	# value of RAND_MAX known to be portable is 32767. Clamp accordingly by
	# discarding one bit's worth of data. Should the seed turn out to be
	# empty, we instruct awk to seed based on the time of day, in seconds.
	seed=$(_random_uint16) && : $(( seed >>= 1 ))

	# For APIPA (RFC 3927), the 169.254.0.0/16 address block is
	# reserved. This provides 65024 addresses, having accounted for the
	# fact that the first and last /24 are reserved for future use.
	awk -v seed="$seed" 'BEGIN {
		if (seed != "") {
			srand(seed)
		} else {
			srand()
		}
		for (i = 1; i < 255; i++) {
			for (j = 0; j < 256; j++) {
				printf("%f %d %d\n", rand(), i, j)
			}
		}
	}' | sort -k 1,1 -n
}

apipa_start()
{
	local addr rc

	_exists || return

	einfo "Searching for free addresses in 169.254.0.0/16"
	eindent

	exec 3>&1
	addr=$(
		_random_apipa_octets |
		{
			while read -r f1 f2 f3; do
				next_addr="169.254.$f2.$f3"
				vebegin "$next_addr/16" >&3
				if ! arping_address "$next_addr" >&3; then
					printf %s "$next_addr"
					exit 0
				fi
			done
			exit 1
		}
	)
	rc=$?
	exec 3>&-

	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

	eoutdent
	return "$rc"
}