Merge pull request #4764 from opencontainers/dependabot/go_modules/github.com/vishvananda/netlink-1.3.1

build(deps): bump github.com/vishvananda/netlink from 1.3.0 to 1.3.1
This commit is contained in:
Rodrigo Campos
2025-05-13 13:57:19 -03:00
committed by GitHub
53 changed files with 1973 additions and 375 deletions

4
go.mod
View File

@@ -20,7 +20,7 @@ require (
github.com/seccomp/libseccomp-golang v0.11.0 github.com/seccomp/libseccomp-golang v0.11.0
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/urfave/cli v1.22.16 github.com/urfave/cli v1.22.16
github.com/vishvananda/netlink v1.3.0 github.com/vishvananda/netlink v1.3.1
golang.org/x/net v0.40.0 golang.org/x/net v0.40.0
golang.org/x/sys v0.33.0 golang.org/x/sys v0.33.0
google.golang.org/protobuf v1.36.6 google.golang.org/protobuf v1.36.6
@@ -30,5 +30,5 @@ require (
github.com/cilium/ebpf v0.17.3 // indirect github.com/cilium/ebpf v0.17.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect github.com/vishvananda/netns v0.0.5 // indirect
) )

8
go.sum
View File

@@ -73,10 +73,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=

View File

@@ -1,9 +1,9 @@
package netlink package netlink
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"strings"
"syscall" "syscall"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
@@ -17,6 +17,7 @@ import (
// //
// If `addr` is an IPv4 address and the broadcast address is not given, it // 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. // 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 { func AddrAdd(link Link, addr *Addr) error {
return pkgHandle.AddrAdd(link, addr) return pkgHandle.AddrAdd(link, addr)
} }
@@ -27,6 +28,7 @@ func AddrAdd(link Link, addr *Addr) error {
// //
// If `addr` is an IPv4 address and the broadcast address is not given, it // 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. // 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 { 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) req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
return h.addrHandle(link, addr, req) return h.addrHandle(link, addr, req)
@@ -38,6 +40,7 @@ func (h *Handle) AddrAdd(link Link, addr *Addr) error {
// //
// If `addr` is an IPv4 address and the broadcast address is not given, it // 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. // 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 { func AddrReplace(link Link, addr *Addr) error {
return pkgHandle.AddrReplace(link, addr) return pkgHandle.AddrReplace(link, addr)
} }
@@ -48,6 +51,7 @@ func AddrReplace(link Link, addr *Addr) error {
// //
// If `addr` is an IPv4 address and the broadcast address is not given, it // 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. // 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 { 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) req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK)
return h.addrHandle(link, addr, req) return h.addrHandle(link, addr, req)
@@ -56,18 +60,13 @@ func (h *Handle) AddrReplace(link Link, addr *Addr) error {
// AddrDel will delete an IP address from a link device. // AddrDel will delete an IP address from a link device.
// //
// Equivalent to: `ip addr del $addr dev $link` // 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 { func AddrDel(link Link, addr *Addr) error {
return pkgHandle.AddrDel(link, addr) return pkgHandle.AddrDel(link, addr)
} }
// AddrDel will delete an IP address from a link device. // 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 // Equivalent to: `ip addr del $addr dev $link`
// will be automatically computed based on the IP mask if /30 or larger.
func (h *Handle) AddrDel(link Link, addr *Addr) error { func (h *Handle) AddrDel(link Link, addr *Addr) error {
req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK) req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK)
return h.addrHandle(link, addr, req) return h.addrHandle(link, addr, req)
@@ -81,9 +80,6 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
msg.Index = uint32(addr.LinkIndex) msg.Index = uint32(addr.LinkIndex)
} else { } else {
base := link.Attrs() base := link.Attrs()
if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
return fmt.Errorf("label must begin with interface name")
}
h.ensureIndex(base) h.ensureIndex(base)
msg.Index = uint32(base.Index) msg.Index = uint32(base.Index)
} }
@@ -141,6 +137,10 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
addr.Broadcast = calcBroadcast addr.Broadcast = calcBroadcast
} }
if net.IPv4zero.Equal(addr.Broadcast) {
addr.Broadcast = nil
}
if addr.Broadcast != nil { if addr.Broadcast != nil {
req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast)) req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast))
} }
@@ -169,6 +169,9 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
// AddrList gets a list of IP addresses in the system. // AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`. // Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family. // 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) { func AddrList(link Link, family int) ([]Addr, error) {
return pkgHandle.AddrList(link, family) return pkgHandle.AddrList(link, family)
} }
@@ -176,14 +179,17 @@ func AddrList(link Link, family int) ([]Addr, error) {
// AddrList gets a list of IP addresses in the system. // AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`. // Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family. // 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) { func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP)
msg := nl.NewIfAddrmsg(family) msg := nl.NewIfAddrmsg(family)
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
indexFilter := 0 indexFilter := 0
@@ -212,7 +218,7 @@ func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
res = append(res, addr) res = append(res, addr)
} }
return res, nil return res, executeErr
} }
func parseAddr(m []byte) (addr Addr, family int, err error) { func parseAddr(m []byte) (addr Addr, family int, err error) {

View File

@@ -1,29 +1,127 @@
package netlink package netlink
import ( import (
"errors"
"fmt" "fmt"
"syscall"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
// BridgeVlanTunnelShow gets vlanid-tunnelid mapping.
// Equivalent to: `bridge vlan tunnelshow`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func BridgeVlanTunnelShow() ([]nl.TunnelInfo, error) {
return pkgHandle.BridgeVlanTunnelShow()
}
func (h *Handle) BridgeVlanTunnelShow() ([]nl.TunnelInfo, 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, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
ret := make([]nl.TunnelInfo, 0)
for _, m := range msgs {
msg := nl.DeserializeIfInfomsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
for _, attr := range attrs {
switch attr.Attr.Type {
case unix.IFLA_AF_SPEC:
nestedAttrs, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, fmt.Errorf("failed to parse nested attr %v", err)
}
for _, nestAttr := range nestedAttrs {
switch nestAttr.Attr.Type {
case nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO:
ret, err = parseTunnelInfo(&nestAttr, ret)
if err != nil {
return nil, fmt.Errorf("failed to parse tunnelinfo %v", err)
}
}
}
}
}
}
return ret, executeErr
}
func parseTunnelInfo(nestAttr *syscall.NetlinkRouteAttr, results []nl.TunnelInfo) ([]nl.TunnelInfo, error) {
tunnelInfos, err := nl.ParseRouteAttr(nestAttr.Value)
if err != nil {
return nil, fmt.Errorf("failed to parse nested attr %v", err)
}
var tunnelId uint32
var vid uint16
var flag uint16
for _, tunnelInfo := range tunnelInfos {
switch tunnelInfo.Attr.Type {
case nl.IFLA_BRIDGE_VLAN_TUNNEL_ID:
tunnelId = native.Uint32(tunnelInfo.Value)
case nl.IFLA_BRIDGE_VLAN_TUNNEL_VID:
vid = native.Uint16(tunnelInfo.Value)
case nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS:
flag = native.Uint16(tunnelInfo.Value)
}
}
if flag == nl.BRIDGE_VLAN_INFO_RANGE_END {
lastTi := results[len(results)-1]
vni := lastTi.TunId + 1
for i := lastTi.Vid + 1; i < vid; i++ {
t := nl.TunnelInfo{
TunId: vni,
Vid: i,
}
results = append(results, t)
vni++
}
}
t := nl.TunnelInfo{
TunId: tunnelId,
Vid: vid,
}
results = append(results, t)
return results, nil
}
// BridgeVlanList gets a map of device id to bridge vlan infos. // BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show` // Equivalent to: `bridge vlan show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) { func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
return pkgHandle.BridgeVlanList() return pkgHandle.BridgeVlanList()
} }
// BridgeVlanList gets a map of device id to bridge vlan infos. // BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show` // 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) { func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE) msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
req.AddData(msg) req.AddData(msg)
req.AddData(nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN)))) 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) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
ret := make(map[int32][]*nl.BridgeVlanInfo) ret := make(map[int32][]*nl.BridgeVlanInfo)
for _, m := range msgs { for _, m := range msgs {
@@ -51,7 +149,39 @@ func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
} }
} }
} }
return ret, nil return ret, executeErr
}
// BridgeVlanAddTunnelInfo adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID tunnel_info id TUNID [ self ] [ master ]`
func BridgeVlanAddTunnelInfo(link Link, vid uint16, tunid uint32, self, master bool) error {
return pkgHandle.BridgeVlanAddTunnelInfo(link, vid, 0, tunid, 0, self, master)
}
// BridgeVlanAddRangeTunnelInfoRange adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID-VIDEND tunnel_info id VIN-VINEND [ self ] [ master ]`
func BridgeVlanAddRangeTunnelInfoRange(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return pkgHandle.BridgeVlanAddTunnelInfo(link, vid, vidEnd, tunid, tunidEnd, self, master)
}
func (h *Handle) BridgeVlanAddTunnelInfo(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, vidEnd, tunid, tunidEnd, false, false, self, master)
}
// BridgeVlanDelTunnelInfo adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID tunnel_info id TUNID [ self ] [ master ]`
func BridgeVlanDelTunnelInfo(link Link, vid uint16, tunid uint32, self, master bool) error {
return pkgHandle.BridgeVlanDelTunnelInfo(link, vid, 0, tunid, 0, self, master)
}
// BridgeVlanDelRangeTunnelInfoRange adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID-VIDEND tunnel_info id VIN-VINEND [ self ] [ master ]`
func BridgeVlanDelRangeTunnelInfoRange(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return pkgHandle.BridgeVlanDelTunnelInfo(link, vid, vidEnd, tunid, tunidEnd, self, master)
}
func (h *Handle) BridgeVlanDelTunnelInfo(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, vidEnd, tunid, tunidEnd, false, false, self, master)
} }
// BridgeVlanAdd adds a new vlan filter entry // BridgeVlanAdd adds a new vlan filter entry
@@ -63,7 +193,7 @@ func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) err
// BridgeVlanAdd adds a new vlan filter entry // BridgeVlanAdd adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` // Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error { func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, 0, pvid, untagged, self, master) return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, 0, 0, 0, pvid, untagged, self, master)
} }
// BridgeVlanAddRange adds a new vlan filter entry // BridgeVlanAddRange adds a new vlan filter entry
@@ -75,7 +205,7 @@ func BridgeVlanAddRange(link Link, vid, vidEnd uint16, pvid, untagged, self, mas
// BridgeVlanAddRange adds a new vlan filter entry // BridgeVlanAddRange adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID-VIDEND [ pvid ] [ untagged ] [ self ] [ master ]` // Equivalent to: `bridge vlan add dev DEV vid VID-VIDEND [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanAddRange(link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error { func (h *Handle) BridgeVlanAddRange(link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, vidEnd, pvid, untagged, self, master) return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, vidEnd, 0, 0, pvid, untagged, self, master)
} }
// BridgeVlanDel adds a new vlan filter entry // BridgeVlanDel adds a new vlan filter entry
@@ -87,7 +217,7 @@ func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) err
// BridgeVlanDel adds a new vlan filter entry // BridgeVlanDel adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` // Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error { func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, 0, pvid, untagged, self, master) return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, 0, 0, 0, pvid, untagged, self, master)
} }
// BridgeVlanDelRange adds a new vlan filter entry // BridgeVlanDelRange adds a new vlan filter entry
@@ -99,10 +229,10 @@ func BridgeVlanDelRange(link Link, vid, vidEnd uint16, pvid, untagged, self, mas
// BridgeVlanDelRange adds a new vlan filter entry // BridgeVlanDelRange adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID-VIDEND [ pvid ] [ untagged ] [ self ] [ master ]` // Equivalent to: `bridge vlan del dev DEV vid VID-VIDEND [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanDelRange(link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error { func (h *Handle) BridgeVlanDelRange(link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, vidEnd, pvid, untagged, self, master) return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, vidEnd, 0, 0, pvid, untagged, self, master)
} }
func (h *Handle) bridgeVlanModify(cmd int, link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error { func (h *Handle) bridgeVlanModify(cmd int, link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, pvid, untagged, self, master bool) error {
base := link.Attrs() base := link.Attrs()
h.ensureIndex(base) h.ensureIndex(base)
req := h.newNetlinkRequest(cmd, unix.NLM_F_ACK) req := h.newNetlinkRequest(cmd, unix.NLM_F_ACK)
@@ -122,6 +252,25 @@ func (h *Handle) bridgeVlanModify(cmd int, link Link, vid, vidEnd uint16, pvid,
if flags > 0 { if flags > 0 {
br.AddRtAttr(nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags)) br.AddRtAttr(nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags))
} }
if tunid != 0 {
if tunidEnd != 0 {
tiStart := br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO, nil)
tiStart.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_ID, nl.Uint32Attr(tunid))
tiStart.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_VID, nl.Uint16Attr(vid))
tiStart.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, nl.Uint16Attr(nl.BRIDGE_VLAN_INFO_RANGE_BEGIN))
tiEnd := br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO, nil)
tiEnd.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_ID, nl.Uint32Attr(tunidEnd))
tiEnd.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_VID, nl.Uint16Attr(vidEnd))
tiEnd.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, nl.Uint16Attr(nl.BRIDGE_VLAN_INFO_RANGE_END))
} else {
ti := br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO, nil)
ti.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_ID, nl.Uint32Attr(tunid))
ti.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_VID, nl.Uint16Attr(vid))
ti.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, nl.Uint16Attr(0))
}
} else {
vlanInfo := &nl.BridgeVlanInfo{Vid: vid} vlanInfo := &nl.BridgeVlanInfo{Vid: vid}
if pvid { if pvid {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID
@@ -142,6 +291,7 @@ func (h *Handle) bridgeVlanModify(cmd int, link Link, vid, vidEnd uint16, pvid,
} else { } else {
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize()) br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
} }
}
req.AddData(br) req.AddData(br)
_, err := req.Execute(unix.NETLINK_ROUTE, 0) _, err := req.Execute(unix.NETLINK_ROUTE, 0)

View File

