Update netlink vendoring (#1471)
* github.com/ema/qdisc * github.com/mdlayher/genetlink * github.com/mdlayher/wifi Signed-off-by: Ben Kochie <superq@gmail.com>
This commit is contained in:
parent
8c3de12c22
commit
0e77317955
245 changed files with 6515 additions and 1699 deletions
317
vendor/github.com/mdlayher/netlink/conn.go
generated
vendored
317
vendor/github.com/mdlayher/netlink/conn.go
generated
vendored
|
|
@ -2,9 +2,9 @@ package netlink
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
|
@ -12,30 +12,16 @@ import (
|
|||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
// Error messages which can be returned by Validate.
|
||||
var (
|
||||
errMismatchedSequence = errors.New("mismatched sequence in netlink reply")
|
||||
errMismatchedPID = errors.New("mismatched PID in netlink reply")
|
||||
errShortErrorMessage = errors.New("not enough data for netlink error code")
|
||||
)
|
||||
|
||||
// Errors which can be returned by a Socket that does not implement
|
||||
// all exposed methods of Conn.
|
||||
var (
|
||||
errReadWriteCloserNotSupported = errors.New("raw read/write/closer not supported")
|
||||
errMulticastGroupsNotSupported = errors.New("multicast groups not supported")
|
||||
errBPFFiltersNotSupported = errors.New("BPF filters not supported")
|
||||
errOptionsNotSupported = errors.New("options not supported")
|
||||
errSetBufferNotSupported = errors.New("setting buffer sizes not supported")
|
||||
errSyscallConnNotSupported = errors.New("syscall.RawConn operation not supported")
|
||||
)
|
||||
|
||||
// A Conn is a connection to netlink. A Conn can be used to send and
|
||||
// receives messages to and from netlink.
|
||||
//
|
||||
// A Conn is safe for concurrent use, but to avoid contention in
|
||||
// high-throughput applications, the caller should almost certainly create a
|
||||
// pool of Conns and distribute them among workers.
|
||||
//
|
||||
// A Conn is capable of manipulating netlink subsystems from within a specific
|
||||
// Linux network namespace, but special care must be taken when doing so. See
|
||||
// the documentation of Config for details.
|
||||
type Conn struct {
|
||||
// sock is the operating system-specific implementation of
|
||||
// a netlink sockets connection.
|
||||
|
|
@ -50,6 +36,10 @@ type Conn struct {
|
|||
|
||||
// d provides debugging capabilities for a Conn if not nil.
|
||||
d *debugger
|
||||
|
||||
// mu serializes access to the netlink socket for the request/response
|
||||
// transaction within Execute.
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// A Socket is an operating-system specific implementation of netlink
|
||||
|
|
@ -107,24 +97,40 @@ func (c *Conn) debug(fn func(d *debugger)) {
|
|||
fn(c.d)
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
// Close closes the connection. Close will unblock any concurrent calls to
|
||||
// Receive which are waiting on a response from the kernel.
|
||||
func (c *Conn) Close() error {
|
||||
return c.sock.Close()
|
||||
// Close does not acquire a lock because it must be able to interrupt any
|
||||
// blocked system calls, such as when Receive is waiting on a multicast
|
||||
// group message.
|
||||
//
|
||||
// We rely on the kernel to deal with concurrent operations to the netlink
|
||||
// socket itself.
|
||||
return newOpError("close", c.sock.Close())
|
||||
}
|
||||
|
||||
// Execute sends a single Message to netlink using Conn.Send, receives one or more
|
||||
// replies using Conn.Receive, and then checks the validity of the replies against
|
||||
// Execute sends a single Message to netlink using Send, receives one or more
|
||||
// replies using Receive, and then checks the validity of the replies against
|
||||
// the request using Validate.
|
||||
//
|
||||
// See the documentation of Conn.Send, Conn.Receive, and Validate for details about
|
||||
// Execute acquires a lock for the duration of the function call which blocks
|
||||
// concurrent calls to Send, SendMessages, and Receive, in order to ensure
|
||||
// consistency between netlink request/reply messages.
|
||||
//
|
||||
// See the documentation of Send, Receive, and Validate for details about
|
||||
// each function.
|
||||
func (c *Conn) Execute(message Message) ([]Message, error) {
|
||||
req, err := c.Send(message)
|
||||
// Acquire the write lock and invoke the internal implementations of Send
|
||||
// and Receive which require the lock already be held.
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
req, err := c.lockedSend(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
replies, err := c.Receive()
|
||||
replies, err := c.lockedReceive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -136,24 +142,14 @@ func (c *Conn) Execute(message Message) ([]Message, error) {
|
|||
return replies, nil
|
||||
}
|
||||
|
||||
func (c *Conn) fixMsg(m *Message, ml int) {
|
||||
if m.Header.Length == 0 {
|
||||
m.Header.Length = uint32(nlmsgAlign(ml))
|
||||
}
|
||||
|
||||
if m.Header.Sequence == 0 {
|
||||
m.Header.Sequence = c.nextSequence()
|
||||
}
|
||||
|
||||
if m.Header.PID == 0 {
|
||||
m.Header.PID = c.pid
|
||||
}
|
||||
}
|
||||
|
||||
// SendMessages sends multiple Messages to netlink. The handling of
|
||||
// a Header's Length, Sequence and PID fields is the same as when
|
||||
// calling Send.
|
||||
func (c *Conn) SendMessages(messages []Message) ([]Message, error) {
|
||||
// Wait for any concurrent calls to Execute to finish before proceeding.
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
for idx, m := range messages {
|
||||
ml := nlmsgLength(len(m.Data))
|
||||
|
||||
|
|
@ -176,7 +172,7 @@ func (c *Conn) SendMessages(messages []Message) ([]Message, error) {
|
|||
d.debugf(1, "send msgs: err: %v", err)
|
||||
})
|
||||
|
||||
return nil, err
|
||||
return nil, newOpError("send-messages", err)
|
||||
}
|
||||
|
||||
return messages, nil
|
||||
|
|
@ -196,6 +192,17 @@ func (c *Conn) SendMessages(messages []Message) ([]Message, error) {
|
|||
// If Header.PID is 0, it will be automatically populated using a PID
|
||||
// assigned by netlink.
|
||||
func (c *Conn) Send(message Message) (Message, error) {
|
||||
// Wait for any concurrent calls to Execute to finish before proceeding.
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.lockedSend(message)
|
||||
}
|
||||
|
||||
// lockedSend implements Send, but must be called with c.mu acquired for reading.
|
||||
// We rely on the kernel to deal with concurrent reads and writes to the netlink
|
||||
// socket itself.
|
||||
func (c *Conn) lockedSend(message Message) (Message, error) {
|
||||
ml := nlmsgLength(len(message.Data))
|
||||
|
||||
// TODO(mdlayher): fine-tune this limit.
|
||||
|
|
@ -214,7 +221,7 @@ func (c *Conn) Send(message Message) (Message, error) {
|
|||
d.debugf(1, "send: err: %v", err)
|
||||
})
|
||||
|
||||
return Message{}, err
|
||||
return Message{}, newOpError("send", err)
|
||||
}
|
||||
|
||||
return message, nil
|
||||
|
|
@ -226,6 +233,17 @@ func (c *Conn) Send(message Message) (Message, error) {
|
|||
//
|
||||
// If any of the messages indicate a netlink error, that error will be returned.
|
||||
func (c *Conn) Receive() ([]Message, error) {
|
||||
// Wait for any concurrent calls to Execute to finish before proceeding.
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.lockedReceive()
|
||||
}
|
||||
|
||||
// lockedReceive implements Receive, but must be called with c.mu acquired for reading.
|
||||
// We rely on the kernel to deal with concurrent reads and writes to the netlink
|
||||
// socket itself.
|
||||
func (c *Conn) lockedReceive() ([]Message, error) {
|
||||
msgs, err := c.receive()
|
||||
if err != nil {
|
||||
c.debug(func(d *debugger) {
|
||||
|
|
@ -248,7 +266,7 @@ func (c *Conn) Receive() ([]Message, error) {
|
|||
|
||||
// Trim the final message with multi-part done indicator if
|
||||
// present.
|
||||
if m := msgs[len(msgs)-1]; m.Header.Flags&HeaderFlagsMulti != 0 && m.Header.Type == HeaderTypeDone {
|
||||
if m := msgs[len(msgs)-1]; m.Header.Flags&Multi != 0 && m.Header.Type == Done {
|
||||
return msgs[:len(msgs)-1], nil
|
||||
}
|
||||
|
||||
|
|
@ -258,11 +276,18 @@ func (c *Conn) Receive() ([]Message, error) {
|
|||
// receive is the internal implementation of Conn.Receive, which can be called
|
||||
// recursively to handle multi-part messages.
|
||||
func (c *Conn) receive() ([]Message, error) {
|
||||
// NB: All non-nil errors returned from this function *must* be of type
|
||||
// OpError in order to maintain the appropriate contract with callers of
|
||||
// this package.
|
||||
//
|
||||
// This contract also applies to functions called within this function,
|
||||
// such as checkMessage.
|
||||
|
||||
var res []Message
|
||||
for {
|
||||
msgs, err := c.sock.Receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, newOpError("receive", err)
|
||||
}
|
||||
|
||||
// If this message is multi-part, we will need to perform an recursive call
|
||||
|
|
@ -275,14 +300,14 @@ func (c *Conn) receive() ([]Message, error) {
|
|||
}
|
||||
|
||||
// Does this message indicate a multi-part message?
|
||||
if m.Header.Flags&HeaderFlagsMulti == 0 {
|
||||
if m.Header.Flags&Multi == 0 {
|
||||
// No, check the next messages.
|
||||
continue
|
||||
}
|
||||
|
||||
// Does this message indicate the last message in a series of
|
||||
// multi-part messages from a single read?
|
||||
multi = m.Header.Type != HeaderTypeDone
|
||||
multi = m.Header.Type != Done
|
||||
}
|
||||
|
||||
res = append(res, msgs...)
|
||||
|
|
@ -294,51 +319,6 @@ func (c *Conn) receive() ([]Message, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// An fder is a Socket that supports retrieving its raw file descriptor.
|
||||
type fder interface {
|
||||
Socket
|
||||
FD() int
|
||||
}
|
||||
|
||||
var _ io.ReadWriteCloser = &fileReadWriteCloser{}
|
||||
|
||||
// A fileReadWriteCloser is a limited *os.File which only allows access to its
|
||||
// Read and Write methods.
|
||||
type fileReadWriteCloser struct {
|
||||
f *os.File
|
||||
}
|
||||
|
||||
// Read implements io.ReadWriteCloser.
|
||||
func (rwc *fileReadWriteCloser) Read(b []byte) (int, error) { return rwc.f.Read(b) }
|
||||
|
||||
// Write implements io.ReadWriteCloser.
|
||||
func (rwc *fileReadWriteCloser) Write(b []byte) (int, error) { return rwc.f.Write(b) }
|
||||
|
||||
// Close implements io.ReadWriteCloser.
|
||||
func (rwc *fileReadWriteCloser) Close() error { return rwc.f.Close() }
|
||||
|
||||
// ReadWriteCloser returns a raw io.ReadWriteCloser backed by the connection
|
||||
// of the Conn.
|
||||
//
|
||||
// ReadWriteCloser is intended for advanced use cases, such as those that do
|
||||
// not involve standard netlink message passing.
|
||||
//
|
||||
// Once invoked, it is the caller's responsibility to ensure that operations
|
||||
// performed using Conn and the raw io.ReadWriteCloser do not conflict with
|
||||
// each other. In almost all scenarios, only one of the two should be used.
|
||||
func (c *Conn) ReadWriteCloser() (io.ReadWriteCloser, error) {
|
||||
fc, ok := c.sock.(fder)
|
||||
if !ok {
|
||||
return nil, errReadWriteCloserNotSupported
|
||||
}
|
||||
|
||||
return &fileReadWriteCloser{
|
||||
// Backing the io.ReadWriteCloser with an *os.File enables easy reading
|
||||
// and writing without more system call boilerplate.
|
||||
f: os.NewFile(uintptr(fc.FD()), "netlink"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// A groupJoinLeaver is a Socket that supports joining and leaving
|
||||
// netlink multicast groups.
|
||||
type groupJoinLeaver interface {
|
||||
|
|
@ -349,22 +329,22 @@ type groupJoinLeaver interface {
|
|||
|
||||
// JoinGroup joins a netlink multicast group by its ID.
|
||||
func (c *Conn) JoinGroup(group uint32) error {
|
||||
gc, ok := c.sock.(groupJoinLeaver)
|
||||
conn, ok := c.sock.(groupJoinLeaver)
|
||||
if !ok {
|
||||
return errMulticastGroupsNotSupported
|
||||
return notSupported("join-group")
|
||||
}
|
||||
|
||||
return gc.JoinGroup(group)
|
||||
return newOpError("join-group", conn.JoinGroup(group))
|
||||
}
|
||||
|
||||
// LeaveGroup leaves a netlink multicast group by its ID.
|
||||
func (c *Conn) LeaveGroup(group uint32) error {
|
||||
gc, ok := c.sock.(groupJoinLeaver)
|
||||
conn, ok := c.sock.(groupJoinLeaver)
|
||||
if !ok {
|
||||
return errMulticastGroupsNotSupported
|
||||
return notSupported("leave-group")
|
||||
}
|
||||
|
||||
return gc.LeaveGroup(group)
|
||||
return newOpError("leave-group", conn.LeaveGroup(group))
|
||||
}
|
||||
|
||||
// A bpfSetter is a Socket that supports setting and removing BPF filters.
|
||||
|
|
@ -376,22 +356,69 @@ type bpfSetter interface {
|
|||
|
||||
// SetBPF attaches an assembled BPF program to a Conn.
|
||||
func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
|
||||
bc, ok := c.sock.(bpfSetter)
|
||||
conn, ok := c.sock.(bpfSetter)
|
||||
if !ok {
|
||||
return errBPFFiltersNotSupported
|
||||
return notSupported("set-bpf")
|
||||
}
|
||||
|
||||
return bc.SetBPF(filter)
|
||||
return newOpError("set-bpf", conn.SetBPF(filter))
|
||||
}
|
||||
|
||||
// RemoveBPF removes a BPF filter from a Conn.
|
||||
func (c *Conn) RemoveBPF() error {
|
||||
s, ok := c.sock.(bpfSetter)
|
||||
conn, ok := c.sock.(bpfSetter)
|
||||
if !ok {
|
||||
return errBPFFiltersNotSupported
|
||||
return notSupported("remove-bpf")
|
||||
}
|
||||
|
||||
return s.RemoveBPF()
|
||||
return newOpError("remove-bpf", conn.RemoveBPF())
|
||||
}
|
||||
|
||||
// A deadlineSetter is a Socket that supports setting deadlines.
|
||||
type deadlineSetter interface {
|
||||
Socket
|
||||
SetDeadline(time.Time) error
|
||||
SetReadDeadline(time.Time) error
|
||||
SetWriteDeadline(time.Time) error
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated with the connection.
|
||||
//
|
||||
// Deadline functionality is only supported on Go 1.12+. Calling this function
|
||||
// on older versions of Go will result in an error.
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
conn, ok := c.sock.(deadlineSetter)
|
||||
if !ok {
|
||||
return notSupported("set-deadline")
|
||||
}
|
||||
|
||||
return newOpError("set-deadline", conn.SetDeadline(t))
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the connection.
|
||||
//
|
||||
// Deadline functionality is only supported on Go 1.12+. Calling this function
|
||||
// on older versions of Go will result in an error.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
conn, ok := c.sock.(deadlineSetter)
|
||||
if !ok {
|
||||
return notSupported("set-read-deadline")
|
||||
}
|
||||
|
||||
return newOpError("set-read-deadline", conn.SetReadDeadline(t))
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the connection.
|
||||
//
|
||||
// Deadline functionality is only supported on Go 1.12+. Calling this function
|
||||
// on older versions of Go will result in an error.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
conn, ok := c.sock.(deadlineSetter)
|
||||
if !ok {
|
||||
return notSupported("set-write-deadline")
|
||||
}
|
||||
|
||||
return newOpError("set-write-deadline", conn.SetWriteDeadline(t))
|
||||
}
|
||||
|
||||
// A ConnOption is a boolean option that may be set for a Conn.
|
||||
|
|
@ -405,6 +432,7 @@ const (
|
|||
NoENOBUFS
|
||||
ListenAllNSID
|
||||
CapAcknowledge
|
||||
ExtendedAcknowledge
|
||||
)
|
||||
|
||||
// An optionSetter is a Socket that supports setting netlink options.
|
||||
|
|
@ -415,12 +443,12 @@ type optionSetter interface {
|
|||
|
||||
// SetOption enables or disables a netlink socket option for the Conn.
|
||||
func (c *Conn) SetOption(option ConnOption, enable bool) error {
|
||||
fc, ok := c.sock.(optionSetter)
|
||||
conn, ok := c.sock.(optionSetter)
|
||||
if !ok {
|
||||
return errOptionsNotSupported
|
||||
return notSupported("set-option")
|
||||
}
|
||||
|
||||
return fc.SetOption(option, enable)
|
||||
return newOpError("set-option", conn.SetOption(option, enable))
|
||||
}
|
||||
|
||||
// A bufferSetter is a Socket that supports setting connection buffer sizes.
|
||||
|
|
@ -435,10 +463,10 @@ type bufferSetter interface {
|
|||
func (c *Conn) SetReadBuffer(bytes int) error {
|
||||
conn, ok := c.sock.(bufferSetter)
|
||||
if !ok {
|
||||
return errSetBufferNotSupported
|
||||
return notSupported("set-read-buffer")
|
||||
}
|
||||
|
||||
return conn.SetReadBuffer(bytes)
|
||||
return newOpError("set-read-buffer", conn.SetReadBuffer(bytes))
|
||||
}
|
||||
|
||||
// SetWriteBuffer sets the size of the operating system's transmit buffer
|
||||
|
|
@ -446,10 +474,16 @@ func (c *Conn) SetReadBuffer(bytes int) error {
|
|||
func (c *Conn) SetWriteBuffer(bytes int) error {
|
||||
conn, ok := c.sock.(bufferSetter)
|
||||
if !ok {
|
||||
return errSetBufferNotSupported
|
||||
return notSupported("set-write-buffer")
|
||||
}
|
||||
|
||||
return conn.SetWriteBuffer(bytes)
|
||||
return newOpError("set-write-buffer", conn.SetWriteBuffer(bytes))
|
||||
}
|
||||
|
||||
// A filer is a Socket that supports retrieving its associated *os.File.
|
||||
type filer interface {
|
||||
Socket
|
||||
File() *os.File
|
||||
}
|
||||
|
||||
var _ syscall.Conn = &Conn{}
|
||||
|
|
@ -460,8 +494,10 @@ var _ syscall.Conn = &Conn{}
|
|||
// SyscallConn returns a raw network connection. This implements the
|
||||
// syscall.Conn interface.
|
||||
//
|
||||
// Only the Control method of the returned syscall.RawConn is currently
|
||||
// implemented.
|
||||
// On Go 1.12+, all methods of the returned syscall.RawConn are supported and
|
||||
// the Conn is integrated with the runtime network poller. On versions of Go
|
||||
// prior to Go 1.12, only the Control method of the returned syscall.RawConn
|
||||
// is implemented.
|
||||
//
|
||||
// SyscallConn is intended for advanced use cases, such as getting and setting
|
||||
// arbitrary socket options using the netlink socket's file descriptor.
|
||||
|
|
@ -470,33 +506,29 @@ var _ syscall.Conn = &Conn{}
|
|||
// performed using Conn and the syscall.RawConn do not conflict with
|
||||
// each other.
|
||||
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
|
||||
conn, ok := c.sock.(fder)
|
||||
fc, ok := c.sock.(filer)
|
||||
if !ok {
|
||||
return nil, errSyscallConnNotSupported
|
||||
return nil, notSupported("syscall-conn")
|
||||
}
|
||||
|
||||
return &rawConn{
|
||||
fd: uintptr(conn.FD()),
|
||||
}, nil
|
||||
return newRawConn(fc.File())
|
||||
}
|
||||
|
||||
var _ syscall.RawConn = &rawConn{}
|
||||
// fixMsg updates the fields of m using the logic specified in Send.
|
||||
func (c *Conn) fixMsg(m *Message, ml int) {
|
||||
if m.Header.Length == 0 {
|
||||
m.Header.Length = uint32(nlmsgAlign(ml))
|
||||
}
|
||||
|
||||
// A rawConn is a syscall.RawConn.
|
||||
type rawConn struct {
|
||||
fd uintptr
|
||||
if m.Header.Sequence == 0 {
|
||||
m.Header.Sequence = c.nextSequence()
|
||||
}
|
||||
|
||||
if m.Header.PID == 0 {
|
||||
m.Header.PID = c.pid
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *rawConn) Control(f func(fd uintptr)) error {
|
||||
f(rc.fd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(mdlayher): implement Read and Write?
|
||||
|
||||
func (rc *rawConn) Read(_ func(fd uintptr) (done bool)) error { return errSyscallConnNotSupported }
|
||||
func (rc *rawConn) Write(_ func(fd uintptr) (done bool)) error { return errSyscallConnNotSupported }
|
||||
|
||||
// nextSequence atomically increments Conn's sequence number and returns
|
||||
// the incremented value.
|
||||
func (c *Conn) nextSequence() uint32 {
|
||||
|
|
@ -511,7 +543,7 @@ func Validate(request Message, replies []Message) error {
|
|||
// - request had no sequence, meaning we are probably validating
|
||||
// a multicast reply
|
||||
if m.Header.Sequence != request.Header.Sequence && request.Header.Sequence != 0 {
|
||||
return errMismatchedSequence
|
||||
return newOpError("validate", errMismatchedSequence)
|
||||
}
|
||||
|
||||
// Check for mismatched PID, unless:
|
||||
|
|
@ -520,7 +552,7 @@ func Validate(request Message, replies []Message) error {
|
|||
// - netlink has not yet assigned us a PID
|
||||
// - response had no PID, meaning it's from the kernel as a multicast reply
|
||||
if m.Header.PID != request.Header.PID && request.Header.PID != 0 && m.Header.PID != 0 {
|
||||
return errMismatchedPID
|
||||
return newOpError("validate", errMismatchedPID)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +565,22 @@ type Config struct {
|
|||
// no multicast group subscriptions will be made.
|
||||
Groups uint32
|
||||
|
||||
// Network namespace the Conn needs to operate in. If set to 0,
|
||||
// no network namespace will be entered.
|
||||
// NetNS specifies the network namespace the Conn will operate in.
|
||||
//
|
||||
// If set (non-zero), Conn will enter the specified network namespace and
|
||||
// an error will occur in Dial 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), Dial 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue