Merge pull request #735 from martinetd/crash-LTE

Update github.com/vishvananda/netlink to v1.2.0-beta
This commit is contained in:
Dan Williams 2022-04-27 11:02:30 -05:00 committed by GitHub
commit 6264f7bff9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 2155 additions and 332 deletions

2
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/onsi/ginkgo v1.16.4 github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.15.0 github.com/onsi/gomega v1.15.0
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1 github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 github.com/vishvananda/netlink v1.2.0-beta
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e
) )

4
go.sum
View File

@ -582,8 +582,8 @@ github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 h1:+UB2BJA852UkGH42H+Oee69djmxS3ANzl2b/JtT1YiA= github.com/vishvananda/netlink v1.2.0-beta h1:CTNzkunO9iTkRaupF540+w47mexyQgNkA/ibnuKc39w=
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.2.0-beta/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=

View File

@ -1,20 +0,0 @@
language: go
go:
- "1.12.x"
- "1.13.x"
- "1.14.x"
before_script:
# make sure we keep path in tact when we sudo
- sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
# modprobe ip_gre or else the first gre device can't be deleted
- sudo modprobe ip_gre
# modprobe nf_conntrack for the conntrack testing
- sudo modprobe nf_conntrack
- sudo modprobe nf_conntrack_netlink
- sudo modprobe nf_conntrack_ipv4
- sudo modprobe nf_conntrack_ipv6
- sudo modprobe sch_hfsc
- sudo modprobe sch_sfq
install:
- go get -v -t ./...
go_import_path: github.com/vishvananda/netlink

View File

@ -1,6 +1,6 @@
# netlink - netlink library for go # # 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://github.com/vishvananda/netlink/actions/workflows/main.yml/badge.svg) [![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 The netlink package provides a simple netlink library for go. Netlink
is the interface a user-space program in linux uses to communicate with is the interface a user-space program in linux uses to communicate with

View File

@ -176,7 +176,7 @@ func AddrList(link Link, family int) ([]Addr, error) {
// The list can be filtered by link and ip family. // The list can be filtered by link and ip family.
func (h *Handle) AddrList(link Link, family int) ([]Addr, error) { func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(family) msg := nl.NewIfAddrmsg(family)
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
@ -268,7 +268,7 @@ func parseAddr(m []byte) (addr Addr, family int, err error) {
// But obviously, as there are IPv6 PtP addresses, too, // But obviously, as there are IPv6 PtP addresses, too,
// IFA_LOCAL should also be handled for IPv6. // IFA_LOCAL should also be handled for IPv6.
if local != nil { 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 addr.IPNet = dst
} else { } else {
addr.IPNet = local addr.IPNet = local
@ -296,13 +296,13 @@ type AddrUpdate struct {
// AddrSubscribe takes a chan down which notifications will be sent // AddrSubscribe takes a chan down which notifications will be sent
// when addresses change. Close the 'done' chan to stop subscription. // when addresses change. Close the 'done' chan to stop subscription.
func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0) return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0, nil)
} }
// AddrSubscribeAt works like AddrSubscribe plus it allows the caller // AddrSubscribeAt works like AddrSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns). // to choose the network namespace in which to subscribe (ns).
func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
return addrSubscribeAt(ns, netns.None(), ch, done, nil, false, 0) return addrSubscribeAt(ns, netns.None(), ch, done, nil, false, 0, nil)
} }
// AddrSubscribeOptions contains a set of options to use with // AddrSubscribeOptions contains a set of options to use with
@ -312,6 +312,7 @@ type AddrSubscribeOptions struct {
ErrorCallback func(error) ErrorCallback func(error)
ListExisting bool ListExisting bool
ReceiveBufferSize int ReceiveBufferSize int
ReceiveTimeout *unix.Timeval
} }
// AddrSubscribeWithOptions work like AddrSubscribe but enable to // AddrSubscribeWithOptions work like AddrSubscribe but enable to
@ -322,14 +323,20 @@ func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, option
none := netns.None() none := netns.None()
options.Namespace = &none options.Namespace = &none
} }
return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting, options.ReceiveBufferSize) return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting, options.ReceiveBufferSize, options.ReceiveTimeout)
} }
func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool, rcvbuf int) error { func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool, rcvbuf int, rcvTimeout *unix.Timeval) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR) s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR)
if err != nil { if err != nil {
return err return err
} }
if rcvTimeout != nil {
if err := s.SetReceiveTimeout(rcvTimeout); err != nil {
return err
}
}
if done != nil { if done != nil {
go func() { go func() {
<-done <-done
@ -357,7 +364,8 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
if err != nil { if err != nil {
if cberr != nil { if cberr != nil {
cberr(err) cberr(fmt.Errorf("Receive failed: %v",
err))
} }
return return
} }
@ -372,7 +380,6 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
continue continue
} }
if m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if error == 0 {
continue continue

View File

@ -16,6 +16,30 @@ const (
BPF_PROG_TYPE_SCHED_ACT BPF_PROG_TYPE_SCHED_ACT
BPF_PROG_TYPE_TRACEPOINT BPF_PROG_TYPE_TRACEPOINT
BPF_PROG_TYPE_XDP 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 { type BPFAttr struct {

View File

@ -191,9 +191,9 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
opt.Fsc.Set(fm1/8, fd, fm2/8) opt.Fsc.Set(fm1/8, fd, fm2/8)
um1, ud, um2 := hfsc.Usc.Attrs() um1, ud, um2 := hfsc.Usc.Attrs()
opt.Usc.Set(um1/8, ud, um2/8) opt.Usc.Set(um1/8, ud, um2/8)
nl.NewRtAttrChild(options, nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc)) options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
nl.NewRtAttrChild(options, nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc)) options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
nl.NewRtAttrChild(options, nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc)) options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
} }
req.AddData(options) req.AddData(options)
return nil return nil
@ -341,7 +341,6 @@ func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, err
func parseTcStats(data []byte) (*ClassStatistics, error) { func parseTcStats(data []byte) (*ClassStatistics, error) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.Write(data) buf.Write(data)
native := nl.NativeEndian()
tcStats := &tcStats{} tcStats := &tcStats{}
if err := binary.Read(buf, native, tcStats); err != nil { if err := binary.Read(buf, native, tcStats); err != nil {
return nil, err return nil, err
@ -363,7 +362,6 @@ func parseTcStats(data []byte) (*ClassStatistics, error) {
func parseGnetStats(data []byte, gnetStats interface{}) error { func parseGnetStats(data []byte, gnetStats interface{}) error {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.Write(data) buf.Write(data)
native := nl.NativeEndian()
return binary.Read(buf, native, gnetStats) return binary.Read(buf, native, gnetStats)
} }

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"time"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -145,16 +146,23 @@ type ConntrackFlow struct {
Forward ipTuple Forward ipTuple
Reverse ipTuple Reverse ipTuple
Mark uint32 Mark uint32
TimeStart uint64
TimeStop uint64
TimeOut uint32
} }
func (s *ConntrackFlow) String() string { func (s *ConntrackFlow) String() string {
// conntrack cmd output: // 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 // 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, 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.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.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 // This method parse the ip tuple structure
@ -174,25 +182,43 @@ func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 {
tpl.DstIP = v tpl.DstIP = v
} }
} }
// Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO // Get total length of nested protocol-specific info.
reader.Seek(4, seekCurrent) _, _, protoInfoTotalLen := parseNfAttrTL(reader)
_, t, _, v := parseNfAttrTLV(reader) _, t, l, v := parseNfAttrTLV(reader)
// Track the number of bytes read.
protoInfoBytesRead := uint16(nl.SizeofNfattr) + l
if t == nl.CTA_PROTO_NUM { if t == nl.CTA_PROTO_NUM {
tpl.Protocol = uint8(v[0]) 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) reader.Seek(3, seekCurrent)
protoInfoBytesRead += 3
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
_, t, _ := parseNfAttrTL(reader) _, t, _ := parseNfAttrTL(reader)
protoInfoBytesRead += uint16(nl.SizeofNfattr)
switch t { switch t {
case nl.CTA_PROTO_SRC_PORT: case nl.CTA_PROTO_SRC_PORT:
parseBERaw16(reader, &tpl.SrcPort) parseBERaw16(reader, &tpl.SrcPort)
protoInfoBytesRead += 2
case nl.CTA_PROTO_DST_PORT: case nl.CTA_PROTO_DST_PORT:
parseBERaw16(reader, &tpl.DstPort) parseBERaw16(reader, &tpl.DstPort)
protoInfoBytesRead += 2
} }
// Skip some padding 2 byte // Skip 2 bytes of padding
reader.Seek(2, seekCurrent) 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 return tpl.Protocol
} }
@ -211,10 +237,14 @@ func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) {
binary.Read(r, nl.NativeEndian(), &attrType) binary.Read(r, nl.NativeEndian(), &attrType)
isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED
attrType = attrType & (nl.NLA_F_NESTED - 1) attrType = attrType & (nl.NLA_F_NESTED - 1)
return isNested, attrType, len 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) { func parseBERaw16(r *bytes.Reader, v *uint16) {
binary.Read(r, binary.BigEndian, v) binary.Read(r, binary.BigEndian, v)
} }
@ -241,6 +271,36 @@ func parseByteAndPacketCounters(r *bytes.Reader) (bytes, packets uint64) {
return 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) { func parseConnectionMark(r *bytes.Reader) (mark uint32) {
parseBERaw32(r, &mark) parseBERaw32(r, &mark)
return return
@ -266,25 +326,37 @@ func parseRawData(data []byte) *ConntrackFlow {
if nested, t, l := parseNfAttrTL(reader); nested { if nested, t, l := parseNfAttrTL(reader); nested {
switch t { switch t {
case nl.CTA_TUPLE_ORIG: 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) parseIpTuple(reader, &s.Forward)
} }
case nl.CTA_TUPLE_REPLY: 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) parseIpTuple(reader, &s.Reverse)
} else { } else {
// Header not recognized skip it // Header not recognized skip it
reader.Seek(int64(l), seekCurrent) skipNfAttrValue(reader, l)
} }
case nl.CTA_COUNTERS_ORIG: case nl.CTA_COUNTERS_ORIG:
s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader) s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader)
case nl.CTA_COUNTERS_REPLY: case nl.CTA_COUNTERS_REPLY:
s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader) 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 { } else {
switch t { switch t {
case nl.CTA_MARK: case nl.CTA_MARK:
s.Mark = parseConnectionMark(reader) 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 { type ConntrackFilter struct {
ipFilter map[ConntrackFilterType]net.IP ipNetFilter map[ConntrackFilterType]*net.IPNet
portFilter map[ConntrackFilterType]uint16 portFilter map[ConntrackFilterType]uint16
protoFilter uint8 protoFilter uint8
} }
// AddIP adds an IP to the conntrack filter // AddIPNet adds a IP subnet to the conntrack filter
func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error { func (f *ConntrackFilter) AddIPNet(tp ConntrackFilterType, ipNet *net.IPNet) error {
if f.ipFilter == nil { if ipNet == nil {
f.ipFilter = make(map[ConntrackFilterType]net.IP) 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") return errors.New("Filter attribute already present")
} }
f.ipFilter[tp] = ip f.ipNetFilter[tp] = ipNet
return nil 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 // AddPort adds a Port to the conntrack filter if the Layer 4 protocol allows it
func (f *ConntrackFilter) AddPort(tp ConntrackFilterType, port uint16) error { func (f *ConntrackFilter) AddPort(tp ConntrackFilterType, port uint16) error {
switch f.protoFilter { 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 // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
// false otherwise // false otherwise
func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool { 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 // empty filter always not match
return false return false
} }
@ -408,30 +491,30 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
match := true match := true
// IP conntrack filter // IP conntrack filter
if len(f.ipFilter) > 0 { if len(f.ipNetFilter) > 0 {
// -orig-src ip Source address from original direction // -orig-src ip Source address from original direction
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found { if elem, found := f.ipNetFilter[ConntrackOrigSrcIP]; found {
match = match && elem.Equal(flow.Forward.SrcIP) match = match && elem.Contains(flow.Forward.SrcIP)
} }
// -orig-dst ip Destination address from original direction // -orig-dst ip Destination address from original direction
if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found { if elem, found := f.ipNetFilter[ConntrackOrigDstIP]; match && found {
match = match && elem.Equal(flow.Forward.DstIP) match = match && elem.Contains(flow.Forward.DstIP)
} }
// -src-nat ip Source NAT ip // -src-nat ip Source NAT ip
if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found { if elem, found := f.ipNetFilter[ConntrackReplySrcIP]; match && found {
match = match && elem.Equal(flow.Reverse.SrcIP) match = match && elem.Contains(flow.Reverse.SrcIP)
} }
// -dst-nat ip Destination NAT ip // -dst-nat ip Destination NAT ip
if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found { if elem, found := f.ipNetFilter[ConntrackReplyDstIP]; match && found {
match = match && elem.Equal(flow.Reverse.DstIP) match = match && elem.Contains(flow.Reverse.DstIP)
} }
// Match source or destination reply IP // Match source or destination reply IP
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found { if elem, found := f.ipNetFilter[ConntrackReplyAnyIP]; match && found {
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP)) match = match && (elem.Contains(flow.Reverse.SrcIP) || elem.Contains(flow.Reverse.DstIP))
} }
} }

View File

@ -1,9 +1,11 @@
package netlink package netlink
import ( import (
"fmt"
"net"
"strings"
"syscall" "syscall"
"fmt"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -27,6 +29,20 @@ type DevlinkDevice struct {
Attrs DevlinkDevAttrs 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 // DevlinkPort represents port and its attributes
type DevlinkPort struct { type DevlinkPort struct {
BusName string BusName string
@ -37,6 +53,35 @@ type DevlinkPort struct {
NetdevIfIndex uint32 NetdevIfIndex uint32
RdmaDeviceName string RdmaDeviceName string
PortFlavour uint16 PortFlavour uint16
Fn *DevlinkPortFn
}
type DevLinkPortAddAttrs struct {
Controller uint32
SfNumber uint32
PortIndex uint32
PfNumber uint16
SfNumberValid bool
PortIndexValid bool
ControllerValid bool
}
// DevlinkDeviceInfo represents devlink info
type DevlinkDeviceInfo struct {
Driver string
SerialNumber string
BoardID string
FwApp string
FwAppBoundleID string
FwAppName string
FwBoundleID string
FwMgmt string
FwMgmtAPI string
FwMgmtBuild string
FwNetlist string
FwNetlistBuild string
FwPsidAPI string
FwUndi string
} }
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) { func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
@ -107,9 +152,9 @@ func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error
for _, a := range attrs { for _, a := range attrs {
switch a.Attr.Type { switch a.Attr.Type {
case nl.DEVLINK_ATTR_BUS_NAME: 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: 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: case nl.DEVLINK_ATTR_ESWITCH_MODE:
d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value)) d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value))
case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE: case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE:
@ -138,12 +183,12 @@ func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK) req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK)
req.AddData(msg) req.AddData(msg)
b := make([]byte, len(dev.BusName)) b := make([]byte, len(dev.BusName)+1)
copy(b, dev.BusName) copy(b, dev.BusName)
data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
req.AddData(data) req.AddData(data)
b = make([]byte, len(dev.DeviceName)) b = make([]byte, len(dev.DeviceName)+1)
copy(b, dev.DeviceName) copy(b, dev.DeviceName)
data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
req.AddData(data) req.AddData(data)
@ -287,21 +332,33 @@ func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error
for _, a := range attrs { for _, a := range attrs {
switch a.Attr.Type { switch a.Attr.Type {
case nl.DEVLINK_ATTR_BUS_NAME: case nl.DEVLINK_ATTR_BUS_NAME:
port.BusName = string(a.Value) port.BusName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_DEV_NAME: case nl.DEVLINK_ATTR_DEV_NAME:
port.DeviceName = string(a.Value) port.DeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_INDEX: case nl.DEVLINK_ATTR_PORT_INDEX:
port.PortIndex = native.Uint32(a.Value) port.PortIndex = native.Uint32(a.Value)
case nl.DEVLINK_ATTR_PORT_TYPE: case nl.DEVLINK_ATTR_PORT_TYPE:
port.PortType = native.Uint16(a.Value) port.PortType = native.Uint16(a.Value)
case nl.DEVLINK_ATTR_PORT_NETDEV_NAME: case nl.DEVLINK_ATTR_PORT_NETDEV_NAME:
port.NetdeviceName = string(a.Value) port.NetdeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX: case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX:
port.NetdevIfIndex = native.Uint32(a.Value) port.NetdevIfIndex = native.Uint32(a.Value)
case nl.DEVLINK_ATTR_PORT_IBDEV_NAME: case nl.DEVLINK_ATTR_PORT_IBDEV_NAME:
port.RdmaDeviceName = string(a.Value) port.RdmaDeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_FLAVOUR: case nl.DEVLINK_ATTR_PORT_FLAVOUR:
port.PortFlavour = native.Uint16(a.Value) 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 return nil
@ -391,3 +448,281 @@ func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint
func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex) 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)
}
// devlinkInfoGetter is function that is responsible for getting devlink info message
// this is introduced for test purpose
type devlinkInfoGetter func(bus, device string) ([]byte, error)
// DevlinkGetDeviceInfoByName returns devlink info for selected device,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func (h *Handle) DevlinkGetDeviceInfoByName(Bus string, Device string, getInfoMsg devlinkInfoGetter) (*DevlinkDeviceInfo, error) {
info, err := h.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, getInfoMsg)
if err != nil {
return nil, err
}
return parseInfoData(info), nil
}
// DevlinkGetDeviceInfoByName returns devlink info for selected device,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func DevlinkGetDeviceInfoByName(Bus string, Device string) (*DevlinkDeviceInfo, error) {
return pkgHandle.DevlinkGetDeviceInfoByName(Bus, Device, pkgHandle.getDevlinkInfoMsg)
}
// DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func (h *Handle) DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string, getInfoMsg devlinkInfoGetter) (map[string]string, error) {
response, err := getInfoMsg(Bus, Device)
if err != nil {
return nil, err
}
info, err := parseInfoMsg(response)
if err != nil {
return nil, err
}
return info, nil
}
// DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string) (map[string]string, error) {
return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, pkgHandle.getDevlinkInfoMsg)
}
// GetDevlinkInfo returns devlink info for target device,
// otherwise returns an error code.
func (d *DevlinkDevice) GetDevlinkInfo() (*DevlinkDeviceInfo, error) {
return pkgHandle.DevlinkGetDeviceInfoByName(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
}
// GetDevlinkInfoAsMap returns devlink info for target device as a map,
// otherwise returns an error code.
func (d *DevlinkDevice) GetDevlinkInfoAsMap() (map[string]string, error) {
return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
}
func (h *Handle) getDevlinkInfoMsg(bus, device string) ([]byte, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_INFO_GET, bus, device)
if err != nil {
return nil, err
}
response, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
if len(response) < 1 {
return nil, fmt.Errorf("getDevlinkInfoMsg: message too short")
}
return response[0], nil
}
func parseInfoMsg(msg []byte) (map[string]string, error) {
if len(msg) < nl.SizeofGenlmsg {
return nil, fmt.Errorf("parseInfoMsg: message too short")
}
info := make(map[string]string)
err := collectInfoData(msg[nl.SizeofGenlmsg:], info)
if err != nil {
return nil, err
}
return info, nil
}
func collectInfoData(msg []byte, data map[string]string) error {
attrs, err := nl.ParseRouteAttr(msg)
if err != nil {
return err
}
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.DEVLINK_ATTR_INFO_DRIVER_NAME:
data["driver"] = parseInfoValue(attr.Value)
case nl.DEVLINK_ATTR_INFO_SERIAL_NUMBER:
data["serialNumber"] = parseInfoValue(attr.Value)
case nl.DEVLINK_ATTR_INFO_VERSION_RUNNING, nl.DEVLINK_ATTR_INFO_VERSION_FIXED,
nl.DEVLINK_ATTR_INFO_VERSION_STORED:
key, value, err := getNestedInfoData(attr.Value)
if err != nil {
return err
}
data[key] = value
}
}
if len(data) == 0 {
return fmt.Errorf("collectInfoData: could not read attributes")
}
return nil
}
func getNestedInfoData(msg []byte) (string, string, error) {
nestedAttrs, err := nl.ParseRouteAttr(msg)
var key, value string
if err != nil {
return "", "", err
}
if len(nestedAttrs) != 2 {
return "", "", fmt.Errorf("getNestedInfoData: too few attributes in nested structure")
}
for _, nestedAttr := range nestedAttrs {
switch nestedAttr.Attr.Type {
case nl.DEVLINK_ATTR_INFO_VERSION_NAME:
key = parseInfoValue(nestedAttr.Value)
case nl.DEVLINK_ATTR_INFO_VERSION_VALUE:
value = parseInfoValue(nestedAttr.Value)
}
}
if key == "" {
return "", "", fmt.Errorf("getNestedInfoData: key not found")
}
if value == "" {
return "", "", fmt.Errorf("getNestedInfoData: value not found")
}
return key, value, nil
}
func parseInfoData(data map[string]string) *DevlinkDeviceInfo {
info := new(DevlinkDeviceInfo)
for key, value := range data {
switch key {
case "driver":
info.Driver = value
case "serialNumber":
info.SerialNumber = value
case "board.id":
info.BoardID = value
case "fw.app":
info.FwApp = value
case "fw.app.bundle_id":
info.FwAppBoundleID = value
case "fw.app.name":
info.FwAppName = value
case "fw.bundle_id":
info.FwBoundleID = value
case "fw.mgmt":
info.FwMgmt = value
case "fw.mgmt.api":
info.FwMgmtAPI = value
case "fw.mgmt.build":
info.FwMgmtBuild = value
case "fw.netlist":
info.FwNetlist = value
case "fw.netlist.build":
info.FwNetlistBuild = value
case "fw.psid.api":
info.FwPsidAPI = value
case "fw.undi":
info.FwUndi = value
}
}
return info
}
func parseInfoValue(value []byte) string {
v := strings.ReplaceAll(string(value), "\x00", "")
return strings.TrimSpace(v)
}