@@ -1,6 +1,8 @@
package netlink package netlink
import ( import (
"errors"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix" "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. // ChainList gets a list of chains in the system.
// Equivalent to: `tc chain list`. // Equivalent to: `tc chain list`.
// The list can be filtered by link. // 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) { func ChainList(link Link, parent uint32) ([]Chain, error) {
return pkgHandle.ChainList(link, parent) 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. // ChainList gets a list of chains in the system.
// Equivalent to: `tc chain list`. // Equivalent to: `tc chain list`.
// The list can be filtered by link. // 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) { func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
req := h.newNetlinkRequest(unix.RTM_GETCHAIN, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETCHAIN, unix.NLM_F_DUMP)
index := int32(0) index := int32(0)
@@ -78,9 +86,9 @@ func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
} }
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []Chain var res []Chain
@@ -108,5 +116,5 @@ func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
res = append(res, chain) 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. // ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`. // Equivalent to: `tc class show`.
//
// Generally returns nothing if link and parent are not specified. // 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) { func ClassList(link Link, parent uint32) ([]Class, error) {
return pkgHandle.ClassList(link, parent) return pkgHandle.ClassList(link, parent)
} }
// ClassList gets a list of classes in the system. // ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`. // Equivalent to: `tc class show`.
//
// Generally returns nothing if link and parent are not specified. // 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) { func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
msg := &nl.TcMsg{ msg := &nl.TcMsg{
@@ -222,9 +228,9 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
} }
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []Class var res []Class
@@ -295,7 +301,7 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
res = append(res, class) res = append(res, class)
} }
return res, nil return res, executeErr
} }
func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) { func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {

View File

@@ -5,6 +5,7 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io/fs"
"net" "net"
"time" "time"
@@ -44,6 +45,9 @@ type InetFamily uint8
// ConntrackTableList returns the flow list of a table of a specific family // ConntrackTableList returns the flow list of a table of a specific family
// conntrack -L [table] [options] List conntrack or expectation table // 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) { func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
return pkgHandle.ConntrackTableList(table, family) return pkgHandle.ConntrackTableList(table, family)
} }
@@ -70,7 +74,7 @@ func ConntrackUpdate(table ConntrackTableType, family InetFamily, flow *Conntrac
// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
// conntrack -D [table] parameters Delete conntrack or expectation // conntrack -D [table] parameters Delete conntrack or expectation
// //
// Deprecated: use [ConntrackDeleteFilter] instead. // Deprecated: use [ConntrackDeleteFilters] instead.
func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) { func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {
return pkgHandle.ConntrackDeleteFilters(table, family, filter) return pkgHandle.ConntrackDeleteFilters(table, family, filter)
} }
@@ -83,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 // 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 // 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) { func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
res, err := h.dumpConntrackTable(table, family) res, executeErr := h.dumpConntrackTable(table, family)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
// Deserialize all the flows // Deserialize all the flows
@@ -95,7 +102,7 @@ func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily)
result = append(result, parseRawData(dataRaw)) 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 // ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
@@ -152,11 +159,18 @@ 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 // 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 // conntrack -D [table] parameters Delete conntrack or expectation
func (h *Handle) ConntrackDeleteFilters(table ConntrackTableType, family InetFamily, filters ...CustomConntrackFilter) (uint, error) { func (h *Handle) ConntrackDeleteFilters(table ConntrackTableType, family InetFamily, filters ...CustomConntrackFilter) (uint, error) {
var finalErr error
res, err := h.dumpConntrackTable(table, family) res, err := h.dumpConntrackTable(table, family)
if err != nil { if err != nil {
if !errors.Is(err, ErrDumpInterrupted) {
return 0, err 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 matched uint
for _, dataRaw := range res { for _, dataRaw := range res {
flow := parseRawData(dataRaw) flow := parseRawData(dataRaw)
@@ -165,15 +179,20 @@ func (h *Handle) ConntrackDeleteFilters(table ConntrackTableType, family InetFam
req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK) 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 // skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already
req2.AddRawData(dataRaw[4:]) req2.AddRawData(dataRaw[4:])
req2.Execute(unix.NETLINK_NETFILTER, 0) if _, err = req2.Execute(unix.NETLINK_NETFILTER, 0); err == nil || errors.Is(err, fs.ErrNotExist) {
matched++ matched++
// flow is already deleted, no need to match on other filters and continue to the next flow. // flow is already deleted, no need to match on other filters and continue to the next flow.
break break
} else {
totalFilterErrors++
} }
} }
} }
}
return matched, nil if totalFilterErrors > 0 {
finalErr = errors.Join(finalErr, fmt.Errorf("failed to delete %d conntrack flows with %d filters", totalFilterErrors, len(filters)))
}
return matched, finalErr
} }
func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest { func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest {

View File

@@ -33,7 +33,7 @@ func ConntrackTableFlush(table ConntrackTableType) error {
// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
// conntrack -D [table] parameters Delete conntrack or expectation // conntrack -D [table] parameters Delete conntrack or expectation
// //
// Deprecated: use [ConntrackDeleteFilter] instead. // Deprecated: use [ConntrackDeleteFilters] instead.
func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) {
return 0, ErrNotImplemented return 0, ErrNotImplemented
} }

View File

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

View File

@@ -54,6 +54,7 @@ func (filter *U32) Type() string {
type Flower struct { type Flower struct {
FilterAttrs FilterAttrs
ClassId uint32
DestIP net.IP DestIP net.IP
DestIPMask net.IPMask DestIPMask net.IPMask
SrcIP net.IP SrcIP net.IP
@@ -65,11 +66,18 @@ type Flower struct {
EncSrcIPMask net.IPMask EncSrcIPMask net.IPMask
EncDestPort uint16 EncDestPort uint16
EncKeyId uint32 EncKeyId uint32
SrcMac net.HardwareAddr
DestMac net.HardwareAddr
VlanId uint16
SkipHw bool SkipHw bool
SkipSw bool SkipSw bool
IPProto *nl.IPProto IPProto *nl.IPProto
DestPort uint16 DestPort uint16
SrcPort uint16 SrcPort uint16
SrcPortRangeMin uint16
SrcPortRangeMax uint16
DstPortRangeMin uint16
DstPortRangeMax uint16
Actions []Action Actions []Action
} }
@@ -135,6 +143,15 @@ func (filter *Flower) encode(parent *nl.RtAttr) error {
if filter.EncKeyId != 0 { if filter.EncKeyId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_KEY_ID, htonl(filter.EncKeyId)) 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 { if filter.IPProto != nil {
ipproto := *filter.IPProto ipproto := *filter.IPProto
parent.AddRtAttr(nl.TCA_FLOWER_KEY_IP_PROTO, ipproto.Serialize()) parent.AddRtAttr(nl.TCA_FLOWER_KEY_IP_PROTO, ipproto.Serialize())
@@ -159,6 +176,19 @@ func (filter *Flower) encode(parent *nl.RtAttr) error {
} }
} }
} }
if filter.SrcPortRangeMin != 0 && filter.SrcPortRangeMax != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_SRC_MIN, htons(filter.SrcPortRangeMin))
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_SRC_MAX, htons(filter.SrcPortRangeMax))
}
if filter.DstPortRangeMin != 0 && filter.DstPortRangeMax != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_DST_MIN, htons(filter.DstPortRangeMin))
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_DST_MAX, htons(filter.DstPortRangeMax))
}
if filter.ClassId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_CLASSID, nl.Uint32Attr(filter.ClassId))
}
var flags uint32 = 0 var flags uint32 = 0
if filter.SkipHw { if filter.SkipHw {
@@ -201,6 +231,13 @@ func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error {
filter.EncDestPort = ntohs(datum.Value) filter.EncDestPort = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_ENC_KEY_ID: case nl.TCA_FLOWER_KEY_ENC_KEY_ID:
filter.EncKeyId = ntohl(datum.Value) 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: case nl.TCA_FLOWER_KEY_IP_PROTO:
val := new(nl.IPProto) val := new(nl.IPProto)
*val = nl.IPProto(datum.Value[0]) *val = nl.IPProto(datum.Value[0])
@@ -228,6 +265,16 @@ func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error {
if skipHw != 0 { if skipHw != 0 {
filter.SkipHw = true filter.SkipHw = true
} }
case nl.TCA_FLOWER_KEY_PORT_SRC_MIN:
filter.SrcPortRangeMin = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_PORT_SRC_MAX:
filter.SrcPortRangeMax = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_PORT_DST_MIN:
filter.DstPortRangeMin = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_PORT_DST_MAX:
filter.DstPortRangeMax = ntohs(datum.Value)
case nl.TCA_FLOWER_CLASSID:
filter.ClassId = native.Uint32(datum.Value)
} }
} }
return nil return nil
@@ -405,14 +452,20 @@ func (h *Handle) filterModify(filter Filter, proto, flags int) error {
// FilterList gets a list of filters in the system. // FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`. // Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified. // 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) { func FilterList(link Link, parent uint32) ([]Filter, error) {
return pkgHandle.FilterList(link, parent) return pkgHandle.FilterList(link, parent)
} }
// FilterList gets a list of filters in the system. // FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`. // Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified. // 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) { func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP)
msg := &nl.TcMsg{ msg := &nl.TcMsg{
@@ -426,9 +479,9 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
} }
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []Filter var res []Filter
@@ -516,7 +569,7 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
} }
} }
return res, nil return res, executeErr
} }
func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) { func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) {
@@ -616,6 +669,22 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
} }
toTcGen(action.Attrs(), &mirred.TcGen) toTcGen(action.Attrs(), &mirred.TcGen)
aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize()) 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: case *TunnelKeyAction:
table := attr.AddRtAttr(tabIndex, nil) table := attr.AddRtAttr(tabIndex, nil)
tabIndex++ tabIndex++
@@ -699,6 +768,17 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize()) aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd))) aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd)))
aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name)) aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name))
case *SampleAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("sample"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
gen := nl.TcGen{}
toTcGen(action.Attrs(), &gen)
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_RATE, nl.Uint32Attr(action.Rate))
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP, nl.Uint32Attr(action.Group))
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_TRUNC_SIZE, nl.Uint32Attr(action.TruncSize))
case *GenericAction: case *GenericAction:
table := attr.AddRtAttr(tabIndex, nil) table := attr.AddRtAttr(tabIndex, nil)
tabIndex++ tabIndex++
@@ -711,6 +791,7 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
table := attr.AddRtAttr(tabIndex, nil) table := attr.AddRtAttr(tabIndex, nil)
tabIndex++ tabIndex++
pedit := nl.TcPedit{} pedit := nl.TcPedit{}
toTcGen(action.Attrs(), &pedit.Sel.TcGen)
if action.SrcMacAddr != nil { if action.SrcMacAddr != nil {
pedit.SetEthSrc(action.SrcMacAddr) pedit.SetEthSrc(action.SrcMacAddr)
} }
@@ -784,8 +865,12 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
action = &ConnmarkAction{} action = &ConnmarkAction{}
case "csum": case "csum":
action = &CsumAction{} action = &CsumAction{}
case "sample":
action = &SampleAction{}
case "gact": case "gact":
action = &GenericAction{} action = &GenericAction{}
case "vlan":
action = &VlanAction{}
case "tunnel_key": case "tunnel_key":
action = &TunnelKeyAction{} action = &TunnelKeyAction{}
case "skbedit": case "skbedit":
@@ -816,7 +901,17 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
tcTs := nl.DeserializeTcf(adatum.Value) tcTs := nl.DeserializeTcf(adatum.Value)
actionTimestamp = toTimeStamp(tcTs) 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": case "tunnel_key":
switch adatum.Attr.Type { switch adatum.Attr.Type {
case nl.TCA_TUNNEL_KEY_PARMS: case nl.TCA_TUNNEL_KEY_PARMS:
@@ -896,6 +991,18 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
tcTs := nl.DeserializeTcf(adatum.Value) tcTs := nl.DeserializeTcf(adatum.Value)
actionTimestamp = toTimeStamp(tcTs) actionTimestamp = toTimeStamp(tcTs)
} }
case "sample":
switch adatum.Attr.Type {
case nl.TCA_ACT_SAMPLE_PARMS:
gen := *nl.DeserializeTcGen(adatum.Value)
toAttrs(&gen, action.Attrs())
case nl.TCA_ACT_SAMPLE_RATE:
action.(*SampleAction).Rate = native.Uint32(adatum.Value[0:4])
case nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP:
action.(*SampleAction).Group = native.Uint32(adatum.Value[0:4])
case nl.TCA_ACT_SAMPLE_TRUNC_SIZE:
action.(*SampleAction).TruncSize = native.Uint32(adatum.Value[0:4])
}
case "gact": case "gact":
switch adatum.Attr.Type { switch adatum.Attr.Type {
case nl.TCA_GACT_PARMS: case nl.TCA_GACT_PARMS:
@@ -920,10 +1027,12 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
actionnStatistic = (*ActionStatistic)(s) actionnStatistic = (*ActionStatistic)(s)
} }
} }
if action != nil {
action.Attrs().Statistics = actionnStatistic action.Attrs().Statistics = actionnStatistic
action.Attrs().Timestamp = actionTimestamp action.Attrs().Timestamp = actionTimestamp
actions = append(actions, action) actions = append(actions, action)
} }
}
return actions, nil return actions, nil
} }

View File

@@ -1,16 +1,7 @@
package netlink package netlink
import ( import (
"errors" "net"
)
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")
) )
type Fou struct { type Fou struct {
@@ -18,4 +9,8 @@ type Fou struct {
Port int Port int
Protocol int Protocol int
EncapType int EncapType int
Local net.IP
Peer net.IP
PeerPort int
IfIndex int
} }

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package netlink package netlink
@@ -183,6 +184,10 @@ func (h *Handle) LinkSetGROIPv4MaxSize(link Link, maxSize int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (h *Handle) LinkSetIP6AddrGenMode(link Link, mode int) error {
return ErrNotImplemented
}
func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error { func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error {
return ErrNotImplemented return ErrNotImplemented
} }

View File

@@ -86,5 +86,5 @@ func newIocltStringSetReq(linkName string) (*Ifreq, *ethtoolSset) {
// getSocketUDP returns file descriptor to new UDP socket // getSocketUDP returns file descriptor to new UDP socket
// It is used for communication with ioctl interface. // It is used for communication with ioctl interface.
func getSocketUDP() (int, error) { func getSocketUDP() (int, error) {
return syscall.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0) return syscall.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
} }

View File

@@ -147,9 +147,11 @@ func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOption
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename)))
cadtFlags := optionsToBitflag(options)
revision := options.Revision revision := options.Revision
if revision == 0 { if revision == 0 {
revision = getIpsetDefaultWithTypeName(typename) revision = getIpsetDefaultRevision(typename, cadtFlags)
} }
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision)))
@@ -181,18 +183,6 @@ func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOption
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout})
} }
var cadtFlags uint32
if options.Comments {
cadtFlags |= nl.IPSET_FLAG_WITH_COMMENT
}
if options.Counters {
cadtFlags |= nl.IPSET_FLAG_WITH_COUNTERS
}
if options.Skbinfo {
cadtFlags |= nl.IPSET_FLAG_WITH_SKBINFO
}
if cadtFlags != 0 { if cadtFlags != 0 {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER, Value: cadtFlags}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER, Value: cadtFlags})
} }
@@ -395,14 +385,89 @@ func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
return req return req
} }
func getIpsetDefaultWithTypeName(typename string) uint8 { // NOTE: This can't just take typename into account, it also has to take desired
// feature support into account, on a per-set-type basis, to return the correct revision, see e.g.
// https://github.com/Olipro/ipset/blob/9f145b49100104d6570fe5c31a5236816ebb4f8f/kernel/net/netfilter/ipset/ip_set_hash_ipport.c#L30
//
// This means that whenever a new "type" of ipset is added, returning the "correct" default revision
// requires adding a new case here for that type, and consulting the ipset C code to figure out the correct
// combination of type name, feature bit flags, and revision ranges.
//
// Care should be taken as some types share the same revision ranges for the same features, and others do not.
// When in doubt, mimic the C code.
func getIpsetDefaultRevision(typename string, featureFlags uint32) uint8 {
switch typename { switch typename {
case "hash:ip,port", case "hash:ip,port",
"hash:ip,port,ip", "hash:ip,port,ip":
"hash:ip,port,net", // Taken from
// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
if (featureFlags & nl.IPSET_FLAG_WITH_SKBINFO) != 0 {
return 5
}
if (featureFlags & nl.IPSET_FLAG_WITH_FORCEADD) != 0 {
return 4
}
if (featureFlags & nl.IPSET_FLAG_WITH_COMMENT) != 0 {
return 3
}
if (featureFlags & nl.IPSET_FLAG_WITH_COUNTERS) != 0 {
return 2
}
// the min revision this library supports for this type
return 1
case "hash:ip,port,net",
"hash:net,port": "hash:net,port":
// Taken from
// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
// - ipset/kernel/net/netfilter/ipset/ip_set_hash_netport.c
if (featureFlags & nl.IPSET_FLAG_WITH_SKBINFO) != 0 {
return 7
}
if (featureFlags & nl.IPSET_FLAG_WITH_FORCEADD) != 0 {
return 6
}
if (featureFlags & nl.IPSET_FLAG_WITH_COMMENT) != 0 {
return 5
}
if (featureFlags & nl.IPSET_FLAG_WITH_COUNTERS) != 0 {
return 4
}
if (featureFlags & nl.IPSET_FLAG_NOMATCH) != 0 {
return 3
}
// the min revision this library supports for this type
return 2
case "hash:ip":
// Taken from
// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ip.c
if (featureFlags & nl.IPSET_FLAG_WITH_SKBINFO) != 0 {
return 4
}
if (featureFlags & nl.IPSET_FLAG_WITH_FORCEADD) != 0 {
return 3
}
if (featureFlags & nl.IPSET_FLAG_WITH_COMMENT) != 0 {
return 2
}
// the min revision this library supports for this type
return 1 return 1
} }
// can't map the correct revision for this type.
return 0 return 0
} }
@@ -579,3 +644,19 @@ func parseIPSetEntry(data []byte) (entry IPSetEntry) {
} }
return return
} }
func optionsToBitflag(options IpsetCreateOptions) uint32 {
var cadtFlags uint32
if options.Comments {
cadtFlags |= nl.IPSET_FLAG_WITH_COMMENT
}
if options.Counters {
cadtFlags |= nl.IPSET_FLAG_WITH_COUNTERS
}
if options.Skbinfo {
cadtFlags |= nl.IPSET_FLAG_WITH_SKBINFO
}
return cadtFlags
}

View File

