Skip to content

Commit

Permalink
Created examples directory and adjusted README
Browse files Browse the repository at this point in the history
  • Loading branch information
insomniacslk committed Jan 28, 2019
1 parent 51ac989 commit 89df477
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 237 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.*.swp
examples/client6/client6
examples/client4/client4
examples/packetcrafting6/packetcrafting6
198 changes: 7 additions & 191 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,199 +31,15 @@ go get -u github.com/insomniacslk/dhcp/dhcpv{4,6}
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())
* [dhcpv6 client](examples/client6/)
* [dhcpv6 server](examples/server6/)
* [dhcpv6 packet crafting](examples/packetcrafting6)
* TODO dhcpv4 client
* TODO dhcpv4 server
* TODO dhcpv4 packet crafting

// 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
See more example code at https://github.com/insomniacslk/exdhcp


# Public projects that use it
Expand Down
46 changes: 0 additions & 46 deletions dhcpv6/server6/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,6 @@ import (
"github.com/insomniacslk/dhcp/dhcpv6"
)

/*
To use the DHCPv6 server code you have to call NewServer with two arguments:
- a handler function, that will be called every time a valid DHCPv6 packet is
received, and
- an address to listen on.
The handler is a function that takes as input a packet connection, that can be
used to reply to the client; a peer address, that identifies the client sending
the request, and the DHCPv6 packet itself. Just implement your custom logic in
the handler.
The address to listen on is used to know IP address, port and optionally the
scope to create and UDP6 socket to listen on for DHCPv6 traffic.
Example program:
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)
}
}
*/

// Handler is a type that defines the handler function to be called every time a
// valid DHCPv6 message is received
type Handler func(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6)
Expand Down
5 changes: 5 additions & 0 deletions examples/client6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# DHCPv6 client

A minimal DHCPv6 client can be implemented in a few lines of code, by using the default
client parameters. The example in [main.go](./main.go) lets you specify the
interface to send packets through, and defaults to `eth0`.
42 changes: 42 additions & 0 deletions examples/client6/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"flag"
"log"

"github.com/insomniacslk/dhcp/dhcpv6/client6"
)

var (
iface = flag.String("i", "eth0", "Interface to configure via DHCPv6")
)

func main() {
flag.Parse()
log.Printf("Starting DHCPv6 client on interface %s", *iface)

// NewClient sets up a new DHCPv6 client with default values
// for read and write timeouts, for destination address and listening
// address
client := client6.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(*iface)

// 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)
}
}
26 changes: 26 additions & 0 deletions examples/packetcrafting6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# DHCPv6 packet crafting

It is easy to parse, create and manipulate DHCPv6 packets. The `DHCPv6`
interface is the central way to move packets around. Two concrete
implementations, `DHCPv6Message` and `DHCPv6Relay` take care of the
details. The example in [main.go](./main.go) shows how to craft a DHCP6
Solicit message with a custom DUID_LLT, encapsulate it in a Relay message,
and print its details.


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]
```
Loading

0 comments on commit 89df477

Please sign in to comment.