summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/isc_dhcpd/parse.go
blob: cb4161745b52bbe7190669ae3d8f912babf760cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// SPDX-License-Identifier: GPL-3.0-or-later

package isc_dhcpd

import (
	"bufio"
	"bytes"
	"net"
	"os"
)

/*
Documentation (v4.4): https://kb.isc.org/docs/en/isc-dhcp-44-manual-pages-dhcpdleases

DHCPv4 prepare declaration:
  prepare ip-address {
    statements...
  }

DHCPv6 prepare declaration:
  ia_ta IAID_DUID {
    cltt date;
    iaaddr ipv6-address {
      statements...
    }
  }
  ia_na IAID_DUID {
    cltt date;
    iaaddr ipv6-address {
      statements...
    }
  }
  ia_pd IAID_DUID {
    cltt date;
    iaprefix ipv6-address/prefix-length {
      statements...
    }
  }
*/

type leaseEntry struct {
	ip           net.IP
	bindingState string
}

func (l leaseEntry) hasIP() bool           { return l.ip != nil }
func (l leaseEntry) hasBindingState() bool { return l.bindingState != "" }

func parseDHCPdLeasesFile(filepath string) ([]leaseEntry, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer func() { _ = f.Close() }()

	leasesSet := make(map[string]leaseEntry)
	l := leaseEntry{}
	sc := bufio.NewScanner(f)

	for sc.Scan() {
		bs := bytes.TrimSpace(sc.Bytes())
		switch {
		case !l.hasIP() && bytes.HasPrefix(bs, []byte("lease")):
			// "lease 192.168.0.1 {" => "192.168.0.1"
			s := string(bs)
			l.ip = net.ParseIP(s[6 : len(s)-2])
		case !l.hasIP() && bytes.HasPrefix(bs, []byte("iaaddr")):
			// "iaaddr 1985:470:1f0b:c9a::001 {" =>  "1985:470:1f0b:c9a::001"
			s := string(bs)
			l.ip = net.ParseIP(s[7 : len(s)-2])
		case l.hasIP() && !l.hasBindingState() && bytes.HasPrefix(bs, []byte("binding state")):
			// "binding state active;" => "active"
			s := string(bs)
			l.bindingState = s[14 : len(s)-1]
		case bytes.HasPrefix(bs, []byte("}")):
			if l.hasIP() && l.hasBindingState() {
				leasesSet[l.ip.String()] = l
			}
			l = leaseEntry{}
		}
	}

	if len(leasesSet) == 0 {
		return nil, nil
	}

	leases := make([]leaseEntry, 0, len(leasesSet))
	for _, l := range leasesSet {
		leases = append(leases, l)
	}
	return leases, nil
}