runtime: vendor: Bump the netlink package dependency

We need to be able to get the IP family from the netlink route meesages,
and the Route.Family field only got recently added to the netlink
package.

The update generates static check warnings about the call for
nethandler.Delete() being deprecated in favor of a Close() call instead.
So we include the s/Delete()/Close()/ change as part of this PR.

Signed-off-by: Samuel Ortiz <s.ortiz@apple.com>
This commit is contained in:
Samuel Ortiz 2021-09-28 17:55:46 +02:00
parent fd5c858390
commit f85fe70231
51 changed files with 2330 additions and 295 deletions

View File

@ -170,7 +170,7 @@ func newNetmon(params netmonParams) (*netmon, error) {
func (n *netmon) cleanup() {
os.RemoveAll(n.storagePath)
n.netHandler.Delete()
n.netHandler.Close()
close(n.linkDoneCh)
close(n.rtDoneCh)
}

View File

@ -348,7 +348,7 @@ func TestScanNetwork(t *testing.T) {
handler, err := netlink.NewHandle(netlinkFamily)
assert.Nil(t, err)
assert.NotNil(t, handler)
defer handler.Delete()
defer handler.Close()
idx, expected := testCreateDummyNetwork(t, handler)
@ -480,7 +480,7 @@ func TestActionsCLI(t *testing.T) {
handler, err := netlink.NewHandle(netlinkFamily)
assert.Nil(t, err)
assert.NotNil(t, handler)
defer handler.Delete()
defer handler.Close()
n.netHandler = handler
@ -569,7 +569,7 @@ func TestHandleRTMNewLink(t *testing.T) {
handler, err := netlink.NewHandle(netlinkFamily)
assert.Nil(t, err)
assert.NotNil(t, handler)
defer handler.Delete()
defer handler.Close()
n.netHandler = handler
err = n.handleRTMNewLink(ev)
assert.NotNil(t, err)
@ -690,7 +690,7 @@ func TestHandleRouteEvent(t *testing.T) {
handler, err := netlink.NewHandle(netlinkFamily)
assert.Nil(t, err)
assert.NotNil(t, handler)
defer handler.Delete()
defer handler.Close()
n.netHandler = handler

View File

@ -40,7 +40,7 @@ require (
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/stretchr/testify v1.6.1
github.com/urfave/cli v1.22.2
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
github.com/vishvananda/netlink v1.1.1-0.20210924202909-187053b97868
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
go.opentelemetry.io/otel v0.15.0
go.opentelemetry.io/otel/exporters/trace/jaeger v0.15.0

View File

@ -532,8 +532,9 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netlink v1.1.1-0.20210924202909-187053b97868 h1:FFT5/l13iFxg+2dzyoiXZPmMtoclsyBKnUqTEzYpDXw=
github.com/vishvananda/netlink v1.1.1-0.20210924202909-187053b97868/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=

View File

@ -1,6 +1,6 @@
# netlink - netlink library for go #
[![Build Status](https://travis-ci.org/vishvananda/netlink.png?branch=master)](https://travis-ci.org/vishvananda/netlink) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink)
[![Build Status](https://app.travis-ci.com/vishvananda/netlink.svg?branch=master)](https://app.travis-ci.com/github/vishvananda/netlink) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink)
The netlink package provides a simple netlink library for go. Netlink
is the interface a user-space program in linux uses to communicate with

View File

@ -268,7 +268,7 @@ func parseAddr(m []byte) (addr Addr, family int, err error) {
// But obviously, as there are IPv6 PtP addresses, too,
// IFA_LOCAL should also be handled for IPv6.
if local != nil {
if family == FAMILY_V4 && local.IP.Equal(dst.IP) {
if family == FAMILY_V4 && dst != nil && local.IP.Equal(dst.IP) {
addr.IPNet = dst
} else {
addr.IPNet = local
@ -357,7 +357,8 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
msgs, from, err := s.Receive()
if err != nil {
if cberr != nil {
cberr(err)
cberr(fmt.Errorf("Receive failed: %v",
err))
}
return
}
@ -372,7 +373,6 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue

View File

@ -16,6 +16,30 @@ const (
BPF_PROG_TYPE_SCHED_ACT
BPF_PROG_TYPE_TRACEPOINT
BPF_PROG_TYPE_XDP
BPF_PROG_TYPE_PERF_EVENT
BPF_PROG_TYPE_CGROUP_SKB
BPF_PROG_TYPE_CGROUP_SOCK
BPF_PROG_TYPE_LWT_IN
BPF_PROG_TYPE_LWT_OUT
BPF_PROG_TYPE_LWT_XMIT
BPF_PROG_TYPE_SOCK_OPS
BPF_PROG_TYPE_SK_SKB
BPF_PROG_TYPE_CGROUP_DEVICE
BPF_PROG_TYPE_SK_MSG
BPF_PROG_TYPE_RAW_TRACEPOINT
BPF_PROG_TYPE_CGROUP_SOCK_ADDR
BPF_PROG_TYPE_LWT_SEG6LOCAL
BPF_PROG_TYPE_LIRC_MODE2
BPF_PROG_TYPE_SK_REUSEPORT
BPF_PROG_TYPE_FLOW_DISSECTOR
BPF_PROG_TYPE_CGROUP_SYSCTL
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE
BPF_PROG_TYPE_CGROUP_SOCKOPT
BPF_PROG_TYPE_TRACING
BPF_PROG_TYPE_STRUCT_OPS
BPF_PROG_TYPE_EXT
BPF_PROG_TYPE_LSM
BPF_PROG_TYPE_SK_LOOKUP
)
type BPFAttr struct {

View File

@ -176,6 +176,12 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
options.AddRtAttr(nl.TCA_HTB_PARMS, opt.Serialize())
options.AddRtAttr(nl.TCA_HTB_RTAB, SerializeRtab(rtab))
options.AddRtAttr(nl.TCA_HTB_CTAB, SerializeRtab(ctab))
if htb.Rate >= uint64(1<<32) {
options.AddRtAttr(nl.TCA_HTB_RATE64, nl.Uint64Attr(htb.Rate))
}
if htb.Ceil >= uint64(1<<32) {
options.AddRtAttr(nl.TCA_HTB_CEIL64, nl.Uint64Attr(htb.Ceil))
}
case "hfsc":
hfsc := class.(*HfscClass)
opt := nl.HfscCopt{}
@ -185,9 +191,9 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
opt.Fsc.Set(fm1/8, fd, fm2/8)
um1, ud, um2 := hfsc.Usc.Attrs()
opt.Usc.Set(um1/8, ud, um2/8)
nl.NewRtAttrChild(options, nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
nl.NewRtAttrChild(options, nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
nl.NewRtAttrChild(options, nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
}
req.AddData(options)
return nil
@ -306,6 +312,10 @@ func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, erro
htb.Quantum = opt.Quantum
htb.Level = opt.Level
htb.Prio = opt.Prio
case nl.TCA_HTB_RATE64:
htb.Rate = native.Uint64(datum.Value[0:8])
case nl.TCA_HTB_CEIL64:
htb.Ceil = native.Uint64(datum.Value[0:8])
}
}
return detailed, nil
@ -331,7 +341,6 @@ func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, err
func parseTcStats(data []byte) (*ClassStatistics, error) {
buf := &bytes.Buffer{}
buf.Write(data)
native := nl.NativeEndian()
tcStats := &tcStats{}
if err := binary.Read(buf, native, tcStats); err != nil {
return nil, err
@ -353,7 +362,6 @@ func parseTcStats(data []byte) (*ClassStatistics, error) {
func parseGnetStats(data []byte, gnetStats interface{}) error {
buf := &bytes.Buffer{}
buf.Write(data)
native := nl.NativeEndian()
return binary.Read(buf, native, gnetStats)
}

View File

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net"
"time"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
@ -145,16 +146,23 @@ type ConntrackFlow struct {
Forward ipTuple
Reverse ipTuple
Mark uint32
TimeStart uint64
TimeStop uint64
TimeOut uint32
}
func (s *ConntrackFlow) String() string {
// conntrack cmd output:
// udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 packets=5 bytes=532 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 packets=10 bytes=1078 mark=0
return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d packets=%d bytes=%d\tsrc=%s dst=%s sport=%d dport=%d packets=%d bytes=%d mark=%d",
// start=2019-07-26 01:26:21.557800506 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=30(sec)
start := time.Unix(0, int64(s.TimeStart))
stop := time.Unix(0, int64(s.TimeStop))
timeout := int32(s.TimeOut)
return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d packets=%d bytes=%d\tsrc=%s dst=%s sport=%d dport=%d packets=%d bytes=%d mark=0x%x start=%v stop=%v timeout=%d(sec)",
nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol,
s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, s.Forward.Packets, s.Forward.Bytes,
s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Reverse.Packets, s.Reverse.Bytes,
s.Mark)
s.Mark, start, stop, timeout)
}
// This method parse the ip tuple structure
@ -174,25 +182,43 @@ func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 {
tpl.DstIP = v
}
}
// Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO
reader.Seek(4, seekCurrent)
_, t, _, v := parseNfAttrTLV(reader)
// Get total length of nested protocol-specific info.
_, _, protoInfoTotalLen := parseNfAttrTL(reader)
_, t, l, v := parseNfAttrTLV(reader)
// Track the number of bytes read.
protoInfoBytesRead := uint16(nl.SizeofNfattr) + l
if t == nl.CTA_PROTO_NUM {
tpl.Protocol = uint8(v[0])
}
// Skip some padding 3 bytes
// We only parse TCP & UDP headers. Skip the others.
if tpl.Protocol != 6 && tpl.Protocol != 17 {
// skip the rest
bytesRemaining := protoInfoTotalLen - protoInfoBytesRead
reader.Seek(int64(bytesRemaining), seekCurrent)
return tpl.Protocol
}
// Skip 3 bytes of padding
reader.Seek(3, seekCurrent)
protoInfoBytesRead += 3
for i := 0; i < 2; i++ {
_, t, _ := parseNfAttrTL(reader)
protoInfoBytesRead += uint16(nl.SizeofNfattr)
switch t {
case nl.CTA_PROTO_SRC_PORT:
parseBERaw16(reader, &tpl.SrcPort)
protoInfoBytesRead += 2
case nl.CTA_PROTO_DST_PORT:
parseBERaw16(reader, &tpl.DstPort)
protoInfoBytesRead += 2
}
// Skip some padding 2 byte
// Skip 2 bytes of padding
reader.Seek(2, seekCurrent)
protoInfoBytesRead += 2
}
// Skip any remaining/unknown parts of the message
bytesRemaining := protoInfoTotalLen - protoInfoBytesRead
reader.Seek(int64(bytesRemaining), seekCurrent)
return tpl.Protocol
}
@ -211,10 +237,14 @@ func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) {
binary.Read(r, nl.NativeEndian(), &attrType)
isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED
attrType = attrType & (nl.NLA_F_NESTED - 1)
return isNested, attrType, len
}
func skipNfAttrValue(r *bytes.Reader, len uint16) {
len = (len + nl.NLA_ALIGNTO - 1) & ^(nl.NLA_ALIGNTO - 1)
r.Seek(int64(len), seekCurrent)
}
func parseBERaw16(r *bytes.Reader, v *uint16) {
binary.Read(r, binary.BigEndian, v)
}
@ -241,6 +271,36 @@ func parseByteAndPacketCounters(r *bytes.Reader) (bytes, packets uint64) {
return
}
// when the flow is alive, only the timestamp_start is returned in structure
func parseTimeStamp(r *bytes.Reader, readSize uint16) (tstart, tstop uint64) {
var numTimeStamps int
oneItem := nl.SizeofNfattr + 8 // 4 bytes attr header + 8 bytes timestamp
if readSize == uint16(oneItem) {
numTimeStamps = 1
} else if readSize == 2*uint16(oneItem) {
numTimeStamps = 2
} else {
return
}
for i := 0; i < numTimeStamps; i++ {
switch _, t, _ := parseNfAttrTL(r); t {
case nl.CTA_TIMESTAMP_START:
parseBERaw64(r, &tstart)
case nl.CTA_TIMESTAMP_STOP:
parseBERaw64(r, &tstop)
default:
return
}
}
return
}
func parseTimeOut(r *bytes.Reader) (ttimeout uint32) {
parseBERaw32(r, &ttimeout)
return
}
func parseConnectionMark(r *bytes.Reader) (mark uint32) {
parseBERaw32(r, &mark)
return
@ -266,25 +326,37 @@ func parseRawData(data []byte) *ConntrackFlow {
if nested, t, l := parseNfAttrTL(reader); nested {
switch t {
case nl.CTA_TUPLE_ORIG:
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
if nested, t, l = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
parseIpTuple(reader, &s.Forward)
}
case nl.CTA_TUPLE_REPLY:
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
if nested, t, l = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
parseIpTuple(reader, &s.Reverse)
} else {
// Header not recognized skip it
reader.Seek(int64(l), seekCurrent)
skipNfAttrValue(reader, l)
}
case nl.CTA_COUNTERS_ORIG:
s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader)
case nl.CTA_COUNTERS_REPLY:
s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader)
case nl.CTA_TIMESTAMP:
s.TimeStart, s.TimeStop = parseTimeStamp(reader, l)
case nl.CTA_PROTOINFO:
skipNfAttrValue(reader, l)
default:
skipNfAttrValue(reader, l)
}
} else {
switch t {
case nl.CTA_MARK:
s.Mark = parseConnectionMark(reader)
case nl.CTA_TIMEOUT:
s.TimeOut = parseTimeOut(reader)
case nl.CTA_STATUS, nl.CTA_USE, nl.CTA_ID:
skipNfAttrValue(reader, l)
default:
skipNfAttrValue(reader, l)
}
}
}
@ -346,23 +418,34 @@ type CustomConntrackFilter interface {
}
type ConntrackFilter struct {
ipFilter map[ConntrackFilterType]net.IP
ipNetFilter map[ConntrackFilterType]*net.IPNet
portFilter map[ConntrackFilterType]uint16
protoFilter uint8
}
// AddIP adds an IP to the conntrack filter
func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
if f.ipFilter == nil {
f.ipFilter = make(map[ConntrackFilterType]net.IP)
// AddIPNet adds a IP subnet to the conntrack filter
func (f *ConntrackFilter) AddIPNet(tp ConntrackFilterType, ipNet *net.IPNet) error {
if ipNet == nil {
return fmt.Errorf("Filter attribute empty")
}
if _, ok := f.ipFilter[tp]; ok {
if f.ipNetFilter == nil {
f.ipNetFilter = make(map[ConntrackFilterType]*net.IPNet)
}
if _, ok := f.ipNetFilter[tp]; ok {
return errors.New("Filter attribute already present")
}
f.ipFilter[tp] = ip
f.ipNetFilter[tp] = ipNet
return nil
}
// AddIP adds an IP to the conntrack filter
func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
if ip == nil {
return fmt.Errorf("Filter attribute empty")
}
return f.AddIPNet(tp, NewIPNet(ip))
}
// AddPort adds a Port to the conntrack filter if the Layer 4 protocol allows it
func (f *ConntrackFilter) AddPort(tp ConntrackFilterType, port uint16) error {
switch f.protoFilter {
@ -394,7 +477,7 @@ func (f *ConntrackFilter) AddProtocol(proto uint8) error {
// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
// false otherwise
func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
if len(f.ipFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 {
if len(f.ipNetFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 {
// empty filter always not match
return false
}
@ -408,30 +491,30 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
match := true
// IP conntrack filter
if len(f.ipFilter) > 0 {
if len(f.ipNetFilter) > 0 {
// -orig-src ip Source address from original direction
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
match = match && elem.Equal(flow.Forward.SrcIP)
if elem, found := f.ipNetFilter[ConntrackOrigSrcIP]; found {
match = match && elem.Contains(flow.Forward.SrcIP)
}
// -orig-dst ip Destination address from original direction
if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found {
match = match && elem.Equal(flow.Forward.DstIP)
if elem, found := f.ipNetFilter[ConntrackOrigDstIP]; match && found {
match = match && elem.Contains(flow.Forward.DstIP)
}
// -src-nat ip Source NAT ip
if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found {
match = match && elem.Equal(flow.Reverse.SrcIP)
if elem, found := f.ipNetFilter[ConntrackReplySrcIP]; match && found {
match = match && elem.Contains(flow.Reverse.SrcIP)
}
// -dst-nat ip Destination NAT ip
if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found {
match = match && elem.Equal(flow.Reverse.DstIP)
if elem, found := f.ipNetFilter[ConntrackReplyDstIP]; match && found {
match = match && elem.Contains(flow.Reverse.DstIP)
}
// Match source or destination reply IP
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found {
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
if elem, found := f.ipNetFilter[ConntrackReplyAnyIP]; match && found {
match = match && (elem.Contains(flow.Reverse.SrcIP) || elem.Contains(flow.Reverse.DstIP))
}
}

View File

@ -1,9 +1,10 @@
package netlink
import (
"fmt"
"net"
"syscall"
"fmt"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
@ -27,6 +28,43 @@ type DevlinkDevice struct {
Attrs DevlinkDevAttrs
}
// DevlinkPortFn represents port function and its attributes
type DevlinkPortFn struct {
HwAddr net.HardwareAddr
State uint8
OpState uint8
}
// DevlinkPortFnSetAttrs represents attributes to set
type DevlinkPortFnSetAttrs struct {
FnAttrs DevlinkPortFn
HwAddrValid bool
StateValid bool
}
// DevlinkPort represents port and its attributes
type DevlinkPort struct {
BusName string
DeviceName string
PortIndex uint32
PortType uint16
NetdeviceName string
NetdevIfIndex uint32
RdmaDeviceName string
PortFlavour uint16
Fn *DevlinkPortFn
}
type DevLinkPortAddAttrs struct {
Controller uint32
SfNumber uint32
PortIndex uint32
PfNumber uint16
SfNumberValid bool
PortIndexValid bool
ControllerValid bool
}
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
devices := make([]*DevlinkDevice, 0, len(msgs))
for _, m := range msgs {
@ -95,9 +133,9 @@ func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error
for _, a := range attrs {
switch a.Attr.Type {
case nl.DEVLINK_ATTR_BUS_NAME:
d.BusName = string(a.Value)
d.BusName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_DEV_NAME:
d.DeviceName = string(a.Value)
d.DeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_ESWITCH_MODE:
d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value))
case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE:
@ -126,12 +164,12 @@ func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK)
req.AddData(msg)
b := make([]byte, len(dev.BusName))
b := make([]byte, len(dev.BusName)+1)
copy(b, dev.BusName)
data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
req.AddData(data)
b = make([]byte, len(dev.DeviceName))
b = make([]byte, len(dev.DeviceName)+1)
copy(b, dev.DeviceName)
data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
req.AddData(data)
@ -270,3 +308,206 @@ func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error
func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error {
return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode)
}
func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
for _, a := range attrs {
switch a.Attr.Type {
case nl.DEVLINK_ATTR_BUS_NAME:
port.BusName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_DEV_NAME:
port.DeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_INDEX:
port.PortIndex = native.Uint32(a.Value)
case nl.DEVLINK_ATTR_PORT_TYPE:
port.PortType = native.Uint16(a.Value)
case nl.DEVLINK_ATTR_PORT_NETDEV_NAME:
port.NetdeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX:
port.NetdevIfIndex = native.Uint32(a.Value)
case nl.DEVLINK_ATTR_PORT_IBDEV_NAME:
port.RdmaDeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_FLAVOUR:
port.PortFlavour = native.Uint16(a.Value)
case nl.DEVLINK_ATTR_PORT_FUNCTION:
port.Fn = &DevlinkPortFn{}
for nested := range nl.ParseAttributes(a.Value) {
switch nested.Type {
case nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR:
port.Fn.HwAddr = nested.Value[:]
case nl.DEVLINK_PORT_FN_ATTR_STATE:
port.Fn.State = uint8(nested.Value[0])
case nl.DEVLINK_PORT_FN_ATTR_OPSTATE:
port.Fn.OpState = uint8(nested.Value[0])
}
}
}
}
return nil
}
func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) {
ports := make([]*DevlinkPort, 0, len(msgs))
for _, m := range msgs {
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
if err != nil {
return nil, err
}
port := &DevlinkPort{}
if err = port.parseAttributes(attrs); err != nil {
return nil, err
}
ports = append(ports, port)
}
return ports, nil
}
// DevLinkGetPortList provides a pointer to devlink ports and nil error,
// otherwise returns an error code.
func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) {
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
if err != nil {
return nil, err
}
msg := &nl.Genlmsg{
Command: nl.DEVLINK_CMD_PORT_GET,
Version: nl.GENL_DEVLINK_VERSION,
}
req := h.newNetlinkRequest(int(f.ID),
unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
ports, err := parseDevLinkAllPortList(msgs)
if err != nil {
return nil, err
}
return ports, nil
}
// DevLinkGetPortList provides a pointer to devlink ports and nil error,
// otherwise returns an error code.
func DevLinkGetAllPortList() ([]*DevlinkPort, error) {
return pkgHandle.DevLinkGetAllPortList()
}
func parseDevlinkPortMsg(msgs [][]byte) (*DevlinkPort, error) {
m := msgs[0]
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
if err != nil {
return nil, err
}
port := &DevlinkPort{}
if err = port.parseAttributes(attrs); err != nil {
return nil, err
}
return port, nil
}
// DevLinkGetPortByIndexprovides a pointer to devlink device and nil error,
// otherwise returns an error code.
func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_GET, Bus, Device)
if err != nil {
return nil, err
}
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
port, err := parseDevlinkPortMsg(respmsg)
return port, err
}
// DevLinkGetPortByIndex provides a pointer to devlink portand nil error,
// otherwise returns an error code.
func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex)
}
// DevLinkPortAdd adds a devlink port and returns a port on success
// otherwise returns nil port and an error code.
func (h *Handle) DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_NEW, Bus, Device)
if err != nil {
return nil, err
}
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(Flavour)))
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(Attrs.PfNumber)))
if Flavour == nl.DEVLINK_PORT_FLAVOUR_PCI_SF && Attrs.SfNumberValid {
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(Attrs.SfNumber)))
}
if Attrs.PortIndexValid {
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(Attrs.PortIndex)))
}
if Attrs.ControllerValid {
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(Attrs.Controller)))
}
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
port, err := parseDevlinkPortMsg(respmsg)
return port, err
}
// DevLinkPortAdd adds a devlink port and returns a port on success
// otherwise returns nil port and an error code.
func DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
return pkgHandle.DevLinkPortAdd(Bus, Device, Flavour, Attrs)
}
// DevLinkPortDel deletes a devlink port and returns success or error code.
func (h *Handle) DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_DEL, Bus, Device)
if err != nil {
return err
}
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
return err
}
// DevLinkPortDel deletes a devlink port and returns success or error code.
func DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
return pkgHandle.DevLinkPortDel(Bus, Device, PortIndex)
}
// DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
// It returns 0 on success or error code.
func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_SET, Bus, Device)
if err != nil {
return err
}
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
fnAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION|unix.NLA_F_NESTED, nil)
if FnAttrs.HwAddrValid {
fnAttr.AddRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(FnAttrs.FnAttrs.HwAddr))
}
if FnAttrs.StateValid {
fnAttr.AddRtAttr(nl.DEVLINK_PORT_FN_ATTR_STATE, nl.Uint8Attr(FnAttrs.FnAttrs.State))
}
req.AddData(fnAttr)
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
return err
}
// DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
// It returns 0 on success or error code.
func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs)
}