@@ -56,6 +56,8 @@ type LinkAttrs struct {
Vfs []VfInfo // virtual functions available on link Vfs []VfInfo // virtual functions available on link
Group uint32 Group uint32
PermHWAddr net.HardwareAddr PermHWAddr net.HardwareAddr
ParentDev string
ParentDevBus string
Slave LinkSlave Slave LinkSlave
} }
@@ -290,6 +292,13 @@ type Vlan struct {
LinkAttrs LinkAttrs
VlanId int VlanId int
VlanProtocol VlanProtocol VlanProtocol VlanProtocol
IngressQosMap map[uint32]uint32
EgressQosMap map[uint32]uint32
ReorderHdr *bool
Gvrp *bool
LooseBinding *bool
Mvrp *bool
BridgeBinding *bool
} }
func (vlan *Vlan) Attrs() *LinkAttrs { func (vlan *Vlan) Attrs() *LinkAttrs {
@@ -350,6 +359,7 @@ type Tuntap struct {
Flags TuntapFlag Flags TuntapFlag
NonPersist bool NonPersist bool
Queues int Queues int
DisabledQueues int
Fds []*os.File Fds []*os.File
Owner uint32 Owner uint32
Group uint32 Group uint32
@@ -377,6 +387,13 @@ const (
NETKIT_POLICY_BLACKHOLE NetkitPolicy = 2 NETKIT_POLICY_BLACKHOLE NetkitPolicy = 2
) )
type NetkitScrub int
const (
NETKIT_SCRUB_NONE NetkitScrub = 0
NETKIT_SCRUB_DEFAULT NetkitScrub = 1
)
func (n *Netkit) IsPrimary() bool { func (n *Netkit) IsPrimary() bool {
return n.isPrimary return n.isPrimary
} }
@@ -391,6 +408,9 @@ type Netkit struct {
Mode NetkitMode Mode NetkitMode
Policy NetkitPolicy Policy NetkitPolicy
PeerPolicy NetkitPolicy PeerPolicy NetkitPolicy
Scrub NetkitScrub
PeerScrub NetkitScrub
supportsScrub bool
isPrimary bool isPrimary bool
peerLinkAttrs LinkAttrs peerLinkAttrs LinkAttrs
} }
@@ -403,12 +423,27 @@ func (n *Netkit) Type() string {
return "netkit" return "netkit"
} }
func (n *Netkit) SupportsScrub() bool {
return n.supportsScrub
}
// Veth devices must specify PeerName on create // Veth devices must specify PeerName on create
type Veth struct { type Veth struct {
LinkAttrs LinkAttrs
PeerName string // veth on create only PeerName string // veth on create only
PeerHardwareAddr net.HardwareAddr PeerHardwareAddr net.HardwareAddr
PeerNamespace interface{} PeerNamespace interface{}
PeerTxQLen int
PeerNumTxQueues uint32
PeerNumRxQueues uint32
PeerMTU uint32
}
func NewVeth(attr LinkAttrs) *Veth {
return &Veth{
LinkAttrs: attr,
PeerTxQLen: -1,
}
} }
func (veth *Veth) Attrs() *LinkAttrs { func (veth *Veth) Attrs() *LinkAttrs {
@@ -1042,6 +1077,8 @@ type Geneve struct {
FlowBased bool FlowBased bool
InnerProtoInherit bool InnerProtoInherit bool
Df GeneveDf Df GeneveDf
PortLow int
PortHigh int
} }
func (geneve *Geneve) Attrs() *LinkAttrs { func (geneve *Geneve) Attrs() *LinkAttrs {

View File

@@ -3,6 +3,7 @@ package netlink
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
@@ -1682,6 +1683,73 @@ func (h *Handle) linkModify(link Link, flags int) error {
native.PutUint16(b, uint16(link.VlanId)) native.PutUint16(b, uint16(link.VlanId))
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_VLAN_ID, b) data.AddRtAttr(nl.IFLA_VLAN_ID, b)
var vlanFlags uint32
var vlanFlagsMask uint32
if link.ReorderHdr != nil {
vlanFlagsMask |= nl.VLAN_FLAG_REORDER_HDR
if *link.ReorderHdr {
vlanFlags |= nl.VLAN_FLAG_REORDER_HDR
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_REORDER_HDR)
}
}
if link.Gvrp != nil {
vlanFlagsMask |= nl.VLAN_FLAG_GVRP
if *link.Gvrp {
vlanFlags |= nl.VLAN_FLAG_GVRP
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_GVRP)
}
}
if link.Mvrp != nil {
vlanFlagsMask |= nl.VLAN_FLAG_MVRP
if *link.Mvrp {
vlanFlags |= nl.VLAN_FLAG_MVRP
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_MVRP)
}
}
if link.LooseBinding != nil {
vlanFlagsMask |= nl.VLAN_FLAG_LOOSE_BINDING
if *link.LooseBinding {
vlanFlags |= nl.VLAN_FLAG_LOOSE_BINDING
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_LOOSE_BINDING)
}
}
if link.BridgeBinding != nil {
vlanFlagsMask |= nl.VLAN_FLAG_BRIDGE_BINDING
if *link.BridgeBinding {
vlanFlags |= nl.VLAN_FLAG_BRIDGE_BINDING
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_BRIDGE_BINDING)
}
}
buf := &bytes.Buffer{}
buf.Write(nl.Uint32Attr(vlanFlags))
buf.Write(nl.Uint32Attr(vlanFlagsMask))
data.AddRtAttr(nl.IFLA_VLAN_FLAGS, buf.Bytes())
if link.IngressQosMap != nil {
ingressMap := data.AddRtAttr(nl.IFLA_VLAN_INGRESS_QOS, nil)
for from, to := range link.IngressQosMap {
buf := &bytes.Buffer{}
buf.Write(nl.Uint32Attr(from))
buf.Write(nl.Uint32Attr(to))
ingressMap.AddRtAttr(nl.IFLA_VLAN_QOS_MAPPING, buf.Bytes())
}
}
if link.EgressQosMap != nil {
egressMap := data.AddRtAttr(nl.IFLA_VLAN_EGRESS_QOS, nil)
for from, to := range link.EgressQosMap {
buf := &bytes.Buffer{}
buf.Write(nl.Uint32Attr(from))
buf.Write(nl.Uint32Attr(to))
egressMap.AddRtAttr(nl.IFLA_VLAN_QOS_MAPPING, buf.Bytes())
}
}
if link.VlanProtocol != VLAN_PROTOCOL_UNKNOWN { if link.VlanProtocol != VLAN_PROTOCOL_UNKNOWN {
data.AddRtAttr(nl.IFLA_VLAN_PROTOCOL, htons(uint16(link.VlanProtocol))) data.AddRtAttr(nl.IFLA_VLAN_PROTOCOL, htons(uint16(link.VlanProtocol)))
@@ -1695,16 +1763,25 @@ func (h *Handle) linkModify(link Link, flags int) error {
peer := data.AddRtAttr(nl.VETH_INFO_PEER, nil) peer := data.AddRtAttr(nl.VETH_INFO_PEER, nil)
nl.NewIfInfomsgChild(peer, unix.AF_UNSPEC) nl.NewIfInfomsgChild(peer, unix.AF_UNSPEC)
peer.AddRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(link.PeerName)) peer.AddRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(link.PeerName))
if base.TxQLen >= 0 {
if link.PeerTxQLen >= 0 {
peer.AddRtAttr(unix.IFLA_TXQLEN, nl.Uint32Attr(uint32(link.PeerTxQLen)))
} else if base.TxQLen >= 0 {
peer.AddRtAttr(unix.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen))) peer.AddRtAttr(unix.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
} }
if base.NumTxQueues > 0 { if link.PeerNumTxQueues > 0 {
peer.AddRtAttr(unix.IFLA_NUM_TX_QUEUES, nl.Uint32Attr(link.PeerNumTxQueues))
} else if base.NumTxQueues > 0 {
peer.AddRtAttr(unix.IFLA_NUM_TX_QUEUES, nl.Uint32Attr(uint32(base.NumTxQueues))) peer.AddRtAttr(unix.IFLA_NUM_TX_QUEUES, nl.Uint32Attr(uint32(base.NumTxQueues)))
} }
if base.NumRxQueues > 0 { if link.PeerNumRxQueues > 0 {
peer.AddRtAttr(unix.IFLA_NUM_RX_QUEUES, nl.Uint32Attr(link.PeerNumRxQueues))
} else if base.NumRxQueues > 0 {
peer.AddRtAttr(unix.IFLA_NUM_RX_QUEUES, nl.Uint32Attr(uint32(base.NumRxQueues))) peer.AddRtAttr(unix.IFLA_NUM_RX_QUEUES, nl.Uint32Attr(uint32(base.NumRxQueues)))
} }
if base.MTU > 0 { if link.PeerMTU > 0 {
peer.AddRtAttr(unix.IFLA_MTU, nl.Uint32Attr(link.PeerMTU))
} else if base.MTU > 0 {
peer.AddRtAttr(unix.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU))) peer.AddRtAttr(unix.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
} }
if link.PeerHardwareAddr != nil { if link.PeerHardwareAddr != nil {
@@ -1807,20 +1884,20 @@ func (h *Handle) LinkDel(link Link) error {
} }
func (h *Handle) linkByNameDump(name string) (Link, error) { func (h *Handle) linkByNameDump(name string) (Link, error) {
links, err := h.LinkList() links, executeErr := h.LinkList()
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
for _, link := range links { for _, link := range links {
if link.Attrs().Name == name { if link.Attrs().Name == name {
return link, nil return link, executeErr
} }
// support finding interfaces also via altnames // support finding interfaces also via altnames
for _, altName := range link.Attrs().AltNames { for _, altName := range link.Attrs().AltNames {
if altName == name { if altName == name {
return link, nil return link, executeErr
} }
} }
} }
@@ -1828,25 +1905,33 @@ func (h *Handle) linkByNameDump(name string) (Link, error) {
} }
func (h *Handle) linkByAliasDump(alias string) (Link, error) { func (h *Handle) linkByAliasDump(alias string) (Link, error) {
links, err := h.LinkList() links, executeErr := h.LinkList()
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
for _, link := range links { for _, link := range links {
if link.Attrs().Alias == alias { if link.Attrs().Alias == alias {
return link, nil return link, executeErr
} }
} }
return nil, LinkNotFoundError{fmt.Errorf("Link alias %s not found", alias)} return nil, LinkNotFoundError{fmt.Errorf("Link alias %s not found", alias)}
} }
// LinkByName finds a link by name and returns a pointer to the object. // 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) { func LinkByName(name string) (Link, error) {
return pkgHandle.LinkByName(name) return pkgHandle.LinkByName(name)
} }
// LinkByName finds a link by name and returns a pointer to the object. // 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) { func (h *Handle) LinkByName(name string) (Link, error) {
if h.lookupByDump { if h.lookupByDump {
return h.linkByNameDump(name) return h.linkByNameDump(name)
@@ -1879,12 +1964,20 @@ func (h *Handle) LinkByName(name string) (Link, error) {
// LinkByAlias finds a link by its alias and returns a pointer to the object. // 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 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) { func LinkByAlias(alias string) (Link, error) {
return pkgHandle.LinkByAlias(alias) return pkgHandle.LinkByAlias(alias)
} }
// LinkByAlias finds a link by its alias and returns a pointer to the object. // 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 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) { func (h *Handle) LinkByAlias(alias string) (Link, error) {
if h.lookupByDump { if h.lookupByDump {
return h.linkByAliasDump(alias) return h.linkByAliasDump(alias)
@@ -2246,6 +2339,10 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
break 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 +2418,9 @@ func LinkList() ([]Link, error) {
// LinkList gets a list of link devices. // LinkList gets a list of link devices.
// Equivalent to: `ip link show` // Equivalent to: `ip link show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) LinkList() ([]Link, error) { func (h *Handle) LinkList() ([]Link, error) {
// NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need // NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need
// to get the message ourselves to parse link type. // to get the message ourselves to parse link type.
@@ -2331,9 +2431,9 @@ func (h *Handle) LinkList() ([]Link, error) {
attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF)) attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF))
req.AddData(attr) req.AddData(attr)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []Link var res []Link
@@ -2345,7 +2445,7 @@ func (h *Handle) LinkList() ([]Link, error) {
res = append(res, link) res = append(res, link)
} }
return res, nil return res, executeErr
} }
// LinkUpdate is used to pass information back from LinkSubscribe() // LinkUpdate is used to pass information back from LinkSubscribe()
@@ -2381,6 +2481,10 @@ type LinkSubscribeOptions struct {
// LinkSubscribeWithOptions work like LinkSubscribe but enable to // LinkSubscribeWithOptions work like LinkSubscribe but enable to
// provide additional options to modify the behavior. Currently, the // provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback. // 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 { func LinkSubscribeWithOptions(ch chan<- LinkUpdate, done <-chan struct{}, options LinkSubscribeOptions) error {
if options.Namespace == nil { if options.Namespace == nil {
none := netns.None() none := netns.None()
@@ -2440,6 +2544,9 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
continue continue
} }
for _, m := range msgs { 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 m.Header.Type == unix.NLMSG_DONE {
continue continue
} }
@@ -2513,6 +2620,14 @@ func (h *Handle) LinkSetLearning(link Link, mode bool) error {
return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING) return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING)
} }
func LinkSetVlanTunnel(link Link, mode bool) error {
return pkgHandle.LinkSetVlanTunnel(link, mode)
}
func (h *Handle) LinkSetVlanTunnel(link Link, mode bool) error {
return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_VLAN_TUNNEL)
}
func LinkSetRootBlock(link Link, mode bool) error { func LinkSetRootBlock(link Link, mode bool) error {
return pkgHandle.LinkSetRootBlock(link, mode) return pkgHandle.LinkSetRootBlock(link, mode)
} }
@@ -2639,9 +2754,38 @@ func (h *Handle) LinkSetGroup(link Link, group int) error {
return err return err
} }
// LinkSetIP6AddrGenMode sets the IPv6 address generation mode of the link device.
// Equivalent to: `ip link set $link addrgenmode $mode`
func LinkSetIP6AddrGenMode(link Link, mode int) error {
return pkgHandle.LinkSetIP6AddrGenMode(link, mode)
}
// LinkSetIP6AddrGenMode sets the IPv6 address generation mode of the link device.
// Equivalent to: `ip link set $link addrgenmode $mode`
func (h *Handle) LinkSetIP6AddrGenMode(link Link, mode int) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Index = int32(base.Index)
req.AddData(msg)
b := make([]byte, 1)
b[0] = uint8(mode)
data := nl.NewRtAttr(unix.IFLA_INET6_ADDR_GEN_MODE, b)
af := nl.NewRtAttr(unix.AF_INET6, data.Serialize())
spec := nl.NewRtAttr(unix.IFLA_AF_SPEC, af.Serialize())
req.AddData(spec)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
func addNetkitAttrs(nk *Netkit, linkInfo *nl.RtAttr, flag int) error { func addNetkitAttrs(nk *Netkit, linkInfo *nl.RtAttr, flag int) error {
if nk.peerLinkAttrs.HardwareAddr != nil || nk.HardwareAddr != nil { if nk.Mode != NETKIT_MODE_L2 && (nk.LinkAttrs.HardwareAddr != nil || nk.peerLinkAttrs.HardwareAddr != nil) {
return fmt.Errorf("netkit doesn't support setting Ethernet") return fmt.Errorf("netkit only allows setting Ethernet in L2 mode")
} }
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
@@ -2649,6 +2793,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_MODE, nl.Uint32Attr(uint32(nk.Mode)))
data.AddRtAttr(nl.IFLA_NETKIT_POLICY, nl.Uint32Attr(uint32(nk.Policy))) 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_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 { if (flag & unix.NLM_F_EXCL) == 0 {
// Modifying peer link attributes will not take effect // Modifying peer link attributes will not take effect
@@ -2691,6 +2837,9 @@ func addNetkitAttrs(nk *Netkit, linkInfo *nl.RtAttr, flag int) error {
peer.AddRtAttr(unix.IFLA_NET_NS_FD, nl.Uint32Attr(uint32(ns))) peer.AddRtAttr(unix.IFLA_NET_NS_FD, nl.Uint32Attr(uint32(ns)))
} }
} }
if nk.peerLinkAttrs.HardwareAddr != nil {
peer.AddRtAttr(unix.IFLA_ADDRESS, []byte(nk.peerLinkAttrs.HardwareAddr))
}
return nil return nil
} }
@@ -2709,16 +2858,75 @@ func parseNetkitData(link Link, data []syscall.NetlinkRouteAttr) {
netkit.Policy = NetkitPolicy(native.Uint32(datum.Value[0:4])) netkit.Policy = NetkitPolicy(native.Uint32(datum.Value[0:4]))
case nl.IFLA_NETKIT_PEER_POLICY: case nl.IFLA_NETKIT_PEER_POLICY:
netkit.PeerPolicy = NetkitPolicy(native.Uint32(datum.Value[0:4])) 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]))
} }
} }
} }
func parseVlanQosMap(data []byte) map[uint32]uint32 {
values, err := nl.ParseRouteAttr(data)
if err != nil {
return nil
}
qosMap := make(map[uint32]uint32)
for _, value := range values {
switch value.Attr.Type {
case nl.IFLA_VLAN_QOS_MAPPING:
from := native.Uint32(value.Value[:4])
to := native.Uint32(value.Value[4:])
qosMap[from] = to
}
}
return qosMap
}
func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) { func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) {
vlan := link.(*Vlan) vlan := link.(*Vlan)
for _, datum := range data { for _, datum := range data {
switch datum.Attr.Type { switch datum.Attr.Type {
case nl.IFLA_VLAN_ID: case nl.IFLA_VLAN_ID:
vlan.VlanId = int(native.Uint16(datum.Value[0:2])) vlan.VlanId = int(native.Uint16(datum.Value[0:2]))
case nl.IFLA_VLAN_FLAGS:
flags := native.Uint32(datum.Value[0:4])
trueVal := true
falseVal := false
if flags&nl.VLAN_FLAG_REORDER_HDR != 0 {
vlan.ReorderHdr = &trueVal
} else {
vlan.ReorderHdr = &falseVal
}
if flags&nl.VLAN_FLAG_GVRP != 0 {
vlan.Gvrp = &trueVal
} else {
vlan.Gvrp = &falseVal
}
if flags&nl.VLAN_FLAG_LOOSE_BINDING != 0 {
vlan.LooseBinding = &trueVal
} else {
vlan.LooseBinding = &falseVal
}
if flags&nl.VLAN_FLAG_MVRP != 0 {
vlan.Mvrp = &trueVal
} else {
vlan.Mvrp = &falseVal
}
if flags&nl.VLAN_FLAG_BRIDGE_BINDING != 0 {
vlan.BridgeBinding = &trueVal
} else {
vlan.BridgeBinding = &falseVal
}
case nl.IFLA_VLAN_EGRESS_QOS:
vlan.EgressQosMap = parseVlanQosMap(datum.Value)
case nl.IFLA_VLAN_INGRESS_QOS:
vlan.IngressQosMap = parseVlanQosMap(datum.Value)
case nl.IFLA_VLAN_PROTOCOL: case nl.IFLA_VLAN_PROTOCOL:
vlan.VlanProtocol = VlanProtocol(int(ntohs(datum.Value[0:2]))) vlan.VlanProtocol = VlanProtocol(int(ntohs(datum.Value[0:2])))
} }
@@ -2782,7 +2990,7 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
case nl.IFLA_VXLAN_PORT_RANGE: case nl.IFLA_VXLAN_PORT_RANGE:
buf := bytes.NewBuffer(datum.Value[0:4]) buf := bytes.NewBuffer(datum.Value[0:4])
var pr vxlanPortRange var pr vxlanPortRange
if binary.Read(buf, binary.BigEndian, &pr) != nil { if binary.Read(buf, binary.BigEndian, &pr) == nil {
vxlan.PortLow = int(pr.Lo) vxlan.PortLow = int(pr.Lo)
vxlan.PortHigh = int(pr.Hi) vxlan.PortHigh = int(pr.Hi)
} }
@@ -3006,7 +3214,6 @@ func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) {
} }
} }
// copied from pkg/net_linux.go
func linkFlags(rawFlags uint32) net.Flags { func linkFlags(rawFlags uint32) net.Flags {
var f net.Flags var f net.Flags
if rawFlags&unix.IFF_UP != 0 { if rawFlags&unix.IFF_UP != 0 {
@@ -3024,9 +3231,16 @@ func linkFlags(rawFlags uint32) net.Flags {
if rawFlags&unix.IFF_MULTICAST != 0 { if rawFlags&unix.IFF_MULTICAST != 0 {
f |= net.FlagMulticast f |= net.FlagMulticast
} }
if rawFlags&unix.IFF_RUNNING != 0 {
f |= net.FlagRunning
}
return f return f
} }
type genevePortRange struct {
Lo, Hi uint16
}
func addGeneveAttrs(geneve *Geneve, linkInfo *nl.RtAttr) { func addGeneveAttrs(geneve *Geneve, linkInfo *nl.RtAttr) {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
@@ -3063,6 +3277,15 @@ func addGeneveAttrs(geneve *Geneve, linkInfo *nl.RtAttr) {
data.AddRtAttr(nl.IFLA_GENEVE_TOS, nl.Uint8Attr(geneve.Tos)) data.AddRtAttr(nl.IFLA_GENEVE_TOS, nl.Uint8Attr(geneve.Tos))
} }
if geneve.PortLow > 0 || geneve.PortHigh > 0 {
pr := genevePortRange{uint16(geneve.PortLow), uint16(geneve.PortHigh)}
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, &pr)
data.AddRtAttr(nl.IFLA_GENEVE_PORT_RANGE, buf.Bytes())
}
data.AddRtAttr(nl.IFLA_GENEVE_DF, nl.Uint8Attr(uint8(geneve.Df))) data.AddRtAttr(nl.IFLA_GENEVE_DF, nl.Uint8Attr(uint8(geneve.Df)))
} }
@@ -3084,6 +3307,13 @@ func parseGeneveData(link Link, data []syscall.NetlinkRouteAttr) {
geneve.FlowBased = true geneve.FlowBased = true
case nl.IFLA_GENEVE_INNER_PROTO_INHERIT: case nl.IFLA_GENEVE_INNER_PROTO_INHERIT:
geneve.InnerProtoInherit = true geneve.InnerProtoInherit = true
case nl.IFLA_GENEVE_PORT_RANGE:
buf := bytes.NewBuffer(datum.Value[0:4])
var pr genevePortRange
if binary.Read(buf, binary.BigEndian, &pr) == nil {
geneve.PortLow = int(pr.Lo)
geneve.PortHigh = int(pr.Hi)
}
} }
} }
} }
@@ -3859,11 +4089,27 @@ func parseTuntapData(link Link, data []syscall.NetlinkRouteAttr) {
tuntap.Group = native.Uint32(datum.Value) tuntap.Group = native.Uint32(datum.Value)
case nl.IFLA_TUN_TYPE: case nl.IFLA_TUN_TYPE:
tuntap.Mode = TuntapMode(uint8(datum.Value[0])) tuntap.Mode = TuntapMode(uint8(datum.Value[0]))
case nl.IFLA_TUN_PI:
if datum.Value[0] == 0 {
tuntap.Flags |= TUNTAP_NO_PI
}
case nl.IFLA_TUN_VNET_HDR:
if datum.Value[0] == 1 {
tuntap.Flags |= TUNTAP_VNET_HDR
}
case nl.IFLA_TUN_PERSIST: case nl.IFLA_TUN_PERSIST:
tuntap.NonPersist = false tuntap.NonPersist = false
if uint8(datum.Value[0]) == 0 { if uint8(datum.Value[0]) == 0 {
tuntap.NonPersist = true tuntap.NonPersist = true
} }
case nl.IFLA_TUN_MULTI_QUEUE:
if datum.Value[0] == 1 {
tuntap.Flags |= TUNTAP_MULTI_QUEUE
}
case nl.IFLA_TUN_NUM_QUEUES:
tuntap.Queues = int(native.Uint32(datum.Value))
case nl.IFLA_TUN_NUM_DISABLED_QUEUES:
tuntap.DisabledQueues = int(native.Uint32(datum.Value))
} }
} }
} }

