
The dhcp server is systemd-networkd, and the dhcp plugin can request an ip but can not renew it. The systemd-networkd just ignore the renew request. ``` 2024/09/14 21:46:00 no DHCP packet received within 10s 2024/09/14 21:46:00 retrying in 31.529038 seconds 2024/09/14 21:46:42 no DHCP packet received within 10s 2024/09/14 21:46:42 retrying in 63.150490 seconds 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: no more tries 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: renewal time expired, rebinding 2024/09/14 21:47:45 Link "eth1" down. Attempting to set up 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: lease rebound, expiration is 2024-09-14 22:47:45.309270751 +0800 CST m=+11730.048516519 ``` Follow the https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.6, following options must not be sent in renew - Requested IP Address - Server Identifier Since the upstream code has been inactive for 6 years, we should switch to another dhcpv4 library. The new selected one is https://github.com/insomniacslk/dhcp. Signed-off-by: Songmin Li <lisongmin@protonmail.com>
105 lines
2.3 KiB
Go
105 lines
2.3 KiB
Go
package dhcpv4
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/u-root/uio/uio"
|
|
)
|
|
|
|
// Route is a classless static route as per RFC 3442.
|
|
type Route struct {
|
|
// Dest is the destination network.
|
|
Dest *net.IPNet
|
|
|
|
// Router is the router to use for the given destination network.
|
|
Router net.IP
|
|
}
|
|
|
|
// Marshal implements uio.Marshaler.
|
|
//
|
|
// Format described in RFC 3442:
|
|
//
|
|
// <size of mask in number of bits>
|
|
// <destination address, omitting octets that must be zero per mask>
|
|
// <route IP>
|
|
func (r Route) Marshal(buf *uio.Lexer) {
|
|
ones, _ := r.Dest.Mask.Size()
|
|
buf.Write8(uint8(ones))
|
|
|
|
// Only write the non-zero octets.
|
|
dstLen := (ones + 7) / 8
|
|
buf.WriteBytes(r.Dest.IP.To4()[:dstLen])
|
|
|
|
buf.WriteBytes(r.Router.To4())
|
|
}
|
|
|
|
// Unmarshal implements uio.Unmarshaler.
|
|
func (r *Route) Unmarshal(buf *uio.Lexer) error {
|
|
maskSize := buf.Read8()
|
|
if maskSize > 32 {
|
|
return fmt.Errorf("invalid mask length %d in route option", maskSize)
|
|
}
|
|
r.Dest = &net.IPNet{
|
|
IP: make([]byte, net.IPv4len),
|
|
Mask: net.CIDRMask(int(maskSize), 32),
|
|
}
|
|
|
|
dstLen := (maskSize + 7) / 8
|
|
buf.ReadBytes(r.Dest.IP[:dstLen])
|
|
|
|
r.Router = buf.CopyN(net.IPv4len)
|
|
return buf.Error()
|
|
}
|
|
|
|
// String prints the destination network and router IP.
|
|
func (r *Route) String() string {
|
|
return fmt.Sprintf("route to %s via %s", r.Dest, r.Router)
|
|
}
|
|
|
|
// Routes is a collection of network routes.
|
|
type Routes []*Route
|
|
|
|
// FromBytes parses routes from a set of bytes as described by RFC 3442.
|
|
func (r *Routes) FromBytes(p []byte) error {
|
|
buf := uio.NewBigEndianBuffer(p)
|
|
for buf.Has(1) {
|
|
var route Route
|
|
if err := route.Unmarshal(buf); err != nil {
|
|
return err
|
|
}
|
|
*r = append(*r, &route)
|
|
}
|
|
return buf.FinError()
|
|
}
|
|
|
|
// ToBytes marshals a set of routes as described by RFC 3442.
|
|
func (r Routes) ToBytes() []byte {
|
|
buf := uio.NewBigEndianBuffer(nil)
|
|
for _, route := range r {
|
|
route.Marshal(buf)
|
|
}
|
|
return buf.Data()
|
|
}
|
|
|
|
// String prints all routes.
|
|
func (r Routes) String() string {
|
|
s := make([]string, 0, len(r))
|
|
for _, route := range r {
|
|
s = append(s, route.String())
|
|
}
|
|
return strings.Join(s, "; ")
|
|
}
|
|
|
|
// OptClasslessStaticRoute returns a new DHCPv4 Classless Static Route
|
|
// option.
|
|
//
|
|
// The Classless Static Route option is described by RFC 3442.
|
|
func OptClasslessStaticRoute(routes ...*Route) Option {
|
|
return Option{
|
|
Code: OptionClasslessStaticRoute,
|
|
Value: Routes(routes),
|
|
}
|
|
}
|