Explorar el Código

plugins/prefix: Initial plugin handler

Signed-off-by: Anatole Denis <anatole@unverle.fr>
Anatole Denis hace 6 años
padre
commit
6342ac1088

+ 1 - 0
cmds/coredhcp-generator/core-plugins.txt

@@ -3,6 +3,7 @@ github.com/coredhcp/coredhcp/plugins/file
 github.com/coredhcp/coredhcp/plugins/leasetime
 github.com/coredhcp/coredhcp/plugins/netmask
 github.com/coredhcp/coredhcp/plugins/nbp
+github.com/coredhcp/coredhcp/plugins/prefix
 github.com/coredhcp/coredhcp/plugins/range
 github.com/coredhcp/coredhcp/plugins/router
 github.com/coredhcp/coredhcp/plugins/serverid

+ 2 - 0
cmds/coredhcp/main.go

@@ -24,6 +24,7 @@ import (
 	pl_leasetime "github.com/coredhcp/coredhcp/plugins/leasetime"
 	pl_nbp "github.com/coredhcp/coredhcp/plugins/nbp"
 	pl_netmask "github.com/coredhcp/coredhcp/plugins/netmask"
+	pl_prefix "github.com/coredhcp/coredhcp/plugins/prefix"
 	pl_range "github.com/coredhcp/coredhcp/plugins/range"
 	pl_router "github.com/coredhcp/coredhcp/plugins/router"
 	pl_serverid "github.com/coredhcp/coredhcp/plugins/serverid"
@@ -62,6 +63,7 @@ var desiredPlugins = []*plugins.Plugin{
 	&pl_leasetime.Plugin,
 	&pl_nbp.Plugin,
 	&pl_netmask.Plugin,
+	&pl_prefix.Plugin,
 	&pl_range.Plugin,
 	&pl_router.Plugin,
 	&pl_serverid.Plugin,

+ 144 - 0
plugins/prefix/plugin.go

@@ -0,0 +1,144 @@
+// 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 prefix implements a plugin offering prefixes to clients requesting them
+// This plugin attributes prefixes to clients requesting them with IA_PREFIX requests.
+//
+// Arguments for the plugin configuration are as follows, in this order:
+// - prefix: The base prefix from which assigned prefixes are carved
+// - max: maximum size of the prefix delegated to clients. When a client requests a larger prefix
+// than this, this is the size of the offered prefix
+package prefix
+
+// FIXME: various settings will be hardcoded (default size, minimum size, lease times) pending a
+// better configuration system
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"strconv"
+
+	"github.com/insomniacslk/dhcp/dhcpv6"
+	dhcpIana "github.com/insomniacslk/dhcp/iana"
+
+	"github.com/coredhcp/coredhcp/handler"
+	"github.com/coredhcp/coredhcp/logger"
+	"github.com/coredhcp/coredhcp/plugins"
+	"github.com/coredhcp/coredhcp/plugins/prefix/allocators"
+	"github.com/coredhcp/coredhcp/plugins/prefix/allocators/fixedsize"
+)
+
+var log = logger.GetLogger("plugins/prefix")
+
+// Plugin registers the prefix. Prefix delegation only exists for DHCPv6
+var Plugin = plugins.Plugin{
+	Name:   "prefix",
+	Setup6: setupPrefix,
+}
+
+func setupPrefix(args ...string) (handler.Handler6, error) {
+	// - prefix: 2001:db8::/48 64
+	if len(args) < 2 {
+		return nil, errors.New("Need both a subnet and an allocation max size")
+	}
+
+	_, prefix, err := net.ParseCIDR(args[0])
+	if err != nil {
+		return nil, fmt.Errorf("Invalid pool subnet: %v", err)
+	}
+
+	allocSize, err := strconv.Atoi(args[1])
+	if err != nil || allocSize > 128 || allocSize < 0 {
+		return nil, fmt.Errorf("Invalid prefix length: %v", err)
+	}
+
+	// TODO: select allocators based on heuristics or user configuration
+	alloc, err := fixedsize.NewFixedSizeAllocator(*prefix, allocSize)
+	if err != nil {
+		return nil, fmt.Errorf("Could not initialize prefix allocator: %v", err)
+	}
+
+	return (&Handler{
+		leases:    make(map[uint64]dhcpv6.Duid),
+		allocator: alloc,
+	}).Handle, nil
+}
+
+// Handler holds state of allocations for the plugin
+type Handler struct {
+	leases    map[uint64]dhcpv6.Duid
+	allocator allocators.Allocator
+}
+
+func (h *Handler) allocate(p net.IPNet) (net.IPNet, error) {
+	// TODO: handle the leases array
+	// TODO: handle renewal
+	// TODO: handle expiration / fast re-commit (when requesting an expired prefix, don't go through the allocator)
+	return h.allocator.Allocate(p)
+}
+
+// Handle processes DHCPv6 packets for the prefix plugin for a given allocator/leaseset
+func (h *Handler) Handle(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
+	msg, err := req.GetInnerMessage()
+	if err != nil {
+		log.Error(err)
+		return nil, true
+	}
+	// Each request IA_PD requires an IA_PD response
+	for _, iapd := range msg.Options.IAPD() {
+		if err != nil {
+			log.Errorf("Malformed IAPD received: %v", err)
+			resp.AddOption(&dhcpv6.OptStatusCode{StatusCode: dhcpIana.StatusMalformedQuery})
+			return resp, true
+		}
+
+		iapdResp := &dhcpv6.OptIAPD{
+			IaId: iapd.IaId,
+			T1:   3600,
+			T2:   3600,
+		}
+
+		hints := iapd.Options.Prefixes()
+		if len(hints) == 0 {
+			// If there are no IAPrefix hints, this is still a valid IA_PD request (just
+			// unspecified) and we must attempt to allocate a prefix; so we include an
+			// empty hint which is equivalent to no hint
+			hints = []*dhcpv6.OptIAPrefix{&dhcpv6.OptIAPrefix{}}
+		}
+
+		for _, iaprefix := range hints {
+			// FIXME: This allocates both in ADVERTISE and REPLY.
+			// Need to offer an available prefix without reserving it in ADVERTISE, or
+			// with an immediately-expired reservation; then confirm it in REPLY
+			if iaprefix.Prefix == nil {
+				iaprefix.Prefix = &net.IPNet{}
+			}
+			allocated, err := h.allocate(*iaprefix.Prefix)
+			if err != nil {
+				log.Debugf("Nothing allocated for hinted prefix %s", iaprefix)
+				continue
+			}
+
+			r := &dhcpv6.OptIAPrefix{
+				PreferredLifetime: 3600,
+				ValidLifetime:     3600,
+				Prefix:            &allocated,
+			}
+
+			iapdResp.Options.Add(r)
+		}
+
+		if len(iapdResp.Options.Options) == 0 {
+			log.Debugf("No valid prefix to return for IAID %x", iapd.IaId)
+			iapdResp.Options.Add(&dhcpv6.OptStatusCode{
+				StatusCode: dhcpIana.StatusNoPrefixAvail,
+			})
+		}
+
+		resp.AddOption(iapdResp)
+	}
+
+	return resp, false
+}

+ 82 - 0
plugins/prefix/plugin_test.go

@@ -0,0 +1,82 @@
+// 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 prefix
+
+import (
+	"net"
+	"testing"
+
+	"github.com/insomniacslk/dhcp/dhcpv6"
+	dhcpIana "github.com/insomniacslk/dhcp/iana"
+)
+
+func TestRoundTrip(t *testing.T) {
+	reqIAID := [4]uint8{0x12, 0x34, 0x56, 0x78}
+
+	req, err := dhcpv6.NewMessage()
+	if err != nil {
+		t.Fatal(err)
+	}
+	req.AddOption(dhcpv6.OptClientID(dhcpv6.Duid{
+		Type:          dhcpv6.DUID_LL,
+		HwType:        dhcpIana.HWTypeEthernet,
+		LinkLayerAddr: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+	}))
+	req.AddOption(&dhcpv6.OptIAPD{
+		IaId: reqIAID,
+		T1:   0,
+		T2:   0,
+	})
+
+	resp, err := dhcpv6.NewAdvertiseFromSolicit(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	handler, err := setupPrefix("2001:db8::/48", "64")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	result, final := handler(req, resp)
+	if final {
+		t.Log("Handler declared final")
+	}
+	t.Logf("%#v", result)
+
+	// Sanity checks on the response
+	success := result.GetOption(dhcpv6.OptionStatusCode)
+	if len(success) > 1 {
+		t.Fatal("Got multiple StatusCode options")
+	} else if len(success) == 0 { // Everything OK
+	} else if status, err := dhcpv6.ParseOptStatusCode(success[0].ToBytes()); err != nil || status.StatusCode != dhcpIana.StatusSuccess {
+		t.Fatalf("Did not get a (implicit or explicit) success status code: %v", success)
+	}
+
+	var iapd *dhcpv6.OptIAPD
+	{
+		// Check for IA_PD
+		iapds := result.(*dhcpv6.Message).Options.IAPD()
+		if len(iapds) != 1 {
+			t.Fatal("Malformed response, expected exactly 1 IAPD")
+		}
+		iapd = iapds[0]
+	}
+	if iapd.IaId != reqIAID {
+		t.Fatalf("IAID doesn't match: request %x, response: %x", iapd.IaId, reqIAID)
+	}
+
+	// Check the status code
+	if status := result.(*dhcpv6.Message).Options.Status(); status != nil && status.StatusCode != dhcpIana.StatusSuccess {
+		t.Fatalf("Did not get a (implicit or explicit) success status code: %v", success)
+	}
+
+	t.Logf("%#v", iapd)
+	// Check IAPrefix within IAPD
+	if len(iapd.Options.Prefixes()) != 1 {
+		t.Fatalf("Response did not contain exactly one prefix in the IA_PD option (found %s)",
+			iapd.Options.Prefixes())
+	}
+}