forked from j-keck/lsleases
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdhcp.go
168 lines (145 loc) · 4.94 KB
/
dhcp.go
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package main
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"log"
"net"
"time"
)
var (
discover = 1
offer = 2
request = 3
decline = 4
ack = 5
nak = 6
release = 7
inform = 8
messageTypes = map[int]string{
discover: "DHCP Discover",
offer: "DHCP Offer",
request: "DHCP Request",
decline: "DHCP Decline",
ack: "DHCP Ack",
nak: "DHCP NAK",
release: "DHCP Release",
inform: "DHCP Inform",
}
)
type dhcpDatagram struct {
op uint8 // Message op code / message type (1 = BOOTREQUEST, 2 = BOOTREPLY)
htype uint8 // Hardware address type
hlen uint8 // Hardware address length
hops uint8
xid uint32
secs uint16
flags uint16
ciaddr [4]byte
yiaddr [4]byte
siaddr [4]byte
giaddr [4]byte
chaddr [16]byte
sname [64]byte
file [128]byte
options map[uint8][]byte
}
func (datagram dhcpDatagram) Mac() string {
return net.HardwareAddr(datagram.chaddr[:datagram.hlen]).String()
}
func (datagram dhcpDatagram) HostName() (string, error) {
if h, ok := datagram.options[12]; ok {
return string(h), nil
}
return "", fmt.Errorf("field 'host name' not found - datagram: %+v", datagram)
}
func (datagram dhcpDatagram) MessageType() (string, error) {
if id, ok := datagram.options[53]; ok {
return messageTypes[int(id[0])], nil
}
return "", fmt.Errorf("field 'message type' not found - datagram: %+v", datagram)
}
func (datagram dhcpDatagram) RequestedIPAddress() (string, error) {
if reqIPBytes, ok := datagram.options[50]; ok {
return net.IP(reqIPBytes).String(), nil
}
return "", fmt.Errorf("field 'RequestedIPAddress not found - datagram: %+v", datagram)
}
func parseDhcpDatagram(rawDatagram []byte) (datagram dhcpDatagram, err error) {
// "panic catcher"
// converts a panic to an error and save it into 'err'
defer func() {
if r := recover(); r != nil {
err = errors.New(r.(string))
}
}()
buffer := bytes.NewBuffer(rawDatagram)
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.op), "read htype")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.htype), "read htype")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.hlen), "read hlen")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.hops), "read hpos")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.xid), "read xid")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.secs), "read secs")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.flags), "read flags")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.ciaddr), "read ciaddr")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.yiaddr), "read yiaddr")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.siaddr), "read siaddr")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.giaddr), "read giaddr")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.chaddr), "read chaddr")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.sname), "read sname")
panicOnError(binary.Read(buffer, binary.BigEndian, &datagram.file), "read file")
buffer.Next(4) // skip Magic cookie
options := make(map[uint8][]byte)
for {
var code, length uint8
panicOnError(binary.Read(buffer, binary.BigEndian, &code), "read options code")
if code == 255 { // END Flag
break
}
panicOnError(binary.Read(buffer, binary.BigEndian, &length), "read options length")
content := buffer.Next(int(length))
options[code] = content
}
datagram.options = options
return datagram, err
}
func dhcpListener(dhcpLeaseChan chan<- *DHCPLease) {
addr, err := net.ResolveUDPAddr("udp4", ":67")
exitOnError(err, "resolve udp4 addr")
con, err := net.ListenUDP("udp4", addr)
exitOnError(err, "listen udp4")
for {
rawBuffer := make([]byte, 512)
n, err := con.Read(rawBuffer)
exitOnError(err, "error reading from connection")
verboseLog.Println("new dhcp datagram received")
if datagram, err := parseDhcpDatagram(rawBuffer[:n]); err != nil {
log.Printf("parse dhcp datagram error: '%s'\n", err.Error())
} else {
messageType, _ := datagram.MessageType()
if messageType == messageTypes[request] {
verboseLog.Printf("process datagram with type: '%s' - src mac: '%s'", messageType, datagram.Mac())
reqIPAddr, err := datagram.RequestedIPAddress()
if err != nil {
fmt.Println("RequestIPAddress not found! - error: ", err)
continue
}
hostName, err := datagram.HostName()
if err != nil {
fmt.Println("HostName not found! use <UNKNOW> - error: ", err)
hostName = "<UNKNOW>"
}
lease := &DHCPLease{
Created: time.Now(),
IP: reqIPAddr,
Mac: datagram.Mac(),
Name: hostName}
verboseLog.Printf("trigger new DHCPLease event: ip='%s', mac='%s', name='%s'", lease.IP, lease.Mac, lease.Name)
dhcpLeaseChan <- lease
} else {
verboseLog.Printf("ignore datagram with type: '%s' - src mac: '%s'", messageType, datagram.Mac())
}
}
}
}