kdhcp/server/server.go

200 lines
4.1 KiB
Go
Raw Normal View History

2023-05-02 16:27:27 +00:00
package server
import (
2023-05-15 13:09:12 +00:00
"fmt"
2023-05-02 16:27:27 +00:00
"net"
2023-05-06 05:21:27 +00:00
"time"
2023-05-02 16:27:27 +00:00
2023-05-09 07:28:54 +00:00
log "git.wntrmute.dev/kyle/goutils/log"
2023-05-02 16:27:27 +00:00
"git.wntrmute.dev/kyle/kdhcp/config"
2023-05-02 23:33:14 +00:00
"git.wntrmute.dev/kyle/kdhcp/dhcp"
2023-05-06 05:21:27 +00:00
"git.wntrmute.dev/kyle/kdhcp/iptools"
2023-05-15 13:09:12 +00:00
"git.wntrmute.dev/kyle/kdhcp/leases"
2023-05-09 07:28:54 +00:00
"github.com/benbjohnson/clock"
2023-05-02 16:27:27 +00:00
)
const (
2023-05-06 05:21:27 +00:00
DefaultPool = "default"
MaxPacketSize = 512
MaxResponseWait = 5 * time.Minute
2023-05-02 16:27:27 +00:00
)
2023-05-15 13:09:12 +00:00
func addrReply(addr net.Addr) (net.Addr, error) {
udpAddr, err := net.ResolveUDPAddr("udp4", addr.String())
if err != nil {
return nil, err
}
if udpAddr.IP.Equal(net.IPv4zero) {
return &net.UDPAddr{
IP: net.IPv4bcast,
Port: udpAddr.Port,
}, nil
}
return udpAddr, nil
}
2023-05-02 16:27:27 +00:00
type Server struct {
Conn net.PacketConn
Config *config.Config
2023-05-06 05:21:27 +00:00
Pools map[string]*iptools.Pool
2023-05-15 13:09:12 +00:00
Static map[string]*leases.Info
2023-05-09 07:28:54 +00:00
clock clock.Clock
2023-05-02 16:27:27 +00:00
}
func (s *Server) Close() error {
return s.Conn.Close()
}
func (s *Server) Bind() (err error) {
2023-05-02 23:33:14 +00:00
// In order to read DHCP packets, we'll need to listen on all addresses.
2023-05-06 05:21:27 +00:00
// That being said, we also want to limit our listening to the DHCP
// network device.
2023-05-02 23:33:14 +00:00
ip := net.IP([]byte{0, 0, 0, 0})
s.Conn, err = BindInterface(ip, s.Config.Port, s.Config.Interface)
2023-05-02 16:27:27 +00:00
return err
}
func (s *Server) ReadFrom() ([]byte, net.Addr, error) {
b := make([]byte, MaxPacketSize)
n, addr, err := s.Conn.ReadFrom(b)
if err != nil {
return nil, nil, err
}
b = b[:n]
return b, addr, nil
}
2023-05-15 13:09:12 +00:00
func (s *Server) ReadDHCPRequest() (*dhcp.Packet, net.Addr, error) {
2023-05-02 23:33:14 +00:00
pkt, addr, err := s.ReadFrom()
if err != nil {
2023-05-15 13:09:12 +00:00
return nil, nil, err
2023-05-02 23:33:14 +00:00
}
2023-05-06 05:21:27 +00:00
log.Debugf("server: read packet from %s", addr)
2023-05-15 13:09:12 +00:00
req, err := dhcp.ReadPacket(pkt)
if err != nil {
return nil, nil, err
}
return req, addr, nil
2023-05-02 23:33:14 +00:00
}
2023-05-02 16:27:27 +00:00
func (s *Server) WriteTo(b []byte, addr net.Addr) error {
2023-05-15 13:09:12 +00:00
n, err := s.Conn.WriteTo(b, addr)
if err != nil {
return err
}
log.Debugf("wrote %d bytes to %s", n, addr)
return err
2023-05-02 16:27:27 +00:00
}
2023-05-15 13:09:12 +00:00
func (s *Server) AcceptPacket() (*dhcp.Packet, net.Addr, error) {
request, addr, err := s.ReadDHCPRequest()
2023-05-02 16:27:27 +00:00
if err != nil {
2023-05-15 13:09:12 +00:00
return nil, nil, err
2023-05-02 16:27:27 +00:00
}
2023-05-02 23:33:14 +00:00
log.Debugf("BOOTP request received from %x", request.HardwareAddress)
2023-05-15 13:09:12 +00:00
return request, addr, nil
2023-05-02 16:27:27 +00:00
}
2023-05-15 13:09:12 +00:00
func (srv *Server) Listen() {
go srv.updatePoolLoop()
go func() {
for {
time.Sleep(5 * time.Minute)
if err := srv.SaveLeases(); err != nil {
log.Warningf("server: while saving leases: %s", err)
}
}
}()
2023-05-09 07:28:54 +00:00
2023-05-02 16:27:27 +00:00
for {
2023-05-15 13:09:12 +00:00
req, addr, err := srv.AcceptPacket()
2023-05-09 07:28:54 +00:00
if err != nil {
2023-05-02 16:27:27 +00:00
log.Errf("server: error reading packet: %s", err)
continue
}
2023-05-09 07:28:54 +00:00
2023-05-15 13:09:12 +00:00
if req.MessageType != dhcp.MessageTypeBootRequest {
log.Debugf("ignoring BOOTP message type %d", req.MessageType)
2023-05-09 07:28:54 +00:00
continue
}
2023-05-15 13:09:12 +00:00
switch req.DHCPType {
case dhcp.DHCPMessageTypeDiscover:
go srv.WriteDHCPResponse(req, addr)
case dhcp.DHCPMessageTypeRequest:
log.Debugf("not handling DHCP request")
case dhcp.DHCPMessageTypeRelease:
log.Debugf("not handling DHCP release")
default:
log.Debugf("not handling unknown request type %d", req.DHCPType)
}
2023-05-02 16:27:27 +00:00
}
}
func New(cfg *config.Config) (*Server, error) {
srv := &Server{
Config: cfg,
2023-05-06 05:21:27 +00:00
Pools: map[string]*iptools.Pool{},
2023-05-15 13:09:12 +00:00
Static: map[string]*leases.Info{},
2023-05-09 07:28:54 +00:00
clock: clock.New(),
2023-05-02 16:27:27 +00:00
}
2023-05-06 05:21:27 +00:00
if err := srv.loadPoolsFromConfig(); err != nil {
return nil, err
}
if err := srv.LoadLeases(); err != nil {
return nil, err
}
if err := srv.Bind(); err != nil {
2023-05-02 16:27:27 +00:00
return nil, err
}
log.Infof("server: bound to %s:%s", cfg.Interface, srv.Conn.LocalAddr())
return srv, nil
}
2023-05-06 05:21:27 +00:00
2023-05-15 13:09:12 +00:00
func (srv *Server) SelectLease(req *dhcp.Packet, t time.Time) *leases.Info {
2023-05-06 05:21:27 +00:00
if li, ok := srv.Static[req.HostName]; ok {
return li
}
if pool, ok := srv.Pools[req.HostName]; ok {
2023-05-15 13:09:12 +00:00
return pool.Peek(req, t, MaxResponseWait)
2023-05-06 05:21:27 +00:00
}
if pool, ok := srv.Pools[DefaultPool]; ok {
2023-05-15 13:09:12 +00:00
return pool.Peek(req, t, MaxResponseWait)
2023-05-06 05:21:27 +00:00
}
return nil
}
2023-05-15 13:09:12 +00:00
func (srv *Server) AcceptLease(req *dhcp.Packet, li *leases.Info, t time.Time) error {
if _, ok := srv.Static[li.HostName]; ok {
return nil
}
for _, pool := range srv.Pools {
if _, ok := pool.Active[li.Addr]; ok {
return nil
}
if _, ok := pool.Limbo[li.Addr]; ok {
return pool.Accept(li.Addr, t)
}
}
return fmt.Errorf("could not accept unknown lease: %s", li)
}