Repository: insomniacslk/dhcp

Issue:
Options which are not mandatory should not be enforced
According to RFC 2131/2132 "All other [options] MAY" be included in a DHCPOFFER. Thus the gateway is not mandatory and in fact I can configure a DHCP server which "just" hands out an IP address, no problem. However, the client returns no options at all and thus fails the request.  In addition, due to the fact that several tests look for an error to occur pass because the error returned by not having a router defined masks the actual to-be-tested behavior.

Hints from issue comments:
@pmazzini: > However, the client returns no options at all and thus fails the request.

Is this the `nclient4` or the `client4`? Do you have logs or code pointers to understand better the problem?@rschmied: Thanks for the response... See this diff https://github.com/insomniacslk/dhcp/compare/master...rschmied:dhcp:dev  So, it's the netboot option handling and not necessarily client specific. 

On IOS, this is a perfectly OK DHCP configuration:

```
ip dhcp pool my-pool
 network 10.0.18.0 255.255.255.0
!
```

As you can see, it does not define any default gateway (GIADDR). When using this DHCP client to acquire an address from the server then it will fail as no router in the options is considered an error. Which, in fact, it is not. No gateway needed to acquire an address.@rschmied: Any additional comments or insights on this? Have you looked at the diff? Should I create a PR from it. Let me know. Thanks!@pmazzini: Do you want to submit a PR?@rschmied: 🙏

Solution patch:
diff --git a/netboot/netconf.go b/netboot/netconf.go
index 6370b5d..607b7fe 100644
--- a/netboot/netconf.go
+++ b/netboot/netconf.go
@@ -122,12 +122,8 @@ func GetNetConfFromPacketv4(d *dhcpv4.DHCPv4) (*NetConf, error) {
 		netconf.DNSSearchList = dnsSearchList.Labels
 	}
 
-	// get default gateway
-	routersList := d.Router()
-	if len(routersList) == 0 {
-		return nil, errors.New("no routers specified in the corresponding option")
-	}
-	netconf.Routers = routersList
+	// get default gateway (empty is perfectly fine)
+	netconf.Routers = d.Router()
 
 	// get NTP servers
 	netconf.NTPServers = d.NTPServers()
@@ -177,7 +173,6 @@ func IfUp(ifname string, timeout time.Duration) (_ *net.Interface, err error) {
 	}
 
 	return nil, fmt.Errorf("timed out while waiting for %s to come up", ifname)
-
 }
 
 // ConfigureInterface configures a network interface with the configuration held by a



