Songmin Li d61e7e5e1f fix(dhcp): can not renew an ip address
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>
2024-10-14 17:42:30 +02:00

242 lines
6.9 KiB
Go

package packet
import (
"net"
"syscall"
"time"
"golang.org/x/net/bpf"
)
const (
// network is the network reported in net.OpError.
network = "packet"
// Operation names which may be returned in net.OpError.
opClose = "close"
opGetsockopt = "getsockopt"
opListen = "listen"
opRawControl = "raw-control"
opRawRead = "raw-read"
opRawWrite = "raw-write"
opRead = "read"
opSet = "set"
opSetsockopt = "setsockopt"
opSyscallConn = "syscall-conn"
opWrite = "write"
)
// Config contains options for a Conn.
type Config struct {
// Filter is an optional assembled BPF filter which can be applied to the
// Conn before bind(2) is called.
//
// The Conn.SetBPF method serves the same purpose once a Conn has already
// been opened, but setting Filter applies the BPF filter before the Conn is
// bound. This ensures that unexpected packets will not be captured before
// the Conn is opened.
Filter []bpf.RawInstruction
}
// Type is a socket type used when creating a Conn with Listen.
//enumcheck:exhaustive
type Type int
// Possible Type values. Note that the zero value is not valid: callers must
// always specify one of Raw or Datagram when calling Listen.
const (
_ Type = iota
Raw
Datagram
)
// Listen opens a packet sockets connection on the specified interface, using
// the given socket type and protocol values.
//
// The socket type must be one of the Type constants: Raw or Datagram.
//
// The Config specifies optional configuration for the Conn. A nil *Config
// applies the default configuration.
func Listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
l, err := listen(ifi, socketType, protocol, cfg)
if err != nil {
return nil, opError(opListen, err, &Addr{HardwareAddr: ifi.HardwareAddr})
}
return l, nil
}
// TODO(mdlayher): we want to support FileConn for advanced use cases, but this
// library would also need a big endian protocol value and an interface index.
// For now we won't bother, but reconsider in the future.
var (
_ net.PacketConn = &Conn{}
_ syscall.Conn = &Conn{}
_ bpf.Setter = &Conn{}
)
// A Conn is an Linux packet sockets (AF_PACKET) implementation of a
// net.PacketConn.
type Conn struct {
c *conn
// Metadata about the local connection.
addr *Addr
ifIndex int
protocol uint16
}
// Close closes the connection.
func (c *Conn) Close() error {
return c.opError(opClose, c.c.Close())
}
// LocalAddr returns the local network address. The Addr returned is shared by
// all invocations of LocalAddr, so do not modify it.
func (c *Conn) LocalAddr() net.Addr { return c.addr }
// ReadFrom implements the net.PacketConn ReadFrom method.
func (c *Conn) ReadFrom(b []byte) (int, net.Addr, error) {
return c.readFrom(b)
}
// WriteTo implements the net.PacketConn WriteTo method.
func (c *Conn) WriteTo(b []byte, addr net.Addr) (int, error) {
return c.writeTo(b, addr)
}
// SetDeadline implements the net.PacketConn SetDeadline method.
func (c *Conn) SetDeadline(t time.Time) error {
return c.opError(opSet, c.c.SetDeadline(t))
}
// SetReadDeadline implements the net.PacketConn SetReadDeadline method.
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.opError(opSet, c.c.SetReadDeadline(t))
}
// SetWriteDeadline implements the net.PacketConn SetWriteDeadline method.
func (c *Conn) SetWriteDeadline(t time.Time) error {
return c.opError(opSet, c.c.SetWriteDeadline(t))
}
// SetBPF attaches an assembled BPF program to the Conn.
func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
return c.opError(opSetsockopt, c.c.SetBPF(filter))
}
// SetPromiscuous enables or disables promiscuous mode on the Conn, allowing it
// to receive traffic that is not addressed to the Conn's network interface.
func (c *Conn) SetPromiscuous(enable bool) error {
return c.setPromiscuous(enable)
}
// Stats contains statistics about a Conn reported by the Linux kernel.
type Stats struct {
// The total number of packets received.
Packets uint32
// The number of packets dropped.
Drops uint32
// The total number of times that a receive queue is frozen. May be zero if
// the Linux kernel is not new enough to support TPACKET_V3 statistics.
FreezeQueueCount uint32
}
// Stats retrieves statistics about the Conn from the Linux kernel.
//
// Note that calling Stats will reset the kernel's internal counters for this
// Conn. If you want to maintain cumulative statistics by polling Stats over
// time, you must do so in your calling code.
func (c *Conn) Stats() (*Stats, error) { return c.stats() }
// SyscallConn returns a raw network connection. This implements the
// syscall.Conn interface.
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
rc, err := c.c.SyscallConn()
if err != nil {
return nil, c.opError(opSyscallConn, err)
}
return &rawConn{
rc: rc,
addr: c.addr,
}, nil
}
// opError is a convenience for the function opError that also passes the local
// and remote addresses of the Conn.
func (c *Conn) opError(op string, err error) error {
return opError(op, err, c.addr)
}
// TODO(mdlayher): see if we can port smarter net.OpError logic into
// socket.Conn's SyscallConn type to avoid the need for this wrapper.
var _ syscall.RawConn = &rawConn{}
// A rawConn is a syscall.RawConn that wraps an internal syscall.RawConn in order
// to produce net.OpError error values.
type rawConn struct {
rc syscall.RawConn
addr *Addr
}
// Control implements the syscall.RawConn Control method.
func (rc *rawConn) Control(fn func(fd uintptr)) error {
return rc.opError(opRawControl, rc.rc.Control(fn))
}
// Control implements the syscall.RawConn Read method.
func (rc *rawConn) Read(fn func(fd uintptr) (done bool)) error {
return rc.opError(opRawRead, rc.rc.Read(fn))
}
// Control implements the syscall.RawConn Write method.
func (rc *rawConn) Write(fn func(fd uintptr) (done bool)) error {
return rc.opError(opRawWrite, rc.rc.Write(fn))
}
// opError is a convenience for the function opError that also passes the
// address of the rawConn.
func (rc *rawConn) opError(op string, err error) error {
return opError(op, err, rc.addr)
}
var _ net.Addr = &Addr{}
// TODO(mdlayher): expose sll_hatype and sll_pkttype on receive Addr only.
// An Addr is a physical-layer address.
type Addr struct {
HardwareAddr net.HardwareAddr
}
// Network returns the address's network name, "packet".
func (a *Addr) Network() string { return network }
// String returns the string representation of an Addr.
func (a *Addr) String() string {
return a.HardwareAddr.String()
}
// opError unpacks err if possible, producing a net.OpError with the input
// parameters in order to implement net.PacketConn. As a convenience, opError
// returns nil if the input error is nil.
func opError(op string, err error, local net.Addr) error {
if err == nil {
return nil
}
// TODO(mdlayher): try to comply with net.PacketConn as best as we can; land
// a nettest.TestPacketConn API upstream.
return &net.OpError{
Op: op,
Net: network,
Addr: local,
Err: err,
}
}