+ DHCP: add ra_slaac_only, ra_allow_slaac config settings
Close # 2076
Squashed commit of the following:
commit 7a938fc6ffa0adc1e705f59778d7e7442b83d4b9
Merge: 3db2605e 6222d17d
Author: Simon Zolin <s.zolin@adguard.com>
Date: Thu Sep 24 16:34:23 2020 +0300
Merge remote-tracking branch 'origin/master' into 2076-dhcp-ra-slaac
commit 3db2605efda79e8d361fb46c32bb9ec753de0e02
Author: Simon Zolin <s.zolin@adguard.com>
Date: Thu Sep 24 16:21:36 2020 +0300
minor
commit de31c6aeebd78dce54ca54fbff010f3dd50ce974
Author: Simon Zolin <s.zolin@adguard.com>
Date: Thu Sep 24 12:23:09 2020 +0300
don't initialize DHCPv6 server if we must force the clients to use SLAAC
commit 7c4239899c85880641dc5b4826ae34386bc5ee94
Author: Simon Zolin <s.zolin@adguard.com>
Date: Tue Sep 22 19:34:27 2020 +0300
minor
commit 2b57688ce4c31d45d2236bd397e0b3041dac68c1
Author: Simon Zolin <s.zolin@adguard.com>
Date: Mon Sep 21 19:50:11 2020 +0300
+ DHCP: add ra_slaac_only, ra_allow_slaac config settings
This commit is contained in:
parent
6222d17d86
commit
990f531f54
|
@ -33,6 +33,7 @@ Contents:
|
||||||
* Static IP check/set
|
* Static IP check/set
|
||||||
* API: Add a static lease
|
* API: Add a static lease
|
||||||
* API: Reset DHCP configuration
|
* API: Reset DHCP configuration
|
||||||
|
* RA+SLAAC
|
||||||
* DNS general settings
|
* DNS general settings
|
||||||
* API: Get DNS general settings
|
* API: Get DNS general settings
|
||||||
* API: Set DNS general settings
|
* API: Set DNS general settings
|
||||||
|
@ -725,6 +726,53 @@ Response:
|
||||||
200 OK
|
200 OK
|
||||||
|
|
||||||
|
|
||||||
|
### RA+SLAAC
|
||||||
|
|
||||||
|
There are 3 options for a client to get IPv6 address:
|
||||||
|
|
||||||
|
1. via DHCPv6.
|
||||||
|
Client doesn't receive any `ICMPv6.RouterAdvertisement` packets, so it tries to use DHCPv6.
|
||||||
|
2. via SLAAC.
|
||||||
|
Client receives a `ICMPv6.RouterAdvertisement` packet with `Managed=false` flag and IPv6 prefix.
|
||||||
|
Client then assigns to itself an IPv6 address using this prefix and its MAC address.
|
||||||
|
DHCPv6 server won't be started in this case.
|
||||||
|
3. via DHCPv6 or SLAAC.
|
||||||
|
Client receives a `ICMPv6.RouterAdvertisement` packet with `Managed=true` flag and IPv6 prefix.
|
||||||
|
Client may choose to use SLAAC or DHCPv6 to obtain an IPv6 address.
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
|
||||||
|
dhcp:
|
||||||
|
...
|
||||||
|
dhcpv6:
|
||||||
|
...
|
||||||
|
ra_slaac_only: false
|
||||||
|
ra_allow_slaac: false
|
||||||
|
|
||||||
|
* `ra_slaac_only:false; ra_allow_slaac:false`: use option #1.
|
||||||
|
Don't send any `ICMPv6.RouterAdvertisement` packets.
|
||||||
|
* `ra_slaac_only:true; ra_allow_slaac:false`: use option #2.
|
||||||
|
Periodically send `ICMPv6.RouterAdvertisement(Flags=(Managed=false,Other=false))` packets.
|
||||||
|
* `ra_slaac_only:false; ra_allow_slaac:true`: use option #3.
|
||||||
|
Periodically send `ICMPv6.RouterAdvertisement(Flags=(Managed=true,Other=true))` packets.
|
||||||
|
|
||||||
|
ICMPv6.RouterAdvertisement packet description:
|
||||||
|
|
||||||
|
ICMPv6:
|
||||||
|
Type=RouterAdvertisement(134)
|
||||||
|
Flags
|
||||||
|
Managed=<BOOL>
|
||||||
|
Other=<BOOL>
|
||||||
|
Option=Prefix information(3)
|
||||||
|
<IPv6 address prefix (/64) of the network interface>
|
||||||
|
Option=MTU(5)
|
||||||
|
<...>
|
||||||
|
Option=Source link-layer address(1)
|
||||||
|
<MAC address>
|
||||||
|
Option=Recursive DNS Server(25)
|
||||||
|
<IPv6 address of DNS server>
|
||||||
|
|
||||||
|
|
||||||
## TLS
|
## TLS
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,10 @@ To set up a test environment for DHCP server you need:
|
||||||
|
|
||||||
3. Start your VM, install an OS. Configure your network interface to use DHCP and the OS should ask for a IP address from our DHCP server.
|
3. Start your VM, install an OS. Configure your network interface to use DHCP and the OS should ask for a IP address from our DHCP server.
|
||||||
|
|
||||||
|
4. To see the current IP address on client OS you can use `ip a` command on Linux or `ipconfig` on Windows.
|
||||||
|
|
||||||
|
5. To force the client OS to request an IP from DHCP server again, you can use `dhclient` on Linux or `ipconfig /release` on Windows.
|
||||||
|
|
||||||
### Configure server
|
### Configure server
|
||||||
|
|
||||||
1. Edit server configuration file 'AdGuardHome.yaml', for example:
|
1. Edit server configuration file 'AdGuardHome.yaml', for example:
|
||||||
|
@ -37,12 +41,19 @@ To set up a test environment for DHCP server you need:
|
||||||
dhcp:
|
dhcp:
|
||||||
enabled: true
|
enabled: true
|
||||||
interface_name: vboxnet0
|
interface_name: vboxnet0
|
||||||
gateway_ip: 192.168.56.1
|
dhcpv4:
|
||||||
subnet_mask: 255.255.255.0
|
gateway_ip: 192.168.56.1
|
||||||
range_start: 192.168.56.2
|
subnet_mask: 255.255.255.0
|
||||||
range_end: 192.168.56.2
|
range_start: 192.168.56.2
|
||||||
lease_duration: 86400
|
range_end: 192.168.56.2
|
||||||
icmp_timeout_msec: 1000
|
lease_duration: 86400
|
||||||
|
icmp_timeout_msec: 1000
|
||||||
|
options: []
|
||||||
|
dhcpv6:
|
||||||
|
range_start: 2001::1
|
||||||
|
lease_duration: 86400
|
||||||
|
ra_slaac_only: false
|
||||||
|
ra_allow_slaac: false
|
||||||
|
|
||||||
2. Start the server
|
2. Start the server
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
package dhcpd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type raCtx struct {
|
||||||
|
raAllowSlaac bool // send RA packets without MO flags
|
||||||
|
raSlaacOnly bool // send RA packets with MO flags
|
||||||
|
ipAddr net.IP // source IP address (link-local-unicast)
|
||||||
|
dnsIPAddr net.IP // IP address for DNS Server option
|
||||||
|
prefixIPAddr net.IP // IP address for Prefix option
|
||||||
|
ifaceName string
|
||||||
|
iface *net.Interface
|
||||||
|
packetSendPeriod time.Duration // how often RA packets are sent
|
||||||
|
|
||||||
|
conn *icmp.PacketConn // ICMPv6 socket
|
||||||
|
stop atomic.Value // stop the packet sending loop
|
||||||
|
}
|
||||||
|
|
||||||
|
type icmpv6RA struct {
|
||||||
|
managedAddressConfiguration bool
|
||||||
|
otherConfiguration bool
|
||||||
|
prefix net.IP
|
||||||
|
prefixLen int
|
||||||
|
sourceLinkLayerAddress net.HardwareAddr
|
||||||
|
recursiveDNSServer net.IP
|
||||||
|
mtu uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an ICMPv6.RouterAdvertisement packet with all necessary options.
|
||||||
|
//
|
||||||
|
// ICMPv6:
|
||||||
|
// type[1]
|
||||||
|
// code[1]
|
||||||
|
// chksum[2]
|
||||||
|
// body (RouterAdvertisement):
|
||||||
|
// Cur Hop Limit[1]
|
||||||
|
// Flags[1]: MO......
|
||||||
|
// Router Lifetime[2]
|
||||||
|
// Reachable Time[4]
|
||||||
|
// Retrans Timer[4]
|
||||||
|
// Option=Prefix Information(3):
|
||||||
|
// Type[1]
|
||||||
|
// Length * 8bytes[1]
|
||||||
|
// Prefix Length[1]
|
||||||
|
// Flags[1]: LA......
|
||||||
|
// Valid Lifetime[4]
|
||||||
|
// Preferred Lifetime[4]
|
||||||
|
// Reserved[4]
|
||||||
|
// Prefix[16]
|
||||||
|
// Option=MTU(5):
|
||||||
|
// Type[1]
|
||||||
|
// Length * 8bytes[1]
|
||||||
|
// Reserved[2]
|
||||||
|
// MTU[4]
|
||||||
|
// Option=Source link-layer address(1):
|
||||||
|
// Link-Layer Address[6]
|
||||||
|
// Option=Recursive DNS Server(25):
|
||||||
|
// Type[1]
|
||||||
|
// Length * 8bytes[1]
|
||||||
|
// Reserved[2]
|
||||||
|
// Lifetime[4]
|
||||||
|
// Addresses of IPv6 Recursive DNS Servers[16]
|
||||||
|
func createICMPv6RAPacket(params icmpv6RA) []byte {
|
||||||
|
data := make([]byte, 88)
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
// ICMPv6:
|
||||||
|
|
||||||
|
data[i] = 134 // type
|
||||||
|
data[i+1] = 0 // code
|
||||||
|
data[i+2] = 0 // chksum
|
||||||
|
data[i+3] = 0
|
||||||
|
i += 4
|
||||||
|
|
||||||
|
// RouterAdvertisement:
|
||||||
|
|
||||||
|
data[i] = 64 // Cur Hop Limit[1]
|
||||||
|
i++
|
||||||
|
|
||||||
|
data[i] = 0 // Flags[1]: MO......
|
||||||
|
if params.managedAddressConfiguration {
|
||||||
|
data[i] |= 0x80
|
||||||
|
}
|
||||||
|
if params.otherConfiguration {
|
||||||
|
data[i] |= 0x40
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(data[i:], 1800) // Router Lifetime[2]
|
||||||
|
i += 2
|
||||||
|
binary.BigEndian.PutUint32(data[i:], 0) // Reachable Time[4]
|
||||||
|
i += 4
|
||||||
|
binary.BigEndian.PutUint32(data[i:], 0) // Retrans Timer[4]
|
||||||
|
i += 4
|
||||||
|
|
||||||
|
// Option=Prefix Information:
|
||||||
|
|
||||||
|
data[i] = 3 // Type
|
||||||
|
data[i+1] = 4 // Length
|
||||||
|
i += 2
|
||||||
|
data[i] = byte(params.prefixLen) // Prefix Length[1]
|
||||||
|
i++
|
||||||
|
data[i] = 0xc0 // Flags[1]
|
||||||
|
i++
|
||||||
|
binary.BigEndian.PutUint32(data[i:], 3600) // Valid Lifetime[4]
|
||||||
|
i += 4
|
||||||
|
binary.BigEndian.PutUint32(data[i:], 3600) // Preferred Lifetime[4]
|
||||||
|
i += 4
|
||||||
|
binary.BigEndian.PutUint32(data[i:], 0) // Reserved[4]
|
||||||
|
i += 4
|
||||||
|
copy(data[i:], params.prefix[:8]) // Prefix[16]
|
||||||
|
binary.BigEndian.PutUint32(data[i+8:], 0)
|
||||||
|
binary.BigEndian.PutUint32(data[i+12:], 0)
|
||||||
|
i += 16
|
||||||
|
|
||||||
|
// Option=MTU:
|
||||||
|
|
||||||
|
data[i] = 5 // Type
|
||||||
|
data[i+1] = 1 // Length
|
||||||
|
i += 2
|
||||||
|
binary.BigEndian.PutUint16(data[i:], 0) // Reserved[2]
|
||||||
|
i += 2
|
||||||
|
binary.BigEndian.PutUint32(data[i:], params.mtu) // MTU[4]
|
||||||
|
i += 4
|
||||||
|
|
||||||
|
// Option=Source link-layer address:
|
||||||
|
|
||||||
|
data[i] = 1 // Type
|
||||||
|
data[i+1] = 1 // Length
|
||||||
|
i += 2
|
||||||
|
copy(data[i:], params.sourceLinkLayerAddress) // Link-Layer Address[6]
|
||||||
|
i += 6
|
||||||
|
|
||||||
|
// Option=Recursive DNS Server:
|
||||||
|
|
||||||
|
data[i] = 25 // Type
|
||||||
|
data[i+1] = 3 // Length
|
||||||
|
i += 2
|
||||||
|
binary.BigEndian.PutUint16(data[i:], 0) // Reserved[2]
|
||||||
|
i += 2
|
||||||
|
binary.BigEndian.PutUint32(data[i:], 3600) // Lifetime[4]
|
||||||
|
i += 4
|
||||||
|
copy(data[i:], params.recursiveDNSServer) // Addresses of IPv6 Recursive DNS Servers[16]
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init - initialize RA module
|
||||||
|
func (ra *raCtx) Init() error {
|
||||||
|
ra.stop.Store(0)
|
||||||
|
ra.conn = nil
|
||||||
|
if !(ra.raAllowSlaac || ra.raSlaacOnly) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("DHCPv6 RA: source IP address: %s DNS IP address: %s",
|
||||||
|
ra.ipAddr, ra.dnsIPAddr)
|
||||||
|
|
||||||
|
params := icmpv6RA{
|
||||||
|
managedAddressConfiguration: !ra.raSlaacOnly,
|
||||||
|
otherConfiguration: !ra.raSlaacOnly,
|
||||||
|
mtu: uint32(ra.iface.MTU),
|
||||||
|
prefixLen: 64,
|
||||||
|
recursiveDNSServer: ra.dnsIPAddr,
|
||||||
|
sourceLinkLayerAddress: ra.iface.HardwareAddr,
|
||||||
|
}
|
||||||
|
params.prefix = make([]byte, 16)
|
||||||
|
copy(params.prefix, ra.prefixIPAddr[:8]) // /64
|
||||||
|
|
||||||
|
data := createICMPv6RAPacket(params)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName
|
||||||
|
ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DHCPv6 RA: icmp.ListenPacket: %s", err)
|
||||||
|
}
|
||||||
|
success := false
|
||||||
|
defer func() {
|
||||||
|
if !success {
|
||||||
|
ra.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
con6 := ra.conn.IPv6PacketConn()
|
||||||
|
|
||||||
|
if err := con6.SetHopLimit(255); err != nil {
|
||||||
|
return fmt.Errorf("DHCPv6 RA: SetHopLimit: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := con6.SetMulticastHopLimit(255); err != nil {
|
||||||
|
return fmt.Errorf("DHCPv6 RA: SetMulticastHopLimit: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &ipv6.ControlMessage{
|
||||||
|
HopLimit: 255,
|
||||||
|
Src: ra.ipAddr,
|
||||||
|
IfIndex: ra.iface.Index,
|
||||||
|
}
|
||||||
|
addr := &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff02::1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Debug("DHCPv6 RA: starting to send periodic RouterAdvertisement packets")
|
||||||
|
for ra.stop.Load() == 0 {
|
||||||
|
_, err = con6.WriteTo(data, msg, addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("DHCPv6 RA: WriteTo: %s", err)
|
||||||
|
}
|
||||||
|
time.Sleep(ra.packetSendPeriod)
|
||||||
|
}
|
||||||
|
log.Debug("DHCPv6 RA: loop exit")
|
||||||
|
}()
|
||||||
|
|
||||||
|
success = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close - close module
|
||||||
|
func (ra *raCtx) Close() {
|
||||||
|
log.Debug("DHCPv6 RA: closing")
|
||||||
|
|
||||||
|
ra.stop.Store(1)
|
||||||
|
|
||||||
|
if ra.conn != nil {
|
||||||
|
ra.conn.Close()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package dhcpd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRA(t *testing.T) {
|
||||||
|
ra := icmpv6RA{
|
||||||
|
managedAddressConfiguration: false,
|
||||||
|
otherConfiguration: true,
|
||||||
|
mtu: 1500,
|
||||||
|
prefix: net.ParseIP("1234::"),
|
||||||
|
prefixLen: 64,
|
||||||
|
recursiveDNSServer: net.ParseIP("fe80::800:27ff:fe00:0"),
|
||||||
|
sourceLinkLayerAddress: []byte{0x0a, 0x00, 0x27, 0x00, 0x00, 0x00},
|
||||||
|
}
|
||||||
|
data := createICMPv6RAPacket(ra)
|
||||||
|
dataCorrect := []byte{
|
||||||
|
0x86, 0x00, 0x00, 0x00, 0x40, 0x40, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc, 0x01, 0x01, 0x0a, 0x00, 0x27, 0x00, 0x00, 0x00,
|
||||||
|
0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x10, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x00, 0x27, 0xff, 0xfe, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
assert.True(t, bytes.Equal(data, dataCorrect))
|
||||||
|
}
|
|
@ -83,6 +83,9 @@ type V6ServerConf struct {
|
||||||
|
|
||||||
LeaseDuration uint32 `yaml:"lease_duration"` // in seconds
|
LeaseDuration uint32 `yaml:"lease_duration"` // in seconds
|
||||||
|
|
||||||
|
RaSlaacOnly bool `yaml:"ra_slaac_only"` // send ICMPv6.RA packets without MO flags
|
||||||
|
RaAllowSlaac bool `yaml:"ra_allow_slaac"` // send ICMPv6.RA packets with MO flags
|
||||||
|
|
||||||
ipStart net.IP // starting IP address for dynamic leases
|
ipStart net.IP // starting IP address for dynamic leases
|
||||||
leaseTime time.Duration // the time during which a dynamic lease is considered valid
|
leaseTime time.Duration // the time during which a dynamic lease is considered valid
|
||||||
dnsIPAddrs []net.IP // IPv6 addresses to return to DHCP clients as DNS server addresses
|
dnsIPAddrs []net.IP // IPv6 addresses to return to DHCP clients as DNS server addresses
|
||||||
|
|
43
dhcpd/v6.go
43
dhcpd/v6.go
|
@ -25,6 +25,8 @@ type v6Server struct {
|
||||||
ipAddrs [256]byte
|
ipAddrs [256]byte
|
||||||
sid dhcpv6.Duid
|
sid dhcpv6.Duid
|
||||||
|
|
||||||
|
ra raCtx // RA module
|
||||||
|
|
||||||
conf V6ServerConf
|
conf V6ServerConf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,6 +559,27 @@ func getIfaceIPv6(iface net.Interface) []net.IP {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize RA module
|
||||||
|
func (s *v6Server) initRA(iface *net.Interface) error {
|
||||||
|
// choose the source IP address - should be link-local-unicast
|
||||||
|
s.ra.ipAddr = s.conf.dnsIPAddrs[0]
|
||||||
|
for _, ip := range s.conf.dnsIPAddrs {
|
||||||
|
if ip.IsLinkLocalUnicast() {
|
||||||
|
s.ra.ipAddr = ip
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ra.raAllowSlaac = s.conf.RaAllowSlaac
|
||||||
|
s.ra.raSlaacOnly = s.conf.RaSlaacOnly
|
||||||
|
s.ra.dnsIPAddr = s.ra.ipAddr
|
||||||
|
s.ra.prefixIPAddr = s.conf.ipStart
|
||||||
|
s.ra.ifaceName = s.conf.InterfaceName
|
||||||
|
s.ra.iface = iface
|
||||||
|
s.ra.packetSendPeriod = 1 * time.Second
|
||||||
|
return s.ra.Init()
|
||||||
|
}
|
||||||
|
|
||||||
// Start - start server
|
// Start - start server
|
||||||
func (s *v6Server) Start() error {
|
func (s *v6Server) Start() error {
|
||||||
if !s.conf.Enabled {
|
if !s.conf.Enabled {
|
||||||
|
@ -568,13 +591,25 @@ func (s *v6Server) Start() error {
|
||||||
return wrapErrPrint(err, "Couldn't find interface by name %s", s.conf.InterfaceName)
|
return wrapErrPrint(err, "Couldn't find interface by name %s", s.conf.InterfaceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("DHCPv6: starting...")
|
|
||||||
s.conf.dnsIPAddrs = getIfaceIPv6(*iface)
|
s.conf.dnsIPAddrs = getIfaceIPv6(*iface)
|
||||||
if len(s.conf.dnsIPAddrs) == 0 {
|
if len(s.conf.dnsIPAddrs) == 0 {
|
||||||
log.Debug("DHCPv6: no IPv6 address for interface %s", iface.Name)
|
log.Debug("DHCPv6: no IPv6 address for interface %s", iface.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = s.initRA(iface)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't initialize DHCPv6 server if we must force the clients to use SLAAC
|
||||||
|
if s.conf.RaSlaacOnly {
|
||||||
|
log.Debug("DHCPv6: not starting DHCPv6 server due to ra_slaac_only=true")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("DHCPv6: starting...")
|
||||||
|
|
||||||
if len(iface.HardwareAddr) != 6 {
|
if len(iface.HardwareAddr) != 6 {
|
||||||
return fmt.Errorf("DHCPv6: invalid MAC %s", iface.HardwareAddr)
|
return fmt.Errorf("DHCPv6: invalid MAC %s", iface.HardwareAddr)
|
||||||
}
|
}
|
||||||
|
@ -598,11 +633,15 @@ func (s *v6Server) Start() error {
|
||||||
err = s.srv.Serve()
|
err = s.srv.Serve()
|
||||||
log.Debug("DHCPv6: srv.Serve: %s", err)
|
log.Debug("DHCPv6: srv.Serve: %s", err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop - stop server
|
// Stop - stop server
|
||||||
func (s *v6Server) Stop() {
|
func (s *v6Server) Stop() {
|
||||||
|
s.ra.Close()
|
||||||
|
|
||||||
|
// DHCPv6 server may not be initialized if ra_slaac_only=true
|
||||||
if s.srv == nil {
|
if s.srv == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -626,7 +665,7 @@ func v6Create(conf V6ServerConf) (DHCPServer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s.conf.ipStart = net.ParseIP(conf.RangeStart)
|
s.conf.ipStart = net.ParseIP(conf.RangeStart)
|
||||||
if s.conf.ipStart == nil {
|
if s.conf.ipStart == nil || s.conf.ipStart.To16() == nil {
|
||||||
return s, fmt.Errorf("DHCPv6: invalid range-start IP: %s", conf.RangeStart)
|
return s, fmt.Errorf("DHCPv6: invalid range-start IP: %s", conf.RangeStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue