mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-07-04 01:07:49 +02:00
internal/clientinfo: add kea-dhcp4 parser
This commit is contained in:
committed by
Cuong Manh Le
parent
eaad24e5e5
commit
9c1665a759
@@ -18,4 +18,5 @@ type LeaseFileFormat string
|
|||||||
const (
|
const (
|
||||||
Dnsmasq LeaseFileFormat = "dnsmasq"
|
Dnsmasq LeaseFileFormat = "dnsmasq"
|
||||||
IscDhcpd LeaseFileFormat = "isc-dhcpd"
|
IscDhcpd LeaseFileFormat = "isc-dhcpd"
|
||||||
|
KeaDHCP4 LeaseFileFormat = "kea-dhcp4"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package clientinfo
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -202,7 +203,8 @@ func (d *dhcp) dnsmasqReadClientInfoFile(name string) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dnsmasqReadClientInfoReader likes ctrld.Dnsmasq, but reading from an io.Reader instead of file.
|
// dnsmasqReadClientInfoReader performs the same task as dnsmasqReadClientInfoFile,
|
||||||
|
// but by reading from an io.Reader instead of file.
|
||||||
func (d *dhcp) dnsmasqReadClientInfoReader(reader io.Reader) error {
|
func (d *dhcp) dnsmasqReadClientInfoReader(reader io.Reader) error {
|
||||||
return lineread.Reader(reader, func(line []byte) error {
|
return lineread.Reader(reader, func(line []byte) error {
|
||||||
fields := bytes.Fields(line)
|
fields := bytes.Fields(line)
|
||||||
@@ -244,7 +246,8 @@ func (d *dhcp) iscDHCPReadClientInfoFile(name string) error {
|
|||||||
return d.iscDHCPReadClientInfoReader(f)
|
return d.iscDHCPReadClientInfoReader(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// iscDHCPReadClientInfoReader likes ctrld.IscDhcpd, but reading from an io.Reader instead of file.
|
// iscDHCPReadClientInfoReader performs the same task as iscDHCPReadClientInfoFile,
|
||||||
|
// but by reading from an io.Reader instead of file.
|
||||||
func (d *dhcp) iscDHCPReadClientInfoReader(reader io.Reader) error {
|
func (d *dhcp) iscDHCPReadClientInfoReader(reader io.Reader) error {
|
||||||
s := bufio.NewScanner(reader)
|
s := bufio.NewScanner(reader)
|
||||||
var ip, mac, hostname string
|
var ip, mac, hostname string
|
||||||
@@ -287,6 +290,58 @@ func (d *dhcp) iscDHCPReadClientInfoReader(reader io.Reader) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keaDhcp4ReadClientInfoFile populates dhcp table with client info reading from kea dhcp4 lease file.
|
||||||
|
func (d *dhcp) keaDhcp4ReadClientInfoFile(name string) error {
|
||||||
|
f, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return d.keaDhcp4ReadClientInfoReader(bufio.NewReader(f))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// keaDhcp4ReadClientInfoReader performs the same task as keaDhcp4ReadClientInfoFile,
|
||||||
|
// but by reading from an io.Reader instead of file.
|
||||||
|
func (d *dhcp) keaDhcp4ReadClientInfoReader(r io.Reader) error {
|
||||||
|
cr := csv.NewReader(r)
|
||||||
|
for {
|
||||||
|
record, err := cr.Read()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(record) < 9 {
|
||||||
|
continue // hostname is at 9th field, so skipping record with not enough fields.
|
||||||
|
}
|
||||||
|
if record[0] == "address" {
|
||||||
|
continue // skip header.
|
||||||
|
}
|
||||||
|
mac := record[1]
|
||||||
|
if _, err := net.ParseMAC(mac); err != nil { // skip invalid MAC
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip := normalizeIP(record[0])
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
ctrld.ProxyLogger.Load().Warn().Msgf("invalid ip address entry: %q", ip)
|
||||||
|
ip = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
d.mac.Store(ip, mac)
|
||||||
|
d.ip.Store(mac, ip)
|
||||||
|
hostname := record[8]
|
||||||
|
if hostname == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := normalizeHostname(hostname)
|
||||||
|
d.mac2name.Store(mac, name)
|
||||||
|
d.ip2name.Store(ip, name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// addSelf populates current host info to dhcp, so queries from
|
// addSelf populates current host info to dhcp, so queries from
|
||||||
// the host itself can be attached with proper client info.
|
// the host itself can be attached with proper client info.
|
||||||
func (d *dhcp) addSelf() {
|
func (d *dhcp) addSelf() {
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ var clientInfoFiles = map[string]ctrld.LeaseFileFormat{
|
|||||||
"/run/dhcpd.leases": ctrld.IscDhcpd, // EdgeOS
|
"/run/dhcpd.leases": ctrld.IscDhcpd, // EdgeOS
|
||||||
"/var/dhcpd/var/db/dhcpd.leases": ctrld.IscDhcpd, // Pfsense
|
"/var/dhcpd/var/db/dhcpd.leases": ctrld.IscDhcpd, // Pfsense
|
||||||
"/home/pi/.router/run/dhcp/dnsmasq.leases": ctrld.Dnsmasq, // Firewalla
|
"/home/pi/.router/run/dhcp/dnsmasq.leases": ctrld.Dnsmasq, // Firewalla
|
||||||
|
"/var/lib/kea/dhcp4.leases": ctrld.KeaDHCP4, // Pfsense
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,41 @@ lease 192.168.1.2 {
|
|||||||
"00:00:00:00:00:04",
|
"00:00:00:00:00:04",
|
||||||
"example",
|
"example",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kea-dhcp4 good",
|
||||||
|
`address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id
|
||||||
|
192.168.0.123,00:00:00:00:00:05,00:00:00:00:00:05,7200,1703290639,1,0,0,foo,0,,0
|
||||||
|
`,
|
||||||
|
d.keaDhcp4ReadClientInfoReader,
|
||||||
|
"00:00:00:00:00:05",
|
||||||
|
"foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kea-dhcp4 no-header",
|
||||||
|
`192.168.0.123,00:00:00:00:00:05,00:00:00:00:00:05,7200,1703290639,1,0,0,foo,0,,0`,
|
||||||
|
d.keaDhcp4ReadClientInfoReader,
|
||||||
|
"00:00:00:00:00:05",
|
||||||
|
"foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kea-dhcp4 hostname *",
|
||||||
|
`address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id
|
||||||
|
192.168.0.123,00:00:00:00:00:05,00:00:00:00:00:05,7200,1703290639,1,0,0,*,0,,0
|
||||||
|
`,
|
||||||
|
d.keaDhcp4ReadClientInfoReader,
|
||||||
|
"00:00:00:00:00:05",
|
||||||
|
"*",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kea-dhcp4 bad",
|
||||||
|
`address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id
|
||||||
|
192.168.0.123,00:00:00:00:00:05,00:00:00:00:00:05,7200,1703290639,1,0,0,foo,0,,0
|
||||||
|
192.168.0.124,invalid_MAC,00:00:00:00:00:05,7200,1703290639,1,0,0,foo,0,,0
|
||||||
|
`,
|
||||||
|
d.keaDhcp4ReadClientInfoReader,
|
||||||
|
"00:00:00:00:00:05",
|
||||||
|
"foo",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
@@ -76,6 +111,12 @@ lease 192.168.1.2 {
|
|||||||
t.Errorf("readClientInfoReader() error = %v", err)
|
t.Errorf("readClientInfoReader() error = %v", err)
|
||||||
}
|
}
|
||||||
val, existed := d.mac2name.Load(tc.mac)
|
val, existed := d.mac2name.Load(tc.mac)
|
||||||
|
if tc.hostname == "*" {
|
||||||
|
if existed {
|
||||||
|
t.Errorf("* hostname must be skipped")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if !existed {
|
if !existed {
|
||||||
t.Error("client info missing")
|
t.Error("client info missing")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user