kdhcp/config/config.go

168 lines
3.7 KiB
Go

package config
import (
"errors"
"fmt"
"io/ioutil"
"net"
log "git.wntrmute.dev/kyle/goutils/log"
"git.wntrmute.dev/kyle/kdhcp/iptools"
"git.wntrmute.dev/kyle/kdhcp/leases"
"gopkg.in/yaml.v2"
)
const (
CurrentVersion = 1
)
func ensureV4(ip net.IP) (net.IP, error) {
ip4 := ip.To4()
if ip4 == nil {
return ip4, fmt.Errorf("%s isn't an IPv4 address", ip)
}
return ip4, nil
}
type Network struct {
IP net.IP `yaml:"address"`
Gateway net.IP `yaml:"gateway"`
Mask net.IP `yaml:"mask"`
Broadcast net.IP `yaml:"broadcast"`
DNS []net.IP `yaml:"dns"`
Domain string `yaml:"domain"`
}
func (n Network) NetworkInfo() *leases.Server {
lnet := &leases.Network{
Mask: iptools.NetIPtoAddr(n.Mask),
Gateway: iptools.NetIPtoAddr(n.Gateway),
Domain: n.Domain,
Broadcast: iptools.NetIPtoAddr(n.Broadcast),
}
for _, addr := range n.DNS {
lnet.DNS = append(lnet.DNS, iptools.NetIPtoAddr(addr))
}
return &leases.Server{
Addr: iptools.NetIPtoAddr(n.IP),
Network: lnet,
}
}
func (n *Network) ensureV4() (err error) {
n.IP, err = ensureV4(n.IP)
if err != nil {
return fmt.Errorf("config: IP %w", err)
}
n.Gateway, err = ensureV4(n.Gateway)
if err != nil {
return fmt.Errorf("config: gateway %w", err)
}
n.Mask, err = ensureV4(n.Mask)
if err != nil {
return fmt.Errorf("config: mask %w", err)
}
n.Broadcast, err = ensureV4(n.Broadcast)
if err != nil {
return fmt.Errorf("config: broadcast %w", err)
}
for i := range n.DNS {
n.DNS[i], err = ensureV4(n.DNS[i])
if err != nil {
return fmt.Errorf("config: DNS address %w", err)
}
}
return nil
}
type ConfigFile struct {
Server *Config `yaml:"kdhcp"`
}
type Config struct {
Version int `yaml:"version"`
Interface string `yaml:"interface"`
Address string `yaml:"address"`
Port int `yaml:"port"`
LeaseFile string `yaml:"lease_file"`
Network *Network `yaml:"network"`
Pools map[string]*iptools.Range `yaml:"pools"`
Statics map[string]net.IP `yaml:"statics"`
}
func (cfg *Config) process() (err error) {
switch {
case cfg.Version == 0:
log.Warningln("config: Version is 0, which indicates it hasn't been set. The config may be invalid.")
case cfg.Version > CurrentVersion:
log.Warningf("config: Version is greater than the current version %d. The config may not behave as expected.", CurrentVersion)
}
_, err = net.InterfaceByName(cfg.Interface)
if err != nil {
return fmt.Errorf("config: while looking up interface %s: %w", cfg.Interface, err)
}
err = cfg.Network.ensureV4()
if err != nil {
return err
}
for k, v := range cfg.Pools {
if err = v.Validate(); err != nil {
return fmt.Errorf("config: pool %s %w", k, err)
}
cfg.Pools[k] = v
}
for k, v := range cfg.Statics {
cfg.Statics[k], err = ensureV4(v)
if err != nil {
return fmt.Errorf("config: %s %w", k, err)
}
}
return nil
}
func Load(path string) (*Config, error) {
if path == "" {
path = FindConfigPath()
}
if path == "" {
return nil, errors.New("config: no config file path specified and couldn't find a valid config file path")
}
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("config: loading %s: %w", path, err)
}
configFile := &ConfigFile{}
err = yaml.Unmarshal(data, configFile)
if err != nil {
return nil, fmt.Errorf("config: while unmarshaling %s: %w", path, err)
}
if configFile.Server == nil {
log.Fatal("missing `kdhcp` section of config")
}
config := configFile.Server
if err = config.process(); err != nil {
return nil, err
}
log.Debugf("config: read configuration from %s", path)
return config, nil
}