Skip to content

Commit bd8ef91

Browse files
authored
Merge branch 'master' into travis_build_macos
2 parents 519b3e8 + 175868d commit bd8ef91

File tree

181 files changed

+6776
-5748
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+6776
-5748
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
.*.swp
2+
examples/client6/client6
3+
examples/client4/client4
4+
examples/packetcrafting6/packetcrafting6

.travis/tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ done
2828

2929
# Skip go1.9 for this check. rtr7/router7 depends on miekg/dns, which does not
3030
# support go1.9
31-
if [ "$TRAVIS_GO_VERSION" = "1.9" ]
31+
if [[ "$TRAVIS_GO_VERSION" =~ ^1.(9|10|11)$ ]]
3232
then
3333
exit 0
3434
fi

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
* Sean Karlage (BSDP package, and of tons of improvements to the DHCPv4 package)
66
* Owen Mooney (several option fixes and modifiers)
77
* Mikolaj Walczak (asynchronous DHCPv6 client)
8+
* Chris Koch (tons of improvements in DHCPv4 and DHCPv6 internals and interface)

README.md

Lines changed: 8 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -31,199 +31,15 @@ go get -u github.com/insomniacslk/dhcp/dhcpv{4,6}
3131
The sections below will illustrate how to use the `dhcpv6` and `dhcpv4`
3232
packages.
3333

