bump netlink

Signed-off-by: Daman Arora <aroradaman@gmail.com>
This commit is contained in:
Daman Arora 2025-02-07 15:17:29 +05:30
parent d2ad0cc7c0
commit 07c279d06b
33 changed files with 682 additions and 217 deletions

2
go.mod
View File

@ -60,7 +60,7 @@ require (
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
github.com/vishvananda/netlink v1.3.1-0.20240905180732-b1ce50cfa9be
github.com/vishvananda/netlink v1.3.1-0.20250206174618-62fb240731fa
github.com/vishvananda/netns v0.0.4
go.etcd.io/etcd/api/v3 v3.5.16
go.etcd.io/etcd/client/pkg/v3 v3.5.16

4
go.sum
View File

@ -462,8 +462,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/vishvananda/netlink v1.3.1-0.20240905180732-b1ce50cfa9be h1:xdCMvyhnKzaepIUgVpUmTJo/+H1AQ7HuFYn1hv7/Neo=
github.com/vishvananda/netlink v1.3.1-0.20240905180732-b1ce50cfa9be/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/vishvananda/netlink v1.3.1-0.20250206174618-62fb240731fa h1:iAhToRwOrdk+pKzclvLM7nKZhsg8f7dVrgkFccDUbUw=
github.com/vishvananda/netlink v1.3.1-0.20250206174618-62fb240731fa/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"net"
"strings"
@ -17,6 +18,7 @@ import (
//
// If `addr` is an IPv4 address and the broadcast address is not given, it
// will be automatically computed based on the IP mask if /30 or larger.
// If `net.IPv4zero` is given as the broadcast address, broadcast is disabled.
func AddrAdd(link Link, addr *Addr) error {
return pkgHandle.AddrAdd(link, addr)
}
@ -27,6 +29,7 @@ func AddrAdd(link Link, addr *Addr) error {
//
// If `addr` is an IPv4 address and the broadcast address is not given, it
// will be automatically computed based on the IP mask if /30 or larger.
// If `net.IPv4zero` is given as the broadcast address, broadcast is disabled.
func (h *Handle) AddrAdd(link Link, addr *Addr) error {
req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
return h.addrHandle(link, addr, req)
@ -38,6 +41,7 @@ func (h *Handle) AddrAdd(link Link, addr *Addr) error {
//
// If `addr` is an IPv4 address and the broadcast address is not given, it
// will be automatically computed based on the IP mask if /30 or larger.
// If `net.IPv4zero` is given as the broadcast address, broadcast is disabled.
func AddrReplace(link Link, addr *Addr) error {
return pkgHandle.AddrReplace(link, addr)
}
@ -48,6 +52,7 @@ func AddrReplace(link Link, addr *Addr) error {
//
// If `addr` is an IPv4 address and the broadcast address is not given, it
// will be automatically computed based on the IP mask if /30 or larger.
// If `net.IPv4zero` is given as the broadcast address, broadcast is disabled.
func (h *Handle) AddrReplace(link Link, addr *Addr) error {
req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK)
return h.addrHandle(link, addr, req)
@ -56,18 +61,13 @@ func (h *Handle) AddrReplace(link Link, addr *Addr) error {
// AddrDel will delete an IP address from a link device.
//
// Equivalent to: `ip addr del $addr dev $link`
//
// If `addr` is an IPv4 address and the broadcast address is not given, it
// will be automatically computed based on the IP mask if /30 or larger.
func AddrDel(link Link, addr *Addr) error {
return pkgHandle.AddrDel(link, addr)
}
// AddrDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
//
// If `addr` is an IPv4 address and the broadcast address is not given, it
// will be automatically computed based on the IP mask if /30 or larger.
// Equivalent to: `ip addr del $addr dev $link`
func (h *Handle) AddrDel(link Link, addr *Addr) error {
req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK)
return h.addrHandle(link, addr, req)
@ -141,6 +141,10 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
addr.Broadcast = calcBroadcast
}
if net.IPv4zero.Equal(addr.Broadcast) {
addr.Broadcast = nil
}
if addr.Broadcast != nil {
req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast))
}
@ -169,6 +173,9 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
// AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func AddrList(link Link, family int) ([]Addr, error) {
return pkgHandle.AddrList(link, family)
}
@ -176,14 +183,17 @@ func AddrList(link Link, family int) ([]Addr, error) {
// AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP)
msg := nl.NewIfAddrmsg(family)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
indexFilter := 0
@ -212,7 +222,7 @@ func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
res = append(res, addr)
}
return res, nil
return res, executeErr
}
func parseAddr(m []byte) (addr Addr, family int, err error) {

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"github.com/vishvananda/netlink/nl"
@ -9,21 +10,27 @@ import (
// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
return pkgHandle.BridgeVlanList()
}
// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
req.AddData(msg)
req.AddData(nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
ret := make(map[int32][]*nl.BridgeVlanInfo)
for _, m := range msgs {
@ -51,7 +58,7 @@ func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
}
}
}
return ret, nil
return ret, executeErr
}
// BridgeVlanAdd adds a new vlan filter entry

View File

@ -1,6 +1,8 @@
package netlink
import (
"errors"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
@ -56,6 +58,9 @@ func (h *Handle) chainModify(cmd, flags int, link Link, chain Chain) error {
// ChainList gets a list of chains in the system.
// Equivalent to: `tc chain list`.
// The list can be filtered by link.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func ChainList(link Link, parent uint32) ([]Chain, error) {
return pkgHandle.ChainList(link, parent)
}
@ -63,6 +68,9 @@ func ChainList(link Link, parent uint32) ([]Chain, error) {
// ChainList gets a list of chains in the system.
// Equivalent to: `tc chain list`.
// The list can be filtered by link.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
req := h.newNetlinkRequest(unix.RTM_GETCHAIN, unix.NLM_F_DUMP)
index := int32(0)
@ -78,9 +86,9 @@ func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
}
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []Chain
@ -108,5 +116,5 @@ func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
res = append(res, chain)
}
return res, nil
return res, executeErr
}

View File

