-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for IPv6-only preferred (RFC8925) and
control of link-local autoconfigure (RFC2563) Signed-off-by: Brian Candler <b.candler@pobox.com>
- Loading branch information
Showing
8 changed files
with
343 additions
and
471 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,86 @@ | ||
// Copyright 2018-present the CoreDHCP Authors. All rights reserved | ||
// This source code is licensed under the MIT license found in the | ||
// LICENSE file in the root directory of this source tree. | ||
|
||
package autoconfigure | ||
|
||
// This plugin implements RFC2563: | ||
// 1. If the client has been allocated an IP address, do nothing | ||
// 2. If the client has not been allocated an IP address | ||
// (yiaddr=0.0.0.0), then: | ||
// 2a. If the client has requested the "AutoConfigure" option, | ||
// then add the defined value to the response | ||
// 2b. Otherwise, terminate processing and send no reply | ||
// | ||
// This plugin should be used at the end of the plugin chain, | ||
// after any IP address allocation has taken place. | ||
// | ||
// The optional argument is the string "DoNotAutoConfigure" or | ||
// "AutoConfigure" (or "0" or "1" respectively). The default | ||
// is DoNotAutoConfigure. | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/coredhcp/coredhcp/handler" | ||
"github.com/coredhcp/coredhcp/logger" | ||
"github.com/coredhcp/coredhcp/plugins" | ||
"github.com/insomniacslk/dhcp/dhcpv4" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
var log = logger.GetLogger("plugins/autoconfigure") | ||
|
||
var autoconfigure dhcpv4.AutoConfiguration | ||
|
||
var Plugin = plugins.Plugin{ | ||
Name: "autoconfigure", | ||
Setup4: setup4, | ||
} | ||
|
||
var argMap = map[string]dhcpv4.AutoConfiguration{ | ||
"0": dhcpv4.AutoConfiguration(0), | ||
"1": dhcpv4.AutoConfiguration(1), | ||
"DoNotAutoConfigure": dhcpv4.DoNotAutoConfigure, | ||
"AutoConfigure": dhcpv4.AutoConfigure, | ||
} | ||
|
||
func setup4(args ...string) (handler.Handler4, error) { | ||
if len(args) > 0 { | ||
var ok bool | ||
autoconfigure, ok = argMap[args[0]] | ||
if !ok { | ||
return nil, fmt.Errorf("unexpected value '%v' for autoconfigure argument", args[0]) | ||
} | ||
} | ||
if len(args) > 1 { | ||
return nil, errors.New("too many arguments") | ||
} | ||
return Handler4, nil | ||
} | ||
|
||
func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) { | ||
if resp.MessageType() != dhcpv4.MessageTypeOffer || !resp.YourIPAddr.IsUnspecified() { | ||
return resp, false | ||
} | ||
|
||
ac, ok := req.AutoConfigure() | ||
if ok { | ||
resp.UpdateOption(dhcpv4.OptAutoConfigure(autoconfigure)) | ||
log.WithFields(logrus.Fields{ | ||
"mac": req.ClientHWAddr.String(), | ||
"autoconfigure": fmt.Sprintf("%v", ac), | ||
}).Debugf("Responded with autoconfigure %v", autoconfigure) | ||
return resp, false | ||
} | ||
|
||
log.WithFields(logrus.Fields{ | ||
"mac": req.ClientHWAddr.String(), | ||
"autoconfigure": "nil", | ||
}).Debugf("Client does not support autoconfigure") | ||
// RFC2563 2.3: if no address is chosen for the host [...] | ||
// If the DHCPDISCOVER does not contain the Auto-Configure option, | ||
// it is not answered. | ||
return nil, true | ||
} |
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,118 @@ | ||
// Copyright 2018-present the CoreDHCP Authors. All rights reserved | ||
// This source code is licensed under the MIT license found in the | ||
// LICENSE file in the root directory of this source tree. | ||
|
||
package autoconfigure | ||
|
||
import ( | ||
"bytes" | ||
"net" | ||
"testing" | ||
|
||
"github.com/insomniacslk/dhcp/dhcpv4" | ||
) | ||
|
||
func TestOptionRequested0(t *testing.T) { | ||
req, err := dhcpv4.NewDiscovery(net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
req.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionAutoConfigure, []byte{1})) | ||
stub, err := dhcpv4.NewReplyFromRequest(req, | ||
dhcpv4.WithMessageType(dhcpv4.MessageTypeOffer), | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
resp, stop := Handler4(req, stub) | ||
if resp == nil { | ||
t.Fatal("plugin did not return a message") | ||
} | ||
if stop { | ||
t.Error("plugin interrupted processing") | ||
} | ||
opt := resp.Options.Get(dhcpv4.OptionAutoConfigure) | ||
if opt == nil { | ||
t.Fatal("plugin did not return the Auto-Configure option") | ||
} | ||
if !bytes.Equal(opt, []byte{0}) { | ||
t.Errorf("plugin gave wrong option response: %v", opt) | ||
} | ||
} | ||
|
||
func TestOptionRequested1(t *testing.T) { | ||
req, err := dhcpv4.NewDiscovery(net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
req.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionAutoConfigure, []byte{1})) | ||
stub, err := dhcpv4.NewReplyFromRequest(req, | ||
dhcpv4.WithMessageType(dhcpv4.MessageTypeOffer), | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
autoconfigure = 1 | ||
resp, stop := Handler4(req, stub) | ||
if resp == nil { | ||
t.Fatal("plugin did not return a message") | ||
} | ||
if stop { | ||
t.Error("plugin interrupted processing") | ||
} | ||
opt := resp.Options.Get(dhcpv4.OptionAutoConfigure) | ||
if opt == nil { | ||
t.Fatal("plugin did not return the Auto-Configure option") | ||
} | ||
if !bytes.Equal(opt, []byte{1}) { | ||
t.Errorf("plugin gave wrong option response: %v", opt) | ||
} | ||
} | ||
|
||
func TestNotRequestedAssignedIP(t *testing.T) { | ||
req, err := dhcpv4.NewDiscovery(net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
stub, err := dhcpv4.NewReplyFromRequest(req, | ||
dhcpv4.WithMessageType(dhcpv4.MessageTypeOffer), | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
stub.YourIPAddr = net.ParseIP("192.0.2.100") | ||
|
||
resp, stop := Handler4(req, stub) | ||
if resp == nil { | ||
t.Fatal("plugin did not return a message") | ||
} | ||
if stop { | ||
t.Error("plugin interrupted processing") | ||
} | ||
if resp.Options.Get(dhcpv4.OptionAutoConfigure) != nil { | ||
t.Error("plugin responsed with AutoConfigure option") | ||
} | ||
} | ||
|
||
func TestNotRequestedNoIP(t *testing.T) { | ||
req, err := dhcpv4.NewDiscovery(net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
stub, err := dhcpv4.NewReplyFromRequest(req, | ||
dhcpv4.WithMessageType(dhcpv4.MessageTypeOffer), | ||
) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
resp, stop := Handler4(req, stub) | ||
if resp != nil { | ||
t.Error("plugin returned a message") | ||
} | ||
if !stop { | ||
t.Error("plugin did not interrupt processing") | ||
} | ||
} |
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,64 @@ | ||
// Copyright 2018-present the CoreDHCP Authors. All rights reserved | ||
// This source code is licensed under the MIT license found in the | ||
// LICENSE file in the root directory of this source tree. | ||
|
||
package ipv6only | ||
|
||
// This plugin implements RFC8925: if the client has requested the | ||
// IPv6-Only Preferred option, then add the option response and then | ||
// terminate processing immediately. | ||
// | ||
// This module should be invoked *before* any IP address | ||
// allocation has been done, so that the yiaddr is 0.0.0.0 and | ||
// no pool addresses are consumed for compatible clients. | ||
// | ||
// The optional argument is the V6ONLY_WAIT configuration variable, | ||
// described in RFC8925 section 3.2. | ||
|
||
import ( | ||
"errors" | ||
"time" | ||
|
||
"github.com/coredhcp/coredhcp/handler" | ||
"github.com/coredhcp/coredhcp/logger" | ||
"github.com/coredhcp/coredhcp/plugins" | ||
"github.com/insomniacslk/dhcp/dhcpv4" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
var log = logger.GetLogger("plugins/ipv6only") | ||
|
||
var v6only_wait time.Duration | ||
|
||
var Plugin = plugins.Plugin{ | ||
Name: "ipv6only", | ||
Setup4: setup4, | ||
} | ||
|
||
func setup4(args ...string) (handler.Handler4, error) { | ||
if len(args) > 0 { | ||
dur, err := time.ParseDuration(args[0]) | ||
if err != nil { | ||
log.Errorf("invalid duration: %v", args[0]) | ||
return nil, errors.New("ipv6only failed to initialize") | ||
} | ||
v6only_wait = dur | ||
} | ||
if len(args) > 1 { | ||
return nil, errors.New("too many arguments") | ||
} | ||
return Handler4, nil | ||
} | ||
|
||
func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) { | ||
v6pref := req.IsOptionRequested(dhcpv4.OptionIPv6OnlyPreferred) | ||
log.WithFields(logrus.Fields{ | ||
"mac": req.ClientHWAddr.String(), | ||
"ipv6only": v6pref, | ||
}).Debug("ipv6only status") | ||
if v6pref { | ||
resp.UpdateOption(dhcpv4.OptIPv6OnlyPreferred(v6only_wait)) | ||
return resp, true | ||
} | ||
return resp, false | ||
} |
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,65 @@ | ||
// Copyright 2018-present the CoreDHCP Authors. All rights reserved | ||
// This source code is licensed under the MIT license found in the | ||
// LICENSE file in the root directory of this source tree. | ||
|
||
package ipv6only | ||
|
||
import ( | ||
"bytes" | ||
"net" | ||
"testing" | ||
"time" | ||
|
||
"github.com/insomniacslk/dhcp/dhcpv4" | ||
) | ||
|
||
func TestOptionRequested(t *testing.T) { | ||
req, err := dhcpv4.NewDiscovery(net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
req.UpdateOption(dhcpv4.OptParameterRequestList(dhcpv4.OptionBroadcastAddress, dhcpv4.OptionIPv6OnlyPreferred)) | ||
stub, err := dhcpv4.NewReplyFromRequest(req) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
v6only_wait = 0x1234 * time.Second | ||
|
||
resp, stop := Handler4(req, stub) | ||
if resp == nil { | ||
t.Fatal("plugin did not return a message") | ||
} | ||
if !stop { | ||
t.Error("plugin did not interrupt processing") | ||
} | ||
opt := resp.Options.Get(dhcpv4.OptionIPv6OnlyPreferred) | ||
if opt == nil { | ||
t.Fatal("plugin did not return the IPv6-Only Preferred option") | ||
} | ||
if !bytes.Equal(opt, []byte{0x00, 0x00, 0x12, 0x34}) { | ||
t.Errorf("plugin gave wrong option response: %v", opt) | ||
} | ||
} | ||
|
||
func TestNotRequested(t *testing.T) { | ||
req, err := dhcpv4.NewDiscovery(net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
stub, err := dhcpv4.NewReplyFromRequest(req) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
resp, stop := Handler4(req, stub) | ||
if resp == nil { | ||
t.Fatal("plugin did not return a message") | ||
} | ||
if stop { | ||
t.Error("plugin interrupted processing") | ||
} | ||
if resp.Options.Get(dhcpv4.OptionIPv6OnlyPreferred) != nil { | ||
t.Error("Found IPv6-Only Preferred option when not requested") | ||
} | ||
} |