dhcp4: persist DHCPACK to renew existing lease after reboot

This commit is contained in:
Michael Stapelberg 2018-06-15 17:30:57 +02:00
parent 4f4f286a43
commit 8b85084429
3 changed files with 40 additions and 12 deletions

View File

@ -5,6 +5,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -21,16 +22,22 @@ import (
var log = teelogger.NewConsole() var log = teelogger.NewConsole()
func logic() error { func logic() error {
const configPath = "/perm/dhcp4/wire/lease.json" const leasePath = "/perm/dhcp4/wire/lease.json"
if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(leasePath), 0755); err != nil {
return err return err
} }
iface, err := net.InterfaceByName("uplink0") iface, err := net.InterfaceByName("uplink0")
if err != nil { if err != nil {
return err 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{ c := dhcp4.Client{
Interface: iface, Interface: iface,
Ack: ack,
} }
usr2 := make(chan os.Signal, 1) usr2 := make(chan os.Signal, 1)
signal.Notify(usr2, syscall.SIGUSR2) signal.Notify(usr2, syscall.SIGUSR2)
@ -45,8 +52,11 @@ func logic() error {
if err != nil { if err != nil {
return err return err
} }
if err := ioutil.WriteFile(configPath, b, 0644); err != nil { if err := ioutil.WriteFile(leasePath, b, 0644); err != nil {
return err 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 { if err := notify.Process("/user/netconfi", syscall.SIGUSR1); err != nil {
log.Printf("notifying netconfig: %v", err) log.Printf("notifying netconfig: %v", err)

View File

@ -88,6 +88,7 @@ func TestDHCPv4(t *testing.T) {
c := dhcp4.Client{ c := dhcp4.Client{
Interface: iface, Interface: iface,
} }
// Obtain first, then renew
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
if !c.ObtainOrRenew() { if !c.ObtainOrRenew() {
t.Fatal(c.Err()) t.Fatal(c.Err())
@ -96,6 +97,21 @@ func TestDHCPv4(t *testing.T) {
t.Fatal(err) 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() cfg := c.Config()
t.Logf("cfg = %+v", cfg) t.Logf("cfg = %+v", cfg)
if got, want := cfg.Router, "192.168.23.1"; got != want { 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", "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", "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", "DHCPRELEASE(veth0b) 192.168.23.4 02:73:53:00:ca:fe",
} }
trimSpace := func(line string) string { trimSpace := func(line string) string {

View File

@ -38,7 +38,7 @@ type Client struct {
generateXID func([]byte) generateXID func([]byte)
// last DHCPACK packet for renewal/release // last DHCPACK packet for renewal/release
ack dhcp4.Packet Ack dhcp4.Packet
} }
// ObtainOrRenew returns false when encountering a permanent error. // ObtainOrRenew returns false when encountering a permanent error.
@ -76,7 +76,6 @@ func (c *Client) ObtainOrRenew() bool {
c.dhcp = dhcp c.dhcp = dhcp
}) })
c.err = nil // clear previous error c.err = nil // clear previous error
// TODO: renew if c.ack != nil, fall back if renewal fails
ok, ack, err := c.dhcpRequest() ok, 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 {
@ -90,7 +89,7 @@ func (c *Client) ObtainOrRenew() bool {
c.err = fmt.Errorf("received DHCPNAK") c.err = fmt.Errorf("received DHCPNAK")
return true // temporary error return true // temporary error
} }
c.ack = ack c.Ack = ack
opts := ack.ParseOptions() opts := ack.ParseOptions()
// DHCPACK (described in RFC2131 4.3.1) // DHCPACK (described in RFC2131 4.3.1)
@ -136,8 +135,8 @@ func (c *Client) ObtainOrRenew() bool {
} }
func (c *Client) Release() error { func (c *Client) Release() error {
err := c.dhcp.Release(c.ack) err := c.dhcp.Release(c.Ack)
c.ack = nil c.Ack = nil
return err return err
} }
@ -173,7 +172,7 @@ func (c *Client) addClientId(p *dhcp4.Packet) {
// includes the hostname. // includes the hostname.
func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) { func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) {
var last dhcp4.Packet var last dhcp4.Packet
if c.ack == nil { if c.Ack == nil {
discoveryPacket := c.dhcp.DiscoverPacket() discoveryPacket := c.dhcp.DiscoverPacket()
c.addHostname(&discoveryPacket) c.addHostname(&discoveryPacket)
c.addClientId(&discoveryPacket) c.addClientId(&discoveryPacket)
@ -189,7 +188,7 @@ func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) {
} }
last = offerPacket last = offerPacket
} else { } else {
last = c.ack last = c.Ack
} }
requestPacket := c.dhcp.RequestPacket(&last) requestPacket := c.dhcp.RequestPacket(&last)
@ -208,7 +207,7 @@ func (c *Client) dhcpRequest() (bool, dhcp4.Packet, error) {
acknowledgementOptions := acknowledgement.ParseOptions() acknowledgementOptions := acknowledgement.ParseOptions()
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK { if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
c.ack = nil // start over c.Ack = nil // start over
return false, acknowledgement, nil return false, acknowledgement, nil
} }