dhcp4: switch to github.com/rtr7/dhcp4
All existing DHCPv4 packages I looked at were unappealing for one reason or another, so we’re now using a little helper to glue github.com/google/gopacket and github.com/mdlayher/raw together, which suffices for our use-case and gives us more control.
This commit is contained in:
parent
30e9a6677b
commit
8c55c5ba44
@ -28,6 +28,9 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/jpillora/backoff"
|
||||||
"github.com/rtr7/router7/internal/dhcp4"
|
"github.com/rtr7/router7/internal/dhcp4"
|
||||||
"github.com/rtr7/router7/internal/notify"
|
"github.com/rtr7/router7/internal/notify"
|
||||||
"github.com/rtr7/router7/internal/teelogger"
|
"github.com/rtr7/router7/internal/teelogger"
|
||||||
@ -45,9 +48,15 @@ func logic() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
const ackFn = "/perm/dhcp4/wire/ack"
|
const ackFn = "/perm/dhcp4/wire/ack"
|
||||||
ack, err := ioutil.ReadFile(ackFn)
|
var ack *layers.DHCPv4
|
||||||
|
ackB, err := ioutil.ReadFile(ackFn)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
log.Printf("Loading previous DHCPACK packet from %s: %v", ackFn, err)
|
log.Printf("Loading previous DHCPACK packet from %s: %v", ackFn, err)
|
||||||
|
} else {
|
||||||
|
pkt := gopacket.NewPacket(ackB, layers.LayerTypeDHCPv4, gopacket.DecodeOptions{})
|
||||||
|
if dhcp, ok := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4); ok {
|
||||||
|
ack = dhcp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c := dhcp4.Client{
|
c := dhcp4.Client{
|
||||||
Interface: iface,
|
Interface: iface,
|
||||||
@ -55,12 +64,20 @@ func logic() error {
|
|||||||
}
|
}
|
||||||
usr2 := make(chan os.Signal, 1)
|
usr2 := make(chan os.Signal, 1)
|
||||||
signal.Notify(usr2, syscall.SIGUSR2)
|
signal.Notify(usr2, syscall.SIGUSR2)
|
||||||
|
backoff := backoff.Backoff{
|
||||||
|
Factor: 2,
|
||||||
|
Jitter: true,
|
||||||
|
Min: 10 * time.Second,
|
||||||
|
Max: 1 * time.Minute,
|
||||||
|
}
|
||||||
for c.ObtainOrRenew() {
|
for c.ObtainOrRenew() {
|
||||||
if err := c.Err(); err != nil {
|
if err := c.Err(); err != nil {
|
||||||
log.Printf("Temporary error: %v", err)
|
dur := backoff.Duration()
|
||||||
time.Sleep(1 * time.Second)
|
log.Printf("Temporary error: %v (waiting %v)", err, dur)
|
||||||
|
time.Sleep(dur)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
backoff.Reset()
|
||||||
log.Printf("lease: %+v", c.Config())
|
log.Printf("lease: %+v", c.Config())
|
||||||
b, err := json.Marshal(c.Config())
|
b, err := json.Marshal(c.Config())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -69,7 +86,15 @@ func logic() error {
|
|||||||
if err := ioutil.WriteFile(leasePath, b, 0644); err != nil {
|
if err := ioutil.WriteFile(leasePath, b, 0644); err != nil {
|
||||||
return fmt.Errorf("persisting lease to %s: %v", leasePath, err)
|
return fmt.Errorf("persisting lease to %s: %v", leasePath, err)
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(ackFn, c.Ack, 0644); err != nil {
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
gopacket.SerializeLayers(buf,
|
||||||
|
gopacket.SerializeOptions{
|
||||||
|
FixLengths: true,
|
||||||
|
ComputeChecksums: true,
|
||||||
|
},
|
||||||
|
c.Ack,
|
||||||
|
)
|
||||||
|
if err := ioutil.WriteFile(ackFn, buf.Bytes(), 0644); err != nil {
|
||||||
return fmt.Errorf("persisting DHCPACK to %s: %v", ackFn, err)
|
return fmt.Errorf("persisting DHCPACK to %s: %v", ackFn, err)
|
||||||
}
|
}
|
||||||
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
|
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
|
||||||
|
@ -17,7 +17,6 @@ package dhcp4
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@ -25,10 +24,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/mdlayher/raw"
|
||||||
|
"github.com/rtr7/dhcp4"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/d2g/dhcp4"
|
|
||||||
"github.com/d2g/dhcp4client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -44,15 +43,40 @@ type Client struct {
|
|||||||
|
|
||||||
err error
|
err error
|
||||||
once sync.Once
|
once sync.Once
|
||||||
dhcp *dhcp4client.Client
|
connection net.PacketConn
|
||||||
connection dhcp4client.ConnectionInt
|
|
||||||
hardwareAddr net.HardwareAddr
|
hardwareAddr net.HardwareAddr
|
||||||
|
hostname string
|
||||||
cfg Config
|
cfg Config
|
||||||
timeNow func() time.Time
|
timeNow func() time.Time
|
||||||
generateXID func([]byte)
|
generateXID func() uint32
|
||||||
|
|
||||||
// last DHCPACK packet for renewal/release
|
// last DHCPACK packet for renewal/release
|
||||||
Ack dhcp4.Packet
|
Ack *layers.DHCPv4
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverID(pkt *layers.DHCPv4) []layers.DHCPOption {
|
||||||
|
for _, o := range pkt.Options {
|
||||||
|
if o.Type == layers.DHCPOptServerID {
|
||||||
|
return []layers.DHCPOption{o}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) packet(xid uint32, opts []layers.DHCPOption) *layers.DHCPv4 {
|
||||||
|
return &layers.DHCPv4{
|
||||||
|
Operation: layers.DHCPOpRequest,
|
||||||
|
HardwareType: layers.LinkTypeEthernet,
|
||||||
|
HardwareLen: uint8(len(layers.EthernetBroadcast)),
|
||||||
|
HardwareOpts: 0, // TODO: document
|
||||||
|
Xid: xid,
|
||||||
|
Secs: 0, // TODO: fill in?
|
||||||
|
Flags: 0, // TODO: document
|
||||||
|
ClientHWAddr: c.hardwareAddr,
|
||||||
|
ServerName: nil,
|
||||||
|
File: nil,
|
||||||
|
Options: opts,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObtainOrRenew returns false when encountering a permanent error.
|
// ObtainOrRenew returns false when encountering a permanent error.
|
||||||
@ -62,12 +86,14 @@ func (c *Client) ObtainOrRenew() bool {
|
|||||||
c.timeNow = time.Now
|
c.timeNow = time.Now
|
||||||
}
|
}
|
||||||
if c.connection == nil && c.Interface != nil {
|
if c.connection == nil && c.Interface != nil {
|
||||||
pktsock, err := dhcp4client.NewPacketSock(c.Interface.Index)
|
conn, err := raw.ListenPacket(c.Interface, syscall.ETH_P_IP, &raw.Config{
|
||||||
|
LinuxSockDGRAM: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.err = err
|
c.err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.connection = pktsock
|
c.connection = conn
|
||||||
}
|
}
|
||||||
if c.connection == nil && c.Interface == nil {
|
if c.connection == nil && c.Interface == nil {
|
||||||
c.err = fmt.Errorf("Interface is nil")
|
c.err = fmt.Errorf("Interface is nil")
|
||||||
@ -76,21 +102,20 @@ func (c *Client) ObtainOrRenew() bool {
|
|||||||
if c.hardwareAddr == nil {
|
if c.hardwareAddr == nil {
|
||||||
c.hardwareAddr = c.Interface.HardwareAddr
|
c.hardwareAddr = c.Interface.HardwareAddr
|
||||||
}
|
}
|
||||||
dhcp, err := dhcp4client.New(
|
if c.generateXID == nil {
|
||||||
dhcp4client.HardwareAddr(c.hardwareAddr),
|
c.generateXID = dhcp4.XIDGenerator(c.hardwareAddr)
|
||||||
dhcp4client.Timeout(10*time.Second),
|
}
|
||||||
dhcp4client.Broadcast(false),
|
if c.hostname == "" {
|
||||||
dhcp4client.Connection(c.connection),
|
var utsname unix.Utsname
|
||||||
dhcp4client.GenerateXID(c.generateXID),
|
if err := unix.Uname(&utsname); err != nil {
|
||||||
)
|
log.Fatal(err)
|
||||||
if err != nil {
|
}
|
||||||
c.err = err
|
c.hostname = string(utsname.Nodename[:bytes.IndexByte(utsname.Nodename[:], 0)])
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c.dhcp = dhcp
|
|
||||||
})
|
})
|
||||||
|
// TODO: handle c.err from c.once
|
||||||
c.err = nil // clear previous error
|
c.err = nil // clear previous error
|
||||||
ok, ack, err := c.dhcpRequest()
|
ack, err := c.dhcpRequest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||||
c.err = fmt.Errorf("DHCP: timeout (server(s) unreachable)")
|
c.err = fmt.Errorf("DHCP: timeout (server(s) unreachable)")
|
||||||
@ -99,61 +124,55 @@ func (c *Client) ObtainOrRenew() bool {
|
|||||||
c.err = fmt.Errorf("DHCP: %v", err)
|
c.err = fmt.Errorf("DHCP: %v", err)
|
||||||
return true // temporary error
|
return true // temporary error
|
||||||
}
|
}
|
||||||
if !ok {
|
|
||||||
c.err = fmt.Errorf("received DHCPNAK")
|
|
||||||
return true // temporary error
|
|
||||||
}
|
|
||||||
c.Ack = ack
|
c.Ack = ack
|
||||||
opts := ack.ParseOptions()
|
c.cfg.ClientIP = ack.YourClientIP.String()
|
||||||
|
|
||||||
// DHCPACK (described in RFC2131 4.3.1)
|
|
||||||
// - yiaddr: IP address assigned to client
|
|
||||||
c.cfg.ClientIP = ack.YIAddr().String()
|
|
||||||
|
|
||||||
if b, ok := opts[dhcp4.OptionSubnetMask]; ok {
|
|
||||||
mask := net.IPMask(b)
|
|
||||||
c.cfg.SubnetMask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3])
|
|
||||||
}
|
|
||||||
|
|
||||||
// if b, ok := opts[dhcp4.OptionBroadcastAddress]; ok {
|
|
||||||
// if err := cs.SetBroadcast(net.IP(b)); err != nil {
|
|
||||||
// log.Fatalf("setBroadcast(%v): %v", net.IP(b), err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if b, ok := opts[dhcp4.OptionRouter]; ok {
|
|
||||||
c.cfg.Router = net.IP(b).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := opts[dhcp4.OptionDomainNameServer]; ok {
|
|
||||||
c.cfg.DNS = nil
|
|
||||||
for len(b) > 0 {
|
|
||||||
c.cfg.DNS = append(c.cfg.DNS, net.IP(b[:4]).String())
|
|
||||||
b = b[4:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
leaseTime := 10 * time.Minute // seems sensible as a fallback
|
leaseTime := 10 * time.Minute // seems sensible as a fallback
|
||||||
if b, ok := opts[dhcp4.OptionIPAddressLeaseTime]; ok && len(b) == 4 {
|
|
||||||
leaseTime = parseDHCPDuration(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// As per RFC 2131 section 4.4.5:
|
// As per RFC 2131 section 4.4.5:
|
||||||
// renewal time defaults to 50% of the lease time
|
// renewal time defaults to 50% of the lease time
|
||||||
renewalTime := time.Duration(float64(leaseTime) * 0.5)
|
var renewalTime *time.Duration
|
||||||
if b, ok := opts[dhcp4.OptionRenewalTimeValue]; ok && len(b) == 4 {
|
|
||||||
renewalTime = parseDHCPDuration(b)
|
for _, opt := range dhcp4.ParseOptions(c.Ack.Options) {
|
||||||
|
switch o := opt.(type) {
|
||||||
|
case *dhcp4.OptSubnetMask:
|
||||||
|
c.cfg.SubnetMask = fmt.Sprintf("%d.%d.%d.%d", o.Mask[0], o.Mask[1], o.Mask[2], o.Mask[3])
|
||||||
|
|
||||||
|
case *dhcp4.OptRouter:
|
||||||
|
c.cfg.Router = o.Router.String()
|
||||||
|
|
||||||
|
case *dhcp4.OptDNS:
|
||||||
|
c.cfg.DNS = make([]string, len(o.DNS))
|
||||||
|
for idx, ip := range o.DNS {
|
||||||
|
c.cfg.DNS[idx] = ip.String()
|
||||||
}
|
}
|
||||||
c.cfg.RenewAfter = c.timeNow().Add(renewalTime)
|
|
||||||
|
case *dhcp4.OptLeaseTime:
|
||||||
|
leaseTime = o.LeaseTime
|
||||||
|
|
||||||
|
case *dhcp4.OptT1:
|
||||||
|
renewalTime = &o.T1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if renewalTime == nil {
|
||||||
|
d := time.Duration(float64(leaseTime) * 0.5)
|
||||||
|
renewalTime = &d
|
||||||
|
}
|
||||||
|
c.cfg.RenewAfter = c.timeNow().Add(*renewalTime)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Release() error {
|
func (c *Client) Release() error {
|
||||||
err := c.dhcp.Release(c.Ack)
|
release := c.packet(c.generateXID(), append([]layers.DHCPOption{
|
||||||
c.Ack = nil
|
dhcp4.MessageTypeOpt(layers.DHCPMsgTypeRelease),
|
||||||
|
}, serverID(c.Ack)...))
|
||||||
|
release.ClientIP = c.Ack.YourClientIP
|
||||||
|
if err := dhcp4.Write(c.connection, release); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Ack = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Err() error {
|
func (c *Client) Err() error {
|
||||||
return c.err
|
return c.err
|
||||||
}
|
}
|
||||||
@ -162,78 +181,77 @@ func (c *Client) Config() Config {
|
|||||||
return c.cfg
|
return c.cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDHCPDuration(b []byte) time.Duration {
|
func (c *Client) dhcpRequest() (*layers.DHCPv4, error) {
|
||||||
return time.Duration(binary.BigEndian.Uint32(b)) * time.Second
|
var last *layers.DHCPv4
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) addHostname(p *dhcp4.Packet) {
|
if c.Ack != nil {
|
||||||
var utsname unix.Utsname
|
|
||||||
if err := unix.Uname(&utsname); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
nnb := utsname.Nodename[:bytes.IndexByte(utsname.Nodename[:], 0)]
|
|
||||||
p.AddOption(dhcp4.OptionHostName, nnb)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) addClientId(p *dhcp4.Packet) {
|
|
||||||
id := make([]byte, len(c.hardwareAddr)+1)
|
|
||||||
id[0] = 1 // hardware type ethernet, https://tools.ietf.org/html/rfc1700
|
|
||||||
copy(id[1:], c.hardwareAddr)
|
|
||||||
p.AddOption(dhcp4.OptionClientIdentifier, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) addOptionParameterRequest(p *dhcp4.Packet, opts []dhcp4.OptionCode) {
|
|
||||||
op := make([]byte, len(opts))
|
|
||||||
for i, o := range opts {
|
|
||||||
op[i] = byte(o)
|
|
||||||
}
|
|
||||||
p.AddOption(dhcp4.OptionParameterRequestList, op)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dhcpRequest is a copy of (dhcp4client/Client).Request which
|
|
||||||
// includes the hostname.
|
|
||||||
func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) {
|
|
||||||
var last dhcp4.Packet
|
|
||||||
if c.Ack == nil {
|
|
||||||
discoveryPacket := c.dhcp.DiscoverPacket()
|
|
||||||
c.addHostname(&discoveryPacket)
|
|
||||||
c.addClientId(&discoveryPacket)
|
|
||||||
c.addOptionParameterRequest(&discoveryPacket, []dhcp4.OptionCode{dhcp4.OptionDomainNameServer, dhcp4.OptionRouter, dhcp4.OptionSubnetMask})
|
|
||||||
discoveryPacket.PadToMinSize()
|
|
||||||
|
|
||||||
if err := c.dhcp.SendPacket(discoveryPacket); err != nil {
|
|
||||||
return false, discoveryPacket, err
|
|
||||||
}
|
|
||||||
|
|
||||||
offerPacket, err := c.dhcp.GetOffer(&discoveryPacket)
|
|
||||||
if err != nil {
|
|
||||||
return false, offerPacket, err
|
|
||||||
}
|
|
||||||
last = offerPacket
|
|
||||||
} else {
|
|
||||||
last = c.Ack
|
last = c.Ack
|
||||||
|
} else {
|
||||||
|
discover := c.packet(c.generateXID(), []layers.DHCPOption{
|
||||||
|
dhcp4.MessageTypeOpt(layers.DHCPMsgTypeDiscover),
|
||||||
|
dhcp4.HostnameOpt(c.hostname),
|
||||||
|
dhcp4.ClientIDOpt(layers.LinkTypeEthernet, c.hardwareAddr),
|
||||||
|
dhcp4.ParamsRequestOpt(
|
||||||
|
layers.DHCPOptDNS,
|
||||||
|
layers.DHCPOptRouter,
|
||||||
|
layers.DHCPOptSubnetMask),
|
||||||
|
})
|
||||||
|
if err := dhcp4.Write(c.connection, discover); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
requestPacket := c.dhcp.RequestPacket(&last)
|
// Look for DHCPOFFER packet (TODO: RFC)
|
||||||
c.addHostname(&requestPacket)
|
c.connection.SetDeadline(time.Now().Add(10 * time.Second))
|
||||||
c.addClientId(&requestPacket)
|
for {
|
||||||
c.addOptionParameterRequest(&requestPacket, []dhcp4.OptionCode{dhcp4.OptionDomainNameServer, dhcp4.OptionRouter, dhcp4.OptionSubnetMask})
|
offer, err := dhcp4.Read(c.connection)
|
||||||
requestPacket.PadToMinSize()
|
|
||||||
|
|
||||||
if err := c.dhcp.SendPacket(requestPacket); err != nil {
|
|
||||||
return false, requestPacket, err
|
|
||||||
}
|
|
||||||
|
|
||||||
acknowledgement, err := c.dhcp.GetAcknowledgement(&requestPacket)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, acknowledgement, err
|
return nil, err
|
||||||
|
}
|
||||||
|
if offer == nil {
|
||||||
|
continue // not a DHCPv4 packet
|
||||||
|
}
|
||||||
|
if offer.Xid != discover.Xid {
|
||||||
|
continue // broadcast reply for different DHCP transaction
|
||||||
|
}
|
||||||
|
if !dhcp4.HasMessageType(offer.Options, layers.DHCPMsgTypeOffer) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
last = offer
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acknowledgementOptions := acknowledgement.ParseOptions()
|
// Build a DHCPREQUEST packet:
|
||||||
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
|
request := c.packet(last.Xid, append([]layers.DHCPOption{
|
||||||
c.Ack = nil // start over
|
dhcp4.MessageTypeOpt(layers.DHCPMsgTypeRequest),
|
||||||
return false, acknowledgement, nil
|
dhcp4.RequestIPOpt(last.YourClientIP),
|
||||||
|
dhcp4.HostnameOpt(c.hostname),
|
||||||
|
dhcp4.ClientIDOpt(layers.LinkTypeEthernet, c.hardwareAddr),
|
||||||
|
dhcp4.ParamsRequestOpt(
|
||||||
|
layers.DHCPOptDNS,
|
||||||
|
layers.DHCPOptRouter,
|
||||||
|
layers.DHCPOptSubnetMask),
|
||||||
|
}, serverID(last)...))
|
||||||
|
if err := dhcp4.Write(c.connection, request); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, acknowledgement, nil
|
c.connection.SetDeadline(time.Now().Add(10 * time.Second))
|
||||||
|
for {
|
||||||
|
// Look for DHCPACK packet (described in RFC2131 4.3.1):
|
||||||
|
ack, err := dhcp4.Read(c.connection)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ack == nil {
|
||||||
|
continue // not a DHCPv4 packet
|
||||||
|
}
|
||||||
|
if ack.Xid != request.Xid {
|
||||||
|
continue // broadcast reply for different DHCP transaction
|
||||||
|
}
|
||||||
|
if !dhcp4.HasMessageType(ack.Options, layers.DHCPMsgTypeAck) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return ack, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,9 @@ func TestDHCP4(t *testing.T) {
|
|||||||
hardwareAddr: mac,
|
hardwareAddr: mac,
|
||||||
timeNow: func() time.Time { return now },
|
timeNow: func() time.Time { return now },
|
||||||
connection: conn,
|
connection: conn,
|
||||||
generateXID: func(b []byte) {
|
generateXID: func() uint32 {
|
||||||
if got, want := len(b), 4; got != want {
|
|
||||||
t.Fatalf("github.com/d2g/dhcp4client request unexpected amount of bytes: got %d, want %d", got, want)
|
|
||||||
}
|
|
||||||
// TODO: read the transaction ID from the pcap file
|
// TODO: read the transaction ID from the pcap file
|
||||||
copy(b, []byte{0x77, 0x08, 0xd7, 0x24})
|
return 0x7708d724
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/d2g/dhcp4client"
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
"github.com/google/gopacket/layers"
|
"github.com/google/gopacket/layers"
|
||||||
"github.com/google/gopacket/pcapgo"
|
"github.com/google/gopacket/pcapgo"
|
||||||
@ -182,19 +181,23 @@ type dhcp4conn struct {
|
|||||||
// pcap file input, writing packets to pcap file output (if non-empty).
|
// pcap file input, writing packets to pcap file output (if non-empty).
|
||||||
//
|
//
|
||||||
// See https://en.wikipedia.org/wiki/Pcap for details on pcap.
|
// See https://en.wikipedia.org/wiki/Pcap for details on pcap.
|
||||||
func NewDHCP4Conn(input, output string) (dhcp4client.ConnectionInt, error) {
|
func NewDHCP4Conn(input, output string) (net.PacketConn, error) {
|
||||||
pcapr, pcapw, err := pcapopen(input, output)
|
pcapr, pcapw, err := pcapopen(input, output)
|
||||||
return &dhcp4conn{pcapr, pcapw}, err
|
return &dhcp4conn{pcapr: pcapr, pcapw: pcapw}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *dhcp4conn) LocalAddr() net.Addr { return nil }
|
||||||
func (r *dhcp4conn) Close() error { return nil }
|
func (r *dhcp4conn) Close() error { return nil }
|
||||||
func (r *dhcp4conn) SetReadTimeout(t time.Duration) error { return nil }
|
func (r *dhcp4conn) SetDeadline(t time.Time) error { return nil }
|
||||||
|
func (r *dhcp4conn) SetReadDeadline(t time.Time) error { return nil }
|
||||||
|
func (r *dhcp4conn) SetWriteDeadline(t time.Time) error { return nil }
|
||||||
|
|
||||||
func (r *dhcp4conn) Write(b []byte) error {
|
func (r *dhcp4conn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||||
if r.pcapw == nil {
|
if r.pcapw == nil {
|
||||||
return nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
return pcapwrite(r.pcapw,
|
|
||||||
|
return len(b), pcapwrite(r.pcapw,
|
||||||
&layers.IPv4{
|
&layers.IPv4{
|
||||||
Version: 4,
|
Version: 4,
|
||||||
TTL: 255,
|
TTL: 255,
|
||||||
@ -209,8 +212,20 @@ func (r *dhcp4conn) Write(b []byte) error {
|
|||||||
b)
|
b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *dhcp4conn) ReadFrom() ([]byte, net.IP, error) {
|
func (r *dhcp4conn) ReadFrom(buf []byte) (int, net.Addr, error) {
|
||||||
buf := make([]byte, 9000)
|
data, _, err := r.pcapr.ReadPacketData()
|
||||||
_, ip, err := readFrom(r.pcapr, buf)
|
if err != nil {
|
||||||
return buf, ip, err
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
pkt := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.DecodeOptions{})
|
||||||
|
// TODO: get source IP
|
||||||
|
eth := pkt.Layer(layers.LayerTypeEthernet)
|
||||||
|
if eth == nil {
|
||||||
|
return 0, nil, fmt.Errorf("pcap contained unexpected non-IPv4 packet")
|
||||||
|
}
|
||||||
|
|
||||||
|
//log.Printf("ReadFrom(): %x, %v, pkt = %+v", udp.LayerPayload(), err, pkt)
|
||||||
|
copy(buf, eth.LayerPayload())
|
||||||
|
ip := net.ParseIP("192.168.23.1")
|
||||||
|
return len(eth.LayerPayload()), &net.IPAddr{IP: ip}, nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user