Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pull request: dhcpd: wait for interfaces' ip addresses to appear
Merge in DNS/adguard-home from 2304-dncp-backoff to master Updates #2304. Squashed commit of the following: commit c9bff8b Author: Ainar Garipov <A.Garipov@AdGuard.COM> Date: Fri Nov 27 14:08:03 2020 +0300 dhcpd: try for 5s instead of 10s commit 983cf47 Author: Ainar Garipov <A.Garipov@AdGuard.COM> Date: Wed Nov 25 19:58:41 2020 +0300 dhcpd: wait for interfaces' ip addresses to appear
- Loading branch information
Showing
7 changed files
with
364 additions
and
171 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package dhcpd | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"time" | ||
|
||
"github.com/AdguardTeam/golibs/log" | ||
) | ||
|
||
// ipVersion is a documentational alias for int. Use it when the integer means | ||
// IP version. | ||
type ipVersion = int | ||
|
||
// IP version constants. | ||
const ( | ||
ipVersion4 ipVersion = 4 | ||
ipVersion6 ipVersion = 6 | ||
) | ||
|
||
// netIface is the interface for network interface methods. | ||
type netIface interface { | ||
Addrs() ([]net.Addr, error) | ||
} | ||
|
||
// ifaceIPAddrs returns the interface's IP addresses. | ||
func ifaceIPAddrs(iface netIface, ipv ipVersion) (ips []net.IP, err error) { | ||
addrs, err := iface.Addrs() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, a := range addrs { | ||
var ip net.IP | ||
switch a := a.(type) { | ||
case *net.IPAddr: | ||
ip = a.IP | ||
case *net.IPNet: | ||
ip = a.IP | ||
default: | ||
continue | ||
} | ||
|
||
// Assume that net.(*Interface).Addrs can only return valid IPv4 | ||
// and IPv6 addresses. Thus, if it isn't an IPv4 address, it | ||
// must be an IPv6 one. | ||
switch ipv { | ||
case ipVersion4: | ||
if ip4 := ip.To4(); ip4 != nil { | ||
ips = append(ips, ip4) | ||
} | ||
case ipVersion6: | ||
if ip6 := ip.To4(); ip6 == nil { | ||
ips = append(ips, ip) | ||
} | ||
default: | ||
return nil, fmt.Errorf("invalid ip version %d", ipv) | ||
} | ||
} | ||
|
||
return ips, nil | ||
} | ||
|
||
// Currently used defaults for ifaceDNSAddrs. | ||
const ( | ||
defaultMaxAttempts int = 10 | ||
|
||
defaultBackoff time.Duration = 500 * time.Millisecond | ||
) | ||
|
||
// ifaceDNSIPAddrs returns IP addresses of the interface suitable to send to | ||
// clients as DNS addresses. If err is nil, addrs contains either no addresses | ||
// or at least two. | ||
// | ||
// It makes up to maxAttempts attempts to get the addresses if there are none, | ||
// each time using the provided backoff. Sometimes an interface needs a few | ||
// seconds to really ititialize. | ||
// | ||
// See https://github.com/AdguardTeam/AdGuardHome/issues/2304. | ||
func ifaceDNSIPAddrs( | ||
iface netIface, | ||
ipv ipVersion, | ||
maxAttempts int, | ||
backoff time.Duration, | ||
) (addrs []net.IP, err error) { | ||
var n int | ||
waitForIP: | ||
for n = 1; n <= maxAttempts; n++ { | ||
addrs, err = ifaceIPAddrs(iface, ipv) | ||
if err != nil { | ||
return nil, fmt.Errorf("getting ip addrs: %w", err) | ||
} | ||
|
||
switch len(addrs) { | ||
case 0: | ||
log.Debug("dhcpv%d: attempt %d: no ip addresses", ipv, n) | ||
|
||
time.Sleep(backoff) | ||
case 1: | ||
// Some Android devices use 8.8.8.8 if there is not | ||
// a secondary DNS server. Fix that by setting the | ||
// secondary DNS address to the same address. | ||
// | ||
// See https://github.com/AdguardTeam/AdGuardHome/issues/1708. | ||
log.Debug("dhcpv%d: setting secondary dns ip to itself", ipv) | ||
addrs = append(addrs, addrs[0]) | ||
|
||
fallthrough | ||
default: | ||
break waitForIP | ||
} | ||
} | ||
|
||
if len(addrs) == 0 { | ||
// Don't return errors in case the users want to try and enable | ||
// the DHCP server later. | ||
log.Error("dhcpv%d: no ip address for interface after %d attempts and %s", ipv, n, time.Duration(n)*backoff) | ||
} else { | ||
log.Debug("dhcpv%d: got addresses %s after %d attempts", ipv, addrs, n) | ||
} | ||
|
||
return addrs, nil | ||
} |
Oops, something went wrong.