1package netlink
2
3import (
4	"fmt"
5	"time"
6
7	"github.com/vishvananda/netlink/nl"
8	"github.com/vishvananda/netns"
9	"golang.org/x/sys/unix"
10)
11
12// Empty handle used by the netlink package methods
13var pkgHandle = &Handle{}
14
15// Handle is an handle for the netlink requests on a
16// specific network namespace. All the requests on the
17// same netlink family share the same netlink socket,
18// which gets released when the handle is deleted.
19type Handle struct {
20	sockets      map[int]*nl.SocketHandle
21	lookupByDump bool
22}
23
24// SupportsNetlinkFamily reports whether the passed netlink family is supported by this Handle
25func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool {
26	_, ok := h.sockets[nlFamily]
27	return ok
28}
29
30// NewHandle returns a netlink handle on the current network namespace.
31// Caller may specify the netlink families the handle should support.
32// If no families are specified, all the families the netlink package
33// supports will be automatically added.
34func NewHandle(nlFamilies ...int) (*Handle, error) {
35	return newHandle(netns.None(), netns.None(), nlFamilies...)
36}
37
38// SetSocketTimeout sets the send and receive timeout for each socket in the
39// netlink handle. Although the socket timeout has granularity of one
40// microsecond, the effective granularity is floored by the kernel timer tick,
41// which default value is four milliseconds.
42func (h *Handle) SetSocketTimeout(to time.Duration) error {
43	if to < time.Microsecond {
44		return fmt.Errorf("invalid timeout, minimul value is %s", time.Microsecond)
45	}
46	tv := unix.NsecToTimeval(to.Nanoseconds())
47	for _, sh := range h.sockets {
48		if err := sh.Socket.SetSendTimeout(&tv); err != nil {
49			return err
50		}
51		if err := sh.Socket.SetReceiveTimeout(&tv); err != nil {
52			return err
53		}
54	}
55	return nil
56}
57
58// SetSocketReceiveBufferSize sets the receive buffer size for each
59// socket in the netlink handle. The maximum value is capped by
60// /proc/sys/net/core/rmem_max.
61func (h *Handle) SetSocketReceiveBufferSize(size int, force bool) error {
62	opt := unix.SO_RCVBUF
63	if force {
64		opt = unix.SO_RCVBUFFORCE
65	}
66	for _, sh := range h.sockets {
67		fd := sh.Socket.GetFd()
68		err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, opt, size)
69		if err != nil {
70			return err
71		}
72	}
73	return nil
74}
75
76// GetSocketReceiveBufferSize gets the receiver buffer size for each
77// socket in the netlink handle. The retrieved value should be the
78// double to the one set for SetSocketReceiveBufferSize.
79func (h *Handle) GetSocketReceiveBufferSize() ([]int, error) {
80	results := make([]int, len(h.sockets))
81	i := 0
82	for _, sh := range h.sockets {
83		fd := sh.Socket.GetFd()
84		size, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF)
85		if err != nil {
86			return nil, err
87		}
88		results[i] = size
89		i++
90	}
91	return results, nil
92}
93
94// NewHandleAt returns a netlink handle on the network namespace
95// specified by ns. If ns=netns.None(), current network namespace
96// will be assumed
97func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error) {
98	return newHandle(ns, netns.None(), nlFamilies...)
99}
100
101// NewHandleAtFrom works as NewHandle but allows client to specify the
102// new and the origin netns Handle.
103func NewHandleAtFrom(newNs, curNs netns.NsHandle) (*Handle, error) {
104	return newHandle(newNs, curNs)
105}
106
107func newHandle(newNs, curNs netns.NsHandle, nlFamilies ...int) (*Handle, error) {
108	h := &Handle{sockets: map[int]*nl.SocketHandle{}}
109	fams := nl.SupportedNlFamilies
110	if len(nlFamilies) != 0 {
111		fams = nlFamilies
112	}
113	for _, f := range fams {
114		s, err := nl.GetNetlinkSocketAt(newNs, curNs, f)
115		if err != nil {
116			return nil, err
117		}
118		h.sockets[f] = &nl.SocketHandle{Socket: s}
119	}
120	return h, nil
121}
122
123// Delete releases the resources allocated to this handle
124func (h *Handle) Delete() {
125	for _, sh := range h.sockets {
126		sh.Close()
127	}
128	h.sockets = nil
129}
130
131func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest {
132	// Do this so that package API still use nl package variable nextSeqNr
133	if h.sockets == nil {
134		return nl.NewNetlinkRequest(proto, flags)
135	}
136	return &nl.NetlinkRequest{
137		NlMsghdr: unix.NlMsghdr{
138			Len:   uint32(unix.SizeofNlMsghdr),
139			Type:  uint16(proto),
140			Flags: unix.NLM_F_REQUEST | uint16(flags),
141		},
142		Sockets: h.sockets,
143	}
144}
145