Compare commits

...

3 Commits

Author SHA1 Message Date
Adrian Rumpold
d11855bfd0 feat: Turn into Cobra application, add CLI flag for listen interface 2024-01-04 19:27:09 +01:00
Adrian Rumpold
7d3a40987b deps: Upgrade zap to v1.26.0 2024-01-04 18:50:10 +01:00
Adrian Rumpold
001517eb15 chore: Refactor package structure 2024-01-04 18:49:54 +01:00
9 changed files with 414 additions and 320 deletions

330
cmd/root.go Normal file
View File

@@ -0,0 +1,330 @@
package cmd
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"os"
"unsafe"
"github.com/adrianokf/go-dhcp/pkg/leases"
"github.com/adrianokf/go-dhcp/pkg/messages"
"github.com/adrianokf/go-dhcp/pkg/types"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
var listenInterface string
var manager = leases.NewLeaseManager()
var magic = [4]byte{0x63, 0x82, 0x53, 0x63}
func parseOptions(data []byte) messages.Options {
s := zap.S()
i := 0
options := make(messages.Options)
out:
for i < len(data) {
code := messages.OptionCode(data[i])
switch code {
case messages.OptionEnd:
s.Debug("Found END option at offset ", i)
break out
case messages.OptionPad:
s.Debug("Found padding option at offset ", i)
i += 1
continue
}
size := int(data[i+1])
payload := data[i+2 : i+2+size]
s.Debugf("code=%d, size=%d, payload=%x", code, size, payload)
options[code] = messages.Option{Code: code, Data: data[i+1 : i+2+size]}
i += size + 2
}
s.Debugf("Parsed options: ", options)
return options
}
func prepareOffer(request messages.DhcpMessage, lease leases.Lease) messages.DhcpMessage {
var sname [64]byte
var file [128]byte
var siaddr types.Ipv4Addr
copy(sname[:], "go-dhcp-server")
copy(siaddr[:], net.IPv4(10, 0, 0, 1).To4())
dhcp := messages.DhcpMessage{
Op: messages.BOOTREPLY,
Htype: 1, // Ethernet
Hlen: 6, // Ethernet address length
Hops: 0,
Secs: 0,
Flags: request.Flags,
Xid: lease.TransactionId,
Siaddr: siaddr,
Ciaddr: [4]byte{0, 0, 0, 0},
Yiaddr: lease.Address,
Giaddr: request.Giaddr,
Chaddr: request.Chaddr,
Magic: magic,
Sname: sname,
File: file,
}
return dhcp
}
func prepareAck(request messages.DhcpMessage, lease leases.Lease) messages.DhcpMessage {
var sname [64]byte
var file [128]byte
copy(sname[:], "go-dhcp-server")
dhcp := messages.DhcpMessage{
Op: messages.BOOTREPLY,
Htype: 1, // Ethernet
Hlen: 6, // Ethernet address length
Hops: 0,
Secs: 0,
Flags: 0,
Xid: request.Xid,
Siaddr: lease.Address,
Ciaddr: request.Ciaddr,
Yiaddr: lease.Address,
Giaddr: request.Giaddr,
Chaddr: request.Chaddr,
Magic: magic,
Sname: sname,
File: file,
}
return dhcp
}
// sendMessage transmits a DHCP message with options via a UDP connection
// The end option (code 255) is automatically appended and does not need to
// be passed explicitly.
func sendMessage(conn *net.UDPConn, message messages.DhcpMessage, options []messages.Option) error {
buf := make([]byte, 0)
w := bytes.NewBuffer(buf)
err := binary.Write(w, binary.BigEndian, message)
if err != nil {
return err
}
for _, v := range options {
err = w.WriteByte(byte(v.Code))
if err != nil {
return err
}
_, err = w.Write(v.Data)
if err != nil {
return err
}
}
// Automatically add END option, so the caller doesn't
// need to specificy it for every invocation.
err = w.WriteByte(byte(messages.OptionEnd))
if err != nil {
return err
}
msg := w.Bytes()
zap.S().Debug("Msg", msg)
_, err = conn.Write(msg)
if err != nil {
return err
}
return nil
}
func handleOffer(dhcp messages.DhcpMessage, remote *net.UDPAddr) error {
s := zap.S()
lease, err := manager.Request(dhcp.Xid, dhcp.Chaddr)
if err != nil {
panic(err)
}
offer := prepareOffer(dhcp, *lease)
localAddr, _ := net.ResolveUDPAddr("udp", "172.17.0.1:68")
clientAddr, _ := net.ResolveUDPAddr("udp", "255.255.255.255:68")
conn, err := net.DialUDP("udp", localAddr, clientAddr)
if err != nil {
panic(err)
}
defer conn.Close()
s.Info("Sending DHCPOFFER...")
options := []messages.Option{
{
Code: messages.OptionDHCPMessageType,
Data: []byte{1, byte(messages.MessageTypeOffer)},
},
}
sendMessage(conn, offer, options)
return nil
}
func handleAck(dhcp messages.DhcpMessage, remote *net.UDPAddr) error {
s := zap.S()
lease, err := manager.Lookup(dhcp.Xid)
if err != nil {
panic(err)
}
ack := prepareAck(dhcp, *lease)
options := []messages.Option{
{
Code: messages.OptionDHCPMessageType,
Data: []byte{1, byte(messages.MessageTypeAck)},
},
{
Code: messages.OptionIPAddressLeaseTime,
Data: append([]byte{4}, u32tob(3600)...),
},
}
s.Debug("Options: ", options)
localAddr, _ := net.ResolveUDPAddr("udp", "172.17.0.1:68")
clientAddr, _ := net.ResolveUDPAddr("udp", "255.255.255.255:68")
conn, err := net.DialUDP("udp", localAddr, clientAddr)
if err != nil {
panic(err)
}
defer conn.Close()
s.Info("Sending DHCPACK")
sendMessage(conn, ack, options)
lease, err = manager.Request(dhcp.Xid, dhcp.Chaddr)
if err != nil {
return err
}
s.Debug("Found lease", lease)
return nil
}
func handleMsg(data []byte, remote *net.UDPAddr) {
s := zap.S()
s.Debugf("Connection from client %v", remote.IP)
var dhcp messages.DhcpMessage
reader := bytes.NewReader(data)
binary.Read(reader, binary.BigEndian, &dhcp)
dhcp.Debug(s)
if dhcp.Magic != [4]byte{0x63, 0x82, 0x53, 0x63} {
panic("Invalid DHCP magic field")
}
optDataOffset := int(unsafe.Sizeof(dhcp))
optData := data[optDataOffset:]
s.Debug("Raw options data:", optData)
options := parseOptions(optData)
dhcpMsgType := options[messages.OptionDHCPMessageType]
s.Info("DHCP message type ", dhcpMsgType)
switch messages.MessageType(dhcpMsgType.Data[1]) {
case messages.MessageTypeDiscover:
go handleOffer(dhcp, remote)
case messages.MessageTypeRequest:
go handleAck(dhcp, remote)
}
}
func runServer(interfaceName string) {
var addr *net.UDPAddr
if interfaceName == "all" {
zap.L().Debug("Listening on all interfaces")
addr, _ = net.ResolveUDPAddr("udp4", ":67")
} else {
zap.S().Debugf("Listening on interface %s", interfaceName)
iface, err := net.InterfaceByName(interfaceName)
if err != nil {
panic(err)
}
addrs, err := iface.Addrs()
if err != nil {
panic(err)
}
// Find first IPv4 address associated with the interface
var ip net.IP = nil
for _, ifaddr := range addrs {
switch a := ifaddr.(type) {
case *net.IPAddr:
ip = a.IP
case *net.IPNet:
ip = a.IP
}
ip = ip.To4()
if ip != nil {
break
}
}
if ip == nil {
zap.S().Panicf("No IPv4 address associated with interface %s", interfaceName)
}
fmt.Printf("%+v\n", ip)
addr, _ = net.ResolveUDPAddr("udp4", ip.String()+":67")
}
conn, err := net.ListenUDP("udp4", addr)
if err != nil {
panic(err)
}
defer conn.Close()
zap.S().Infof("Listening for incoming connections on %s", addr.String())
for {
buf := make([]byte, 1024)
rlen, remote, err := conn.ReadFrom(buf[:])
if err != nil {
panic(err)
}
// Do stuff with the read bytes
remoteAddr, ok := remote.(*net.UDPAddr)
if !ok {
zap.S().Warn("Not a valid remote IP address: ", remote)
continue
}
go handleMsg(buf[0:rlen], remoteAddr)
}
}
var rootCmd = &cobra.Command{
Use: "go-dhcp",
Short: "go-dhcp is a simple DHCP server written in Go",
Run: func(cmd *cobra.Command, args []string) {
runServer(listenInterface)
},
}
func init() {
// Set up logging
logger, _ := zap.NewDevelopment()
defer logger.Sync() // flushes buffer, if any
zap.ReplaceGlobals(logger)
rootCmd.PersistentFlags().StringVarP(&listenInterface, "interface", "i", "all", "Interface to listen on")
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@@ -1,16 +1,9 @@
package main
package cmd
import (
"encoding/binary"
"net"
)
func int2ip(nn uint32) net.IP {
ip := make(net.IP, 4)
binary.BigEndian.PutUint32(ip, nn)
return ip
}
func u32tob(u uint32) []byte {
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, u)

12
go.mod
View File

@@ -2,9 +2,15 @@ module github.com/adrianokf/go-dhcp
go 1.17
require go.uber.org/zap v1.21.0
require go.uber.org/zap v1.26.0
require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
)
require (
github.com/spf13/cobra v1.8.0
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
)

