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 (
"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)

View File

@ -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 {

View File

@ -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
}