View File

@ -6,6 +6,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"net"
"syscall"
"github.com/vishvananda/netlink/nl"
@ -36,6 +37,7 @@ type U32 struct {
ClassId uint32
Divisor uint32 // Divisor MUST be power of 2.
Hash uint32
Link uint32
RedirIndex int
Sel *TcU32Sel
Actions []Action
@ -119,6 +121,131 @@ func (filter *Fw) Type() string {
return "fw"
}
type Flower struct {
FilterAttrs
DestIP net.IP
DestIPMask net.IPMask
SrcIP net.IP
SrcIPMask net.IPMask
EthType uint16
EncDestIP net.IP
EncDestIPMask net.IPMask
EncSrcIP net.IP
EncSrcIPMask net.IPMask
EncDestPort uint16
EncKeyId uint32
Actions []Action
}
func (filter *Flower) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
func (filter *Flower) Type() string {
return "flower"
}
func (filter *Flower) encodeIP(parent *nl.RtAttr, ip net.IP, mask net.IPMask, v4Type, v6Type int, v4MaskType, v6MaskType int) {
ipType := v4Type
maskType := v4MaskType
encodeMask := mask
if mask == nil {
encodeMask = net.CIDRMask(32, 32)
}
v4IP := ip.To4()
if v4IP == nil {
ipType = v6Type
maskType = v6MaskType
if mask == nil {
encodeMask = net.CIDRMask(128, 128)
}
} else {
ip = v4IP
}
parent.AddRtAttr(ipType, ip)
parent.AddRtAttr(maskType, encodeMask)
}
func (filter *Flower) encode(parent *nl.RtAttr) error {
if filter.EthType != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_TYPE, htons(filter.EthType))
}
if filter.SrcIP != nil {
filter.encodeIP(parent, filter.SrcIP, filter.SrcIPMask,
nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC,
nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK)
}
if filter.DestIP != nil {
filter.encodeIP(parent, filter.DestIP, filter.DestIPMask,
nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST,
nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK)
}
if filter.EncSrcIP != nil {
filter.encodeIP(parent, filter.EncSrcIP, filter.EncSrcIPMask,
nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC,
nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK)
}
if filter.EncDestIP != nil {
filter.encodeIP(parent, filter.EncDestIP, filter.EncSrcIPMask,
nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST,
nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK)
}
if filter.EncDestPort != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT, htons(filter.EncDestPort))
}
if filter.EncKeyId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_KEY_ID, htonl(filter.EncKeyId))
}
actionsAttr := parent.AddRtAttr(nl.TCA_FLOWER_ACT, nil)
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
return nil
}
func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error {
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_FLOWER_KEY_ETH_TYPE:
filter.EthType = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC:
filter.SrcIP = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK:
filter.SrcIPMask = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST:
filter.DestIP = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK:
filter.DestIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC:
filter.EncSrcIP = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK:
filter.EncSrcIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST:
filter.EncDestIP = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK:
filter.EncDestIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT:
filter.EncDestPort = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_ENC_KEY_ID:
filter.EncKeyId = ntohl(datum.Value)
case nl.TCA_FLOWER_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return err
}
filter.Actions, err = parseActions(tables)
if err != nil {
return err
}
}
}
return nil
}
// FilterDel will delete a filter from the system.
// Equivalent to: `tc filter del $filter`
func FilterDel(filter Filter) error {
@ -168,7 +295,6 @@ func (h *Handle) FilterReplace(filter Filter) error {
}
func (h *Handle) filterModify(filter Filter, flags int) error {
native = nl.NativeEndian()
req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK)
base := filter.Attrs()
msg := &nl.TcMsg{
@ -225,6 +351,9 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if filter.Hash != 0 {
options.AddRtAttr(nl.TCA_U32_HASH, nl.Uint32Attr(filter.Hash))
}
if filter.Link != 0 {
options.AddRtAttr(nl.TCA_U32_LINK, nl.Uint32Attr(filter.Link))
}
actionsAttr := options.AddRtAttr(nl.TCA_U32_ACT, nil)
// backwards compatibility
if filter.RedirIndex != 0 {
@ -283,6 +412,10 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId))
}
case *Flower:
if err := filter.encode(options); err != nil {
return err
}
}
req.AddData(options)
@ -351,6 +484,8 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
filter = &BpfFilter{}
case "matchall":
filter = &MatchAll{}
case "flower":
filter = &Flower{}
default:
filter = &GenericFilter{FilterType: filterType}
}
@ -380,6 +515,11 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
if err != nil {
return nil, err
}
case "flower":
detailed, err = parseFlowerData(filter, data)
if err != nil {
return nil, err
}
default:
detailed = true
}
@ -628,7 +768,6 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
}
func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
u32 := filter.(*U32)
detailed := false
for _, datum := range data {
@ -666,13 +805,14 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
u32.Divisor = native.Uint32(datum.Value)
case nl.TCA_U32_HASH:
u32.Hash = native.Uint32(datum.Value)
case nl.TCA_U32_LINK:
u32.Link = native.Uint32(datum.Value)
}
}
return detailed, nil
}
func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
fw := filter.(*Fw)
detailed := true
for _, datum := range data {
@ -701,7 +841,6 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
}
func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
bpf := filter.(*BpfFilter)
detailed := true
for _, datum := range data {
@ -727,7 +866,6 @@ func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
}
func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
matchall := filter.(*MatchAll)
detailed := true
for _, datum := range data {
@ -748,6 +886,10 @@ func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, er
return detailed, nil
}
func parseFlowerData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
return true, filter.(*Flower).decode(data)
}
func AlignToAtm(size uint) uint {
var linksize, cells int
cells = int(size / nl.ATM_CELL_PAYLOAD)
@ -795,14 +937,12 @@ func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, lin
func DeserializeRtab(b []byte) [256]uint32 {
var rtab [256]uint32
native := nl.NativeEndian()
r := bytes.NewReader(b)
_ = binary.Read(r, native, &rtab)
return rtab
}
func SerializeRtab(rtab [256]uint32) []byte {
native := nl.NativeEndian()
var w bytes.Buffer
_ = binary.Write(&w, native, rtab)
return w.Bytes()

View File

@ -15,12 +15,28 @@ var pkgHandle = &Handle{}
// Handle is an handle for the netlink requests on a
// specific network namespace. All the requests on the
// same netlink family share the same netlink socket,
// which gets released when the handle is deleted.
// which gets released when the handle is Close'd.
type Handle struct {
sockets map[int]*nl.SocketHandle
lookupByDump bool
}
// SetSocketTimeout configures timeout for default netlink sockets
func SetSocketTimeout(to time.Duration) error {
if to < time.Microsecond {
return fmt.Errorf("invalid timeout, minimul value is %s", time.Microsecond)
}
nl.SocketTimeoutTv = unix.NsecToTimeval(to.Nanoseconds())
return nil
}
// GetSocketTimeout returns the timeout value used by default netlink sockets
func GetSocketTimeout() time.Duration {
nsec := unix.TimevalToNsec(nl.SocketTimeoutTv)
return time.Duration(nsec) * time.Nanosecond
}
// SupportsNetlinkFamily reports whether the passed netlink family is supported by this Handle
func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool {
_, ok := h.sockets[nlFamily]
@ -120,14 +136,22 @@ func newHandle(newNs, curNs netns.NsHandle, nlFamilies ...int) (*Handle, error)
return h, nil
}
// Delete releases the resources allocated to this handle
func (h *Handle) Delete() {
// Close releases the resources allocated to this handle
func (h *Handle) Close() {
for _, sh := range h.sockets {
sh.Close()
}
h.sockets = nil
}
// Delete releases the resources allocated to this handle
//
// Deprecated: use Close instead which is in line with typical resource release
// patterns for files and other resources.
func (h *Handle) Delete() {
h.Close()
}
func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest {
// Do this so that package API still use nl package variable nextSeqNr
if h.sockets == nil {

View File

@ -23,6 +23,8 @@ func NewHandleAtFrom(newNs, curNs netns.NsHandle) (*Handle, error) {
return nil, ErrNotImplemented
}
func (h *Handle) Close() {}
func (h *Handle) Delete() {}
func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool {
@ -237,6 +239,10 @@ func (h *Handle) RouteAdd(route *Route) error {
return ErrNotImplemented
}
func (h *Handle) RouteAppend(route *Route) error {
return ErrNotImplemented
}
func (h *Handle) RouteDel(route *Route) error {
return ErrNotImplemented
}

View File

@ -27,4 +27,5 @@ const (
type InetDiagTCPInfoResp struct {
InetDiagMsg *Socket
TCPInfo *TCPInfo
TCPBBRInfo *TCPBBRInfo
}

View File

@ -1,6 +1,7 @@
package netlink
import (
"encoding/binary"
"log"
"net"
"syscall"
@ -11,25 +12,40 @@ import (
// IPSetEntry is used for adding, updating, retreiving and deleting entries
type IPSetEntry struct {
Comment string
MAC net.HardwareAddr
IP net.IP
Timeout *uint32
Packets *uint64
Bytes *uint64
Comment string
MAC net.HardwareAddr
IP net.IP
CIDR uint8
Timeout *uint32
Packets *uint64
Bytes *uint64
Protocol *uint8
Port *uint16
IP2 net.IP
CIDR2 uint8
IFace string
Mark *uint32
Replace bool // replace existing entry
}
// IPSetResult is the result of a dump request for a set
type IPSetResult struct {
Nfgenmsg *nl.Nfgenmsg
Protocol uint8
Revision uint8
Family uint8
Flags uint8
SetName string
TypeName string
Nfgenmsg *nl.Nfgenmsg
Protocol uint8
ProtocolMinVersion uint8
Revision uint8
Family uint8
Flags uint8
SetName string
TypeName string
Comment string
MarkMask uint32
IPFrom net.IP
IPTo net.IP
PortFrom uint16
PortTo uint16
HashSize uint32
NumEntries uint32
@ -38,6 +54,7 @@ type IPSetResult struct {
SizeInMemory uint32
CadtFlags uint32
Timeout *uint32
LineNo uint32
Entries []IPSetEntry
}
@ -49,10 +66,16 @@ type IpsetCreateOptions struct {
Counters bool
Comments bool
Skbinfo bool
Revision uint8
IPFrom net.IP
IPTo net.IP
PortFrom uint16
PortTo uint16
}
// IpsetProtocol returns the ipset protocol version from the kernel
func IpsetProtocol() (uint8, error) {
func IpsetProtocol() (uint8, uint8, error) {
return pkgHandle.IpsetProtocol()
}
@ -83,23 +106,23 @@ func IpsetListAll() ([]IPSetResult, error) {
// IpsetAdd adds an entry to an existing ipset.
func IpsetAdd(setname string, entry *IPSetEntry) error {
return pkgHandle.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry)
return pkgHandle.IpsetAdd(setname, entry)
}
// IpsetDele deletes an entry from an existing ipset.
// IpsetDel deletes an entry from an existing ipset.
func IpsetDel(setname string, entry *IPSetEntry) error {
return pkgHandle.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry)
return pkgHandle.IpsetDel(setname, entry)
}
func (h *Handle) IpsetProtocol() (uint8, error) {
func (h *Handle) IpsetProtocol() (protocol uint8, minVersion uint8, err error) {
req := h.newIpsetRequest(nl.IPSET_CMD_PROTOCOL)
msgs, err := req.Execute(unix.NETLINK_NETFILTER, 0)
if err != nil {
return 0, err
return 0, 0, err
}
return ipsetUnserialize(msgs).Protocol, nil
response := ipsetUnserialize(msgs)
return response.Protocol, response.ProtocolMinVersion, nil
}
func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOptions) error {
@ -111,11 +134,30 @@ 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_TYPENAME, nl.ZeroTerminated(typename)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(0)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(0)))
revision := options.Revision
if revision == 0 {
revision = getIpsetDefaultWithTypeName(typename)
}
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision)))
data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
var family uint8
switch typename {
case "hash:mac":
case "bitmap:port":
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf, options.PortFrom)
binary.BigEndian.PutUint16(buf[2:], options.PortTo)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_FROM|int(nl.NLA_F_NET_BYTEORDER), buf[:2]))
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_TO|int(nl.NLA_F_NET_BYTEORDER), buf[2:]))
default:
family = unix.AF_INET
}
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(family)))
if timeout := options.Timeout; timeout != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout})
}
@ -184,9 +226,24 @@ func (h *Handle) IpsetListAll() ([]IPSetResult, error) {
return result, nil
}
// IpsetAdd adds an entry to an existing ipset.
func (h *Handle) IpsetAdd(setname string, entry *IPSetEntry) error {
return h.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry)
}
// IpsetDel deletes an entry from an existing ipset.
func (h *Handle) IpsetDel(setname string, entry *IPSetEntry) error {
return h.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry)
}
func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error {
req := h.newIpsetRequest(nlCmd)
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
if entry.Comment != "" {
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_COMMENT, nl.ZeroTerminated(entry.Comment)))
}
data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
if !entry.Replace {
@ -196,10 +253,49 @@ func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error
if entry.Timeout != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout})
}
if entry.IP != nil {
nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NESTED), nestedData.Serialize()))
}
if entry.MAC != nil {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER, entry.MAC))
}
if entry.CIDR != 0 {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR, nl.Uint8Attr(entry.CIDR)))
}
if entry.IP2 != nil {
nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP2)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP2|int(nl.NLA_F_NESTED), nestedData.Serialize()))
}
if entry.CIDR2 != 0 {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR2, nl.Uint8Attr(entry.CIDR2)))
}
if entry.Port != nil {
if entry.Protocol == nil {
// use tcp protocol as default
val := uint8(unix.IPPROTO_TCP)
entry.Protocol = &val
}
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PROTO, nl.Uint8Attr(*entry.Protocol)))
buf := make([]byte, 2)
binary.BigEndian.PutUint16(buf, *entry.Port)
data.AddChild(nl.NewRtAttr(int(nl.IPSET_ATTR_PORT|nl.NLA_F_NET_BYTEORDER), buf))
}
if entry.IFace != "" {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IFACE, nl.ZeroTerminated(entry.IFace)))
}
if entry.Mark != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER, Value: *entry.Mark})
}
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0})
req.AddData(data)
@ -222,6 +318,17 @@ func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
return req
}
func getIpsetDefaultWithTypeName(typename string) uint8 {
switch typename {
case "hash:ip,port",
"hash:ip,port,ip",
"hash:ip,port,net",
"hash:net,port":
return 1
}
return 0
}
func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) {
msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0)
@ -249,6 +356,8 @@ func (result *IPSetResult) unserialize(msg []byte) {
result.Protocol = attr.Value[0]
case nl.IPSET_ATTR_SETNAME:
result.SetName = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_COMMENT:
result.Comment = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_TYPENAME:
result.TypeName = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_REVISION:
@ -261,6 +370,10 @@ func (result *IPSetResult) unserialize(msg []byte) {
result.parseAttrData(attr.Value)
case nl.IPSET_ATTR_ADT | nl.NLA_F_NESTED:
result.parseAttrADT(attr.Value)
case nl.IPSET_ATTR_PROTOCOL_MIN:
result.ProtocolMinVersion = attr.Value[0]
case nl.IPSET_ATTR_MARKMASK:
result.MarkMask = attr.Uint32()
default:
log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
}
@ -285,6 +398,36 @@ func (result *IPSetResult) parseAttrData(data []byte) {
result.SizeInMemory = attr.Uint32()
case nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER:
result.CadtFlags = attr.Uint32()
case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
for nested := range nl.ParseAttributes(attr.Value) {
switch nested.Type {
case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER:
result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value})
case nl.IPSET_ATTR_IP:
result.IPFrom = nested.Value
default:
log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
}
}
case nl.IPSET_ATTR_IP_TO | nl.NLA_F_NESTED:
for nested := range nl.ParseAttributes(attr.Value) {
switch nested.Type {
case nl.IPSET_ATTR_IP:
result.IPTo = nested.Value
default:
log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
}
}
case nl.IPSET_ATTR_PORT_FROM | nl.NLA_F_NET_BYTEORDER:
result.PortFrom = networkOrder.Uint16(attr.Value)
case nl.IPSET_ATTR_PORT_TO | nl.NLA_F_NET_BYTEORDER:
result.PortTo = networkOrder.Uint16(attr.Value)
case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER:
result.LineNo = attr.Uint32()
case nl.IPSET_ATTR_COMMENT:
result.Comment = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_MARKMASK:
result.MarkMask = attr.Uint32()
default:
log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
}
@ -316,6 +459,8 @@ func parseIPSetEntry(data []byte) (entry IPSetEntry) {
entry.Packets = &val
case nl.IPSET_ATTR_ETHER:
entry.MAC = net.HardwareAddr(attr.Value)
case nl.IPSET_ATTR_IP:
entry.IP = net.IP(attr.Value)
case nl.IPSET_ATTR_COMMENT:
entry.Comment = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
@ -327,6 +472,30 @@ func parseIPSetEntry(data []byte) (entry IPSetEntry) {
log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
}
}
case nl.IPSET_ATTR_IP2 | nl.NLA_F_NESTED:
for attr := range nl.ParseAttributes(attr.Value) {
switch attr.Type {
case nl.IPSET_ATTR_IP:
entry.IP2 = net.IP(attr.Value)
default:
log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
}
}
case nl.IPSET_ATTR_CIDR:
entry.CIDR = attr.Value[0]
case nl.IPSET_ATTR_CIDR2:
entry.CIDR2 = attr.Value[0]
case nl.IPSET_ATTR_PORT | nl.NLA_F_NET_BYTEORDER:
val := networkOrder.Uint16(attr.Value)
entry.Port = &val
case nl.IPSET_ATTR_PROTO:
val := attr.Value[0]
entry.Protocol = &val
case nl.IPSET_ATTR_IFACE:
entry.IFace = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER:
val := attr.Uint32()
entry.Mark = &val
default:
log.Printf("unknown ADT attribute from kernel: %+v", attr)
}

