Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
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
with
364 additions
and 171 deletions.
- +38 −27 HACKING.md
- +2 −2 internal/dhcpd/check_other_dhcp.go
- +5 −41 internal/dhcpd/v4.go
- +123 −0 internal/dhcpd/v46.go
- +189 −0 internal/dhcpd/v46_test.go
- +7 −46 internal/dhcpd/v6.go
- +0 −55 internal/dhcpd/v6_test.go
@@ -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.