34-
See more example code at https://github.com/insomniacslk/exdhcp
35-
36-
37-
## DHCPv6 client
38-
39-
To run a DHCPv6 transaction on the interface "eth0":
40-
41-
```go
42-
package main
43-
44-
import (
45-
"log"
46-
47-
"github.com/insomniacslk/dhcp/dhcpv6"
48-
)
49-
50-
51-
func main() {
52-
// NewClient sets up a new DHCPv6 client with default values
53-
// for read and write timeouts, for destination address and listening
54-
// address
55-
client := dhcpv6.NewClient()
56-
57-
// Exchange runs a Solicit-Advertise-Request-Reply transaction on the
58-
// specified network interface, and returns a list of DHCPv6 packets
59-
// (a "conversation") and an error if any. Notice that Exchange may
60-
// return a non-empty packet list even if there is an error. This is
61-
// intended, because the transaction may fail at any point, and we
62-
// still want to know what packets were exchanged until then.
63-
// A default Solicit packet will be used during the "conversation",
64-
// which can be manipulated by using modifiers.
65-
conversation, err := client.Exchange("eth0")
66-
67-
// Summary() prints a verbose representation of the exchanged packets.
68-
for _, packet := range conversation {
69-
log.Print(packet.Summary())
70-
}
71-
// error handling is done *after* printing, so we still print the
72-
// exchanged packets if any, as explained above.
73-
if err != nil {
74-
log.Fatal(err)
75-
}
76-
}
77-
```
78-
79-
80-
## DHCPv6 packet crafting and manipulation
81-
82-
```go
83-
package main
84-
85-
import (
86-
"log"
87-
"net"
88-
89-
"github.com/insomniacslk/dhcp/dhcpv6"
90-
"github.com/insomniacslk/dhcp/iana"
91-
)
92-
93-
func main() {
94-
// In this example we create and manipulate a DHCPv6 solicit packet
95-
// and encapsulate it in a relay packet. To to this, we use
96-
// `dhcpv6.DHCPv6Message` and `dhcpv6.DHCPv6Relay`, two structures
97-
// that implement the `dhcpv6.DHCPv6` interface.
98-
// Then print the wire-format representation of the packet.
99-
100-
// Create the DHCPv6 Solicit first, using the interface "eth0"
101-
// to get the MAC address
102-
msg, err := dhcpv6.NewSolicitForInterface("eth0")
103-
if err != nil {
104-
log.Fatal(err)
105-
}
106-
107-
// In this example I want to redact the MAC address of my
108-
// network interface, so instead of replacing it manually,
109-
// I will show how to use modifiers for the purpose.
110-
// A Modifier is simply a function that can be applied on
111-
// a DHCPv6 object to manipulate it. Here we use it to
112-
// replace the MAC address with a dummy one.
113-
// Modifiers can be passed to many functions, for example
114-
// to constructors, `Exchange()`, `Solicit()`, etc. Check
115-
// the source code to know where to use them.
116-
// Existing modifiers are implemented in dhcpv6/modifiers.go .
117-
mac, err := net.ParseMAC("00:fa:ce:b0:0c:00")
118-
if err != nil {
119-
log.Fatal(err)
120-
}
121-
duid := dhcpv6.Duid{
122-
Type: dhcpv6.DUID_LLT,
123-
HwType: iana.HwTypeEthernet,
124-
Time: dhcpv6.GetTime(),
125-
LinkLayerAddr: mac,
126-
}
127-
// As suggested above, an alternative is to call
128-
// dhcpv6.NewSolicitForInterface("eth0", dhcpv6.WithCLientID(duid))
129-
msg = dhcpv6.WithClientID(duid)(msg)
130-
131-
// Now encapsulate the message in a DHCPv6 relay.
132-
// As per RFC3315, the link-address and peer-address have
133-
// to be set by the relay agent. We use dummy values here.
134-
linkAddr := net.ParseIP("2001:0db8::1")
135-
peerAddr := net.ParseIP("2001:0db8::2")
136-
relay, err := dhcpv6.EncapsulateRelay(msg, dhcpv6.MessageTypeRelayForward, linkAddr, peerAddr)
137-
if err != nil {
138-
log.Fatal(err)
139-
}
140-
141-
// Print a verbose representation of the relay packet, that will also
142-
// show a short representation of the inner Solicit message.
143-
// To print a detailed summary of the inner packet, extract it
144-
// first from the relay using `relay.GetInnerMessage()`.
145-
log.Print(relay.Summary())
146-
147-
// And finally, print the bytes that would be sent on the wire
148-
log.Print(relay.ToBytes())
34+
* [dhcpv6 client](examples/client6/)
35+
* [dhcpv6 server](examples/server6/)
36+
* [dhcpv6 packet crafting](examples/packetcrafting6)
37+
* TODO dhcpv4 client
38+
* TODO dhcpv4 server
39+
* TODO dhcpv4 packet crafting
14940

150-
// Note: there are many more functions in the library, check them
151-
// out in the source code. For example, if you want to decode a
152-
// byte stream into a DHCPv6 message or relay, you can use
153-
// `dhcpv6.FromBytes`.
154-
}
155-
```
156-
157-
The output (slightly modified for readability) is
158-
```
159-
$ go run main.go
160-
2018/11/08 13:56:31 DHCPv6Relay
161-
messageType=RELAY-FORW
162-
hopcount=0
163-
linkaddr=2001:db8::1
164-
peeraddr=2001:db8::2
165-
options=[OptRelayMsg{relaymsg=DHCPv6Message(messageType=SOLICIT transactionID=0x9e0242, 4 options)}]
166-
167-
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
168-
0 0 0 0 0 0 0 0 0 0 0 2 0 9 0 52 1 158 2 66 0 1 0 14
169-
0 1 0 1 35 118 253 15 0 250 206 176 12 0 0 6 0 4 0 23
170-
0 24 0 8 0 2 0 0 0 3 0 12 250 206 176 12 0 0 14 16 0
171-
0 21 24]
172-
```
173-
174-
## DHCPv6 server
175-
176-
A DHCPv6 server requires the user to implement a request handler. Basically the
177-
user has to provide the logic to answer to each packet. The library offers a few
178-
facilities to forge response packets, e.g. `NewAdvertiseFromSolicit`,
179-
`NewReplyFromDHCPv6Message` and so on. Look at the source code to see what's
180-
available.
181-
182-
An example server that will print (but not reply to) the client's request is
183-
shown below:
184-
185-
```go
186-
package main
187-
188-
import (
189-
"log"
190-
"net"
191-
192-
"github.com/insomniacslk/dhcp/dhcpv6"
193-
)
194-
195-
func handler(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6) {
196-
// this function will just print the received DHCPv6 message, without replying
197-
log.Print(m.Summary())
198-
}
19941

200-
func main() {
201-
laddr := net.UDPAddr{
202-
IP: net.ParseIP("::1"),
203-
Port: dhcpv6.DefaultServerPort,
204-
}
205-
server := dhcpv6.NewServer(laddr, handler)
206-
207-
defer server.Close()
208-
if err := server.ActivateAndServe(); err != nil {
209-
log.Panic(err)
210-
}
211-
}
212-
```
213-
214-
## DHCPv4 client
215-
216-
TODO
217-
218-
219-
## DHCPv4 packet parsing
220-
221-
TODO
222-
223-
224-
## DHCPv4 server
225-
226-
TODO
42+
See more example code at https://github.com/insomniacslk/exdhcp
22743