View File

@@ -1,5 +1,14 @@
package netlink package netlink
import (
"fmt"
"os"
"strings"
"syscall"
"golang.org/x/sys/unix"
)
// ideally golang.org/x/sys/unix would define IfReq but it only has // ideally golang.org/x/sys/unix would define IfReq but it only has
// IFNAMSIZ, hence this minimalistic implementation // IFNAMSIZ, hence this minimalistic implementation
const ( const (
@@ -7,8 +16,136 @@ const (
IFNAMSIZ = 16 IFNAMSIZ = 16
) )
const TUN = "/dev/net/tun"
type ifReq struct { type ifReq struct {
Name [IFNAMSIZ]byte Name [IFNAMSIZ]byte
Flags uint16 Flags uint16
pad [SizeOfIfReq - IFNAMSIZ - 2]byte pad [SizeOfIfReq - IFNAMSIZ - 2]byte
} }
// AddQueues opens and attaches multiple queue file descriptors to an existing
// TUN/TAP interface in multi-queue mode.
//
// It performs TUNSETIFF ioctl on each opened file descriptor with the current
// tuntap configuration. Each resulting fd is set to non-blocking mode and
// returned as *os.File.
//
// If the interface was created with a name pattern (e.g. "tap%d"),
// the first successful TUNSETIFF call will return the resolved name,
// which is saved back into tuntap.Name.
//
// This method assumes that the interface already exists and is in multi-queue mode.
// The returned FDs are also appended to tuntap.Fds and tuntap.Queues is updated.
//
// It is the caller's responsibility to close the FDs when they are no longer needed.
func (tuntap *Tuntap) AddQueues(count int) ([]*os.File, error) {
if tuntap.Mode < unix.IFF_TUN || tuntap.Mode > unix.IFF_TAP {
return nil, fmt.Errorf("Tuntap.Mode %v unknown", tuntap.Mode)
}
if tuntap.Flags&TUNTAP_MULTI_QUEUE == 0 {
return nil, fmt.Errorf("TUNTAP_MULTI_QUEUE not set")
}
if count < 1 {
return nil, fmt.Errorf("count must be >= 1")
}
req, err := unix.NewIfreq(tuntap.Name)
if err != nil {
return nil, err
}
req.SetUint16(uint16(tuntap.Mode) | uint16(tuntap.Flags))
var fds []*os.File
for i := 0; i < count; i++ {
localReq := req
fd, err := unix.Open(TUN, os.O_RDWR|syscall.O_CLOEXEC, 0)
if err != nil {
cleanupFds(fds)
return nil, err
}
err = unix.IoctlIfreq(fd, unix.TUNSETIFF, req)
if err != nil {
// close the new fd
unix.Close(fd)
// and the already opened ones
cleanupFds(fds)
return nil, fmt.Errorf("tuntap IOCTL TUNSETIFF failed [%d]: %w", i, err)
}
// Set the tun device to non-blocking before use. The below comment
// taken from:
//
// https://github.com/mistsys/tuntap/commit/161418c25003bbee77d085a34af64d189df62bea
//
// Note there is a complication because in go, if a device node is
// opened, go sets it to use nonblocking I/O. However a /dev/net/tun
// doesn't work with epoll until after the TUNSETIFF ioctl has been
// done. So we open the unix fd directly, do the ioctl, then put the
// fd in nonblocking mode, an then finally wrap it in a os.File,
// which will see the nonblocking mode and add the fd to the
// pollable set, so later on when we Read() from it blocked the
// calling thread in the kernel.
//
// See
// https://github.com/golang/go/issues/30426
// which got exposed in go 1.13 by the fix to
// https://github.com/golang/go/issues/30624
err = unix.SetNonblock(fd, true)
if err != nil {
cleanupFds(fds)
return nil, fmt.Errorf("tuntap set to non-blocking failed [%d]: %w", i, err)
}
// create the file from the file descriptor and store it
file := os.NewFile(uintptr(fd), TUN)
fds = append(fds, file)
// 1) we only care for the name of the first tap in the multi queue set
// 2) if the original name was empty, the localReq has now the actual name
//
// In addition:
// This ensures that the link name is always identical to what the kernel returns.
// Not only in case of an empty name, but also when using name templates.
// e.g. when the provided name is "tap%d", the kernel replaces %d with the next available number.
if i == 0 {
tuntap.Name = strings.Trim(localReq.Name(), "\x00")
}
}
tuntap.Fds = append(tuntap.Fds, fds...)
tuntap.Queues = len(tuntap.Fds)
return fds, nil
}
// RemoveQueues closes the given TAP queue file descriptors and removes them
// from the tuntap.Fds list.
//
// This is a logical counterpart to AddQueues and allows releasing specific queues
// (e.g., to simulate queue failure or perform partial detach).
//
// The method updates tuntap.Queues to reflect the number of remaining active queues.
//
// It is safe to call with a subset of tuntap.Fds, but the caller must ensure
// that the passed *os.File descriptors belong to this interface.
func (tuntap *Tuntap) RemoveQueues(fds ...*os.File) error {
toClose := make(map[uintptr]struct{}, len(fds))
for _, fd := range fds {
toClose[fd.Fd()] = struct{}{}
}
var newFds []*os.File
for _, fd := range tuntap.Fds {
if _, shouldClose := toClose[fd.Fd()]; shouldClose {
if err := fd.Close(); err != nil {
return fmt.Errorf("failed to close queue fd %d: %w", fd.Fd(), err)
}
tuntap.Queues--
} else {
newFds = append(newFds, fd)
}
}
tuntap.Fds = newFds
return nil
}

View File

@@ -19,6 +19,14 @@ type Neigh struct {
Vlan int Vlan int
VNI int VNI int
MasterIndex int MasterIndex int
// These values are expressed as "clock ticks ago". To
// convert these clock ticks to seconds divide by sysconf(_SC_CLK_TCK).
// When _SC_CLK_TCK is 100, for example, the ndm_* times are expressed
// in centiseconds.
Confirmed uint32 // The last time ARP/ND succeeded OR higher layer confirmation was received
Used uint32 // The last time ARP/ND took place for this neighbor
Updated uint32 // The time when the current NUD state was entered
} }
// String returns $ip/$hwaddr $label // String returns $ip/$hwaddr $label

View File