Test patch:
diff --git a/netboot/netconf_test.go b/netboot/netconf_test.go
index e2541cd..85ad602 100644
--- a/netboot/netconf_test.go
+++ b/netboot/netconf_test.go
@@ -36,7 +36,7 @@ func TestGetNetConfFromPacketv6Invalid(t *testing.T) {
 
 func TestGetNetConfFromPacketv6NoSearchList(t *testing.T) {
 	addrs := []dhcpv6.OptIAAddress{
-		dhcpv6.OptIAAddress{
+		{
 			IPv6Addr:          net.ParseIP("::1"),
 			PreferredLifetime: 3600 * time.Second,
 			ValidLifetime:     5200 * time.Second,
@@ -52,7 +52,7 @@ func TestGetNetConfFromPacketv6NoSearchList(t *testing.T) {
 
 func TestGetNetConfFromPacketv6(t *testing.T) {
 	addrs := []dhcpv6.OptIAAddress{
-		dhcpv6.OptIAAddress{
+		{
 			IPv6Addr:          net.ParseIP("::1"),
 			PreferredLifetime: 3600 * time.Second,
 			ValidLifetime:     5200 * time.Second,
@@ -101,74 +101,6 @@ func TestGetNetConfFromPacketv4NullMask(t *testing.T) {
 	require.Error(t, err)
 }
 
-func TestGetNetConfFromPacketv4NoLeaseTime(t *testing.T) {
-	d, _ := dhcpv4.New(
-		dhcpv4.WithNetmask(net.IPv4Mask(255, 255, 255, 0)),
-		dhcpv4.WithYourIP(net.ParseIP("10.0.0.1")),
-	)
-	_, err := GetNetConfFromPacketv4(d)
-	require.Error(t, err)
-}
-
-func TestGetNetConfFromPacketv4EmptyDNSList(t *testing.T) {
-	d, _ := dhcpv4.New(
-		dhcpv4.WithNetmask(net.IPv4Mask(255, 255, 255, 0)),
-		dhcpv4.WithLeaseTime(uint32(0)),
-		dhcpv4.WithDNS(),
-		dhcpv4.WithYourIP(net.ParseIP("10.0.0.1")),
-	)
-	_, err := GetNetConfFromPacketv4(d)
-	require.Error(t, err)
-}
-
-func TestGetNetConfFromPacketv4NoSearchList(t *testing.T) {
-	d, _ := dhcpv4.New(
-		dhcpv4.WithNetmask(net.IPv4Mask(255, 255, 255, 0)),
-		dhcpv4.WithLeaseTime(uint32(0)),
-		dhcpv4.WithDNS(net.ParseIP("10.10.0.1"), net.ParseIP("10.10.0.2")),
-		dhcpv4.WithYourIP(net.ParseIP("10.0.0.1")),
-	)
-	_, err := GetNetConfFromPacketv4(d)
-	require.Error(t, err)
-}
-
-func TestGetNetConfFromPacketv4EmptySearchList(t *testing.T) {
-	d, _ := dhcpv4.New(
-		dhcpv4.WithNetmask(net.IPv4Mask(255, 255, 255, 0)),
-		dhcpv4.WithLeaseTime(uint32(0)),
-		dhcpv4.WithDNS(net.ParseIP("10.10.0.1"), net.ParseIP("10.10.0.2")),
-		dhcpv4.WithDomainSearchList(),
-		dhcpv4.WithYourIP(net.ParseIP("10.0.0.1")),
-	)
-	_, err := GetNetConfFromPacketv4(d)
-	require.Error(t, err)
-}
-
-func TestGetNetConfFromPacketv4NoRouter(t *testing.T) {
-	d, _ := dhcpv4.New(
-		dhcpv4.WithNetmask(net.IPv4Mask(255, 255, 255, 0)),
-		dhcpv4.WithLeaseTime(uint32(0)),
-		dhcpv4.WithDNS(net.ParseIP("10.10.0.1"), net.ParseIP("10.10.0.2")),
-		dhcpv4.WithDomainSearchList("slackware.it", "dhcp.slackware.it"),
-		dhcpv4.WithYourIP(net.ParseIP("10.0.0.1")),
-	)
-	_, err := GetNetConfFromPacketv4(d)
-	require.Error(t, err)
-}
-
-func TestGetNetConfFromPacketv4EmptyRouter(t *testing.T) {
-	d, _ := dhcpv4.New(
-		dhcpv4.WithNetmask(net.IPv4Mask(255, 255, 255, 0)),
-		dhcpv4.WithLeaseTime(uint32(0)),
-		dhcpv4.WithDNS(net.ParseIP("10.10.0.1"), net.ParseIP("10.10.0.2")),
-		dhcpv4.WithDomainSearchList("slackware.it", "dhcp.slackware.it"),
-		dhcpv4.WithRouter(),
-		dhcpv4.WithYourIP(net.ParseIP("10.0.0.1")),
-	)
-	_, err := GetNetConfFromPacketv4(d)
-	require.Error(t, err)
-}
-
 func TestGetNetConfFromPacketv4(t *testing.T) {
 	d, _ := dhcpv4.New(
 		dhcpv4.WithNetmask(net.IPv4Mask(255, 255, 255, 0)),