22844

22945
# Public projects that use it
@@ -235,3 +51,4 @@ TODO
23551
* Bender from Pinterest, a library for load-testing, https://github.com/pinterest/bender
23652
* FBender from Facebook, a tool for load-testing based on Bender, https://github.com/facebookincubator/fbender
23753
* CoreDHCP, a fast, multithreaded, modular and extensible DHCP server, https://github.com/coredhcp/coredhcp
54+
* u-root, an embeddable root file system, https://github.com/u-root/u-root

dhcpv4/async/client.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
"sync"
88
"time"
99

10-
"github.com/fanliao/go-promise"
10+
promise "github.com/fanliao/go-promise"
1111
"github.com/insomniacslk/dhcp/dhcpv4"
12+
"github.com/insomniacslk/dhcp/dhcpv4/client4"
1213
)
1314

1415
// Default ports
@@ -41,8 +42,8 @@ type Client struct {
4142
// NewClient creates an asynchronous client
4243
func NewClient() *Client {
4344
return &Client{
44-
ReadTimeout: dhcpv4.DefaultReadTimeout,
45-
WriteTimeout: dhcpv4.DefaultWriteTimeout,
45+
ReadTimeout: client4.DefaultReadTimeout,
46+
WriteTimeout: client4.DefaultWriteTimeout,
4647
}
4748
}
4849

@@ -159,7 +160,7 @@ func (c *Client) receive(_ *dhcpv4.DHCPv4) {
159160

160161
c.connection.SetReadDeadline(time.Now().Add(c.ReadTimeout))
161162
for {
162-
buffer := make([]byte, dhcpv4.MaxUDPReceivedPacketSize)
163+
buffer := make([]byte, client4.MaxUDPReceivedPacketSize)
163164
n, _, _, _, err := c.connection.ReadMsgUDP(buffer, oobdata)
164165
if err != nil {
165166
if err, ok := err.(net.Error); !ok || !err.Timeout() {
@@ -196,7 +197,7 @@ func (c *Client) remoteAddr() (*net.UDPAddr, error) {
196197
// Returns a future which resolves to response and error.
197198
func (c *Client) Send(message *dhcpv4.DHCPv4, modifiers ...dhcpv4.Modifier) *promise.Future {
198199
for _, mod := range modifiers {
199-
message = mod(message)
200+
mod(message)
200201
}
201202

202203
p := promise.NewPromise()

dhcpv4/async/client_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/insomniacslk/dhcp/dhcpv4"
10+
"github.com/insomniacslk/dhcp/dhcpv4/client4"
1011
"github.com/stretchr/testify/require"
1112
)
1213

@@ -19,7 +20,7 @@ func serve(ctx context.Context, addr *net.UDPAddr, response *dhcpv4.DHCPv4) erro
1920
go func() {
2021
defer conn.Close()
2122
oobdata := []byte{}
22-
buffer := make([]byte, dhcpv4.MaxUDPReceivedPacketSize)
23+
buffer := make([]byte, client4.MaxUDPReceivedPacketSize)
2324
for {
2425
select {
2526
case <-ctx.Done():
@@ -48,8 +49,8 @@ func serve(ctx context.Context, addr *net.UDPAddr, response *dhcpv4.DHCPv4) erro
4849
func TestNewClient(t *testing.T) {
4950
c := NewClient()
5051
require.NotNil(t, c)
51-
require.Equal(t, c.ReadTimeout, dhcpv4.DefaultReadTimeout)
52-
require.Equal(t, c.ReadTimeout, dhcpv4.DefaultWriteTimeout)
52+
require.Equal(t, c.ReadTimeout, client4.DefaultReadTimeout)
53+
require.Equal(t, c.ReadTimeout, client4.DefaultWriteTimeout)
5354
}
5455

5556
func TestOpenInvalidAddrFailes(t *testing.T) {

dhcpv4/bindtodevice_bsd.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// +build freebsd openbsd netbsd
2+
3+
package dhcpv4
4+
5+
import (
6+
"net"
7+
"syscall"
8+
)
9+
10+
// BindToInterface emulates linux's SO_BINDTODEVICE option for a socket by using
11+
// IP_RECVIF.
12+
func BindToInterface(fd int, ifname string) error {
13+
iface, err := net.InterfaceByName(ifname)
14+
if err != nil {
15+
return err
16+
}
17+
return syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVIF, iface.Index)
18+
}

dhcpv4/bsdp/boot_image.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package bsdp
33
import (
44
"fmt"
55

6+
"github.com/insomniacslk/dhcp/dhcpv4"
67
"github.com/u-root/u-root/pkg/uio"
78
)
89

@@ -18,9 +19,9 @@ const (
1819
// 4 - 127 are reserved for future use.
1920
)
2021

21-
// BootImageTypeToString maps the different BootImageTypes to human-readable
22+
// bootImageTypeToString maps the different BootImageTypes to human-readable
2223
// representations.
23-
var BootImageTypeToString = map[BootImageType]string{
24+
var bootImageTypeToString = map[BootImageType]string{
2425
BootImageTypeMacOS9: "macOS 9",
2526
BootImageTypeMacOSX: "macOS",
2627
BootImageTypeMacOSXServer: "macOS Server",
@@ -35,6 +36,16 @@ type BootImageID struct {
3536
Index uint16
3637
}
3738

39+
// ToBytes implements dhcpv4.OptionValue.
40+
func (b BootImageID) ToBytes() []byte {
41+
return uio.ToBigEndian(b)
42+
}
43+
44+
// FromBytes reads data into b.
45+
func (b *BootImageID) FromBytes(data []byte) error {
46+
return uio.FromBigEndian(b, data)
47+
}
48+
3849
// Marshal writes the binary representation to buf.
3950
func (b BootImageID) Marshal(buf *uio.Lexer) {
4051
var byte0 byte
@@ -55,7 +66,7 @@ func (b BootImageID) String() string {
5566
} else {
5667
s += " uninstallable"
5768
}
58-
t, ok := BootImageTypeToString[b.ImageType]
69+
t, ok := bootImageTypeToString[b.ImageType]
5970
if !ok {
6071
t = "unknown"
6172
}
@@ -99,3 +110,27 @@ func (b *BootImage) Unmarshal(buf *uio.Lexer) error {
99110
b.Name = string(buf.Consume(int(nameLength)))
100111
return buf.Error()
101112
}
113+
114+
func getBootImageID(code dhcpv4.OptionCode, o dhcpv4.Options) *BootImageID {
115+
v := o.Get(code)
116+
if v == nil {
117+
return nil
118+
}
119+
var b BootImageID
120+
if err := uio.FromBigEndian(&b, v); err != nil {
121+
return nil
122+
}
123+
return &b
124+
}
125+
126+
// OptDefaultBootImageID returns a new default boot image ID option as per
127+
// BSDP.
128+
func OptDefaultBootImageID(b BootImageID) dhcpv4.Option {
129+
return dhcpv4.Option{Code: OptionDefaultBootImageID, Value: b}
130+
}
131+
132+
// OptSelectedBootImageID returns a new selected boot image ID option as per
133+
// BSDP.
134+
func OptSelectedBootImageID(b BootImageID) dhcpv4.Option {
135+
return dhcpv4.Option{Code: OptionSelectedBootImageID, Value: b}
136+
}

0 commit comments

Comments
 (0)