github.com/netdata/go.d.plugin@v0.58.1/modules/isc_dhcpd/parse.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package isc_dhcpd
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"net"
     9  	"os"
    10  )
    11  
    12  /*
    13  Documentation (v4.4): https://kb.isc.org/docs/en/isc-dhcp-44-manual-pages-dhcpdleases
    14  
    15  DHCPv4 prepare declaration:
    16    prepare ip-address {
    17      statements...
    18    }
    19  
    20  DHCPv6 prepare declaration:
    21    ia_ta IAID_DUID {
    22      cltt date;
    23      iaaddr ipv6-address {
    24        statements...
    25      }
    26    }
    27    ia_na IAID_DUID {
    28      cltt date;
    29      iaaddr ipv6-address {
    30        statements...
    31      }
    32    }
    33    ia_pd IAID_DUID {
    34      cltt date;
    35      iaprefix ipv6-address/prefix-length {
    36        statements...
    37      }
    38    }
    39  */
    40  
    41  type leaseEntry struct {
    42  	ip           net.IP
    43  	bindingState string
    44  }
    45  
    46  func (l leaseEntry) hasIP() bool           { return l.ip != nil }
    47  func (l leaseEntry) hasBindingState() bool { return l.bindingState != "" }
    48  
    49  func parseDHCPdLeasesFile(filepath string) ([]leaseEntry, error) {
    50  	f, err := os.Open(filepath)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	defer func() { _ = f.Close() }()
    55  
    56  	leasesSet := make(map[string]leaseEntry)
    57  	l := leaseEntry{}
    58  	sc := bufio.NewScanner(f)
    59  
    60  	for sc.Scan() {
    61  		bs := bytes.TrimSpace(sc.Bytes())
    62  		switch {
    63  		case !l.hasIP() && bytes.HasPrefix(bs, []byte("lease")):
    64  			// "lease 192.168.0.1 {" => "192.168.0.1"
    65  			s := string(bs)
    66  			l.ip = net.ParseIP(s[6 : len(s)-2])
    67  		case !l.hasIP() && bytes.HasPrefix(bs, []byte("iaaddr")):
    68  			// "iaaddr 1985:470:1f0b:c9a::001 {" =>  "1985:470:1f0b:c9a::001"
    69  			s := string(bs)
    70  			l.ip = net.ParseIP(s[7 : len(s)-2])
    71  		case l.hasIP() && !l.hasBindingState() && bytes.HasPrefix(bs, []byte("binding state")):
    72  			// "binding state active;" => "active"
    73  			s := string(bs)
    74  			l.bindingState = s[14 : len(s)-1]
    75  		case bytes.HasPrefix(bs, []byte("}")):
    76  			if l.hasIP() && l.hasBindingState() {
    77  				leasesSet[l.ip.String()] = l
    78  			}
    79  			l = leaseEntry{}
    80  		}
    81  	}
    82  
    83  	if len(leasesSet) == 0 {
    84  		return nil, nil
    85  	}
    86  
    87  	leases := make([]leaseEntry, 0, len(leasesSet))
    88  	for _, l := range leasesSet {
    89  		leases = append(leases, l)
    90  	}
    91  	return leases, nil
    92  }