View File

@ -35,10 +35,13 @@ type LinkAttrs struct {
Alias string
Statistics *LinkStatistics
Promisc int
Allmulti int
Multi int
Xdp *LinkXdp
EncapType string
Protinfo *Protinfo
OperState LinkOperState
PhysSwitchID int
NetNsID int
NumTxQueues int
NumRxQueues int
@ -456,6 +459,19 @@ func (ipvlan *IPVlan) Type() string {
return "ipvlan"
}
// IPVtap - IPVtap is a virtual interfaces based on ipvlan
type IPVtap struct {
IPVlan
}
func (ipvtap *IPVtap) Attrs() *LinkAttrs {
return &ipvtap.LinkAttrs
}
func (ipvtap IPVtap) Type() string {
return "ipvtap"
}
// VlanProtocol type
type VlanProtocol int
@ -555,6 +571,27 @@ const (
BOND_ARP_VALIDATE_ALL
)
var bondArpValidateToString = map[BondArpValidate]string{
BOND_ARP_VALIDATE_NONE: "none",
BOND_ARP_VALIDATE_ACTIVE: "active",
BOND_ARP_VALIDATE_BACKUP: "backup",
BOND_ARP_VALIDATE_ALL: "none",
}
var StringToBondArpValidateMap = map[string]BondArpValidate{
"none": BOND_ARP_VALIDATE_NONE,
"active": BOND_ARP_VALIDATE_ACTIVE,
"backup": BOND_ARP_VALIDATE_BACKUP,
"all": BOND_ARP_VALIDATE_ALL,
}
func (b BondArpValidate) String() string {
s, ok := bondArpValidateToString[b]
if !ok {
return fmt.Sprintf("BondArpValidate(%d)", b)
}
return s
}
// BondPrimaryReselect type
type BondPrimaryReselect int
@ -565,6 +602,25 @@ const (
BOND_PRIMARY_RESELECT_FAILURE
)
var bondPrimaryReselectToString = map[BondPrimaryReselect]string{
BOND_PRIMARY_RESELECT_ALWAYS: "always",
BOND_PRIMARY_RESELECT_BETTER: "better",
BOND_PRIMARY_RESELECT_FAILURE: "failure",
}
var StringToBondPrimaryReselectMap = map[string]BondPrimaryReselect{
"always": BOND_PRIMARY_RESELECT_ALWAYS,
"better": BOND_PRIMARY_RESELECT_BETTER,
"failure": BOND_PRIMARY_RESELECT_FAILURE,
}
func (b BondPrimaryReselect) String() string {
s, ok := bondPrimaryReselectToString[b]
if !ok {
return fmt.Sprintf("BondPrimaryReselect(%d)", b)
}
return s
}
// BondArpAllTargets type
type BondArpAllTargets int
@ -574,6 +630,23 @@ const (
BOND_ARP_ALL_TARGETS_ALL
)
var bondArpAllTargetsToString = map[BondArpAllTargets]string{
BOND_ARP_ALL_TARGETS_ANY: "any",
BOND_ARP_ALL_TARGETS_ALL: "all",
}
var StringToBondArpAllTargetsMap = map[string]BondArpAllTargets{
"any": BOND_ARP_ALL_TARGETS_ANY,
"all": BOND_ARP_ALL_TARGETS_ALL,
}
func (b BondArpAllTargets) String() string {
s, ok := bondArpAllTargetsToString[b]
if !ok {
return fmt.Sprintf("BondArpAllTargets(%d)", b)
}
return s
}
// BondFailOverMac type
type BondFailOverMac int
@ -584,6 +657,25 @@ const (
BOND_FAIL_OVER_MAC_FOLLOW
)
var bondFailOverMacToString = map[BondFailOverMac]string{
BOND_FAIL_OVER_MAC_NONE: "none",
BOND_FAIL_OVER_MAC_ACTIVE: "active",
BOND_FAIL_OVER_MAC_FOLLOW: "follow",
}
var StringToBondFailOverMacMap = map[string]BondFailOverMac{
"none": BOND_FAIL_OVER_MAC_NONE,
"active": BOND_FAIL_OVER_MAC_ACTIVE,
"follow": BOND_FAIL_OVER_MAC_FOLLOW,
}
func (b BondFailOverMac) String() string {
s, ok := bondFailOverMacToString[b]
if !ok {
return fmt.Sprintf("BondFailOverMac(%d)", b)
}
return s
}
// BondXmitHashPolicy type
type BondXmitHashPolicy int
@ -675,6 +767,25 @@ const (
BOND_AD_SELECT_COUNT
)
var bondAdSelectToString = map[BondAdSelect]string{
BOND_AD_SELECT_STABLE: "stable",
BOND_AD_SELECT_BANDWIDTH: "bandwidth",
BOND_AD_SELECT_COUNT: "count",
}
var StringToBondAdSelectMap = map[string]BondAdSelect{
"stable": BOND_AD_SELECT_STABLE,
"bandwidth": BOND_AD_SELECT_BANDWIDTH,
"count": BOND_AD_SELECT_COUNT,
}
func (b BondAdSelect) String() string {
s, ok := bondAdSelectToString[b]
if !ok {
return fmt.Sprintf("BondAdSelect(%d)", b)
}
return s
}
// BondAdInfo represents ad info for bond
type BondAdInfo struct {
AggregatorId int
@ -706,7 +817,7 @@ type Bond struct {
AllSlavesActive int
MinLinks int
LpInterval int
PackersPerSlave int
PacketsPerSlave int
LacpRate BondLacpRate
AdSelect BondAdSelect
// looking at iproute tool AdInfo can only be retrived. It can't be set.
@ -739,7 +850,7 @@ func NewLinkBond(atr LinkAttrs) *Bond {
AllSlavesActive: -1,
MinLinks: -1,
LpInterval: -1,
PackersPerSlave: -1,
PacketsPerSlave: -1,
LacpRate: -1,
AdSelect: -1,
AdActorSysPrio: -1,
@ -789,8 +900,10 @@ func (bond *Bond) Type() string {
type BondSlaveState uint8
const (
BondStateActive = iota // Link is active.
BondStateBackup // Link is backup.
//BondStateActive Link is active.
BondStateActive BondSlaveState = iota
//BondStateBackup Link is backup.
BondStateBackup
)
func (s BondSlaveState) String() string {
@ -804,15 +917,19 @@ func (s BondSlaveState) String() string {
}
}
// BondSlaveState represents the values of the IFLA_BOND_SLAVE_MII_STATUS bond slave
// BondSlaveMiiStatus represents the values of the IFLA_BOND_SLAVE_MII_STATUS bond slave
// attribute, which contains the status of MII link monitoring
type BondSlaveMiiStatus uint8
const (
BondLinkUp = iota // link is up and running.
BondLinkFail // link has just gone down.
BondLinkDown // link has been down for too long time.
BondLinkBack // link is going back.
//BondLinkUp link is up and running.
BondLinkUp BondSlaveMiiStatus = iota
//BondLinkFail link has just gone down.
BondLinkFail
//BondLinkDown link has been down for too long time.
BondLinkDown
//BondLinkBack link is going back.
BondLinkBack
)
func (s BondSlaveMiiStatus) String() string {
@ -845,6 +962,38 @@ func (b *BondSlave) SlaveType() string {
return "bond"
}
type VrfSlave struct {
Table uint32
}
func (v *VrfSlave) SlaveType() string {
return "vrf"
}
// Geneve devices must specify RemoteIP and ID (VNI) on create
// https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/drivers/net/geneve.c#L1209-L1223
type Geneve struct {
LinkAttrs
ID uint32 // vni
Remote net.IP
Ttl uint8
Tos uint8
Dport uint16
UdpCsum uint8
UdpZeroCsum6Tx uint8
UdpZeroCsum6Rx uint8
Link uint32
FlowBased bool
}
func (geneve *Geneve) Attrs() *LinkAttrs {
return &geneve.LinkAttrs
}
func (geneve *Geneve) Type() string {
return "geneve"
}
// Gretap devices must specify LocalIP and RemoteIP on create
type Gretap struct {
LinkAttrs
@ -1068,6 +1217,58 @@ var StringToIPoIBMode = map[string]IPoIBMode{
"connected": IPOIB_MODE_CONNECTED,
}
const (
CAN_STATE_ERROR_ACTIVE = iota
CAN_STATE_ERROR_WARNING
CAN_STATE_ERROR_PASSIVE
CAN_STATE_BUS_OFF
CAN_STATE_STOPPED
CAN_STATE_SLEEPING
)
type Can struct {
LinkAttrs
BitRate uint32
SamplePoint uint32
TimeQuanta uint32
PropagationSegment uint32
PhaseSegment1 uint32
PhaseSegment2 uint32
SyncJumpWidth uint32
BitRatePreScaler uint32
Name string
TimeSegment1Min uint32
TimeSegment1Max uint32
TimeSegment2Min uint32
TimeSegment2Max uint32
SyncJumpWidthMax uint32
BitRatePreScalerMin uint32
BitRatePreScalerMax uint32
BitRatePreScalerInc uint32
ClockFrequency uint32
State uint32
Mask uint32
Flags uint32
TxError uint16
RxError uint16
RestartMs uint32
}
func (can *Can) Attrs() *LinkAttrs {
return &can.LinkAttrs
}
func (can *Can) Type() string {
return "can"
}
type IPoIB struct {
LinkAttrs
Pkey uint16
@ -1083,11 +1284,27 @@ func (ipoib *IPoIB) Type() string {
return "ipoib"
}
type BareUDP struct {
LinkAttrs
Port uint16
EtherType uint16
SrcPortMin uint16
MultiProto bool
}
func (bareudp *BareUDP) Attrs() *LinkAttrs {
return &bareudp.LinkAttrs
}
func (bareudp *BareUDP) Type() string {
return "bareudp"
}
// iproute2 supported devices;
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
// gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon |
// bond_slave | ipvlan | xfrm
// bond_slave | ipvlan | xfrm | bareudp
// LinkNotFoundError wraps the various not found errors when
// getting/reading links. This is intended for better error

View File

@ -34,14 +34,27 @@ const (
TUNTAP_MULTI_QUEUE_DEFAULTS TuntapFlag = TUNTAP_MULTI_QUEUE | TUNTAP_NO_PI
)
var StringToTuntapModeMap = map[string]TuntapMode{
"tun": TUNTAP_MODE_TUN,
"tap": TUNTAP_MODE_TAP,
}
func (ttm TuntapMode) String() string {
switch ttm {
case TUNTAP_MODE_TUN:
return "tun"
case TUNTAP_MODE_TAP:
return "tap"
}
return "unknown"
}
const (
VF_LINK_STATE_AUTO uint32 = 0
VF_LINK_STATE_ENABLE uint32 = 1
VF_LINK_STATE_DISABLE uint32 = 2
)
var lookupByDump = false
var macvlanModes = [...]uint32{
0,
nl.MACVLAN_MODE_PRIVATE,
@ -138,7 +151,6 @@ func (h *Handle) LinkSetAllmulticastOn(link Link) error {
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Change = unix.IFF_ALLMULTI
msg.Flags = unix.IFF_ALLMULTI
msg.Index = int32(base.Index)
req.AddData(msg)
@ -168,6 +180,51 @@ func (h *Handle) LinkSetAllmulticastOff(link Link) error {
return err
}
// LinkSetMulticastOn enables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast on`
func LinkSetMulticastOn(link Link) error {
return pkgHandle.LinkSetMulticastOn(link)
}
// LinkSetMulticastOn enables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast on`
func (h *Handle) LinkSetMulticastOn(link Link) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK)
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Change = unix.IFF_MULTICAST
msg.Flags = unix.IFF_MULTICAST
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
// LinkSetAllmulticastOff disables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast off`
func LinkSetMulticastOff(link Link) error {
return pkgHandle.LinkSetMulticastOff(link)
}
// LinkSetAllmulticastOff disables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast off`
func (h *Handle) LinkSetMulticastOff(link Link) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK)
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Change = unix.IFF_MULTICAST
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
func MacvlanMACAddrAdd(link Link, addr net.HardwareAddr) error {
return pkgHandle.MacvlanMACAddrAdd(link, addr)
}
@ -532,13 +589,13 @@ func (h *Handle) LinkSetVfVlanQos(link Link, vf, vlan, qos int) error {
req.AddData(msg)
data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil)
info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil)
info := data.AddRtAttr(nl.IFLA_VF_INFO, nil)
vfmsg := nl.VfVlan{
Vf: uint32(vf),
Vlan: uint32(vlan),
Qos: uint32(qos),
}
nl.NewRtAttrChild(info, nl.IFLA_VF_VLAN, vfmsg.Serialize())
info.AddRtAttr(nl.IFLA_VF_VLAN, vfmsg.Serialize())
req.AddData(data)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
@ -1046,8 +1103,8 @@ func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) {
if bond.LpInterval >= 0 {
data.AddRtAttr(nl.IFLA_BOND_LP_INTERVAL, nl.Uint32Attr(uint32(bond.LpInterval)))
}
if bond.PackersPerSlave >= 0 {
data.AddRtAttr(nl.IFLA_BOND_PACKETS_PER_SLAVE, nl.Uint32Attr(uint32(bond.PackersPerSlave)))
if bond.PacketsPerSlave >= 0 {
data.AddRtAttr(nl.IFLA_BOND_PACKETS_PER_SLAVE, nl.Uint32Attr(uint32(bond.PacketsPerSlave)))
}
if bond.LacpRate >= 0 {
data.AddRtAttr(nl.IFLA_BOND_AD_LACP_RATE, nl.Uint8Attr(uint8(bond.LacpRate)))
@ -1203,9 +1260,26 @@ func (h *Handle) linkModify(link Link, flags int) error {
}
control := func(file *os.File, f func(fd uintptr)) error {
name := file.Name()
conn, err := file.SyscallConn()
if err != nil {
return fmt.Errorf("SyscallConn() failed on %s: %v", name, err)
}
if err := conn.Control(f); err != nil {
return fmt.Errorf("Failed to get file descriptor for %s: %v", name, err)
}
return nil
}
// only persist interface if NonPersist is NOT set
if !tuntap.NonPersist {
_, _, errno := unix.Syscall(unix.SYS_IOCTL, fds[0].Fd(), uintptr(unix.TUNSETPERSIST), 1)
var errno syscall.Errno
if err := control(fds[0], func(fd uintptr) {
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(unix.TUNSETPERSIST), 1)
}); err != nil {
return err
}
if errno != 0 {
cleanupFds(fds)
return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno)
@ -1222,7 +1296,10 @@ func (h *Handle) linkModify(link Link, flags int) error {
// un-persist (e.g. allow the interface to be removed) the tuntap
// should not hurt if not set prior, condition might be not needed
if !tuntap.NonPersist {
_, _, _ = unix.Syscall(unix.SYS_IOCTL, fds[0].Fd(), uintptr(unix.TUNSETPERSIST), 0)
// ignore error
_ = control(fds[0], func(fd uintptr) {
_, _, _ = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(unix.TUNSETPERSIST), 0)
})
}
cleanupFds(fds)
return err
@ -1394,6 +1471,10 @@ func (h *Handle) linkModify(link Link, flags int) error {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode)))
data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag)))
case *IPVtap:
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode)))
data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag)))
case *Macvlan:
if link.Mode != MACVLAN_MODE_DEFAULT {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
@ -1404,6 +1485,8 @@ func (h *Handle) linkModify(link Link, flags int) error {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[link.Mode]))
}
case *Geneve:
addGeneveAttrs(link, linkInfo)
case *Gretap:
addGretapAttrs(link, linkInfo)
case *Iptun:
@ -1426,6 +1509,8 @@ func (h *Handle) linkModify(link Link, flags int) error {
addXfrmiAttrs(link, linkInfo)
case *IPoIB:
addIPoIBAttrs(link, linkInfo)
case *BareUDP:
addBareUDPAttrs(link, linkInfo)
}
req.AddData(linkInfo)
@ -1607,7 +1692,7 @@ func execGetLink(req *nl.NetlinkRequest) (Link, error) {
}
}
// linkDeserialize deserializes a raw message received from netlink into
// LinkDeserialize deserializes a raw message received from netlink into
// a link object.
func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
msg := nl.DeserializeIfInfomsg(m)
@ -1625,6 +1710,13 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
if msg.Flags&unix.IFF_PROMISC != 0 {
base.Promisc = 1
}
if msg.Flags&unix.IFF_ALLMULTI != 0 {
base.Allmulti = 1
}
if msg.Flags&unix.IFF_MULTICAST != 0 {
base.Multi = 1
}
var (
link Link
stats32 *LinkStatistics32
@ -1663,10 +1755,14 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &Bond{}
case "ipvlan":
link = &IPVlan{}
case "ipvtap":
link = &IPVtap{}
case "macvlan":
link = &Macvlan{}
case "macvtap":
link = &Macvtap{}
case "geneve":
link = &Geneve{}
case "gretap":
link = &Gretap{}
case "ip6gretap":
@ -1693,6 +1789,10 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &Tuntap{}
case "ipoib":
link = &IPoIB{}
case "can":
link = &Can{}
case "bareudp":
link = &BareUDP{}
default:
link = &GenericLink{LinkType: linkType}
}
@ -1710,10 +1810,14 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
parseBondData(link, data)
case "ipvlan":
parseIPVlanData(link, data)
case "ipvtap":
parseIPVtapData(link, data)
case "macvlan":
parseMacvlanData(link, data)
case "macvtap":
parseMacvtapData(link, data)
case "geneve":
parseGeneveData(link, data)
case "gretap":
parseGretapData(link, data)
case "ip6gretap":
@ -1742,13 +1846,21 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
parseTuntapData(link, data)
case "ipoib":
parseIPoIBData(link, data)
case "can":
parseCanData(link, data)
case "bareudp":
parseBareUDPData(link, data)
}
case nl.IFLA_INFO_SLAVE_KIND:
slaveType = string(info.Value[:len(info.Value)-1])
switch slaveType {
case "bond":
linkSlave = &BondSlave{}
case "vrf":
linkSlave = &VrfSlave{}
}
case nl.IFLA_INFO_SLAVE_DATA:
switch slaveType {
case "bond":
@ -1757,6 +1869,12 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
return nil, err
}
parseBondSlaveData(linkSlave, data)
case "vrf":
data, err := nl.ParseRouteAttr(info.Value)
if err != nil {
return nil, err
}
parseVrfSlaveData(linkSlave, data)
}
}
}
@ -1810,6 +1928,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
}
case unix.IFLA_OPERSTATE:
base.OperState = LinkOperState(uint8(attr.Value[0]))
case unix.IFLA_PHYS_SWITCH_ID:
base.PhysSwitchID = int(native.Uint32(attr.Value[0:4]))
case unix.IFLA_LINK_NETNSID:
base.NetNsID = int(native.Uint32(attr.Value[0:4]))
case unix.IFLA_GSO_MAX_SIZE:
@ -1998,7 +2118,8 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
msgs, from, err := s.Receive()
if err != nil {
if cberr != nil {
cberr(err)
cberr(fmt.Errorf("Receive failed: %v",
err))
}
return
}
@ -2013,15 +2134,15 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
cberr(fmt.Errorf("error message: %v",
syscall.Errno(-error)))
}
return
continue
}
ifmsg := nl.DeserializeIfInfomsg(m.Data)
header := unix.NlMsghdr(m.Header)
@ -2030,7 +2151,7 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
if cberr != nil {
cberr(err)
}
return
continue
}
ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: header, Link: link}
}
@ -2299,7 +2420,7 @@ func parseBondData(link Link, data []syscall.NetlinkRouteAttr) {
case nl.IFLA_BOND_LP_INTERVAL:
bond.LpInterval = int(native.Uint32(data[i].Value[0:4]))
case nl.IFLA_BOND_PACKETS_PER_SLAVE:
bond.PackersPerSlave = int(native.Uint32(data[i].Value[0:4]))
bond.PacketsPerSlave = int(native.Uint32(data[i].Value[0:4]))
case nl.IFLA_BOND_AD_LACP_RATE:
bond.LacpRate = BondLacpRate(data[i].Value[0])
case nl.IFLA_BOND_AD_SELECT:
@ -2379,6 +2500,16 @@ func parseBondSlaveData(slave LinkSlave, data []syscall.NetlinkRouteAttr) {
}
}
func parseVrfSlaveData(slave LinkSlave, data []syscall.NetlinkRouteAttr) {
vrfSlave := slave.(*VrfSlave)
for i := range data {
switch data[i].Attr.Type {
case nl.IFLA_BOND_SLAVE_STATE:
vrfSlave.Table = native.Uint32(data[i].Value[0:4])
}
}
}
func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
ipv := link.(*IPVlan)
for _, datum := range data {
@ -2391,6 +2522,18 @@ func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
}
}
func parseIPVtapData(link Link, data []syscall.NetlinkRouteAttr) {
ipv := link.(*IPVtap)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_IPVLAN_MODE:
ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4]))
case nl.IFLA_IPVLAN_FLAG:
ipv.Flag = IPVlanFlag(native.Uint32(datum.Value[0:4]))
}
}
}
func parseMacvtapData(link Link, data []syscall.NetlinkRouteAttr) {
macv := link.(*Macvtap)
parseMacvlanData(&macv.Macvlan, data)
@ -2448,6 +2591,58 @@ func linkFlags(rawFlags uint32) net.Flags {
return f
}
func addGeneveAttrs(geneve *Geneve, linkInfo *nl.RtAttr) {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
if geneve.FlowBased {
// In flow based mode, no other attributes need to be configured
linkInfo.AddRtAttr(nl.IFLA_GENEVE_COLLECT_METADATA, boolAttr(geneve.FlowBased))
return
}
if ip := geneve.Remote; ip != nil {
if ip4 := ip.To4(); ip4 != nil {
data.AddRtAttr(nl.IFLA_GENEVE_REMOTE, ip.To4())
} else {
data.AddRtAttr(nl.IFLA_GENEVE_REMOTE6, []byte(ip))
}
}
if geneve.ID != 0 {
data.AddRtAttr(nl.IFLA_GENEVE_ID, nl.Uint32Attr(geneve.ID))
}
if geneve.Dport != 0 {
data.AddRtAttr(nl.IFLA_GENEVE_PORT, htons(geneve.Dport))
}
if geneve.Ttl != 0 {
data.AddRtAttr(nl.IFLA_GENEVE_TTL, nl.Uint8Attr(geneve.Ttl))
}
if geneve.Tos != 0 {
data.AddRtAttr(nl.IFLA_GENEVE_TOS, nl.Uint8Attr(geneve.Tos))
}
}
func parseGeneveData(link Link, data []syscall.NetlinkRouteAttr) {
geneve := link.(*Geneve)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_GENEVE_ID:
geneve.ID = native.Uint32(datum.Value[0:4])
case nl.IFLA_GENEVE_REMOTE, nl.IFLA_GENEVE_REMOTE6:
geneve.Remote = datum.Value
case nl.IFLA_GENEVE_PORT:
geneve.Dport = ntohs(datum.Value[0:2])
case nl.IFLA_GENEVE_TTL:
geneve.Ttl = uint8(datum.Value[0])
case nl.IFLA_GENEVE_TOS:
geneve.Tos = uint8(datum.Value[0])
}
}
}
func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
@ -2679,6 +2874,10 @@ func addIptunAttrs(iptun *Iptun, linkInfo *nl.RtAttr) {
func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) {
iptun := link.(*Iptun)
for _, datum := range data {
// NOTE: same with vxlan, ip tunnel may also has null datum.Value
if len(datum.Value) == 0 {
continue
}
switch datum.Attr.Type {
case nl.IFLA_IPTUN_LOCAL:
iptun.Local = net.IP(datum.Value[0:4])
@ -3172,9 +3371,86 @@ func parseIPoIBData(link Link, data []syscall.NetlinkRouteAttr) {
}
}
func parseCanData(link Link, data []syscall.NetlinkRouteAttr) {
can := link.(*Can)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_CAN_BITTIMING:
can.BitRate = native.Uint32(datum.Value)
can.SamplePoint = native.Uint32(datum.Value[4:])
can.TimeQuanta = native.Uint32(datum.Value[8:])
can.PropagationSegment = native.Uint32(datum.Value[12:])
can.PhaseSegment1 = native.Uint32(datum.Value[16:])
can.PhaseSegment2 = native.Uint32(datum.Value[20:])
can.SyncJumpWidth = native.Uint32(datum.Value[24:])
can.BitRatePreScaler = native.Uint32(datum.Value[28:])
case nl.IFLA_CAN_BITTIMING_CONST:
can.Name = string(datum.Value[:16])
can.TimeSegment1Min = native.Uint32(datum.Value[16:])
can.TimeSegment1Max = native.Uint32(datum.Value[20:])
can.TimeSegment2Min = native.Uint32(datum.Value[24:])
can.TimeSegment2Max = native.Uint32(datum.Value[28:])
can.SyncJumpWidthMax = native.Uint32(datum.Value[32:])
can.BitRatePreScalerMin = native.Uint32(datum.Value[36:])
can.BitRatePreScalerMax = native.Uint32(datum.Value[40:])
can.BitRatePreScalerInc = native.Uint32(datum.Value[44:])
case nl.IFLA_CAN_CLOCK:
can.ClockFrequency = native.Uint32(datum.Value)
case nl.IFLA_CAN_STATE:
can.State = native.Uint32(datum.Value)
case nl.IFLA_CAN_CTRLMODE:
can.Mask = native.Uint32(datum.Value)
can.Flags = native.Uint32(datum.Value[4:])
case nl.IFLA_CAN_BERR_COUNTER:
can.TxError = native.Uint16(datum.Value)
can.RxError = native.Uint16(datum.Value[2:])
case nl.IFLA_CAN_RESTART_MS:
can.RestartMs = native.Uint32(datum.Value)
case nl.IFLA_CAN_DATA_BITTIMING_CONST:
case nl.IFLA_CAN_RESTART:
case nl.IFLA_CAN_DATA_BITTIMING:
case nl.IFLA_CAN_TERMINATION:
case nl.IFLA_CAN_TERMINATION_CONST:
case nl.IFLA_CAN_BITRATE_CONST:
case nl.IFLA_CAN_DATA_BITRATE_CONST:
case nl.IFLA_CAN_BITRATE_MAX:
}
}
}
func addIPoIBAttrs(ipoib *IPoIB, linkInfo *nl.RtAttr) {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_IPOIB_PKEY, nl.Uint16Attr(uint16(ipoib.Pkey)))
data.AddRtAttr(nl.IFLA_IPOIB_MODE, nl.Uint16Attr(uint16(ipoib.Mode)))
data.AddRtAttr(nl.IFLA_IPOIB_UMCAST, nl.Uint16Attr(uint16(ipoib.Umcast)))
}
func addBareUDPAttrs(bareudp *BareUDP, linkInfo *nl.RtAttr) {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_BAREUDP_PORT, nl.Uint16Attr(nl.Swap16(bareudp.Port)))
data.AddRtAttr(nl.IFLA_BAREUDP_ETHERTYPE, nl.Uint16Attr(nl.Swap16(bareudp.EtherType)))
if bareudp.SrcPortMin != 0 {
data.AddRtAttr(nl.IFLA_BAREUDP_SRCPORT_MIN, nl.Uint16Attr(bareudp.SrcPortMin))
}
if bareudp.MultiProto {
data.AddRtAttr(nl.IFLA_BAREUDP_MULTIPROTO_MODE, []byte{})
}
}
func parseBareUDPData(link Link, data []syscall.NetlinkRouteAttr) {
bareudp := link.(*BareUDP)
for _, attr := range data {
switch attr.Attr.Type {
case nl.IFLA_BAREUDP_PORT:
bareudp.Port = binary.BigEndian.Uint16(attr.Value)
case nl.IFLA_BAREUDP_ETHERTYPE:
bareudp.EtherType = binary.BigEndian.Uint16(attr.Value)
case nl.IFLA_BAREUDP_SRCPORT_MIN:
bareudp.SrcPortMin = native.Uint16(attr.Value)
case nl.IFLA_BAREUDP_MULTIPROTO_MODE:
bareudp.MultiProto = true
}
}
}

