From 8b850844297b7ac17d98ee8fe754a1a1606d4f9e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 15 Jun 2018 17:30:57 +0200 Subject: [PATCH] dhcp4: persist DHCPACK to renew existing lease after reboot --- cmd/dhcp4/dhcp4.go | 18 ++++++++++++++---- integrationdhcpv4_test.go | 19 +++++++++++++++++++ internal/dhcp4/dhcp4.go | 15 +++++++-------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/cmd/dhcp4/dhcp4.go b/cmd/dhcp4/dhcp4.go index 5df9b1c..050516e 100644 --- a/cmd/dhcp4/dhcp4.go +++ b/cmd/dhcp4/dhcp4.go @@ -5,6 +5,7 @@ package main import ( "encoding/json" "flag" + "fmt" "io/ioutil" "net" "os" @@ -21,16 +22,22 @@ import ( var log = teelogger.NewConsole() func logic() error { - const configPath = "/perm/dhcp4/wire/lease.json" - if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil { + const leasePath = "/perm/dhcp4/wire/lease.json" + if err := os.MkdirAll(filepath.Dir(leasePath), 0755); err != nil { return err } iface, err := net.InterfaceByName("uplink0") if err != nil { return err } + const ackFn = "/perm/dhcp4/wire/ack" + ack, err := ioutil.ReadFile(ackFn) + if err != nil && !os.IsNotExist(err) { + log.Printf("Loading previous DHCPACK packet from %s: %v", ackFn, err) + } c := dhcp4.Client{ Interface: iface, + Ack: ack, } usr2 := make(chan os.Signal, 1) signal.Notify(usr2, syscall.SIGUSR2) @@ -45,8 +52,11 @@ func logic() error { if err != nil { return err } - if err := ioutil.WriteFile(configPath, b, 0644); err != nil { - return err + if err := ioutil.WriteFile(leasePath, b, 0644); err != nil { + return fmt.Errorf("persisting lease to %s: %v", leasePath, err) + } + if err := ioutil.WriteFile(ackFn, c.Ack, 0644); err != nil { + return fmt.Errorf("persisting DHCPACK to %s: %v", ackFn, err) } if err := notify.Process("/user/netconfi", syscall.SIGUSR1); err != nil { log.Printf("notifying netconfig: %v", err) diff --git a/integrationdhcpv4_test.go b/integrationdhcpv4_test.go index 163607b..ec4d352 100644 --- a/integrationdhcpv4_test.go +++ b/integrationdhcpv4_test.go @@ -88,6 +88,7 @@ func TestDHCPv4(t *testing.T) { c := dhcp4.Client{ Interface: iface, } + // Obtain first, then renew for i := 0; i < 2; i++ { if !c.ObtainOrRenew() { t.Fatal(c.Err()) @@ -96,6 +97,21 @@ func TestDHCPv4(t *testing.T) { t.Fatal(err) } } + + // Renew once more, but with a new client object (simulating a dhcp4 process + // restart). + ack := c.Ack + c = dhcp4.Client{ + Interface: iface, + Ack: ack, + } + if !c.ObtainOrRenew() { + t.Fatal(c.Err()) + } + if err := c.Err(); err != nil { + t.Fatal(err) + } + cfg := c.Config() t.Logf("cfg = %+v", cfg) if got, want := cfg.Router, "192.168.23.1"; got != want { @@ -121,6 +137,9 @@ func TestDHCPv4(t *testing.T) { "DHCPREQUEST(veth0b) 192.168.23.4 02:73:53:00:ca:fe", "DHCPACK(veth0b) 192.168.23.4 02:73:53:00:ca:fe midna", + "DHCPREQUEST(veth0b) 192.168.23.4 02:73:53:00:ca:fe", + "DHCPACK(veth0b) 192.168.23.4 02:73:53:00:ca:fe midna", + "DHCPRELEASE(veth0b) 192.168.23.4 02:73:53:00:ca:fe", } trimSpace := func(line string) string { diff --git a/internal/dhcp4/dhcp4.go b/internal/dhcp4/dhcp4.go index 3928a60..51becec 100644 --- a/internal/dhcp4/dhcp4.go +++ b/internal/dhcp4/dhcp4.go @@ -38,7 +38,7 @@ type Client struct { generateXID func([]byte) // last DHCPACK packet for renewal/release - ack dhcp4.Packet + Ack dhcp4.Packet } // ObtainOrRenew returns false when encountering a permanent error. @@ -76,7 +76,6 @@ func (c *Client) ObtainOrRenew() bool { c.dhcp = dhcp }) c.err = nil // clear previous error - // TODO: renew if c.ack != nil, fall back if renewal fails ok, ack, err := c.dhcpRequest() if err != nil { if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN { @@ -90,7 +89,7 @@ func (c *Client) ObtainOrRenew() bool { c.err = fmt.Errorf("received DHCPNAK") return true // temporary error } - c.ack = ack + c.Ack = ack opts := ack.ParseOptions() // DHCPACK (described in RFC2131 4.3.1) @@ -136,8 +135,8 @@ func (c *Client) ObtainOrRenew() bool { } func (c *Client) Release() error { - err := c.dhcp.Release(c.ack) - c.ack = nil + err := c.dhcp.Release(c.Ack) + c.Ack = nil return err } @@ -173,7 +172,7 @@ func (c *Client) addClientId(p *dhcp4.Packet) { // includes the hostname. func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) { var last dhcp4.Packet - if c.ack == nil { + if c.Ack == nil { discoveryPacket := c.dhcp.DiscoverPacket() c.addHostname(&discoveryPacket) c.addClientId(&discoveryPacket) @@ -189,7 +188,7 @@ func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) { } last = offerPacket } else { - last = c.ack + last = c.Ack } requestPacket := c.dhcp.RequestPacket(&last) @@ -208,7 +207,7 @@ func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) { acknowledgementOptions := acknowledgement.ParseOptions() if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK { - c.ack = nil // start over + c.Ack = nil // start over return false, acknowledgement, nil }