working offers

This commit is contained in:
2023-05-15 13:09:12 +00:00
parent f4fca2ffd0
commit cec46e68e8
29 changed files with 745 additions and 180 deletions

View File

@@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "server",
srcs = [
"dhcp_transaction.go",
"ifi.go",
"ifi_linux.go",
"lease_state.go",
@@ -15,6 +16,7 @@ go_library(
"//config",
"//dhcp",
"//iptools",
"//leases",
"@com_github_benbjohnson_clock//:clock",
"@dev_wntrmute_git_kyle_goutils//log",
"@in_gopkg_yaml_v2//:yaml_v2",

View File

@@ -0,0 +1,58 @@
package server
import (
"net"
log "git.wntrmute.dev/kyle/goutils/log"
"git.wntrmute.dev/kyle/kdhcp/dhcp"
)
func (srv *Server) WriteDHCPResponse(req *dhcp.Packet, ip net.Addr) error {
lease := srv.SelectLease(req, srv.clock.Now())
if lease == nil {
log.Errln("server: couldn't find available lease")
return nil
}
log.Debugf("available lease: %s", lease)
response, err := dhcp.NewOffer(req, srv.Config.Network.NetworkInfo(), lease)
if err != nil {
log.Errln(err.Error())
return err
}
log.Debugf("building offer for address %s", lease.Addr)
pkt, err := dhcp.WritePacket(response)
if err != nil {
log.Errln(err.Error())
return err
}
ip, err = addrReply(ip)
if err != nil {
log.Errln(err.Error())
return err
}
log.Debugf("writing offer to %s", ip)
_, err = srv.Conn.WriteTo(pkt, ip)
if err != nil {
log.Errf("failed to write packet: %s", err)
return err
}
log.Infof("sending offer for address %s to %s", lease.Addr, req.HardwareAddress)
go func() {
if err := srv.SaveLeases(); err != nil {
log.Warningf("server: while saving leases: %s", err)
}
}()
err = srv.AcceptLease(req, lease, srv.clock.Now())
if err != nil {
log.Errln(err.Error())
return err
}
return nil
}

View File

@@ -3,20 +3,20 @@
package server
import (
"fmt"
"net"
"syscall"
log "git.wntrmute.dev/kyle/goutils/log"
)
func BindInterface(ip net.IP, port int, dev string) (net.PacketConn, error) {
if port == 0 {
port = 67
}
udpAddr := &net.UDPAddr{
IP: ip,
Port: port,
udpAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf(":%d", port))
if err != nil {
return nil, err
}
log.Debugf("listen on %s", udpAddr)
conn, err := net.ListenUDP("udp4", udpAddr)
if err != nil {
return nil, err

View File

@@ -1,62 +1,66 @@
package server
import (
"fmt"
"encoding/json"
"os"
log "git.wntrmute.dev/kyle/goutils/log"
"git.wntrmute.dev/kyle/kdhcp/iptools"
"gopkg.in/yaml.v2"
"git.wntrmute.dev/kyle/kdhcp/leases"
)
type LeaseState struct {
Pools map[string]*iptools.Pool `yaml:"pools"`
Static map[string]*iptools.LeaseInfo `yaml:"static"`
Pools map[string]*iptools.Pool `json:"pools"`
Static map[string]*leases.Info `json:"static"`
}
func (srv *Server) SaveLeases() error {
leaseFile, err := os.Create(srv.Config.LeaseFile)
if err != nil {
return fmt.Errorf("server: while saving leases: %w", err)
}
defer leaseFile.Close()
encoder := yaml.NewEncoder(leaseFile)
state := &LeaseState{
Pools: srv.Pools,
Static: srv.Static,
}
if err = encoder.Encode(state); err != nil {
return fmt.Errorf("server: while saving leases: %w", err)
out, err := json.MarshalIndent(state, "", " ")
if err != nil {
return err
}
if err = encoder.Close(); err != nil {
return fmt.Errorf("server: while saving leases: %w", err)
err = os.WriteFile(srv.Config.LeaseFile, out, 0644)
if err != nil {
return err
}
log.Infof("wrote lease state to file %s", srv.Config.LeaseFile)
return nil
}
func (srv *Server) LoadLeases() error {
leaseState := &LeaseState{}
leaseFile, err := os.Open(srv.Config.LeaseFile)
state := &LeaseState{}
stateBytes, err := os.ReadFile(srv.Config.LeaseFile)
if err != nil {
if os.IsNotExist(err) {
log.Warningf("server: not loading leases from %s: lease file not found", srv.Config.LeaseFile)
log.Infof("not restoring leases from %s: file doesn't exist", srv.Config.LeaseFile)
return nil
}
return fmt.Errorf("server: while reading leases: %w", err)
}
defer leaseFile.Close()
decoder := yaml.NewDecoder(leaseFile)
if err = decoder.Decode(leaseState); err != nil {
return fmt.Errorf("server: while reading leases: %w", err)
return err
}
srv.Pools = leaseState.Pools
srv.Static = leaseState.Static
err = json.Unmarshal(stateBytes, state)
if err != nil {
switch err := err.(type) {
case *json.UnmarshalTypeError:
log.Infof("type error: %q: [ \"bad type: got %s; want %s\" ]", err.Field, err.Value, err.Type.String())
return err
case *json.InvalidUnmarshalError:
log.Infof("invalid unmarshal error: %s", err)
}
return err
}
log.Infoln("restored lease states")
srv.Pools = state.Pools
srv.Static = state.Static
return nil
}

View File

@@ -7,6 +7,7 @@ import (
log "git.wntrmute.dev/kyle/goutils/log"
"git.wntrmute.dev/kyle/kdhcp/iptools"
"git.wntrmute.dev/kyle/kdhcp/leases"
)
// pools.go adds pool functionality to the server.
@@ -18,7 +19,7 @@ func (srv *Server) loadPoolsFromConfig() error {
return fmt.Errorf("server: while instantiating pools, could not load IP %s", ip)
}
log.Debugf("server: added static host entry %s -> %s", host, addr)
srv.Static[host] = &iptools.LeaseInfo{
srv.Static[host] = &leases.Info{
HostName: host,
Addr: addr,
}

View File

@@ -1,7 +1,7 @@
package server
import (
"errors"
"fmt"
"net"
"time"
@@ -9,6 +9,7 @@ import (
"git.wntrmute.dev/kyle/kdhcp/config"
"git.wntrmute.dev/kyle/kdhcp/dhcp"
"git.wntrmute.dev/kyle/kdhcp/iptools"
"git.wntrmute.dev/kyle/kdhcp/leases"
"github.com/benbjohnson/clock"
)
@@ -18,11 +19,27 @@ const (
MaxResponseWait = 5 * time.Minute
)
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
}
type Server struct {
Conn net.PacketConn
Config *config.Config
Pools map[string]*iptools.Pool
Static map[string]*iptools.LeaseInfo
Static map[string]*leases.Info
clock clock.Clock
}
@@ -51,48 +68,74 @@ func (s *Server) ReadFrom() ([]byte, net.Addr, error) {
return b, addr, nil
}
func (s *Server) ReadDHCPRequest() (*dhcp.BootRequest, error) {
func (s *Server) ReadDHCPRequest() (*dhcp.Packet, net.Addr, error) {
pkt, addr, err := s.ReadFrom()
if err != nil {
return nil, err
return nil, nil, err
}
log.Debugf("server: read packet from %s", addr)
return dhcp.ReadRequest(pkt)
req, err := dhcp.ReadPacket(pkt)
if err != nil {
return nil, nil, err
}
return req, addr, nil
}
func (s *Server) WriteTo(b []byte, addr net.Addr) error {
return errors.New("server: not implemented")
n, err := s.Conn.WriteTo(b, addr)
if err != nil {
return err
}
log.Debugf("wrote %d bytes to %s", n, addr)
return err
}
func (s *Server) AcceptPacket() (*dhcp.BootRequest, error) {
request, err := s.ReadDHCPRequest()
func (s *Server) AcceptPacket() (*dhcp.Packet, net.Addr, error) {
request, addr, err := s.ReadDHCPRequest()
if err != nil {
return nil, err
return nil, nil, err
}
log.Debugf("BOOTP request received from %x", request.HardwareAddress)
return request, nil
return request, addr, nil
}
func (s *Server) Listen() {
go s.updatePoolLoop()
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)
}
}
}()
for {
req, err := s.AcceptPacket()
req, addr, err := srv.AcceptPacket()
if err != nil {
log.Errf("server: error reading packet: %s", err)
continue
}
lease := s.SelectLease(req, s.clock.Now())
if err != nil {
log.Err("server: couldn't find available lease")
if req.MessageType != dhcp.MessageTypeBootRequest {
log.Debugf("ignoring BOOTP message type %d", req.MessageType)
continue
}
log.Infof("available lease: %s", lease)
continue
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)
}
}
}
@@ -100,7 +143,7 @@ func New(cfg *config.Config) (*Server, error) {
srv := &Server{
Config: cfg,
Pools: map[string]*iptools.Pool{},
Static: map[string]*iptools.LeaseInfo{},
Static: map[string]*leases.Info{},
clock: clock.New(),
}
@@ -121,18 +164,36 @@ func New(cfg *config.Config) (*Server, error) {
return srv, nil
}
func (srv *Server) SelectLease(req *dhcp.BootRequest, t time.Time) *iptools.LeaseInfo {
func (srv *Server) SelectLease(req *dhcp.Packet, t time.Time) *leases.Info {
if li, ok := srv.Static[req.HostName]; ok {
return li
}
if pool, ok := srv.Pools[req.HostName]; ok {
return pool.Peek(t, MaxResponseWait)
return pool.Peek(req, t, MaxResponseWait)
}
if pool, ok := srv.Pools[DefaultPool]; ok {
return pool.Peek(t, MaxResponseWait)
return pool.Peek(req, t, MaxResponseWait)
}
return nil
}
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)
}