mirror of
https://github.com/rancher/plugins.git
synced 2025-09-03 14:20:22 +00:00
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>
155 lines
4.2 KiB
Go
155 lines
4.2 KiB
Go
// Copyright 2018 the u-root Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build go1.12 && (darwin || freebsd || linux || netbsd || openbsd || dragonfly)
|
|
// +build go1.12
|
|
// +build darwin freebsd linux netbsd openbsd dragonfly
|
|
|
|
package nclient4
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
|
|
"github.com/mdlayher/packet"
|
|
"github.com/u-root/uio/uio"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
var (
|
|
// BroadcastMac is the broadcast MAC address.
|
|
//
|
|
// Any UDP packet sent to this address is broadcast on the subnet.
|
|
BroadcastMac = net.HardwareAddr([]byte{255, 255, 255, 255, 255, 255})
|
|
)
|
|
|
|
var (
|
|
// ErrUDPAddrIsRequired is an error used when a passed argument is not of type "*net.UDPAddr".
|
|
ErrUDPAddrIsRequired = errors.New("must supply UDPAddr")
|
|
)
|
|
|
|
// NewRawUDPConn returns a UDP connection bound to the interface and port
|
|
// given based on a raw packet socket. All packets are broadcasted.
|
|
//
|
|
// The interface can be completely unconfigured.
|
|
func NewRawUDPConn(iface string, port int) (net.PacketConn, error) {
|
|
ifc, err := net.InterfaceByName(iface)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rawConn, err := packet.Listen(ifc, packet.Datagram, unix.ETH_P_IP, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewBroadcastUDPConn(rawConn, &net.UDPAddr{Port: port}), nil
|
|
}
|
|
|
|
// BroadcastRawUDPConn uses a raw socket to send UDP packets to the broadcast
|
|
// MAC address.
|
|
type BroadcastRawUDPConn struct {
|
|
// PacketConn is a raw DGRAM socket.
|
|
net.PacketConn
|
|
|
|
// boundAddr is the address this RawUDPConn is "bound" to.
|
|
//
|
|
// Calls to ReadFrom will only return packets destined to this address.
|
|
boundAddr *net.UDPAddr
|
|
}
|
|
|
|
// NewBroadcastUDPConn returns a PacketConn that marshals and unmarshals UDP
|
|
// packets, sending them to the broadcast MAC at on rawPacketConn.
|
|
//
|
|
// Calls to ReadFrom will only return packets destined to boundAddr.
|
|
func NewBroadcastUDPConn(rawPacketConn net.PacketConn, boundAddr *net.UDPAddr) net.PacketConn {
|
|
return &BroadcastRawUDPConn{
|
|
PacketConn: rawPacketConn,
|
|
boundAddr: boundAddr,
|
|
}
|
|
}
|
|
|
|
func udpMatch(addr *net.UDPAddr, bound *net.UDPAddr) bool {
|
|
if bound == nil {
|
|
return true
|
|
}
|
|
if bound.IP != nil && !bound.IP.Equal(addr.IP) {
|
|
return false
|
|
}
|
|
return bound.Port == addr.Port
|
|
}
|
|
|
|
// ReadFrom implements net.PacketConn.ReadFrom.
|
|
//
|
|
// ReadFrom reads raw IP packets and will try to match them against
|
|
// upc.boundAddr. Any matching packets are returned via the given buffer.
|
|
func (upc *BroadcastRawUDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
ipHdrMaxLen := ipv4MaximumHeaderSize
|
|
udpHdrLen := udpMinimumSize
|
|
|
|
for {
|
|
pkt := make([]byte, ipHdrMaxLen+udpHdrLen+len(b))
|
|
n, _, err := upc.PacketConn.ReadFrom(pkt)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
if n == 0 {
|
|
return 0, nil, io.EOF
|
|
}
|
|
pkt = pkt[:n]
|
|
buf := uio.NewBigEndianBuffer(pkt)
|
|
|
|
ipHdr := ipv4(buf.Data())
|
|
|
|
if !ipHdr.isValid(n) {
|
|
continue
|
|
}
|
|
|
|
ipHdr = ipv4(buf.Consume(int(ipHdr.headerLength())))
|
|
|
|
if ipHdr.transportProtocol() != udpProtocolNumber {
|
|
continue
|
|
}
|
|
|
|
if !buf.Has(udpHdrLen) {
|
|
continue
|
|
}
|
|
|
|
udpHdr := udp(buf.Consume(udpHdrLen))
|
|
|
|
addr := &net.UDPAddr{
|
|
IP: ipHdr.destinationAddress(),
|
|
Port: int(udpHdr.destinationPort()),
|
|
}
|
|
if !udpMatch(addr, upc.boundAddr) {
|
|
continue
|
|
}
|
|
srcAddr := &net.UDPAddr{
|
|
IP: ipHdr.sourceAddress(),
|
|
Port: int(udpHdr.sourcePort()),
|
|
}
|
|
// Extra padding after end of IP packet should be ignored,
|
|
// if not dhcp option parsing will fail.
|
|
dhcpLen := int(ipHdr.payloadLength()) - udpHdrLen
|
|
return copy(b, buf.Consume(dhcpLen)), srcAddr, nil
|
|
}
|
|
}
|
|
|
|
// WriteTo implements net.PacketConn.WriteTo and broadcasts all packets at the
|
|
// raw socket level.
|
|
//
|
|
// WriteTo wraps the given packet in the appropriate UDP and IP header before
|
|
// sending it on the packet conn.
|
|
func (upc *BroadcastRawUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
|
udpAddr, ok := addr.(*net.UDPAddr)
|
|
if !ok {
|
|
return 0, ErrUDPAddrIsRequired
|
|
}
|
|
|
|
// Using the boundAddr is not quite right here, but it works.
|
|
pkt := udp4pkt(b, udpAddr, upc.boundAddr)
|
|
|
|
// Broadcasting is not always right, but hell, what the ARP do I know.
|
|
return upc.PacketConn.WriteTo(pkt, &packet.Addr{HardwareAddr: BroadcastMac})
|
|
}
|