Selaa lähdekoodia

plugins: Decapsulate DHCPv6 relayed requests

The `request` argument to handlers passes the full DHCPv6 message as
received by the server. Notably, relayed messages will stay encapsulated
in each other. These two plugins (server_id and file) are accessing the
options of the outermost message, causing incorrect behavior when
handling relayed messages.

Signed-off-by: Anatole Denis <anatole@unverle.fr>
Anatole Denis 6 vuotta sitten
vanhempi
sitoutus
b566efa666
3 muutettua tiedostoa jossa 54 lisäystä ja 9 poistoa
  1. 7 1
      plugins/file/plugin.go
  2. 16 8
      plugins/server_id/plugin.go
  3. 31 0
      plugins/server_id/plugin_test.go

+ 7 - 1
plugins/file/plugin.go

@@ -149,7 +149,13 @@ func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
 			net.ParseIP("2001:4860:4860::4444"),
 		},
 	})
-	if oro := req.GetOption(dhcpv6.OptionORO); len(oro) > 0 {
+
+	decap, err := req.GetInnerMessage()
+	if err != nil {
+		log.Errorf("Could not decapsulate: %v", err)
+		return nil, true
+	}
+	if oro := decap.GetOption(dhcpv6.OptionORO); len(oro) > 0 {
 		for _, code := range oro[0].(*dhcpv6.OptRequestedOption).RequestedOptions() {
 			if code == dhcpv6.OptionBootfileURL {
 				// bootfile URL is requested

+ 16 - 8
plugins/server_id/plugin.go

@@ -31,12 +31,20 @@ func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
 		log.Fatal("BUG: Plugin is running uninitialized!")
 		return resp, false
 	}
-	if opt := req.GetOneOption(dhcpv6.OptionServerID); opt != nil {
+
+	msg, err := req.GetInnerMessage()
+	if err != nil {
+		// BUG: this should already have failed in the main handler. Abort
+		log.Error(err)
+		return nil, true
+	}
+
+	if opt := msg.GetOneOption(dhcpv6.OptionServerID); opt != nil {
 		// RFC8415 §16.{2,5,7}
 		// These message types MUST be discarded if they contain *any* ServerID option
-		if req.Type() == dhcpv6.MessageTypeSolicit ||
-			req.Type() == dhcpv6.MessageTypeConfirm ||
-			req.Type() == dhcpv6.MessageTypeRebind {
+		if msg.Type() == dhcpv6.MessageTypeSolicit ||
+			msg.Type() == dhcpv6.MessageTypeConfirm ||
+			msg.Type() == dhcpv6.MessageTypeRebind {
 			return nil, true
 		}
 
@@ -49,10 +57,10 @@ func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
 	} else {
 		// RFC8415 §16.{6,8,10,11}
 		// These message types MUST be discarded if they *don't* contain a ServerID option
-		if req.Type() == dhcpv6.MessageTypeRequest ||
-			req.Type() == dhcpv6.MessageTypeRenew ||
-			req.Type() == dhcpv6.MessageTypeDecline ||
-			req.Type() == dhcpv6.MessageTypeRelease {
+		if msg.Type() == dhcpv6.MessageTypeRequest ||
+			msg.Type() == dhcpv6.MessageTypeRenew ||
+			msg.Type() == dhcpv6.MessageTypeDecline ||
+			msg.Type() == dhcpv6.MessageTypeRelease {
 			return nil, true
 		}
 	}

+ 31 - 0
plugins/server_id/plugin_test.go

@@ -1,6 +1,7 @@
 package serverid
 
 import (
+	"net"
 	"testing"
 
 	"github.com/insomniacslk/dhcp/dhcpv6"
@@ -92,3 +93,33 @@ func TestAddServerIDV6(t *testing.T) {
 		}
 	}
 }
+
+func TestRejectInnerMessageServerID(t *testing.T) {
+	req, err := dhcpv6.NewMessage()
+	if err != nil {
+		t.Fatal(err)
+	}
+	v6ServerID = makeTestDUID("0000000000000000")
+
+	req.MessageType = dhcpv6.MessageTypeSolicit
+	dhcpv6.WithClientID(*makeTestDUID("1000000000000000"))(req)
+	dhcpv6.WithServerID(*makeTestDUID("0000000000000000"))(req)
+
+	stub, err := dhcpv6.NewAdvertiseFromSolicit(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	relayedRequest, err := dhcpv6.EncapsulateRelay(req, dhcpv6.MessageTypeRelayForward, net.IPv6loopback, net.IPv6loopback)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	resp, stop := Handler6(relayedRequest, stub)
+	if resp != nil {
+		t.Error("server_id is sending a response message to a relayed solicit with a ServerID")
+	}
+	if !stop {
+		t.Error("server_id did not interrupt processing on a relayed solicit with a ServerID")
+	}
+}