Forráskód Böngészése

server4: Fix peer address logic (#53)

The RFC has this to say about how to select the address to respond to:

>  If the 'giaddr' field in a DHCP message from a client is non-zero,
>  the server sends any return messages to the 'DHCP server' port on the
>  BOOTP relay agent whose address appears in 'giaddr'. If the 'giaddr'
>  field is zero and the 'ciaddr' field is nonzero, then the server
>  unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
>  If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
>  set, then the server broadcasts DHCPOFFER and DHCPACK messages to
>  0xffffffff. If the broadcast bit is not set and 'giaddr' is zero and
>  'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
>  messages to the client's hardware address and 'yiaddr' address.  In
>  all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
>  messages to 0xffffffff.

We can implement almost all of this. Unfortunately the bit about non-broadcast
responses ("If the broadcast bit is not set and 'giaddr' is zero and
'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
messages to the client's hardware address and 'yiaddr' address")
is impossible to implement with the current API offered by go and the
dhcp library (it requires raw sockets and a way to pass L2 information
in the peer address).

The RFC also says this:

> If unicasting is not possible, the message MAY be sent as an IP
> broadcast using an IP broadcast address (preferably 0xffffffff) as the
> IP destination address and the link-layer broadcast address as the
> link-layer destination address.

Which we'll do for the time being

Signed-off-by: Anatole Denis <anatole@unverle.fr>
Anatole Denis 6 éve
szülő
commit
406e854316
1 módosított fájl, 19 hozzáadás és 4 törlés
  1. 19 4
      coredhcp.go

+ 19 - 4
coredhcp.go

@@ -162,7 +162,7 @@ func (s *Server) MainHandler6(conn net.PacketConn, peer net.Addr, req dhcpv6.DHC
 }
 
 // MainHandler4 is like MainHandler6, but for DHCPv4 packets.
-func (s *Server) MainHandler4(conn net.PacketConn, peer net.Addr, req *dhcpv4.DHCPv4) {
+func (s *Server) MainHandler4(conn net.PacketConn, _peer net.Addr, req *dhcpv4.DHCPv4) {
 	var (
 		resp, tmp *dhcpv4.DHCPv4
 		err       error
@@ -196,16 +196,31 @@ func (s *Server) MainHandler4(conn net.PacketConn, peer net.Addr, req *dhcpv4.DH
 	}
 
 	if resp != nil {
+		var peer net.Addr
 		if !req.GatewayIPAddr.IsUnspecified() {
 			// TODO: make RFC8357 compliant
 			peer = &net.UDPAddr{IP: req.GatewayIPAddr, Port: dhcpv4.ServerPort}
+		} else if resp.MessageType() == dhcpv4.MessageTypeNak {
+			peer = &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpv4.ClientPort}
+		} else if !req.ClientIPAddr.IsUnspecified() {
+			peer = &net.UDPAddr{IP: req.ClientIPAddr, Port: dhcpv4.ClientPort}
+		} else if req.IsBroadcast() {
+			peer = &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpv4.ClientPort}
+		} else {
+			// FIXME: we're supposed to unicast to a specific *L2* address, and an L3
+			// address that's not yet assigned.
+			// I don't know how to do that with this API...
+			//peer = &net.UDPAddr{IP: resp.YourIPAddr, Port: dhcpv4.ClientPort}
+			log.Warn("Cannot handle non-broadcast-capable unspecified peers in an RFC-compliant way. " +
+				"Response will be broadcast")
+
+			peer = &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpv4.ClientPort}
 		}
-		if req.ClientIPAddr.IsUnspecified() {
-			peer = &net.UDPAddr{IP: net.IPv4(255, 255, 255, 255), Port: dhcpv4.ClientPort}
-		}
+
 		if _, err := conn.WriteTo(resp.ToBytes(), peer); err != nil {
 			log.Printf("MainHandler4: conn.Write to %v failed: %v", peer, err)
 		}
+
 	} else {
 		log.Print("MainHandler4: dropping request because response is nil")
 	}