@ -201,14 +201,20 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
// ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func ClassList(link Link, parent uint32) ([]Class, error) {
return pkgHandle.ClassList(link, parent)
}
// ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
msg := &nl.TcMsg{
@ -222,9 +228,9 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
}
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []Class
@ -295,7 +301,7 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
res = append(res, class)
}
return res, nil
return res, executeErr
}
func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {

View File

@ -5,8 +5,8 @@ import (
"encoding/binary"
"errors"
"fmt"
"io/fs"
"net"
"strings"
"time"
"github.com/vishvananda/netlink/nl"
@ -45,6 +45,9 @@ type InetFamily uint8
// ConntrackTableList returns the flow list of a table of a specific family
// conntrack -L [table] [options] List conntrack or expectation table
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
return pkgHandle.ConntrackTableList(table, family)
}
@ -84,10 +87,13 @@ func ConntrackDeleteFilters(table ConntrackTableType, family InetFamily, filters
// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
// conntrack -L [table] [options] List conntrack or expectation table
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
res, err := h.dumpConntrackTable(table, family)
if err != nil {
return nil, err
res, executeErr := h.dumpConntrackTable(table, family)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
// Deserialize all the flows
@ -96,7 +102,7 @@ func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily)
result = append(result, parseRawData(dataRaw))
}
return result, nil
return result, executeErr
}
// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
@ -153,13 +159,19 @@ func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFami
// ConntrackDeleteFilters deletes entries on the specified table matching any of the specified filters using the netlink handle passed
// conntrack -D [table] parameters Delete conntrack or expectation
func (h *Handle) ConntrackDeleteFilters(table ConntrackTableType, family InetFamily, filters ...CustomConntrackFilter) (uint, error) {
var finalErr error
res, err := h.dumpConntrackTable(table, family)
if err != nil {
return 0, err
if !errors.Is(err, ErrDumpInterrupted) {
return 0, err
}
// This allows us to at least do a best effort to try to clean the
// entries matching the filter.
finalErr = err
}
var totalFilterErrors int
var matched uint
var errMsgs []string
for _, dataRaw := range res {
flow := parseRawData(dataRaw)
for _, filter := range filters {
@ -167,19 +179,20 @@ func (h *Handle) ConntrackDeleteFilters(table ConntrackTableType, family InetFam
req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK)
// skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already
req2.AddRawData(dataRaw[4:])
if _, err = req2.Execute(unix.NETLINK_NETFILTER, 0); err == nil {
if _, err = req2.Execute(unix.NETLINK_NETFILTER, 0); err == nil || errors.Is(err, fs.ErrNotExist) {
matched++
// flow is already deleted, no need to match on other filters and continue to the next flow.
break
} else {
totalFilterErrors++
}
errMsgs = append(errMsgs, fmt.Sprintf("failed to delete conntrack flow '%s': %s", flow.String(), err.Error()))
}
}
}
if len(errMsgs) > 0 {
return matched, fmt.Errorf(strings.Join(errMsgs, "; "))
if totalFilterErrors > 0 {
finalErr = errors.Join(finalErr, fmt.Errorf("failed to delete %d conntrack flows with %d filters", totalFilterErrors, len(filters)))
}
return matched, nil
return matched, finalErr
}
func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest {

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"net"
"strings"
@ -466,6 +467,8 @@ func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
// otherwise returns an error code.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
if err != nil {
@ -478,9 +481,9 @@ func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
req := h.newNetlinkRequest(int(f.ID),
unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
devices, err := parseDevLinkDeviceList(msgs)
if err != nil {
@ -489,11 +492,14 @@ func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
for _, d := range devices {
h.getEswitchAttrs(f, d)
}
return devices, nil
return devices, executeErr
}
// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
// otherwise returns an error code.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
return pkgHandle.DevLinkGetDeviceList()
}
@ -646,6 +652,8 @@ func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) {
// DevLinkGetPortList provides a pointer to devlink ports and nil error,
// otherwise returns an error code.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) {
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
if err != nil {
@ -658,19 +666,21 @@ func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) {
req := h.newNetlinkRequest(int(f.ID),
unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
ports, err := parseDevLinkAllPortList(msgs)
if err != nil {
return nil, err
}
return ports, nil
return ports, executeErr
}
// DevLinkGetPortList provides a pointer to devlink ports and nil error,
// otherwise returns an error code.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func DevLinkGetAllPortList() ([]*DevlinkPort, error) {
return pkgHandle.DevLinkGetAllPortList()
}
@ -738,15 +748,18 @@ func (h *Handle) DevlinkGetDeviceResources(bus string, device string) (*DevlinkR
// DevlinkGetDeviceParams returns parameters for devlink device
// Equivalent to: `devlink dev param show <bus>/<device>`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device)
if err != nil {
return nil, err
}
req.Flags |= unix.NLM_F_DUMP
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
respmsg, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var params []*DevlinkParam
for _, m := range respmsg {
@ -761,11 +774,14 @@ func (h *Handle) DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkPa
params = append(params, p)
}
return params, nil
return params, executeErr
}
// DevlinkGetDeviceParams returns parameters for devlink device
// Equivalent to: `devlink dev param show <bus>/<device>`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
return pkgHandle.DevlinkGetDeviceParams(bus, device)
}

View File

@ -231,6 +231,35 @@ func NewCsumAction() *CsumAction {
}
}
type VlanAct int8
type VlanAction struct {
ActionAttrs
Action VlanAct
VlanID uint16
}
const (
TCA_VLAN_ACT_POP VlanAct = 1
TCA_VLAN_ACT_PUSH VlanAct = 2
)
func (action *VlanAction) Type() string {
return "vlan"
}
func (action *VlanAction) Attrs() *ActionAttrs {
return &action.ActionAttrs
}
func NewVlanAction() *VlanAction {
return &VlanAction{
ActionAttrs: ActionAttrs{
Action: TC_ACT_PIPE,
},
}
}
type MirredAct uint8
func (a MirredAct) String() string {

View File

@ -65,6 +65,9 @@ type Flower struct {
EncSrcIPMask net.IPMask
EncDestPort uint16
EncKeyId uint32
SrcMac net.HardwareAddr
DestMac net.HardwareAddr
VlanId uint16
SkipHw bool
SkipSw bool
IPProto *nl.IPProto
@ -135,6 +138,15 @@ func (filter *Flower) encode(parent *nl.RtAttr) error {
if filter.EncKeyId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_KEY_ID, htonl(filter.EncKeyId))
}
if filter.SrcMac != nil {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_SRC, filter.SrcMac)
}
if filter.DestMac != nil {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_DST, filter.DestMac)
}
if filter.VlanId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_VLAN_ID, nl.Uint16Attr(filter.VlanId))
}
if filter.IPProto != nil {
ipproto := *filter.IPProto
parent.AddRtAttr(nl.TCA_FLOWER_KEY_IP_PROTO, ipproto.Serialize())
@ -201,6 +213,13 @@ func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error {
filter.EncDestPort = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_ENC_KEY_ID:
filter.EncKeyId = ntohl(datum.Value)
case nl.TCA_FLOWER_KEY_ETH_SRC:
filter.SrcMac = datum.Value
case nl.TCA_FLOWER_KEY_ETH_DST:
filter.DestMac = datum.Value
case nl.TCA_FLOWER_KEY_VLAN_ID:
filter.VlanId = native.Uint16(datum.Value[0:2])
filter.EthType = unix.ETH_P_8021Q
case nl.TCA_FLOWER_KEY_IP_PROTO:
val := new(nl.IPProto)
*val = nl.IPProto(datum.Value[0])
@ -405,14 +424,20 @@ func (h *Handle) filterModify(filter Filter, proto, flags int) error {
// FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func FilterList(link Link, parent uint32) ([]Filter, error) {
return pkgHandle.FilterList(link, parent)
}
// FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP)
msg := &nl.TcMsg{
@ -426,9 +451,9 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
}
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []Filter
@ -516,7 +541,7 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
}
}
return res, nil
return res, executeErr
}
func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) {
@ -616,6 +641,22 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
}
toTcGen(action.Attrs(), &mirred.TcGen)
aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize())
case *VlanAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("vlan"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
vlan := nl.TcVlan{
Action: int32(action.Action),
}
toTcGen(action.Attrs(), &vlan.TcGen)
aopts.AddRtAttr(nl.TCA_VLAN_PARMS, vlan.Serialize())
if action.Action == TCA_VLAN_ACT_PUSH && action.VlanID == 0 {
return fmt.Errorf("vlan id is required for push action")
}
if action.VlanID != 0 {
aopts.AddRtAttr(nl.TCA_VLAN_PUSH_VLAN_ID, nl.Uint16Attr(action.VlanID))
}
case *TunnelKeyAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
@ -786,6 +827,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
action = &CsumAction{}
case "gact":
action = &GenericAction{}
case "vlan":
action = &VlanAction{}
case "tunnel_key":
action = &TunnelKeyAction{}
case "skbedit":
@ -816,7 +859,17 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
tcTs := nl.DeserializeTcf(adatum.Value)
actionTimestamp = toTimeStamp(tcTs)
}
case "vlan":
switch adatum.Attr.Type {
case nl.TCA_VLAN_PARMS:
vlan := *nl.DeserializeTcVlan(adatum.Value)
action.(*VlanAction).ActionAttrs = ActionAttrs{}
toAttrs(&vlan.TcGen, action.Attrs())
action.(*VlanAction).Action = VlanAct(vlan.Action)
case nl.TCA_VLAN_PUSH_VLAN_ID:
vlanId := native.Uint16(adatum.Value[0:2])
action.(*VlanAction).VlanID = vlanId
}
case "tunnel_key":
switch adatum.Attr.Type {
case nl.TCA_TUNNEL_KEY_PARMS:

View File

@ -1,16 +1,7 @@
package netlink
import (
"errors"
)
var (
// ErrAttrHeaderTruncated is returned when a netlink attribute's header is
// truncated.
ErrAttrHeaderTruncated = errors.New("attribute header truncated")
// ErrAttrBodyTruncated is returned when a netlink attribute's body is
// truncated.
ErrAttrBodyTruncated = errors.New("attribute body truncated")
"net"
)
type Fou struct {
@ -18,4 +9,8 @@ type Fou struct {
Port int
Protocol int
EncapType int
Local net.IP
Peer net.IP
PeerPort int
IfIndex int
}

View File

@ -1,3 +1,4 @@
//go:build linux
// +build linux
package netlink
@ -5,6 +6,8 @@ package netlink
import (
"encoding/binary"
"errors"
"log"
"net"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
@ -29,6 +32,12 @@ const (
FOU_ATTR_IPPROTO
FOU_ATTR_TYPE
FOU_ATTR_REMCSUM_NOPARTIAL
FOU_ATTR_LOCAL_V4
FOU_ATTR_LOCAL_V6
FOU_ATTR_PEER_V4
FOU_ATTR_PEER_V6
FOU_ATTR_PEER_PORT
FOU_ATTR_IFINDEX
FOU_ATTR_MAX = FOU_ATTR_REMCSUM_NOPARTIAL
)
@ -128,10 +137,14 @@ func (h *Handle) FouDel(f Fou) error {
return nil
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func FouList(fam int) ([]Fou, error) {
return pkgHandle.FouList(fam)
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) FouList(fam int) ([]Fou, error) {
fam_id, err := FouFamilyId()
if err != nil {
@ -150,9 +163,9 @@ func (h *Handle) FouList(fam int) ([]Fou, error) {
req.AddRawData(raw)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(err, ErrDumpInterrupted) {
return nil, executeErr
}
fous := make([]Fou, 0, len(msgs))
@ -165,45 +178,32 @@ func (h *Handle) FouList(fam int) ([]Fou, error) {
fous = append(fous, f)
}
return fous, nil
return fous, executeErr
}
func deserializeFouMsg(msg []byte) (Fou, error) {
// we'll skip to byte 4 to first attribute
msg = msg[3:]
var shift int
fou := Fou{}
for {
// attribute header is at least 16 bits
if len(msg) < 4 {
return fou, ErrAttrHeaderTruncated
}
lgt := int(binary.BigEndian.Uint16(msg[0:2]))
if len(msg) < lgt+4 {
return fou, ErrAttrBodyTruncated
}
attr := binary.BigEndian.Uint16(msg[2:4])
shift = lgt + 3
switch attr {
for attr := range nl.ParseAttributes(msg[4:]) {
switch attr.Type {
case FOU_ATTR_AF:
fou.Family = int(msg[5])
fou.Family = int(attr.Value[0])
case FOU_ATTR_PORT:
fou.Port = int(binary.BigEndian.Uint16(msg[5:7]))
// port is 2 bytes
shift = lgt + 2
fou.Port = int(networkOrder.Uint16(attr.Value))
case FOU_ATTR_IPPROTO:
fou.Protocol = int(msg[5])
fou.Protocol = int(attr.Value[0])
case FOU_ATTR_TYPE:
fou.EncapType = int(msg[5])
}
msg = msg[shift:]
if len(msg) < 4 {
break
fou.EncapType = int(attr.Value[0])
case FOU_ATTR_LOCAL_V4, FOU_ATTR_LOCAL_V6:
fou.Local = net.IP(attr.Value)
case FOU_ATTR_PEER_V4, FOU_ATTR_PEER_V6:
fou.Peer = net.IP(attr.Value)
case FOU_ATTR_PEER_PORT:
fou.PeerPort = int(networkOrder.Uint16(attr.Value))
case FOU_ATTR_IFINDEX:
fou.IfIndex = int(native.Uint16(attr.Value))
default:
log.Printf("unknown fou attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
}
}

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux
package netlink

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"syscall"
@ -126,6 +127,8 @@ func parseFamilies(msgs [][]byte) ([]*GenlFamily, error) {
return families, nil
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
msg := &nl.Genlmsg{
Command: nl.GENL_CTRL_CMD_GETFAMILY,
@ -133,13 +136,19 @@ func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
}
req := h.newNetlinkRequest(nl.GENL_ID_CTRL, unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
families, err := parseFamilies(msgs)
if err != nil {
return nil, err
}
return parseFamilies(msgs)
return families, executeErr
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func GenlFamilyList() ([]*GenlFamily, error) {
return pkgHandle.GenlFamilyList()
}

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"net"
"strings"
@ -74,6 +75,8 @@ func parsePDP(msgs [][]byte) ([]*PDP, error) {
return pdps, nil
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) GTPPDPList() ([]*PDP, error) {
f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
if err != nil {
@ -85,13 +88,19 @@ func (h *Handle) GTPPDPList() ([]*PDP, error) {
}
req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(err, ErrDumpInterrupted) {
return nil, executeErr
}
pdps, err := parsePDP(msgs)
if err != nil {
return nil, err
}
return parsePDP(msgs)
return pdps, executeErr
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func GTPPDPList() ([]*PDP, error) {
return pkgHandle.GTPPDPList()
}

View File

@ -56,6 +56,8 @@ type LinkAttrs struct {
Vfs []VfInfo // virtual functions available on link
Group uint32
PermHWAddr net.HardwareAddr
ParentDev string
ParentDevBus string
Slave LinkSlave
}
@ -377,6 +379,13 @@ const (
NETKIT_POLICY_BLACKHOLE NetkitPolicy = 2
)
type NetkitScrub int
const (
NETKIT_SCRUB_NONE NetkitScrub = 0
NETKIT_SCRUB_DEFAULT NetkitScrub = 1
)
func (n *Netkit) IsPrimary() bool {
return n.isPrimary
}
@ -391,6 +400,9 @@ type Netkit struct {
Mode NetkitMode
Policy NetkitPolicy
PeerPolicy NetkitPolicy
Scrub NetkitScrub
PeerScrub NetkitScrub
supportsScrub bool
isPrimary bool
peerLinkAttrs LinkAttrs
}
@ -403,6 +415,10 @@ func (n *Netkit) Type() string {
return "netkit"
}
func (n *Netkit) SupportsScrub() bool {
return n.supportsScrub
}
// Veth devices must specify PeerName on create
type Veth struct {
LinkAttrs
@ -761,19 +777,19 @@ const (
)
var bondXmitHashPolicyToString = map[BondXmitHashPolicy]string{
BOND_XMIT_HASH_POLICY_LAYER2: "layer2",
BOND_XMIT_HASH_POLICY_LAYER3_4: "layer3+4",
BOND_XMIT_HASH_POLICY_LAYER2_3: "layer2+3",
BOND_XMIT_HASH_POLICY_ENCAP2_3: "encap2+3",
BOND_XMIT_HASH_POLICY_ENCAP3_4: "encap3+4",
BOND_XMIT_HASH_POLICY_LAYER2: "layer2",
BOND_XMIT_HASH_POLICY_LAYER3_4: "layer3+4",
BOND_XMIT_HASH_POLICY_LAYER2_3: "layer2+3",
BOND_XMIT_HASH_POLICY_ENCAP2_3: "encap2+3",
BOND_XMIT_HASH_POLICY_ENCAP3_4: "encap3+4",
BOND_XMIT_HASH_POLICY_VLAN_SRCMAC: "vlan+srcmac",
}
var StringToBondXmitHashPolicyMap = map[string]BondXmitHashPolicy{
"layer2": BOND_XMIT_HASH_POLICY_LAYER2,
"layer3+4": BOND_XMIT_HASH_POLICY_LAYER3_4,
"layer2+3": BOND_XMIT_HASH_POLICY_LAYER2_3,
"encap2+3": BOND_XMIT_HASH_POLICY_ENCAP2_3,
"encap3+4": BOND_XMIT_HASH_POLICY_ENCAP3_4,
"layer2": BOND_XMIT_HASH_POLICY_LAYER2,
"layer3+4": BOND_XMIT_HASH_POLICY_LAYER3_4,
"layer2+3": BOND_XMIT_HASH_POLICY_LAYER2_3,
"encap2+3": BOND_XMIT_HASH_POLICY_ENCAP2_3,
"encap3+4": BOND_XMIT_HASH_POLICY_ENCAP3_4,
"vlan+srcmac": BOND_XMIT_HASH_POLICY_VLAN_SRCMAC,
}

View File

@ -3,6 +3,7 @@ package netlink
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"net"
@ -1807,20 +1808,20 @@ func (h *Handle) LinkDel(link Link) error {
}
func (h *Handle) linkByNameDump(name string) (Link, error) {
links, err := h.LinkList()
if err != nil {
return nil, err
links, executeErr := h.LinkList()
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
for _, link := range links {
if link.Attrs().Name == name {
return link, nil
return link, executeErr
}
// support finding interfaces also via altnames
for _, altName := range link.Attrs().AltNames {
if altName == name {
return link, nil
return link, executeErr
}
}
}
@ -1828,25 +1829,33 @@ func (h *Handle) linkByNameDump(name string) (Link, error) {
}
func (h *Handle) linkByAliasDump(alias string) (Link, error) {
links, err := h.LinkList()
if err != nil {
return nil, err
links, executeErr := h.LinkList()
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
for _, link := range links {
if link.Attrs().Alias == alias {
return link, nil
return link, executeErr
}
}
return nil, LinkNotFoundError{fmt.Errorf("Link alias %s not found", alias)}
}
// LinkByName finds a link by name and returns a pointer to the object.
//
// If the kernel doesn't support IFLA_IFNAME, this method will fall back to
// filtering a dump of all link names. In this case, if the returned error is
// [ErrDumpInterrupted] the result may be missing or outdated.
func LinkByName(name string) (Link, error) {
return pkgHandle.LinkByName(name)
}
// LinkByName finds a link by name and returns a pointer to the object.
//
// If the kernel doesn't support IFLA_IFNAME, this method will fall back to
// filtering a dump of all link names. In this case, if the returned error is
// [ErrDumpInterrupted] the result may be missing or outdated.
func (h *Handle) LinkByName(name string) (Link, error) {
if h.lookupByDump {
return h.linkByNameDump(name)
@ -1879,12 +1888,20 @@ func (h *Handle) LinkByName(name string) (Link, error) {
// LinkByAlias finds a link by its alias and returns a pointer to the object.
// If there are multiple links with the alias it returns the first one
//
// If the kernel doesn't support IFLA_IFALIAS, this method will fall back to
// filtering a dump of all link names. In this case, if the returned error is
// [ErrDumpInterrupted] the result may be missing or outdated.
func LinkByAlias(alias string) (Link, error) {
return pkgHandle.LinkByAlias(alias)
}
// LinkByAlias finds a link by its alias and returns a pointer to the object.
// If there are multiple links with the alias it returns the first one
//
// If the kernel doesn't support IFLA_IFALIAS, this method will fall back to
// filtering a dump of all link names. In this case, if the returned error is
// [ErrDumpInterrupted] the result may be missing or outdated.
func (h *Handle) LinkByAlias(alias string) (Link, error) {
if h.lookupByDump {
return h.linkByAliasDump(alias)
@ -2246,6 +2263,10 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
break
}
}
case unix.IFLA_PARENT_DEV_NAME:
base.ParentDev = string(attr.Value[:len(attr.Value)-1])
case unix.IFLA_PARENT_DEV_BUS_NAME:
base.ParentDevBus = string(attr.Value[:len(attr.Value)-1])
}
}
@ -2321,6 +2342,9 @@ func LinkList() ([]Link, error) {
// LinkList gets a list of link devices.
// Equivalent to: `ip link show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) LinkList() ([]Link, error) {
// NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need
// to get the message ourselves to parse link type.
@ -2331,9 +2355,9 @@ func (h *Handle) LinkList() ([]Link, error) {
attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF))
req.AddData(attr)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []Link
@ -2345,7 +2369,7 @@ func (h *Handle) LinkList() ([]Link, error) {
res = append(res, link)
}
return res, nil
return res, executeErr
}
// LinkUpdate is used to pass information back from LinkSubscribe()
@ -2381,6 +2405,10 @@ type LinkSubscribeOptions struct {
// LinkSubscribeWithOptions work like LinkSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
//
// When options.ListExisting is true, options.ErrorCallback may be
// called with [ErrDumpInterrupted] to indicate that results from
// the initial dump of links may be inconsistent or incomplete.
func LinkSubscribeWithOptions(ch chan<- LinkUpdate, done <-chan struct{}, options LinkSubscribeOptions) error {
if options.Namespace == nil {
none := netns.None()
@ -2440,6 +2468,9 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
continue
}
for _, m := range msgs {
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 && cberr != nil {
cberr(ErrDumpInterrupted)
}
if m.Header.Type == unix.NLMSG_DONE {
continue
}
@ -2649,6 +2680,8 @@ func addNetkitAttrs(nk *Netkit, linkInfo *nl.RtAttr, flag int) error {
data.AddRtAttr(nl.IFLA_NETKIT_MODE, nl.Uint32Attr(uint32(nk.Mode)))
data.AddRtAttr(nl.IFLA_NETKIT_POLICY, nl.Uint32Attr(uint32(nk.Policy)))
data.AddRtAttr(nl.IFLA_NETKIT_PEER_POLICY, nl.Uint32Attr(uint32(nk.PeerPolicy)))
data.AddRtAttr(nl.IFLA_NETKIT_SCRUB, nl.Uint32Attr(uint32(nk.Scrub)))
data.AddRtAttr(nl.IFLA_NETKIT_PEER_SCRUB, nl.Uint32Attr(uint32(nk.PeerScrub)))
if (flag & unix.NLM_F_EXCL) == 0 {
// Modifying peer link attributes will not take effect
@ -2709,6 +2742,12 @@ func parseNetkitData(link Link, data []syscall.NetlinkRouteAttr) {
netkit.Policy = NetkitPolicy(native.Uint32(datum.Value[0:4]))
case nl.IFLA_NETKIT_PEER_POLICY:
netkit.PeerPolicy = NetkitPolicy(native.Uint32(datum.Value[0:4]))
case nl.IFLA_NETKIT_SCRUB:
netkit.supportsScrub = true
netkit.Scrub = NetkitScrub(native.Uint32(datum.Value[0:4]))
case nl.IFLA_NETKIT_PEER_SCRUB:
netkit.supportsScrub = true
netkit.PeerScrub = NetkitScrub(native.Uint32(datum.Value[0:4]))
}
}
}
@ -3006,7 +3045,6 @@ func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) {
}
}
// copied from pkg/net_linux.go
func linkFlags(rawFlags uint32) net.Flags {
var f net.Flags
if rawFlags&unix.IFF_UP != 0 {
@ -3024,6 +3062,9 @@ func linkFlags(rawFlags uint32) net.Flags {
if rawFlags&unix.IFF_MULTICAST != 0 {
f |= net.FlagMulticast
}
if rawFlags&unix.IFF_RUNNING != 0 {
f |= net.FlagRunning
}
return f
}

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"net"
"syscall"
@ -206,6 +207,9 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
// NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func NeighList(linkIndex, family int) ([]Neigh, error) {
return pkgHandle.NeighList(linkIndex, family)
}
@ -213,6 +217,9 @@ func NeighList(linkIndex, family int) ([]Neigh, error) {
// NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
return pkgHandle.NeighProxyList(linkIndex, family)
}
@ -220,6 +227,9 @@ func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
// NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
return h.NeighListExecute(Ndmsg{
Family: uint8(family),
@ -230,6 +240,9 @@ func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
// NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link, ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
return h.NeighListExecute(Ndmsg{
Family: uint8(family),
@ -239,18 +252,24 @@ func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
}
// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
return pkgHandle.NeighListExecute(msg)
}
// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
req.AddData(&msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []Neigh
@ -281,7 +300,7 @@ func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
res = append(res, *neigh)
}
return res, nil
return res, executeErr
}
func NeighDeserialize(m []byte) (*Neigh, error) {
@ -364,6 +383,10 @@ type NeighSubscribeOptions struct {
// NeighSubscribeWithOptions work like NeighSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
//
// When options.ListExisting is true, options.ErrorCallback may be
// called with [ErrDumpInterrupted] to indicate that results from
// the initial dump of links may be inconsistent or incomplete.
func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
if options.Namespace == nil {
none := netns.None()
@ -428,6 +451,9 @@ func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <
continue
}
for _, m := range msgs {
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 && cberr != nil {
cberr(ErrDumpInterrupted)
}
if m.Header.Type == unix.NLMSG_DONE {
if listExisting {
// This will be called after handling AF_UNSPEC

View File

@ -9,3 +9,6 @@ const (
FAMILY_V6 = nl.FAMILY_V6
FAMILY_MPLS = nl.FAMILY_MPLS
)
// ErrDumpInterrupted is an alias for [nl.ErrDumpInterrupted].
var ErrDumpInterrupted = nl.ErrDumpInterrupted

View File

@ -38,6 +38,8 @@ const (
IFLA_NETKIT_POLICY
IFLA_NETKIT_PEER_POLICY
IFLA_NETKIT_MODE
IFLA_NETKIT_SCRUB
IFLA_NETKIT_PEER_SCRUB
IFLA_NETKIT_MAX = IFLA_NETKIT_MODE
)

View File

@ -45,6 +45,26 @@ var SocketTimeoutTv = unix.Timeval{Sec: 60, Usec: 0}
// ErrorMessageReporting is the default error message reporting configuration for the new netlink sockets
var EnableErrorMessageReporting bool = false
// ErrDumpInterrupted is an instance of errDumpInterrupted, used to report that
// a netlink function has set the NLM_F_DUMP_INTR flag in a response message,
// indicating that the results may be incomplete or inconsistent.
var ErrDumpInterrupted = errDumpInterrupted{}
// errDumpInterrupted is an error type, used to report that NLM_F_DUMP_INTR was
// set in a netlink response.
type errDumpInterrupted struct{}
func (errDumpInterrupted) Error() string {
return "results may be incomplete or inconsistent"
}
// Before errDumpInterrupted was introduced, EINTR was returned when a netlink
// response had NLM_F_DUMP_INTR. Retain backward compatibility with code that
// may be checking for EINTR using Is.
func (e errDumpInterrupted) Is(target error) bool {
return target == unix.EINTR
}
// GetIPFamily returns the family type of a net.IP.
func GetIPFamily(ip net.IP) int {
if len(ip) <= net.IPv4len {
@ -494,22 +514,26 @@ func (req *NetlinkRequest) AddRawData(data []byte) {
// Execute the request against the given sockType.
// Returns a list of netlink messages in serialized format, optionally filtered
// by resType.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) {
var res [][]byte
err := req.ExecuteIter(sockType, resType, func(msg []byte) bool {
res = append(res, msg)
return true
})
if err != nil {
if err != nil && !errors.Is(err, ErrDumpInterrupted) {
return nil, err
}
return res, nil
return res, err
}
// ExecuteIter executes the request against the given sockType.
// Calls the provided callback func once for each netlink message.
// If the callback returns false, it is not called again, but
// the remaining messages are consumed/discarded.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
//
// Thread safety: ExecuteIter holds a lock on the socket until
// it finishes iteration so the callback must not call back into
@ -561,6 +585,8 @@ func (req *NetlinkRequest) ExecuteIter(sockType int, resType uint16, f func(msg
return err
}
dumpIntr := false
done:
for {
msgs, from, err := s.Receive()
@ -582,7 +608,7 @@ done:
}
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 {
return syscall.Errno(unix.EINTR)
dumpIntr = true
}
if m.Header.Type == unix.NLMSG_DONE || m.Header.Type == unix.NLMSG_ERROR {
@ -636,6 +662,9 @@ done:
}
}
}
if dumpIntr {
return ErrDumpInterrupted
}
return nil
}

View File

@ -115,6 +115,7 @@ const (
SizeofTcConnmark = SizeofTcGen + 0x04
SizeofTcCsum = SizeofTcGen + 0x04
SizeofTcMirred = SizeofTcGen + 0x08
SizeofTcVlan = SizeofTcGen + 0x04
SizeofTcTunnelKey = SizeofTcGen + 0x04
SizeofTcSkbEdit = SizeofTcGen
SizeofTcPolice = 2*SizeofTcRateSpec + 0x20
@ -816,6 +817,41 @@ func (x *TcMirred) Serialize() []byte {
return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:]
}
const (
TCA_VLAN_UNSPEC = iota
TCA_VLAN_TM
TCA_VLAN_PARMS
TCA_VLAN_PUSH_VLAN_ID
TCA_VLAN_PUSH_VLAN_PROTOCOL
TCA_VLAN_PAD
TCA_VLAN_PUSH_VLAN_PRIORITY
TCA_VLAN_PUSH_ETH_DST
TCA_VLAN_PUSH_ETH_SRC
TCA_VLAN_MAX
)
//struct tc_vlan {
// tc_gen;
// int v_action;
//};
type TcVlan struct {
TcGen
Action int32
}
func (msg *TcVlan) Len() int {
return SizeofTcVlan
}
func DeserializeTcVlan(b []byte) *TcVlan {
return (*TcVlan)(unsafe.Pointer(&b[0:SizeofTcVlan][0]))
}
func (x *TcVlan) Serialize() []byte {
return (*(*[SizeofTcVlan]byte)(unsafe.Pointer(x)))[:]
}
const (
TCA_TUNNEL_KEY_UNSPEC = iota
TCA_TUNNEL_KEY_TM
@ -1239,8 +1275,8 @@ const (
)
// /* TCA_PEDIT_KEY_EX_HDR_TYPE_NETWROK is a special case for legacy users. It
// * means no specific header type - offset is relative to the network layer
// */
// - means no specific header type - offset is relative to the network layer
// */
type PeditHeaderType uint16
const (

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"syscall"
@ -8,10 +9,14 @@ import (
"golang.org/x/sys/unix"
)
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func LinkGetProtinfo(link Link) (Protinfo, error) {
return pkgHandle.LinkGetProtinfo(link)
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) {
base := link.Attrs()
h.ensureIndex(base)
@ -19,9 +24,9 @@ func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) {
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, 0)
if err != nil {
return pi, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return pi, executeErr
}
for _, m := range msgs {
@ -43,7 +48,7 @@ func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) {
}
pi = parseProtinfo(infos)
return pi, nil
return pi, executeErr
}
}
return pi, fmt.Errorf("Device with index %d not found", base.Index)

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"io/ioutil"
"strconv"
@ -338,6 +339,9 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
// QdiscList gets a list of qdiscs in the system.
// Equivalent to: `tc qdisc show`.
// The list can be filtered by link.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func QdiscList(link Link) ([]Qdisc, error) {
return pkgHandle.QdiscList(link)
}
@ -345,6 +349,9 @@ func QdiscList(link Link) ([]Qdisc, error) {
// QdiscList gets a list of qdiscs in the system.
// Equivalent to: `tc qdisc show`.
// The list can be filtered by link.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
req := h.newNetlinkRequest(unix.RTM_GETQDISC, unix.NLM_F_DUMP)
index := int32(0)
@ -359,9 +366,9 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
}
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []Qdisc
@ -497,7 +504,7 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
res = append(res, qdisc)
}
return res, nil
return res, executeErr
}
func parsePfifoFastData(qdisc Qdisc, value []byte) error {

View File

@ -3,6 +3,7 @@ package netlink
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
@ -85,19 +86,25 @@ func execRdmaSetLink(req *nl.NetlinkRequest) error {
// RdmaLinkList gets a list of RDMA link devices.
// Equivalent to: `rdma dev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func RdmaLinkList() ([]*RdmaLink, error) {
return pkgHandle.RdmaLinkList()
}
// RdmaLinkList gets a list of RDMA link devices.
// Equivalent to: `rdma dev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) {
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET)
req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP)
msgs, err := req.Execute(unix.NETLINK_RDMA, 0)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_RDMA, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []*RdmaLink
@ -109,17 +116,23 @@ func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) {
res = append(res, link)
}
return res, nil
return res, executeErr
}
// RdmaLinkByName finds a link by name and returns a pointer to the object if
// found and nil error, otherwise returns error code.
//
// If the returned error is [ErrDumpInterrupted], the result may be missing or
// outdated and the caller should retry.
func RdmaLinkByName(name string) (*RdmaLink, error) {
return pkgHandle.RdmaLinkByName(name)
}
// RdmaLinkByName finds a link by name and returns a pointer to the object if
// found and nil error, otherwise returns error code.
//
// If the returned error is [ErrDumpInterrupted], the result may be missing or
// outdated and the caller should retry.
func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) {
links, err := h.RdmaLinkList()
if err != nil {
@ -288,6 +301,8 @@ func RdmaLinkDel(name string) error {
}
// RdmaLinkDel deletes an rdma link.
//
// If the returned error is [ErrDumpInterrupted], the caller should retry.
func (h *Handle) RdmaLinkDel(name string) error {
link, err := h.RdmaLinkByName(name)
if err != nil {
@ -307,6 +322,7 @@ func (h *Handle) RdmaLinkDel(name string) error {
// RdmaLinkAdd adds an rdma link for the specified type to the network device.
// Similar to: rdma link add NAME type TYPE netdev NETDEV
//
// NAME - specifies the new name of the rdma link to add
// TYPE - specifies which rdma type to use. Link types:
// rxe - Soft RoCE driver

View File

@ -3,6 +3,7 @@ package netlink
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"strconv"
@ -1163,6 +1164,9 @@ func (h *Handle) prepareRouteReq(route *Route, req *nl.NetlinkRequest, msg *nl.R
// RouteList gets a list of routes in the system.
// Equivalent to: `ip route show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func RouteList(link Link, family int) ([]Route, error) {
return pkgHandle.RouteList(link, family)
}
@ -1170,6 +1174,9 @@ func RouteList(link Link, family int) ([]Route, error) {
// RouteList gets a list of routes in the system.
// Equivalent to: `ip route show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) RouteList(link Link, family int) ([]Route, error) {
routeFilter := &Route{}
if link != nil {
@ -1188,6 +1195,9 @@ func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, e
// RouteListFiltered gets a list of routes in the system filtered with specified rules.
// All rules must be defined in RouteFilter struct
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
var res []Route
err := h.RouteListFilteredIter(family, filter, filterMask, func(route Route) (cont bool) {
@ -1202,17 +1212,22 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
// RouteListFilteredIter passes each route that matches the filter to the given iterator func. Iteration continues
// until all routes are loaded or the func returns false.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func RouteListFilteredIter(family int, filter *Route, filterMask uint64, f func(Route) (cont bool)) error {
return pkgHandle.RouteListFilteredIter(family, filter, filterMask, f)
}
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) RouteListFilteredIter(family int, filter *Route, filterMask uint64, f func(Route) (cont bool)) error {
req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP)
rtmsg := &nl.RtMsg{}
rtmsg.Family = uint8(family)
var parseErr error
err := h.routeHandleIter(filter, req, rtmsg, func(m []byte) bool {
executeErr := h.routeHandleIter(filter, req, rtmsg, func(m []byte) bool {
msg := nl.DeserializeRtMsg(m)
if family != FAMILY_ALL && msg.Family != uint8(family) {
// Ignore routes not matching requested family
@ -1270,13 +1285,13 @@ func (h *Handle) RouteListFilteredIter(family int, filter *Route, filterMask uin
}
return f(route)
})
if err != nil {
return err
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return executeErr
}
if parseErr != nil {
return parseErr
}
return nil
return executeErr
}
// deserializeRoute decodes a binary netlink message into a Route struct
@ -1684,6 +1699,10 @@ type RouteSubscribeOptions struct {
// RouteSubscribeWithOptions work like RouteSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
//
// When options.ListExisting is true, options.ErrorCallback may be
// called with [ErrDumpInterrupted] to indicate that results from
// the initial dump of links may be inconsistent or incomplete.
func RouteSubscribeWithOptions(ch chan<- RouteUpdate, done <-chan struct{}, options RouteSubscribeOptions) error {
if options.Namespace == nil {
none := netns.None()
@ -1743,6 +1762,9 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
continue
}
for _, m := range msgs {
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 && cberr != nil {
cberr(ErrDumpInterrupted)
}
if m.Header.Type == unix.NLMSG_DONE {
continue
}

View File

@ -2,6 +2,7 @@ package netlink
import (
"bytes"
"errors"
"fmt"
"net"
@ -183,12 +184,18 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
// RuleList lists rules in the system.
// Equivalent to: ip rule list
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func RuleList(family int) ([]Rule, error) {
return pkgHandle.RuleList(family)
}
// RuleList lists rules in the system.
// Equivalent to: ip rule list
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) RuleList(family int) ([]Rule, error) {
return h.RuleListFiltered(family, nil, 0)
}
@ -196,20 +203,26 @@ func (h *Handle) RuleList(family int) ([]Rule, error) {
// RuleListFiltered gets a list of rules in the system filtered by the
// specified rule template `filter`.
// Equivalent to: ip rule list
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func RuleListFiltered(family int, filter *Rule, filterMask uint64) ([]Rule, error) {
return pkgHandle.RuleListFiltered(family, filter, filterMask)
}
// RuleListFiltered lists rules in the system.
// Equivalent to: ip rule list
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) ([]Rule, error) {
req := h.newNetlinkRequest(unix.RTM_GETRULE, unix.NLM_F_DUMP|unix.NLM_F_REQUEST)
msg := nl.NewIfInfomsg(family)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWRULE)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWRULE)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res = make([]Rule, 0)
@ -306,7 +319,7 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
res = append(res, *rule)
}
return res, nil
return res, executeErr
}
func (pr *RulePortRange) toRtAttrData() []byte {

View File

@ -157,6 +157,9 @@ func (u *UnixSocket) deserialize(b []byte) error {
}
// SocketGet returns the Socket identified by its local and remote addresses.
//
// If the returned error is [ErrDumpInterrupted], the search for a result may
// be incomplete and the caller should retry.
func (h *Handle) SocketGet(local, remote net.Addr) (*Socket, error) {
var protocol uint8
var localIP, remoteIP net.IP
@ -232,6 +235,9 @@ func (h *Handle) SocketGet(local, remote net.Addr) (*Socket, error) {
}
// SocketGet returns the Socket identified by its local and remote addresses.
//
// If the returned error is [ErrDumpInterrupted], the search for a result may
// be incomplete and the caller should retry.
func SocketGet(local, remote net.Addr) (*Socket, error) {
return pkgHandle.SocketGet(local, remote)
}
@ -283,6 +289,9 @@ func SocketDestroy(local, remote net.Addr) error {
}
// SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type and return with extension TCP info.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
// Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
@ -295,9 +304,9 @@ func (h *Handle) SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error)
// Do the query and parse the result
var result []*InetDiagTCPInfoResp
var err error
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{}
var err error
if err = sockInfo.deserialize(msg); err != nil {
return false
}
@ -315,18 +324,24 @@ func (h *Handle) SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error)
return true
})
if err != nil {
return nil, err
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
return result, nil
return result, executeErr
}
// SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type and return with extension TCP info.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
return pkgHandle.SocketDiagTCPInfo(family)
}
// SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) SocketDiagTCP(family uint8) ([]*Socket, error) {
// Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
@ -339,27 +354,32 @@ func (h *Handle) SocketDiagTCP(family uint8) ([]*Socket, error) {
// Do the query and parse the result
var result []*Socket
var err error
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{}
if err = sockInfo.deserialize(msg); err != nil {
if err := sockInfo.deserialize(msg); err != nil {
return false
}
result = append(result, sockInfo)
return true
})
if err != nil {
return nil, err
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
return result, nil
return result, executeErr
}
// SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func SocketDiagTCP(family uint8) ([]*Socket, error) {
return pkgHandle.SocketDiagTCP(family)
}
// SocketDiagUDPInfo requests INET_DIAG_INFO for UDP protocol for specified family type and return with extension info.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error) {
// Construct the request
var extensions uint8
@ -377,14 +397,14 @@ func (h *Handle) SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error)
// Do the query and parse the result
var result []*InetDiagUDPInfoResp
var err error
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{}
if err = sockInfo.deserialize(msg); err != nil {
if err := sockInfo.deserialize(msg); err != nil {
return false
}
var attrs []syscall.NetlinkRouteAttr
var err error
if attrs, err = nl.ParseRouteAttr(msg[sizeofSocket:]); err != nil {
return false
}
@ -397,18 +417,24 @@ func (h *Handle) SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error)
result = append(result, res)
return true
})
if err != nil {
return nil, err
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
return result, nil
return result, executeErr
}
// SocketDiagUDPInfo requests INET_DIAG_INFO for UDP protocol for specified family type and return with extension info.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error) {
return pkgHandle.SocketDiagUDPInfo(family)
}
// SocketDiagUDP requests INET_DIAG_INFO for UDP protocol for specified family type and return related socket.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) SocketDiagUDP(family uint8) ([]*Socket, error) {
// Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
@ -421,27 +447,32 @@ func (h *Handle) SocketDiagUDP(family uint8) ([]*Socket, error) {
// Do the query and parse the result
var result []*Socket
var err error
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{}
if err = sockInfo.deserialize(msg); err != nil {
if err := sockInfo.deserialize(msg); err != nil {
return false
}
result = append(result, sockInfo)
return true
})
if err != nil {
return nil, err
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
return result, nil
return result, executeErr
}
// SocketDiagUDP requests INET_DIAG_INFO for UDP protocol for specified family type and return related socket.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func SocketDiagUDP(family uint8) ([]*Socket, error) {
return pkgHandle.SocketDiagUDP(family)
}
// UnixSocketDiagInfo requests UNIX_DIAG_INFO for unix sockets and return with extension info.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
// Construct the request
var extensions uint8
@ -456,10 +487,9 @@ func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
})
var result []*UnixDiagInfoResp
var err error
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &UnixSocket{}
if err = sockInfo.deserialize(msg); err != nil {
if err := sockInfo.deserialize(msg); err != nil {
return false
}
@ -469,6 +499,7 @@ func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
}
var attrs []syscall.NetlinkRouteAttr
var err error
if attrs, err = nl.ParseRouteAttr(msg[sizeofSocket:]); err != nil {
return false
}
@ -480,18 +511,24 @@ func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
result = append(result, res)
return true
})
if err != nil {
return nil, err
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
return result, nil
return result, executeErr
}
// UnixSocketDiagInfo requests UNIX_DIAG_INFO for unix sockets and return with extension info.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
return pkgHandle.UnixSocketDiagInfo()
}
// UnixSocketDiag requests UNIX_DIAG_INFO for unix sockets.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) UnixSocketDiag() ([]*UnixSocket, error) {
// Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
@ -501,10 +538,9 @@ func (h *Handle) UnixSocketDiag() ([]*UnixSocket, error) {
})
var result []*UnixSocket
var err error
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &UnixSocket{}
if err = sockInfo.deserialize(msg); err != nil {
if err := sockInfo.deserialize(msg); err != nil {
return false
}
@ -514,13 +550,16 @@ func (h *Handle) UnixSocketDiag() ([]*UnixSocket, error) {
}
return true
})
if err != nil {
return nil, err
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
return result, nil
return result, executeErr
}
// UnixSocketDiag requests UNIX_DIAG_INFO for unix sockets.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func UnixSocketDiag() ([]*UnixSocket, error) {
return pkgHandle.UnixSocketDiag()
}

View File

@ -52,8 +52,10 @@ func (s *XDPSocket) deserialize(b []byte) error {
return nil
}
// XDPSocketGet returns the XDP socket identified by its inode number and/or
// SocketXDPGetInfo returns the XDP socket identified by its inode number and/or
// socket cookie. Specify the cookie as SOCK_ANY_COOKIE if
//
// If the returned error is [ErrDumpInterrupted], the caller should retry.
func SocketXDPGetInfo(ino uint32, cookie uint64) (*XDPDiagInfoResp, error) {
// We have a problem here: dumping AF_XDP sockets currently does not support
// filtering. We thus need to dump all XSKs and then only filter afterwards
@ -85,6 +87,9 @@ func SocketXDPGetInfo(ino uint32, cookie uint64) (*XDPDiagInfoResp, error) {
}
// SocketDiagXDP requests XDP_DIAG_INFO for XDP family sockets.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func SocketDiagXDP() ([]*XDPDiagInfoResp, error) {
var result []*XDPDiagInfoResp
err := socketDiagXDPExecutor(func(m syscall.NetlinkMessage) error {
@ -105,10 +110,10 @@ func SocketDiagXDP() ([]*XDPDiagInfoResp, error) {
result = append(result, res)
return nil
})
if err != nil {
if err != nil && !errors.Is(err, ErrDumpInterrupted) {
return nil, err
}
return result, nil
return result, err
}
// socketDiagXDPExecutor requests XDP_DIAG_INFO for XDP family sockets.
@ -128,6 +133,7 @@ func socketDiagXDPExecutor(receiver func(syscall.NetlinkMessage) error) error {
return err
}
dumpIntr := false
loop:
for {
msgs, from, err := s.Receive()
@ -142,6 +148,9 @@ loop:
}
for _, m := range msgs {
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 {
dumpIntr = true
}
switch m.Header.Type {
case unix.NLMSG_DONE:
break loop
@ -154,6 +163,9 @@ loop:
}
}
}
if dumpIntr {
return ErrDumpInterrupted
}
return nil
}

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"net"
"syscall"
@ -118,6 +119,9 @@ func VDPADelDev(name string) error {
// VDPAGetDevList returns list of VDPA devices
// Equivalent to: `vdpa dev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func VDPAGetDevList() ([]*VDPADev, error) {
return pkgHandle.VDPAGetDevList()
}
@ -130,6 +134,9 @@ func VDPAGetDevByName(name string) (*VDPADev, error) {
// VDPAGetDevConfigList returns list of VDPA devices configurations
// Equivalent to: `vdpa dev config show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func VDPAGetDevConfigList() ([]*VDPADevConfig, error) {
return pkgHandle.VDPAGetDevConfigList()
}
@ -148,6 +155,9 @@ func VDPAGetDevVStats(name string, queueIndex uint32) (*VDPADevVStats, error) {
// VDPAGetMGMTDevList returns list of mgmt devices
// Equivalent to: `vdpa mgmtdev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func VDPAGetMGMTDevList() ([]*VDPAMGMTDev, error) {
return pkgHandle.VDPAGetMGMTDevList()
}
@ -261,9 +271,9 @@ func (h *Handle) vdpaRequest(command uint8, extraFlags int, attrs []*nl.RtAttr)
req.AddData(a)
}
resp, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
resp, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
messages := make([]vdpaNetlinkMessage, 0, len(resp))
for _, m := range resp {
@ -273,10 +283,13 @@ func (h *Handle) vdpaRequest(command uint8, extraFlags int, attrs []*nl.RtAttr)
}
messages = append(messages, attrs)
}
return messages, nil
return messages, executeErr
}
// dump all devices if dev is nil
//
// If dev is nil and the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) vdpaDevGet(dev *string) ([]*VDPADev, error) {
var extraFlags int
var attrs []*nl.RtAttr
@ -285,9 +298,9 @@ func (h *Handle) vdpaDevGet(dev *string) ([]*VDPADev, error) {
} else {
extraFlags = extraFlags | unix.NLM_F_DUMP
}
messages, err := h.vdpaRequest(nl.VDPA_CMD_DEV_GET, extraFlags, attrs)
if err != nil {
return nil, err
messages, executeErr := h.vdpaRequest(nl.VDPA_CMD_DEV_GET, extraFlags, attrs)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
devs := make([]*VDPADev, 0, len(messages))
for _, m := range messages {
@ -295,10 +308,13 @@ func (h *Handle) vdpaDevGet(dev *string) ([]*VDPADev, error) {
d.parseAttributes(m)
devs = append(devs, d)
}
return devs, nil
return devs, executeErr
}
// dump all devices if dev is nil
//
// If dev is nil, and the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) vdpaDevConfigGet(dev *string) ([]*VDPADevConfig, error) {
var extraFlags int
var attrs []*nl.RtAttr
@ -307,9 +323,9 @@ func (h *Handle) vdpaDevConfigGet(dev *string) ([]*VDPADevConfig, error) {
} else {
extraFlags = extraFlags | unix.NLM_F_DUMP
}
messages, err := h.vdpaRequest(nl.VDPA_CMD_DEV_CONFIG_GET, extraFlags, attrs)
if err != nil {
return nil, err
messages, executeErr := h.vdpaRequest(nl.VDPA_CMD_DEV_CONFIG_GET, extraFlags, attrs)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
cfgs := make([]*VDPADevConfig, 0, len(messages))
for _, m := range messages {
@ -317,10 +333,13 @@ func (h *Handle) vdpaDevConfigGet(dev *string) ([]*VDPADevConfig, error) {
cfg.parseAttributes(m)
cfgs = append(cfgs, cfg)
}
return cfgs, nil
return cfgs, executeErr
}
// dump all devices if dev is nil
//
// If dev is nil and the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) vdpaMGMTDevGet(bus, dev *string) ([]*VDPAMGMTDev, error) {
var extraFlags int
var attrs []*nl.RtAttr
@ -336,9 +355,9 @@ func (h *Handle) vdpaMGMTDevGet(bus, dev *string) ([]*VDPAMGMTDev, error) {
} else {
extraFlags = extraFlags | unix.NLM_F_DUMP
}
messages, err := h.vdpaRequest(nl.VDPA_CMD_MGMTDEV_GET, extraFlags, attrs)
if err != nil {
return nil, err
messages, executeErr := h.vdpaRequest(nl.VDPA_CMD_MGMTDEV_GET, extraFlags, attrs)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
cfgs := make([]*VDPAMGMTDev, 0, len(messages))
for _, m := range messages {
@ -346,7 +365,7 @@ func (h *Handle) vdpaMGMTDevGet(bus, dev *string) ([]*VDPAMGMTDev, error) {
cfg.parseAttributes(m)
cfgs = append(cfgs, cfg)
}
return cfgs, nil
return cfgs, executeErr
}
// VDPANewDev adds new VDPA device
@ -385,6 +404,9 @@ func (h *Handle) VDPADelDev(name string) error {
// VDPAGetDevList returns list of VDPA devices
// Equivalent to: `vdpa dev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) VDPAGetDevList() ([]*VDPADev, error) {
return h.vdpaDevGet(nil)
}
@ -404,6 +426,9 @@ func (h *Handle) VDPAGetDevByName(name string) (*VDPADev, error) {
// VDPAGetDevConfigList returns list of VDPA devices configurations
// Equivalent to: `vdpa dev config show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) VDPAGetDevConfigList() ([]*VDPADevConfig, error) {
return h.vdpaDevConfigGet(nil)
}
@ -441,6 +466,9 @@ func (h *Handle) VDPAGetDevVStats(name string, queueIndex uint32) (*VDPADevVStat
// VDPAGetMGMTDevList returns list of mgmt devices
// Equivalent to: `vdpa mgmtdev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) VDPAGetMGMTDevList() ([]*VDPAMGMTDev, error) {
return h.vdpaMGMTDevGet(nil, nil)
}

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"net"
@ -215,6 +216,9 @@ func (h *Handle) XfrmPolicyDel(policy *XfrmPolicy) error {
// XfrmPolicyList gets a list of xfrm policies in the system.
// Equivalent to: `ip xfrm policy show`.
// The list can be filtered by ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
return pkgHandle.XfrmPolicyList(family)
}
@ -222,15 +226,18 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
// XfrmPolicyList gets a list of xfrm policies in the system.
// Equivalent to: `ip xfrm policy show`.
// The list can be filtered by ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) XfrmPolicyList(family int) ([]XfrmPolicy, error) {
req := h.newNetlinkRequest(nl.XFRM_MSG_GETPOLICY, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(family)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []XfrmPolicy
@ -243,7 +250,7 @@ func (h *Handle) XfrmPolicyList(family int) ([]XfrmPolicy, error) {
return nil, err
}
}
return res, nil
return res, executeErr
}
// XfrmPolicyGet gets a the policy described by the index or selector, if found.

View File

@ -1,6 +1,7 @@
package netlink
import (
"errors"
"fmt"
"net"
"time"
@ -382,6 +383,9 @@ func (h *Handle) XfrmStateDel(state *XfrmState) error {
// XfrmStateList gets a list of xfrm states in the system.
// Equivalent to: `ip [-4|-6] xfrm state show`.
// The list can be filtered by ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func XfrmStateList(family int) ([]XfrmState, error) {
return pkgHandle.XfrmStateList(family)
}
@ -389,12 +393,15 @@ func XfrmStateList(family int) ([]XfrmState, error) {
// XfrmStateList gets a list of xfrm states in the system.
// Equivalent to: `ip xfrm state show`.
// The list can be filtered by ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) {
req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, unix.NLM_F_DUMP)
msgs, err := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []XfrmState
@ -407,7 +414,7 @@ func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) {
return nil, err
}
}
return res, nil
return res, executeErr
}
// XfrmStateGet gets the xfrm state described by the ID, if found.

2
vendor/modules.txt vendored
View File

@ -547,7 +547,7 @@ github.com/stretchr/testify/require
# github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75
## explicit; go 1.15
github.com/tmc/grpc-websocket-proxy/wsproxy
# github.com/vishvananda/netlink v1.3.1-0.20240905180732-b1ce50cfa9be
# github.com/vishvananda/netlink v1.3.1-0.20250206174618-62fb240731fa
## explicit; go 1.12
github.com/vishvananda/netlink
github.com/vishvananda/netlink/nl