Only select dynamic ip addresses
This commit is contained in:
parent
ac040ac6f7
commit
a484431d13
4 changed files with 157 additions and 7 deletions
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,9 +14,39 @@ type HealthchecksConfig struct {
|
||||||
PingKey string `json:"ping_key"`
|
PingKey string `json:"ping_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DNSMasqConfig struct {
|
||||||
|
DHCPRangeStart netip.Addr
|
||||||
|
DHCPRangeEnd netip.Addr
|
||||||
|
LeasesPath string
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
HealthchecksConfig `json:"healthchecks"`
|
HealthchecksConfig `json:"healthchecks"`
|
||||||
LeasesPath string `json:"leases_path"`
|
DNSMasqConfigFile string `json:"dnsmasq_config"`
|
||||||
|
DNSMasqConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) parseDNSMasqConf() error {
|
||||||
|
dnsMasqRootContent, err := os.ReadFile(c.DNSMasqConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read dnsmasq config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsmasqRawConf, err := parseDNSMasqOptions(string(dnsMasqRootContent))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse dnsmasq config: %w", err)
|
||||||
|
}
|
||||||
|
c.LeasesPath = dnsmasqRawConf.leaseFile
|
||||||
|
c.DHCPRangeStart, err = netip.ParseAddr(dnsmasqRawConf.rangeStart)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse dhcp range start: %w", err)
|
||||||
|
}
|
||||||
|
c.DHCPRangeEnd, err = netip.ParseAddr(dnsmasqRawConf.rangeEnd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse dhcp range end: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultConfig() *Config {
|
func defaultConfig() *Config {
|
||||||
|
@ -23,7 +54,7 @@ func defaultConfig() *Config {
|
||||||
HealthchecksConfig: HealthchecksConfig{
|
HealthchecksConfig: HealthchecksConfig{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
},
|
},
|
||||||
LeasesPath: "/var/lib/misc/dnsmasq.leases",
|
DNSMasqConfigFile: "etc/dnsmasq.conf",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +62,8 @@ func New(path string) (*Config, error) {
|
||||||
fileContent, err := os.ReadFile(path)
|
fileContent, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
return defaultConfig(), nil
|
conf := defaultConfig()
|
||||||
|
return conf, conf.parseDNSMasqConf()
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to read config file %w", err)
|
return nil, fmt.Errorf("failed to read config file %w", err)
|
||||||
}
|
}
|
||||||
|
@ -40,5 +72,5 @@ func New(path string) (*Config, error) {
|
||||||
if err := json.Unmarshal(fileContent, &c); err != nil {
|
if err := json.Unmarshal(fileContent, &c); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
||||||
}
|
}
|
||||||
return &c, nil
|
return &c, c.parseDNSMasqConf()
|
||||||
}
|
}
|
||||||
|
|
96
internal/config/dnsmasq.go
Normal file
96
internal/config/dnsmasq.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
leaseFileKey = "dhcp-leasefile"
|
||||||
|
rangeKey = "dhcp-range"
|
||||||
|
confFileKey = "conf-file"
|
||||||
|
confDirKey = "conf-dir"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tryParseLineOption(line, key string) (string, bool) {
|
||||||
|
if !strings.HasPrefix(line, key) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, val, found := strings.Cut(line, "=")
|
||||||
|
return val, found
|
||||||
|
}
|
||||||
|
|
||||||
|
type dnsmasqConfOptions struct {
|
||||||
|
leaseFile string
|
||||||
|
rangeStart string
|
||||||
|
rangeEnd string
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultDNSMasqOptions() map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
confDirKey: "/etc/dnsmasq.d",
|
||||||
|
confFileKey: "",
|
||||||
|
leaseFileKey: "/var/lib/misc/dnsmasq.leases",
|
||||||
|
rangeKey: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDNSMasqConfFile(content string, options map[string]string) {
|
||||||
|
for _, l := range strings.Split(content, "\n") {
|
||||||
|
for key := range options {
|
||||||
|
newVal, found := tryParseLineOption(l, key)
|
||||||
|
if found {
|
||||||
|
options[key] = newVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDNSMasqOptions(rootFileContent string) (dnsmasqConfOptions, error) {
|
||||||
|
rawOptions := defaultDNSMasqOptions()
|
||||||
|
|
||||||
|
// start with parsing the root file
|
||||||
|
parseDNSMasqConfFile(rootFileContent, rawOptions)
|
||||||
|
|
||||||
|
// get all additional conf files in the provided directory
|
||||||
|
confDir := rawOptions[confDirKey]
|
||||||
|
if confDir != "" {
|
||||||
|
entries, err := os.ReadDir(confDir)
|
||||||
|
if err != nil {
|
||||||
|
return dnsmasqConfOptions{}, fmt.Errorf("failed to get list of additional config files: %w", err)
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() || !strings.HasSuffix(e.Name(), ".conf") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fileContent, err := os.ReadFile(path.Join(confDir, e.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return dnsmasqConfOptions{}, fmt.Errorf("failed to read sub config file: %w", err)
|
||||||
|
}
|
||||||
|
parseDNSMasqConfFile(string(fileContent), rawOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get any additional config file
|
||||||
|
if additionalFile := rawOptions[confFileKey]; additionalFile != "" {
|
||||||
|
filecontent, err := os.ReadFile(additionalFile)
|
||||||
|
if err != nil {
|
||||||
|
return dnsmasqConfOptions{}, fmt.Errorf("failed to read additional config file: %w", err)
|
||||||
|
}
|
||||||
|
parseDNSMasqConfFile(string(filecontent), rawOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
dhcpRange := strings.Split(rawOptions[rangeKey], ",")
|
||||||
|
if len(dhcpRange) < 2 {
|
||||||
|
return dnsmasqConfOptions{}, fmt.Errorf("invalid value for DHCP range: %s", rawOptions[rangeKey])
|
||||||
|
}
|
||||||
|
|
||||||
|
return dnsmasqConfOptions{
|
||||||
|
leaseFile: rawOptions[leaseFileKey],
|
||||||
|
rangeStart: dhcpRange[0],
|
||||||
|
rangeEnd: dhcpRange[1],
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package dnsmasq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -11,7 +12,7 @@ import (
|
||||||
type Lease struct {
|
type Lease struct {
|
||||||
ExpireDate time.Time
|
ExpireDate time.Time
|
||||||
Mac string
|
Mac string
|
||||||
IP string
|
IP netip.Addr
|
||||||
Hostname string
|
Hostname string
|
||||||
ClientID string
|
ClientID string
|
||||||
}
|
}
|
||||||
|
@ -31,10 +32,15 @@ func parseLeaseLine(rawLine string) (*Lease, error) {
|
||||||
return nil, fmt.Errorf("unexpected unix timestamp value %s", lineValues[0])
|
return nil, fmt.Errorf("unexpected unix timestamp value %s", lineValues[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ip, err := netip.ParseAddr(lineValues[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unexpected IP address: %s: %w", lineValues[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
return &Lease{
|
return &Lease{
|
||||||
ExpireDate: time.Unix(int64(expireTimeInt), 0),
|
ExpireDate: time.Unix(int64(expireTimeInt), 0),
|
||||||
Mac: lineValues[1],
|
Mac: lineValues[1],
|
||||||
IP: lineValues[2],
|
IP: ip,
|
||||||
Hostname: lineValues[3],
|
Hostname: lineValues[3],
|
||||||
ClientID: lineValues[4],
|
ClientID: lineValues[4],
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -59,3 +65,19 @@ func GetLeases(leasesPath string) ([]*Lease, error) {
|
||||||
}
|
}
|
||||||
return leases, nil
|
return leases, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDynamicLeases(leasesPath string, dhcpStart, dhcpEnd netip.Addr) ([]*Lease, error) {
|
||||||
|
allLeases, err := GetLeases(leasesPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredLeases := []*Lease{}
|
||||||
|
for _, l := range allLeases {
|
||||||
|
if l.IP.Less(dhcpEnd) && dhcpStart.Less(l.IP) {
|
||||||
|
filteredLeases = append(filteredLeases, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredLeases, nil
|
||||||
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -31,7 +31,7 @@ func main() {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to notify process start: %s\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to notify process start: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
leases, err := dnsmasq.GetLeases(conf.LeasesPath)
|
leases, err := dnsmasq.GetDynamicLeases(conf.LeasesPath, conf.DHCPRangeStart, conf.DHCPRangeEnd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to parse leases: %s\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to parse leases: %s\n", err)
|
||||||
if err := healthchecks.Failure(context.Background(), conf.HealthchecksConfig, healtchecksClt, check, "failed to parse lease file"); err != nil {
|
if err := healthchecks.Failure(context.Background(), conf.HealthchecksConfig, healtchecksClt, check, "failed to parse lease file"); err != nil {
|
||||||
|
|
Loading…
Reference in a new issue