dhcpsvc: add handlers
This commit is contained in:
127
internal/dhcpsvc/handler4.go
Normal file
127
internal/dhcpsvc/handler4.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package dhcpsvc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// serveV4 handles the ethernet packet of IPv4 type.
|
||||
func (srv *DHCPServer) serveV4(
|
||||
ctx context.Context,
|
||||
rw responseWriter4,
|
||||
pkt gopacket.Packet,
|
||||
) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "serving dhcpv4: %w") }()
|
||||
|
||||
req, ok := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
|
||||
if !ok {
|
||||
srv.logger.DebugContext(ctx, "skipping non-dhcpv4 packet")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(e.burkov): Handle duplicate Xid.
|
||||
|
||||
if req.Operation != layers.DHCPOpRequest {
|
||||
srv.logger.DebugContext(ctx, "skipping non-request dhcpv4 packet")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
typ, ok := msg4Type(req)
|
||||
if !ok {
|
||||
// The "DHCP message type" option - must be included in every DHCP
|
||||
// message.
|
||||
//
|
||||
// See https://datatracker.ietf.org/doc/html/rfc2131#section-3.
|
||||
return fmt.Errorf("dhcpv4: message type: %w", errors.ErrNoValue)
|
||||
}
|
||||
|
||||
return srv.handleDHCPv4(ctx, rw, typ, req)
|
||||
}
|
||||
|
||||
// handleDHCPv4 handles the DHCPv4 message of the given type.
|
||||
func (srv *DHCPServer) handleDHCPv4(
|
||||
ctx context.Context,
|
||||
rw responseWriter4,
|
||||
typ layers.DHCPMsgType,
|
||||
req *layers.DHCPv4,
|
||||
) (err error) {
|
||||
// Each interface should handle the DISCOVER and REQUEST messages offer and
|
||||
// allocate the available leases. The RELEASE and DECLINE messages should
|
||||
// be handled by the server itself as it should remove the lease.
|
||||
switch typ {
|
||||
case layers.DHCPMsgTypeDiscover:
|
||||
srv.handleDiscover(ctx, rw, req)
|
||||
case layers.DHCPMsgTypeRequest:
|
||||
srv.handleRequest(ctx, rw, req)
|
||||
case layers.DHCPMsgTypeRelease:
|
||||
// TODO(e.burkov): !! Remove the lease, either allocated or offered.
|
||||
case layers.DHCPMsgTypeDecline:
|
||||
// TODO(e.burkov): !! Remove the allocated lease. RFC tells it only
|
||||
// possible if the client found the address already in use.
|
||||
default:
|
||||
// TODO(e.burkov): Handle DHCPINFORM.
|
||||
return fmt.Errorf("dhcpv4: request type: %w: %v", errors.ErrBadEnumValue, typ)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleDiscover handles the DHCPv4 message of discover type.
|
||||
func (srv *DHCPServer) handleDiscover(ctx context.Context, rw responseWriter4, req *layers.DHCPv4) {
|
||||
// TODO(e.burkov): Check existing leases, either allocated or offered.
|
||||
|
||||
for _, iface := range srv.interfaces4 {
|
||||
go iface.handleDiscover(ctx, rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
// handleRequest handles the DHCPv4 message of request type.
|
||||
func (srv *DHCPServer) handleRequest(ctx context.Context, rw responseWriter4, req *layers.DHCPv4) {
|
||||
srvID, hasSrvID := serverID4(req)
|
||||
reqIP, hasReqIP := requestedIPv4(req)
|
||||
|
||||
switch {
|
||||
case hasSrvID && !srvID.IsUnspecified():
|
||||
// If the DHCPREQUEST message contains a server identifier option, the
|
||||
// message is in response to a DHCPOFFER message. Otherwise, the
|
||||
// message is a request to verify or extend an existing lease.
|
||||
iface, hasIface := srv.interfaces4.findInterface(srvID)
|
||||
if !hasIface {
|
||||
srv.logger.DebugContext(ctx, "skipping selecting request", "serverid", srvID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
iface.handleSelecting(ctx, rw, req, reqIP)
|
||||
case hasReqIP && !reqIP.IsUnspecified():
|
||||
// Requested IP address option MUST be filled in with client's notion of
|
||||
// its previously assigned address.
|
||||
iface, hasIface := srv.interfaces4.findInterface(reqIP)
|
||||
if !hasIface {
|
||||
srv.logger.DebugContext(ctx, "skipping init-reboot request", "requestedip", reqIP)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
iface.handleInitReboot(ctx, rw, req, reqIP)
|
||||
default:
|
||||
// Server identifier MUST NOT be filled in, requested IP address option
|
||||
// MUST NOT be filled in.
|
||||
ip, _ := netip.AddrFromSlice(req.ClientIP.To4())
|
||||
iface, hasIface := srv.interfaces4.findInterface(ip)
|
||||
if !hasIface {
|
||||
srv.logger.DebugContext(ctx, "skipping init-reboot request", "clientip", ip)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
iface.handleRenew(ctx, rw, req)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user