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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
# dhcp
[![Build Status](https://travis-ci.org/insomniacslk/dhcp.svg?branch=master)](https://travis-ci.org/insomniacslk/dhcp)
[![codecov](https://codecov.io/gh/insomniacslk/dhcp/branch/master/graph/badge.svg)](https://codecov.io/gh/insomniacslk/dhcp)
[![Go Report Card](https://goreportcard.com/badge/github.com/insomniacslk/dhcp)](https://goreportcard.com/report/github.com/insomniacslk/dhcp)
DHCPv4 and DHCPv6 decoding/encoding library with client and server code, written in Go.
# How to get the library
The library is split into several parts:
* `dhcpv6`: implementation of DHCPv6 packet, client and server
* `dhcpv4`: implementation of DHCPv4 packet, client and server
* `netboot`: network booting wrappers on top of `dhcpv6` and `dhcpv4`
* `iana`: several IANA constants, and helpers used by `dhcpv6` and `dhcpv4`
* `rfc1035label`: simple implementation of RFC1035 labels, used by `dhcpv6` and
`dhcpv4`
* `interfaces`, a thin layer of wrappers around network interfaces
You will probably only need `dhcpv6` and/or `dhcpv4` explicitly. The rest is
pulled in automatically if necessary.
So, to get `dhcpv6` and `dhpv4` just run:
```
go get -u github.com/insomniacslk/dhcp/dhcpv{4,6}
```
# Examples
The sections below will illustrate how to use the `dhcpv6` and `dhcpv4`
packages.
See more example code at https://github.com/insomniacslk/exdhcp
## DHCPv6 client
To run a DHCPv6 transaction on the interface "eth0":
```go
package main
import (
"log"
"github.com/insomniacslk/dhcp/dhcpv6"
)
func main() {
// NewClient sets up a new DHCPv6 client with default values
// for read and write timeouts, for destination address and listening
// address
client := dhcpv6.NewClient()
// Exchange runs a Solicit-Advertise-Request-Reply transaction on the
// specified network interface, and returns a list of DHCPv6 packets
// (a "conversation") and an error if any. Notice that Exchange may
// return a non-empty packet list even if there is an error. This is
// intended, because the transaction may fail at any point, and we
// still want to know what packets were exchanged until then.
// A default Solicit packet will be used during the "conversation",
// which can be manipulated by using modifiers.
conversation, err := client.Exchange("eth0")
// Summary() prints a verbose representation of the exchanged packets.
for _, packet := range conversation {
log.Print(packet.Summary())
}
// error handling is done *after* printing, so we still print the
// exchanged packets if any, as explained above.
if err != nil {
log.Fatal(err)
}
}
```
## DHCPv6 packet crafting and manipulation
```go
package main
import (
"log"
"net"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/iana"
)
func main() {
// In this example we create and manipulate a DHCPv6 solicit packet
// and encapsulate it in a relay packet. To to this, we use
// `dhcpv6.DHCPv6Message` and `dhcpv6.DHCPv6Relay`, two structures
// that implement the `dhcpv6.DHCPv6` interface.
// Then print the wire-format representation of the packet.
// Create the DHCPv6 Solicit first, using the interface "eth0"
// to get the MAC address
msg, err := dhcpv6.NewSolicitForInterface("eth0")
if err != nil {
log.Fatal(err)
}
// In this example I want to redact the MAC address of my
// network interface, so instead of replacing it manually,
// I will show how to use modifiers for the purpose.
// A Modifier is simply a function that can be applied on
// a DHCPv6 object to manipulate it. Here we use it to
// replace the MAC address with a dummy one.
// Modifiers can be passed to many functions, for example
// to constructors, `Exchange()`, `Solicit()`, etc. Check
// the source code to know where to use them.
// Existing modifiers are implemented in dhcpv6/modifiers.go .
mac, err := net.ParseMAC("00:fa:ce:b0:0c:00")
if err != nil {
log.Fatal(err)
}
duid := dhcpv6.Duid{
Type: dhcpv6.DUID_LLT,
HwType: iana.HwTypeEthernet,
Time: dhcpv6.GetTime(),
LinkLayerAddr: mac,
}
// As suggested above, an alternative is to call
// dhcpv6.NewSolicitForInterface("eth0", dhcpv6.WithCLientID(duid))
msg = dhcpv6.WithClientID(duid)(msg)
// Now encapsulate the message in a DHCPv6 relay.
// As per RFC3315, the link-address and peer-address have
// to be set by the relay agent. We use dummy values here.
linkAddr := net.ParseIP("2001:0db8::1")
peerAddr := net.ParseIP("2001:0db8::2")
relay, err := dhcpv6.EncapsulateRelay(msg, dhcpv6.MessageTypeRelayForward, linkAddr, peerAddr)
if err != nil {
log.Fatal(err)
}
// Print a verbose representation of the relay packet, that will also
// show a short representation of the inner Solicit message.
// To print a detailed summary of the inner packet, extract it
// first from the relay using `relay.GetInnerMessage()`.
log.Print(relay.Summary())
// And finally, print the bytes that would be sent on the wire
log.Print(relay.ToBytes())
// Note: there are many more functions in the library, check them
// out in the source code. For example, if you want to decode a
// byte stream into a DHCPv6 message or relay, you can use
// `dhcpv6.FromBytes`.
}
```
The output (slightly modified for readability) is
```
$ go run main.go
2018/11/08 13:56:31 DHCPv6Relay
messageType=RELAY-FORW
hopcount=0
linkaddr=2001:db8::1
peeraddr=2001:db8::2
options=[OptRelayMsg{relaymsg=DHCPv6Message(messageType=SOLICIT transactionID=0x9e0242, 4 options)}]
2018/11/08 13:56:31 [12 0 32 1 13 184 0 0 0 0 0 0 0 0 0 0 0 1 32 1 13 184
0 0 0 0 0 0 0 0 0 0 0 2 0 9 0 52 1 158 2 66 0 1 0 14
0 1 0 1 35 118 253 15 0 250 206 176 12 0 0 6 0 4 0 23
0 24 0 8 0 2 0 0 0 3 0 12 250 206 176 12 0 0 14 16 0
0 21 24]
```
## DHCPv6 server
A DHCPv6 server requires the user to implement a request handler. Basically the
user has to provide the logic to answer to each packet. The library offers a few
facilities to forge response packets, e.g. `NewAdvertiseFromSolicit`,
`NewReplyFromDHCPv6Message` and so on. Look at the source code to see what's
available.
An example server that will print (but not reply to) the client's request is
shown below:
```go
package main
import (
"log"
"net"
"github.com/insomniacslk/dhcp/dhcpv6"
)
func handler(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6) {
// this function will just print the received DHCPv6 message, without replying
log.Print(m.Summary())
}
func main() {
laddr := net.UDPAddr{
IP: net.ParseIP("::1"),
Port: dhcpv6.DefaultServerPort,
}
server := dhcpv6.NewServer(laddr, handler)
defer server.Close()
if err := server.ActivateAndServe(); err != nil {
log.Panic(err)
}
}
```
## DHCPv4 client
TODO
## DHCPv4 packet parsing
TODO
## DHCPv4 server
TODO
# Public projects that use it
* Facebook's DHCP load balancer, `dhcplb`, https://github.com/facebookincubator/dhcplb
* Systemboot, a LinuxBoot distribution that runs as system firmware, https://github.com/systemboot/systemboot
* Router7, a pure-Go router implementation for fiber7 connections, https://github.com/rtr7/router7
* Beats from ElasticSearch, https://github.com/elastic/beats
* Bender from Pinterest, a library for load-testing, https://github.com/pinterest/bender
* FBender from Facebook, a tool for load-testing based on Bender, https://github.com/facebookincubator/fbender
* CoreDHCP, a fast, multithreaded, modular and extensible DHCP server, https://github.com/coredhcp/coredhcp
|