17
go.sum
View File

@@ -1,8 +1,11 @@
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -10,19 +13,32 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -55,3 +71,4 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

278
main.go
View File

@@ -1,283 +1,9 @@
package main
import (
"bytes"
"encoding/binary"
"net"
"unsafe"
"go.uber.org/zap"
"github.com/adrianokf/go-dhcp/cmd"
)
var manager = NewLeaseManager()
var leases []Lease = make([]Lease, 0)
func (dhcp DhcpMessage) Debug(s *zap.SugaredLogger) {
s.Debugf("op=%x, htype=%x, hlen=%x, hops=%x", dhcp.Op, dhcp.Htype, dhcp.Hlen, dhcp.Hops)
s.Debugf("xid=%x", dhcp.Xid)
s.Debugf("secs=%d, flags=%x", dhcp.Secs, dhcp.Flags)
s.Debugf("ciaddr=%x", dhcp.Ciaddr)
s.Debugf("siaddr=%x", dhcp.Siaddr)
s.Debugf("giaddr=%x", dhcp.Giaddr)
s.Debugf("chaddr=%x", dhcp.Chaddr)
s.Debugf("sname=%s", string(dhcp.Sname[:]))
s.Debugf("file=%s", string(dhcp.File[:]))
s.Debugf("magic=%x", dhcp.Magic)
}
func parseOptions(data []byte) Options {
s := zap.S()
i := 0
options := make(Options)
out:
for i < len(data) {
code := OptionCode(data[i])
switch code {
case OptionEnd:
s.Debug("Found END option at offset ", i)
break out
case OptionPad:
s.Debug("Found padding option at offset ", i)
i += 1
continue
}
size := int(data[i+1])
payload := data[i+2 : i+2+size]
s.Debugf("code=%d, size=%d, payload=%x", code, size, payload)
options[code] = Option{Code: code, Data: data[i+1 : i+2+size]}
i += size + 2
}
s.Debugf("Parsed options: ", options)
return options
}
func prepareOffer(request DhcpMessage, lease Lease) DhcpMessage {
var sname [64]byte
var file [128]byte
var siaddr Ipv4Addr
copy(sname[:], "go-dhcp-server")
copy(siaddr[:], net.IPv4(10, 0, 0, 1).To4())
dhcp := DhcpMessage{
Op: BOOTREPLY,
Htype: 1, // Ethernet
Hlen: 6, // Ethernet address length
Hops: 0,
Secs: 0,
Flags: request.Flags,
Xid: lease.TransactionId,
Siaddr: siaddr,
Ciaddr: [4]byte{0, 0, 0, 0},
Yiaddr: lease.Address,
Giaddr: request.Giaddr,
Chaddr: request.Chaddr,
Magic: magic,
Sname: sname,
File: file,
}
return dhcp
}
func prepareAck(request DhcpMessage, lease Lease) DhcpMessage {
var sname [64]byte
var file [128]byte
copy(sname[:], "go-dhcp-server")
dhcp := DhcpMessage{
Op: BOOTREPLY,
Htype: 1, // Ethernet
Hlen: 6, // Ethernet address length
Hops: 0,
Secs: 0,
Flags: 0,
Xid: request.Xid,
Siaddr: lease.Address,
Ciaddr: request.Ciaddr,
Yiaddr: lease.Address,
Giaddr: request.Giaddr,
Chaddr: request.Chaddr,
Magic: magic,
Sname: sname,
File: file,
}
return dhcp
}
// sendMessage transmits a DHCP message with options via a UDP connection
// The end option (code 255) is automatically appended and does not need to
// be passed explicitly.
func sendMessage(conn *net.UDPConn, message DhcpMessage, options []Option) error {
buf := make([]byte, 0)
w := bytes.NewBuffer(buf)
err := binary.Write(w, binary.BigEndian, message)
if err != nil {
return err
}
for _, v := range options {
err = w.WriteByte(byte(v.Code))
if err != nil {
return err
}
_, err = w.Write(v.Data)
if err != nil {
return err
}
}
// Automatically add END option, so the caller doesn't
// need to specificy it for every invocation.
err = w.WriteByte(byte(OptionEnd))
if err != nil {
return err
}
msg := w.Bytes()
zap.S().Debug("Msg", msg)
_, err = conn.Write(msg)
if err != nil {
return err
}
return nil
}
func handleOffer(dhcp DhcpMessage, remote *net.UDPAddr) error {
s := zap.S()
lease, err := manager.Request(dhcp.Xid, dhcp.Chaddr)
if err != nil {
panic(err)
}
offer := prepareOffer(dhcp, *lease)
localAddr, _ := net.ResolveUDPAddr("udp", "172.17.0.1:68")
clientAddr, _ := net.ResolveUDPAddr("udp", "255.255.255.255:68")
conn, err := net.DialUDP("udp", localAddr, clientAddr)
if err != nil {
panic(err)
}
defer conn.Close()
s.Info("Sending DHCPOFFER...")
options := []Option{
{
Code: OptionDHCPMessageType,
Data: []byte{1, byte(MessageTypeOffer)},
},
}
sendMessage(conn, offer, options)
return nil
}
func handleAck(dhcp DhcpMessage, remote *net.UDPAddr) error {
s := zap.S()
lease, err := manager.Lookup(dhcp.Xid)
if err != nil {
panic(err)
}
ack := prepareAck(dhcp, *lease)
options := []Option{
{
Code: OptionDHCPMessageType,
Data: []byte{1, byte(MessageTypeAck)},
},
{
Code: OptionIPAddressLeaseTime,
Data: append([]byte{4}, u32tob(3600)...),
},
}
s.Debug("Options: ", options)
localAddr, _ := net.ResolveUDPAddr("udp", "172.17.0.1:68")
clientAddr, _ := net.ResolveUDPAddr("udp", "255.255.255.255:68")
conn, err := net.DialUDP("udp", localAddr, clientAddr)
if err != nil {
panic(err)
}
defer conn.Close()
s.Info("Sending DHCPACK")
sendMessage(conn, ack, options)
lease, err = manager.Request(dhcp.Xid, dhcp.Chaddr)
if err != nil {
return err
}
s.Debug("Found lease", lease)
return nil
}
func handleMsg(data []byte, remote *net.UDPAddr) {
s := zap.S()
s.Debugf("Connection from client %v", remote.IP)
var dhcp DhcpMessage
reader := bytes.NewReader(data)
binary.Read(reader, binary.BigEndian, &dhcp)
dhcp.Debug(s)
if dhcp.Magic != [4]byte{0x63, 0x82, 0x53, 0x63} {
panic("Invalid DHCP magic field")
}
optDataOffset := int(unsafe.Sizeof(dhcp))
optData := data[optDataOffset:]
s.Debug("Raw options data:", optData)
options := parseOptions(optData)
dhcpMsgType := options[OptionDHCPMessageType]
s.Info("DHCP message type ", dhcpMsgType)
switch MessageType(dhcpMsgType.Data[1]) {
case MessageTypeDiscover:
go handleOffer(dhcp, remote)
case MessageTypeRequest:
go handleAck(dhcp, remote)
}
}
func main() {
// Set up logging
logger, _ := zap.NewDevelopment()
defer logger.Sync() // flushes buffer, if any
zap.ReplaceGlobals(logger)
addr, _ := net.ResolveUDPAddr("udp4", ":67")
conn, err := net.ListenUDP("udp4", addr)
if err != nil {
panic(err)
}
defer conn.Close()
zap.L().Info("Listening for incoming connections")
for {
buf := make([]byte, 1024)
rlen, remote, err := conn.ReadFrom(buf[:])
if err != nil {
panic(err)
}
// Do stuff with the read bytes
remoteAddr, ok := remote.(*net.UDPAddr)
if !ok {
zap.S().Warn("Not a valid remote IP address: ", remote)
continue
}
go handleMsg(buf[0:rlen], remoteAddr)
}
cmd.Execute()
}