@@ -1,6 +1,7 @@
package netlink package netlink
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"syscall" "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). // NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`. // Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family. // 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) { func NeighList(linkIndex, family int) ([]Neigh, error) {
return pkgHandle.NeighList(linkIndex, family) 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. // NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`. // Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link and ip family. // 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) { func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
return pkgHandle.NeighProxyList(linkIndex, family) 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). // NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`. // Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family. // 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) { func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
return h.NeighListExecute(Ndmsg{ return h.NeighListExecute(Ndmsg{
Family: uint8(family), 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. // NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`. // Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link, ip family. // 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) { func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
return h.NeighListExecute(Ndmsg{ return h.NeighListExecute(Ndmsg{
Family: uint8(family), 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. // 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) { func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
return pkgHandle.NeighListExecute(msg) return pkgHandle.NeighListExecute(msg)
} }
// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state. // 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) { func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
req.AddData(&msg) req.AddData(&msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []Neigh var res []Neigh
@@ -281,7 +300,7 @@ func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
res = append(res, *neigh) res = append(res, *neigh)
} }
return res, nil return res, executeErr
} }
func NeighDeserialize(m []byte) (*Neigh, error) { func NeighDeserialize(m []byte) (*Neigh, error) {
@@ -330,6 +349,10 @@ func NeighDeserialize(m []byte) (*Neigh, error) {
neigh.VNI = int(native.Uint32(attr.Value[0:4])) neigh.VNI = int(native.Uint32(attr.Value[0:4]))
case NDA_MASTER: case NDA_MASTER:
neigh.MasterIndex = int(native.Uint32(attr.Value[0:4])) neigh.MasterIndex = int(native.Uint32(attr.Value[0:4]))
case NDA_CACHEINFO:
neigh.Confirmed = native.Uint32(attr.Value[0:4])
neigh.Used = native.Uint32(attr.Value[4:8])
neigh.Updated = native.Uint32(attr.Value[8:12])
} }
} }
@@ -364,6 +387,10 @@ type NeighSubscribeOptions struct {
// NeighSubscribeWithOptions work like NeighSubscribe but enable to // NeighSubscribeWithOptions work like NeighSubscribe but enable to
// provide additional options to modify the behavior. Currently, the // provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback. // 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 { func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
if options.Namespace == nil { if options.Namespace == nil {
none := netns.None() none := netns.None()
@@ -428,6 +455,9 @@ func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <
continue continue
} }
for _, m := range msgs { 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 m.Header.Type == unix.NLMSG_DONE {
if listExisting { if listExisting {
// This will be called after handling AF_UNSPEC // This will be called after handling AF_UNSPEC

View File

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

View File

@@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package netlink package netlink
@@ -144,6 +145,10 @@ func LinkSetGROIPv4MaxSize(link Link, maxSize int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetIP6AddrGenMode(link Link, mode int) error {
return ErrNotImplemented
}
func LinkAdd(link Link) error { func LinkAdd(link Link) error {
return ErrNotImplemented return ErrNotImplemented
} }

View File

@@ -26,6 +26,14 @@ const (
IFLA_BRIDGE_FLAGS = iota IFLA_BRIDGE_FLAGS = iota
IFLA_BRIDGE_MODE IFLA_BRIDGE_MODE
IFLA_BRIDGE_VLAN_INFO IFLA_BRIDGE_VLAN_INFO
IFLA_BRIDGE_VLAN_TUNNEL_INFO
)
const (
IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC = iota
IFLA_BRIDGE_VLAN_TUNNEL_ID
IFLA_BRIDGE_VLAN_TUNNEL_VID
IFLA_BRIDGE_VLAN_TUNNEL_FLAGS
) )
const ( const (
@@ -41,6 +49,11 @@ const (
// __u16 vid; // __u16 vid;
// }; // };
type TunnelInfo struct {
TunId uint32
Vid uint16
}
type BridgeVlanInfo struct { type BridgeVlanInfo struct {
Flags uint16 Flags uint16
Vid uint16 Vid uint16

View File

@@ -31,6 +31,20 @@ const (
IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL
) )
const (
IFLA_VLAN_QOS_UNSPEC = iota
IFLA_VLAN_QOS_MAPPING
IFLA_VLAN_QOS_MAX = IFLA_VLAN_QOS_MAPPING
)
const (
VLAN_FLAG_REORDER_HDR = 1 << iota
VLAN_FLAG_GVRP
VLAN_FLAG_LOOSE_BINDING
VLAN_FLAG_MVRP
VLAN_FLAG_BRIDGE_BINDING
)
const ( const (
IFLA_NETKIT_UNSPEC = iota IFLA_NETKIT_UNSPEC = iota
IFLA_NETKIT_PEER_INFO IFLA_NETKIT_PEER_INFO
@@ -38,6 +52,8 @@ const (
IFLA_NETKIT_POLICY IFLA_NETKIT_POLICY
IFLA_NETKIT_PEER_POLICY IFLA_NETKIT_PEER_POLICY
IFLA_NETKIT_MODE IFLA_NETKIT_MODE
IFLA_NETKIT_SCRUB
IFLA_NETKIT_PEER_SCRUB
IFLA_NETKIT_MAX = IFLA_NETKIT_MODE IFLA_NETKIT_MAX = IFLA_NETKIT_MODE
) )
@@ -232,6 +248,7 @@ const (
IFLA_GENEVE_TTL_INHERIT IFLA_GENEVE_TTL_INHERIT
IFLA_GENEVE_DF IFLA_GENEVE_DF
IFLA_GENEVE_INNER_PROTO_INHERIT IFLA_GENEVE_INNER_PROTO_INHERIT
IFLA_GENEVE_PORT_RANGE
IFLA_GENEVE_MAX = IFLA_GENEVE_INNER_PROTO_INHERIT IFLA_GENEVE_MAX = IFLA_GENEVE_INNER_PROTO_INHERIT
) )
@@ -816,3 +833,10 @@ const (
IFLA_BAREUDP_MULTIPROTO_MODE IFLA_BAREUDP_MULTIPROTO_MODE
IFLA_BAREUDP_MAX = IFLA_BAREUDP_MULTIPROTO_MODE IFLA_BAREUDP_MAX = IFLA_BAREUDP_MULTIPROTO_MODE
) )
const (
IN6_ADDR_GEN_MODE_EUI64 = iota
IN6_ADDR_GEN_MODE_NONE
IN6_ADDR_GEN_MODE_STABLE_PRIVACY
IN6_ADDR_GEN_MODE_RANDOM
)

View File

@@ -4,6 +4,7 @@ package nl
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"net" "net"
"os" "os"
@@ -11,6 +12,7 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
"time"
"unsafe" "unsafe"
"github.com/vishvananda/netns" "github.com/vishvananda/netns"
@@ -43,6 +45,26 @@ var SocketTimeoutTv = unix.Timeval{Sec: 60, Usec: 0}
// ErrorMessageReporting is the default error message reporting configuration for the new netlink sockets // ErrorMessageReporting is the default error message reporting configuration for the new netlink sockets
var EnableErrorMessageReporting bool = false 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. // GetIPFamily returns the family type of a net.IP.
func GetIPFamily(ip net.IP) int { func GetIPFamily(ip net.IP) int {
if len(ip) <= net.IPv4len { if len(ip) <= net.IPv4len {
@@ -492,22 +514,26 @@ func (req *NetlinkRequest) AddRawData(data []byte) {
// Execute the request against the given sockType. // Execute the request against the given sockType.
// Returns a list of netlink messages in serialized format, optionally filtered // Returns a list of netlink messages in serialized format, optionally filtered
// by resType. // by resType.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) { func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) {
var res [][]byte var res [][]byte
err := req.ExecuteIter(sockType, resType, func(msg []byte) bool { err := req.ExecuteIter(sockType, resType, func(msg []byte) bool {
res = append(res, msg) res = append(res, msg)
return true return true
}) })
if err != nil { if err != nil && !errors.Is(err, ErrDumpInterrupted) {
return nil, err return nil, err
} }
return res, nil return res, err
} }
// ExecuteIter executes the request against the given sockType. // ExecuteIter executes the request against the given sockType.
// Calls the provided callback func once for each netlink message. // Calls the provided callback func once for each netlink message.
// If the callback returns false, it is not called again, but // If the callback returns false, it is not called again, but
// the remaining messages are consumed/discarded. // 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 // Thread safety: ExecuteIter holds a lock on the socket until
// it finishes iteration so the callback must not call back into // it finishes iteration so the callback must not call back into
@@ -559,6 +585,8 @@ func (req *NetlinkRequest) ExecuteIter(sockType int, resType uint16, f func(msg
return err return err
} }
dumpIntr := false
done: done:
for { for {
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
@@ -580,7 +608,7 @@ done:
} }
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 { 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 { if m.Header.Type == unix.NLMSG_DONE || m.Header.Type == unix.NLMSG_ERROR {
@@ -634,6 +662,9 @@ done:
} }
} }
} }
if dumpIntr {
return ErrDumpInterrupted
}
return nil return nil
} }
@@ -659,6 +690,8 @@ type NetlinkSocket struct {
fd int32 fd int32
file *os.File file *os.File
lsa unix.SockaddrNetlink lsa unix.SockaddrNetlink
sendTimeout int64 // Access using atomic.Load/StoreInt64
receiveTimeout int64 // Access using atomic.Load/StoreInt64
sync.Mutex sync.Mutex
} }
@@ -756,7 +789,7 @@ func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) {
// Returns the netlink socket on which Receive() method can be called // Returns the netlink socket on which Receive() method can be called
// to retrieve the messages from the kernel. // to retrieve the messages from the kernel.
func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) { func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, protocol) fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, protocol)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -802,8 +835,44 @@ func (s *NetlinkSocket) GetFd() int {
return int(s.fd) return int(s.fd)
} }
func (s *NetlinkSocket) GetTimeouts() (send, receive time.Duration) {
return time.Duration(atomic.LoadInt64(&s.sendTimeout)),
time.Duration(atomic.LoadInt64(&s.receiveTimeout))
}
func (s *NetlinkSocket) Send(request *NetlinkRequest) error { func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
return unix.Sendto(int(s.fd), request.Serialize(), 0, &s.lsa) rawConn, err := s.file.SyscallConn()
if err != nil {
return err
}
var (
deadline time.Time
innerErr error
)
sendTimeout := atomic.LoadInt64(&s.sendTimeout)
if sendTimeout != 0 {
deadline = time.Now().Add(time.Duration(sendTimeout))
}
if err := s.file.SetWriteDeadline(deadline); err != nil {
return err
}
serializedReq := request.Serialize()
err = rawConn.Write(func(fd uintptr) (done bool) {
innerErr = unix.Sendto(int(s.fd), serializedReq, 0, &s.lsa)
return innerErr != unix.EWOULDBLOCK
})
if innerErr != nil {
return innerErr
}
if err != nil {
// The timeout was previously implemented using SO_SNDTIMEO on a blocking
// socket. So, continue to return EAGAIN when the timeout is reached.
if errors.Is(err, os.ErrDeadlineExceeded) {
return unix.EAGAIN
}
return err
}
return nil
} }
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetlink, error) { func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetlink, error) {
@@ -812,20 +881,33 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetli
return nil, nil, err return nil, nil, err
} }
var ( var (
deadline time.Time
fromAddr *unix.SockaddrNetlink fromAddr *unix.SockaddrNetlink
rb [RECEIVE_BUFFER_SIZE]byte rb [RECEIVE_BUFFER_SIZE]byte
nr int nr int
from unix.Sockaddr from unix.Sockaddr
innerErr error innerErr error
) )
receiveTimeout := atomic.LoadInt64(&s.receiveTimeout)
if receiveTimeout != 0 {
deadline = time.Now().Add(time.Duration(receiveTimeout))
}
if err := s.file.SetReadDeadline(deadline); err != nil {
return nil, nil, err
}
err = rawConn.Read(func(fd uintptr) (done bool) { err = rawConn.Read(func(fd uintptr) (done bool) {
nr, from, innerErr = unix.Recvfrom(int(fd), rb[:], 0) nr, from, innerErr = unix.Recvfrom(int(fd), rb[:], 0)
return innerErr != unix.EWOULDBLOCK return innerErr != unix.EWOULDBLOCK
}) })
if innerErr != nil { if innerErr != nil {
err = innerErr return nil, nil, innerErr
} }
if err != nil { if err != nil {
// The timeout was previously implemented using SO_RCVTIMEO on a blocking
// socket. So, continue to return EAGAIN when the timeout is reached.
if errors.Is(err, os.ErrDeadlineExceeded) {
return nil, nil, unix.EAGAIN
}
return nil, nil, err return nil, nil, err
} }
fromAddr, ok := from.(*unix.SockaddrNetlink) fromAddr, ok := from.(*unix.SockaddrNetlink)
@@ -847,16 +929,14 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetli
// SetSendTimeout allows to set a send timeout on the socket // SetSendTimeout allows to set a send timeout on the socket
func (s *NetlinkSocket) SetSendTimeout(timeout *unix.Timeval) error { func (s *NetlinkSocket) SetSendTimeout(timeout *unix.Timeval) error {
// Set a send timeout of SOCKET_SEND_TIMEOUT, this will allow the Send to periodically unblock and avoid that a routine atomic.StoreInt64(&s.sendTimeout, timeout.Nano())
// remains stuck on a send on a closed fd return nil
return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_SNDTIMEO, timeout)
} }
// SetReceiveTimeout allows to set a receive timeout on the socket // SetReceiveTimeout allows to set a receive timeout on the socket
func (s *NetlinkSocket) SetReceiveTimeout(timeout *unix.Timeval) error { func (s *NetlinkSocket) SetReceiveTimeout(timeout *unix.Timeval) error {
// Set a read timeout of SOCKET_READ_TIMEOUT, this will allow the Read to periodically unblock and avoid that a routine atomic.StoreInt64(&s.receiveTimeout, timeout.Nano())
// remains stuck on a recvmsg on a closed fd return nil
return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, timeout)
} }
// SetReceiveBufferSize allows to set a receive buffer size on the socket // SetReceiveBufferSize allows to set a receive buffer size on the socket

View File

