mirror of
https://github.com/rancher/plugins.git
synced 2025-09-02 15:29:39 +00:00
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>
This commit is contained in:
committed by
Casey Callendrello
parent
e4950728ce
commit
d61e7e5e1f
248
vendor/github.com/mdlayher/packet/packet_linux.go
generated
vendored
Normal file
248
vendor/github.com/mdlayher/packet/packet_linux.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/josharian/native"
|
||||
"github.com/mdlayher/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// A conn is the net.PacketConn implementation for packet sockets. We can use
|
||||
// socket.Conn directly on Linux to implement most of the necessary methods.
|
||||
type conn = socket.Conn
|
||||
|
||||
// readFrom implements the net.PacketConn ReadFrom method using recvfrom(2).
|
||||
func (c *Conn) readFrom(b []byte) (int, net.Addr, error) {
|
||||
// From net.PacketConn documentation:
|
||||
//
|
||||
// "[ReadFrom] returns the number of bytes read (0 <= n <= len(p)) and any
|
||||
// error encountered. Callers should always process the n > 0 bytes returned
|
||||
// before considering the error err."
|
||||
//
|
||||
// c.opError will return nil if no error, but either way we return all the
|
||||
// information that we have.
|
||||
n, sa, err := c.c.Recvfrom(context.Background(), b, 0)
|
||||
return n, fromSockaddr(sa), c.opError(opRead, err)
|
||||
}
|
||||
|
||||
// writeTo implements the net.PacketConn WriteTo method.
|
||||
func (c *Conn) writeTo(b []byte, addr net.Addr) (int, error) {
|
||||
sa, err := c.toSockaddr("sendto", addr)
|
||||
if err != nil {
|
||||
return 0, c.opError(opWrite, err)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): it's curious that unix.Sendto does not return the number
|
||||
// of bytes actually sent. Fake it for now, but investigate upstream.
|
||||
if err := c.c.Sendto(context.Background(), b, 0, sa); err != nil {
|
||||
return 0, c.opError(opWrite, err)
|
||||
}
|
||||
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// setPromiscuous wraps setsockopt(2) for the unix.PACKET_MR_PROMISC option.
|
||||
func (c *Conn) setPromiscuous(enable bool) error {
|
||||
mreq := unix.PacketMreq{
|
||||
Ifindex: int32(c.ifIndex),
|
||||
Type: unix.PACKET_MR_PROMISC,
|
||||
}
|
||||
|
||||
membership := unix.PACKET_DROP_MEMBERSHIP
|
||||
if enable {
|
||||
membership = unix.PACKET_ADD_MEMBERSHIP
|
||||
}
|
||||
|
||||
return c.opError(
|
||||
opSetsockopt,
|
||||
c.c.SetsockoptPacketMreq(unix.SOL_PACKET, membership, &mreq),
|
||||
)
|
||||
}
|
||||
|
||||
// stats wraps getsockopt(2) for tpacket_stats* types.
|
||||
func (c *Conn) stats() (*Stats, error) {
|
||||
const (
|
||||
level = unix.SOL_PACKET
|
||||
name = unix.PACKET_STATISTICS
|
||||
)
|
||||
|
||||
// Try to fetch V3 statistics first, they contain more detailed information.
|
||||
if stats, err := c.c.GetsockoptTpacketStatsV3(level, name); err == nil {
|
||||
return &Stats{
|
||||
Packets: stats.Packets,
|
||||
Drops: stats.Drops,
|
||||
FreezeQueueCount: stats.Freeze_q_cnt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// There was an error fetching v3 stats, try to fall back.
|
||||
stats, err := c.c.GetsockoptTpacketStats(level, name)
|
||||
if err != nil {
|
||||
return nil, c.opError(opGetsockopt, err)
|
||||
}
|
||||
|
||||
return &Stats{
|
||||
Packets: stats.Packets,
|
||||
Drops: stats.Drops,
|
||||
// FreezeQueueCount is not present.
|
||||
}, nil
|
||||
}
|
||||
|
||||
// listen is the entry point for Listen on Linux.
|
||||
func listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
|
||||
if cfg == nil {
|
||||
// Default configuration.
|
||||
cfg = &Config{}
|
||||
}
|
||||
|
||||
// Convert Type to the matching SOCK_* constant.
|
||||
var typ int
|
||||
switch socketType {
|
||||
case Raw:
|
||||
typ = unix.SOCK_RAW
|
||||
case Datagram:
|
||||
typ = unix.SOCK_DGRAM
|
||||
default:
|
||||
return nil, errors.New("packet: invalid Type value")
|
||||
}
|
||||
|
||||
// Protocol is intentionally zero in call to socket(2); we can set it on
|
||||
// bind(2) instead. Package raw notes: "Do not specify a protocol to avoid
|
||||
// capturing packets which to not match cfg.Filter."
|
||||
c, err := socket.Socket(unix.AF_PACKET, typ, 0, network, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := bind(c, ifi.Index, protocol, cfg)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// bind binds the *socket.Conn to finalize *Conn setup.
|
||||
func bind(c *socket.Conn, ifIndex, protocol int, cfg *Config) (*Conn, error) {
|
||||
if len(cfg.Filter) > 0 {
|
||||
// The caller wants to apply a BPF filter before bind(2).
|
||||
if err := c.SetBPF(cfg.Filter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// packet(7) says we sll_protocol must be in network byte order.
|
||||
pnet, err := htons(protocol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(mdlayher): investigate the possibility of sll_ifindex = 0 because we
|
||||
// could bind to any interface.
|
||||
err = c.Bind(&unix.SockaddrLinklayer{
|
||||
Protocol: pnet,
|
||||
Ifindex: ifIndex,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lsa, err := c.Getsockname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the physical layer address; sll_halen tells us how many bytes of
|
||||
// sll_addr we should treat as valid.
|
||||
lsall := lsa.(*unix.SockaddrLinklayer)
|
||||
addr := make(net.HardwareAddr, lsall.Halen)
|
||||
copy(addr, lsall.Addr[:])
|
||||
|
||||
return &Conn{
|
||||
c: c,
|
||||
|
||||
addr: &Addr{HardwareAddr: addr},
|
||||
ifIndex: ifIndex,
|
||||
protocol: pnet,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// fromSockaddr converts an opaque unix.Sockaddr to *Addr. If sa is nil, it
|
||||
// returns nil. It panics if sa is not of type *unix.SockaddrLinklayer.
|
||||
func fromSockaddr(sa unix.Sockaddr) *Addr {
|
||||
if sa == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sall := sa.(*unix.SockaddrLinklayer)
|
||||
|
||||
return &Addr{
|
||||
// The syscall already allocated sa; just slice into it with the
|
||||
// appropriate length and type conversion rather than making a copy.
|
||||
HardwareAddr: net.HardwareAddr(sall.Addr[:sall.Halen]),
|
||||
}
|
||||
}
|
||||
|
||||
// toSockaddr converts a net.Addr to an opaque unix.Sockaddr. It returns an
|
||||
// error if the fields cannot be packed into a *unix.SockaddrLinklayer.
|
||||
func (c *Conn) toSockaddr(
|
||||
op string,
|
||||
addr net.Addr,
|
||||
) (unix.Sockaddr, error) {
|
||||
// The typical error convention for net.Conn types is
|
||||
// net.OpError(os.SyscallError(syscall.Errno)), so all calls here should
|
||||
// return os.SyscallError(syscall.Errno) so the caller can apply the final
|
||||
// net.OpError wrapper.
|
||||
|
||||
// Ensure the correct Addr type.
|
||||
a, ok := addr.(*Addr)
|
||||
if !ok || a.HardwareAddr == nil {
|
||||
return nil, os.NewSyscallError(op, unix.EINVAL)
|
||||
}
|
||||
|
||||
// Pack Addr and Conn metadata into the appropriate sockaddr fields. From
|
||||
// packet(7):
|
||||
//
|
||||
// "When you send packets it is enough to specify sll_family, sll_addr,
|
||||
// sll_halen, sll_ifindex, and sll_protocol. The other fields should be 0."
|
||||
//
|
||||
// sll_family is set on the conversion to unix.RawSockaddrLinklayer.
|
||||
sa := unix.SockaddrLinklayer{
|
||||
Ifindex: c.ifIndex,
|
||||
Protocol: c.protocol,
|
||||
}
|
||||
|
||||
// Ensure the input address does not exceed the amount of space available;
|
||||
// for example an IPoIB address is 20 bytes.
|
||||
if len(a.HardwareAddr) > len(sa.Addr) {
|
||||
return nil, os.NewSyscallError(op, unix.EINVAL)
|
||||
}
|
||||
|
||||
sa.Halen = uint8(len(a.HardwareAddr))
|
||||
copy(sa.Addr[:], a.HardwareAddr)
|
||||
|
||||
return &sa, nil
|
||||
}
|
||||
|
||||
// htons converts a short (uint16) from host-to-network byte order.
|
||||
func htons(i int) (uint16, error) {
|
||||
if i < 0 || i > math.MaxUint16 {
|
||||
return 0, errors.New("packet: protocol value out of range")
|
||||
}
|
||||
|
||||
// Store as big endian, retrieve as native endian.
|
||||
var b [2]byte
|
||||
binary.BigEndian.PutUint16(b[:], uint16(i))
|
||||
|
||||
return native.Endian.Uint16(b[:]), nil
|
||||
}
|
Reference in New Issue
Block a user