View File

@ -260,6 +260,40 @@ func NewSkbEditAction() *SkbEditAction {
} }
} }
type PoliceAction struct {
ActionAttrs
Rate uint32 // in byte per second
Burst uint32 // in byte
RCellLog int
Mtu uint32
Mpu uint16 // in byte
PeakRate uint32 // in byte per second
PCellLog int
AvRate uint32 // in byte per second
Overhead uint16
LinkLayer int
ExceedAction TcPolAct
NotExceedAction TcPolAct
}
func (action *PoliceAction) Type() string {
return "police"
}
func (action *PoliceAction) Attrs() *ActionAttrs {
return &action.ActionAttrs
}
func NewPoliceAction() *PoliceAction {
return &PoliceAction{
RCellLog: -1,
PCellLog: -1,
LinkLayer: 1, // ETHERNET
ExceedAction: TC_POLICE_RECLASSIFY,
NotExceedAction: TC_POLICE_OK,
}
}
// MatchAll filters match all packets // MatchAll filters match all packets
type MatchAll struct { type MatchAll struct {
FilterAttrs FilterAttrs
@ -275,20 +309,20 @@ func (filter *MatchAll) Type() string {
return "matchall" return "matchall"
} }
type FilterFwAttrs struct { type FwFilter struct {
ClassId uint32 FilterAttrs
InDev string ClassId uint32
Mask uint32 InDev string
Index uint32 Mask uint32
Buffer uint32 Police *PoliceAction
Mtu uint32 }
Mpu uint16
Rate uint32 func (filter *FwFilter) Attrs() *FilterAttrs {
AvRate uint32 return &filter.FilterAttrs
PeakRate uint32 }
Action TcPolAct
Overhead uint16 func (filter *FwFilter) Type() string {
LinkLayer int return "fw"
} }
type BpfFilter struct { type BpfFilter struct {

View File

@ -6,6 +6,7 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"net"
"syscall" "syscall"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
@ -50,74 +51,129 @@ func (filter *U32) Type() string {
return "u32" return "u32"
} }
// Fw filter filters on firewall marks type Flower struct {
// NOTE: this is in filter_linux because it refers to nl.TcPolice which
// is defined in nl/tc_linux.go
type Fw struct {
FilterAttrs FilterAttrs
ClassId uint32 DestIP net.IP
// TODO remove nl type from interface DestIPMask net.IPMask
Police nl.TcPolice SrcIP net.IP
InDev string SrcIPMask net.IPMask
// TODO Action EthType uint16
Mask uint32 EncDestIP net.IP
AvRate uint32 EncDestIPMask net.IPMask
Rtab [256]uint32 EncSrcIP net.IP
Ptab [256]uint32 EncSrcIPMask net.IPMask
EncDestPort uint16
EncKeyId uint32
Actions []Action
} }
func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) { func (filter *Flower) Attrs() *FilterAttrs {
var rtab [256]uint32
var ptab [256]uint32
rcellLog := -1
pcellLog := -1
avrate := fattrs.AvRate / 8
police := nl.TcPolice{}
police.Rate.Rate = fattrs.Rate / 8
police.PeakRate.Rate = fattrs.PeakRate / 8
buffer := fattrs.Buffer
linklayer := nl.LINKLAYER_ETHERNET
if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC {
linklayer = fattrs.LinkLayer
}
police.Action = int32(fattrs.Action)
if police.Rate.Rate != 0 {
police.Rate.Mpu = fattrs.Mpu
police.Rate.Overhead = fattrs.Overhead
if CalcRtable(&police.Rate, rtab[:], rcellLog, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("TBF: failed to calculate rate table")
}
police.Burst = Xmittime(uint64(police.Rate.Rate), uint32(buffer))
}
police.Mtu = fattrs.Mtu
if police.PeakRate.Rate != 0 {
police.PeakRate.Mpu = fattrs.Mpu
police.PeakRate.Overhead = fattrs.Overhead
if CalcRtable(&police.PeakRate, ptab[:], pcellLog, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("POLICE: failed to calculate peak rate table")
}
}
return &Fw{
FilterAttrs: attrs,
ClassId: fattrs.ClassId,
InDev: fattrs.InDev,
Mask: fattrs.Mask,
Police: police,
AvRate: avrate,
Rtab: rtab,
Ptab: ptab,
}, nil
}
func (filter *Fw) Attrs() *FilterAttrs {
return &filter.FilterAttrs return &filter.FilterAttrs
} }
func (filter *Fw) Type() string { func (filter *Flower) Type() string {
return "fw" 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. // FilterDel will delete a filter from the system.
@ -169,7 +225,6 @@ func (h *Handle) FilterReplace(filter Filter) error {
} }
func (h *Handle) filterModify(filter Filter, flags int) error { func (h *Handle) filterModify(filter Filter, flags int) error {
native = nl.NativeEndian()
req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK) req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK)
base := filter.Attrs() base := filter.Attrs()
msg := &nl.TcMsg{ msg := &nl.TcMsg{
@ -237,7 +292,7 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if err := EncodeActions(actionsAttr, filter.Actions); err != nil { if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err return err
} }
case *Fw: case *FwFilter:
if filter.Mask != 0 { if filter.Mask != 0 {
b := make([]byte, 4) b := make([]byte, 4)
native.PutUint32(b, filter.Mask) native.PutUint32(b, filter.Mask)
@ -246,17 +301,10 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if filter.InDev != "" { if filter.InDev != "" {
options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev)) options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev))
} }
if (filter.Police != nl.TcPolice{}) { if filter.Police != nil {
police := options.AddRtAttr(nl.TCA_FW_POLICE, nil) police := options.AddRtAttr(nl.TCA_FW_POLICE, nil)
police.AddRtAttr(nl.TCA_POLICE_TBF, filter.Police.Serialize()) if err := encodePolice(police, filter.Police); err != nil {
if (filter.Police.Rate != nl.TcRateSpec{}) { return err
payload := SerializeRtab(filter.Rtab)
police.AddRtAttr(nl.TCA_POLICE_RATE, payload)
}
if (filter.Police.PeakRate != nl.TcRateSpec{}) {
payload := SerializeRtab(filter.Ptab)
police.AddRtAttr(nl.TCA_POLICE_PEAKRATE, payload)
} }
} }
if filter.ClassId != 0 { if filter.ClassId != 0 {
@ -287,6 +335,10 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if filter.ClassId != 0 { if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId)) options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId))
} }
case *Flower:
if err := filter.encode(options); err != nil {
return err
}
} }
req.AddData(options) req.AddData(options)
@ -350,11 +402,13 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
case "u32": case "u32":
filter = &U32{} filter = &U32{}
case "fw": case "fw":
filter = &Fw{} filter = &FwFilter{}
case "bpf": case "bpf":
filter = &BpfFilter{} filter = &BpfFilter{}
case "matchall": case "matchall":
filter = &MatchAll{} filter = &MatchAll{}
case "flower":
filter = &Flower{}
default: default:
filter = &GenericFilter{FilterType: filterType} filter = &GenericFilter{FilterType: filterType}
} }
@ -384,6 +438,11 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
case "flower":
detailed, err = parseFlowerData(filter, data)
if err != nil {
return nil, err
}
default: default:
detailed = true detailed = true
} }
@ -415,6 +474,53 @@ func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) {
attrs.Bindcnt = int(tcgen.Bindcnt) attrs.Bindcnt = int(tcgen.Bindcnt)
} }
func encodePolice(attr *nl.RtAttr, action *PoliceAction) error {
var rtab [256]uint32
var ptab [256]uint32
police := nl.TcPolice{}
police.Index = uint32(action.Attrs().Index)
police.Bindcnt = int32(action.Attrs().Bindcnt)
police.Capab = uint32(action.Attrs().Capab)
police.Refcnt = int32(action.Attrs().Refcnt)
police.Rate.Rate = action.Rate
police.PeakRate.Rate = action.PeakRate
police.Action = int32(action.ExceedAction)
if police.Rate.Rate != 0 {
police.Rate.Mpu = action.Mpu
police.Rate.Overhead = action.Overhead
if CalcRtable(&police.Rate, rtab[:], action.RCellLog, action.Mtu, action.LinkLayer) < 0 {
return errors.New("TBF: failed to calculate rate table")
}
police.Burst = Xmittime(uint64(police.Rate.Rate), action.Burst)
}
police.Mtu = action.Mtu
if police.PeakRate.Rate != 0 {
police.PeakRate.Mpu = action.Mpu
police.PeakRate.Overhead = action.Overhead
if CalcRtable(&police.PeakRate, ptab[:], action.PCellLog, action.Mtu, action.LinkLayer) < 0 {
return errors.New("POLICE: failed to calculate peak rate table")
}
}
attr.AddRtAttr(nl.TCA_POLICE_TBF, police.Serialize())
if police.Rate.Rate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_RATE, SerializeRtab(rtab))
}
if police.PeakRate.Rate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_PEAKRATE, SerializeRtab(ptab))
}
if action.AvRate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_AVRATE, nl.Uint32Attr(action.AvRate))
}
if action.NotExceedAction != 0 {
attr.AddRtAttr(nl.TCA_POLICE_RESULT, nl.Uint32Attr(uint32(action.NotExceedAction)))
}
return nil
}
func EncodeActions(attr *nl.RtAttr, actions []Action) error { func EncodeActions(attr *nl.RtAttr, actions []Action) error {
tabIndex := int(nl.TCA_ACT_TAB) tabIndex := int(nl.TCA_ACT_TAB)
@ -422,6 +528,14 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
switch action := action.(type) { switch action := action.(type) {
default: default:
return fmt.Errorf("unknown action type %s", action.Type()) return fmt.Errorf("unknown action type %s", action.Type())
case *PoliceAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("police"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
if err := encodePolice(aopts, action); err != nil {
return err
}
case *MirredAction: case *MirredAction:
table := attr.AddRtAttr(tabIndex, nil) table := attr.AddRtAttr(tabIndex, nil)
tabIndex++ tabIndex++
@ -516,6 +630,29 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
return nil return nil
} }
func parsePolice(data syscall.NetlinkRouteAttr, police *PoliceAction) {
switch data.Attr.Type {
case nl.TCA_POLICE_RESULT:
police.NotExceedAction = TcPolAct(native.Uint32(data.Value[0:4]))
case nl.TCA_POLICE_AVRATE:
police.AvRate = native.Uint32(data.Value[0:4])
case nl.TCA_POLICE_TBF:
p := *nl.DeserializeTcPolice(data.Value)
police.ActionAttrs = ActionAttrs{}
police.Attrs().Index = int(p.Index)
police.Attrs().Bindcnt = int(p.Bindcnt)
police.Attrs().Capab = int(p.Capab)
police.Attrs().Refcnt = int(p.Refcnt)
police.ExceedAction = TcPolAct(p.Action)
police.Rate = p.Rate.Rate
police.PeakRate = p.PeakRate.Rate
police.Burst = Xmitsize(uint64(p.Rate.Rate), p.Burst)
police.Mtu = p.Mtu
police.LinkLayer = int(p.Rate.Linklayer) & nl.TC_LINKLAYER_MASK
police.Overhead = p.Rate.Overhead
}
}
func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
var actions []Action var actions []Action
for _, table := range tables { for _, table := range tables {
@ -544,6 +681,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
action = &TunnelKeyAction{} action = &TunnelKeyAction{}
case "skbedit": case "skbedit":
action = &SkbEditAction{} action = &SkbEditAction{}
case "police":
action = &PoliceAction{}
default: default:
break nextattr break nextattr
} }
@ -622,6 +761,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
gen := *nl.DeserializeTcGen(adatum.Value) gen := *nl.DeserializeTcGen(adatum.Value)
toAttrs(&gen, action.Attrs()) toAttrs(&gen, action.Attrs())
} }
case "police":
parsePolice(adatum, action.(*PoliceAction))
} }
} }
} }
@ -632,7 +773,6 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
} }
func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
u32 := filter.(*U32) u32 := filter.(*U32)
detailed := false detailed := false
for _, datum := range data { for _, datum := range data {
@ -678,8 +818,7 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
} }
func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian() fw := filter.(*FwFilter)
fw := filter.(*Fw)
detailed := true detailed := true
for _, datum := range data { for _, datum := range data {
switch datum.Attr.Type { switch datum.Attr.Type {
@ -690,24 +829,18 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
case nl.TCA_FW_INDEV: case nl.TCA_FW_INDEV:
fw.InDev = string(datum.Value[:len(datum.Value)-1]) fw.InDev = string(datum.Value[:len(datum.Value)-1])
case nl.TCA_FW_POLICE: case nl.TCA_FW_POLICE:
var police PoliceAction
adata, _ := nl.ParseRouteAttr(datum.Value) adata, _ := nl.ParseRouteAttr(datum.Value)
for _, aattr := range adata { for _, aattr := range adata {
switch aattr.Attr.Type { parsePolice(aattr, &police)
case nl.TCA_POLICE_TBF:
fw.Police = *nl.DeserializeTcPolice(aattr.Value)
case nl.TCA_POLICE_RATE:
fw.Rtab = DeserializeRtab(aattr.Value)
case nl.TCA_POLICE_PEAKRATE:
fw.Ptab = DeserializeRtab(aattr.Value)
}
} }
fw.Police = &police
} }
} }
return detailed, nil return detailed, nil
} }
func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
bpf := filter.(*BpfFilter) bpf := filter.(*BpfFilter)
detailed := true detailed := true
for _, datum := range data { for _, datum := range data {
@ -726,14 +859,13 @@ func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
case nl.TCA_BPF_ID: case nl.TCA_BPF_ID:
bpf.Id = int(native.Uint32(datum.Value[0:4])) bpf.Id = int(native.Uint32(datum.Value[0:4]))
case nl.TCA_BPF_TAG: case nl.TCA_BPF_TAG:
bpf.Tag = hex.EncodeToString(datum.Value[:len(datum.Value)-1]) bpf.Tag = hex.EncodeToString(datum.Value)
} }
} }
return detailed, nil return detailed, nil
} }
func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
matchall := filter.(*MatchAll) matchall := filter.(*MatchAll)
detailed := true detailed := true
for _, datum := range data { for _, datum := range data {
@ -754,6 +886,10 @@ func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, er
return detailed, nil return detailed, nil
} }
func parseFlowerData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
return true, filter.(*Flower).decode(data)
}
func AlignToAtm(size uint) uint { func AlignToAtm(size uint) uint {
var linksize, cells int var linksize, cells int
cells = int(size / nl.ATM_CELL_PAYLOAD) cells = int(size / nl.ATM_CELL_PAYLOAD)
@ -801,14 +937,12 @@ func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, lin
func DeserializeRtab(b []byte) [256]uint32 { func DeserializeRtab(b []byte) [256]uint32 {
var rtab [256]uint32 var rtab [256]uint32
native := nl.NativeEndian()
r := bytes.NewReader(b) r := bytes.NewReader(b)
_ = binary.Read(r, native, &rtab) _ = binary.Read(r, native, &rtab)
return rtab return rtab
} }
func SerializeRtab(rtab [256]uint32) []byte { func SerializeRtab(rtab [256]uint32) []byte {
native := nl.NativeEndian()
var w bytes.Buffer var w bytes.Buffer
_ = binary.Write(&w, native, rtab) _ = binary.Write(&w, native, rtab)
return w.Bytes() return w.Bytes()

View File

@ -15,7 +15,7 @@ var pkgHandle = &Handle{}
// Handle is an handle for the netlink requests on a // Handle is an handle for the netlink requests on a
// specific network namespace. All the requests on the // specific network namespace. All the requests on the
// same netlink family share the same netlink socket, // 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 { type Handle struct {
sockets map[int]*nl.SocketHandle sockets map[int]*nl.SocketHandle
lookupByDump bool lookupByDump bool
@ -107,6 +107,21 @@ func (h *Handle) GetSocketReceiveBufferSize() ([]int, error) {
return results, nil return results, nil
} }
// SetStrictCheck sets the strict check socket option for each socket in the netlink handle. Returns early if any set operation fails
func (h *Handle) SetStrictCheck(state bool) error {
for _, sh := range h.sockets {
var stateInt int = 0
if state {
stateInt = 1
}
err := unix.SetsockoptInt(sh.Socket.GetFd(), unix.SOL_NETLINK, unix.NETLINK_GET_STRICT_CHK, stateInt)
if err != nil {
return err
}
}
return nil
}
// NewHandleAt returns a netlink handle on the network namespace // NewHandleAt returns a netlink handle on the network namespace
// specified by ns. If ns=netns.None(), current network namespace // specified by ns. If ns=netns.None(), current network namespace
// will be assumed // will be assumed
@ -136,14 +151,22 @@ func newHandle(newNs, curNs netns.NsHandle, nlFamilies ...int) (*Handle, error)
return h, nil return h, nil
} }
// Delete releases the resources allocated to this handle // Close releases the resources allocated to this handle
func (h *Handle) Delete() { func (h *Handle) Close() {
for _, sh := range h.sockets { for _, sh := range h.sockets {
sh.Close() sh.Close()
} }
h.sockets = nil 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 { func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest {
// Do this so that package API still use nl package variable nextSeqNr // Do this so that package API still use nl package variable nextSeqNr
if h.sockets == nil { if h.sockets == nil {

View File

@ -23,6 +23,8 @@ func NewHandleAtFrom(newNs, curNs netns.NsHandle) (*Handle, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func (h *Handle) Close() {}
func (h *Handle) Delete() {} func (h *Handle) Delete() {}
func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool { func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool {

View File

@ -1,6 +1,7 @@
package netlink package netlink
import ( import (
"encoding/binary"
"log" "log"
"net" "net"
"syscall" "syscall"
@ -11,12 +12,19 @@ import (
// IPSetEntry is used for adding, updating, retreiving and deleting entries // IPSetEntry is used for adding, updating, retreiving and deleting entries
type IPSetEntry struct { type IPSetEntry struct {
Comment string Comment string
MAC net.HardwareAddr MAC net.HardwareAddr
IP net.IP IP net.IP
Timeout *uint32 CIDR uint8
Packets *uint64 Timeout *uint32
Bytes *uint64 Packets *uint64
Bytes *uint64
Protocol *uint8
Port *uint16
IP2 net.IP
CIDR2 uint8
IFace string
Mark *uint32
Replace bool // replace existing entry Replace bool // replace existing entry
} }
@ -32,6 +40,12 @@ type IPSetResult struct {
SetName string SetName string
TypeName string TypeName string
Comment string Comment string
MarkMask uint32
IPFrom net.IP
IPTo net.IP
PortFrom uint16
PortTo uint16
HashSize uint32 HashSize uint32
NumEntries uint32 NumEntries uint32
@ -52,6 +66,12 @@ type IpsetCreateOptions struct {
Counters bool Counters bool
Comments bool Comments bool
Skbinfo bool Skbinfo bool
Revision uint8
IPFrom net.IP
IPTo net.IP
PortFrom uint16
PortTo uint16
} }
// IpsetProtocol returns the ipset protocol version from the kernel // IpsetProtocol returns the ipset protocol version from the kernel
@ -86,12 +106,12 @@ func IpsetListAll() ([]IPSetResult, error) {
// IpsetAdd adds an entry to an existing ipset. // IpsetAdd adds an entry to an existing ipset.
func IpsetAdd(setname string, entry *IPSetEntry) error { func IpsetAdd(setname string, entry *IPSetEntry) error {
return pkgHandle.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry) return pkgHandle.IpsetAdd(setname, entry)
} }
// IpsetDel deletes an entry from an existing ipset. // IpsetDel deletes an entry from an existing ipset.
func IpsetDel(setname string, entry *IPSetEntry) error { 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() (protocol uint8, minVersion uint8, err error) { func (h *Handle) IpsetProtocol() (protocol uint8, minVersion uint8, err error) {
@ -114,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_SETNAME, nl.ZeroTerminated(setname)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(0)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(2))) // 2 == inet 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) 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 { if timeout := options.Timeout; timeout != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout})
} }
@ -187,6 +226,16 @@ func (h *Handle) IpsetListAll() ([]IPSetResult, error) {
return result, nil 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 { func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error {
req := h.newIpsetRequest(nlCmd) req := h.newIpsetRequest(nlCmd)
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
@ -204,15 +253,49 @@ func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error
if entry.Timeout != nil { if entry.Timeout != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout})
} }
if entry.MAC != nil {
nestedData := nl.NewRtAttr(nl.IPSET_ATTR_ETHER|int(nl.NLA_F_NET_BYTEORDER), entry.MAC)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER|int(nl.NLA_F_NESTED), nestedData.Serialize()))
}
if entry.IP != nil { if entry.IP != nil {
nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP) 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())) 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}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0})
req.AddData(data) req.AddData(data)
@ -235,6 +318,17 @@ func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
return req 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) { func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) {
msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0) msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0)
@ -278,6 +372,8 @@ func (result *IPSetResult) unserialize(msg []byte) {
result.parseAttrADT(attr.Value) result.parseAttrADT(attr.Value)
case nl.IPSET_ATTR_PROTOCOL_MIN: case nl.IPSET_ATTR_PROTOCOL_MIN:
result.ProtocolMinVersion = attr.Value[0] result.ProtocolMinVersion = attr.Value[0]
case nl.IPSET_ATTR_MARKMASK:
result.MarkMask = attr.Uint32()
default: default:
log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
} }
@ -307,12 +403,31 @@ func (result *IPSetResult) parseAttrData(data []byte) {
switch nested.Type { switch nested.Type {
case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER: case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER:
result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value}) 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: case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER:
result.LineNo = attr.Uint32() result.LineNo = attr.Uint32()
case nl.IPSET_ATTR_COMMENT: case nl.IPSET_ATTR_COMMENT:
result.Comment = nl.BytesToString(attr.Value) result.Comment = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_MARKMASK:
result.MarkMask = attr.Uint32()
default: default:
log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
} }
@ -357,6 +472,30 @@ func parseIPSetEntry(data []byte) (entry IPSetEntry) {
log.Printf("unknown nested ADT attribute from kernel: %+v", attr) 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: default:
log.Printf("unknown ADT attribute from kernel: %+v", attr) log.Printf("unknown ADT attribute from kernel: %+v", attr)
} }

View File

@ -35,10 +35,13 @@ type LinkAttrs struct {
Alias string Alias string
Statistics *LinkStatistics Statistics *LinkStatistics
Promisc int Promisc int
Allmulti int
Multi int
Xdp *LinkXdp Xdp *LinkXdp
EncapType string EncapType string
Protinfo *Protinfo Protinfo *Protinfo
OperState LinkOperState OperState LinkOperState
PhysSwitchID int
NetNsID int NetNsID int
NumTxQueues int NumTxQueues int
NumRxQueues int NumRxQueues int
@ -456,6 +459,19 @@ func (ipvlan *IPVlan) Type() string {
return "ipvlan" 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 // VlanProtocol type
type VlanProtocol int type VlanProtocol int
@ -946,6 +962,14 @@ func (b *BondSlave) SlaveType() string {
return "bond" return "bond"
} }
type VrfSlave struct {
Table uint32
}
func (v *VrfSlave) SlaveType() string {
return "vrf"
}
// Geneve devices must specify RemoteIP and ID (VNI) on create // Geneve devices must specify RemoteIP and ID (VNI) on create
// https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/drivers/net/geneve.c#L1209-L1223 // https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/drivers/net/geneve.c#L1209-L1223
type Geneve struct { type Geneve struct {
@ -1014,6 +1038,7 @@ type Iptun struct {
EncapType uint16 EncapType uint16
EncapFlags uint16 EncapFlags uint16
FlowBased bool FlowBased bool
Proto uint8
} }
func (iptun *Iptun) Attrs() *LinkAttrs { func (iptun *Iptun) Attrs() *LinkAttrs {
@ -1049,6 +1074,37 @@ func (ip6tnl *Ip6tnl) Type() string {
return "ip6tnl" return "ip6tnl"
} }
// from https://elixir.bootlin.com/linux/v5.15.4/source/include/uapi/linux/if_tunnel.h#L84
type TunnelEncapType uint16
const (
None TunnelEncapType = iota
FOU
GUE
)
// from https://elixir.bootlin.com/linux/v5.15.4/source/include/uapi/linux/if_tunnel.h#L91
type TunnelEncapFlag uint16
const (
CSum TunnelEncapFlag = 1 << 0
CSum6 = 1 << 1
RemCSum = 1 << 2
)
// from https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ip6_tunnel.h#L12
type IP6TunnelFlag uint16
const (
IP6_TNL_F_IGN_ENCAP_LIMIT IP6TunnelFlag = 1 // don't add encapsulation limit if one isn't present in inner packet
IP6_TNL_F_USE_ORIG_TCLASS = 2 // copy the traffic class field from the inner packet
IP6_TNL_F_USE_ORIG_FLOWLABEL = 4 // copy the flowlabel from the inner packet
IP6_TNL_F_MIP6_DEV = 8 // being used for Mobile IPv6
IP6_TNL_F_RCV_DSCP_COPY = 10 // copy DSCP from the outer packet
IP6_TNL_F_USE_ORIG_FWMARK = 20 // copy fwmark from inner packet
IP6_TNL_F_ALLOW_LOCAL_REMOTE = 40 // allow remote endpoint on the local node
)
type Sittun struct { type Sittun struct {
LinkAttrs LinkAttrs
Link uint32 Link uint32
@ -1260,11 +1316,27 @@ func (ipoib *IPoIB) Type() string {
return "ipoib" 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; // iproute2 supported devices;
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap | // vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
// gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon | // gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon |
// bond_slave | ipvlan | xfrm // bond_slave | ipvlan | xfrm | bareudp
// LinkNotFoundError wraps the various not found errors when // LinkNotFoundError wraps the various not found errors when
// getting/reading links. This is intended for better error // getting/reading links. This is intended for better error

View File

@ -55,8 +55,6 @@ const (
VF_LINK_STATE_DISABLE uint32 = 2 VF_LINK_STATE_DISABLE uint32 = 2
) )
var lookupByDump = false
var macvlanModes = [...]uint32{ var macvlanModes = [...]uint32{
0, 0,
nl.MACVLAN_MODE_PRIVATE, nl.MACVLAN_MODE_PRIVATE,
@ -153,7 +151,6 @@ func (h *Handle) LinkSetAllmulticastOn(link Link) error {
msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Change = unix.IFF_ALLMULTI msg.Change = unix.IFF_ALLMULTI
msg.Flags = unix.IFF_ALLMULTI msg.Flags = unix.IFF_ALLMULTI
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
req.AddData(msg) req.AddData(msg)
@ -183,6 +180,51 @@ func (h *Handle) LinkSetAllmulticastOff(link Link) error {
return err 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 { func MacvlanMACAddrAdd(link Link, addr net.HardwareAddr) error {
return pkgHandle.MacvlanMACAddrAdd(link, addr) return pkgHandle.MacvlanMACAddrAdd(link, addr)
} }
@ -547,13 +589,13 @@ func (h *Handle) LinkSetVfVlanQos(link Link, vf, vlan, qos int) error {
req.AddData(msg) req.AddData(msg)
data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) 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{ vfmsg := nl.VfVlan{
Vf: uint32(vf), Vf: uint32(vf),
Vlan: uint32(vlan), Vlan: uint32(vlan),
Qos: uint32(qos), Qos: uint32(qos),
} }
nl.NewRtAttrChild(info, nl.IFLA_VF_VLAN, vfmsg.Serialize()) info.AddRtAttr(nl.IFLA_VF_VLAN, vfmsg.Serialize())
req.AddData(data) req.AddData(data)
_, err := req.Execute(unix.NETLINK_ROUTE, 0) _, err := req.Execute(unix.NETLINK_ROUTE, 0)
@ -1104,6 +1146,10 @@ func (h *Handle) LinkAdd(link Link) error {
return h.linkModify(link, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) return h.linkModify(link, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
} }
func LinkModify(link Link) error {
return pkgHandle.LinkModify(link)
}
func (h *Handle) LinkModify(link Link) error { func (h *Handle) LinkModify(link Link) error {
return h.linkModify(link, unix.NLM_F_REQUEST|unix.NLM_F_ACK) return h.linkModify(link, unix.NLM_F_REQUEST|unix.NLM_F_ACK)
} }
@ -1218,9 +1264,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 // only persist interface if NonPersist is NOT set
if !tuntap.NonPersist { 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 { if errno != 0 {
cleanupFds(fds) cleanupFds(fds)
return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno) return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno)
@ -1237,7 +1300,10 @@ func (h *Handle) linkModify(link Link, flags int) error {
// un-persist (e.g. allow the interface to be removed) the tuntap // un-persist (e.g. allow the interface to be removed) the tuntap
// should not hurt if not set prior, condition might be not needed // should not hurt if not set prior, condition might be not needed
if !tuntap.NonPersist { 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) cleanupFds(fds)
return err return err
@ -1409,6 +1475,10 @@ func (h *Handle) linkModify(link Link, flags int) error {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode))) data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode)))
data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag))) 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: case *Macvlan:
if link.Mode != MACVLAN_MODE_DEFAULT { if link.Mode != MACVLAN_MODE_DEFAULT {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
@ -1443,6 +1513,8 @@ func (h *Handle) linkModify(link Link, flags int) error {
addXfrmiAttrs(link, linkInfo) addXfrmiAttrs(link, linkInfo)
case *IPoIB: case *IPoIB:
addIPoIBAttrs(link, linkInfo) addIPoIBAttrs(link, linkInfo)
case *BareUDP:
addBareUDPAttrs(link, linkInfo)
} }
req.AddData(linkInfo) req.AddData(linkInfo)
@ -1624,7 +1696,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. // a link object.
func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
msg := nl.DeserializeIfInfomsg(m) msg := nl.DeserializeIfInfomsg(m)
@ -1639,9 +1711,17 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
base.RawFlags = msg.Flags base.RawFlags = msg.Flags
base.Flags = linkFlags(msg.Flags) base.Flags = linkFlags(msg.Flags)
base.EncapType = msg.EncapType() base.EncapType = msg.EncapType()
base.NetNsID = -1
if msg.Flags&unix.IFF_PROMISC != 0 { if msg.Flags&unix.IFF_PROMISC != 0 {
base.Promisc = 1 base.Promisc = 1
} }
if msg.Flags&unix.IFF_ALLMULTI != 0 {
base.Allmulti = 1
}
if msg.Flags&unix.IFF_MULTICAST != 0 {
base.Multi = 1
}
var ( var (
link Link link Link
stats32 *LinkStatistics32 stats32 *LinkStatistics32
@ -1680,6 +1760,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &Bond{} link = &Bond{}
case "ipvlan": case "ipvlan":
link = &IPVlan{} link = &IPVlan{}
case "ipvtap":
link = &IPVtap{}
case "macvlan": case "macvlan":
link = &Macvlan{} link = &Macvlan{}
case "macvtap": case "macvtap":
@ -1714,6 +1796,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &IPoIB{} link = &IPoIB{}
case "can": case "can":
link = &Can{} link = &Can{}
case "bareudp":
link = &BareUDP{}
default: default:
link = &GenericLink{LinkType: linkType} link = &GenericLink{LinkType: linkType}
} }
@ -1731,6 +1815,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
parseBondData(link, data) parseBondData(link, data)
case "ipvlan": case "ipvlan":
parseIPVlanData(link, data) parseIPVlanData(link, data)
case "ipvtap":
parseIPVtapData(link, data)
case "macvlan": case "macvlan":
parseMacvlanData(link, data) parseMacvlanData(link, data)
case "macvtap": case "macvtap":
@ -1767,13 +1853,19 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
parseIPoIBData(link, data) parseIPoIBData(link, data)
case "can": case "can":
parseCanData(link, data) parseCanData(link, data)
case "bareudp":
parseBareUDPData(link, data)
} }
case nl.IFLA_INFO_SLAVE_KIND: case nl.IFLA_INFO_SLAVE_KIND:
slaveType = string(info.Value[:len(info.Value)-1]) slaveType = string(info.Value[:len(info.Value)-1])
switch slaveType { switch slaveType {
case "bond": case "bond":
linkSlave = &BondSlave{} linkSlave = &BondSlave{}
case "vrf":
linkSlave = &VrfSlave{}
} }
case nl.IFLA_INFO_SLAVE_DATA: case nl.IFLA_INFO_SLAVE_DATA:
switch slaveType { switch slaveType {
case "bond": case "bond":
@ -1782,6 +1874,12 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
return nil, err return nil, err
} }
parseBondSlaveData(linkSlave, data) parseBondSlaveData(linkSlave, data)
case "vrf":
data, err := nl.ParseRouteAttr(info.Value)
if err != nil {
return nil, err
}
parseVrfSlaveData(linkSlave, data)
} }
} }
} }
@ -1835,6 +1933,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
} }
case unix.IFLA_OPERSTATE: case unix.IFLA_OPERSTATE:
base.OperState = LinkOperState(uint8(attr.Value[0])) 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: case unix.IFLA_LINK_NETNSID:
base.NetNsID = int(native.Uint32(attr.Value[0:4])) base.NetNsID = int(native.Uint32(attr.Value[0:4]))
case unix.IFLA_GSO_MAX_SIZE: case unix.IFLA_GSO_MAX_SIZE:
@ -2023,7 +2123,8 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
if err != nil { if err != nil {
if cberr != nil { if cberr != nil {
cberr(err) cberr(fmt.Errorf("Receive failed: %v",
err))
} }
return return
} }
@ -2038,15 +2139,15 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
continue continue
} }
if m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if error == 0 {
continue continue
} }
if cberr != nil { if cberr != nil {
cberr(syscall.Errno(-error)) cberr(fmt.Errorf("error message: %v",
syscall.Errno(-error)))
} }
return continue
} }
ifmsg := nl.DeserializeIfInfomsg(m.Data) ifmsg := nl.DeserializeIfInfomsg(m.Data)
header := unix.NlMsghdr(m.Header) header := unix.NlMsghdr(m.Header)
@ -2055,7 +2156,7 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
if cberr != nil { if cberr != nil {
cberr(err) cberr(err)
} }
return continue
} }
ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: header, Link: link} ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: header, Link: link}
} }
@ -2404,6 +2505,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) { func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
ipv := link.(*IPVlan) ipv := link.(*IPVlan)
for _, datum := range data { for _, datum := range data {
@ -2416,6 +2527,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) { func parseMacvtapData(link Link, data []syscall.NetlinkRouteAttr) {
macv := link.(*Macvtap) macv := link.(*Macvtap)
parseMacvlanData(&macv.Macvlan, data) parseMacvlanData(&macv.Macvlan, data)
@ -2751,11 +2874,16 @@ func addIptunAttrs(iptun *Iptun, linkInfo *nl.RtAttr) {
data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_FLAGS, nl.Uint16Attr(iptun.EncapFlags)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_FLAGS, nl.Uint16Attr(iptun.EncapFlags))
data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_SPORT, htons(iptun.EncapSport)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_SPORT, htons(iptun.EncapSport))
data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_DPORT, htons(iptun.EncapDport)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_DPORT, htons(iptun.EncapDport))
data.AddRtAttr(nl.IFLA_IPTUN_PROTO, nl.Uint8Attr(iptun.Proto))
} }
func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) { func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) {
iptun := link.(*Iptun) iptun := link.(*Iptun)
for _, datum := range data { 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 { switch datum.Attr.Type {
case nl.IFLA_IPTUN_LOCAL: case nl.IFLA_IPTUN_LOCAL:
iptun.Local = net.IP(datum.Value[0:4]) iptun.Local = net.IP(datum.Value[0:4])
@ -2777,6 +2905,8 @@ func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) {
iptun.EncapFlags = native.Uint16(datum.Value[0:2]) iptun.EncapFlags = native.Uint16(datum.Value[0:2])
case nl.IFLA_IPTUN_COLLECT_METADATA: case nl.IFLA_IPTUN_COLLECT_METADATA:
iptun.FlowBased = true iptun.FlowBased = true
case nl.IFLA_IPTUN_PROTO:
iptun.Proto = datum.Value[0]
} }
} }
} }
@ -3099,8 +3229,9 @@ func parseVfInfo(data []syscall.NetlinkRouteAttr, id int) VfInfo {
func addXfrmiAttrs(xfrmi *Xfrmi, linkInfo *nl.RtAttr) { func addXfrmiAttrs(xfrmi *Xfrmi, linkInfo *nl.RtAttr) {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_XFRM_LINK, nl.Uint32Attr(uint32(xfrmi.ParentIndex))) data.AddRtAttr(nl.IFLA_XFRM_LINK, nl.Uint32Attr(uint32(xfrmi.ParentIndex)))
data.AddRtAttr(nl.IFLA_XFRM_IF_ID, nl.Uint32Attr(xfrmi.Ifid)) if xfrmi.Ifid != 0 {
data.AddRtAttr(nl.IFLA_XFRM_IF_ID, nl.Uint32Attr(xfrmi.Ifid))
}
} }
func parseXfrmiData(link Link, data []syscall.NetlinkRouteAttr) { func parseXfrmiData(link Link, data []syscall.NetlinkRouteAttr) {
@ -3303,3 +3434,32 @@ func addIPoIBAttrs(ipoib *IPoIB, linkInfo *nl.RtAttr) {
data.AddRtAttr(nl.IFLA_IPOIB_MODE, nl.Uint16Attr(uint16(ipoib.Mode))) data.AddRtAttr(nl.IFLA_IPOIB_MODE, nl.Uint16Attr(uint16(ipoib.Mode)))
data.AddRtAttr(nl.IFLA_IPOIB_UMCAST, nl.Uint16Attr(uint16(ipoib.Umcast))) 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

@ -12,6 +12,7 @@ type Neigh struct {
State int State int
Type int Type int
Flags int Flags int
FlagsExt int
IP net.IP IP net.IP
HardwareAddr net.HardwareAddr HardwareAddr net.HardwareAddr
LLIPAddr net.IP //Used in the case of NHRP LLIPAddr net.IP //Used in the case of NHRP

View File

@ -24,7 +24,11 @@ const (
NDA_MASTER NDA_MASTER
NDA_LINK_NETNSID NDA_LINK_NETNSID
NDA_SRC_VNI NDA_SRC_VNI
NDA_MAX = NDA_SRC_VNI NDA_PROTOCOL
NDA_NH_ID
NDA_FDB_EXT_ATTRS
NDA_FLAGS_EXT
NDA_MAX = NDA_FLAGS_EXT
) )
// Neighbor Cache Entry States. // Neighbor Cache Entry States.
@ -42,11 +46,19 @@ const (
// Neighbor Flags // Neighbor Flags
const ( const (
NTF_USE = 0x01 NTF_USE = 0x01
NTF_SELF = 0x02 NTF_SELF = 0x02
NTF_MASTER = 0x04 NTF_MASTER = 0x04
NTF_PROXY = 0x08 NTF_PROXY = 0x08
NTF_ROUTER = 0x80 NTF_EXT_LEARNED = 0x10
NTF_OFFLOADED = 0x20
NTF_STICKY = 0x40
NTF_ROUTER = 0x80
)
// Extended Neighbor Flags
const (
NTF_EXT_MANAGED = 0x00000001
) )
// Ndmsg is for adding, removing or receiving information about a neighbor table entry // Ndmsg is for adding, removing or receiving information about a neighbor table entry
@ -162,11 +174,16 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
if neigh.LLIPAddr != nil { if neigh.LLIPAddr != nil {
llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4()) llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
req.AddData(llIPData) req.AddData(llIPData)
} else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil { } else if neigh.HardwareAddr != nil {
hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
req.AddData(hwData) req.AddData(hwData)
} }
if neigh.FlagsExt != 0 {
flagsExtData := nl.NewRtAttr(NDA_FLAGS_EXT, nl.Uint32Attr(uint32(neigh.FlagsExt)))
req.AddData(flagsExtData)
}
if neigh.Vlan != 0 { if neigh.Vlan != 0 {
vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan))) vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
req.AddData(vlanData) req.AddData(vlanData)
@ -305,6 +322,8 @@ func NeighDeserialize(m []byte) (*Neigh, error) {
} else { } else {
neigh.HardwareAddr = net.HardwareAddr(attr.Value) neigh.HardwareAddr = net.HardwareAddr(attr.Value)
} }
case NDA_FLAGS_EXT:
neigh.FlagsExt = int(native.Uint32(attr.Value[0:4]))
case NDA_VLAN: case NDA_VLAN:
neigh.Vlan = int(native.Uint16(attr.Value[0:2])) neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
case NDA_VNI: case NDA_VNI:
@ -408,7 +427,6 @@ func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <
continue continue
} }
if m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if error == 0 {
continue continue

View File

@ -180,14 +180,30 @@ func RouteAdd(route *Route) error {
return ErrNotImplemented return ErrNotImplemented
} }
func RouteAppend(route *Route) error {
return ErrNotImplemented
}
func RouteDel(route *Route) error { func RouteDel(route *Route) error {
return ErrNotImplemented return ErrNotImplemented
} }
func RouteGet(destination net.IP) ([]Route, error) {
return nil, ErrNotImplemented
}
func RouteList(link Link, family int) ([]Route, error) { func RouteList(link Link, family int) ([]Route, error) {
return nil, ErrNotImplemented 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 { func XfrmPolicyAdd(policy *XfrmPolicy) error {
return ErrNotImplemented return ErrNotImplemented
} }

View File

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

View File

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

View File

@ -11,22 +11,37 @@ const (
const ( const (
DEVLINK_CMD_GET = 1 DEVLINK_CMD_GET = 1
DEVLINK_CMD_PORT_GET = 5 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_GET = 29
DEVLINK_CMD_ESWITCH_SET = 30 DEVLINK_CMD_ESWITCH_SET = 30
DEVLINK_CMD_INFO_GET = 51
) )
const ( const (
DEVLINK_ATTR_BUS_NAME = 1 DEVLINK_ATTR_BUS_NAME = 1
DEVLINK_ATTR_DEV_NAME = 2 DEVLINK_ATTR_DEV_NAME = 2
DEVLINK_ATTR_PORT_INDEX = 3 DEVLINK_ATTR_PORT_INDEX = 3
DEVLINK_ATTR_PORT_TYPE = 4 DEVLINK_ATTR_PORT_TYPE = 4
DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6 DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6
DEVLINK_ATTR_PORT_NETDEV_NAME = 7 DEVLINK_ATTR_PORT_NETDEV_NAME = 7
DEVLINK_ATTR_PORT_IBDEV_NAME = 8 DEVLINK_ATTR_PORT_IBDEV_NAME = 8
DEVLINK_ATTR_ESWITCH_MODE = 25 DEVLINK_ATTR_ESWITCH_MODE = 25
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26 DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62 DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
DEVLINK_ATTR_PORT_FLAVOUR = 77 DEVLINK_ATTR_PORT_FLAVOUR = 77
DEVLINK_ATTR_INFO_DRIVER_NAME = 98
DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99
DEVLINK_ATTR_INFO_VERSION_FIXED = 100
DEVLINK_ATTR_INFO_VERSION_RUNNING = 101
DEVLINK_ATTR_INFO_VERSION_STORED = 102
DEVLINK_ATTR_INFO_VERSION_NAME = 103
DEVLINK_ATTR_INFO_VERSION_VALUE = 104
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 ( const (
@ -53,6 +68,8 @@ const (
DEVLINK_PORT_FLAVOUR_PCI_PF = 3 DEVLINK_PORT_FLAVOUR_PCI_PF = 3
DEVLINK_PORT_FLAVOUR_PCI_VF = 4 DEVLINK_PORT_FLAVOUR_PCI_VF = 4
DEVLINK_PORT_FLAVOUR_VIRTUAL = 5 DEVLINK_PORT_FLAVOUR_VIRTUAL = 5
DEVLINK_PORT_FLAVOUR_UNUSED = 6
DEVLINK_PORT_FLAVOUR_PCI_SF = 7
) )
const ( const (
@ -61,3 +78,19 @@ const (
DEVLINK_PORT_TYPE_ETH = 2 DEVLINK_PORT_TYPE_ETH = 2
DEVLINK_PORT_TYPE_IB = 3 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

@ -709,3 +709,12 @@ const (
IFLA_CAN_BITRATE_MAX IFLA_CAN_BITRATE_MAX
IFLA_CAN_MAX = 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
)

29
vendor/github.com/vishvananda/netlink/nl/lwt_linux.go generated vendored Normal file
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

@ -27,7 +27,8 @@ const (
// tc rules or filters, or other more memory requiring data. // tc rules or filters, or other more memory requiring data.
RECEIVE_BUFFER_SIZE = 65536 RECEIVE_BUFFER_SIZE = 65536
// Kernel netlink pid // Kernel netlink pid
PidKernel uint32 = 0 PidKernel uint32 = 0
SizeofCnMsgOp = 0x18
) )
// SupportedNlFamilies contains the list of netlink families this netlink package supports // SupportedNlFamilies contains the list of netlink families this netlink package supports
@ -38,6 +39,9 @@ var nextSeqNr uint32
// Default netlink socket timeout, 60s // Default netlink socket timeout, 60s
var SocketTimeoutTv = unix.Timeval{Sec: 60, Usec: 0} var SocketTimeoutTv = unix.Timeval{Sec: 60, Usec: 0}
// ErrorMessageReporting is the default error message reporting configuration for the new netlink sockets
var EnableErrorMessageReporting bool = false
// GetIPFamily returns the family type of a net.IP. // GetIPFamily returns the family type of a net.IP.
func GetIPFamily(ip net.IP) int { func GetIPFamily(ip net.IP) int {
if len(ip) <= net.IPv4len { if len(ip) <= net.IPv4len {
@ -80,11 +84,69 @@ func Swap32(i uint32) uint32 {
return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24 return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24
} }
const (
NLMSGERR_ATTR_UNUSED = 0
NLMSGERR_ATTR_MSG = 1
NLMSGERR_ATTR_OFFS = 2
NLMSGERR_ATTR_COOKIE = 3
NLMSGERR_ATTR_POLICY = 4
)
type NetlinkRequestData interface { type NetlinkRequestData interface {
Len() int Len() int
Serialize() []byte Serialize() []byte
} }
const (
PROC_CN_MCAST_LISTEN = 1
PROC_CN_MCAST_IGNORE
)
type CbID struct {
Idx uint32
Val uint32
}
type CnMsg struct {
ID CbID
Seq uint32
Ack uint32
Length uint16
Flags uint16
}
type CnMsgOp struct {
CnMsg
// here we differ from the C header
Op uint32
}
func NewCnMsg(idx, val, op uint32) *CnMsgOp {
var cm CnMsgOp
cm.ID.Idx = idx
cm.ID.Val = val
cm.Ack = 0
cm.Seq = 1
cm.Length = uint16(binary.Size(op))
cm.Op = op
return &cm
}
func (msg *CnMsgOp) Serialize() []byte {
return (*(*[SizeofCnMsgOp]byte)(unsafe.Pointer(msg)))[:]
}
func DeserializeCnMsgOp(b []byte) *CnMsgOp {
return (*CnMsgOp)(unsafe.Pointer(&b[0:SizeofCnMsgOp][0]))
}
func (msg *CnMsgOp) Len() int {
return SizeofCnMsgOp
}
// IfInfomsg is related to links, but it is used for list requests as well // IfInfomsg is related to links, but it is used for list requests as well
type IfInfomsg struct { type IfInfomsg struct {
unix.IfInfomsg unix.IfInfomsg
@ -252,6 +314,12 @@ func (msg *IfInfomsg) EncapType() string {
return fmt.Sprintf("unknown%d", msg.Type) return fmt.Sprintf("unknown%d", msg.Type)
} }
// Round the length of a netlink message up to align it properly.
// Taken from syscall/netlink_linux.go by The Go Authors under BSD-style license.
func nlmAlignOf(msglen int) int {
return (msglen + syscall.NLMSG_ALIGNTO - 1) & ^(syscall.NLMSG_ALIGNTO - 1)
}
func rtaAlignOf(attrlen int) int { func rtaAlignOf(attrlen int) int {
return (attrlen + unix.RTA_ALIGNTO - 1) & ^(unix.RTA_ALIGNTO - 1) return (attrlen + unix.RTA_ALIGNTO - 1) & ^(unix.RTA_ALIGNTO - 1)
} }
@ -436,6 +504,9 @@ func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, erro
if err := s.SetReceiveTimeout(&SocketTimeoutTv); err != nil { if err := s.SetReceiveTimeout(&SocketTimeoutTv); err != nil {
return nil, err return nil, err
} }
if err := s.SetExtAck(EnableErrorMessageReporting); err != nil {
return nil, err
}
defer s.Close() defer s.Close()
} else { } else {
@ -475,11 +546,37 @@ done:
} }
if m.Header.Type == unix.NLMSG_DONE || m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_DONE || m.Header.Type == unix.NLMSG_ERROR {
native := NativeEndian() native := NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) errno := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if errno == 0 {
break done break done
} }
return nil, syscall.Errno(-error) var err error
err = syscall.Errno(-errno)
unreadData := m.Data[4:]
if m.Header.Flags|unix.NLM_F_ACK_TLVS != 0 && len(unreadData) > syscall.SizeofNlMsghdr {
// Skip the echoed request message.
echoReqH := (*syscall.NlMsghdr)(unsafe.Pointer(&unreadData[0]))
unreadData = unreadData[nlmAlignOf(int(echoReqH.Len)):]
// Annotate `err` using nlmsgerr attributes.
for len(unreadData) >= syscall.SizeofRtAttr {
attr := (*syscall.RtAttr)(unsafe.Pointer(&unreadData[0]))
attrData := unreadData[syscall.SizeofRtAttr:attr.Len]
switch attr.Type {
case NLMSGERR_ATTR_MSG:
err = fmt.Errorf("%w: %s", err, string(attrData))
default:
// TODO: handle other NLMSGERR_ATTR types
}
unreadData = unreadData[rtaAlignOf(int(attr.Len)):]
}
}
return nil, err
} }
if resType != 0 && m.Header.Type != resType { if resType != 0 && m.Header.Type != resType {
continue continue
@ -694,6 +791,16 @@ func (s *NetlinkSocket) SetReceiveTimeout(timeout *unix.Timeval) error {
return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, timeout) return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, timeout)
} }
// SetExtAck requests error messages to be reported on the socket
func (s *NetlinkSocket) SetExtAck(enable bool) error {
var enableN int
if enable {
enableN = 1
}
return unix.SetsockoptInt(int(s.fd), unix.SOL_NETLINK, unix.NETLINK_EXT_ACK, enableN)
}
func (s *NetlinkSocket) GetPid() (uint32, error) { func (s *NetlinkSocket) GetPid() (uint32, error) {
fd := int(atomic.LoadInt32(&s.fd)) fd := int(atomic.LoadInt32(&s.fd))
lsa, err := unix.Getsockname(fd) lsa, err := unix.Getsockname(fd)

View File

@ -11,6 +11,8 @@ const (
const ( const (
RDMA_NLDEV_CMD_GET = 1 RDMA_NLDEV_CMD_GET = 1
RDMA_NLDEV_CMD_SET = 2 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_GET = 6
RDMA_NLDEV_CMD_SYS_SET = 7 RDMA_NLDEV_CMD_SYS_SET = 7
) )
@ -30,6 +32,8 @@ const (
RDMA_NLDEV_ATTR_PORT_STATE = 12 RDMA_NLDEV_ATTR_PORT_STATE = 12
RDMA_NLDEV_ATTR_PORT_PHYS_STATE = 13 RDMA_NLDEV_ATTR_PORT_PHYS_STATE = 13
RDMA_NLDEV_ATTR_DEV_NODE_TYPE = 14 RDMA_NLDEV_ATTR_DEV_NODE_TYPE = 14
RDMA_NLDEV_ATTR_NDEV_NAME = 51
RDMA_NLDEV_ATTR_LINK_TYPE = 65
RDMA_NLDEV_SYS_ATTR_NETNS_MODE = 66 RDMA_NLDEV_SYS_ATTR_NETNS_MODE = 66
RDMA_NLDEV_NET_NS_FD = 68 RDMA_NLDEV_NET_NS_FD = 68
) )

View File

@ -23,7 +23,7 @@ func (s1 *IPv6SrHdr) Equal(s2 IPv6SrHdr) bool {
return false return false
} }
for i := range s1.Segments { for i := range s1.Segments {
if s1.Segments[i].Equal(s2.Segments[i]) != true { if !s1.Segments[i].Equal(s2.Segments[i]) {
return false return false
} }
} }
@ -89,7 +89,7 @@ func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) {
} }
buf = buf[12:] buf = buf[12:]
if len(buf)%16 != 0 { 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 return mode, nil, err
} }
for len(buf) > 0 { for len(buf) > 0 {

View File

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

View File

@ -882,6 +882,111 @@ const (
TCA_HFSC_USC 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 { // struct tc_sfq_qopt {
// unsigned quantum; /* Bytes per round allocated to flow */ // unsigned quantum; /* Bytes per round allocated to flow */
// int perturb_period; /* Period of hash perturbation */ // int perturb_period; /* Period of hash perturbation */

View File

@ -0,0 +1,217 @@
package netlink
import (
"bytes"
"encoding/binary"
"fmt"
"os"
"syscall"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
const CN_IDX_PROC = 0x1
const (
PROC_EVENT_NONE = 0x00000000
PROC_EVENT_FORK = 0x00000001
PROC_EVENT_EXEC = 0x00000002
PROC_EVENT_UID = 0x00000004
PROC_EVENT_GID = 0x00000040
PROC_EVENT_SID = 0x00000080
PROC_EVENT_PTRACE = 0x00000100
PROC_EVENT_COMM = 0x00000200
PROC_EVENT_COREDUMP = 0x40000000
PROC_EVENT_EXIT = 0x80000000
)
const (
CN_VAL_PROC = 0x1
PROC_CN_MCAST_LISTEN = 0x1
)
type ProcEventMsg interface {
Pid() uint32
Tgid() uint32
}
type ProcEventHeader struct {
What uint32
CPU uint32
Timestamp uint64
}
type ProcEvent struct {
ProcEventHeader
Msg ProcEventMsg
}
func (pe *ProcEvent) setHeader(h ProcEventHeader) {
pe.What = h.What
pe.CPU = h.CPU
pe.Timestamp = h.Timestamp
}
type ExitProcEvent struct {
ProcessPid uint32
ProcessTgid uint32
ExitCode uint32
ExitSignal uint32
ParentPid uint32
ParentTgid uint32
}
type ExitProcEvent2 struct {
ProcessPid uint32
ProcessTgid uint32
ExitCode uint32
ExitSignal uint32
ParentPid uint32
ParentTgid uint32
}
func (e *ExitProcEvent) Pid() uint32 {
return e.ProcessPid
}
func (e *ExitProcEvent) Tgid() uint32 {
return e.ProcessTgid
}
type ExecProcEvent struct {
ProcessPid uint32
ProcessTgid uint32
}
func (e *ExecProcEvent) Pid() uint32 {
return e.ProcessPid
}
func (e *ExecProcEvent) Tgid() uint32 {
return e.ProcessTgid
}
type ForkProcEvent struct {
ParentPid uint32
ParentTgid uint32
ChildPid uint32
ChildTgid uint32
}
func (e *ForkProcEvent) Pid() uint32 {
return e.ParentPid
}
func (e *ForkProcEvent) Tgid() uint32 {
return e.ParentTgid
}
type CommProcEvent struct {
ProcessPid uint32
ProcessTgid uint32
Comm [16]byte
}
func (e *CommProcEvent) Pid() uint32 {
return e.ProcessPid
}
func (e *CommProcEvent) Tgid() uint32 {
return e.ProcessTgid
}
func ProcEventMonitor(ch chan<- ProcEvent, done <-chan struct{}, errorChan chan<- error) error {
h, err := NewHandle()
if err != nil {
return err
}
defer h.Delete()
s, err := nl.SubscribeAt(netns.None(), netns.None(), unix.NETLINK_CONNECTOR, CN_IDX_PROC)
if err != nil {
return err
}
var nlmsg nl.NetlinkRequest
nlmsg.Pid = uint32(os.Getpid())
nlmsg.Type = unix.NLMSG_DONE
nlmsg.Len = uint32(unix.SizeofNlMsghdr)
cm := nl.NewCnMsg(CN_IDX_PROC, CN_VAL_PROC, PROC_CN_MCAST_LISTEN)
nlmsg.AddData(cm)
s.Send(&nlmsg)
if done != nil {
go func() {
<-done
s.Close()
}()
}
go func() {
defer close(ch)
for {
msgs, from, err := s.Receive()
if err != nil {
errorChan <- err
return
}
if from.Pid != nl.PidKernel {
errorChan <- fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
return
}
for _, m := range msgs {
e := parseNetlinkMessage(m)
if e != nil {
ch <- *e
}
}
}
}()
return nil
}
func parseNetlinkMessage(m syscall.NetlinkMessage) *ProcEvent {
if m.Header.Type == unix.NLMSG_DONE {
buf := bytes.NewBuffer(m.Data)
msg := &nl.CnMsg{}
hdr := &ProcEventHeader{}
binary.Read(buf, nl.NativeEndian(), msg)
binary.Read(buf, nl.NativeEndian(), hdr)
pe := &ProcEvent{}
pe.setHeader(*hdr)
switch hdr.What {
case PROC_EVENT_EXIT:
event := &ExitProcEvent{}
binary.Read(buf, nl.NativeEndian(), event)
pe.Msg = event
return pe
case PROC_EVENT_FORK:
event := &ForkProcEvent{}
binary.Read(buf, nl.NativeEndian(), event)
pe.Msg = event
return pe
case PROC_EVENT_EXEC:
event := &ExecProcEvent{}
binary.Read(buf, nl.NativeEndian(), event)
pe.Msg = event
return pe
case PROC_EVENT_COMM:
event := &CommProcEvent{}
binary.Read(buf, nl.NativeEndian(), event)
pe.Msg = event
return pe
}
return nil
}
return nil
}

View File

@ -468,7 +468,6 @@ func parsePrioData(qdisc Qdisc, value []byte) error {
} }
func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
htb := qdisc.(*Htb) htb := qdisc.(*Htb)
for _, datum := range data { for _, datum := range data {
switch datum.Attr.Type { switch datum.Attr.Type {
@ -488,7 +487,6 @@ func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
} }
func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
fqCodel := qdisc.(*FqCodel) fqCodel := qdisc.(*FqCodel)
for _, datum := range data { for _, datum := range data {
@ -518,13 +516,11 @@ func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
func parseHfscData(qdisc Qdisc, data []byte) error { func parseHfscData(qdisc Qdisc, data []byte) error {
Hfsc := qdisc.(*Hfsc) Hfsc := qdisc.(*Hfsc)
native = nl.NativeEndian()
Hfsc.Defcls = native.Uint16(data) Hfsc.Defcls = native.Uint16(data)
return nil return nil
} }
func parseFqData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { func parseFqData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
fq := qdisc.(*Fq) fq := qdisc.(*Fq)
for _, datum := range data { for _, datum := range data {
switch datum.Attr.Type { switch datum.Attr.Type {
@ -589,7 +585,6 @@ func parseNetemData(qdisc Qdisc, value []byte) error {
} }
func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
tbf := qdisc.(*Tbf) tbf := qdisc.(*Tbf)
for _, datum := range data { for _, datum := range data {
switch datum.Attr.Type { switch datum.Attr.Type {
@ -711,3 +706,7 @@ func Xmittime(rate uint64, size uint32) uint32 {
// https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/tc/tc_core.c#n62 // https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/tc/tc_core.c#n62
return time2Tick(uint32(TIME_UNITS_PER_SEC * (float64(size) / float64(rate)))) return time2Tick(uint32(TIME_UNITS_PER_SEC * (float64(size) / float64(rate))))
} }
func Xmitsize(rate uint64, ticks uint32) uint32 {
return uint32((float64(rate) * float64(tick2Time(ticks))) / TIME_UNITS_PER_SEC)
}

View File

@ -278,3 +278,54 @@ func (h *Handle) RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error {
return execRdmaSetLink(req) 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

@ -11,6 +11,24 @@ type Scope uint8
type NextHopFlag int type NextHopFlag int
const (
RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
RT_FILTER_SCOPE
RT_FILTER_TYPE
RT_FILTER_TOS
RT_FILTER_IIF
RT_FILTER_OIF
RT_FILTER_DST
RT_FILTER_SRC
RT_FILTER_GW
RT_FILTER_TABLE
RT_FILTER_HOPLIMIT
RT_FILTER_PRIORITY
RT_FILTER_MARK
RT_FILTER_MASK
RT_FILTER_REALM
)
type Destination interface { type Destination interface {
Family() int Family() int
Decode([]byte) error Decode([]byte) error
@ -41,6 +59,7 @@ type Route struct {
MultiPath []*NexthopInfo MultiPath []*NexthopInfo
Protocol RouteProtocol Protocol RouteProtocol
Priority int Priority int
Family int
Table int Table int
Type int Type int
Tos int Tos int
@ -49,6 +68,7 @@ type Route struct {
NewDst Destination NewDst Destination
Encap Encap Encap Encap
Via Destination Via Destination
Realm int
MTU int MTU int
Window int Window int
Rtt int Rtt int
@ -94,6 +114,7 @@ func (r Route) String() string {
} }
elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags())) elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags()))
elems = append(elems, fmt.Sprintf("Table: %d", r.Table)) 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, " ")) return fmt.Sprintf("{%s}", strings.Join(elems, " "))
} }
@ -107,6 +128,7 @@ func (r Route) Equal(x Route) bool {
nexthopInfoSlice(r.MultiPath).Equal(x.MultiPath) && nexthopInfoSlice(r.MultiPath).Equal(x.MultiPath) &&
r.Protocol == x.Protocol && r.Protocol == x.Protocol &&
r.Priority == x.Priority && r.Priority == x.Priority &&
r.Realm == x.Realm &&
r.Table == x.Table && r.Table == x.Table &&
r.Type == x.Type && r.Type == x.Type &&
r.Tos == x.Tos && r.Tos == x.Tos &&

View File

@ -41,22 +41,6 @@ func (s Scope) String() string {
} }
} }
const (
RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
RT_FILTER_SCOPE
RT_FILTER_TYPE
RT_FILTER_TOS
RT_FILTER_IIF
RT_FILTER_OIF
RT_FILTER_DST
RT_FILTER_SRC
RT_FILTER_GW
RT_FILTER_TABLE
RT_FILTER_HOPLIMIT
RT_FILTER_PRIORITY
RT_FILTER_MARK
RT_FILTER_MASK
)
const ( const (
FLAG_ONLINK NextHopFlag = unix.RTNH_F_ONLINK FLAG_ONLINK NextHopFlag = unix.RTNH_F_ONLINK
@ -151,7 +135,6 @@ func (e *MPLSEncap) Decode(buf []byte) error {
if len(buf) < 4 { if len(buf) < 4 {
return fmt.Errorf("lack of bytes") return fmt.Errorf("lack of bytes")
} }
native := nl.NativeEndian()
l := native.Uint16(buf) l := native.Uint16(buf)
if len(buf) < int(l) { if len(buf) < int(l) {
return fmt.Errorf("lack of bytes") return fmt.Errorf("lack of bytes")
@ -167,7 +150,6 @@ func (e *MPLSEncap) Decode(buf []byte) error {
func (e *MPLSEncap) Encode() ([]byte, error) { func (e *MPLSEncap) Encode() ([]byte, error) {
s := nl.EncodeMPLSStack(e.Labels...) s := nl.EncodeMPLSStack(e.Labels...)
native := nl.NativeEndian()
hdr := make([]byte, 4) hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4)) native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.MPLS_IPTUNNEL_DST) native.PutUint16(hdr[2:], nl.MPLS_IPTUNNEL_DST)
@ -223,7 +205,6 @@ func (e *SEG6Encap) Decode(buf []byte) error {
if len(buf) < 4 { if len(buf) < 4 {
return fmt.Errorf("lack of bytes") return fmt.Errorf("lack of bytes")
} }
native := nl.NativeEndian()
// Get Length(l) & Type(typ) : 2 + 2 bytes // Get Length(l) & Type(typ) : 2 + 2 bytes
l := native.Uint16(buf) l := native.Uint16(buf)
if len(buf) < int(l) { if len(buf) < int(l) {
@ -243,7 +224,6 @@ func (e *SEG6Encap) Decode(buf []byte) error {
} }
func (e *SEG6Encap) Encode() ([]byte, error) { func (e *SEG6Encap) Encode() ([]byte, error) {
s, err := nl.EncodeSEG6Encap(e.Mode, e.Segments) s, err := nl.EncodeSEG6Encap(e.Mode, e.Segments)
native := nl.NativeEndian()
hdr := make([]byte, 4) hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4)) native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.SEG6_IPTUNNEL_SRH) native.PutUint16(hdr[2:], nl.SEG6_IPTUNNEL_SRH)
@ -253,7 +233,7 @@ func (e *SEG6Encap) String() string {
segs := make([]string, 0, len(e.Segments)) segs := make([]string, 0, len(e.Segments))
// append segment backwards (from n to 0) since seg#0 is the last segment. // append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- { 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), str := fmt.Sprintf("mode %s segs %d [ %s ]", nl.SEG6EncapModeString(e.Mode),
len(e.Segments), strings.Join(segs, " ")) len(e.Segments), strings.Join(segs, " "))
@ -304,7 +284,6 @@ func (e *SEG6LocalEncap) Decode(buf []byte) error {
if err != nil { if err != nil {
return err return err
} }
native := nl.NativeEndian()
for _, attr := range attrs { for _, attr := range attrs {
switch attr.Attr.Type { switch attr.Attr.Type {
case nl.SEG6_LOCAL_ACTION: case nl.SEG6_LOCAL_ACTION:
@ -334,7 +313,6 @@ func (e *SEG6LocalEncap) Decode(buf []byte) error {
} }
func (e *SEG6LocalEncap) Encode() ([]byte, error) { func (e *SEG6LocalEncap) Encode() ([]byte, error) {
var err error var err error
native := nl.NativeEndian()
res := make([]byte, 8) res := make([]byte, 8)
native.PutUint16(res, 8) // length native.PutUint16(res, 8) // length
native.PutUint16(res[2:], nl.SEG6_LOCAL_ACTION) native.PutUint16(res[2:], nl.SEG6_LOCAL_ACTION)
@ -425,7 +403,7 @@ func (e *SEG6LocalEncap) String() string {
segs := make([]string, 0, len(e.Segments)) segs := make([]string, 0, len(e.Segments))
//append segment backwards (from n to 0) since seg#0 is the last segment. //append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- { 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, " "))) strs = append(strs, fmt.Sprintf("segs %d [ %s ]", len(e.Segments), strings.Join(segs, " ")))
} }
@ -466,6 +444,152 @@ func (e *SEG6LocalEncap) Equal(x Encap) bool {
return true 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 { type Via struct {
AddrFamily int AddrFamily int
Addr net.IP Addr net.IP
@ -504,7 +628,6 @@ func (v *Via) Encode() ([]byte, error) {
} }
func (v *Via) Decode(b []byte) error { func (v *Via) Decode(b []byte) error {
native := nl.NativeEndian()
if len(b) < 6 { if len(b) < 6 {
return fmt.Errorf("decoding failed: buffer too small (%d bytes)", len(b)) return fmt.Errorf("decoding failed: buffer too small (%d bytes)", len(b))
} }
@ -552,14 +675,14 @@ func (h *Handle) RouteAppend(route *Route) error {
// RouteAddEcmp will add a route to the system. // RouteAddEcmp will add a route to the system.
func RouteAddEcmp(route *Route) error { func RouteAddEcmp(route *Route) error {
return pkgHandle.RouteAddEcmp(route) return pkgHandle.RouteAddEcmp(route)
} }
// RouteAddEcmp will add a route to the system. // RouteAddEcmp will add a route to the system.
func (h *Handle) RouteAddEcmp(route *Route) error { func (h *Handle) RouteAddEcmp(route *Route) error {
flags := unix.NLM_F_CREATE | unix.NLM_F_ACK flags := unix.NLM_F_CREATE | unix.NLM_F_ACK
req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags) req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags)
return h.routeHandle(route, req, nl.NewRtMsg()) return h.routeHandle(route, req, nl.NewRtMsg())
} }
// RouteReplace will add a route to the system. // RouteReplace will add a route to the system.
@ -635,7 +758,13 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
if err != nil { if err != nil {
return err 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 { if route.Src != nil {
@ -748,6 +877,11 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
native.PutUint32(b, uint32(route.Priority)) native.PutUint32(b, uint32(route.Priority))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PRIORITY, b)) 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 { if route.Tos > 0 {
msg.Tos = uint8(route.Tos) msg.Tos = uint8(route.Tos)
} }
@ -840,10 +974,7 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
req.AddData(attr) req.AddData(attr)
} }
var ( b := make([]byte, 4)
b = make([]byte, 4)
native = nl.NativeEndian()
)
native.PutUint32(b, uint32(route.LinkIndex)) native.PutUint32(b, uint32(route.LinkIndex))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b)) req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
@ -882,8 +1013,9 @@ func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, e
// All rules must be defined in RouteFilter struct // All rules must be defined in RouteFilter struct
func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) { func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP) req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(family) rtmsg := nl.NewRtMsg()
req.AddData(infmsg) rtmsg.Family = uint8(family)
req.AddData(rtmsg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE)
if err != nil { if err != nil {
@ -919,6 +1051,8 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
continue continue
case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos: case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos:
continue continue
case filterMask&RT_FILTER_REALM != 0 && route.Realm != filter.Realm:
continue
case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex: case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex:
continue continue
case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex: case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex:
@ -956,9 +1090,9 @@ func deserializeRoute(m []byte) (Route, error) {
Type: int(msg.Type), Type: int(msg.Type),
Tos: int(msg.Tos), Tos: int(msg.Tos),
Flags: int(msg.Flags), Flags: int(msg.Flags),
Family: int(msg.Family),
} }
native := nl.NativeEndian()
var encap, encapType syscall.NetlinkRouteAttr var encap, encapType syscall.NetlinkRouteAttr
for _, attr := range attrs { for _, attr := range attrs {
switch attr.Attr.Type { switch attr.Attr.Type {
@ -985,6 +1119,8 @@ func deserializeRoute(m []byte) (Route, error) {
route.ILinkIndex = int(native.Uint32(attr.Value[0:4])) route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_PRIORITY: case unix.RTA_PRIORITY:
route.Priority = int(native.Uint32(attr.Value[0:4])) 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: case unix.RTA_TABLE:
route.Table = int(native.Uint32(attr.Value[0:4])) route.Table = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_MULTIPATH: case unix.RTA_MULTIPATH:
@ -1140,6 +1276,11 @@ func deserializeRoute(m []byte) (Route, error) {
if err := e.Decode(encap.Value); err != nil { if err := e.Decode(encap.Value); err != nil {
return route, err return route, err
} }
case nl.LWTUNNEL_ENCAP_BPF:
e = &BpfEncap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
} }
route.Encap = e route.Encap = e
} }
@ -1150,6 +1291,8 @@ func deserializeRoute(m []byte) (Route, error) {
// RouteGetOptions contains a set of options to use with // RouteGetOptions contains a set of options to use with
// RouteGetWithOptions // RouteGetWithOptions
type RouteGetOptions struct { type RouteGetOptions struct {
Iif string
Oif string
VrfName string VrfName string
SrcAddr net.IP SrcAddr net.IP
} }
@ -1198,10 +1341,31 @@ func (h *Handle) RouteGetWithOptions(destination net.IP, options *RouteGetOption
if err != nil { if err != nil {
return nil, err return nil, err
} }
var ( b := make([]byte, 4)
b = make([]byte, 4) native.PutUint32(b, uint32(link.Attrs().Index))
native = nl.NativeEndian()
) 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)) native.PutUint32(b, uint32(link.Attrs().Index))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b)) req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
@ -1298,7 +1462,8 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
if err != nil { if err != nil {
if cberr != nil { if cberr != nil {
cberr(err) cberr(fmt.Errorf("Receive failed: %v",
err))
} }
return return
} }
@ -1313,22 +1478,22 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
continue continue
} }
if m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if error == 0 {
continue continue
} }
if cberr != nil { if cberr != nil {
cberr(syscall.Errno(-error)) cberr(fmt.Errorf("error message: %v",
syscall.Errno(-error)))
} }
return continue
} }
route, err := deserializeRoute(m.Data) route, err := deserializeRoute(m.Data)
if err != nil { if err != nil {
if cberr != nil { if cberr != nil {
cberr(err) cberr(err)
} }
return continue
} }
ch <- RouteUpdate{Type: m.Header.Type, Route: route} ch <- RouteUpdate{Type: m.Header.Type, Route: route}
} }

View File

@ -25,10 +25,22 @@ type Rule struct {
Invert bool Invert bool
Dport *RulePortRange Dport *RulePortRange
Sport *RulePortRange Sport *RulePortRange
IPProto int
} }
func (r Rule) String() string { 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. // NewRule return empty rules.

View File

@ -97,8 +97,6 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
req.AddData(rtAttrs[i]) req.AddData(rtAttrs[i])
} }
native := nl.NativeEndian()
if rule.Priority >= 0 { if rule.Priority >= 0 {
b := make([]byte, 4) b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Priority)) native.PutUint32(b, uint32(rule.Priority))
@ -154,6 +152,12 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b)) req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
} }
if rule.IPProto > 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.IPProto))
req.AddData(nl.NewRtAttr(nl.FRA_IP_PROTO, b))
}
if rule.Dport != nil { if rule.Dport != nil {
b := rule.Dport.toRtAttrData() b := rule.Dport.toRtAttrData()
req.AddData(nl.NewRtAttr(nl.FRA_DPORT_RANGE, b)) req.AddData(nl.NewRtAttr(nl.FRA_DPORT_RANGE, b))
@ -199,7 +203,6 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
return nil, err return nil, err
} }
native := nl.NativeEndian()
var res = make([]Rule, 0) var res = make([]Rule, 0)
for i := range msgs { for i := range msgs {
msg := nl.DeserializeRtMsg(msgs[i]) msg := nl.DeserializeRtMsg(msgs[i])
@ -232,7 +235,7 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
case nl.FRA_FWMASK: case nl.FRA_FWMASK:
rule.Mask = int(native.Uint32(attrs[j].Value[0:4])) rule.Mask = int(native.Uint32(attrs[j].Value[0:4]))
case nl.FRA_TUN_ID: 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: case nl.FRA_IIFNAME:
rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1]) rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
case nl.FRA_OIFNAME: case nl.FRA_OIFNAME:
@ -253,6 +256,8 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
rule.Goto = int(native.Uint32(attrs[j].Value[0:4])) rule.Goto = int(native.Uint32(attrs[j].Value[0:4]))
case nl.FRA_PRIORITY: case nl.FRA_PRIORITY:
rule.Priority = int(native.Uint32(attrs[j].Value[0:4])) rule.Priority = int(native.Uint32(attrs[j].Value[0:4]))
case nl.FRA_IP_PROTO:
rule.IPProto = int(native.Uint32(attrs[j].Value[0:4]))
case nl.FRA_DPORT_RANGE: case nl.FRA_DPORT_RANGE:
rule.Dport = NewRulePortRange(native.Uint16(attrs[j].Value[0:2]), native.Uint16(attrs[j].Value[2:4])) rule.Dport = NewRulePortRange(native.Uint16(attrs[j].Value[0:2]), native.Uint16(attrs[j].Value[2:4]))
case nl.FRA_SPORT_RANGE: case nl.FRA_SPORT_RANGE:

View File

@ -172,12 +172,56 @@ func SocketGet(local, remote net.Addr) (*Socket, error) {
return sock, nil 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) { 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 { if err != nil {
return nil, err 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() defer s.Close()
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP) req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
@ -189,18 +233,17 @@ func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
}) })
s.Send(req) s.Send(req)
var result []*InetDiagTCPInfoResp
loop: loop:
for { for {
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
if err != nil { if err != nil {
return nil, err return err
} }
if from.Pid != nl.PidKernel { 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 { 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 { for _, m := range msgs {
@ -208,28 +251,15 @@ loop:
case unix.NLMSG_DONE: case unix.NLMSG_DONE:
break loop break loop
case unix.NLMSG_ERROR: case unix.NLMSG_ERROR:
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
return nil, syscall.Errno(-error) return syscall.Errno(-error)
} }
sockInfo := &Socket{} if err := receiver(m); err != nil {
if err := sockInfo.deserialize(m.Data); err != nil { return err
return nil, err
} }
attrs, err := nl.ParseRouteAttr(m.Data[sizeofSocket:])
if err != nil {
return nil, err
}
res, err := attrsToInetDiagTCPInfoResp(attrs, sockInfo)
if err != nil {
return nil, err
}
result = append(result, res)
} }
} }
return result, nil return nil
} }
func attrsToInetDiagTCPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagTCPInfoResp, error) { func attrsToInetDiagTCPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagTCPInfoResp, error) {

View File

@ -93,8 +93,10 @@ func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error {
req.AddData(out) req.AddData(out)
} }
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid))) if policy.Ifid != 0 {
req.AddData(ifId) ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
req.AddData(ifId)
}
_, err := req.Execute(unix.NETLINK_XFRM, 0) _, err := req.Execute(unix.NETLINK_XFRM, 0)
return err return err
@ -189,8 +191,10 @@ func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPo
req.AddData(out) req.AddData(out)
} }
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid))) if policy.Ifid != 0 {
req.AddData(ifId) ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
req.AddData(ifId)
}
resType := nl.XFRM_MSG_NEWPOLICY resType := nl.XFRM_MSG_NEWPOLICY
if nlProto == nl.XFRM_MSG_DELPOLICY { if nlProto == nl.XFRM_MSG_DELPOLICY {

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 // A state with spi 0 can't be deleted so don't allow it to be set
if state.Spi == 0 { 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) req := h.newNetlinkRequest(nlProto, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
@ -167,8 +167,10 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
} }
} }
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid))) if state.Ifid != 0 {
req.AddData(ifId) ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
req.AddData(ifId)
}
_, err := req.Execute(unix.NETLINK_XFRM, 0) _, err := req.Execute(unix.NETLINK_XFRM, 0)
return err return err
@ -281,8 +283,10 @@ func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState
req.AddData(out) req.AddData(out)
} }
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid))) if state.Ifid != 0 {
req.AddData(ifId) ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
req.AddData(ifId)
}
resType := nl.XFRM_MSG_NEWSA resType := nl.XFRM_MSG_NEWSA
if nlProto == nl.XFRM_MSG_DELSA { if nlProto == nl.XFRM_MSG_DELSA {

2
vendor/modules.txt vendored
View File

@ -144,7 +144,7 @@ github.com/safchain/ethtool
# github.com/sirupsen/logrus v1.8.1 # github.com/sirupsen/logrus v1.8.1
## explicit; go 1.13 ## explicit; go 1.13
github.com/sirupsen/logrus github.com/sirupsen/logrus
# github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 # github.com/vishvananda/netlink v1.2.0-beta
## explicit; go 1.12 ## explicit; go 1.12
github.com/vishvananda/netlink github.com/vishvananda/netlink
github.com/vishvananda/netlink/nl github.com/vishvananda/netlink/nl