blob: c56270c1b5fdbaaabce76b395bed33e265930a26 [file] [log] [blame]
Lorenz Brun56a7ae62020-10-29 11:03:30 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package dhcp4c
18
19import (
Lorenz Brundbac6cc2020-11-30 10:57:26 +010020 "encoding/binary"
Lorenz Brun56a7ae62020-10-29 11:03:30 +010021 "net"
22 "time"
23
24 "github.com/insomniacslk/dhcp/dhcpv4"
25)
26
27// Lease represents a DHCPv4 lease. It only consists of an IP, an expiration timestamp and options as all other
28// relevant parts of the message have been normalized into their respective options. It also contains some smart
29// getters for commonly-used options which extract only valid information from options.
30type Lease struct {
31 AssignedIP net.IP
32 ExpiresAt time.Time
33 Options dhcpv4.Options
34}
35
36// SubnetMask returns the SubnetMask option or the default mask if not set or invalid.
37// It returns nil if the lease is nil.
38func (l *Lease) SubnetMask() net.IPMask {
39 if l == nil {
40 return nil
41 }
42 mask := net.IPMask(dhcpv4.GetIP(dhcpv4.OptionSubnetMask, l.Options))
43 if _, bits := mask.Size(); bits != 32 { // If given mask is not valid, use the default mask
44 mask = l.AssignedIP.DefaultMask()
45 }
46 return mask
47}
48
49// IPNet returns an IPNet from the assigned IP and subnet mask.
50// It returns nil if the lease is nil.
51func (l *Lease) IPNet() *net.IPNet {
52 if l == nil {
53 return nil
54 }
55 return &net.IPNet{
56 IP: l.AssignedIP,
57 Mask: l.SubnetMask(),
58 }
59}
60
61// Router returns the first valid router from the DHCP router option or nil if none such exists.
62// It returns nil if the lease is nil.
63func (l *Lease) Router() net.IP {
64 if l == nil {
65 return nil
66 }
67 routers := dhcpv4.GetIPs(dhcpv4.OptionRouter, l.Options)
68 for _, r := range routers {
69 if r.IsGlobalUnicast() || r.IsLinkLocalUnicast() {
70 return r
71 }
72 }
73 // No (valid) router found
74 return nil
75}
Lorenz Brundbac6cc2020-11-30 10:57:26 +010076
77// DNSServers represents an ordered collection of DNS servers
78type DNSServers []net.IP
79
80func (a DNSServers) Equal(b DNSServers) bool {
81 if len(a) == len(b) {
82 if len(a) == 0 {
83 return true // both are empty or nil
84 }
85 for i, aVal := range a {
86 if !aVal.Equal(b[i]) {
87 return false
88 }
89 }
90 return true
91 }
92 return false
93
94}
95
96func ip4toInt(ip net.IP) uint32 {
97 ip4 := ip.To4()
98 if ip4 == nil {
99 return 0
100 }
101 return binary.BigEndian.Uint32(ip4)
102}
103
104// DNSServers returns all unique valid DNS servers from the DHCP DomainNameServers options.
105// It returns nil if the lease is nil.
106func (l *Lease) DNSServers() DNSServers {
107 if l == nil {
108 return nil
109 }
110 rawServers := dhcpv4.GetIPs(dhcpv4.OptionDomainNameServer, l.Options)
111 var servers DNSServers
112 serversSeenMap := make(map[uint32]struct{})
113 for _, s := range rawServers {
114 ip4Num := ip4toInt(s)
115 if s.IsGlobalUnicast() || s.IsLinkLocalUnicast() || ip4Num != 0 {
116 if _, ok := serversSeenMap[ip4Num]; ok {
117 continue
118 }
119 serversSeenMap[ip4Num] = struct{}{}
120 servers = append(servers, s)
121 }
122 }
123 return servers
124}