View File

@ -42,11 +42,12 @@ const (
// Neighbor Flags
const (
NTF_USE = 0x01
NTF_SELF = 0x02
NTF_MASTER = 0x04
NTF_PROXY = 0x08
NTF_ROUTER = 0x80
NTF_USE = 0x01
NTF_SELF = 0x02
NTF_MASTER = 0x04
NTF_PROXY = 0x08
NTF_EXT_LEARNED = 0x10
NTF_ROUTER = 0x80
)
// Ndmsg is for adding, removing or receiving information about a neighbor table entry
@ -408,7 +409,6 @@ func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue

View File

@ -180,14 +180,30 @@ func RouteAdd(route *Route) error {
return ErrNotImplemented
}
func RouteAppend(route *Route) error {
return ErrNotImplemented
}
func RouteDel(route *Route) error {
return ErrNotImplemented
}
func RouteGet(destination net.IP) ([]Route, error) {
return nil, ErrNotImplemented
}
func RouteList(link Link, family int) ([]Route, error) {
return nil, ErrNotImplemented
}
func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
return nil, ErrNotImplemented
}
func RouteReplace(route *Route) error {
return ErrNotImplemented
}
func XfrmPolicyAdd(policy *XfrmPolicy) error {
return ErrNotImplemented
}

