mirror of
https://github.com/rancher/plugins.git
synced 2025-09-05 06:45:45 +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
1
vendor/github.com/mdlayher/packet/.gitignore
generated
vendored
Normal file
1
vendor/github.com/mdlayher/packet/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
cmd/packet
|
27
vendor/github.com/mdlayher/packet/CHANGELOG.md
generated
vendored
Normal file
27
vendor/github.com/mdlayher/packet/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# CHANGELOG
|
||||
|
||||
# v1.1.2
|
||||
|
||||
- [Improvement]: updated dependencies, test with Go 1.20.
|
||||
|
||||
# v1.1.1
|
||||
|
||||
- [Bug Fix]: fix test compilation on big endian machines.
|
||||
|
||||
# v1.1.0
|
||||
|
||||
**This is the first release of package packet that only supports Go 1.18+. Users
|
||||
on older versions of Go must use v1.0.0.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go so we can begin using
|
||||
modern versions of `x/sys` and other dependencies.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
**This is the last release of package vsock that supports Go 1.17 and below.**
|
||||
|
||||
- Initial stable commit! The API is mostly a direct translation of the previous
|
||||
`github.com/mdlayher/raw` package APIs, with some updates to make everything
|
||||
focused explicitly on Linux and `AF_PACKET` sockets. Functionally, the two
|
||||
packages are equivalent, and `*raw.Conn` is now backed by `*packet.Conn` in
|
||||
the latest version of the `raw` package.
|
9
vendor/github.com/mdlayher/packet/LICENSE.md
generated
vendored
Normal file
9
vendor/github.com/mdlayher/packet/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (C) 2022 Matt Layher
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
35
vendor/github.com/mdlayher/packet/README.md
generated
vendored
Normal file
35
vendor/github.com/mdlayher/packet/README.md
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# packet [](https://github.com/mdlayher/packet/actions) [](https://pkg.go.dev/github.com/mdlayher/packet) [](https://goreportcard.com/report/github.com/mdlayher/packet)
|
||||
|
||||
Package `packet` provides access to Linux packet sockets (`AF_PACKET`). MIT
|
||||
Licensed.
|
||||
|
||||
## Stability
|
||||
|
||||
See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
|
||||
releases.
|
||||
|
||||
This package has a stable v1 API and any future breaking changes will prompt
|
||||
the release of a new major version. Features and bug fixes will continue to
|
||||
occur in the v1.x.x series.
|
||||
|
||||
This package only supports the two most recent major versions of Go, mirroring
|
||||
Go's own release policy. Older versions of Go may lack critical features and bug
|
||||
fixes which are necessary for this package to function correctly.
|
||||
|
||||
## History
|
||||
|
||||
One of my first major Go networking projects was
|
||||
[`github.com/mdlayher/raw`](https://github.com/mdlayher/raw), which provided
|
||||
access to Linux `AF_PACKET` sockets and *BSD equivalent mechanisms for sending
|
||||
and receiving Ethernet frames. However, the *BSD support languished and I lack
|
||||
the expertise and time to properly maintain code for operating systems I do not
|
||||
use on a daily basis.
|
||||
|
||||
Package `packet` is a successor to package `raw`, but exclusively focused on
|
||||
Linux and `AF_PACKET` sockets. The APIs are nearly identical, but with a few
|
||||
changes which take into account some of the lessons learned while working on
|
||||
`raw`.
|
||||
|
||||
Users are highly encouraged to migrate any existing Linux uses of `raw` to
|
||||
package `packet` instead. This package will be supported for the foreseeable
|
||||
future and will receive continued updates as necessary.
|
2
vendor/github.com/mdlayher/packet/doc.go
generated
vendored
Normal file
2
vendor/github.com/mdlayher/packet/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package packet provides access to Linux packet sockets (AF_PACKET).
|
||||
package packet
|
241
vendor/github.com/mdlayher/packet/packet.go
generated
vendored
Normal file
241
vendor/github.com/mdlayher/packet/packet.go
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
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,
|
||||
}
|
||||
}
|
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
|
||||
}
|
33
vendor/github.com/mdlayher/packet/packet_others.go
generated
vendored
Normal file
33
vendor/github.com/mdlayher/packet/packet_others.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
// errUnimplemented is returned by all functions on non-Linux platforms.
|
||||
var errUnimplemented = fmt.Errorf("packet: not implemented on %s", runtime.GOOS)
|
||||
|
||||
func listen(_ *net.Interface, _ Type, _ int, _ *Config) (*Conn, error) { return nil, errUnimplemented }
|
||||
|
||||
func (*Conn) readFrom(_ []byte) (int, net.Addr, error) { return 0, nil, errUnimplemented }
|
||||
func (*Conn) writeTo(_ []byte, _ net.Addr) (int, error) { return 0, errUnimplemented }
|
||||
func (*Conn) setPromiscuous(_ bool) error { return errUnimplemented }
|
||||
func (*Conn) stats() (*Stats, error) { return nil, errUnimplemented }
|
||||
|
||||
type conn struct{}
|
||||
|
||||
func (*conn) Close() error { return errUnimplemented }
|
||||
func (*conn) SetDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetReadDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetWriteDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetBPF(_ []bpf.RawInstruction) error { return errUnimplemented }
|
||||
func (*conn) SyscallConn() (syscall.RawConn, error) { return nil, errUnimplemented }
|
94
vendor/github.com/mdlayher/socket/CHANGELOG.md
generated
vendored
Normal file
94
vendor/github.com/mdlayher/socket/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.5.1
|
||||
|
||||
- [Improvement]: revert `go.mod` to Go 1.20 to [resolve an issue around Go
|
||||
module version upgrades](https://github.com/mdlayher/socket/issues/13).
|
||||
|
||||
## v0.5.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.21+.
|
||||
Users on older versions of Go must use v0.4.1.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go.
|
||||
- [New API]: add `socket.Conn` wrappers for various `Getsockopt` and
|
||||
`Setsockopt` system calls.
|
||||
|
||||
## v0.4.1
|
||||
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/2a14ceef4da279de1f957c5761fffcc6c87bbd3b):
|
||||
ensure `socket.Conn` can be used with non-socket file descriptors by handling
|
||||
`ENOTSOCK` in the constructor.
|
||||
|
||||
## v0.4.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.18+.
|
||||
Users on older versions of Go must use v0.3.0.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go so we can begin using
|
||||
modern versions of `x/sys` and other dependencies.
|
||||
|
||||
## v0.3.0
|
||||
|
||||
**This is the last release of package socket that supports Go 1.17 and below.**
|
||||
|
||||
- [New API/API change] [PR](https://github.com/mdlayher/socket/pull/8):
|
||||
numerous `socket.Conn` methods now support context cancelation. Future
|
||||
releases will continue adding support as needed.
|
||||
- New `ReadContext` and `WriteContext` methods.
|
||||
- `Connect`, `Recvfrom`, `Recvmsg`, `Sendmsg`, and `Sendto` methods now accept
|
||||
a context.
|
||||
- `Sendto` parameter order was also fixed to match the underlying syscall.
|
||||
|
||||
## v0.2.3
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/a425d96e0f772c053164f8ce4c9c825380a98086):
|
||||
`socket.Conn` has new `Pidfd*` methods for wrapping the `pidfd_*(2)` family of
|
||||
system calls.
|
||||
|
||||
## v0.2.2
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/a2429f1dfe8ec2586df5a09f50ead865276cd027):
|
||||
`socket.Conn` has new `IoctlKCM*` methods for wrapping `ioctl(2)` for `AF_KCM`
|
||||
operations.
|
||||
|
||||
## v0.2.1
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/b18ddbe9caa0e34552b4409a3aa311cb460d2f99):
|
||||
`socket.Conn` has a new `SetsockoptPacketMreq` method for wrapping
|
||||
`setsockopt(2)` for `AF_PACKET` socket options.
|
||||
|
||||
## v0.2.0
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/6e912a68523c45e5fd899239f4b46c402dd856da):
|
||||
`socket.FileConn` can be used to create a `socket.Conn` from an existing
|
||||
`os.File`, which may be provided by systemd socket activation or another
|
||||
external mechanism.
|
||||
- [API change] [commit](https://github.com/mdlayher/socket/commit/66d61f565188c23fe02b24099ddc856d538bf1a7):
|
||||
`socket.Conn.Connect` now returns the `unix.Sockaddr` value provided by
|
||||
`getpeername(2)`, since we have to invoke that system call anyway to verify
|
||||
that a connection to a remote peer was successfully established.
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/b60b2dbe0ac3caff2338446a150083bde8c5c19c):
|
||||
check the correct error from `unix.GetsockoptInt` in the `socket.Conn.Connect`
|
||||
method. Thanks @vcabbage!
|
||||
|
||||
## v0.1.2
|
||||
|
||||
- [Bug Fix]: `socket.Conn.Connect` now properly checks the `SO_ERROR` socket
|
||||
option value after calling `connect(2)` to verify whether or not a connection
|
||||
could successfully be established. This means that `Connect` should now report
|
||||
an error for an `AF_INET` TCP connection refused or `AF_VSOCK` connection
|
||||
reset by peer.
|
||||
- [New API]: add `socket.Conn.Getpeername` for use in `Connect`, but also for
|
||||
use by external callers.
|
||||
|
||||
## v0.1.1
|
||||
|
||||
- [New API]: `socket.Conn` now has `CloseRead`, `CloseWrite`, and `Shutdown`
|
||||
methods.
|
||||
- [Improvement]: internal rework to more robustly handle various errors.
|
||||
|
||||
## v0.1.0
|
||||
|
||||
- Initial unstable release. Most functionality has been developed and ported
|
||||
from package [`netlink`](https://github.com/mdlayher/netlink).
|
9
vendor/github.com/mdlayher/socket/LICENSE.md
generated
vendored
Normal file
9
vendor/github.com/mdlayher/socket/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (C) 2021 Matt Layher
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
vendor/github.com/mdlayher/socket/README.md
generated
vendored
Normal file
23
vendor/github.com/mdlayher/socket/README.md
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# socket [](https://github.com/mdlayher/socket/actions) [](https://pkg.go.dev/github.com/mdlayher/socket) [](https://goreportcard.com/report/github.com/mdlayher/socket)
|
||||
|
||||
Package `socket` provides a low-level network connection type which integrates
|
||||
with Go's runtime network poller to provide asynchronous I/O and deadline
|
||||
support. MIT Licensed.
|
||||
|
||||
This package focuses on UNIX-like operating systems which make use of BSD
|
||||
sockets system call APIs. It is meant to be used as a foundation for the
|
||||
creation of operating system-specific socket packages, for socket families such
|
||||
as Linux's `AF_NETLINK`, `AF_PACKET`, or `AF_VSOCK`. This package should not be
|
||||
used directly in end user applications.
|
||||
|
||||
Any use of package socket should be guarded by build tags, as one would also
|
||||
use when importing the `syscall` or `golang.org/x/sys` packages.
|
||||
|
||||
## Stability
|
||||
|
||||
See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
|
||||
releases.
|
||||
|
||||
This package only supports the two most recent major versions of Go, mirroring
|
||||
Go's own release policy. Older versions of Go may lack critical features and bug
|
||||
fixes which are necessary for this package to function correctly.
|
23
vendor/github.com/mdlayher/socket/accept.go
generated
vendored
Normal file
23
vendor/github.com/mdlayher/socket/accept.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
//go:build !dragonfly && !freebsd && !illumos && !linux
|
||||
// +build !dragonfly,!freebsd,!illumos,!linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const sysAccept = "accept"
|
||||
|
||||
// accept wraps accept(2).
|
||||
func accept(fd, flags int) (int, unix.Sockaddr, error) {
|
||||
if flags != 0 {
|
||||
// These operating systems have no support for flags to accept(2).
|
||||
return 0, nil, fmt.Errorf("socket: Conn.Accept flags are ineffective on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
return unix.Accept(fd)
|
||||
}
|
15
vendor/github.com/mdlayher/socket/accept4.go
generated
vendored
Normal file
15
vendor/github.com/mdlayher/socket/accept4.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
//go:build dragonfly || freebsd || illumos || linux
|
||||
// +build dragonfly freebsd illumos linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const sysAccept = "accept4"
|
||||
|
||||
// accept wraps accept4(2).
|
||||
func accept(fd, flags int) (int, unix.Sockaddr, error) {
|
||||
return unix.Accept4(fd, flags)
|
||||
}
|
894
vendor/github.com/mdlayher/socket/conn.go
generated
vendored
Normal file
894
vendor/github.com/mdlayher/socket/conn.go
generated
vendored
Normal file
@@ -0,0 +1,894 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock in an expected public interface for convenience.
|
||||
var _ interface {
|
||||
io.ReadWriteCloser
|
||||
syscall.Conn
|
||||
SetDeadline(t time.Time) error
|
||||
SetReadDeadline(t time.Time) error
|
||||
SetWriteDeadline(t time.Time) error
|
||||
} = &Conn{}
|
||||
|
||||
// A Conn is a low-level network connection which integrates with Go's runtime
|
||||
// network poller to provide asynchronous I/O and deadline support.
|
||||
//
|
||||
// Many of a Conn's blocking methods support net.Conn deadlines as well as
|
||||
// cancelation via context. Note that passing a context with a deadline set will
|
||||
// override any of the previous deadlines set by calls to the SetDeadline family
|
||||
// of methods.
|
||||
type Conn struct {
|
||||
// Indicates whether or not Conn.Close has been called. Must be accessed
|
||||
// atomically. Atomics definitions must come first in the Conn struct.
|
||||
closed uint32
|
||||
|
||||
// A unique name for the Conn which is also associated with derived file
|
||||
// descriptors such as those created by accept(2).
|
||||
name string
|
||||
|
||||
// facts contains information we have determined about Conn to trigger
|
||||
// alternate behavior in certain functions.
|
||||
facts facts
|
||||
|
||||
// Provides access to the underlying file registered with the runtime
|
||||
// network poller, and arbitrary raw I/O calls.
|
||||
fd *os.File
|
||||
rc syscall.RawConn
|
||||
}
|
||||
|
||||
// facts contains facts about a Conn.
|
||||
type facts struct {
|
||||
// isStream reports whether this is a streaming descriptor, as opposed to a
|
||||
// packet-based descriptor like a UDP socket.
|
||||
isStream bool
|
||||
|
||||
// zeroReadIsEOF reports Whether a zero byte read indicates EOF. This is
|
||||
// false for a message based socket connection.
|
||||
zeroReadIsEOF bool
|
||||
}
|
||||
|
||||
// A Config contains options for a Conn.
|
||||
type Config struct {
|
||||
// NetNS specifies the Linux network namespace the Conn will operate in.
|
||||
// This option is unsupported on other operating systems.
|
||||
//
|
||||
// If set (non-zero), Conn will enter the specified network namespace and an
|
||||
// error will occur in Socket if the operation fails.
|
||||
//
|
||||
// If not set (zero), a best-effort attempt will be made to enter the
|
||||
// network namespace of the calling thread: this means that any changes made
|
||||
// to the calling thread's network namespace will also be reflected in Conn.
|
||||
// If this operation fails (due to lack of permissions or because network
|
||||
// namespaces are disabled by kernel configuration), Socket will not return
|
||||
// an error, and the Conn will operate in the default network namespace of
|
||||
// the process. This enables non-privileged use of Conn in applications
|
||||
// which do not require elevated privileges.
|
||||
//
|
||||
// Entering a network namespace is a privileged operation (root or
|
||||
// CAP_SYS_ADMIN are required), and most applications should leave this set
|
||||
// to 0.
|
||||
NetNS int
|
||||
}
|
||||
|
||||
// High-level methods which provide convenience over raw system calls.
|
||||
|
||||
// Close closes the underlying file descriptor for the Conn, which also causes
|
||||
// all in-flight I/O operations to immediately unblock and return errors. Any
|
||||
// subsequent uses of Conn will result in EBADF.
|
||||
func (c *Conn) Close() error {
|
||||
// The caller has expressed an intent to close the socket, so immediately
|
||||
// increment s.closed to force further calls to result in EBADF before also
|
||||
// closing the file descriptor to unblock any outstanding operations.
|
||||
//
|
||||
// Because other operations simply check for s.closed != 0, we will permit
|
||||
// double Close, which would increment s.closed beyond 1.
|
||||
if atomic.AddUint32(&c.closed, 1) != 1 {
|
||||
// Multiple Close calls.
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.NewSyscallError("close", c.fd.Close())
|
||||
}
|
||||
|
||||
// CloseRead shuts down the reading side of the Conn. Most callers should just
|
||||
// use Close.
|
||||
func (c *Conn) CloseRead() error { return c.Shutdown(unix.SHUT_RD) }
|
||||
|
||||
// CloseWrite shuts down the writing side of the Conn. Most callers should just
|
||||
// use Close.
|
||||
func (c *Conn) CloseWrite() error { return c.Shutdown(unix.SHUT_WR) }
|
||||
|
||||
// Read reads directly from the underlying file descriptor.
|
||||
func (c *Conn) Read(b []byte) (int, error) { return c.fd.Read(b) }
|
||||
|
||||
// ReadContext reads from the underlying file descriptor with added support for
|
||||
// context cancelation.
|
||||
func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) {
|
||||
if c.facts.isStream && len(b) > maxRW {
|
||||
b = b[:maxRW]
|
||||
}
|
||||
|
||||
n, err := readT(c, ctx, "read", func(fd int) (int, error) {
|
||||
return unix.Read(fd, b)
|
||||
})
|
||||
if n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return n, os.NewSyscallError("read", err)
|
||||
}
|
||||
|
||||
// Write writes directly to the underlying file descriptor.
|
||||
func (c *Conn) Write(b []byte) (int, error) { return c.fd.Write(b) }
|
||||
|
||||
// WriteContext writes to the underlying file descriptor with added support for
|
||||
// context cancelation.
|
||||
func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) {
|
||||
var (
|
||||
n, nn int
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, "write", func(fd int) error {
|
||||
max := len(b)
|
||||
if c.facts.isStream && max-nn > maxRW {
|
||||
max = nn + maxRW
|
||||
}
|
||||
|
||||
n, err = unix.Write(fd, b[nn:max])
|
||||
if n > 0 {
|
||||
nn += n
|
||||
}
|
||||
if nn == len(b) {
|
||||
return err
|
||||
}
|
||||
if n == 0 && err == nil {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
if doErr != nil {
|
||||
return 0, doErr
|
||||
}
|
||||
|
||||
return nn, os.NewSyscallError("write", err)
|
||||
}
|
||||
|
||||
// SetDeadline sets both the read and write deadlines associated with the Conn.
|
||||
func (c *Conn) SetDeadline(t time.Time) error { return c.fd.SetDeadline(t) }
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the Conn.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error { return c.fd.SetReadDeadline(t) }
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the Conn.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error { return c.fd.SetWriteDeadline(t) }
|
||||
|
||||
// ReadBuffer gets the size of the operating system's receive buffer associated
|
||||
// with the Conn.
|
||||
func (c *Conn) ReadBuffer() (int, error) {
|
||||
return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF)
|
||||
}
|
||||
|
||||
// WriteBuffer gets the size of the operating system's transmit buffer
|
||||
// associated with the Conn.
|
||||
func (c *Conn) WriteBuffer() (int, error) {
|
||||
return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF)
|
||||
}
|
||||
|
||||
// SetReadBuffer sets the size of the operating system's receive buffer
|
||||
// associated with the Conn.
|
||||
//
|
||||
// When called with elevated privileges on Linux, the SO_RCVBUFFORCE option will
|
||||
// be used to override operating system limits. Otherwise SO_RCVBUF is used
|
||||
// (which obeys operating system limits).
|
||||
func (c *Conn) SetReadBuffer(bytes int) error { return c.setReadBuffer(bytes) }
|
||||
|
||||
// SetWriteBuffer sets the size of the operating system's transmit buffer
|
||||
// associated with the Conn.
|
||||
//
|
||||
// When called with elevated privileges on Linux, the SO_SNDBUFFORCE option will
|
||||
// be used to override operating system limits. Otherwise SO_SNDBUF is used
|
||||
// (which obeys operating system limits).
|
||||
func (c *Conn) SetWriteBuffer(bytes int) error { return c.setWriteBuffer(bytes) }
|
||||
|
||||
// SyscallConn returns a raw network connection. This implements the
|
||||
// syscall.Conn interface.
|
||||
//
|
||||
// SyscallConn is intended for advanced use cases, such as getting and setting
|
||||
// arbitrary socket options using the socket's file descriptor. If possible,
|
||||
// those operations should be performed using methods on Conn instead.
|
||||
//
|
||||
// Once invoked, it is the caller's responsibility to ensure that operations
|
||||
// performed using Conn and the syscall.RawConn do not conflict with each other.
|
||||
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return nil, os.NewSyscallError("syscallconn", unix.EBADF)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): mutex or similar to enforce syscall.RawConn contract of
|
||||
// FD remaining valid for duration of calls?
|
||||
return c.rc, nil
|
||||
}
|
||||
|
||||
// Socket wraps the socket(2) system call to produce a Conn. domain, typ, and
|
||||
// proto are passed directly to socket(2), and name should be a unique name for
|
||||
// the socket type such as "netlink" or "vsock".
|
||||
//
|
||||
// The cfg parameter specifies optional configuration for the Conn. If nil, no
|
||||
// additional configuration will be applied.
|
||||
//
|
||||
// If the operating system supports SOCK_CLOEXEC and SOCK_NONBLOCK, they are
|
||||
// automatically applied to typ to mirror the standard library's socket flag
|
||||
// behaviors.
|
||||
func Socket(domain, typ, proto int, name string, cfg *Config) (*Conn, error) {
|
||||
if cfg == nil {
|
||||
cfg = &Config{}
|
||||
}
|
||||
|
||||
if cfg.NetNS == 0 {
|
||||
// Non-Linux or no network namespace.
|
||||
return socket(domain, typ, proto, name)
|
||||
}
|
||||
|
||||
// Linux only: create Conn in the specified network namespace.
|
||||
return withNetNS(cfg.NetNS, func() (*Conn, error) {
|
||||
return socket(domain, typ, proto, name)
|
||||
})
|
||||
}
|
||||
|
||||
// socket is the internal, cross-platform entry point for socket(2).
|
||||
func socket(domain, typ, proto int, name string) (*Conn, error) {
|
||||
var (
|
||||
fd int
|
||||
err error
|
||||
)
|
||||
|
||||
for {
|
||||
fd, err = unix.Socket(domain, typ|socketFlags, proto)
|
||||
switch {
|
||||
case err == nil:
|
||||
// Some OSes already set CLOEXEC with typ.
|
||||
if !flagCLOEXEC {
|
||||
unix.CloseOnExec(fd)
|
||||
}
|
||||
|
||||
// No error, prepare the Conn.
|
||||
return New(fd, name)
|
||||
case !ready(err):
|
||||
// System call interrupted or not ready, try again.
|
||||
continue
|
||||
case err == unix.EINVAL, err == unix.EPROTONOSUPPORT:
|
||||
// On Linux, SOCK_NONBLOCK and SOCK_CLOEXEC were introduced in
|
||||
// 2.6.27. On FreeBSD, both flags were introduced in FreeBSD 10.
|
||||
// EINVAL and EPROTONOSUPPORT check for earlier versions of these
|
||||
// OSes respectively.
|
||||
//
|
||||
// Mirror what the standard library does when creating file
|
||||
// descriptors: avoid racing a fork/exec with the creation of new
|
||||
// file descriptors, so that child processes do not inherit socket
|
||||
// file descriptors unexpectedly.
|
||||
//
|
||||
// For a more thorough explanation, see similar work in the Go tree:
|
||||
// func sysSocket in net/sock_cloexec.go, as well as the detailed
|
||||
// comment in syscall/exec_unix.go.
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err = unix.Socket(domain, typ, proto)
|
||||
if err != nil {
|
||||
syscall.ForkLock.RUnlock()
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
unix.CloseOnExec(fd)
|
||||
syscall.ForkLock.RUnlock()
|
||||
|
||||
return New(fd, name)
|
||||
default:
|
||||
// Unhandled error.
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FileConn returns a copy of the network connection corresponding to the open
|
||||
// file. It is the caller's responsibility to close the file when finished.
|
||||
// Closing the Conn does not affect the File, and closing the File does not
|
||||
// affect the Conn.
|
||||
func FileConn(f *os.File, name string) (*Conn, error) {
|
||||
// First we'll try to do fctnl(2) with F_DUPFD_CLOEXEC because we can dup
|
||||
// the file descriptor and set the flag in one syscall.
|
||||
fd, err := unix.FcntlInt(f.Fd(), unix.F_DUPFD_CLOEXEC, 0)
|
||||
switch err {
|
||||
case nil:
|
||||
// OK, ready to set up non-blocking I/O.
|
||||
return New(fd, name)
|
||||
case unix.EINVAL:
|
||||
// The kernel rejected our fcntl(2), fall back to separate dup(2) and
|
||||
// setting close on exec.
|
||||
//
|
||||
// Mirror what the standard library does when creating file descriptors:
|
||||
// avoid racing a fork/exec with the creation of new file descriptors,
|
||||
// so that child processes do not inherit socket file descriptors
|
||||
// unexpectedly.
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err := unix.Dup(fd)
|
||||
if err != nil {
|
||||
syscall.ForkLock.RUnlock()
|
||||
return nil, os.NewSyscallError("dup", err)
|
||||
}
|
||||
unix.CloseOnExec(fd)
|
||||
syscall.ForkLock.RUnlock()
|
||||
|
||||
return New(fd, name)
|
||||
default:
|
||||
// Any other errors.
|
||||
return nil, os.NewSyscallError("fcntl", err)
|
||||
}
|
||||
}
|
||||
|
||||
// New wraps an existing file descriptor to create a Conn. name should be a
|
||||
// unique name for the socket type such as "netlink" or "vsock".
|
||||
//
|
||||
// Most callers should use Socket or FileConn to construct a Conn. New is
|
||||
// intended for integrating with specific system calls which provide a file
|
||||
// descriptor that supports asynchronous I/O. The file descriptor is immediately
|
||||
// set to nonblocking mode and registered with Go's runtime network poller for
|
||||
// future I/O operations.
|
||||
//
|
||||
// Unlike FileConn, New does not duplicate the existing file descriptor in any
|
||||
// way. The returned Conn takes ownership of the underlying file descriptor.
|
||||
func New(fd int, name string) (*Conn, error) {
|
||||
// All Conn I/O is nonblocking for integration with Go's runtime network
|
||||
// poller. Depending on the OS this might already be set but it can't hurt
|
||||
// to set it again.
|
||||
if err := unix.SetNonblock(fd, true); err != nil {
|
||||
return nil, os.NewSyscallError("setnonblock", err)
|
||||
}
|
||||
|
||||
// os.NewFile registers the non-blocking file descriptor with the runtime
|
||||
// poller, which is then used for most subsequent operations except those
|
||||
// that require raw I/O via SyscallConn.
|
||||
//
|
||||
// See also: https://golang.org/pkg/os/#NewFile
|
||||
f := os.NewFile(uintptr(fd), name)
|
||||
rc, err := f.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
name: name,
|
||||
fd: f,
|
||||
rc: rc,
|
||||
}
|
||||
|
||||
// Probe the file descriptor for socket settings.
|
||||
sotype, err := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_TYPE)
|
||||
switch {
|
||||
case err == nil:
|
||||
// File is a socket, check its properties.
|
||||
c.facts = facts{
|
||||
isStream: sotype == unix.SOCK_STREAM,
|
||||
zeroReadIsEOF: sotype != unix.SOCK_DGRAM && sotype != unix.SOCK_RAW,
|
||||
}
|
||||
case errors.Is(err, unix.ENOTSOCK):
|
||||
// File is not a socket, treat it as a regular file.
|
||||
c.facts = facts{
|
||||
isStream: true,
|
||||
zeroReadIsEOF: true,
|
||||
}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Low-level methods which provide raw system call access.
|
||||
|
||||
// Accept wraps accept(2) or accept4(2) depending on the operating system, but
|
||||
// returns a Conn for the accepted connection rather than a raw file descriptor.
|
||||
//
|
||||
// If the operating system supports accept4(2) (which allows flags),
|
||||
// SOCK_CLOEXEC and SOCK_NONBLOCK are automatically applied to flags to mirror
|
||||
// the standard library's socket flag behaviors.
|
||||
//
|
||||
// If the operating system only supports accept(2) (which does not allow flags)
|
||||
// and flags is not zero, an error will be returned.
|
||||
//
|
||||
// Accept obeys context cancelation and uses the deadline set on the context to
|
||||
// cancel accepting the next connection. If a deadline is set on ctx, this
|
||||
// deadline will override any previous deadlines set using SetDeadline or
|
||||
// SetReadDeadline. Upon return, the read deadline is cleared.
|
||||
func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
nfd int
|
||||
sa unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) {
|
||||
// Either accept(2) or accept4(2) depending on the OS.
|
||||
nfd, sa, err := accept(fd, flags|socketFlags)
|
||||
return ret{nfd, sa}, err
|
||||
})
|
||||
if err != nil {
|
||||
// internal/poll, context error, or user function error.
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Successfully accepted a connection, wrap it in a Conn for use by the
|
||||
// caller.
|
||||
ac, err := New(r.nfd, c.name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ac, r.sa, nil
|
||||
}
|
||||
|
||||
// Bind wraps bind(2).
|
||||
func (c *Conn) Bind(sa unix.Sockaddr) error {
|
||||
return c.control("bind", func(fd int) error { return unix.Bind(fd, sa) })
|
||||
}
|
||||
|
||||
// Connect wraps connect(2). In order to verify that the underlying socket is
|
||||
// connected to a remote peer, Connect calls getpeername(2) and returns the
|
||||
// unix.Sockaddr from that call.
|
||||
//
|
||||
// Connect obeys context cancelation and uses the deadline set on the context to
|
||||
// cancel connecting to a remote peer. If a deadline is set on ctx, this
|
||||
// deadline will override any previous deadlines set using SetDeadline or
|
||||
// SetWriteDeadline. Upon return, the write deadline is cleared.
|
||||
func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, error) {
|
||||
const op = "connect"
|
||||
|
||||
// TODO(mdlayher): it would seem that trying to connect to unbound vsock
|
||||
// listeners by calling Connect multiple times results in ECONNRESET for the
|
||||
// first and nil error for subsequent calls. Do we need to memoize the
|
||||
// error? Check what the stdlib behavior is.
|
||||
|
||||
var (
|
||||
// Track progress between invocations of the write closure. We don't
|
||||
// have an explicit WaitWrite call like internal/poll does, so we have
|
||||
// to wait until the runtime calls the closure again to indicate we can
|
||||
// write.
|
||||
progress uint32
|
||||
|
||||
// Capture closure sockaddr and error.
|
||||
rsa unix.Sockaddr
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, op, func(fd int) error {
|
||||
if atomic.AddUint32(&progress, 1) == 1 {
|
||||
// First call: initiate connect.
|
||||
return unix.Connect(fd, sa)
|
||||
}
|
||||
|
||||
// Subsequent calls: the runtime network poller indicates fd is
|
||||
// writable. Check for errno.
|
||||
errno, gerr := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_ERROR)
|
||||
if gerr != nil {
|
||||
return gerr
|
||||
}
|
||||
if errno != 0 {
|
||||
// Connection is still not ready or failed. If errno indicates
|
||||
// the socket is not ready, we will wait for the next write
|
||||
// event. Otherwise we propagate this errno back to the as a
|
||||
// permanent error.
|
||||
uerr := unix.Errno(errno)
|
||||
err = uerr
|
||||
return uerr
|
||||
}
|
||||
|
||||
// According to internal/poll, it's possible for the runtime network
|
||||
// poller to spuriously wake us and return errno 0 for SO_ERROR.
|
||||
// Make sure we are actually connected to a peer.
|
||||
peer, err := c.Getpeername()
|
||||
if err != nil {
|
||||
// internal/poll unconditionally goes back to WaitWrite.
|
||||
// Synthesize an error that will do the same for us.
|
||||
return unix.EAGAIN
|
||||
}
|
||||
|
||||
// Connection complete.
|
||||
rsa = peer
|
||||
return nil
|
||||
})
|
||||
if doErr != nil {
|
||||
// internal/poll or context error.
|
||||
return nil, doErr
|
||||
}
|
||||
|
||||
if err == unix.EISCONN {
|
||||
// TODO(mdlayher): is this block obsolete with the addition of the
|
||||
// getsockopt SO_ERROR check above?
|
||||
//
|
||||
// EISCONN is reported if the socket is already established and should
|
||||
// not be treated as an error.
|
||||
// - Darwin reports this for at least TCP sockets
|
||||
// - Linux reports this for at least AF_VSOCK sockets
|
||||
return rsa, nil
|
||||
}
|
||||
|
||||
return rsa, os.NewSyscallError(op, err)
|
||||
}
|
||||
|
||||
// Getsockname wraps getsockname(2).
|
||||
func (c *Conn) Getsockname() (unix.Sockaddr, error) {
|
||||
return controlT(c, "getsockname", unix.Getsockname)
|
||||
}
|
||||
|
||||
// Getpeername wraps getpeername(2).
|
||||
func (c *Conn) Getpeername() (unix.Sockaddr, error) {
|
||||
return controlT(c, "getpeername", unix.Getpeername)
|
||||
}
|
||||
|
||||
// GetsockoptICMPv6Filter wraps getsockopt(2) for *unix.ICMPv6Filter values.
|
||||
func (c *Conn) GetsockoptICMPv6Filter(level, opt int) (*unix.ICMPv6Filter, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (*unix.ICMPv6Filter, error) {
|
||||
return unix.GetsockoptICMPv6Filter(fd, level, opt)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptInt wraps getsockopt(2) for integer values.
|
||||
func (c *Conn) GetsockoptInt(level, opt int) (int, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (int, error) {
|
||||
return unix.GetsockoptInt(fd, level, opt)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptString wraps getsockopt(2) for string values.
|
||||
func (c *Conn) GetsockoptString(level, opt int) (string, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (string, error) {
|
||||
return unix.GetsockoptString(fd, level, opt)
|
||||
})
|
||||
}
|
||||
|
||||
// Listen wraps listen(2).
|
||||
func (c *Conn) Listen(n int) error {
|
||||
return c.control("listen", func(fd int) error { return unix.Listen(fd, n) })
|
||||
}
|
||||
|
||||
// Recvmsg wraps recvmsg(2).
|
||||
func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int, int, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
n, oobn, recvflags int
|
||||
from unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) {
|
||||
n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags)
|
||||
return ret{n, oobn, recvflags, from}, err
|
||||
})
|
||||
if r.n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, 0, 0, nil, io.EOF
|
||||
}
|
||||
|
||||
return r.n, r.oobn, r.recvflags, r.from, err
|
||||
}
|
||||
|
||||
// Recvfrom wraps recvfrom(2).
|
||||
func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
n int
|
||||
addr unix.Sockaddr
|
||||
}
|
||||
|
||||
out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) {
|
||||
n, addr, err := unix.Recvfrom(fd, p, flags)
|
||||
return ret{n, addr}, err
|
||||
})
|
||||
if out.n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
return out.n, out.addr, err
|
||||
}
|
||||
|
||||
// Sendmsg wraps sendmsg(2).
|
||||
func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) {
|
||||
return writeT(c, ctx, "sendmsg", func(fd int) (int, error) {
|
||||
return unix.SendmsgN(fd, p, oob, to, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// Sendto wraps sendto(2).
|
||||
func (c *Conn) Sendto(ctx context.Context, p []byte, flags int, to unix.Sockaddr) error {
|
||||
return c.write(ctx, "sendto", func(fd int) error {
|
||||
return unix.Sendto(fd, p, flags, to)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptICMPv6Filter wraps setsockopt(2) for *unix.ICMPv6Filter values.
|
||||
func (c *Conn) SetsockoptICMPv6Filter(level, opt int, filter *unix.ICMPv6Filter) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptICMPv6Filter(fd, level, opt, filter)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptInt wraps setsockopt(2) for integer values.
|
||||
func (c *Conn) SetsockoptInt(level, opt, value int) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptInt(fd, level, opt, value)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptString wraps setsockopt(2) for string values.
|
||||
func (c *Conn) SetsockoptString(level, opt int, value string) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptString(fd, level, opt, value)
|
||||
})
|
||||
}
|
||||
|
||||
// Shutdown wraps shutdown(2).
|
||||
func (c *Conn) Shutdown(how int) error {
|
||||
return c.control("shutdown", func(fd int) error { return unix.Shutdown(fd, how) })
|
||||
}
|
||||
|
||||
// Conn low-level read/write/control functions. These functions mirror the
|
||||
// syscall.RawConn APIs but the input closures return errors rather than
|
||||
// booleans.
|
||||
|
||||
// read wraps readT to execute a function and capture its error result. This is
|
||||
// a convenience wrapper for functions which don't return any extra values.
|
||||
func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := readT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// write executes f, a write function, against the associated file descriptor.
|
||||
// op is used to create an *os.SyscallError if the file descriptor is closed.
|
||||
func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := writeT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// readT executes c.rc.Read for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: read,
|
||||
Op: op,
|
||||
Do: f,
|
||||
})
|
||||
}
|
||||
|
||||
// writeT executes c.rc.Write for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: write,
|
||||
Op: op,
|
||||
Do: f,
|
||||
})
|
||||
}
|
||||
|
||||
// readWrite indicates if an operation intends to read or write.
|
||||
type readWrite bool
|
||||
|
||||
// Possible readWrite values.
|
||||
const (
|
||||
read readWrite = false
|
||||
write readWrite = true
|
||||
)
|
||||
|
||||
// An rwContext provides arguments to rwT.
|
||||
type rwContext[T any] struct {
|
||||
// The caller's context passed for cancelation.
|
||||
Context context.Context
|
||||
|
||||
// The type of an operation: read or write.
|
||||
Type readWrite
|
||||
|
||||
// The name of the operation used in errors.
|
||||
Op string
|
||||
|
||||
// The actual function to perform.
|
||||
Do func(fd int) (T, error)
|
||||
}
|
||||
|
||||
// rwT executes c.rc.Read or c.rc.Write (depending on the value of rw.Type) for
|
||||
// rw.Op using the input function, returning a newly allocated result T.
|
||||
//
|
||||
// It obeys context cancelation and the rw.Context must not be nil.
|
||||
func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
// If the file descriptor is already closed, do nothing.
|
||||
return *new(T), os.NewSyscallError(rw.Op, unix.EBADF)
|
||||
}
|
||||
|
||||
if err := rw.Context.Err(); err != nil {
|
||||
// Early exit due to context cancel.
|
||||
return *new(T), os.NewSyscallError(rw.Op, err)
|
||||
}
|
||||
|
||||
var (
|
||||
// The read or write function used to access the runtime network poller.
|
||||
poll func(func(uintptr) bool) error
|
||||
|
||||
// The read or write function used to set the matching deadline.
|
||||
deadline func(time.Time) error
|
||||
)
|
||||
|
||||
if rw.Type == write {
|
||||
poll = c.rc.Write
|
||||
deadline = c.SetWriteDeadline
|
||||
} else {
|
||||
poll = c.rc.Read
|
||||
deadline = c.SetReadDeadline
|
||||
}
|
||||
|
||||
var (
|
||||
// Whether or not the context carried a deadline we are actively using
|
||||
// for cancelation.
|
||||
setDeadline bool
|
||||
|
||||
// Signals for the cancelation watcher goroutine.
|
||||
wg sync.WaitGroup
|
||||
doneC = make(chan struct{})
|
||||
|
||||
// Atomic: reports whether we have to disarm the deadline.
|
||||
needDisarm atomic.Bool
|
||||
)
|
||||
|
||||
// On cancel, clean up the watcher.
|
||||
defer func() {
|
||||
close(doneC)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
if d, ok := rw.Context.Deadline(); ok {
|
||||
// The context has an explicit deadline. We will use it for cancelation
|
||||
// but disarm it after poll for the next call.
|
||||
if err := deadline(d); err != nil {
|
||||
return *new(T), err
|
||||
}
|
||||
setDeadline = true
|
||||
needDisarm.Store(true)
|
||||
} else {
|
||||
// The context does not have an explicit deadline. We have to watch for
|
||||
// cancelation so we can propagate that signal to immediately unblock
|
||||
// the runtime network poller.
|
||||
//
|
||||
// TODO(mdlayher): is it possible to detect a background context vs a
|
||||
// context with possible future cancel?
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
select {
|
||||
case <-rw.Context.Done():
|
||||
// Cancel the operation. Make the caller disarm after poll
|
||||
// returns.
|
||||
needDisarm.Store(true)
|
||||
_ = deadline(time.Unix(0, 1))
|
||||
case <-doneC:
|
||||
// Nothing to do.
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var (
|
||||
t T
|
||||
err error
|
||||
)
|
||||
|
||||
pollErr := poll(func(fd uintptr) bool {
|
||||
t, err = rw.Do(int(fd))
|
||||
return ready(err)
|
||||
})
|
||||
|
||||
if needDisarm.Load() {
|
||||
_ = deadline(time.Time{})
|
||||
}
|
||||
|
||||
if pollErr != nil {
|
||||
if rw.Context.Err() != nil || (setDeadline && errors.Is(pollErr, os.ErrDeadlineExceeded)) {
|
||||
// The caller canceled the operation or we set a deadline internally
|
||||
// and it was reached.
|
||||
//
|
||||
// Unpack a plain context error. We wait for the context to be done
|
||||
// to synchronize state externally. Otherwise we have noticed I/O
|
||||
// timeout wakeups when we set a deadline but the context was not
|
||||
// yet marked done.
|
||||
<-rw.Context.Done()
|
||||
return *new(T), os.NewSyscallError(rw.Op, rw.Context.Err())
|
||||
}
|
||||
|
||||
// Error from syscall.RawConn methods. Conventionally the standard
|
||||
// library does not wrap internal/poll errors in os.NewSyscallError.
|
||||
return *new(T), pollErr
|
||||
}
|
||||
|
||||
// Result from user function.
|
||||
return t, os.NewSyscallError(rw.Op, err)
|
||||
}
|
||||
|
||||
// control executes Conn.control for op using the input function.
|
||||
func (c *Conn) control(op string, f func(fd int) error) error {
|
||||
_, err := controlT(c, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// controlT executes c.rc.Control for op using the input function, returning a
|
||||
// newly allocated result T.
|
||||
func controlT[T any](c *Conn, op string, f func(fd int) (T, error)) (T, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
// If the file descriptor is already closed, do nothing.
|
||||
return *new(T), os.NewSyscallError(op, unix.EBADF)
|
||||
}
|
||||
|
||||
var (
|
||||
t T
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.rc.Control(func(fd uintptr) {
|
||||
// Repeatedly attempt the syscall(s) invoked by f until completion is
|
||||
// indicated by the return value of ready or the context is canceled.
|
||||
//
|
||||
// The last values for t and err are captured outside of the closure for
|
||||
// use when the loop breaks.
|
||||
for {
|
||||
t, err = f(int(fd))
|
||||
if ready(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
if doErr != nil {
|
||||
// Error from syscall.RawConn methods. Conventionally the standard
|
||||
// library does not wrap internal/poll errors in os.NewSyscallError.
|
||||
return *new(T), doErr
|
||||
}
|
||||
|
||||
// Result from user function.
|
||||
return t, os.NewSyscallError(op, err)
|
||||
}
|
||||
|
||||
// ready indicates readiness based on the value of err.
|
||||
func ready(err error) bool {
|
||||
switch err {
|
||||
case unix.EAGAIN, unix.EINPROGRESS, unix.EINTR:
|
||||
// When a socket is in non-blocking mode, we might see a variety of errors:
|
||||
// - EAGAIN: most common case for a socket read not being ready
|
||||
// - EINPROGRESS: reported by some sockets when first calling connect
|
||||
// - EINTR: system call interrupted, more frequently occurs in Go 1.14+
|
||||
// because goroutines can be asynchronously preempted
|
||||
//
|
||||
// Return false to let the poller wait for readiness. See the source code
|
||||
// for internal/poll.FD.RawRead for more details.
|
||||
return false
|
||||
default:
|
||||
// Ready regardless of whether there was an error or no error.
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Darwin and FreeBSD can't read or write 2GB+ files at a time,
|
||||
// even on 64-bit systems.
|
||||
// The same is true of socket implementations on many systems.
|
||||
// See golang.org/issue/7812 and golang.org/issue/16266.
|
||||
// Use 1GB instead of, say, 2GB-1, to keep subsequent reads aligned.
|
||||
const maxRW = 1 << 30
|
118
vendor/github.com/mdlayher/socket/conn_linux.go
generated
vendored
Normal file
118
vendor/github.com/mdlayher/socket/conn_linux.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IoctlKCMClone wraps ioctl(2) for unix.KCMClone values, but returns a Conn
|
||||
// rather than a raw file descriptor.
|
||||
func (c *Conn) IoctlKCMClone() (*Conn, error) {
|
||||
info, err := controlT(c, "ioctl", unix.IoctlKCMClone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successful clone, wrap in a Conn for use by the caller.
|
||||
return New(int(info.Fd), c.name)
|
||||
}
|
||||
|
||||
// IoctlKCMAttach wraps ioctl(2) for unix.KCMAttach values.
|
||||
func (c *Conn) IoctlKCMAttach(info unix.KCMAttach) error {
|
||||
return c.control("ioctl", func(fd int) error {
|
||||
return unix.IoctlKCMAttach(fd, info)
|
||||
})
|
||||
}
|
||||
|
||||
// IoctlKCMUnattach wraps ioctl(2) for unix.KCMUnattach values.
|
||||
func (c *Conn) IoctlKCMUnattach(info unix.KCMUnattach) error {
|
||||
return c.control("ioctl", func(fd int) error {
|
||||
return unix.IoctlKCMUnattach(fd, info)
|
||||
})
|
||||
}
|
||||
|
||||
// PidfdGetfd wraps pidfd_getfd(2) for a Conn which wraps a pidfd, but returns a
|
||||
// Conn rather than a raw file descriptor.
|
||||
func (c *Conn) PidfdGetfd(targetFD, flags int) (*Conn, error) {
|
||||
outFD, err := controlT(c, "pidfd_getfd", func(fd int) (int, error) {
|
||||
return unix.PidfdGetfd(fd, targetFD, flags)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successful getfd, wrap in a Conn for use by the caller.
|
||||
return New(outFD, c.name)
|
||||
}
|
||||
|
||||
// PidfdSendSignal wraps pidfd_send_signal(2) for a Conn which wraps a Linux
|
||||
// pidfd.
|
||||
func (c *Conn) PidfdSendSignal(sig unix.Signal, info *unix.Siginfo, flags int) error {
|
||||
return c.control("pidfd_send_signal", func(fd int) error {
|
||||
return unix.PidfdSendSignal(fd, sig, info, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// SetBPF attaches an assembled BPF program to a Conn.
|
||||
func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
|
||||
// We can't point to the first instruction in the array if no instructions
|
||||
// are present.
|
||||
if len(filter) == 0 {
|
||||
return os.NewSyscallError("setsockopt", unix.EINVAL)
|
||||
}
|
||||
|
||||
prog := unix.SockFprog{
|
||||
Len: uint16(len(filter)),
|
||||
Filter: (*unix.SockFilter)(unsafe.Pointer(&filter[0])),
|
||||
}
|
||||
|
||||
return c.SetsockoptSockFprog(unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, &prog)
|
||||
}
|
||||
|
||||
// RemoveBPF removes a BPF filter from a Conn.
|
||||
func (c *Conn) RemoveBPF() error {
|
||||
// 0 argument is ignored.
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_DETACH_FILTER, 0)
|
||||
}
|
||||
|
||||
// SetsockoptPacketMreq wraps setsockopt(2) for unix.PacketMreq values.
|
||||
func (c *Conn) SetsockoptPacketMreq(level, opt int, mreq *unix.PacketMreq) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptPacketMreq(fd, level, opt, mreq)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptSockFprog wraps setsockopt(2) for unix.SockFprog values.
|
||||
func (c *Conn) SetsockoptSockFprog(level, opt int, fprog *unix.SockFprog) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptSockFprog(fd, level, opt, fprog)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptTpacketStats wraps getsockopt(2) for unix.TpacketStats values.
|
||||
func (c *Conn) GetsockoptTpacketStats(level, name int) (*unix.TpacketStats, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (*unix.TpacketStats, error) {
|
||||
return unix.GetsockoptTpacketStats(fd, level, name)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptTpacketStatsV3 wraps getsockopt(2) for unix.TpacketStatsV3 values.
|
||||
func (c *Conn) GetsockoptTpacketStatsV3(level, name int) (*unix.TpacketStatsV3, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (*unix.TpacketStatsV3, error) {
|
||||
return unix.GetsockoptTpacketStatsV3(fd, level, name)
|
||||
})
|
||||
}
|
||||
|
||||
// Waitid wraps waitid(2).
|
||||
func (c *Conn) Waitid(idType int, info *unix.Siginfo, options int, rusage *unix.Rusage) error {
|
||||
return c.read(context.Background(), "waitid", func(fd int) error {
|
||||
return unix.Waitid(idType, fd, info, options, rusage)
|
||||
})
|
||||
}
|
13
vendor/github.com/mdlayher/socket/doc.go
generated
vendored
Normal file
13
vendor/github.com/mdlayher/socket/doc.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package socket provides a low-level network connection type which integrates
|
||||
// with Go's runtime network poller to provide asynchronous I/O and deadline
|
||||
// support.
|
||||
//
|
||||
// This package focuses on UNIX-like operating systems which make use of BSD
|
||||
// sockets system call APIs. It is meant to be used as a foundation for the
|
||||
// creation of operating system-specific socket packages, for socket families
|
||||
// such as Linux's AF_NETLINK, AF_PACKET, or AF_VSOCK. This package should not
|
||||
// be used directly in end user applications.
|
||||
//
|
||||
// Any use of package socket should be guarded by build tags, as one would also
|
||||
// use when importing the syscall or golang.org/x/sys packages.
|
||||
package socket
|
150
vendor/github.com/mdlayher/socket/netns_linux.go
generated
vendored
Normal file
150
vendor/github.com/mdlayher/socket/netns_linux.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// errNetNSDisabled is returned when network namespaces are unavailable on
|
||||
// a given system.
|
||||
var errNetNSDisabled = errors.New("socket: Linux network namespaces are not enabled on this system")
|
||||
|
||||
// withNetNS invokes fn within the context of the network namespace specified by
|
||||
// fd, while also managing the logic required to safely do so by manipulating
|
||||
// thread-local state.
|
||||
func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) {
|
||||
var (
|
||||
eg errgroup.Group
|
||||
conn *Conn
|
||||
)
|
||||
|
||||
eg.Go(func() error {
|
||||
// Retrieve and store the calling OS thread's network namespace so the
|
||||
// thread can be reassigned to it after creating a socket in another network
|
||||
// namespace.
|
||||
runtime.LockOSThread()
|
||||
|
||||
ns, err := threadNetNS()
|
||||
if err != nil {
|
||||
// No thread-local manipulation, unlock.
|
||||
runtime.UnlockOSThread()
|
||||
return err
|
||||
}
|
||||
defer ns.Close()
|
||||
|
||||
// Beyond this point, the thread's network namespace is poisoned. Do not
|
||||
// unlock the OS thread until all network namespace manipulation completes
|
||||
// to avoid returning to the caller with altered thread-local state.
|
||||
|
||||
// Assign the current OS thread the goroutine is locked to to the given
|
||||
// network namespace.
|
||||
if err := ns.Set(fd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt Conn creation and unconditionally restore the original namespace.
|
||||
c, err := fn()
|
||||
if nerr := ns.Restore(); nerr != nil {
|
||||
// Failed to restore original namespace. Return an error and allow the
|
||||
// runtime to terminate the thread.
|
||||
if err == nil {
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
return nerr
|
||||
}
|
||||
|
||||
// No more thread-local state manipulation; return the new Conn.
|
||||
runtime.UnlockOSThread()
|
||||
conn = c
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// A netNS is a handle that can manipulate network namespaces.
|
||||
//
|
||||
// Operations performed on a netNS must use runtime.LockOSThread before
|
||||
// manipulating any network namespaces.
|
||||
type netNS struct {
|
||||
// The handle to a network namespace.
|
||||
f *os.File
|
||||
|
||||
// Indicates if network namespaces are disabled on this system, and thus
|
||||
// operations should become a no-op or return errors.
|
||||
disabled bool
|
||||
}
|
||||
|
||||
// threadNetNS constructs a netNS using the network namespace of the calling
|
||||
// thread. If the namespace is not the default namespace, runtime.LockOSThread
|
||||
// should be invoked first.
|
||||
func threadNetNS() (*netNS, error) {
|
||||
return fileNetNS(fmt.Sprintf("/proc/self/task/%d/ns/net", unix.Gettid()))
|
||||
}
|
||||
|
||||
// fileNetNS opens file and creates a netNS. fileNetNS should only be called
|
||||
// directly in tests.
|
||||
func fileNetNS(file string) (*netNS, error) {
|
||||
f, err := os.Open(file)
|
||||
switch {
|
||||
case err == nil:
|
||||
return &netNS{f: f}, nil
|
||||
case os.IsNotExist(err):
|
||||
// Network namespaces are not enabled on this system. Use this signal
|
||||
// to return errors elsewhere if the caller explicitly asks for a
|
||||
// network namespace to be set.
|
||||
return &netNS{disabled: true}, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Close releases the handle to a network namespace.
|
||||
func (n *netNS) Close() error {
|
||||
return n.do(func() error { return n.f.Close() })
|
||||
}
|
||||
|
||||
// FD returns a file descriptor which represents the network namespace.
|
||||
func (n *netNS) FD() int {
|
||||
if n.disabled {
|
||||
// No reasonable file descriptor value in this case, so specify a
|
||||
// non-existent one.
|
||||
return -1
|
||||
}
|
||||
|
||||
return int(n.f.Fd())
|
||||
}
|
||||
|
||||
// Restore restores the original network namespace for the calling thread.
|
||||
func (n *netNS) Restore() error {
|
||||
return n.do(func() error { return n.Set(n.FD()) })
|
||||
}
|
||||
|
||||
// Set sets a new network namespace for the current thread using fd.
|
||||
func (n *netNS) Set(fd int) error {
|
||||
return n.do(func() error {
|
||||
return os.NewSyscallError("setns", unix.Setns(fd, unix.CLONE_NEWNET))
|
||||
})
|
||||
}
|
||||
|
||||
// do runs fn if network namespaces are enabled on this system.
|
||||
func (n *netNS) do(fn func() error) error {
|
||||
if n.disabled {
|
||||
return errNetNSDisabled
|
||||
}
|
||||
|
||||
return fn()
|
||||
}
|
14
vendor/github.com/mdlayher/socket/netns_others.go
generated
vendored
Normal file
14
vendor/github.com/mdlayher/socket/netns_others.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// withNetNS returns an error on non-Linux systems.
|
||||
func withNetNS(_ int, _ func() (*Conn, error)) (*Conn, error) {
|
||||
return nil, fmt.Errorf("socket: Linux network namespace support is not available on %s", runtime.GOOS)
|
||||
}
|
24
vendor/github.com/mdlayher/socket/setbuffer_linux.go
generated
vendored
Normal file
24
vendor/github.com/mdlayher/socket/setbuffer_linux.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// setReadBuffer wraps the SO_RCVBUF{,FORCE} setsockopt(2) options.
|
||||
func (c *Conn) setReadBuffer(bytes int) error {
|
||||
err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes)
|
||||
if err != nil {
|
||||
err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// setWriteBuffer wraps the SO_SNDBUF{,FORCE} setsockopt(2) options.
|
||||
func (c *Conn) setWriteBuffer(bytes int) error {
|
||||
err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes)
|
||||
if err != nil {
|
||||
err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
|
||||
}
|
||||
return err
|
||||
}
|
16
vendor/github.com/mdlayher/socket/setbuffer_others.go
generated
vendored
Normal file
16
vendor/github.com/mdlayher/socket/setbuffer_others.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// setReadBuffer wraps the SO_RCVBUF setsockopt(2) option.
|
||||
func (c *Conn) setReadBuffer(bytes int) error {
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
|
||||
}
|
||||
|
||||
// setWriteBuffer wraps the SO_SNDBUF setsockopt(2) option.
|
||||
func (c *Conn) setWriteBuffer(bytes int) error {
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
|
||||
}
|
12
vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
generated
vendored
Normal file
12
vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build !darwin
|
||||
// +build !darwin
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const (
|
||||
// These operating systems support CLOEXEC and NONBLOCK socket options.
|
||||
flagCLOEXEC = true
|
||||
socketFlags = unix.SOCK_CLOEXEC | unix.SOCK_NONBLOCK
|
||||
)
|
11
vendor/github.com/mdlayher/socket/typ_none.go
generated
vendored
Normal file
11
vendor/github.com/mdlayher/socket/typ_none.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package socket
|
||||
|
||||
const (
|
||||
// These operating systems do not support CLOEXEC and NONBLOCK socket
|
||||
// options.
|
||||
flagCLOEXEC = false
|
||||
socketFlags = 0
|
||||
)
|
Reference in New Issue
Block a user