@@ -17,7 +17,7 @@ func ParseAttributes(data []byte) <-chan Attribute {
go func() { go func() {
i := 0 i := 0
for i+4 < len(data) { for i+4 <= len(data) {
length := int(native.Uint16(data[i : i+2])) length := int(native.Uint16(data[i : i+2]))
attrType := native.Uint16(data[i+2 : i+4]) attrType := native.Uint16(data[i+2 : i+4])

View File

@@ -15,6 +15,8 @@ const (
RDMA_NLDEV_CMD_DELLINK = 4 RDMA_NLDEV_CMD_DELLINK = 4
RDMA_NLDEV_CMD_SYS_GET = 6 RDMA_NLDEV_CMD_SYS_GET = 6
RDMA_NLDEV_CMD_SYS_SET = 7 RDMA_NLDEV_CMD_SYS_SET = 7
RDMA_NLDEV_CMD_RES_GET = 9
RDMA_NLDEV_CMD_STAT_GET = 17
) )
const ( const (
@@ -32,8 +34,16 @@ const (
RDMA_NLDEV_ATTR_PORT_STATE = 12 RDMA_NLDEV_ATTR_PORT_STATE = 12
RDMA_NLDEV_ATTR_PORT_PHYS_STATE = 13 RDMA_NLDEV_ATTR_PORT_PHYS_STATE = 13
RDMA_NLDEV_ATTR_DEV_NODE_TYPE = 14 RDMA_NLDEV_ATTR_DEV_NODE_TYPE = 14
RDMA_NLDEV_ATTR_RES_SUMMARY = 15
RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY = 16
RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME = 17
RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR = 18
RDMA_NLDEV_ATTR_NDEV_NAME = 51 RDMA_NLDEV_ATTR_NDEV_NAME = 51
RDMA_NLDEV_ATTR_LINK_TYPE = 65 RDMA_NLDEV_ATTR_LINK_TYPE = 65
RDMA_NLDEV_SYS_ATTR_NETNS_MODE = 66 RDMA_NLDEV_SYS_ATTR_NETNS_MODE = 66
RDMA_NLDEV_NET_NS_FD = 68 RDMA_NLDEV_NET_NS_FD = 68
RDMA_NLDEV_ATTR_STAT_HWCOUNTERS = 80
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY = 81
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME = 82
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE = 83
) )

View File

@@ -13,6 +13,7 @@ const (
SEG6_LOCAL_IIF SEG6_LOCAL_IIF
SEG6_LOCAL_OIF SEG6_LOCAL_OIF
SEG6_LOCAL_BPF SEG6_LOCAL_BPF
SEG6_LOCAL_VRFTABLE
__SEG6_LOCAL_MAX __SEG6_LOCAL_MAX
) )
const ( const (

View File

@@ -77,6 +77,17 @@ const (
TCA_ACT_MAX TCA_ACT_MAX
) )
const (
TCA_ACT_SAMPLE_UNSPEC = iota
TCA_ACT_SAMPLE_TM
TCA_ACT_SAMPLE_PARMS
TCA_ACT_SAMPLE_RATE
TCA_ACT_SAMPLE_TRUNC_SIZE
TCA_ACT_SAMPLE_PSAMPLE_GROUP
TCA_ACT_SAMPLE_PAD
TCA_ACT_SAMPLE_MAX
)
const ( const (
TCA_PRIO_UNSPEC = iota TCA_PRIO_UNSPEC = iota
TCA_PRIO_MQ TCA_PRIO_MQ
@@ -115,6 +126,7 @@ const (
SizeofTcConnmark = SizeofTcGen + 0x04 SizeofTcConnmark = SizeofTcGen + 0x04
SizeofTcCsum = SizeofTcGen + 0x04 SizeofTcCsum = SizeofTcGen + 0x04
SizeofTcMirred = SizeofTcGen + 0x08 SizeofTcMirred = SizeofTcGen + 0x08
SizeofTcVlan = SizeofTcGen + 0x04
SizeofTcTunnelKey = SizeofTcGen + 0x04 SizeofTcTunnelKey = SizeofTcGen + 0x04
SizeofTcSkbEdit = SizeofTcGen SizeofTcSkbEdit = SizeofTcGen
SizeofTcPolice = 2*SizeofTcRateSpec + 0x20 SizeofTcPolice = 2*SizeofTcRateSpec + 0x20
@@ -816,6 +828,41 @@ func (x *TcMirred) Serialize() []byte {
return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:] 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 ( const (
TCA_TUNNEL_KEY_UNSPEC = iota TCA_TUNNEL_KEY_UNSPEC = iota
TCA_TUNNEL_KEY_TM TCA_TUNNEL_KEY_TM
@@ -1076,6 +1123,13 @@ const (
TCA_FLOWER_KEY_ENC_OPTS TCA_FLOWER_KEY_ENC_OPTS
TCA_FLOWER_KEY_ENC_OPTS_MASK TCA_FLOWER_KEY_ENC_OPTS_MASK
TCA_FLOWER_IN_HW_COUNT
TCA_FLOWER_KEY_PORT_SRC_MIN /* be16 */
TCA_FLOWER_KEY_PORT_SRC_MAX /* be16 */
TCA_FLOWER_KEY_PORT_DST_MIN /* be16 */
TCA_FLOWER_KEY_PORT_DST_MAX /* be16 */
__TCA_FLOWER_MAX __TCA_FLOWER_MAX
) )
@@ -1091,11 +1145,11 @@ const TCA_CLS_FLAGS_SKIP_SW = 1 << 1 /* don't use filter in SW */
// }; // };
type TcSfqQopt struct { type TcSfqQopt struct {
Quantum uint8 Quantum uint32
Perturb int32 Perturb int32
Limit uint32 Limit uint32
Divisor uint8 Divisor uint32
Flows uint8 Flows uint32
} }
func (x *TcSfqQopt) Len() int { func (x *TcSfqQopt) Len() int {
@@ -1239,7 +1293,7 @@ const (
) )
// /* TCA_PEDIT_KEY_EX_HDR_TYPE_NETWROK is a special case for legacy users. It // /* 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 type PeditHeaderType uint16
@@ -1533,7 +1587,7 @@ func (p *TcPedit) SetIPv6Dst(ip6 net.IP) {
} }
func (p *TcPedit) SetIPv4Src(ip net.IP) { func (p *TcPedit) SetIPv4Src(ip net.IP) {
u32 := NativeEndian().Uint32(ip[:4]) u32 := NativeEndian().Uint32(ip.To4())
tKey := TcPeditKey{} tKey := TcPeditKey{}
tKeyEx := TcPeditKeyEx{} tKeyEx := TcPeditKeyEx{}
@@ -1549,7 +1603,7 @@ func (p *TcPedit) SetIPv4Src(ip net.IP) {
} }
func (p *TcPedit) SetIPv4Dst(ip net.IP) { func (p *TcPedit) SetIPv4Dst(ip net.IP) {
u32 := NativeEndian().Uint32(ip[:4]) u32 := NativeEndian().Uint32(ip.To4())
tKey := TcPeditKey{} tKey := TcPeditKey{}
tKeyEx := TcPeditKeyEx{} tKeyEx := TcPeditKeyEx{}

View File

@@ -82,6 +82,10 @@ const (
XFRMA_SET_MARK /* __u32 */ XFRMA_SET_MARK /* __u32 */
XFRMA_SET_MARK_MASK /* __u32 */ XFRMA_SET_MARK_MASK /* __u32 */
XFRMA_IF_ID /* __u32 */ XFRMA_IF_ID /* __u32 */
XFRMA_MTIMER_THRESH /* __u32 in seconds for input SA */
XFRMA_SA_DIR /* __u8 */
XFRMA_NAT_KEEPALIVE_INTERVAL /* __u32 in seconds for NAT keepalive */
XFRMA_SA_PCPU /* __u32 */
XFRMA_MAX = iota - 1 XFRMA_MAX = iota - 1
) )

View File

@@ -16,6 +16,7 @@ type Protinfo struct {
ProxyArpWiFi bool ProxyArpWiFi bool
Isolated bool Isolated bool
NeighSuppress bool NeighSuppress bool
VlanTunnel bool
} }
// String returns a list of enabled flags // String returns a list of enabled flags
@@ -55,6 +56,9 @@ func (prot *Protinfo) String() string {
if prot.NeighSuppress { if prot.NeighSuppress {
boolStrings = append(boolStrings, "NeighSuppress") boolStrings = append(boolStrings, "NeighSuppress")
} }
if prot.VlanTunnel {
boolStrings = append(boolStrings, "VlanTunnel")
}
return strings.Join(boolStrings, " ") return strings.Join(boolStrings, " ")
} }

View File

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

View File

@@ -374,10 +374,10 @@ func (qdisc *FqCodel) Type() string {
type Sfq struct { type Sfq struct {
QdiscAttrs QdiscAttrs
// TODO: Only the simplified options for SFQ are handled here. Support for the extended one can be added later. // TODO: Only the simplified options for SFQ are handled here. Support for the extended one can be added later.
Quantum uint8 Quantum uint32
Perturb uint8 Perturb int32
Limit uint32 Limit uint32
Divisor uint8 Divisor uint32
} }
func (sfq *Sfq) String() string { func (sfq *Sfq) String() string {

View File

@@ -1,6 +1,7 @@
package netlink package netlink
import ( import (
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strconv" "strconv"
@@ -320,7 +321,7 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
case *Sfq: case *Sfq:
opt := nl.TcSfqQoptV1{} opt := nl.TcSfqQoptV1{}
opt.TcSfqQopt.Quantum = qdisc.Quantum opt.TcSfqQopt.Quantum = qdisc.Quantum
opt.TcSfqQopt.Perturb = int32(qdisc.Perturb) opt.TcSfqQopt.Perturb = qdisc.Perturb
opt.TcSfqQopt.Limit = qdisc.Limit opt.TcSfqQopt.Limit = qdisc.Limit
opt.TcSfqQopt.Divisor = qdisc.Divisor opt.TcSfqQopt.Divisor = qdisc.Divisor
@@ -338,6 +339,9 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
// QdiscList gets a list of qdiscs in the system. // QdiscList gets a list of qdiscs in the system.
// Equivalent to: `tc qdisc show`. // Equivalent to: `tc qdisc show`.
// The list can be filtered by link. // 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) { func QdiscList(link Link) ([]Qdisc, error) {
return pkgHandle.QdiscList(link) return pkgHandle.QdiscList(link)
} }
@@ -345,6 +349,9 @@ func QdiscList(link Link) ([]Qdisc, error) {
// QdiscList gets a list of qdiscs in the system. // QdiscList gets a list of qdiscs in the system.
// Equivalent to: `tc qdisc show`. // Equivalent to: `tc qdisc show`.
// The list can be filtered by link. // 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) { func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
req := h.newNetlinkRequest(unix.RTM_GETQDISC, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETQDISC, unix.NLM_F_DUMP)
index := int32(0) index := int32(0)
@@ -359,9 +366,9 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
} }
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []Qdisc var res []Qdisc
@@ -497,7 +504,7 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
res = append(res, qdisc) res = append(res, qdisc)
} }
return res, nil return res, executeErr
} }
func parsePfifoFastData(qdisc Qdisc, value []byte) error { func parsePfifoFastData(qdisc Qdisc, value []byte) error {
@@ -676,7 +683,7 @@ func parseSfqData(qdisc Qdisc, value []byte) error {
sfq := qdisc.(*Sfq) sfq := qdisc.(*Sfq)
opt := nl.DeserializeTcSfqQoptV1(value) opt := nl.DeserializeTcSfqQoptV1(value)
sfq.Quantum = opt.TcSfqQopt.Quantum sfq.Quantum = opt.TcSfqQopt.Quantum
sfq.Perturb = uint8(opt.TcSfqQopt.Perturb) sfq.Perturb = opt.TcSfqQopt.Perturb
sfq.Limit = opt.TcSfqQopt.Limit sfq.Limit = opt.TcSfqQopt.Limit
sfq.Divisor = opt.TcSfqQopt.Divisor sfq.Divisor = opt.TcSfqQopt.Divisor

View File

@@ -3,6 +3,7 @@ package netlink
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"net" "net"
@@ -17,6 +18,7 @@ type RdmaLinkAttrs struct {
FirmwareVersion string FirmwareVersion string
NodeGuid string NodeGuid string
SysImageGuid string SysImageGuid string
NumPorts uint32
} }
// Link represents a rdma device from netlink. // Link represents a rdma device from netlink.
@@ -68,6 +70,11 @@ func executeOneGetRdmaLink(data []byte) (*RdmaLink, error) {
r := bytes.NewReader(value) r := bytes.NewReader(value)
binary.Read(r, nl.NativeEndian(), &sysGuid) binary.Read(r, nl.NativeEndian(), &sysGuid)
link.Attrs.SysImageGuid = uint64ToGuidString(sysGuid) link.Attrs.SysImageGuid = uint64ToGuidString(sysGuid)
case nl.RDMA_NLDEV_ATTR_PORT_INDEX:
var availablePort uint32
r := bytes.NewReader(value)
binary.Read(r, nl.NativeEndian(), &availablePort)
link.Attrs.NumPorts = availablePort
} }
if (len % 4) != 0 { if (len % 4) != 0 {
// Skip pad bytes // Skip pad bytes
@@ -85,19 +92,25 @@ func execRdmaSetLink(req *nl.NetlinkRequest) error {
// RdmaLinkList gets a list of RDMA link devices. // RdmaLinkList gets a list of RDMA link devices.
// Equivalent to: `rdma dev show` // Equivalent to: `rdma dev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func RdmaLinkList() ([]*RdmaLink, error) { func RdmaLinkList() ([]*RdmaLink, error) {
return pkgHandle.RdmaLinkList() return pkgHandle.RdmaLinkList()
} }
// RdmaLinkList gets a list of RDMA link devices. // RdmaLinkList gets a list of RDMA link devices.
// Equivalent to: `rdma dev show` // Equivalent to: `rdma dev show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) { func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) {
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET) proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET)
req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP) req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP)
msgs, err := req.Execute(unix.NETLINK_RDMA, 0) msgs, executeErr := req.Execute(unix.NETLINK_RDMA, 0)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []*RdmaLink var res []*RdmaLink
@@ -109,17 +122,23 @@ func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) {
res = append(res, link) res = append(res, link)
} }
return res, nil return res, executeErr
} }
// RdmaLinkByName finds a link by name and returns a pointer to the object if // RdmaLinkByName finds a link by name and returns a pointer to the object if
// found and nil error, otherwise returns error code. // 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) { func RdmaLinkByName(name string) (*RdmaLink, error) {
return pkgHandle.RdmaLinkByName(name) return pkgHandle.RdmaLinkByName(name)
} }
// RdmaLinkByName finds a link by name and returns a pointer to the object if // RdmaLinkByName finds a link by name and returns a pointer to the object if
// found and nil error, otherwise returns error code. // 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) { func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) {
links, err := h.RdmaLinkList() links, err := h.RdmaLinkList()
if err != nil { if err != nil {
@@ -288,6 +307,8 @@ func RdmaLinkDel(name string) error {
} }
// RdmaLinkDel deletes an rdma link. // RdmaLinkDel deletes an rdma link.
//
// If the returned error is [ErrDumpInterrupted], the caller should retry.
func (h *Handle) RdmaLinkDel(name string) error { func (h *Handle) RdmaLinkDel(name string) error {
link, err := h.RdmaLinkByName(name) link, err := h.RdmaLinkByName(name)
if err != nil { if err != nil {
@@ -307,6 +328,7 @@ func (h *Handle) RdmaLinkDel(name string) error {
// RdmaLinkAdd adds an rdma link for the specified type to the network device. // RdmaLinkAdd adds an rdma link for the specified type to the network device.
// Similar to: rdma link add NAME type TYPE netdev NETDEV // Similar to: rdma link add NAME type TYPE netdev NETDEV
//
// NAME - specifies the new name of the rdma link to add // NAME - specifies the new name of the rdma link to add
// TYPE - specifies which rdma type to use. Link types: // TYPE - specifies which rdma type to use. Link types:
// rxe - Soft RoCE driver // rxe - Soft RoCE driver
@@ -329,3 +351,212 @@ func (h *Handle) RdmaLinkAdd(linkName string, linkType string, netdev string) er
_, err := req.Execute(unix.NETLINK_RDMA, 0) _, err := req.Execute(unix.NETLINK_RDMA, 0)
return err return err
} }
// RdmaResource represents a rdma device resource tracking summaries
type RdmaResource struct {
Index uint32
Name string
RdmaResourceSummaryEntries map[string]uint64
}
// RdmaResourceList list rdma resource tracking information
// Returns all rdma devices resource tracking summary on success or returns error
// otherwise.
// Equivalent to: `rdma resource'
func RdmaResourceList() ([]*RdmaResource, error) {
return pkgHandle.RdmaResourceList()
}
// RdmaResourceList list rdma resource tracking information
// Returns all rdma devices resource tracking summary on success or returns error
// otherwise.
// Equivalent to: `rdma resource'
func (h *Handle) RdmaResourceList() ([]*RdmaResource, error) {
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_RES_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
}
if len(msgs) == 0 {
return nil, fmt.Errorf("No valid response from kernel")
}
var rdmaResources []*RdmaResource
for _, msg := range msgs {
res, err := executeOneGetRdmaResourceList(msg)
if err != nil {
return nil, err
}
rdmaResources = append(rdmaResources, res)
}
return rdmaResources, nil
}
func parseRdmaCounters(counterType uint16, data []byte) (map[string]uint64, error) {
var counterKeyType, counterValueType uint16
switch counterType {
case nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY:
counterKeyType = nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME
counterValueType = nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR
case nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY:
counterKeyType = nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME
counterValueType = nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE
default:
return nil, fmt.Errorf("Invalid counter type: %d", counterType)
}
counters := make(map[string]uint64)
reader := bytes.NewReader(data)
for reader.Len() >= 4 {
_, attrType, _, value := parseNfAttrTLV(reader)
if attrType != counterType {
return nil, fmt.Errorf("Invalid resource summary entry type; %d", attrType)
}
summaryReader := bytes.NewReader(value)
for summaryReader.Len() >= 4 {
_, attrType, len, value := parseNfAttrTLV(summaryReader)
if attrType != counterKeyType {
return nil, fmt.Errorf("Invalid resource summary entry name type; %d", attrType)
}
name := string(value[0 : len-1])
// Skip pad bytes
if (len % 4) != 0 {
summaryReader.Seek(int64(4-(len%4)), seekCurrent)
}
_, attrType, len, value = parseNfAttrTLV(summaryReader)
if attrType != counterValueType {
return nil, fmt.Errorf("Invalid resource summary entry value type; %d", attrType)
}
counters[name] = native.Uint64(value)
}
}
return counters, nil
}
func executeOneGetRdmaResourceList(data []byte) (*RdmaResource, error) {
var res RdmaResource
reader := bytes.NewReader(data)
for reader.Len() >= 4 {
_, attrType, len, value := parseNfAttrTLV(reader)
switch attrType {
case nl.RDMA_NLDEV_ATTR_DEV_INDEX:
var Index uint32
r := bytes.NewReader(value)
binary.Read(r, nl.NativeEndian(), &Index)
res.Index = Index
case nl.RDMA_NLDEV_ATTR_DEV_NAME:
res.Name = string(value[0 : len-1])
case nl.RDMA_NLDEV_ATTR_RES_SUMMARY:
var err error
res.RdmaResourceSummaryEntries, err = parseRdmaCounters(nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY, value)
if err != nil {
return nil, err
}
}
if (len % 4) != 0 {
// Skip pad bytes
reader.Seek(int64(4-(len%4)), seekCurrent)
}
}
return &res, nil
}
// RdmaPortStatistic represents a rdma port statistic counter
type RdmaPortStatistic struct {
PortIndex uint32
Statistics map[string]uint64
}
// RdmaDeviceStatistic represents a rdma device statistic counter
type RdmaDeviceStatistic struct {
RdmaPortStatistics []*RdmaPortStatistic
}
// RdmaStatistic get rdma device statistic counters
// Returns rdma device statistic counters on success or returns error
// otherwise.
// Equivalent to: `rdma statistic show link [DEV]'
func RdmaStatistic(link *RdmaLink) (*RdmaDeviceStatistic, error) {
return pkgHandle.RdmaStatistic(link)
}
// RdmaStatistic get rdma device statistic counters
// Returns rdma device statistic counters on success or returns error
// otherwise.
// Equivalent to: `rdma statistic show link [DEV]'
func (h *Handle) RdmaStatistic(link *RdmaLink) (*RdmaDeviceStatistic, error) {
rdmaLinkStatistic := make([]*RdmaPortStatistic, 0)
for portIndex := uint32(1); portIndex <= link.Attrs.NumPorts; portIndex++ {
portStatistic, err := h.RdmaPortStatisticList(link, portIndex)
if err != nil {
return nil, err
}
rdmaLinkStatistic = append(rdmaLinkStatistic, portStatistic)
}
return &RdmaDeviceStatistic{RdmaPortStatistics: rdmaLinkStatistic}, nil
}
// RdmaPortStatisticList get rdma device port statistic counters
// Returns rdma device port statistic counters on success or returns error
// otherwise.
// Equivalent to: `rdma statistic show link [DEV/PORT]'
func RdmaPortStatisticList(link *RdmaLink, port uint32) (*RdmaPortStatistic, error) {
return pkgHandle.RdmaPortStatisticList(link, port)
}
// RdmaPortStatisticList get rdma device port statistic counters
// Returns rdma device port statistic counters on success or returns error
// otherwise.
// Equivalent to: `rdma statistic show link [DEV/PORT]'
func (h *Handle) RdmaPortStatisticList(link *RdmaLink, port uint32) (*RdmaPortStatistic, error) {
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_STAT_GET)
req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_REQUEST)
b := make([]byte, 4)
native.PutUint32(b, link.Attrs.Index)
data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b)
req.AddData(data)
b = make([]byte, 4)
native.PutUint32(b, port)
data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_PORT_INDEX, b)
req.AddData(data)
msgs, err := req.Execute(unix.NETLINK_RDMA, 0)
if err != nil {
return nil, err
}
if len(msgs) != 1 {
return nil, fmt.Errorf("No valid response from kernel")
}
return executeOneGetRdmaPortStatistics(msgs[0])
}
func executeOneGetRdmaPortStatistics(data []byte) (*RdmaPortStatistic, error) {
var stat RdmaPortStatistic
reader := bytes.NewReader(data)
for reader.Len() >= 4 {
_, attrType, len, value := parseNfAttrTLV(reader)
switch attrType {
case nl.RDMA_NLDEV_ATTR_PORT_INDEX:
var Index uint32
r := bytes.NewReader(value)
binary.Read(r, nl.NativeEndian(), &Index)
stat.PortIndex = Index
case nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTERS:
var err error
stat.Statistics, err = parseRdmaCounters(nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY, value)
if err != nil {
return nil, err
}
}
if (len % 4) != 0 {
// Skip pad bytes
reader.Seek(int64(4-(len%4)), seekCurrent)
}
}
return &stat, nil
}

View File

