dhcpsvc: multiplex dhcpv4
This commit is contained in:
102
internal/dhcpsvc/serve.go
Normal file
102
internal/dhcpsvc/serve.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package dhcpsvc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
func (srv *DHCPServer) serve(ctx context.Context) {
|
||||
defer slogutil.RecoverAndLog(ctx, srv.logger)
|
||||
|
||||
for pkt := range srv.packetSource.Packets() {
|
||||
etherLayer, ok := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
|
||||
if !ok {
|
||||
srv.logger.DebugContext(ctx, "skipping non-ethernet packet")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
switch typ := etherLayer.EthernetType; typ {
|
||||
case layers.EthernetTypeIPv4:
|
||||
err = srv.serveV4(ctx, pkt)
|
||||
case layers.EthernetTypeIPv6:
|
||||
// TODO(e.burkov): Handle DHCPv6 as well.
|
||||
default:
|
||||
srv.logger.DebugContext(ctx, "skipping ethernet packet", "type", typ)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
srv.logger.ErrorContext(ctx, "serving", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serveV4 handles the ethernet packet of IPv4 type.
|
||||
func (srv *DHCPServer) serveV4(ctx context.Context, pkt gopacket.Packet) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "serving dhcpv4: %w") }()
|
||||
|
||||
msg, 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.
|
||||
|
||||
typ, ok := msgType(msg)
|
||||
if !ok {
|
||||
return errors.Error("no message type in the dhcpv4 message")
|
||||
}
|
||||
|
||||
return srv.handleDHCPv4(ctx, typ, msg)
|
||||
}
|
||||
|
||||
// handleDHCPv4 handles the DHCPv4 message of the given type.
|
||||
func (srv *DHCPServer) handleDHCPv4(
|
||||
ctx context.Context,
|
||||
typ layers.DHCPMsgType,
|
||||
msg *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:
|
||||
for _, iface := range srv.interfaces4 {
|
||||
go iface.handleDiscover(ctx, msg)
|
||||
}
|
||||
case layers.DHCPMsgTypeRequest:
|
||||
for _, iface := range srv.interfaces4 {
|
||||
go iface.handleRequest(ctx, msg)
|
||||
}
|
||||
case layers.DHCPMsgTypeRelease:
|
||||
addr, ok := netip.AddrFromSlice(msg.ClientIP)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid client ip in the release message")
|
||||
}
|
||||
|
||||
return srv.removeLeaseByAddr(ctx, addr)
|
||||
case layers.DHCPMsgTypeDecline:
|
||||
addr, ok := requestedIP(msg)
|
||||
if !ok {
|
||||
return fmt.Errorf("no requested ip in the decline message")
|
||||
}
|
||||
|
||||
return srv.removeLeaseByAddr(ctx, addr)
|
||||
default:
|
||||
// TODO(e.burkov): Handle DHCPINFORM.
|
||||
return fmt.Errorf("dhcpv4 message type: %w: %v", errors.ErrBadEnumValue, typ)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user