Gentoo Archives: gentoo-commits

From: Lars Wendler <polynomial-c@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/netifrc:master commit in: net/
Date: Wed, 27 Jan 2021 14:06:10
Message-Id: 1611756354.31a05f1b3fb90a3b4e9c0e587bdd5a39e8236f6b.polynomial-c@OpenRC
1 commit: 31a05f1b3fb90a3b4e9c0e587bdd5a39e8236f6b
2 Author: Kerin Millar <kfm <AT> plushkava <DOT> net>
3 AuthorDate: Mon Jan 25 02:40:29 2021 +0000
4 Commit: Lars Wendler <polynomial-c <AT> gentoo <DOT> org>
5 CommitDate: Wed Jan 27 14:05:54 2021 +0000
6 URL: https://gitweb.gentoo.org/proj/netifrc.git/commit/?id=31a05f1b
7
8 net/apipa.sh: fix broken implementation by way of a rewrite
9
10 Sadly, the present implementation has never functioned correctly. The
11 original author employed incorrect syntax for what was intended to be a
12 command substitution. As a result, the _random() function is never called.
13 What actually happens is that arping is needlessly executed exactly 64516
14 times, with no address ever being considered as a valid candidate.
15
16 Furthermore, this module has other bugs and is poorly designed. Here are the
17 reasons as to why:-
18
19 • the 169.254.0.0/16 block offers 65534 addresses, not 64516
20 • the main loop is horrendously slow at enumerating the address block
21 • it counts to 64516 but doesn't ensure that each address is unique!
22 • it prefers bash for generating entropy (fine, but non-standard)
23 • it falls back to a non-standard utility for generating entropy
24
25 Therefore, I decided to re-write most of it. The fundamental difference is
26 that all 65534 octet pairs are generated up front before being processed by
27 the main loop. At most, every possible address will now be tested exactly
28 once.
29
30 In fact, this approach turns out to be faster by an order of magnitude. The
31 following synthetic tests - which calculate the time taken to enumerate the
32 entire address space - demonstrate the tremendous difference between the
33 existing code and mine. Of course, to ensure that the comparison was
34 meaningful, I rectified the command substitution bug in the existing code.
35
36 # time bash apipa-old-test.sh
37 real 2m34.367s
38 user 1m9.959s
39 sys 1m37.502s
40
41 # time bash apipa-new-test.sh
42 real 0m1.119s
43 user 0m0.965s
44 sys 0m0.182s
45
46 Note that the new _random_apipa_octets() function is responsible for
47 generating all 65534 combinations of octet pairs in a random order. It
48 mainly relies on awk(1) and sort(1). Where possible, a seed is obtained from
49 /dev/urandom for the benefit of awk's RNG, but this is not required.
50
51 I have isolated and tested the new functions on GNU/Linux, macOS, FreeBSD,
52 NetBSD, OpenBSD and MirBSD. I have individually tested gawk, mawk, nawk,
53 busybox awk and the awk implementations provided by the previously mentioned
54 operating systems in the case that they are distinct. The only
55 incompatiblity that I was personally able to find was with the awk
56 implementation of MirBSD, which affects the final invocation of awk in the
57 _random_apipa_octets function. However, MirBSD was forked from an old
58 version of OpenBSD and seems sufficiently obscure so as not to be worth
59 worrying about. If someone should try to integrate netifrc into MirBSD one
60 day then the matter can be dealt with then.
61
62 Finally, I want to thank Steve Arnold for bringing the original bug to my
63 attention. Congratulations, Steve. You may be the only known user of
64 net/apipa.sh on the planet.
65
66 Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
67 Reported-by: Steve Arnold <nerdboy <AT> gentoo.org>
68 Closes: https://bugs.gentoo.org/766890
69 Signed-off-by: Lars Wendler <polynomial-c <AT> gentoo.org>
70
71 net/apipa.sh | 94 +++++++++++++++++++++++++++++++++++++++++++-----------------
72 1 file changed, 67 insertions(+), 27 deletions(-)
73
74 diff --git a/net/apipa.sh b/net/apipa.sh
75 index 849728b..f3ec534 100644
76 --- a/net/apipa.sh
77 +++ b/net/apipa.sh
78 @@ -1,49 +1,89 @@
79 # Copyright (c) 2007-2008 Roy Marples <roy@×××××××.name>
80 # Released under the 2-clause BSD license.
81 -# shellcheck shell=sh disable=SC1008
82
83 apipa_depend()
84 {
85 program /sbin/arping /bin/arping
86 }
87
88 -_random()
89 +_random_bytes_as_int()
90 {
91 - local r=${RANDOM} # checkbashisms: false positive, we handle it AFTERWARDS
92 - if [ -n "${r}" ]; then
93 - echo "${r}"
94 - else
95 - uuidgen | sed -n -e 's/[^[:digit:]]//g' -e 's/\(^.\{1,7\}\).*/\1/p'
96 - fi
97 + local hex num_bytes="$1"
98 +
99 + # While POSIX does not require that /dev/urandom exist, it is a
100 + # de-facto standard. Therefore, the following approach should be
101 + # highly portable in practice. In the case of Linux, and unlike BSD
102 + # this interface does not block in the event that the CSRNG has not
103 + # yet been seeded. Still, this is acceptable because we do not
104 + # require a guarantee that the entropy be cryptographically secure.
105 + # It's also worth noting that Linux >=5.4 is faster at seeding in
106 + # the absence of RDRAND/RDSEED than previous versions were.
107 + test -e /dev/urandom &&
108 + hex=$(
109 + LC_ALL=C tr -dc '[:xdigit:]' < /dev/urandom |
110 + dd bs="$(( num_bytes * 2 ))" count=1 2>/dev/null) &&
111 + test "${#hex}" = "$(( num_bytes * 2 ))" &&
112 + printf '%d\n' "0x${hex}"
113 +}
114 +
115 +_random_apipa_octets()
116 +{
117 + local seed
118 +
119 + # Obtain a highly random 16-bit seed for use by awk's RNG. In the
120 + # unlikely event that the seed ends up being empty, awk will seed
121 + # based on the time of day, with a granularity of one second.
122 + seed=$(_random_bytes_as_int 2)
123 +
124 + # For APIPA (RFC 3927), the 169.254.0.0/16 address block is
125 + # reserved. This provides 65534 addresses, having accounted for the
126 + # network and broadcast address. Note that we must count from 1.
127 + awk "BEGIN {
128 + srand($seed)
129 + for (i=1; i<65535; i++) print rand() \" \" i
130 + }" |
131 + sort -k 1,1 -n |
132 + POSIXLY_CORRECT=1 awk '{
133 + hex = sprintf("%04x",$2)
134 + printf("%d %d\n", "0x" substr(hex,1,2), "0x" substr(hex,3,2))
135 + }'
136 }
137
138 apipa_start()
139 {
140 - local iface="$1" i1= i2= addr= i=0
141 + local addr rc
142
143 - _exists true || return 1
144 + _exists || return
145
146 einfo "Searching for free addresses in 169.254.0.0/16"
147 eindent
148
149 - while [ ${i} -lt 64516 ]; do
150 - : $(( i1 = (_random % 255) + 1 ))
151 - : $(( i2 = (_random % 255) + 1 ))
152 -
153 - addr="169.254.${i1}.${i2}"
154 - vebegin "${addr}/16"
155 - if ! arping_address "${addr}"; then
156 - eval config_${config_index}="\"${addr}/16 broadcast 169.254.255.255\""
157 - : $(( config_index -= 1 ))
158 - veend 0
159 - eoutdent
160 - return 0
161 - fi
162 + exec 3>&1
163 + addr=$(
164 + _random_apipa_octets |
165 + {
166 + while read -r i1 i2; do
167 + addr="169.254.${i1}.${i2}"
168 + vebegin "${addr}/16" >&3
169 + if ! arping_address "${addr}" >&3; then
170 + printf '%s\n' "${addr}"
171 + exit 0
172 + fi
173 + done
174 + exit 1
175 + }
176 + )
177 + rc=$?
178 + exec 3>&-
179
180 - : $(( i += 1 ))
181 - done
182 + if [ "$rc" = 0 ]; then
183 + eval "config_${config_index}=\"\${addr}/16 broadcast 169.254.255.255\""
184 + : $(( config_index -= 1 ))
185 + veend 0
186 + else
187 + eerror "No free address found!"
188 + fi
189
190 - eerror "No free address found!"
191 eoutdent
192 - return 1
193 + return "$rc"
194 }