View File

@ -87,7 +87,7 @@ func (h *Handle) getNetNsId(attrType int, val uint32) (int, error) {
rtgen := nl.NewRtGenMsg()
req.AddData(rtgen)
b := make([]byte, 4, 4)
b := make([]byte, 4)
native.PutUint32(b, val)
attr := nl.NewRtAttr(attrType, b)
req.AddData(attr)
@ -126,12 +126,12 @@ func (h *Handle) setNetNsId(attrType int, val uint32, newnsid uint32) error {
rtgen := nl.NewRtGenMsg()
req.AddData(rtgen)
b := make([]byte, 4, 4)
b := make([]byte, 4)
native.PutUint32(b, val)
attr := nl.NewRtAttr(attrType, b)
req.AddData(attr)
b1 := make([]byte, 4, 4)
b1 := make([]byte, 4)
native.PutUint32(b1, newnsid)
attr1 := nl.NewRtAttr(NETNSA_NSID, b1)
req.AddData(attr1)

View File

@ -44,6 +44,7 @@ const (
NLA_F_NESTED uint16 = (1 << 15) // #define NLA_F_NESTED (1 << 15)
NLA_F_NET_BYTEORDER uint16 = (1 << 14) // #define NLA_F_NESTED (1 << 14)
NLA_TYPE_MASK = ^(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
NLA_ALIGNTO uint16 = 4 // #define NLA_ALIGNTO 4
)
// enum ctattr_type {

View File

@ -10,16 +10,30 @@ const (
const (
DEVLINK_CMD_GET = 1
DEVLINK_CMD_PORT_GET = 5
DEVLINK_CMD_PORT_SET = 6
DEVLINK_CMD_PORT_NEW = 7
DEVLINK_CMD_PORT_DEL = 8
DEVLINK_CMD_ESWITCH_GET = 29
DEVLINK_CMD_ESWITCH_SET = 30
)
const (
DEVLINK_ATTR_BUS_NAME = 1
DEVLINK_ATTR_DEV_NAME = 2
DEVLINK_ATTR_ESWITCH_MODE = 25
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
DEVLINK_ATTR_BUS_NAME = 1
DEVLINK_ATTR_DEV_NAME = 2
DEVLINK_ATTR_PORT_INDEX = 3
DEVLINK_ATTR_PORT_TYPE = 4
DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6
DEVLINK_ATTR_PORT_NETDEV_NAME = 7
DEVLINK_ATTR_PORT_IBDEV_NAME = 8
DEVLINK_ATTR_ESWITCH_MODE = 25
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
DEVLINK_ATTR_PORT_FLAVOUR = 77
DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127
DEVLINK_ATTR_PORT_FUNCTION = 145
DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150
DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164
)
const (
@ -38,3 +52,37 @@ const (
DEVLINK_ESWITCH_ENCAP_MODE_NONE = 0
DEVLINK_ESWITCH_ENCAP_MODE_BASIC = 1
)
const (
DEVLINK_PORT_FLAVOUR_PHYSICAL = 0
DEVLINK_PORT_FLAVOUR_CPU = 1
DEVLINK_PORT_FLAVOUR_DSA = 2
DEVLINK_PORT_FLAVOUR_PCI_PF = 3
DEVLINK_PORT_FLAVOUR_PCI_VF = 4
DEVLINK_PORT_FLAVOUR_VIRTUAL = 5
DEVLINK_PORT_FLAVOUR_UNUSED = 6
DEVLINK_PORT_FLAVOUR_PCI_SF = 7
)
const (
DEVLINK_PORT_TYPE_NOTSET = 0
DEVLINK_PORT_TYPE_AUTO = 1
DEVLINK_PORT_TYPE_ETH = 2
DEVLINK_PORT_TYPE_IB = 3
)
const (
DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR = 1
DEVLINK_PORT_FN_ATTR_STATE = 2
DEVLINK_PORT_FN_ATTR_OPSTATE = 3
)
const (
DEVLINK_PORT_FN_STATE_INACTIVE = 0
DEVLINK_PORT_FN_STATE_ACTIVE = 1
)
const (
DEVLINK_PORT_FN_OPSTATE_DETACHED = 0
DEVLINK_PORT_FN_OPSTATE_ATTACHED = 1
)

View File

@ -173,6 +173,22 @@ const (
IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE
)
const (
IFLA_GENEVE_UNSPEC = iota
IFLA_GENEVE_ID // vni
IFLA_GENEVE_REMOTE
IFLA_GENEVE_TTL
IFLA_GENEVE_TOS
IFLA_GENEVE_PORT // destination port
IFLA_GENEVE_COLLECT_METADATA
IFLA_GENEVE_REMOTE6
IFLA_GENEVE_UDP_CSUM
IFLA_GENEVE_UDP_ZERO_CSUM6_TX
IFLA_GENEVE_UDP_ZERO_CSUM6_RX
IFLA_GENEVE_LABEL
IFLA_GENEVE_MAX = IFLA_GENEVE_LABEL
)
const (
IFLA_GRE_UNSPEC = iota
IFLA_GRE_LINK
@ -673,3 +689,32 @@ const (
IFLA_IPOIB_UMCAST
IFLA_IPOIB_MAX = IFLA_IPOIB_UMCAST
)
const (
IFLA_CAN_UNSPEC = iota
IFLA_CAN_BITTIMING
IFLA_CAN_BITTIMING_CONST
IFLA_CAN_CLOCK
IFLA_CAN_STATE
IFLA_CAN_CTRLMODE
IFLA_CAN_RESTART_MS
IFLA_CAN_RESTART
IFLA_CAN_BERR_COUNTER
IFLA_CAN_DATA_BITTIMING
IFLA_CAN_DATA_BITTIMING_CONST
IFLA_CAN_TERMINATION
IFLA_CAN_TERMINATION_CONST
IFLA_CAN_BITRATE_CONST
IFLA_CAN_DATA_BITRATE_CONST
IFLA_CAN_BITRATE_MAX
IFLA_CAN_MAX = IFLA_CAN_BITRATE_MAX
)
const (
IFLA_BAREUDP_UNSPEC = iota
IFLA_BAREUDP_PORT
IFLA_BAREUDP_ETHERTYPE
IFLA_BAREUDP_SRCPORT_MIN
IFLA_BAREUDP_MULTIPROTO_MODE
IFLA_BAREUDP_MAX = IFLA_BAREUDP_MULTIPROTO_MODE
)

View File

@ -0,0 +1,29 @@
package nl
const (
LWT_BPF_PROG_UNSPEC = iota
LWT_BPF_PROG_FD
LWT_BPF_PROG_NAME
__LWT_BPF_PROG_MAX
)
const (
LWT_BPF_PROG_MAX = __LWT_BPF_PROG_MAX - 1
)
const (
LWT_BPF_UNSPEC = iota
LWT_BPF_IN
LWT_BPF_OUT
LWT_BPF_XMIT
LWT_BPF_XMIT_HEADROOM
__LWT_BPF_MAX
)
const (
LWT_BPF_MAX = __LWT_BPF_MAX - 1
)
const (
LWT_BPF_MAX_HEADROOM = 256
)

View File

@ -35,6 +35,9 @@ var SupportedNlFamilies = []int{unix.NETLINK_ROUTE, unix.NETLINK_XFRM, unix.NETL
var nextSeqNr uint32
// Default netlink socket timeout, 60s
var SocketTimeoutTv = unix.Timeval{Sec: 60, Usec: 0}
// GetIPFamily returns the family type of a net.IP.
func GetIPFamily(ip net.IP) int {
if len(ip) <= net.IPv4len {
@ -426,6 +429,14 @@ func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, erro
if err != nil {
return nil, err
}
if err := s.SetSendTimeout(&SocketTimeoutTv); err != nil {
return nil, err
}
if err := s.SetReceiveTimeout(&SocketTimeoutTv); err != nil {
return nil, err
}
defer s.Close()
} else {
s.Lock()

View File

@ -3,6 +3,7 @@ package nl
import (
"encoding/binary"
"fmt"
"log"
)
type Attribute struct {
@ -18,9 +19,20 @@ func ParseAttributes(data []byte) <-chan Attribute {
i := 0
for i+4 < len(data) {
length := int(native.Uint16(data[i : i+2]))
attrType := native.Uint16(data[i+2 : i+4])
if length < 4 {
log.Printf("attribute 0x%02x has invalid length of %d bytes", attrType, length)
break
}
if len(data) < i+length {
log.Printf("attribute 0x%02x of length %d is truncated, only %d bytes remaining", attrType, length, len(data)-i)
break
}
result <- Attribute{
Type: native.Uint16(data[i+2 : i+4]),
Type: attrType,
Value: data[i+4 : i+length],
}
i += rtaAlignOf(length)

View File

@ -11,6 +11,8 @@ const (
const (
RDMA_NLDEV_CMD_GET = 1
RDMA_NLDEV_CMD_SET = 2
RDMA_NLDEV_CMD_NEWLINK = 3
RDMA_NLDEV_CMD_DELLINK = 4
RDMA_NLDEV_CMD_SYS_GET = 6
RDMA_NLDEV_CMD_SYS_SET = 7
)
@ -30,6 +32,8 @@ const (
RDMA_NLDEV_ATTR_PORT_STATE = 12
RDMA_NLDEV_ATTR_PORT_PHYS_STATE = 13
RDMA_NLDEV_ATTR_DEV_NODE_TYPE = 14
RDMA_NLDEV_ATTR_NDEV_NAME = 51
RDMA_NLDEV_ATTR_LINK_TYPE = 65
RDMA_NLDEV_SYS_ATTR_NETNS_MODE = 66
RDMA_NLDEV_NET_NS_FD = 68
)

View File

@ -23,7 +23,7 @@ func (s1 *IPv6SrHdr) Equal(s2 IPv6SrHdr) bool {
return false
}
for i := range s1.Segments {
if s1.Segments[i].Equal(s2.Segments[i]) != true {
if !s1.Segments[i].Equal(s2.Segments[i]) {
return false
}
}
@ -89,7 +89,7 @@ func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) {
}
buf = buf[12:]
if len(buf)%16 != 0 {
err := fmt.Errorf("DecodeSEG6Encap: error parsing Segment List (buf len: %d)\n", len(buf))
err := fmt.Errorf("DecodeSEG6Encap: error parsing Segment List (buf len: %d)", len(buf))
return mode, nil, err
}
for len(buf) > 0 {

View File

@ -1,6 +1,6 @@
package nl
// syscall package lack of rule atributes type.
// syscall package lack of rule attributes type.
// Thus there are defined below
const (
FRA_UNSPEC = iota

View File

@ -882,6 +882,111 @@ const (
TCA_HFSC_USC
)
const (
TCA_FLOWER_UNSPEC = iota
TCA_FLOWER_CLASSID
TCA_FLOWER_INDEV
TCA_FLOWER_ACT
TCA_FLOWER_KEY_ETH_DST /* ETH_ALEN */
TCA_FLOWER_KEY_ETH_DST_MASK /* ETH_ALEN */
TCA_FLOWER_KEY_ETH_SRC /* ETH_ALEN */
TCA_FLOWER_KEY_ETH_SRC_MASK /* ETH_ALEN */
TCA_FLOWER_KEY_ETH_TYPE /* be16 */
TCA_FLOWER_KEY_IP_PROTO /* u8 */
TCA_FLOWER_KEY_IPV4_SRC /* be32 */
TCA_FLOWER_KEY_IPV4_SRC_MASK /* be32 */
TCA_FLOWER_KEY_IPV4_DST /* be32 */
TCA_FLOWER_KEY_IPV4_DST_MASK /* be32 */
TCA_FLOWER_KEY_IPV6_SRC /* struct in6_addr */
TCA_FLOWER_KEY_IPV6_SRC_MASK /* struct in6_addr */
TCA_FLOWER_KEY_IPV6_DST /* struct in6_addr */
TCA_FLOWER_KEY_IPV6_DST_MASK /* struct in6_addr */
TCA_FLOWER_KEY_TCP_SRC /* be16 */
TCA_FLOWER_KEY_TCP_DST /* be16 */
TCA_FLOWER_KEY_UDP_SRC /* be16 */
TCA_FLOWER_KEY_UDP_DST /* be16 */
TCA_FLOWER_FLAGS
TCA_FLOWER_KEY_VLAN_ID /* be16 */
TCA_FLOWER_KEY_VLAN_PRIO /* u8 */
TCA_FLOWER_KEY_VLAN_ETH_TYPE /* be16 */
TCA_FLOWER_KEY_ENC_KEY_ID /* be32 */
TCA_FLOWER_KEY_ENC_IPV4_SRC /* be32 */
TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK /* be32 */
TCA_FLOWER_KEY_ENC_IPV4_DST /* be32 */
TCA_FLOWER_KEY_ENC_IPV4_DST_MASK /* be32 */
TCA_FLOWER_KEY_ENC_IPV6_SRC /* struct in6_addr */
TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK /* struct in6_addr */
TCA_FLOWER_KEY_ENC_IPV6_DST /* struct in6_addr */
TCA_FLOWER_KEY_ENC_IPV6_DST_MASK /* struct in6_addr */
TCA_FLOWER_KEY_TCP_SRC_MASK /* be16 */
TCA_FLOWER_KEY_TCP_DST_MASK /* be16 */
TCA_FLOWER_KEY_UDP_SRC_MASK /* be16 */
TCA_FLOWER_KEY_UDP_DST_MASK /* be16 */
TCA_FLOWER_KEY_SCTP_SRC_MASK /* be16 */
TCA_FLOWER_KEY_SCTP_DST_MASK /* be16 */
TCA_FLOWER_KEY_SCTP_SRC /* be16 */
TCA_FLOWER_KEY_SCTP_DST /* be16 */
TCA_FLOWER_KEY_ENC_UDP_SRC_PORT /* be16 */
TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK /* be16 */
TCA_FLOWER_KEY_ENC_UDP_DST_PORT /* be16 */
TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK /* be16 */
TCA_FLOWER_KEY_FLAGS /* be32 */
TCA_FLOWER_KEY_FLAGS_MASK /* be32 */
TCA_FLOWER_KEY_ICMPV4_CODE /* u8 */
TCA_FLOWER_KEY_ICMPV4_CODE_MASK /* u8 */
TCA_FLOWER_KEY_ICMPV4_TYPE /* u8 */
TCA_FLOWER_KEY_ICMPV4_TYPE_MASK /* u8 */
TCA_FLOWER_KEY_ICMPV6_CODE /* u8 */
TCA_FLOWER_KEY_ICMPV6_CODE_MASK /* u8 */
TCA_FLOWER_KEY_ICMPV6_TYPE /* u8 */
TCA_FLOWER_KEY_ICMPV6_TYPE_MASK /* u8 */
TCA_FLOWER_KEY_ARP_SIP /* be32 */
TCA_FLOWER_KEY_ARP_SIP_MASK /* be32 */
TCA_FLOWER_KEY_ARP_TIP /* be32 */
TCA_FLOWER_KEY_ARP_TIP_MASK /* be32 */
TCA_FLOWER_KEY_ARP_OP /* u8 */
TCA_FLOWER_KEY_ARP_OP_MASK /* u8 */
TCA_FLOWER_KEY_ARP_SHA /* ETH_ALEN */
TCA_FLOWER_KEY_ARP_SHA_MASK /* ETH_ALEN */
TCA_FLOWER_KEY_ARP_THA /* ETH_ALEN */
TCA_FLOWER_KEY_ARP_THA_MASK /* ETH_ALEN */
TCA_FLOWER_KEY_MPLS_TTL /* u8 - 8 bits */
TCA_FLOWER_KEY_MPLS_BOS /* u8 - 1 bit */
TCA_FLOWER_KEY_MPLS_TC /* u8 - 3 bits */
TCA_FLOWER_KEY_MPLS_LABEL /* be32 - 20 bits */
TCA_FLOWER_KEY_TCP_FLAGS /* be16 */
TCA_FLOWER_KEY_TCP_FLAGS_MASK /* be16 */
TCA_FLOWER_KEY_IP_TOS /* u8 */
TCA_FLOWER_KEY_IP_TOS_MASK /* u8 */
TCA_FLOWER_KEY_IP_TTL /* u8 */
TCA_FLOWER_KEY_IP_TTL_MASK /* u8 */
TCA_FLOWER_KEY_CVLAN_ID /* be16 */
TCA_FLOWER_KEY_CVLAN_PRIO /* u8 */
TCA_FLOWER_KEY_CVLAN_ETH_TYPE /* be16 */
TCA_FLOWER_KEY_ENC_IP_TOS /* u8 */
TCA_FLOWER_KEY_ENC_IP_TOS_MASK /* u8 */
TCA_FLOWER_KEY_ENC_IP_TTL /* u8 */
TCA_FLOWER_KEY_ENC_IP_TTL_MASK /* u8 */
TCA_FLOWER_KEY_ENC_OPTS
TCA_FLOWER_KEY_ENC_OPTS_MASK
__TCA_FLOWER_MAX
)
// struct tc_sfq_qopt {
// unsigned quantum; /* Bytes per round allocated to flow */
// int perturb_period; /* Period of hash perturbation */

View File

@ -308,13 +308,15 @@ func (qdisc *Fq) Type() string {
// FQ_Codel (Fair Queuing Controlled Delay) is queuing discipline that combines Fair Queuing with the CoDel AQM scheme.
type FqCodel struct {
QdiscAttrs
Target uint32
Limit uint32
Interval uint32
ECN uint32
Flows uint32
Quantum uint32
// There are some more attributes here, but support for them seems not ubiquitous
Target uint32
Limit uint32
Interval uint32
ECN uint32
Flows uint32
Quantum uint32
CEThreshold uint32
DropBatchSize uint32
MemoryLimit uint32
}
func (fqcodel *FqCodel) String() string {

View File

@ -250,7 +250,15 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
if qdisc.Quantum > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum))))
}
if qdisc.CEThreshold > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_CE_THRESHOLD, nl.Uint32Attr(qdisc.CEThreshold))
}
if qdisc.DropBatchSize > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_DROP_BATCH_SIZE, nl.Uint32Attr(qdisc.DropBatchSize))
}
if qdisc.MemoryLimit > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_MEMORY_LIMIT, nl.Uint32Attr(qdisc.MemoryLimit))
}
case *Fq:
options.AddRtAttr(nl.TCA_FQ_RATE_ENABLE, nl.Uint32Attr((uint32(qdisc.Pacing))))
@ -460,7 +468,6 @@ func parsePrioData(qdisc Qdisc, value []byte) error {
}
func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
htb := qdisc.(*Htb)
for _, datum := range data {
switch datum.Attr.Type {
@ -480,7 +487,6 @@ func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
}
func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
fqCodel := qdisc.(*FqCodel)
for _, datum := range data {
@ -497,6 +503,12 @@ func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
fqCodel.Flows = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_QUANTUM:
fqCodel.Quantum = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_CE_THRESHOLD:
fqCodel.CEThreshold = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_DROP_BATCH_SIZE:
fqCodel.DropBatchSize = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_MEMORY_LIMIT:
fqCodel.MemoryLimit = native.Uint32(datum.Value)
}
}
return nil
@ -504,13 +516,11 @@ func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
func parseHfscData(qdisc Qdisc, data []byte) error {
Hfsc := qdisc.(*Hfsc)
native = nl.NativeEndian()
Hfsc.Defcls = native.Uint16(data)
return nil
}
func parseFqData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
fq := qdisc.(*Fq)
for _, datum := range data {
switch datum.Attr.Type {
@ -575,7 +585,6 @@ func parseNetemData(qdisc Qdisc, value []byte) error {
}
func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
tbf := qdisc.(*Tbf)
for _, datum := range data {
switch datum.Attr.Type {

View File

@ -278,3 +278,54 @@ func (h *Handle) RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error {
return execRdmaSetLink(req)
}
// RdmaLinkDel deletes an rdma link
//
// Similar to: rdma link delete NAME
// REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html
func RdmaLinkDel(name string) error {
return pkgHandle.RdmaLinkDel(name)
}
// RdmaLinkDel deletes an rdma link.
func (h *Handle) RdmaLinkDel(name string) error {
link, err := h.RdmaLinkByName(name)
if err != nil {
return err
}
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_DELLINK)
req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
b := make([]byte, 4)
native.PutUint32(b, link.Attrs.Index)
req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b))
_, err = req.Execute(unix.NETLINK_RDMA, 0)
return err
}
// RdmaLinkAdd adds an rdma link for the specified type to the network device.
// Similar to: rdma link add NAME type TYPE netdev NETDEV
// NAME - specifies the new name of the rdma link to add
// TYPE - specifies which rdma type to use. Link types:
// rxe - Soft RoCE driver
// siw - Soft iWARP driver
// NETDEV - specifies the network device to which the link is bound
//
// REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html
func RdmaLinkAdd(linkName, linkType, netdev string) error {
return pkgHandle.RdmaLinkAdd(linkName, linkType, netdev)
}
// RdmaLinkAdd adds an rdma link for the specified type to the network device.
func (h *Handle) RdmaLinkAdd(linkName string, linkType string, netdev string) error {
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_NEWLINK)
req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, nl.ZeroTerminated(linkName)))
req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_LINK_TYPE, nl.ZeroTerminated(linkType)))
req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_NDEV_NAME, nl.ZeroTerminated(netdev)))
_, err := req.Execute(unix.NETLINK_RDMA, 0)
return err
}

