Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Packet data is broken on DHCP OFFER #47

Closed
jdevelop opened this issue Sep 9, 2019 · 2 comments
Closed

Packet data is broken on DHCP OFFER #47

jdevelop opened this issue Sep 9, 2019 · 2 comments

Comments

@jdevelop
Copy link

jdevelop commented Sep 9, 2019

I have a small embedded DHCP server based on your example. While it works fine with dhclient - it fails on systemd-networkd, e.g - the lease can't be obtained. After some tinkering/debugging I realized that the server ID is not set properly. Systemd reports:

DHCP CLIENT (0xfb50f277): DISCOVER
Failed to parse server identifier, ignoring: Invalid argument
DHCP CLIENT (0xfb50f277): received lease lacks address, server address or lease lifetime, ignoring

I was able to isolate the test case with the sample request/response ( also on playground https://play.golang.org/p/54GThoEJZ9K )

It looks like the option bounds are not set properly in the response.

=== RUN   TestOptionValues
[255 255 255 0]
[10 11 12 13]
--- PASS: TestOptionValues (0.00s)
=== RUN   TestOutgoingPacketParsing
--- FAIL: TestOutgoingPacketParsing (0.00s)
    dhcpsrv_test.go:60: Expected server IP [10 101 1 1], actual IP [0 0 0 0 0 0 0 0 0 0 255 255 10 101 1 1]

package main

import (
	"fmt"
	"net"
	"reflect"
	"testing"
	"time"

	dhcp "github.com/krolaw/dhcp4"
)

var (
	incomingData = []byte{1, 1, 6, 0, 244, 101, 245, 221, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 111, 115, 116, 80, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 130, 83, 99, 53, 1, 1, 61, 19, 255, 254, 220, 71, 216, 0, 2, 0, 0, 171, 17, 138, 52, 26, 55, 116, 247, 151, 249, 55, 8, 1, 3, 12, 15, 6, 33, 121, 42, 57, 2, 2, 64, 12, 6, 100, 101, 118, 98, 111, 120, 255}
	outgoingData = []byte{2, 1, 6, 0, 244, 101, 245, 221, 0, 0, 0, 0, 0, 0, 0, 0, 10, 101, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 72, 111, 115, 116, 80, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 130, 83, 99, 53, 1, 2, 54, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 10, 101, 1, 1, 51, 4, 0, 0, 28, 32, 1, 4, 255, 255, 255, 0, 255}
)

func TestOptionValues(t *testing.T) {
	test := []struct {
		opt      dhcp.OptionCode
		expected []byte
	}{
		{
			opt:      dhcp.OptionSubnetMask,
			expected: []byte{255, 255, 255, 0},
		},
		{
			opt:      dhcp.OptionServerIdentifier,
			expected: []byte{10, 11, 12, 13},
		},
	}
	p := dhcp.NewPacket(2)
	for _, opt := range test {
		p.AddOption(opt.opt, opt.expected)
	}
	p.PadToMinSize()
	newOpts := p.ParseOptions()
	for _, opt := range test {
		if target := newOpts[opt.opt]; !reflect.DeepEqual(target, opt.expected) {
			t.Fatalf("Option %d mismatch: expected %+v, actual %+v", opt.opt, opt.expected, target)
		} else {
			fmt.Printf("%+v\n", target)
		}
	}
}

func TestOutgoingPacketParsing(t *testing.T) {
	p := dhcp.Packet(incomingData)
	expected := []byte{10, 101, 1, 1}
	rteOpts := dhcp.Options{
		dhcp.OptionSubnetMask: []byte{255, 255, 255, 0},
	}
	opts := rteOpts.SelectOrderOrAll(p.ParseOptions()[dhcp.OptionParameterRequestList])
	newP := dhcp.ReplyPacket(p, dhcp.Offer, net.IPv4(10, 101, 1, 1), net.IPv4(10, 101, 1, 2), 2*time.Hour, opts)
	if !reflect.DeepEqual([]byte(newP), outgoingData) {
		t.Fatalf("Packet data mismatch: \n%+v\n%+v\n", newP, outgoingData)
	}
	serverID := newP.ParseOptions()[dhcp.OptionServerIdentifier]
	if !reflect.DeepEqual(serverID, expected) {
		t.Fatalf("Expected server IP %+v, actual IP %+v", expected, serverID)
	}
}
@jdevelop jdevelop changed the title Packet data is broken on reply Packet data is broken on DHCP OFFER Sep 9, 2019
@krolaw
Copy link
Owner

krolaw commented Sep 9, 2019

net.IPv4() creates a 16byte representation of IPv4, which isn't what you want. See https://golang.org/pkg/net/#IPv4
Use: net.IP{10, 101, 1, 1} instead.
See: https://play.golang.org/p/XeVW5dnQugD

@jdevelop
Copy link
Author

jdevelop commented Sep 9, 2019

Oh, right! with To4() everything works as expected. Thanks a lot! :)

@jdevelop jdevelop closed this as completed Sep 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants