New Upstream Release - golang-github-d2g-dhcp4

Ready changes

Summary

Merged new upstream version: 0.0~git20170904.a1d1b6c (was: 0.0~git20150413).

Resulting package

Built on 2022-12-14T03:56 (took 10m24s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-d2g-dhcp4-dev

Lintian Result

Diff

diff --git a/debian/changelog b/debian/changelog
index c5a3bbd..551c35d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-d2g-dhcp4 (0.0~git20170904.a1d1b6c-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 14 Dec 2022 03:46:58 -0000
+
 golang-github-d2g-dhcp4 (0.0~git20150413-4) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/helpers_test.go b/helpers_test.go
new file mode 100644
index 0000000..6b364e8
--- /dev/null
+++ b/helpers_test.go
@@ -0,0 +1,397 @@
+package dhcp4
+
+import (
+	"bytes"
+	"net"
+	"reflect"
+	"sort"
+	"testing"
+	"time"
+)
+
+// Verify that all options are returned by Options.SelectOrderOrAll if
+// the input order value is nil.
+func TestSelectOrderOrAllNil(t *testing.T) {
+	assertOptionsSlices(t, 0, "nil order", allOptionsSlice, optMap.SelectOrderOrAll(nil))
+}
+
+// Verify that all options are returned by Options.SelectOrderOrAll if
+// the input order value is not nil over several tests.
+func TestSelectOrderOrAllNotNil(t *testing.T) {
+	for i, tt := range selectOrderTests {
+		assertOptionsSlices(t, i, tt.description, tt.result, optMap.SelectOrderOrAll(tt.order))
+	}
+}
+
+// Verify that no options are returned by Options.SelectOrder if
+// the input order value is nil.
+func TestSelectOrderNil(t *testing.T) {
+	assertOptionsSlices(t, 0, "nil order", nil, optMap.SelectOrder(nil))
+}
+
+// Verify that all options are returned by Options.SelectOrder if
+// the input order value is not nil over several tests.
+func TestSelectOrderNotNil(t *testing.T) {
+	for i, tt := range selectOrderTests {
+		assertOptionsSlices(t, i, tt.description, tt.result, optMap.SelectOrder(tt.order))
+	}
+}
+
+func TestIPRange(t *testing.T) {
+	var tests = []struct {
+		start  net.IP
+		stop   net.IP
+		result int
+	}{
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 1, 1),
+			result: 1,
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 1, 254),
+			result: 254,
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 10, 1),
+			result: 2305,
+		},
+		{
+			start:  net.IPv4(172, 16, 1, 1),
+			stop:   net.IPv4(192, 168, 1, 1),
+			result: 345505793,
+		},
+	}
+
+	for _, tt := range tests {
+		if result := IPRange(tt.start, tt.stop); result != tt.result {
+			t.Fatalf("IPRange(%s, %s), unexpected result: %v != %v",
+				tt.start, tt.stop, result, tt.result)
+		}
+	}
+}
+
+func TestIPAdd(t *testing.T) {
+	var tests = []struct {
+		start  net.IP
+		add    int
+		result net.IP
+	}{
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			add:    0,
+			result: net.IPv4(192, 168, 1, 1),
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			add:    253,
+			result: net.IPv4(192, 168, 1, 254),
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			add:    1024,
+			result: net.IPv4(192, 168, 5, 1),
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			add:    4096,
+			result: net.IPv4(192, 168, 17, 1),
+		},
+	}
+
+	for _, tt := range tests {
+		if result := IPAdd(tt.start, tt.add); !result.Equal(tt.result) {
+			t.Fatalf("IPAdd(%s, %d), unexpected result: %v != %v",
+				tt.start, tt.add, result, tt.result)
+		}
+	}
+}
+
+func TestIPLess(t *testing.T) {
+	var tests = []struct {
+		a      net.IP
+		b      net.IP
+		result bool
+	}{
+		{
+			a:      net.IPv4(192, 168, 1, 1),
+			b:      net.IPv4(192, 168, 1, 1),
+			result: false,
+		},
+		{
+			a:      net.IPv4(192, 168, 1, 1),
+			b:      net.IPv4(192, 168, 0, 1),
+			result: false,
+		},
+		{
+			a:      net.IPv4(192, 168, 0, 1),
+			b:      net.IPv4(192, 168, 1, 1),
+			result: true,
+		},
+		{
+			a:      net.IPv4(192, 168, 0, 1),
+			b:      net.IPv4(192, 168, 10, 1),
+			result: true,
+		},
+	}
+
+	for _, tt := range tests {
+		if result := IPLess(tt.a, tt.b); result != tt.result {
+			t.Fatalf("IPLess(%s, %s), unexpected result: %v != %v",
+				tt.a, tt.b, result, tt.result)
+		}
+	}
+}
+
+func TestIPInRange(t *testing.T) {
+	var tests = []struct {
+		start  net.IP
+		stop   net.IP
+		ip     net.IP
+		result bool
+	}{
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 2, 1),
+			ip:     net.IPv4(192, 168, 3, 1),
+			result: false,
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 10, 1),
+			ip:     net.IPv4(192, 168, 0, 1),
+			result: false,
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 10, 1),
+			ip:     net.IPv4(192, 168, 5, 1),
+			result: true,
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 3, 1),
+			ip:     net.IPv4(192, 168, 3, 0),
+			result: true,
+		},
+		{
+			start:  net.IPv4(192, 168, 1, 1),
+			stop:   net.IPv4(192, 168, 1, 1),
+			ip:     net.IPv4(192, 168, 1, 1),
+			result: true,
+		},
+	}
+
+	for _, tt := range tests {
+		if result := IPInRange(tt.start, tt.stop, tt.ip); result != tt.result {
+			t.Fatalf("IPInRange(%s, %s, %s), unexpected result: %v != %v",
+				tt.start, tt.stop, tt.ip, result, tt.result)
+		}
+	}
+}
+
+func TestOptionsLeaseTime(t *testing.T) {
+	var tests = []struct {
+		duration time.Duration
+		result   []byte
+	}{
+		{
+			duration: 0 * time.Second,
+			result:   []byte{0, 0, 0, 0},
+		},
+		{
+			duration: 2 * time.Second,
+			result:   []byte{0, 0, 0, 2},
+		},
+		{
+			duration: 60 * time.Second,
+			result:   []byte{0, 0, 0, 60},
+		},
+		{
+			duration: 6 * time.Hour,
+			result:   []byte{0, 0, 84, 96},
+		},
+		{
+			duration: 24 * time.Hour,
+			result:   []byte{0, 1, 81, 128},
+		},
+		{
+			duration: 365 * 24 * time.Hour,
+			result:   []byte{1, 225, 51, 128},
+		},
+	}
+
+	for _, tt := range tests {
+		if result := OptionsLeaseTime(tt.duration); !bytes.Equal(result, tt.result) {
+			t.Fatalf("OptionsLeaseTime(%s), unexpected result: %v != %v",
+				tt.duration, result, tt.result)
+		}
+	}
+}
+
+func TestJoinIPs(t *testing.T) {
+	var tests = []struct {
+		ips    []net.IP
+		result []byte
+	}{
+		{
+			ips:    []net.IP{net.IPv4(10, 0, 0, 1)},
+			result: []byte{10, 0, 0, 1},
+		},
+		{
+			ips:    []net.IP{net.IPv4(192, 168, 1, 1), net.IPv4(192, 168, 2, 1)},
+			result: []byte{192, 168, 1, 1, 192, 168, 2, 1},
+		},
+		{
+			ips:    []net.IP{net.IPv4(10, 0, 0, 1), net.IPv4(255, 255, 255, 254)},
+			result: []byte{10, 0, 0, 1, 255, 255, 255, 254},
+		},
+		{
+			ips:    []net.IP{net.IPv4(8, 8, 8, 8), net.IPv4(8, 8, 4, 4), net.IPv4(192, 168, 1, 1)},
+			result: []byte{8, 8, 8, 8, 8, 8, 4, 4, 192, 168, 1, 1},
+		},
+	}
+
+	for _, tt := range tests {
+		if result := JoinIPs(tt.ips); !bytes.Equal(result, tt.result) {
+			t.Fatalf("JoinIPs(%s), unexpected result: %v != %v",
+				tt.ips, result, tt.result)
+		}
+	}
+}
+
+// byOptionCode implements sort.Interface for []Option.
+type byOptionCode []Option
+
+func (b byOptionCode) Len() int               { return len(b) }
+func (b byOptionCode) Less(i int, j int) bool { return b[i].Code < b[j].Code }
+func (b byOptionCode) Swap(i int, j int)      { b[i], b[j] = b[j], b[i] }
+
+// assertOptionsSlices is a test helper which verifies that two options slices
+// are identical.  Several parameters are passed for easy identification of
+// failing tests.
+func assertOptionsSlices(t *testing.T, i int, description string, want []Option, got []Option) {
+	// Verify slices are same length
+	if want, got := len(want), len(got); want != got {
+		t.Fatalf("%02d: test %q, mismatched length: %d != %d",
+			i, description, want, got)
+	}
+
+	// Sort slices
+	sort.Sort(byOptionCode(want))
+	sort.Sort(byOptionCode(got))
+
+	// Verify slices are identical
+	if len(want) > 0 && len(got) > 0 && !reflect.DeepEqual(want, got) {
+		t.Fatalf("%02d: test %q, unexpected options: %v != %v",
+			i, description, want, got)
+	}
+}
+
+// optMap is an Options map which contains a number of option
+// codes and values, used for testing.
+var optMap = Options{
+	OptionSubnetMask:       []byte{255, 255, 255, 0},
+	OptionRouter:           []byte{192, 168, 1, 1},
+	OptionDomainNameServer: []byte{192, 168, 1, 2},
+	OptionTimeServer:       []byte{192, 168, 1, 3},
+	OptionLogServer:        []byte{192, 168, 1, 4},
+}
+
+// allOptionsSlice is a []Option derived from optMap.  It is used
+// for some tests.
+var allOptionsSlice = []Option{
+	Option{
+		Code:  OptionSubnetMask,
+		Value: optMap[OptionSubnetMask],
+	},
+	Option{
+		Code:  OptionRouter,
+		Value: optMap[OptionRouter],
+	},
+	Option{
+		Code:  OptionDomainNameServer,
+		Value: optMap[OptionDomainNameServer],
+	},
+	Option{
+		Code:  OptionTimeServer,
+		Value: optMap[OptionTimeServer],
+	},
+	Option{
+		Code:  OptionLogServer,
+		Value: optMap[OptionLogServer],
+	},
+}
+
+// selectOrderTests is a set of tests used for Options.SelectOrder
+// and Options.SelectOrderOrAll methods.
+var selectOrderTests = []struct {
+	description string
+	order       []byte
+	result      []Option
+}{
+	{
+		description: "subnet mask only",
+		order: []byte{
+			byte(OptionSubnetMask),
+		},
+		result: []Option{
+			Option{
+				Code:  OptionSubnetMask,
+				Value: optMap[OptionSubnetMask],
+			},
+		},
+	},
+	{
+		description: "subnet mask and time server",
+		order: []byte{
+			byte(OptionSubnetMask),
+			byte(OptionTimeServer),
+		},
+		result: []Option{
+			Option{
+				Code:  OptionSubnetMask,
+				Value: optMap[OptionSubnetMask],
+			},
+			Option{
+				Code:  OptionTimeServer,
+				Value: optMap[OptionTimeServer],
+			},
+		},
+	},
+	{
+		description: "domain name server, time server, router",
+		order: []byte{
+			byte(OptionDomainNameServer),
+			byte(OptionTimeServer),
+			byte(OptionRouter),
+		},
+		result: []Option{
+			Option{
+				Code:  OptionDomainNameServer,
+				Value: optMap[OptionDomainNameServer],
+			},
+			Option{
+				Code:  OptionTimeServer,
+				Value: optMap[OptionTimeServer],
+			},
+			Option{
+				Code:  OptionRouter,
+				Value: optMap[OptionRouter],
+			},
+		},
+	},
+	{
+		description: "all options in order",
+		order: []byte{
+			byte(OptionSubnetMask),
+			byte(OptionRouter),
+			byte(OptionDomainNameServer),
+			byte(OptionTimeServer),
+			byte(OptionLogServer),
+		},
+		result: allOptionsSlice,
+	},
+}
diff --git a/option.go b/option.go
index f3239e1..fbf86e6 100644
--- a/option.go
+++ b/option.go
@@ -11,7 +11,7 @@ type Option struct {
 type Options map[OptionCode][]byte
 
 // SelectOrderOrAll has same functionality as SelectOrder, except if the order
-// param is nil, whereby all options are added (in arbitary order).
+// param is nil, whereby all options are added (in arbitrary order).
 func (o Options) SelectOrderOrAll(order []byte) []Option {
 	if order == nil {
 		opts := make([]Option, 0, len(o))
diff --git a/packet.go b/packet.go
index 5e547c8..25d69fb 100644
--- a/packet.go
+++ b/packet.go
@@ -127,10 +127,11 @@ func ReplyPacket(req Packet, mt MessageType, serverId, yIAddr net.IP, leaseDurat
 	p.SetYIAddr(yIAddr)
 	p.SetGIAddr(req.GIAddr())
 	p.SetCHAddr(req.CHAddr())
-	p.SetSecs(req.Secs())
 	p.AddOption(OptionDHCPMessageType, []byte{byte(mt)})
 	p.AddOption(OptionServerIdentifier, []byte(serverId))
-	p.AddOption(OptionIPAddressLeaseTime, OptionsLeaseTime(leaseDuration))
+	if leaseDuration > 0 {
+		p.AddOption(OptionIPAddressLeaseTime, OptionsLeaseTime(leaseDuration))
+	}
 	for _, o := range options {
 		p.AddOption(o.Code, o.Value)
 	}
diff --git a/packet_test.go b/packet_test.go
new file mode 100644
index 0000000..b0839cb
--- /dev/null
+++ b/packet_test.go
@@ -0,0 +1,433 @@
+package dhcp4
+
+import (
+	"bytes"
+	"net"
+	"testing"
+	"time"
+)
+
+func TestNewPacket(t *testing.T) {
+	var tests = []struct {
+		description string
+		opCode      OpCode
+	}{
+		{
+			description: "boot request",
+			opCode:      BootRequest,
+		},
+		{
+			description: "boot reply",
+			opCode:      BootReply,
+		},
+		{
+			description: "unknown opcode",
+			opCode:      3,
+		},
+	}
+
+	for i, tt := range tests {
+		if want, got := newPacket(tt.opCode), NewPacket(tt.opCode); !bytes.Equal(want, got) {
+			t.Fatalf("%02d: NewPacket(%d), test %q, unexpected result: %v != %v",
+				i, tt.opCode, tt.description, want, got)
+		}
+	}
+}
+
+func TestPacketAddOption(t *testing.T) {
+	for i, tt := range optionsTests {
+		// Set up new packet, apply options from slice
+		p := NewPacket(BootRequest)
+		for _, o := range tt.options {
+			p.AddOption(o.Code, o.Value)
+		}
+
+		// Empty options should result in no changes
+		if tt.options == nil || len(tt.options) == 0 {
+			if !bytes.Equal(p, NewPacket(BootRequest)) {
+				t.Fatalf("%02d: test %q, no options applied, but packet contained extra data",
+					i, tt.description)
+			}
+		}
+
+		// Check that each option was properly applied, in order
+
+		// Track length of previous option bytes
+		var offset int
+		for ii, o := range tt.options {
+			// Options start at byte 240 on an empty packet, adding
+			// offset as loops continue
+			start := offset + 240
+			end := start + 2 + len(o.Value)
+
+			// Options bytes: [option] [length] [value...]
+			check := append([]byte{byte(o.Code)}, byte(len(o.Value)))
+			check = append(check, o.Value...)
+
+			// Verify option correctly applied
+			if want, got := p[start:end], check; !bytes.Equal(want, got) {
+				t.Fatalf("%02d: test %q, unexpected option bytes: %v != %v",
+					ii, tt.description, want, got)
+			}
+
+			// Track offset for next loop
+			offset = offset + len(check)
+		}
+
+		// Ensure last byte is always End
+		if p[len(p)-1] != byte(End) {
+			t.Fatalf("%02d: test %q, missing End byte", i, tt.description)
+		}
+	}
+}
+
+func TestPacketParseOptions(t *testing.T) {
+	for i, tt := range optionsTests {
+		// Set up new packet, apply options from slice
+		p := NewPacket(BootRequest)
+		for _, o := range tt.options {
+			p.AddOption(o.Code, o.Value)
+		}
+
+		// Parse options, verify all options are present
+		options := p.ParseOptions()
+		for _, o := range tt.options {
+			var found bool
+
+			// Search for expected option in result map
+			for k, v := range options {
+				if o.Code == k && bytes.Equal(o.Value, v) {
+					found = true
+					break
+				}
+			}
+
+			// Pad option is not parsed, but check all others
+			if !found && o.Code != Pad {
+				t.Fatalf("%02d: test %q, did not find option: %v",
+					i, tt.description, o)
+			}
+		}
+	}
+}
+
+func TestPacketStripOptions(t *testing.T) {
+	for i, tt := range optionsTests {
+		// Set up new packet, apply options from slice
+		p := NewPacket(BootRequest)
+		for _, o := range tt.options {
+			p.AddOption(o.Code, o.Value)
+		}
+
+		// Strip all options, verify options are gone
+		p.StripOptions()
+		if !bytes.Equal(p, NewPacket(BootRequest)) {
+			t.Fatalf("%02d: test %q, options stripped, but packet contained extra data",
+				i, tt.description)
+		}
+	}
+}
+
+func TestPacketPadToMinSize(t *testing.T) {
+	var tests = []struct {
+		before int
+		after  int
+	}{
+		{
+			before: 0,
+			after:  272,
+		},
+		{
+			before: 100,
+			after:  272,
+		},
+		{
+			before: 300,
+			after:  300,
+		},
+		{
+			before: 1024,
+			after:  1024,
+		},
+	}
+
+	for i, tt := range tests {
+		p := make(Packet, tt.before)
+		p.PadToMinSize()
+
+		if want, got := tt.after, len(p); want != got {
+			t.Fatalf("%02d: before %d, unexpected padded length: %d != %d",
+				i, tt.before, want, got)
+		}
+	}
+}
+
+func TestRequestPacket(t *testing.T) {
+	var tests = []struct {
+		description string
+		mt          MessageType
+		chAddr      net.HardwareAddr
+		cIAddr      net.IP
+		xId         []byte
+		broadcast   bool
+		options     []Option
+	}{
+		{
+			description: "discover request",
+			mt:          Discover,
+			chAddr:      net.HardwareAddr{1, 35, 69, 103, 117, 171}, // 01:23:45:67:89:ab
+			cIAddr:      net.IP([]byte{192, 168, 1, 1}),
+			xId:         []byte{0, 1, 2, 3},
+			broadcast:   true,
+			options:     nil,
+		},
+		{
+			description: "request request",
+			mt:          Request,
+			chAddr:      net.HardwareAddr{222, 173, 190, 239, 222, 173}, // de:ad:be:ef:de:ad
+			xId:         []byte{4, 5, 6, 7},
+			broadcast:   false,
+			options:     oneOptionSlice,
+		},
+		{
+			description: "decline request",
+			mt:          Decline,
+			chAddr:      net.HardwareAddr{255, 255, 255, 255, 255, 255}, // ff:ff:ff:ff:ff:ff
+			xId:         []byte{8, 9, 10, 11},
+			broadcast:   true,
+			options:     twoOptionsSlice,
+		},
+	}
+
+	for i, tt := range tests {
+		// Compare our basic test implementation's packet against the library's
+		// implementation
+		want := newRequestPacket(tt.mt, tt.chAddr, tt.cIAddr, tt.xId, tt.broadcast, tt.options)
+		got := RequestPacket(tt.mt, tt.chAddr, tt.cIAddr, tt.xId, tt.broadcast, tt.options)
+
+		if !bytes.Equal(want, got) {
+			t.Fatalf("%02d: RequestPacket(), test %q, unexpected result: %v != %v",
+				i, tt.description, want, got)
+		}
+	}
+}
+
+func TestReplyPacket(t *testing.T) {
+	var tests = []struct {
+		description   string
+		mt            MessageType
+		serverId      net.IP
+		yIAddr        net.IP
+		leaseDuration time.Duration
+		options       []Option
+	}{
+		{
+			description:   "offer reply",
+			mt:            Offer,
+			serverId:      []byte{192, 168, 1, 1},
+			yIAddr:        []byte{192, 168, 1, 1},
+			leaseDuration: 60 * time.Second,
+			options:       nil,
+		},
+		{
+			description:   "ACK reply",
+			mt:            ACK,
+			serverId:      []byte{10, 0, 0, 1},
+			yIAddr:        []byte{192, 168, 1, 1},
+			leaseDuration: 10 * time.Second,
+			options:       oneOptionSlice,
+		},
+		{
+			description:   "NAK reply",
+			mt:            NAK,
+			serverId:      []byte{8, 8, 8, 8},
+			yIAddr:        []byte{8, 8, 4, 4},
+			leaseDuration: 3600 * time.Second,
+			options:       twoOptionsSlice,
+		},
+	}
+
+	for i, tt := range tests {
+		// Compare our basic test implementation's packet against the library's
+		// implementation
+		req := NewPacket(BootRequest)
+		want := newReplyPacket(req, tt.mt, tt.serverId, tt.yIAddr, tt.leaseDuration, tt.options)
+		got := ReplyPacket(req, tt.mt, tt.serverId, tt.yIAddr, tt.leaseDuration, tt.options)
+
+		if !bytes.Equal(want, got) {
+			t.Fatalf("%02d: ReplyPacket(), test %q, unexpected result: %v != %v",
+				i, tt.description, want, got)
+		}
+	}
+}
+
+// newPacket mimics the raw logic of NewPacket, and verifies that its
+// behavior does not change.
+func newPacket(opCode OpCode) Packet {
+	const ethernetHType = 1
+	var cookie = []byte{99, 130, 83, 99}
+
+	p := make(Packet, 241)
+	p[0] = byte(opCode)
+	p[1] = ethernetHType
+	copy(p[236:240], cookie)
+	p[240] = byte(End)
+
+	return p
+}
+
+// newRequestPacket mimics the raw logic of RequestPacket, and verifies that
+// its behavior does not change.
+func newRequestPacket(mt MessageType, chAddr net.HardwareAddr, cIAddr net.IP, xId []byte, broadcast bool, options []Option) Packet {
+	// Craft packet using our test method
+	p := newPacket(BootRequest)
+
+	// SetCHAddr
+	copy(p[28:44], chAddr)
+	p[2] = byte(len(chAddr))
+
+	// SetXId
+	copy(p[4:8], xId)
+
+	// SetCIAddr
+	if cIAddr != nil {
+		copy(net.IP(p[12:16]), cIAddr.To4())
+	}
+
+	// SetBroadcast
+	if broadcast {
+		p[10:12][0] ^= 128
+	}
+
+	// AddOption already tested, so no need to duplicate the logic
+	p.AddOption(OptionDHCPMessageType, []byte{byte(mt)})
+	for _, o := range options {
+		p.AddOption(o.Code, o.Value)
+	}
+
+	// PadToMinSize already tested
+	p.PadToMinSize()
+
+	return p
+}
+
+// newReplyPacket mimics the raw logic of ReplyPacket, and verifies that
+// its behavior does not change.
+func newReplyPacket(req Packet, mt MessageType, serverId, yIAddr net.IP, leaseDuration time.Duration, options []Option) Packet {
+	// Craft packet using our test method
+	p := newPacket(BootReply)
+
+	// SetXId
+	copy(p[4:8], req[4:8])
+
+	// SetFlags
+	copy(p[10:22], req[10:12])
+
+	// SetYIAddr
+	copy(p[16:20], yIAddr)
+
+	// SetGIAddr
+	copy(p[24:28], req[24:28])
+
+	// SetCHAddr
+	hLen := req[2]
+	if hLen > 16 {
+		hLen = 16
+	}
+	c := make([]byte, hLen)
+	copy(c, p[28:28+hLen])
+
+	copy(p[28:44], c)
+	p[2] = byte(len(c))
+
+	// SetSecs
+	copy(p[8:10], req[8:10])
+
+	// AddOption already tested, so no need to duplicate the logic
+	p.AddOption(OptionDHCPMessageType, []byte{byte(mt)})
+	p.AddOption(OptionServerIdentifier, []byte(serverId))
+	p.AddOption(OptionIPAddressLeaseTime, OptionsLeaseTime(leaseDuration))
+	for _, o := range options {
+		p.AddOption(o.Code, o.Value)
+	}
+
+	// PadToMinSize already tested
+	p.PadToMinSize()
+
+	return p
+}
+
+// oneOptionSlice is a test helper of []Option with a single
+// Option.
+var oneOptionSlice = []Option{
+	Option{
+		Code:  OptionSubnetMask,
+		Value: []byte{255, 255, 255, 0},
+	},
+}
+
+// twoOptionSlice is a test helper of []Option with two
+// Option values.
+var twoOptionsSlice = []Option{
+	Option{
+		Code:  OptionSubnetMask,
+		Value: []byte{255, 255, 255, 0},
+	},
+	Option{
+		Code:  OptionDomainNameServer,
+		Value: []byte{8, 8, 8, 8},
+	},
+}
+
+// optionsTests are tests used when applying and stripping Options
+// from Packets.
+var optionsTests = []struct {
+	description string
+	options     []Option
+}{
+	{
+		description: "nil options",
+		options:     nil,
+	},
+	{
+		description: "empty options",
+		options:     []Option{},
+	},
+	{
+		description: "padding option",
+		options: []Option{
+			Option{
+				Code: Pad,
+			},
+		},
+	},
+	{
+		description: "one option",
+		options:     oneOptionSlice,
+	},
+	{
+		description: "two options",
+		options:     twoOptionsSlice,
+	},
+	{
+		description: "four options",
+		options: []Option{
+			Option{
+				Code:  OptionSubnetMask,
+				Value: []byte{255, 255, 255, 0},
+			},
+			Option{
+				Code:  OptionDomainNameServer,
+				Value: []byte{8, 8, 8, 8},
+			},
+			Option{
+				Code:  OptionTimeServer,
+				Value: []byte{127, 0, 0, 1},
+			},
+			Option{
+				Code:  OptionMessage,
+				Value: []byte{'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'},
+			},
+		},
+	},
+}

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/d2g/dhcp4/helpers_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/d2g/dhcp4/packet_test.go

No differences were encountered in the control files

More details

Full run details