View File

@ -27,6 +27,9 @@ type Encap interface {
Equal(Encap) bool
}
//Protocol describe what was the originator of the route
type RouteProtocol int
// Route represents a netlink route.
type Route struct {
LinkIndex int
@ -36,8 +39,9 @@ type Route struct {
Src net.IP
Gw net.IP
MultiPath []*NexthopInfo
Protocol int
Protocol RouteProtocol
Priority int
Family int
Table int
Type int
Tos int
@ -45,6 +49,8 @@ type Route struct {
MPLSDst *int
NewDst Destination
Encap Encap
Via Destination
Realm int
MTU int
Window int
Rtt int
@ -79,6 +85,9 @@ func (r Route) String() string {
if r.Encap != nil {
elems = append(elems, fmt.Sprintf("Encap: %s", r.Encap))
}
if r.Via != nil {
elems = append(elems, fmt.Sprintf("Via: %s", r.Via))
}
elems = append(elems, fmt.Sprintf("Src: %s", r.Src))
if len(r.MultiPath) > 0 {
elems = append(elems, fmt.Sprintf("Gw: %s", r.MultiPath))
@ -87,6 +96,7 @@ func (r Route) String() string {
}
elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags()))
elems = append(elems, fmt.Sprintf("Table: %d", r.Table))
elems = append(elems, fmt.Sprintf("Realm: %d", r.Realm))
return fmt.Sprintf("{%s}", strings.Join(elems, " "))
}
@ -100,6 +110,7 @@ func (r Route) Equal(x Route) bool {
nexthopInfoSlice(r.MultiPath).Equal(x.MultiPath) &&
r.Protocol == x.Protocol &&
r.Priority == x.Priority &&
r.Realm == x.Realm &&
r.Table == x.Table &&
r.Type == x.Type &&
r.Tos == x.Tos &&
@ -107,6 +118,7 @@ func (r Route) Equal(x Route) bool {
r.Flags == x.Flags &&
(r.MPLSDst == x.MPLSDst || (r.MPLSDst != nil && x.MPLSDst != nil && *r.MPLSDst == *x.MPLSDst)) &&
(r.NewDst == x.NewDst || (r.NewDst != nil && r.NewDst.Equal(x.NewDst))) &&
(r.Via == x.Via || (r.Via != nil && r.Via.Equal(x.Via))) &&
(r.Encap == x.Encap || (r.Encap != nil && r.Encap.Equal(x.Encap)))
}
@ -136,6 +148,7 @@ type NexthopInfo struct {
Flags int
NewDst Destination
Encap Encap
Via Destination
}
func (n *NexthopInfo) String() string {
@ -147,6 +160,9 @@ func (n *NexthopInfo) String() string {
if n.Encap != nil {
elems = append(elems, fmt.Sprintf("Encap: %s", n.Encap))
}
if n.Via != nil {
elems = append(elems, fmt.Sprintf("Via: %s", n.Via))
}
elems = append(elems, fmt.Sprintf("Weight: %d", n.Hops+1))
elems = append(elems, fmt.Sprintf("Gw: %s", n.Gw))
elems = append(elems, fmt.Sprintf("Flags: %s", n.ListFlags()))

View File

@ -1,8 +1,11 @@
package netlink
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"strconv"
"strings"
"syscall"
@ -21,6 +24,23 @@ const (
SCOPE_NOWHERE Scope = unix.RT_SCOPE_NOWHERE
)
func (s Scope) String() string {
switch s {
case SCOPE_UNIVERSE:
return "universe"
case SCOPE_SITE:
return "site"
case SCOPE_LINK:
return "link"
case SCOPE_HOST:
return "host"
case SCOPE_NOWHERE:
return "nowhere"
default:
return "unknown"
}
}
const (
RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
RT_FILTER_SCOPE
@ -36,6 +56,7 @@ const (
RT_FILTER_PRIORITY
RT_FILTER_MARK
RT_FILTER_MASK
RT_FILTER_REALM
)
const (
@ -131,7 +152,6 @@ func (e *MPLSEncap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
l := native.Uint16(buf)
if len(buf) < int(l) {
return fmt.Errorf("lack of bytes")
@ -147,7 +167,6 @@ func (e *MPLSEncap) Decode(buf []byte) error {
func (e *MPLSEncap) Encode() ([]byte, error) {
s := nl.EncodeMPLSStack(e.Labels...)
native := nl.NativeEndian()
hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.MPLS_IPTUNNEL_DST)
@ -203,7 +222,6 @@ func (e *SEG6Encap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
// Get Length(l) & Type(typ) : 2 + 2 bytes
l := native.Uint16(buf)
if len(buf) < int(l) {
@ -223,7 +241,6 @@ func (e *SEG6Encap) Decode(buf []byte) error {
}
func (e *SEG6Encap) Encode() ([]byte, error) {
s, err := nl.EncodeSEG6Encap(e.Mode, e.Segments)
native := nl.NativeEndian()
hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.SEG6_IPTUNNEL_SRH)
@ -233,7 +250,7 @@ func (e *SEG6Encap) String() string {
segs := make([]string, 0, len(e.Segments))
// append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- {
segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1]))
segs = append(segs, e.Segments[i-1].String())
}
str := fmt.Sprintf("mode %s segs %d [ %s ]", nl.SEG6EncapModeString(e.Mode),
len(e.Segments), strings.Join(segs, " "))
@ -284,7 +301,6 @@ func (e *SEG6LocalEncap) Decode(buf []byte) error {
if err != nil {
return err
}
native := nl.NativeEndian()
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.SEG6_LOCAL_ACTION:
@ -314,7 +330,6 @@ func (e *SEG6LocalEncap) Decode(buf []byte) error {
}
func (e *SEG6LocalEncap) Encode() ([]byte, error) {
var err error
native := nl.NativeEndian()
res := make([]byte, 8)
native.PutUint16(res, 8) // length
native.PutUint16(res[2:], nl.SEG6_LOCAL_ACTION)
@ -405,7 +420,7 @@ func (e *SEG6LocalEncap) String() string {
segs := make([]string, 0, len(e.Segments))
//append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- {
segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1]))
segs = append(segs, e.Segments[i-1].String())
}
strs = append(strs, fmt.Sprintf("segs %d [ %s ]", len(e.Segments), strings.Join(segs, " ")))
}
@ -446,6 +461,207 @@ func (e *SEG6LocalEncap) Equal(x Encap) bool {
return true
}
// Encap BPF definitions
type bpfObj struct {
progFd int
progName string
}
type BpfEncap struct {
progs [nl.LWT_BPF_MAX]bpfObj
headroom int
}
// SetProg adds a bpf function to the route via netlink RTA_ENCAP. The fd must be a bpf
// program loaded with bpf(type=BPF_PROG_TYPE_LWT_*) matching the direction the program should
// be applied to (LWT_BPF_IN, LWT_BPF_OUT, LWT_BPF_XMIT).
func (e *BpfEncap) SetProg(mode, progFd int, progName string) error {
if progFd <= 0 {
return fmt.Errorf("lwt bpf SetProg: invalid fd")
}
if mode <= nl.LWT_BPF_UNSPEC || mode >= nl.LWT_BPF_XMIT_HEADROOM {
return fmt.Errorf("lwt bpf SetProg:invalid mode")
}
e.progs[mode].progFd = progFd
e.progs[mode].progName = fmt.Sprintf("%s[fd:%d]", progName, progFd)
return nil
}
// SetXmitHeadroom sets the xmit headroom (LWT_BPF_MAX_HEADROOM) via netlink RTA_ENCAP.
// maximum headroom is LWT_BPF_MAX_HEADROOM
func (e *BpfEncap) SetXmitHeadroom(headroom int) error {
if headroom > nl.LWT_BPF_MAX_HEADROOM || headroom < 0 {
return fmt.Errorf("invalid headroom size. range is 0 - %d", nl.LWT_BPF_MAX_HEADROOM)
}
e.headroom = headroom
return nil
}
func (e *BpfEncap) Type() int {
return nl.LWTUNNEL_ENCAP_BPF
}
func (e *BpfEncap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("lwt bpf decode: lack of bytes")
}
native := nl.NativeEndian()
attrs, err := nl.ParseRouteAttr(buf)
if err != nil {
return fmt.Errorf("lwt bpf decode: failed parsing attribute. err: %v", err)
}
for _, attr := range attrs {
if int(attr.Attr.Type) < 1 {
// nl.LWT_BPF_UNSPEC
continue
}
if int(attr.Attr.Type) > nl.LWT_BPF_MAX {
return fmt.Errorf("lwt bpf decode: received unknown attribute type: %d", attr.Attr.Type)
}
switch int(attr.Attr.Type) {
case nl.LWT_BPF_MAX_HEADROOM:
e.headroom = int(native.Uint32(attr.Value))
default:
bpfO := bpfObj{}
parsedAttrs, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return fmt.Errorf("lwt bpf decode: failed parsing route attribute")
}
for _, parsedAttr := range parsedAttrs {
switch int(parsedAttr.Attr.Type) {
case nl.LWT_BPF_PROG_FD:
bpfO.progFd = int(native.Uint32(parsedAttr.Value))
case nl.LWT_BPF_PROG_NAME:
bpfO.progName = string(parsedAttr.Value)
default:
return fmt.Errorf("lwt bpf decode: received unknown attribute: type: %d, len: %d", parsedAttr.Attr.Type, parsedAttr.Attr.Len)
}
}
e.progs[attr.Attr.Type] = bpfO
}
}
return nil
}
func (e *BpfEncap) Encode() ([]byte, error) {
buf := make([]byte, 0)
native = nl.NativeEndian()
for index, attr := range e.progs {
nlMsg := nl.NewRtAttr(index, []byte{})
if attr.progFd != 0 {
nlMsg.AddRtAttr(nl.LWT_BPF_PROG_FD, nl.Uint32Attr(uint32(attr.progFd)))
}
if attr.progName != "" {
nlMsg.AddRtAttr(nl.LWT_BPF_PROG_NAME, nl.ZeroTerminated(attr.progName))
}
if nlMsg.Len() > 4 {
buf = append(buf, nlMsg.Serialize()...)
}
}
if len(buf) <= 4 {
return nil, fmt.Errorf("lwt bpf encode: bpf obj definitions returned empty buffer")
}
if e.headroom > 0 {
hRoom := nl.NewRtAttr(nl.LWT_BPF_XMIT_HEADROOM, nl.Uint32Attr(uint32(e.headroom)))
buf = append(buf, hRoom.Serialize()...)
}
return buf, nil
}
func (e *BpfEncap) String() string {
progs := make([]string, 0)
for index, obj := range e.progs {
empty := bpfObj{}
switch index {
case nl.LWT_BPF_IN:
if obj != empty {
progs = append(progs, fmt.Sprintf("in: %s", obj.progName))
}
case nl.LWT_BPF_OUT:
if obj != empty {
progs = append(progs, fmt.Sprintf("out: %s", obj.progName))
}
case nl.LWT_BPF_XMIT:
if obj != empty {
progs = append(progs, fmt.Sprintf("xmit: %s", obj.progName))
}
}
}
if e.headroom > 0 {
progs = append(progs, fmt.Sprintf("xmit headroom: %d", e.headroom))
}
return strings.Join(progs, " ")
}
func (e *BpfEncap) Equal(x Encap) bool {
o, ok := x.(*BpfEncap)
if !ok {
return false
}
if e.headroom != o.headroom {
return false
}
for i := range o.progs {
if o.progs[i] != e.progs[i] {
return false
}
}
return true
}
type Via struct {
AddrFamily int
Addr net.IP
}
func (v *Via) Equal(x Destination) bool {
o, ok := x.(*Via)
if !ok {
return false
}
if v.AddrFamily == x.Family() && v.Addr.Equal(o.Addr) {
return true
}
return false
}
func (v *Via) String() string {
return fmt.Sprintf("Family: %d, Address: %s", v.AddrFamily, v.Addr.String())
}
func (v *Via) Family() int {
return v.AddrFamily
}
func (v *Via) Encode() ([]byte, error) {
buf := &bytes.Buffer{}
err := binary.Write(buf, native, uint16(v.AddrFamily))
if err != nil {
return nil, err
}
err = binary.Write(buf, native, v.Addr)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (v *Via) Decode(b []byte) error {
if len(b) < 6 {
return fmt.Errorf("decoding failed: buffer too small (%d bytes)", len(b))
}
v.AddrFamily = int(native.Uint16(b[0:2]))
if v.AddrFamily == nl.FAMILY_V4 {
v.Addr = net.IP(b[2:6])
return nil
} else if v.AddrFamily == nl.FAMILY_V6 {
if len(b) < 18 {
return fmt.Errorf("decoding failed: buffer too small (%d bytes)", len(b))
}
v.Addr = net.IP(b[2:])
return nil
}
return fmt.Errorf("decoding failed: address family %d unknown", v.AddrFamily)
}
// RouteAdd will add a route to the system.
// Equivalent to: `ip route add $route`
func RouteAdd(route *Route) error {
@ -460,6 +676,32 @@ func (h *Handle) RouteAdd(route *Route) error {
return h.routeHandle(route, req, nl.NewRtMsg())
}
// RouteAppend will append a route to the system.
// Equivalent to: `ip route append $route`
func RouteAppend(route *Route) error {
return pkgHandle.RouteAppend(route)
}
// RouteAppend will append a route to the system.
// Equivalent to: `ip route append $route`
func (h *Handle) RouteAppend(route *Route) error {
flags := unix.NLM_F_CREATE | unix.NLM_F_APPEND | unix.NLM_F_ACK
req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags)
return h.routeHandle(route, req, nl.NewRtMsg())
}
// RouteAddEcmp will add a route to the system.
func RouteAddEcmp(route *Route) error {
return pkgHandle.RouteAddEcmp(route)
}
// RouteAddEcmp will add a route to the system.
func (h *Handle) RouteAddEcmp(route *Route) error {
flags := unix.NLM_F_CREATE | unix.NLM_F_ACK
req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags)
return h.routeHandle(route, req, nl.NewRtMsg())
}
// RouteReplace will add a route to the system.
// Equivalent to: `ip route replace $route`
func RouteReplace(route *Route) error {
@ -533,7 +775,13 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
if err != nil {
return err
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP, buf))
switch route.Encap.Type() {
case nl.LWTUNNEL_ENCAP_BPF:
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP|unix.NLA_F_NESTED, buf))
default:
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP, buf))
}
}
if route.Src != nil {
@ -567,6 +815,14 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_GATEWAY, gwData))
}
if route.Via != nil {
buf, err := route.Via.Encode()
if err != nil {
return fmt.Errorf("failed to encode RTA_VIA: %v", err)
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_VIA, buf))
}
if len(route.MultiPath) > 0 {
buf := []byte{}
for _, nh := range route.MultiPath {
@ -609,6 +865,13 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
}
children = append(children, nl.NewRtAttr(unix.RTA_ENCAP, buf))
}
if nh.Via != nil {
buf, err := nh.Via.Encode()
if err != nil {
return err
}
children = append(children, nl.NewRtAttr(unix.RTA_VIA, buf))
}
rtnh.Children = children
buf = append(buf, rtnh.Serialize()...)
}
@ -631,6 +894,11 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
native.PutUint32(b, uint32(route.Priority))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PRIORITY, b))
}
if route.Realm > 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(route.Realm))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_FLOW, b))
}
if route.Tos > 0 {
msg.Tos = uint8(route.Tos)
}
@ -723,10 +991,7 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
req.AddData(attr)
}
var (
b = make([]byte, 4)
native = nl.NativeEndian()
)
b := make([]byte, 4)
native.PutUint32(b, uint32(route.LinkIndex))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
@ -802,6 +1067,8 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
continue
case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos:
continue
case filterMask&RT_FILTER_REALM != 0 && route.Realm != filter.Realm:
continue
case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex:
continue
case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex:
@ -834,14 +1101,14 @@ func deserializeRoute(m []byte) (Route, error) {
}
route := Route{
Scope: Scope(msg.Scope),
Protocol: int(msg.Protocol),
Protocol: RouteProtocol(int(msg.Protocol)),
Table: int(msg.Table),
Type: int(msg.Type),
Tos: int(msg.Tos),
Flags: int(msg.Flags),
Family: int(msg.Family),
}
native := nl.NativeEndian()
var encap, encapType syscall.NetlinkRouteAttr
for _, attr := range attrs {
switch attr.Attr.Type {
@ -868,6 +1135,8 @@ func deserializeRoute(m []byte) (Route, error) {
route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_PRIORITY:
route.Priority = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_FLOW:
route.Realm = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_TABLE:
route.Table = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_MULTIPATH:
@ -907,6 +1176,12 @@ func deserializeRoute(m []byte) (Route, error) {
encapType = attr
case unix.RTA_ENCAP:
encap = attr
case unix.RTA_VIA:
d := &Via{}
if err := d.Decode(attr.Value); err != nil {
return nil, nil, err
}
info.Via = d
}
}
@ -944,6 +1219,12 @@ func deserializeRoute(m []byte) (Route, error) {
return route, err
}
route.NewDst = d
case unix.RTA_VIA:
v := &Via{}
if err := v.Decode(attr.Value); err != nil {
return route, err
}
route.Via = v
case unix.RTA_ENCAP_TYPE:
encapType = attr
case unix.RTA_ENCAP:
@ -1011,6 +1292,11 @@ func deserializeRoute(m []byte) (Route, error) {
if err := e.Decode(encap.Value); err != nil {
return route, err
}
case nl.LWTUNNEL_ENCAP_BPF:
e = &BpfEncap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
}
route.Encap = e
}
@ -1021,7 +1307,10 @@ func deserializeRoute(m []byte) (Route, error) {
// RouteGetOptions contains a set of options to use with
// RouteGetWithOptions
type RouteGetOptions struct {
Iif string
Oif string
VrfName string
SrcAddr net.IP
}
// RouteGetWithOptions gets a route to a specific destination from the host system.
@ -1053,23 +1342,61 @@ func (h *Handle) RouteGetWithOptions(destination net.IP, options *RouteGetOption
msg := &nl.RtMsg{}
msg.Family = uint8(family)
msg.Dst_len = bitlen
if options != nil && options.SrcAddr != nil {
msg.Src_len = bitlen
}
msg.Flags = unix.RTM_F_LOOKUP_TABLE
req.AddData(msg)
rtaDst := nl.NewRtAttr(unix.RTA_DST, destinationData)
req.AddData(rtaDst)
if options != nil {
link, err := LinkByName(options.VrfName)
if err != nil {
return nil, err
}
var (
b = make([]byte, 4)
native = nl.NativeEndian()
)
native.PutUint32(b, uint32(link.Attrs().Index))
if options.VrfName != "" {
link, err := LinkByName(options.VrfName)
if err != nil {
return nil, err
}
b := make([]byte, 4)
native.PutUint32(b, uint32(link.Attrs().Index))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
}
if len(options.Iif) > 0 {
link, err := LinkByName(options.Iif)
if err != nil {
return nil, err
}
b := make([]byte, 4)
native.PutUint32(b, uint32(link.Attrs().Index))
req.AddData(nl.NewRtAttr(unix.RTA_IIF, b))
}
if len(options.Oif) > 0 {
link, err := LinkByName(options.Oif)
if err != nil {
return nil, err
}
b := make([]byte, 4)
native.PutUint32(b, uint32(link.Attrs().Index))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
}
if options.SrcAddr != nil {
var srcAddr []byte
if family == FAMILY_V4 {
srcAddr = options.SrcAddr.To4()
} else {
srcAddr = options.SrcAddr.To16()
}
req.AddData(nl.NewRtAttr(unix.RTA_SRC, srcAddr))
}
}
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE)
@ -1151,7 +1478,8 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
msgs, from, err := s.Receive()
if err != nil {
if cberr != nil {
cberr(err)
cberr(fmt.Errorf("Receive failed: %v",
err))
}
return
}
@ -1166,22 +1494,22 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
cberr(fmt.Errorf("error message: %v",
syscall.Errno(-error)))
}
return
continue
}
route, err := deserializeRoute(m.Data)
if err != nil {
if cberr != nil {
cberr(err)
}
return
continue
}
ch <- RouteUpdate{Type: m.Header.Type, Route: route}
}
@ -1190,3 +1518,54 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
return nil
}
func (p RouteProtocol) String() string {
switch int(p) {
case unix.RTPROT_BABEL:
return "babel"
case unix.RTPROT_BGP:
return "bgp"
case unix.RTPROT_BIRD:
return "bird"
case unix.RTPROT_BOOT:
return "boot"
case unix.RTPROT_DHCP:
return "dhcp"
case unix.RTPROT_DNROUTED:
return "dnrouted"
case unix.RTPROT_EIGRP:
return "eigrp"
case unix.RTPROT_GATED:
return "gated"
case unix.RTPROT_ISIS:
return "isis"
//case unix.RTPROT_KEEPALIVED:
// return "keepalived"
case unix.RTPROT_KERNEL:
return "kernel"
case unix.RTPROT_MROUTED:
return "mrouted"
case unix.RTPROT_MRT:
return "mrt"
case unix.RTPROT_NTK:
return "ntk"
case unix.RTPROT_OSPF:
return "ospf"
case unix.RTPROT_RA:
return "ra"
case unix.RTPROT_REDIRECT:
return "redirect"
case unix.RTPROT_RIP:
return "rip"
case unix.RTPROT_STATIC:
return "static"
case unix.RTPROT_UNSPEC:
return "unspec"
case unix.RTPROT_XORP:
return "xorp"
case unix.RTPROT_ZEBRA:
return "zebra"
default:
return strconv.Itoa(int(p))
}
}