@@ -45,7 +45,7 @@ type Encap interface {
Equal(Encap) bool Equal(Encap) bool
} }
//Protocol describe what was the originator of the route // Protocol describe what was the originator of the route
type RouteProtocol int type RouteProtocol int
// Route represents a netlink route. // Route represents a netlink route.
@@ -70,6 +70,7 @@ type Route struct {
Via Destination Via Destination
Realm int Realm int
MTU int MTU int
MTULock bool
Window int Window int
Rtt int Rtt int
RttVar int RttVar int
@@ -81,6 +82,7 @@ type Route struct {
InitCwnd int InitCwnd int
Features int Features int
RtoMin int RtoMin int
RtoMinLock bool
InitRwnd int InitRwnd int
QuickACK int QuickACK int
Congctl string Congctl string

View File

@@ -3,6 +3,7 @@ package netlink
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
@@ -269,6 +270,7 @@ type SEG6LocalEncap struct {
Action int Action int
Segments []net.IP // from SRH in seg6_local_lwt Segments []net.IP // from SRH in seg6_local_lwt
Table int // table id for End.T and End.DT6 Table int // table id for End.T and End.DT6
VrfTable int // vrftable id for END.DT4 and END.DT6
InAddr net.IP InAddr net.IP
In6Addr net.IP In6Addr net.IP
Iif int Iif int
@@ -304,6 +306,9 @@ func (e *SEG6LocalEncap) Decode(buf []byte) error {
case nl.SEG6_LOCAL_TABLE: case nl.SEG6_LOCAL_TABLE:
e.Table = int(native.Uint32(attr.Value[0:4])) e.Table = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_TABLE] = true e.Flags[nl.SEG6_LOCAL_TABLE] = true
case nl.SEG6_LOCAL_VRFTABLE:
e.VrfTable = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_VRFTABLE] = true
case nl.SEG6_LOCAL_NH4: case nl.SEG6_LOCAL_NH4:
e.InAddr = net.IP(attr.Value[0:4]) e.InAddr = net.IP(attr.Value[0:4])
e.Flags[nl.SEG6_LOCAL_NH4] = true e.Flags[nl.SEG6_LOCAL_NH4] = true
@@ -360,6 +365,15 @@ func (e *SEG6LocalEncap) Encode() ([]byte, error) {
native.PutUint32(attr[4:], uint32(e.Table)) native.PutUint32(attr[4:], uint32(e.Table))
res = append(res, attr...) res = append(res, attr...)
} }
if e.Flags[nl.SEG6_LOCAL_VRFTABLE] {
attr := make([]byte, 8)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_VRFTABLE)
native.PutUint32(attr[4:], uint32(e.VrfTable))
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_NH4] { if e.Flags[nl.SEG6_LOCAL_NH4] {
attr := make([]byte, 4) attr := make([]byte, 4)
native.PutUint16(attr, 8) native.PutUint16(attr, 8)
@@ -412,6 +426,11 @@ func (e *SEG6LocalEncap) String() string {
if e.Flags[nl.SEG6_LOCAL_TABLE] { if e.Flags[nl.SEG6_LOCAL_TABLE] {
strs = append(strs, fmt.Sprintf("table %d", e.Table)) strs = append(strs, fmt.Sprintf("table %d", e.Table))
} }
if e.Flags[nl.SEG6_LOCAL_VRFTABLE] {
strs = append(strs, fmt.Sprintf("vrftable %d", e.VrfTable))
}
if e.Flags[nl.SEG6_LOCAL_NH4] { if e.Flags[nl.SEG6_LOCAL_NH4] {
strs = append(strs, fmt.Sprintf("nh4 %s", e.InAddr)) strs = append(strs, fmt.Sprintf("nh4 %s", e.InAddr))
} }
@@ -476,7 +495,7 @@ func (e *SEG6LocalEncap) Equal(x Encap) bool {
if !e.InAddr.Equal(o.InAddr) || !e.In6Addr.Equal(o.In6Addr) { if !e.InAddr.Equal(o.InAddr) || !e.In6Addr.Equal(o.In6Addr) {
return false return false
} }
if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif || e.bpf != o.bpf { if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif || e.bpf != o.bpf || e.VrfTable != o.VrfTable {
return false return false
} }
return true return true
@@ -1071,6 +1090,10 @@ func (h *Handle) prepareRouteReq(route *Route, req *nl.NetlinkRequest, msg *nl.R
if route.MTU > 0 { if route.MTU > 0 {
b := nl.Uint32Attr(uint32(route.MTU)) b := nl.Uint32Attr(uint32(route.MTU))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_MTU, b)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_MTU, b))
if route.MTULock {
b := nl.Uint32Attr(uint32(1 << unix.RTAX_MTU))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_LOCK, b))
}
} }
if route.Window > 0 { if route.Window > 0 {
b := nl.Uint32Attr(uint32(route.Window)) b := nl.Uint32Attr(uint32(route.Window))
@@ -1115,6 +1138,10 @@ func (h *Handle) prepareRouteReq(route *Route, req *nl.NetlinkRequest, msg *nl.R
if route.RtoMin > 0 { if route.RtoMin > 0 {
b := nl.Uint32Attr(uint32(route.RtoMin)) b := nl.Uint32Attr(uint32(route.RtoMin))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_RTO_MIN, b)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_RTO_MIN, b))
if route.RtoMinLock {
b := nl.Uint32Attr(uint32(1 << unix.RTAX_RTO_MIN))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_LOCK, b))
}
} }
if route.InitRwnd > 0 { if route.InitRwnd > 0 {
b := nl.Uint32Attr(uint32(route.InitRwnd)) b := nl.Uint32Attr(uint32(route.InitRwnd))
@@ -1163,6 +1190,9 @@ func (h *Handle) prepareRouteReq(route *Route, req *nl.NetlinkRequest, msg *nl.R
// RouteList gets a list of routes in the system. // RouteList gets a list of routes in the system.
// Equivalent to: `ip route show`. // Equivalent to: `ip route show`.
// The list can be filtered by link and ip family. // 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) { func RouteList(link Link, family int) ([]Route, error) {
return pkgHandle.RouteList(link, family) return pkgHandle.RouteList(link, family)
} }
@@ -1170,6 +1200,9 @@ func RouteList(link Link, family int) ([]Route, error) {
// RouteList gets a list of routes in the system. // RouteList gets a list of routes in the system.
// Equivalent to: `ip route show`. // Equivalent to: `ip route show`.
// The list can be filtered by link and ip family. // 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) { func (h *Handle) RouteList(link Link, family int) ([]Route, error) {
routeFilter := &Route{} routeFilter := &Route{}
if link != nil { if link != nil {
@@ -1188,6 +1221,9 @@ func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, e
// RouteListFiltered gets a list of routes in the system filtered with specified rules. // RouteListFiltered gets a list of routes in the system filtered with specified rules.
// All rules must be defined in RouteFilter struct // 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) { func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
var res []Route var res []Route
err := h.RouteListFilteredIter(family, filter, filterMask, func(route Route) (cont bool) { err := h.RouteListFilteredIter(family, filter, filterMask, func(route Route) (cont bool) {
@@ -1202,17 +1238,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 // 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. // 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 { func RouteListFilteredIter(family int, filter *Route, filterMask uint64, f func(Route) (cont bool)) error {
return pkgHandle.RouteListFilteredIter(family, filter, filterMask, f) 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 { 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) req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP)
rtmsg := &nl.RtMsg{} rtmsg := &nl.RtMsg{}
rtmsg.Family = uint8(family) rtmsg.Family = uint8(family)
var parseErr error 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) msg := nl.DeserializeRtMsg(m)
if family != FAMILY_ALL && msg.Family != uint8(family) { if family != FAMILY_ALL && msg.Family != uint8(family) {
// Ignore routes not matching requested family // Ignore routes not matching requested family
@@ -1270,13 +1311,13 @@ func (h *Handle) RouteListFilteredIter(family int, filter *Route, filterMask uin
} }
return f(route) return f(route)
}) })
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return err return executeErr
} }
if parseErr != nil { if parseErr != nil {
return parseErr return parseErr
} }
return nil return executeErr
} }
// deserializeRoute decodes a binary netlink message into a Route struct // deserializeRoute decodes a binary netlink message into a Route struct
@@ -1425,6 +1466,9 @@ func deserializeRoute(m []byte) (Route, error) {
switch metric.Attr.Type { switch metric.Attr.Type {
case unix.RTAX_MTU: case unix.RTAX_MTU:
route.MTU = int(native.Uint32(metric.Value[0:4])) route.MTU = int(native.Uint32(metric.Value[0:4]))
case unix.RTAX_LOCK:
route.MTULock = native.Uint32(metric.Value[0:4]) == uint32(1<<unix.RTAX_MTU)
route.RtoMinLock = native.Uint32(metric.Value[0:4]) == uint32(1<<unix.RTAX_RTO_MIN)
case unix.RTAX_WINDOW: case unix.RTAX_WINDOW:
route.Window = int(native.Uint32(metric.Value[0:4])) route.Window = int(native.Uint32(metric.Value[0:4]))
case unix.RTAX_RTT: case unix.RTAX_RTT:
@@ -1518,6 +1562,7 @@ type RouteGetOptions struct {
Iif string Iif string
IifIndex int IifIndex int
Oif string Oif string
OifIndex int
VrfName string VrfName string
SrcAddr net.IP SrcAddr net.IP
UID *uint32 UID *uint32
@@ -1597,14 +1642,20 @@ func (h *Handle) RouteGetWithOptions(destination net.IP, options *RouteGetOption
req.AddData(nl.NewRtAttr(unix.RTA_IIF, b)) req.AddData(nl.NewRtAttr(unix.RTA_IIF, b))
} }
oifIndex := uint32(0)
if len(options.Oif) > 0 { if len(options.Oif) > 0 {
link, err := h.LinkByName(options.Oif) link, err := h.LinkByName(options.Oif)
if err != nil { if err != nil {
return nil, err return nil, err
} }
oifIndex = uint32(link.Attrs().Index)
} else if options.OifIndex > 0 {
oifIndex = uint32(options.OifIndex)
}
if oifIndex > 0 {
b := make([]byte, 4) b := make([]byte, 4)
native.PutUint32(b, uint32(link.Attrs().Index)) native.PutUint32(b, oifIndex)
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b)) req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
} }
@@ -1684,6 +1735,10 @@ type RouteSubscribeOptions struct {
// RouteSubscribeWithOptions work like RouteSubscribe but enable to // RouteSubscribeWithOptions work like RouteSubscribe but enable to
// provide additional options to modify the behavior. Currently, the // provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback. // 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 { func RouteSubscribeWithOptions(ch chan<- RouteUpdate, done <-chan struct{}, options RouteSubscribeOptions) error {
if options.Namespace == nil { if options.Namespace == nil {
none := netns.None() none := netns.None()
@@ -1743,6 +1798,9 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
continue continue
} }
for _, m := range msgs { 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 m.Header.Type == unix.NLMSG_DONE {
continue continue
} }

View File

@@ -2,6 +2,7 @@ package netlink
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"net" "net"
@@ -183,12 +184,18 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
// RuleList lists rules in the system. // RuleList lists rules in the system.
// Equivalent to: ip rule list // Equivalent to: ip rule list
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func RuleList(family int) ([]Rule, error) { func RuleList(family int) ([]Rule, error) {
return pkgHandle.RuleList(family) return pkgHandle.RuleList(family)
} }
// RuleList lists rules in the system. // RuleList lists rules in the system.
// Equivalent to: ip rule list // 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) { func (h *Handle) RuleList(family int) ([]Rule, error) {
return h.RuleListFiltered(family, nil, 0) 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 // RuleListFiltered gets a list of rules in the system filtered by the
// specified rule template `filter`. // specified rule template `filter`.
// Equivalent to: ip rule list // 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) { func RuleListFiltered(family int, filter *Rule, filterMask uint64) ([]Rule, error) {
return pkgHandle.RuleListFiltered(family, filter, filterMask) return pkgHandle.RuleListFiltered(family, filter, filterMask)
} }
// RuleListFiltered lists rules in the system. // RuleListFiltered lists rules in the system.
// Equivalent to: ip rule list // 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) { 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) req := h.newNetlinkRequest(unix.RTM_GETRULE, unix.NLM_F_DUMP|unix.NLM_F_REQUEST)
msg := nl.NewIfInfomsg(family) msg := nl.NewIfInfomsg(family)
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWRULE) msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWRULE)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res = make([]Rule, 0) var res = make([]Rule, 0)
@@ -306,7 +319,7 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
res = append(res, *rule) res = append(res, *rule)
} }
return res, nil return res, executeErr
} }
func (pr *RulePortRange) toRtAttrData() []byte { 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. // 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) { func (h *Handle) SocketGet(local, remote net.Addr) (*Socket, error) {
var protocol uint8 var protocol uint8
var localIP, remoteIP net.IP 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. // 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) { func SocketGet(local, remote net.Addr) (*Socket, error) {
return pkgHandle.SocketGet(local, remote) 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. // 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) { func (h *Handle) SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
// Construct the request // Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP) 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 // Do the query and parse the result
var result []*InetDiagTCPInfoResp var result []*InetDiagTCPInfoResp
var err error executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{} sockInfo := &Socket{}
var err error
if err = sockInfo.deserialize(msg); err != nil { if err = sockInfo.deserialize(msg); err != nil {
return false return false
} }
@@ -315,18 +324,24 @@ func (h *Handle) SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error)
return true return true
}) })
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err 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. // 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) { func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
return pkgHandle.SocketDiagTCPInfo(family) return pkgHandle.SocketDiagTCPInfo(family)
} }
// SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket. // 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) { func (h *Handle) SocketDiagTCP(family uint8) ([]*Socket, error) {
// Construct the request // Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP) 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 // Do the query and parse the result
var result []*Socket var result []*Socket
var err error executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{} sockInfo := &Socket{}
if err = sockInfo.deserialize(msg); err != nil { if err := sockInfo.deserialize(msg); err != nil {
return false return false
} }
result = append(result, sockInfo) result = append(result, sockInfo)
return true return true
}) })
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err 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. // 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) { func SocketDiagTCP(family uint8) ([]*Socket, error) {
return pkgHandle.SocketDiagTCP(family) return pkgHandle.SocketDiagTCP(family)
} }
// SocketDiagUDPInfo requests INET_DIAG_INFO for UDP protocol for specified family type and return with extension info. // 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) { func (h *Handle) SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error) {
// Construct the request // Construct the request
var extensions uint8 var extensions uint8
@@ -377,14 +397,14 @@ func (h *Handle) SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error)
// Do the query and parse the result // Do the query and parse the result
var result []*InetDiagUDPInfoResp var result []*InetDiagUDPInfoResp
var err error executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{} sockInfo := &Socket{}
if err = sockInfo.deserialize(msg); err != nil { if err := sockInfo.deserialize(msg); err != nil {
return false return false
} }
var attrs []syscall.NetlinkRouteAttr var attrs []syscall.NetlinkRouteAttr
var err error
if attrs, err = nl.ParseRouteAttr(msg[sizeofSocket:]); err != nil { if attrs, err = nl.ParseRouteAttr(msg[sizeofSocket:]); err != nil {
return false return false
} }
@@ -397,18 +417,24 @@ func (h *Handle) SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error)
result = append(result, res) result = append(result, res)
return true return true
}) })
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err 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. // 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) { func SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error) {
return pkgHandle.SocketDiagUDPInfo(family) return pkgHandle.SocketDiagUDPInfo(family)
} }
// SocketDiagUDP requests INET_DIAG_INFO for UDP protocol for specified family type and return related socket. // 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) { func (h *Handle) SocketDiagUDP(family uint8) ([]*Socket, error) {
// Construct the request // Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP) 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 // Do the query and parse the result
var result []*Socket var result []*Socket
var err error executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &Socket{} sockInfo := &Socket{}
if err = sockInfo.deserialize(msg); err != nil { if err := sockInfo.deserialize(msg); err != nil {
return false return false
} }
result = append(result, sockInfo) result = append(result, sockInfo)
return true return true
}) })
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err 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. // 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) { func SocketDiagUDP(family uint8) ([]*Socket, error) {
return pkgHandle.SocketDiagUDP(family) return pkgHandle.SocketDiagUDP(family)
} }
// UnixSocketDiagInfo requests UNIX_DIAG_INFO for unix sockets and return with extension info. // 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) { func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
// Construct the request // Construct the request
var extensions uint8 var extensions uint8
@@ -456,10 +487,9 @@ func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
}) })
var result []*UnixDiagInfoResp var result []*UnixDiagInfoResp
var err error executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &UnixSocket{} sockInfo := &UnixSocket{}
if err = sockInfo.deserialize(msg); err != nil { if err := sockInfo.deserialize(msg); err != nil {
return false return false
} }
@@ -469,7 +499,8 @@ func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
} }
var attrs []syscall.NetlinkRouteAttr var attrs []syscall.NetlinkRouteAttr
if attrs, err = nl.ParseRouteAttr(msg[sizeofSocket:]); err != nil { var err error
if attrs, err = nl.ParseRouteAttr(msg[sizeofUnixSocket:]); err != nil {
return false return false
} }
@@ -480,18 +511,24 @@ func (h *Handle) UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
result = append(result, res) result = append(result, res)
return true return true
}) })
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
return result, nil return result, executeErr
} }
// UnixSocketDiagInfo requests UNIX_DIAG_INFO for unix sockets and return with extension info. // 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) { func UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
return pkgHandle.UnixSocketDiagInfo() return pkgHandle.UnixSocketDiagInfo()
} }
// UnixSocketDiag requests UNIX_DIAG_INFO for unix sockets. // 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) { func (h *Handle) UnixSocketDiag() ([]*UnixSocket, error) {
// Construct the request // Construct the request
req := h.newNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP) 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 result []*UnixSocket
var err error executeErr := req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
err = req.ExecuteIter(unix.NETLINK_INET_DIAG, nl.SOCK_DIAG_BY_FAMILY, func(msg []byte) bool {
sockInfo := &UnixSocket{} sockInfo := &UnixSocket{}
if err = sockInfo.deserialize(msg); err != nil { if err := sockInfo.deserialize(msg); err != nil {
return false return false
} }
@@ -514,13 +550,16 @@ func (h *Handle) UnixSocketDiag() ([]*UnixSocket, error) {
} }
return true return true
}) })
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
return result, nil return result, executeErr
} }
// UnixSocketDiag requests UNIX_DIAG_INFO for unix sockets. // UnixSocketDiag requests UNIX_DIAG_INFO for unix sockets.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func UnixSocketDiag() ([]*UnixSocket, error) { func UnixSocketDiag() ([]*UnixSocket, error) {
return pkgHandle.UnixSocketDiag() return pkgHandle.UnixSocketDiag()
} }