View File

@@ -1,32 +1,48 @@
package main
package leases
import (
"errors"
"fmt"
"time"
"github.com/adrianokf/go-dhcp/pkg/types"
"go.uber.org/zap"
)
type LeaseState uint8
const (
Offered LeaseState = iota
Requested
)
type Lease struct {
TransactionId types.TxId
TTL time.Time
State LeaseState
ClientAddr types.HwAddr
Address types.Ipv4Addr
}
type ILeaseManager interface {
Request(xid TxId, clientAddr HwAddr) (*Lease, error)
Request(xid types.TxId, clientAddr types.HwAddr) (*Lease, error)
Release(l Lease) error
Lookup(xid TxId) error
Lookup(xid types.TxId) error
}
type LeaseManager struct {
leases map[TxId]Lease
leases map[types.TxId]Lease
count byte
}
func NewLeaseManager() *LeaseManager {
m := &LeaseManager{
leases: make(map[TxId]Lease),
leases: make(map[types.TxId]Lease),
}
return m
}
func (m *LeaseManager) Request(xid TxId, clientAddr HwAddr) (*Lease, error) {
func (m *LeaseManager) Request(xid types.TxId, clientAddr types.HwAddr) (*Lease, error) {
zap.S().Debugf("LeaseManager.Request(%v, %v)", xid, clientAddr)
if m.count > 254 {
@@ -58,7 +74,7 @@ func (m LeaseManager) Release(l Lease) error {
return nil
}
func (m LeaseManager) Lookup(xid TxId) (*Lease, error) {
func (m LeaseManager) Lookup(xid types.TxId) (*Lease, error) {
lease, found := m.leases[xid]
if found {
return &lease, nil

View File

@@ -1,9 +1,15 @@
package main
package messages
import (
"time"
"github.com/adrianokf/go-dhcp/pkg/types"
"go.uber.org/zap"
)
type Message interface {
Marshal() ([]byte, error)
Unmarshal([]byte) (Message, error)
}
type OptionCode byte
// DHCPv4 Options
@@ -195,40 +201,35 @@ const (
MessageTypeInform
)
type LeaseState uint8
type TxId [4]byte
type Ipv4Addr [4]byte
type HwAddr [16]byte
var magic = [4]byte{0x63, 0x82, 0x53, 0x63}
type DhcpMessage struct {
Op DhcpOp
Htype MessageType
Hlen byte
Hops byte
Xid TxId
Xid types.TxId
Secs uint16
Flags uint16
Ciaddr Ipv4Addr
Yiaddr Ipv4Addr
Siaddr Ipv4Addr
Giaddr Ipv4Addr
Chaddr HwAddr
Ciaddr types.Ipv4Addr
Yiaddr types.Ipv4Addr
Siaddr types.Ipv4Addr
Giaddr types.Ipv4Addr
Chaddr types.HwAddr
Sname [64]byte
File [128]byte
Magic [4]byte
}
const (
Offered LeaseState = iota
Requested
)
func (dhcp DhcpMessage) Debug(s *zap.SugaredLogger) {
s.Debugf("op=%x, htype=%x, hlen=%x, hops=%x", dhcp.Op, dhcp.Htype, dhcp.Hlen, dhcp.Hops)
s.Debugf("xid=%x", dhcp.Xid)
s.Debugf("secs=%d, flags=%x", dhcp.Secs, dhcp.Flags)
type Lease struct {
TransactionId TxId
TTL time.Time
State LeaseState
ClientAddr HwAddr
Address Ipv4Addr
s.Debugf("ciaddr=%x", dhcp.Ciaddr)
s.Debugf("siaddr=%x", dhcp.Siaddr)
s.Debugf("giaddr=%x", dhcp.Giaddr)
s.Debugf("chaddr=%x", dhcp.Chaddr)
s.Debugf("sname=%s", string(dhcp.Sname[:]))
s.Debugf("file=%s", string(dhcp.File[:]))
s.Debugf("magic=%x", dhcp.Magic)
}

View File

@@ -1,4 +1,4 @@
package main
package messages
type Option struct {
Code OptionCode

5
pkg/types/types.go Normal file
View File

@@ -0,0 +1,5 @@
package types
type TxId [4]byte
type Ipv4Addr [4]byte
type HwAddr [16]byte