View File

@ -2,6 +2,8 @@
package netlink
import "strconv"
func (r *Route) ListFlags() []string {
return []string{}
}
@ -9,3 +11,11 @@ func (r *Route) ListFlags() []string {
func (n *NexthopInfo) ListFlags() []string {
return []string{}
}
func (s Scope) String() string {
return "unknown"
}
func (p RouteProtocol) String() string {
return strconv.Itoa(int(p))
}

View File

@ -28,7 +28,18 @@ type Rule struct {
}
func (r Rule) String() string {
return fmt.Sprintf("ip rule %d: from %s table %d", r.Priority, r.Src, r.Table)
from := "all"
if r.Src != nil && r.Src.String() != "<nil>" {
from = r.Src.String()
}
to := "all"
if r.Dst != nil && r.Dst.String() != "<nil>" {
to = r.Dst.String()
}
return fmt.Sprintf("ip rule %d: from %s to %s table %d",
r.Priority, from, to, r.Table)
}
// NewRule return empty rules.

View File

@ -97,8 +97,6 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
req.AddData(rtAttrs[i])
}
native := nl.NativeEndian()
if rule.Priority >= 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Priority))
@ -199,7 +197,6 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
return nil, err
}
native := nl.NativeEndian()
var res = make([]Rule, 0)
for i := range msgs {
msg := nl.DeserializeRtMsg(msgs[i])
@ -232,7 +229,7 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
case nl.FRA_FWMASK:
rule.Mask = int(native.Uint32(attrs[j].Value[0:4]))
case nl.FRA_TUN_ID:
rule.TunID = uint(native.Uint64(attrs[j].Value[0:4]))
rule.TunID = uint(native.Uint64(attrs[j].Value[0:8]))
case nl.FRA_IIFNAME:
rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
case nl.FRA_OIFNAME:

View File

@ -172,35 +172,78 @@ func SocketGet(local, remote net.Addr) (*Socket, error) {
return sock, nil
}
// SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type.
// SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type and return with extension TCP info.
func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
var result []*InetDiagTCPInfoResp
err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error {
sockInfo := &Socket{}
if err := sockInfo.deserialize(m.Data); err != nil {
return err
}
attrs, err := nl.ParseRouteAttr(m.Data[sizeofSocket:])
if err != nil {
return err
}
res, err := attrsToInetDiagTCPInfoResp(attrs, sockInfo)
if err != nil {
return err
}
result = append(result, res)
return nil
})
if err != nil {
return nil, err
}
return result, nil
}
// SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket.
func SocketDiagTCP(family uint8) ([]*Socket, error) {
var result []*Socket
err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error {
sockInfo := &Socket{}
if err := sockInfo.deserialize(m.Data); err != nil {
return err
}
result = append(result, sockInfo)
return nil
})
if err != nil {
return nil, err
}
return result, nil
}
// socketDiagTCPExecutor requests INET_DIAG_INFO for TCP protocol for specified family type.
func socketDiagTCPExecutor(family uint8, receiver func(syscall.NetlinkMessage) error) error {
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
if err != nil {
return err
}
defer s.Close()
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
req.AddData(&socketRequest{
Family: family,
Protocol: unix.IPPROTO_TCP,
Ext: INET_DIAG_INFO,
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
States: uint32(0xfff), // All TCP states
})
s.Send(req)
var result []*InetDiagTCPInfoResp
loop:
for {
msgs, from, err := s.Receive()
if err != nil {
return nil, err
return err
}
if from.Pid != nl.PidKernel {
return nil, fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
return fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
}
if len(msgs) == 0 {
return nil, errors.New("no message nor error from netlink")
return errors.New("no message nor error from netlink")
}
for _, m := range msgs {
@ -208,31 +251,41 @@ loop:
case unix.NLMSG_DONE:
break loop
case unix.NLMSG_ERROR:
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
return nil, syscall.Errno(-error)
return syscall.Errno(-error)
}
sockInfo := &Socket{}
if err := sockInfo.deserialize(m.Data); err != nil {
return nil, err
if err := receiver(m); err != nil {
return err
}
attrs, err := nl.ParseRouteAttr(m.Data[sizeofSocket:])
if err != nil {
return nil, err
}
var tcpInfo *TCPInfo
for _, a := range attrs {
if a.Attr.Type == INET_DIAG_INFO {
tcpInfo = &TCPInfo{}
if err := tcpInfo.deserialize(a.Value); err != nil {
return nil, err
}
break
}
}
r := &InetDiagTCPInfoResp{InetDiagMsg: sockInfo, TCPInfo: tcpInfo}
result = append(result, r)
}
}
return result, nil
return nil
}
func attrsToInetDiagTCPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagTCPInfoResp, error) {
var tcpInfo *TCPInfo
var tcpBBRInfo *TCPBBRInfo
for _, a := range attrs {
if a.Attr.Type == INET_DIAG_INFO {
tcpInfo = &TCPInfo{}
if err := tcpInfo.deserialize(a.Value); err != nil {
return nil, err
}
continue
}
if a.Attr.Type == INET_DIAG_BBRINFO {
tcpBBRInfo = &TCPBBRInfo{}
if err := tcpBBRInfo.deserialize(a.Value); err != nil {
return nil, err
}
continue
}
}
return &InetDiagTCPInfoResp{
InetDiagMsg: sockInfo,
TCPInfo: tcpInfo,
TCPBBRInfo: tcpBBRInfo,
}, nil
}