View File

@@ -52,8 +52,10 @@ func (s *XDPSocket) deserialize(b []byte) error {
return nil 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 // 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) { func SocketXDPGetInfo(ino uint32, cookie uint64) (*XDPDiagInfoResp, error) {
// We have a problem here: dumping AF_XDP sockets currently does not support // 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 // 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. // 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) { func SocketDiagXDP() ([]*XDPDiagInfoResp, error) {
var result []*XDPDiagInfoResp var result []*XDPDiagInfoResp
err := socketDiagXDPExecutor(func(m syscall.NetlinkMessage) error { err := socketDiagXDPExecutor(func(m syscall.NetlinkMessage) error {
@@ -105,10 +110,10 @@ func SocketDiagXDP() ([]*XDPDiagInfoResp, error) {
result = append(result, res) result = append(result, res)
return nil return nil
}) })
if err != nil { if err != nil && !errors.Is(err, ErrDumpInterrupted) {
return nil, err return nil, err
} }
return result, nil return result, err
} }
// socketDiagXDPExecutor requests XDP_DIAG_INFO for XDP family sockets. // socketDiagXDPExecutor requests XDP_DIAG_INFO for XDP family sockets.
@@ -128,6 +133,7 @@ func socketDiagXDPExecutor(receiver func(syscall.NetlinkMessage) error) error {
return err return err
} }
dumpIntr := false
loop: loop:
for { for {
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
@@ -142,6 +148,9 @@ loop:
} }
for _, m := range msgs { for _, m := range msgs {
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 {
dumpIntr = true
}
switch m.Header.Type { switch m.Header.Type {
case unix.NLMSG_DONE: case unix.NLMSG_DONE:
break loop break loop
@@ -154,6 +163,9 @@ loop:
} }
} }
} }
if dumpIntr {
return ErrDumpInterrupted
}
return nil return nil
} }

View File

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

View File

@@ -48,6 +48,14 @@ const (
XFRM_MODE_MAX XFRM_MODE_MAX
) )
// SADir is an enum representing an ipsec template direction.
type SADir uint8
const (
XFRM_SA_DIR_IN SADir = iota + 1
XFRM_SA_DIR_OUT
)
func (m Mode) String() string { func (m Mode) String() string {
switch m { switch m {
case XFRM_MODE_TRANSPORT: case XFRM_MODE_TRANSPORT:

View File

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

View File

@@ -1,6 +1,7 @@
package netlink package netlink
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"time" "time"
@@ -112,7 +113,9 @@ type XfrmState struct {
Statistics XfrmStateStats Statistics XfrmStateStats
Mark *XfrmMark Mark *XfrmMark
OutputMark *XfrmMark OutputMark *XfrmMark
SADir SADir
Ifid int Ifid int
Pcpunum *uint32
Auth *XfrmStateAlgo Auth *XfrmStateAlgo
Crypt *XfrmStateAlgo Crypt *XfrmStateAlgo
Aead *XfrmStateAlgo Aead *XfrmStateAlgo
@@ -125,8 +128,8 @@ type XfrmState struct {
} }
func (sa XfrmState) String() string { func (sa XfrmState) String() string {
return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, OutputMark: %v, Ifid: %d, Auth: %v, Crypt: %v, Aead: %v, Encap: %v, ESN: %t, DontEncapDSCP: %t, OSeqMayWrap: %t, Replay: %v", return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, OutputMark: %v, SADir: %d, Ifid: %d, Pcpunum: %d, Auth: %v, Crypt: %v, Aead: %v, Encap: %v, ESN: %t, DontEncapDSCP: %t, OSeqMayWrap: %t, Replay: %v",
sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.OutputMark, sa.Ifid, sa.Auth, sa.Crypt, sa.Aead, sa.Encap, sa.ESN, sa.DontEncapDSCP, sa.OSeqMayWrap, sa.Replay) sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.OutputMark, sa.SADir, sa.Ifid, *sa.Pcpunum, sa.Auth, sa.Crypt, sa.Aead, sa.Encap, sa.ESN, sa.DontEncapDSCP, sa.OSeqMayWrap, sa.Replay)
} }
func (sa XfrmState) Print(stats bool) string { func (sa XfrmState) Print(stats bool) string {
if !stats { if !stats {
@@ -332,11 +335,21 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
req.AddData(out) req.AddData(out)
} }
if state.SADir != 0 {
saDir := nl.NewRtAttr(nl.XFRMA_SA_DIR, nl.Uint8Attr(uint8(state.SADir)))
req.AddData(saDir)
}
if state.Ifid != 0 { if state.Ifid != 0 {
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid))) ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
req.AddData(ifId) req.AddData(ifId)
} }
if state.Pcpunum != nil {
pcpuNum := nl.NewRtAttr(nl.XFRMA_SA_PCPU, nl.Uint32Attr(uint32(*state.Pcpunum)))
req.AddData(pcpuNum)
}
_, err := req.Execute(unix.NETLINK_XFRM, 0) _, err := req.Execute(unix.NETLINK_XFRM, 0)
return err return err
} }
@@ -382,6 +395,9 @@ func (h *Handle) XfrmStateDel(state *XfrmState) error {
// XfrmStateList gets a list of xfrm states in the system. // XfrmStateList gets a list of xfrm states in the system.
// Equivalent to: `ip [-4|-6] xfrm state show`. // Equivalent to: `ip [-4|-6] xfrm state show`.
// The list can be filtered by ip family. // 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) { func XfrmStateList(family int) ([]XfrmState, error) {
return pkgHandle.XfrmStateList(family) return pkgHandle.XfrmStateList(family)
} }
@@ -389,12 +405,15 @@ func XfrmStateList(family int) ([]XfrmState, error) {
// XfrmStateList gets a list of xfrm states in the system. // XfrmStateList gets a list of xfrm states in the system.
// Equivalent to: `ip xfrm state show`. // Equivalent to: `ip xfrm state show`.
// The list can be filtered by ip family. // 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) { func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) {
req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, unix.NLM_F_DUMP) req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, unix.NLM_F_DUMP)
msgs, err := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWSA) msgs, executeErr := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
if err != nil { if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, err return nil, executeErr
} }
var res []XfrmState var res []XfrmState
@@ -407,7 +426,7 @@ func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) {
return nil, err return nil, err
} }
} }
return res, nil return res, executeErr
} }
// XfrmStateGet gets the xfrm state described by the ID, if found. // XfrmStateGet gets the xfrm state described by the ID, if found.
@@ -452,6 +471,11 @@ func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState
req.AddData(ifId) req.AddData(ifId)
} }
if state.Pcpunum != nil {
pcpuNum := nl.NewRtAttr(nl.XFRMA_SA_PCPU, nl.Uint32Attr(uint32(*state.Pcpunum)))
req.AddData(pcpuNum)
}
resType := nl.XFRM_MSG_NEWSA resType := nl.XFRM_MSG_NEWSA
if nlProto == nl.XFRM_MSG_DELSA { if nlProto == nl.XFRM_MSG_DELSA {
resType = 0 resType = 0
@@ -574,8 +598,13 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
if state.OutputMark.Mask == 0xffffffff { if state.OutputMark.Mask == 0xffffffff {
state.OutputMark.Mask = 0 state.OutputMark.Mask = 0
} }
case nl.XFRMA_SA_DIR:
state.SADir = SADir(attr.Value[0])
case nl.XFRMA_IF_ID: case nl.XFRMA_IF_ID:
state.Ifid = int(native.Uint32(attr.Value)) state.Ifid = int(native.Uint32(attr.Value))
case nl.XFRMA_SA_PCPU:
pcpuNum := native.Uint32(attr.Value)
state.Pcpunum = &pcpuNum
case nl.XFRMA_REPLAY_VAL: case nl.XFRMA_REPLAY_VAL:
if state.Replay == nil { if state.Replay == nil {
state.Replay = new(XfrmReplayState) state.Replay = new(XfrmReplayState)

View File

@@ -1,2 +1,26 @@
linters:
enable:
- errcheck
- errorlint
- gocritic
- gosec
- gosimple
- govet
- gci
- misspell
- nonamedreturns
- staticcheck
- unconvert
- unparam
- unused
- whitespace
linters-settings:
gci:
sections:
- standard
- default
- prefix(github.com/vishvananda)
run: run:
timeout: 5m timeout: 5m

9
vendor/github.com/vishvananda/netns/.yamllint.yml generated vendored Normal file
View File

@@ -0,0 +1,9 @@
---
extends: default
rules:
document-start: disable
line-length: disable
truthy:
ignore: |
.github/workflows/*.yml

View File

@@ -26,19 +26,19 @@ const bindMountPath = "/run/netns" /* Bind mount path for named netns */
// Setns sets namespace using golang.org/x/sys/unix.Setns. // Setns sets namespace using golang.org/x/sys/unix.Setns.
// //
// Deprecated: Use golang.org/x/sys/unix.Setns instead. // Deprecated: Use golang.org/x/sys/unix.Setns instead.
func Setns(ns NsHandle, nstype int) (err error) { func Setns(ns NsHandle, nstype int) error {
return unix.Setns(int(ns), nstype) return unix.Setns(int(ns), nstype)
} }
// Set sets the current network namespace to the namespace represented // Set sets the current network namespace to the namespace represented
// by NsHandle. // by NsHandle.
func Set(ns NsHandle) (err error) { func Set(ns NsHandle) error {
return unix.Setns(int(ns), unix.CLONE_NEWNET) return unix.Setns(int(ns), unix.CLONE_NEWNET)
} }
// New creates a new network namespace, sets it as current and returns // New creates a new network namespace, sets it as current and returns
// a handle to it. // a handle to it.
func New() (ns NsHandle, err error) { func New() (NsHandle, error) {
if err := unix.Unshare(unix.CLONE_NEWNET); err != nil { if err := unix.Unshare(unix.CLONE_NEWNET); err != nil {
return -1, err return -1, err
} }
@@ -49,7 +49,7 @@ func New() (ns NsHandle, err error) {
// and returns a handle to it // and returns a handle to it
func NewNamed(name string) (NsHandle, error) { func NewNamed(name string) (NsHandle, error) {
if _, err := os.Stat(bindMountPath); os.IsNotExist(err) { if _, err := os.Stat(bindMountPath); os.IsNotExist(err) {
err = os.MkdirAll(bindMountPath, 0755) err = os.MkdirAll(bindMountPath, 0o755)
if err != nil { if err != nil {
return None(), err return None(), err
} }
@@ -62,7 +62,7 @@ func NewNamed(name string) (NsHandle, error) {
namedPath := path.Join(bindMountPath, name) namedPath := path.Join(bindMountPath, name)
f, err := os.OpenFile(namedPath, os.O_CREATE|os.O_EXCL, 0444) f, err := os.OpenFile(namedPath, os.O_CREATE|os.O_EXCL, 0o444)
if err != nil { if err != nil {
newNs.Close() newNs.Close()
return None(), err return None(), err
@@ -217,11 +217,12 @@ func getPidForContainer(id string) (int, error) {
id += "*" id += "*"
var pidFile string var pidFile string
if cgroupVer == 1 { switch cgroupVer {
case 1:
pidFile = "tasks" pidFile = "tasks"
} else if cgroupVer == 2 { case 2:
pidFile = "cgroup.procs" pidFile = "cgroup.procs"
} else { default:
return -1, fmt.Errorf("Invalid cgroup version '%d'", cgroupVer) return -1, fmt.Errorf("Invalid cgroup version '%d'", cgroupVer)
} }
@@ -247,6 +248,10 @@ func getPidForContainer(id string) (int, error) {
filepath.Join(cgroupRoot, "kubepods.slice", "*.slice", "*", "docker-"+id+".scope", pidFile), filepath.Join(cgroupRoot, "kubepods.slice", "*.slice", "*", "docker-"+id+".scope", pidFile),
// Same as above but for Guaranteed QoS // Same as above but for Guaranteed QoS
filepath.Join(cgroupRoot, "kubepods.slice", "*", "docker-"+id+".scope", pidFile), filepath.Join(cgroupRoot, "kubepods.slice", "*", "docker-"+id+".scope", pidFile),
// Support for nerdctl
filepath.Join(cgroupRoot, "system.slice", "nerdctl-"+id+".scope", pidFile),
// Support for finch
filepath.Join(cgroupRoot, "..", "systemd", "finch", id, pidFile),
} }
var filename string var filename string
@@ -276,7 +281,7 @@ func getPidForContainer(id string) (int, error) {
pid, err = strconv.Atoi(result[0]) pid, err = strconv.Atoi(result[0])
if err != nil { if err != nil {
return pid, fmt.Errorf("Invalid pid '%s': %s", result[0], err) return pid, fmt.Errorf("Invalid pid '%s': %w", result[0], err)
} }
return pid, nil return pid, nil

View File

@@ -3,27 +3,23 @@
package netns package netns
import ( import "errors"
"errors"
)
var ( var ErrNotImplemented = errors.New("not implemented")
ErrNotImplemented = errors.New("not implemented")
)
// Setns sets namespace using golang.org/x/sys/unix.Setns on Linux. It // Setns sets namespace using golang.org/x/sys/unix.Setns on Linux. It
// is not implemented on other platforms. // is not implemented on other platforms.
// //
// Deprecated: Use golang.org/x/sys/unix.Setns instead. // Deprecated: Use golang.org/x/sys/unix.Setns instead.
func Setns(ns NsHandle, nstype int) (err error) { func Setns(ns NsHandle, nstype int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func Set(ns NsHandle) (err error) { func Set(ns NsHandle) error {
return ErrNotImplemented return ErrNotImplemented
} }
func New() (ns NsHandle, err error) { func New() (NsHandle, error) {
return -1, ErrNotImplemented return -1, ErrNotImplemented
} }
@@ -51,7 +47,7 @@ func GetFromPid(pid int) (NsHandle, error) {
return -1, ErrNotImplemented return -1, ErrNotImplemented
} }
func GetFromThread(pid, tid int) (NsHandle, error) { func GetFromThread(pid int, tid int) (NsHandle, error) {
return -1, ErrNotImplemented return -1, ErrNotImplemented
} }

4
vendor/modules.txt vendored
View File

@@ -84,11 +84,11 @@ github.com/sirupsen/logrus/hooks/test
# github.com/urfave/cli v1.22.16 # github.com/urfave/cli v1.22.16
## explicit; go 1.11 ## explicit; go 1.11
github.com/urfave/cli github.com/urfave/cli
# github.com/vishvananda/netlink v1.3.0 # github.com/vishvananda/netlink v1.3.1
## explicit; go 1.12 ## explicit; go 1.12
github.com/vishvananda/netlink github.com/vishvananda/netlink
github.com/vishvananda/netlink/nl github.com/vishvananda/netlink/nl
# github.com/vishvananda/netns v0.0.4 # github.com/vishvananda/netns v0.0.5
## explicit; go 1.17 ## explicit; go 1.17
github.com/vishvananda/netns github.com/vishvananda/netns
# golang.org/x/net v0.40.0 # golang.org/x/net v0.40.0