View File

@ -16,3 +16,69 @@ const (
TCP_NEW_SYN_REC
TCP_MAX_STATES
)
type TCPInfo struct {
State uint8
Ca_state uint8
Retransmits uint8
Probes uint8
Backoff uint8
Options uint8
Snd_wscale uint8 // no uint4
Rcv_wscale uint8
Delivery_rate_app_limited uint8
Fastopen_client_fail uint8
Rto uint32
Ato uint32
Snd_mss uint32
Rcv_mss uint32
Unacked uint32
Sacked uint32
Lost uint32
Retrans uint32
Fackets uint32
Last_data_sent uint32
Last_ack_sent uint32
Last_data_recv uint32
Last_ack_recv uint32
Pmtu uint32
Rcv_ssthresh uint32
Rtt uint32
Rttvar uint32
Snd_ssthresh uint32
Snd_cwnd uint32
Advmss uint32
Reordering uint32
Rcv_rtt uint32
Rcv_space uint32
Total_retrans uint32
Pacing_rate uint64
Max_pacing_rate uint64
Bytes_acked uint64 /* RFC4898 tcpEStatsAppHCThruOctetsAcked */
Bytes_received uint64 /* RFC4898 tcpEStatsAppHCThruOctetsReceived */
Segs_out uint32 /* RFC4898 tcpEStatsPerfSegsOut */
Segs_in uint32 /* RFC4898 tcpEStatsPerfSegsIn */
Notsent_bytes uint32
Min_rtt uint32
Data_segs_in uint32 /* RFC4898 tcpEStatsDataSegsIn */
Data_segs_out uint32 /* RFC4898 tcpEStatsDataSegsOut */
Delivery_rate uint64
Busy_time uint64 /* Time (usec) busy sending data */
Rwnd_limited uint64 /* Time (usec) limited by receive window */
Sndbuf_limited uint64 /* Time (usec) limited by send buffer */
Delivered uint32
Delivered_ce uint32
Bytes_sent uint64 /* RFC4898 tcpEStatsPerfHCDataOctetsOut */
Bytes_retrans uint64 /* RFC4898 tcpEStatsPerfOctetsRetrans */
Dsack_dups uint32 /* RFC4898 tcpEStatsStackDSACKDups */
Reord_seen uint32 /* reordering events seen */
Rcv_ooopack uint32 /* Out-of-order packets received */
Snd_wnd uint32 /* peer's advertised receive window after * scaling (bytes) */
}
type TCPBBRInfo struct {
BBRBW uint64
BBRMinRTT uint32
BBRPacingGain uint32
BBRCwndGain uint32
}

View File

@ -2,67 +2,13 @@ package netlink
import (
"bytes"
"errors"
"io"
)
type TCPInfo struct {
State uint8
Ca_state uint8
Retransmits uint8
Probes uint8
Backoff uint8
Options uint8
Snd_wscale uint8 // no uint4
Rcv_wscale uint8
Delivery_rate_app_limited uint8
Fastopen_client_fail uint8
Rto uint32
Ato uint32
Snd_mss uint32
Rcv_mss uint32
Unacked uint32
Sacked uint32
Lost uint32
Retrans uint32
Fackets uint32
Last_data_sent uint32
Last_ack_sent uint32
Last_data_recv uint32
Last_ack_recv uint32
Pmtu uint32
Rcv_ssthresh uint32
Rtt uint32
Rttvar uint32
Snd_ssthresh uint32
Snd_cwnd uint32
Advmss uint32
Reordering uint32
Rcv_rtt uint32
Rcv_space uint32
Total_retrans uint32
Pacing_rate uint64
Max_pacing_rate uint64
Bytes_acked uint64 /* RFC4898 tcpEStatsAppHCThruOctetsAcked */
Bytes_received uint64 /* RFC4898 tcpEStatsAppHCThruOctetsReceived */
Segs_out uint32 /* RFC4898 tcpEStatsPerfSegsOut */
Segs_in uint32 /* RFC4898 tcpEStatsPerfSegsIn */
Notsent_bytes uint32
Min_rtt uint32
Data_segs_in uint32 /* RFC4898 tcpEStatsDataSegsIn */
Data_segs_out uint32 /* RFC4898 tcpEStatsDataSegsOut */
Delivery_rate uint64
Busy_time uint64 /* Time (usec) busy sending data */
Rwnd_limited uint64 /* Time (usec) limited by receive window */
Sndbuf_limited uint64 /* Time (usec) limited by send buffer */
Delivered uint32
Delivered_ce uint32
Bytes_sent uint64 /* RFC4898 tcpEStatsPerfHCDataOctetsOut */
Bytes_retrans uint64 /* RFC4898 tcpEStatsPerfOctetsRetrans */
Dsack_dups uint32 /* RFC4898 tcpEStatsStackDSACKDups */
Reord_seen uint32 /* reordering events seen */
Rcv_ooopack uint32 /* Out-of-order packets received */
Snd_wnd uint32 /* peer's advertised receive window after * scaling (bytes) */
}
const (
tcpBBRInfoLen = 20
)
func checkDeserErr(err error) error {
if err == io.EOF {
@ -391,3 +337,17 @@ func (t *TCPInfo) deserialize(b []byte) error {
t.Snd_wnd = native.Uint32(next)
return nil
}
func (t *TCPBBRInfo) deserialize(b []byte) error {
if len(b) != tcpBBRInfoLen {
return errors.New("Invalid length")
}
rb := bytes.NewBuffer(b)
t.BBRBW = native.Uint64(rb.Next(8))
t.BBRMinRTT = native.Uint32(rb.Next(4))
t.BBRPacingGain = native.Uint32(rb.Next(4))
t.BBRCwndGain = native.Uint32(rb.Next(4))
return nil
}

View File

@ -58,12 +58,13 @@ func (a PolicyAction) String() string {
// policy. These rules are matched with XfrmState to determine encryption
// and authentication algorithms.
type XfrmPolicyTmpl struct {
Dst net.IP
Src net.IP
Proto Proto
Mode Mode
Spi int
Reqid int
Dst net.IP
Src net.IP
Proto Proto
Mode Mode
Spi int
Reqid int
Optional int
}
func (t XfrmPolicyTmpl) String() string {

View File

@ -79,6 +79,7 @@ func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error {
userTmpl.XfrmId.Spi = nl.Swap32(uint32(tmpl.Spi))
userTmpl.Mode = uint8(tmpl.Mode)
userTmpl.Reqid = uint32(tmpl.Reqid)
userTmpl.Optional = uint8(tmpl.Optional)
userTmpl.Aalgos = ^uint32(0)
userTmpl.Ealgos = ^uint32(0)
userTmpl.Calgos = ^uint32(0)
@ -247,6 +248,7 @@ func parseXfrmPolicy(m []byte, family int) (*XfrmPolicy, error) {
resTmpl.Mode = Mode(tmpl.Mode)
resTmpl.Spi = int(nl.Swap32(tmpl.XfrmId.Spi))
resTmpl.Reqid = int(tmpl.Reqid)
resTmpl.Optional = int(tmpl.Optional)
policy.Tmpls = append(policy.Tmpls, resTmpl)
}
case nl.XFRMA_MARK:

View File

@ -94,7 +94,7 @@ type XfrmState struct {
Limits XfrmStateLimits
Statistics XfrmStateStats
Mark *XfrmMark
OutputMark int
OutputMark *XfrmMark
Ifid int
Auth *XfrmStateAlgo
Crypt *XfrmStateAlgo
@ -104,7 +104,7 @@ type XfrmState struct {
}
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: %d, Ifid: %d, Auth: %v, Crypt: %v, Aead: %v, Encap: %v, ESN: %t",
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",
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)
}
func (sa XfrmState) Print(stats bool) string {

View File

@ -111,7 +111,7 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
// A state with spi 0 can't be deleted so don't allow it to be set
if state.Spi == 0 {
return fmt.Errorf("Spi must be set when adding xfrm state.")
return fmt.Errorf("Spi must be set when adding xfrm state")
}
req := h.newNetlinkRequest(nlProto, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
@ -158,9 +158,13 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
out := nl.NewRtAttr(nl.XFRMA_REPLAY_ESN_VAL, writeReplayEsn(state.ReplayWindow))
req.AddData(out)
}
if state.OutputMark != 0 {
out := nl.NewRtAttr(nl.XFRMA_OUTPUT_MARK, nl.Uint32Attr(uint32(state.OutputMark)))
if state.OutputMark != nil {
out := nl.NewRtAttr(nl.XFRMA_SET_MARK, nl.Uint32Attr(state.OutputMark.Value))
req.AddData(out)
if state.OutputMark.Mask != 0 {
out = nl.NewRtAttr(nl.XFRMA_SET_MARK_MASK, nl.Uint32Attr(state.OutputMark.Mask))
req.AddData(out)
}
}
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
@ -377,8 +381,19 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
state.Mark = new(XfrmMark)
state.Mark.Value = mark.Value
state.Mark.Mask = mark.Mask
case nl.XFRMA_OUTPUT_MARK:
state.OutputMark = int(native.Uint32(attr.Value))
case nl.XFRMA_SET_MARK:
if state.OutputMark == nil {
state.OutputMark = new(XfrmMark)
}
state.OutputMark.Value = native.Uint32(attr.Value)
case nl.XFRMA_SET_MARK_MASK:
if state.OutputMark == nil {
state.OutputMark = new(XfrmMark)
}
state.OutputMark.Mask = native.Uint32(attr.Value)
if state.OutputMark.Mask == 0xffffffff {
state.OutputMark.Mask = 0
}
case nl.XFRMA_IF_ID:
state.Ifid = int(native.Uint32(attr.Value))
}

View File

@ -256,7 +256,7 @@ github.com/stretchr/testify/assert
# github.com/urfave/cli v1.22.2
## explicit
github.com/urfave/cli
# github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
# github.com/vishvananda/netlink v1.1.1-0.20210924202909-187053b97868
## explicit
github.com/vishvananda/netlink
github.com/vishvananda/netlink/nl

View File

@ -575,7 +575,7 @@ func tapNetworkPair(ctx context.Context, endpoint Endpoint, queues int, disableV
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
netPair := endpoint.NetworkPair()
@ -672,7 +672,7 @@ func setupTCFiltering(ctx context.Context, endpoint Endpoint, queues int, disabl
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
netPair := endpoint.NetworkPair()
@ -847,7 +847,7 @@ func untapNetworkPair(ctx context.Context, endpoint Endpoint) error {
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
netPair := endpoint.NetworkPair()
@ -891,7 +891,7 @@ func removeTCFiltering(ctx context.Context, endpoint Endpoint) error {
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
netPair := endpoint.NetworkPair()
@ -1168,7 +1168,7 @@ func createEndpointsFromScan(networkNSPath string, config *NetworkConfig) ([]End
if err != nil {
return []Endpoint{}, err
}
defer netlinkHandle.Delete()
defer netlinkHandle.Close()
linkList, err := netlinkHandle.LinkList()
if err != nil {
@ -1568,7 +1568,7 @@ func addIFBDevice() (int, error) {
if err != nil {
return -1, err
}
defer netHandle.Delete()
defer netHandle.Close()
// There exists error when using netlink library to create ifb interface
cmd := exec.Command("ip", "link", "add", "dev", "ifb0", "type", "ifb")
@ -1744,7 +1744,7 @@ func removeTxRateLimiter(endpoint Endpoint, networkNSPath string) error {
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
// remove ifb interface
ifbLink, err := netlink.LinkByName("ifb0")

View File

@ -188,7 +188,8 @@ func TestCreateGetTunTapLink(t *testing.T) {
assert := assert.New(t)
netHandle, err := netlink.NewHandle()
defer netHandle.Delete()
assert.NoError(err)
defer netHandle.Close()
assert.NoError(err)
@ -213,7 +214,8 @@ func TestCreateMacVtap(t *testing.T) {
assert := assert.New(t)
netHandle, err := netlink.NewHandle()
defer netHandle.Delete()
assert.NoError(err)
defer netHandle.Close()
assert.NoError(err)
@ -258,7 +260,7 @@ func TestTcRedirectNetwork(t *testing.T) {
netHandle, err := netlink.NewHandle()
assert.NoError(err)
defer netHandle.Delete()
defer netHandle.Close()
// Create a test veth interface.
vethName := "foo"
@ -296,7 +298,7 @@ func TestRxRateLimiter(t *testing.T) {
netHandle, err := netlink.NewHandle()
assert.NoError(err)
defer netHandle.Delete()
defer netHandle.Close()
// Create a test veth interface.
vethName := "foo"
@ -345,7 +347,7 @@ func TestTxRateLimiter(t *testing.T) {
netHandle, err := netlink.NewHandle()
assert.NoError(err)
defer netHandle.Delete()
defer netHandle.Close()
// Create a test veth interface.
vethName := "foo"

View File

@ -77,7 +77,7 @@ func TestIsPhysicalIface(t *testing.T) {
netlinkHandle, err := netlink.NewHandleAt(netnsHandle)
assert.NoError(err)
defer netlinkHandle.Delete()
defer netlinkHandle.Close()
err = netlinkHandle.LinkAdd(link)
assert.NoError(err)

View File

@ -156,7 +156,7 @@ func tapNetwork(endpoint *TapEndpoint, numCPUs uint32, disableVhostNet bool) err
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
tapLink, fds, err := createLink(netHandle, endpoint.TapInterface.TAPIface.Name, &netlink.Tuntap{}, int(numCPUs))
if err != nil {
@ -192,7 +192,7 @@ func unTapNetwork(name string) error {
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
tapLink, err := getLinkByName(netHandle, name, &netlink.Tuntap{})
if err != nil {
return fmt.Errorf("Could not get TAP interface: %s", err)

View File

@ -173,7 +173,7 @@ func tuntapNetwork(endpoint *TuntapEndpoint, numCPUs uint32, disableVhostNet boo
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
tapLink, _, err := createLink(netHandle, endpoint.TuntapInterface.TAPIface.Name, &netlink.Tuntap{}, int(numCPUs))
if err != nil {
@ -201,7 +201,7 @@ func unTuntapNetwork(name string) error {
if err != nil {
return err
}
defer netHandle.Delete()
defer netHandle.Close()
tapLink, err := getLinkByName(netHandle, name, &netlink.Tuntap{})
if err != nil {
return fmt.Errorf("Could not get TAP interface: %s", err)