vendor folder bump.

This commit is contained in:
Sebastian Sch
2018-11-18 15:43:35 +02:00
parent 18874aac7d
commit dc536993e2
458 changed files with 66427 additions and 13303 deletions

View File

@ -1,4 +1,7 @@
language: go
go:
- "1.10.x"
- "1.11.x"
before_script:
# make sure we keep path in tact when we sudo
- sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
@ -9,5 +12,6 @@ before_script:
- sudo modprobe nf_conntrack_netlink
- sudo modprobe nf_conntrack_ipv4
- sudo modprobe nf_conntrack_ipv6
- sudo modprobe sch_hfsc
install:
- go get github.com/vishvananda/netns

5
vendor/github.com/vishvananda/netlink/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,5 @@
# Changelog
## 1.0.0 (2018-03-15)
Initial release tagging

View File

@ -3,7 +3,8 @@ DIRS := \
nl
DEPS = \
github.com/vishvananda/netns
github.com/vishvananda/netns \
golang.org/x/sys/unix
uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
testdirs = $(call uniq,$(foreach d,$(1),$(dir $(wildcard $(d)/*_test.go))))
@ -18,7 +19,7 @@ $(call goroot,$(DEPS)):
.PHONY: $(call testdirs,$(DIRS))
$(call testdirs,$(DIRS)):
sudo -E go test -test.parallel 4 -timeout 60s -v github.com/vishvananda/netlink/$@
go test -test.exec sudo -test.parallel 4 -timeout 60s -test.v github.com/vishvananda/netlink/$@
$(call fmt,$(call testdirs,$(DIRS))):
! gofmt -l $(subst fmt-,,$@)/*.go | grep -q .

View File

@ -38,15 +38,18 @@ Add a new bridge and add eth1 into it:
package main
import (
"net"
"fmt"
"github.com/vishvananda/netlink"
)
func main() {
la := netlink.NewLinkAttrs()
la.Name = "foo"
mybridge := &netlink.Bridge{la}}
_ := netlink.LinkAdd(mybridge)
mybridge := &netlink.Bridge{LinkAttrs: la}
err := netlink.LinkAdd(mybridge)
if err != nil {
fmt.Printf("could not add %s: %v\n", la.Name, err)
}
eth1, _ := netlink.LinkByName("eth1")
netlink.LinkSetMaster(eth1, mybridge)
}
@ -63,7 +66,6 @@ Add a new ip address to loopback:
package main
import (
"net"
"github.com/vishvananda/netlink"
)
@ -87,3 +89,4 @@ There are also a few pieces of low level netlink functionality that still
need to be implemented. Routing rules are not in place and some of the
more advanced link types. Hopefully there is decent structure and testing
in place to make these fairly straightforward to add.

View File

@ -2,13 +2,13 @@ package netlink
import (
"fmt"
"log"
"net"
"strings"
"syscall"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
// IFA_FLAGS is a u32 attribute.
@ -23,7 +23,7 @@ func AddrAdd(link Link, addr *Addr) error {
// AddrAdd will add an IP address to a link device.
// Equivalent to: `ip addr add $addr dev $link`
func (h *Handle) AddrAdd(link Link, addr *Addr) error {
req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
return h.addrHandle(link, addr, req)
}
@ -36,7 +36,7 @@ func AddrReplace(link Link, addr *Addr) error {
// AddrReplace will replace (or, if not present, add) an IP address on a link device.
// Equivalent to: `ip addr replace $addr dev $link`
func (h *Handle) AddrReplace(link Link, addr *Addr) error {
req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK)
return h.addrHandle(link, addr, req)
}
@ -49,7 +49,7 @@ func AddrDel(link Link, addr *Addr) error {
// AddrDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func (h *Handle) AddrDel(link Link, addr *Addr) error {
req := h.newNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK)
return h.addrHandle(link, addr, req)
}
@ -65,7 +65,11 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
msg := nl.NewIfAddrmsg(family)
msg.Index = uint32(base.Index)
msg.Scope = uint8(addr.Scope)
prefixlen, _ := addr.Mask.Size()
mask := addr.Mask
if addr.Peer != nil {
mask = addr.Peer.Mask
}
prefixlen, masklen := mask.Size()
msg.Prefixlen = uint8(prefixlen)
req.AddData(msg)
@ -76,7 +80,7 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
localAddrData = addr.IP.To16()
}
localData := nl.NewRtAttr(syscall.IFA_LOCAL, localAddrData)
localData := nl.NewRtAttr(unix.IFA_LOCAL, localAddrData)
req.AddData(localData)
var peerAddrData []byte
if addr.Peer != nil {
@ -89,7 +93,7 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
peerAddrData = localAddrData
}
addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, peerAddrData)
addressData := nl.NewRtAttr(unix.IFA_ADDRESS, peerAddrData)
req.AddData(addressData)
if addr.Flags != 0 {
@ -103,16 +107,34 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
}
}
if addr.Broadcast != nil {
req.AddData(nl.NewRtAttr(syscall.IFA_BROADCAST, addr.Broadcast))
if family == FAMILY_V4 {
if addr.Broadcast == nil {
calcBroadcast := make(net.IP, masklen/8)
for i := range localAddrData {
calcBroadcast[i] = localAddrData[i] | ^mask[i]
}
addr.Broadcast = calcBroadcast
}
req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast))
if addr.Label != "" {
labelData := nl.NewRtAttr(unix.IFA_LABEL, nl.ZeroTerminated(addr.Label))
req.AddData(labelData)
}
}
if addr.Label != "" {
labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
req.AddData(labelData)
// 0 is the default value for these attributes. However, 0 means "expired", while the least-surprising default
// value should be "forever". To compensate for that, only add the attributes if at least one of the values is
// non-zero, which means the caller has explicitly set them
if addr.ValidLft > 0 || addr.PreferedLft > 0 {
cachedata := nl.IfaCacheInfo{
IfaValid: uint32(addr.ValidLft),
IfaPrefered: uint32(addr.PreferedLft),
}
req.AddData(nl.NewRtAttr(unix.IFA_CACHEINFO, cachedata.Serialize()))
}
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -127,11 +149,11 @@ func AddrList(link Link, family int) ([]Addr, error) {
// Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family.
func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
req := h.newNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(family)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
if err != nil {
return nil, err
}
@ -183,21 +205,25 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) {
var local, dst *net.IPNet
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.IFA_ADDRESS:
case unix.IFA_ADDRESS:
dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
}
addr.Peer = dst
case syscall.IFA_LOCAL:
case unix.IFA_LOCAL:
// iproute2 manual:
// If a peer address is specified, the local address
// cannot have a prefix length. The network prefix is
// associated with the peer rather than with the local
// address.
n := 8 * len(attr.Value)
local = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
Mask: net.CIDRMask(n, n),
}
addr.IPNet = local
case syscall.IFA_BROADCAST:
case unix.IFA_BROADCAST:
addr.Broadcast = attr.Value
case syscall.IFA_LABEL:
case unix.IFA_LABEL:
addr.Label = string(attr.Value[:len(attr.Value)-1])
case IFA_FLAGS:
addr.Flags = int(native.Uint32(attr.Value[0:4]))
@ -208,12 +234,24 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) {
}
}
// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
// libnl addr.c comment:
// IPv6 sends the local address as IFA_ADDRESS with no
// IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS
// with IFA_ADDRESS being the peer address if they differ
//
// But obviously, as there are IPv6 PtP addresses, too,
// IFA_LOCAL should also be handled for IPv6.
if local != nil {
addr.IPNet = local
if family == FAMILY_V4 && local.IP.Equal(dst.IP) {
addr.IPNet = dst
} else {
addr.IPNet = local
addr.Peer = dst
}
} else {
addr.IPNet = dst
}
addr.Scope = int(msg.Scope)
return
@ -232,17 +270,36 @@ type AddrUpdate struct {
// AddrSubscribe takes a chan down which notifications will be sent
// when addresses change. Close the 'done' chan to stop subscription.
func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
return addrSubscribe(netns.None(), netns.None(), ch, done)
return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}
// AddrSubscribeAt works like AddrSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
return addrSubscribe(ns, netns.None(), ch, done)
return addrSubscribeAt(ns, netns.None(), ch, done, nil, false)
}
func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR)
// AddrSubscribeOptions contains a set of options to use with
// AddrSubscribeWithOptions.
type AddrSubscribeOptions struct {
Namespace *netns.NsHandle
ErrorCallback func(error)
ListExisting bool
}
// AddrSubscribeWithOptions work like AddrSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error {
if options.Namespace == nil {
none := netns.None()
options.Namespace = &none
}
return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}
func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR)
if err != nil {
return err
}
@ -252,30 +309,59 @@ func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-cha
s.Close()
}()
}
if listExisting {
req := pkgHandle.newNetlinkRequest(unix.RTM_GETADDR,
unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(infmsg)
if err := s.Send(req); err != nil {
return err
}
}
go func() {
defer close(ch)
for {
msgs, err := s.Receive()
if err != nil {
log.Printf("netlink.AddrSubscribe: Receive() error: %v", err)
if cberr != nil {
cberr(err)
}
return
}
for _, m := range msgs {
msgType := m.Header.Type
if msgType != syscall.RTM_NEWADDR && msgType != syscall.RTM_DELADDR {
log.Printf("netlink.AddrSubscribe: bad message type: %d", msgType)
if m.Header.Type == unix.NLMSG_DONE {
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
}
return
}
msgType := m.Header.Type
if msgType != unix.RTM_NEWADDR && msgType != unix.RTM_DELADDR {
if cberr != nil {
cberr(fmt.Errorf("bad message type: %d", msgType))
}
return
}
addr, _, ifindex, err := parseAddr(m.Data)
if err != nil {
log.Printf("netlink.AddrSubscribe: could not parse address: %v", err)
continue
if cberr != nil {
cberr(fmt.Errorf("could not parse address: %v", err))
}
return
}
ch <- AddrUpdate{LinkAddress: *addr.IPNet,
LinkIndex: ifindex,
NewAddr: msgType == syscall.RTM_NEWADDR,
NewAddr: msgType == unix.RTM_NEWADDR,
Flags: addr.Flags,
Scope: addr.Scope,
PreferedLft: addr.PreferedLft,

View File

@ -1,49 +1,12 @@
package netlink
/*
#include <asm/types.h>
#include <asm/unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
import (
"unsafe"
static int load_simple_bpf(int prog_type, int ret) {
#ifdef __NR_bpf
// { return ret; }
__u64 __attribute__((aligned(8))) insns[] = {
0x00000000000000b7ull | ((__u64)ret<<32),
0x0000000000000095ull,
};
__u8 __attribute__((aligned(8))) license[] = "ASL2";
// Copied from a header file since libc is notoriously slow to update.
// The call will succeed or fail and that will be our indication on
// whether or not it is supported.
struct {
__u32 prog_type;
__u32 insn_cnt;
__u64 insns;
__u64 license;
__u32 log_level;
__u32 log_size;
__u64 log_buf;
__u32 kern_version;
} __attribute__((aligned(8))) attr = {
.prog_type = prog_type,
.insn_cnt = 2,
.insns = (uintptr_t)&insns,
.license = (uintptr_t)&license,
};
return syscall(__NR_bpf, 5, &attr, sizeof(attr));
#else
errno = EINVAL;
return -1;
#endif
}
*/
import "C"
"golang.org/x/sys/unix"
)
type BpfProgType C.int
type BpfProgType uint32
const (
BPF_PROG_TYPE_UNSPEC BpfProgType = iota
@ -55,8 +18,36 @@ const (
BPF_PROG_TYPE_XDP
)
// loadSimpleBpf loads a trivial bpf program for testing purposes
func loadSimpleBpf(progType BpfProgType, ret int) (int, error) {
fd, err := C.load_simple_bpf(C.int(progType), C.int(ret))
return int(fd), err
type BPFAttr struct {
ProgType uint32
InsnCnt uint32
Insns uintptr
License uintptr
LogLevel uint32
LogSize uint32
LogBuf uintptr
KernVersion uint32
}
// loadSimpleBpf loads a trivial bpf program for testing purposes.
func loadSimpleBpf(progType BpfProgType, ret uint32) (int, error) {
insns := []uint64{
0x00000000000000b7 | (uint64(ret) << 32),
0x0000000000000095,
}
license := []byte{'A', 'S', 'L', '2', '\x00'}
attr := BPFAttr{
ProgType: uint32(progType),
InsnCnt: uint32(len(insns)),
Insns: uintptr(unsafe.Pointer(&insns[0])),
License: uintptr(unsafe.Pointer(&license[0])),
}
fd, _, errno := unix.Syscall(unix.SYS_BPF,
5, /* bpf cmd */
uintptr(unsafe.Pointer(&attr)),
unsafe.Sizeof(attr))
if errno != 0 {
return 0, errno
}
return int(fd), nil
}

View File

@ -2,9 +2,9 @@ package netlink
import (
"fmt"
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// BridgeVlanList gets a map of device id to bridge vlan infos.
@ -16,12 +16,12 @@ func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))
req.AddData(nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if err != nil {
return nil, err
}
@ -35,7 +35,7 @@ func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
}
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.IFLA_AF_SPEC:
case unix.IFLA_AF_SPEC:
//nested attr
nestAttrs, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
@ -63,7 +63,7 @@ func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) err
// BridgeVlanAdd adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(syscall.RTM_SETLINK, link, vid, pvid, untagged, self, master)
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, pvid, untagged, self, master)
}
// BridgeVlanDel adds a new vlan filter entry
@ -75,19 +75,19 @@ func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) err
// BridgeVlanDel adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(syscall.RTM_DELLINK, link, vid, pvid, untagged, self, master)
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, pvid, untagged, self, master)
}
func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged, self, master bool) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(cmd, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(cmd, unix.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
msg.Index = int32(base.Index)
req.AddData(msg)
br := nl.NewRtAttr(nl.IFLA_AF_SPEC, nil)
br := nl.NewRtAttr(unix.IFLA_AF_SPEC, nil)
var flags uint16
if self {
flags |= nl.BRIDGE_FLAGS_SELF
@ -96,7 +96,7 @@ func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged
flags |= nl.BRIDGE_FLAGS_MASTER
}
if flags > 0 {
nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags))
br.AddRtAttr(nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags))
}
vlanInfo := &nl.BridgeVlanInfo{Vid: vid}
if pvid {
@ -105,9 +105,9 @@ func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged
if untagged {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED
}
nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
req.AddData(br)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
if err != nil {
return err
}

View File

@ -4,25 +4,76 @@ import (
"fmt"
)
// Class interfaces for all classes
type Class interface {
Attrs() *ClassAttrs
Type() string
}
// Generic networking statistics for netlink users.
// This file contains "gnet_" prefixed structs and relevant functions.
// See Documentation/networking/getn_stats.txt in Linux source code for more details.
// GnetStatsBasic Ref: struct gnet_stats_basic { ... }
type GnetStatsBasic struct {
Bytes uint64 // number of seen bytes
Packets uint32 // number of seen packets
}
// GnetStatsRateEst Ref: struct gnet_stats_rate_est { ... }
type GnetStatsRateEst struct {
Bps uint32 // current byte rate
Pps uint32 // current packet rate
}
// GnetStatsRateEst64 Ref: struct gnet_stats_rate_est64 { ... }
type GnetStatsRateEst64 struct {
Bps uint64 // current byte rate
Pps uint64 // current packet rate
}
// GnetStatsQueue Ref: struct gnet_stats_queue { ... }
type GnetStatsQueue struct {
Qlen uint32 // queue length
Backlog uint32 // backlog size of queue
Drops uint32 // number of dropped packets
Requeues uint32 // number of requues
Overlimits uint32 // number of enqueues over the limit
}
// ClassStatistics representaion based on generic networking statisticsfor netlink.
// See Documentation/networking/gen_stats.txt in Linux source code for more details.
type ClassStatistics struct {
Basic *GnetStatsBasic
Queue *GnetStatsQueue
RateEst *GnetStatsRateEst
}
// NewClassStatistics Construct a ClassStatistics struct which fields are all initialized by 0.
func NewClassStatistics() *ClassStatistics {
return &ClassStatistics{
Basic: &GnetStatsBasic{},
Queue: &GnetStatsQueue{},
RateEst: &GnetStatsRateEst{},
}
}
// ClassAttrs represents a netlink class. A filter is associated with a link,
// has a handle and a parent. The root filter of a device should have a
// parent == HANDLE_ROOT.
type ClassAttrs struct {
LinkIndex int
Handle uint32
Parent uint32
Leaf uint32
LinkIndex int
Handle uint32
Parent uint32
Leaf uint32
Statistics *ClassStatistics
}
func (q ClassAttrs) String() string {
return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Leaf: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Leaf)
}
// HtbClassAttrs stores the attributes of HTB class
type HtbClassAttrs struct {
// TODO handle all attributes
Rate uint64
@ -54,10 +105,12 @@ func (q HtbClass) String() string {
return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer)
}
// Attrs returns the class attributes
func (q *HtbClass) Attrs() *ClassAttrs {
return &q.ClassAttrs
}
// Type return the class type
func (q *HtbClass) Type() string {
return "htb"
}
@ -69,10 +122,90 @@ type GenericClass struct {
ClassType string
}
// Attrs return the class attributes
func (class *GenericClass) Attrs() *ClassAttrs {
return &class.ClassAttrs
}
// Type retrun the class type
func (class *GenericClass) Type() string {
return class.ClassType
}
// ServiceCurve is the way the HFSC curve are represented
type ServiceCurve struct {
m1 uint32
d uint32
m2 uint32
}
// Attrs return the parameters of the service curve
func (c *ServiceCurve) Attrs() (uint32, uint32, uint32) {
return c.m1, c.d, c.m2
}
// HfscClass is a representation of the HFSC class
type HfscClass struct {
ClassAttrs
Rsc ServiceCurve
Fsc ServiceCurve
Usc ServiceCurve
}
// SetUsc sets the Usc curve
func (hfsc *HfscClass) SetUsc(m1 uint32, d uint32, m2 uint32) {
hfsc.Usc = ServiceCurve{m1: m1 / 8, d: d, m2: m2 / 8}
}
// SetFsc sets the Fsc curve
func (hfsc *HfscClass) SetFsc(m1 uint32, d uint32, m2 uint32) {
hfsc.Fsc = ServiceCurve{m1: m1 / 8, d: d, m2: m2 / 8}
}
// SetRsc sets the Rsc curve
func (hfsc *HfscClass) SetRsc(m1 uint32, d uint32, m2 uint32) {
hfsc.Rsc = ServiceCurve{m1: m1 / 8, d: d, m2: m2 / 8}
}
// SetSC implements the SC from the tc CLI
func (hfsc *HfscClass) SetSC(m1 uint32, d uint32, m2 uint32) {
hfsc.Rsc = ServiceCurve{m1: m1 / 8, d: d, m2: m2 / 8}
hfsc.Fsc = ServiceCurve{m1: m1 / 8, d: d, m2: m2 / 8}
}
// SetUL implements the UL from the tc CLI
func (hfsc *HfscClass) SetUL(m1 uint32, d uint32, m2 uint32) {
hfsc.Usc = ServiceCurve{m1: m1 / 8, d: d, m2: m2 / 8}
}
// SetLS implemtens the LS from the tc CLI
func (hfsc *HfscClass) SetLS(m1 uint32, d uint32, m2 uint32) {
hfsc.Fsc = ServiceCurve{m1: m1 / 8, d: d, m2: m2 / 8}
}
// NewHfscClass returns a new HFSC struct with the set parameters
func NewHfscClass(attrs ClassAttrs) *HfscClass {
return &HfscClass{
ClassAttrs: attrs,
Rsc: ServiceCurve{},
Fsc: ServiceCurve{},
Usc: ServiceCurve{},
}
}
func (hfsc *HfscClass) String() string {
return fmt.Sprintf(
"{%s -- {RSC: {m1=%d d=%d m2=%d}} {FSC: {m1=%d d=%d m2=%d}} {USC: {m1=%d d=%d m2=%d}}}",
hfsc.Attrs(), hfsc.Rsc.m1*8, hfsc.Rsc.d, hfsc.Rsc.m2*8, hfsc.Fsc.m1*8, hfsc.Fsc.d, hfsc.Fsc.m2*8, hfsc.Usc.m1*8, hfsc.Usc.d, hfsc.Usc.m2*8,
)
}
// Attrs return the Hfsc parameters
func (hfsc *HfscClass) Attrs() *ClassAttrs {
return &hfsc.ClassAttrs
}
// Type return the type of the class
func (hfsc *HfscClass) Type() string {
return "hfsc"
}

View File

@ -1,13 +1,34 @@
package netlink
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// NOTE: function is in here because it uses other linux functions
// Internal tc_stats representation in Go struct.
// This is for internal uses only to deserialize the payload of rtattr.
// After the deserialization, this should be converted into the canonical stats
// struct, ClassStatistics, in case of statistics of a class.
// Ref: struct tc_stats { ... }
type tcStats struct {
Bytes uint64 // Number of enqueued bytes
Packets uint32 // Number of enqueued packets
Drops uint32 // Packets dropped because of lack of resources
Overlimits uint32 // Number of throttle events when this flow goes out of allocated bandwidth
Bps uint32 // Current flow byte rate
Pps uint32 // Current flow packet rate
Qlen uint32
Backlog uint32
}
// NewHtbClass NOTE: function is in here because it uses other linux functions
func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
mtu := 1600
rate := cattrs.Rate / 8
@ -50,7 +71,7 @@ func ClassDel(class Class) error {
// ClassDel will delete a class from the system.
// Equivalent to: `tc class del $class`
func (h *Handle) ClassDel(class Class) error {
return h.classModify(syscall.RTM_DELTCLASS, 0, class)
return h.classModify(unix.RTM_DELTCLASS, 0, class)
}
// ClassChange will change a class in place
@ -64,7 +85,7 @@ func ClassChange(class Class) error {
// Equivalent to: `tc class change $class`
// The parent and handle MUST NOT be changed.
func (h *Handle) ClassChange(class Class) error {
return h.classModify(syscall.RTM_NEWTCLASS, 0, class)
return h.classModify(unix.RTM_NEWTCLASS, 0, class)
}
// ClassReplace will replace a class to the system.
@ -82,7 +103,7 @@ func ClassReplace(class Class) error {
// If a class already exist with this parent/handle pair, the class is changed.
// If a class does not already exist with this parent/handle, a new class is created.
func (h *Handle) ClassReplace(class Class) error {
return h.classModify(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE, class)
return h.classModify(unix.RTM_NEWTCLASS, unix.NLM_F_CREATE, class)
}
// ClassAdd will add a class to the system.
@ -95,14 +116,14 @@ func ClassAdd(class Class) error {
// Equivalent to: `tc class add $class`
func (h *Handle) ClassAdd(class Class) error {
return h.classModify(
syscall.RTM_NEWTCLASS,
syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
unix.RTM_NEWTCLASS,
unix.NLM_F_CREATE|unix.NLM_F_EXCL,
class,
)
}
func (h *Handle) classModify(cmd, flags int, class Class) error {
req := h.newNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
base := class.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
@ -112,12 +133,12 @@ func (h *Handle) classModify(cmd, flags int, class Class) error {
}
req.AddData(msg)
if cmd != syscall.RTM_DELTCLASS {
if cmd != unix.RTM_DELTCLASS {
if err := classPayload(req, class); err != nil {
return err
}
}
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -125,7 +146,9 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if htb, ok := class.(*HtbClass); ok {
switch class.Type() {
case "htb":
htb := class.(*HtbClass)
opt := nl.TcHtbCopt{}
opt.Buffer = htb.Buffer
opt.Cbuffer = htb.Cbuffer
@ -141,18 +164,27 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
var rtab [256]uint32
var ctab [256]uint32
tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
if CalcRtable(&tcrate, rtab, cellLog, uint32(mtu), linklayer) < 0 {
if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 {
return errors.New("HTB: failed to calculate rate table")
}
opt.Rate = tcrate
tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
if CalcRtable(&tcceil, ctab, ccellLog, uint32(mtu), linklayer) < 0 {
if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 {
return errors.New("HTB: failed to calculate ceil rate table")
}
opt.Ceil = tcceil
nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize())
nl.NewRtAttrChild(options, nl.TCA_HTB_RTAB, SerializeRtab(rtab))
nl.NewRtAttrChild(options, nl.TCA_HTB_CTAB, SerializeRtab(ctab))
options.AddRtAttr(nl.TCA_HTB_PARMS, opt.Serialize())
options.AddRtAttr(nl.TCA_HTB_RTAB, SerializeRtab(rtab))
options.AddRtAttr(nl.TCA_HTB_CTAB, SerializeRtab(ctab))
case "hfsc":
hfsc := class.(*HfscClass)
opt := nl.HfscCopt{}
opt.Rsc.Set(hfsc.Rsc.Attrs())
opt.Fsc.Set(hfsc.Fsc.Attrs())
opt.Usc.Set(hfsc.Usc.Attrs())
options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
}
req.AddData(options)
return nil
@ -169,7 +201,7 @@ func ClassList(link Link, parent uint32) ([]Class, error) {
// Equivalent to: `tc class show`.
// Generally returns nothing if link and parent are not specified.
func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
req := h.newNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Parent: parent,
@ -181,7 +213,7 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
}
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTCLASS)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
if err != nil {
return nil, err
}
@ -196,9 +228,10 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
}
base := ClassAttrs{
LinkIndex: int(msg.Ifindex),
Handle: msg.Handle,
Parent: msg.Parent,
LinkIndex: int(msg.Ifindex),
Handle: msg.Handle,
Parent: msg.Parent,
Statistics: nil,
}
var class Class
@ -210,6 +243,8 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
switch classType {
case "htb":
class = &HtbClass{}
case "hfsc":
class = &HfscClass{}
default:
class = &GenericClass{ClassType: classType}
}
@ -224,6 +259,26 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
if err != nil {
return nil, err
}
case "hfsc":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
_, err = parseHfscClassData(class, data)
if err != nil {
return nil, err
}
}
// For backward compatibility.
case nl.TCA_STATS:
base.Statistics, err = parseTcStats(attr.Value)
if err != nil {
return nil, err
}
case nl.TCA_STATS2:
base.Statistics, err = parseTcStats2(attr.Value)
if err != nil {
return nil, err
}
}
}
@ -252,3 +307,78 @@ func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, erro
}
return detailed, nil
}
func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
hfsc := class.(*HfscClass)
detailed := false
for _, datum := range data {
m1, d, m2 := nl.DeserializeHfscCurve(datum.Value).Attrs()
switch datum.Attr.Type {
case nl.TCA_HFSC_RSC:
hfsc.Rsc = ServiceCurve{m1: m1, d: d, m2: m2}
case nl.TCA_HFSC_FSC:
hfsc.Fsc = ServiceCurve{m1: m1, d: d, m2: m2}
case nl.TCA_HFSC_USC:
hfsc.Usc = ServiceCurve{m1: m1, d: d, m2: m2}
}
}
return detailed, nil
}
func parseTcStats(data []byte) (*ClassStatistics, error) {
buf := &bytes.Buffer{}
buf.Write(data)
native := nl.NativeEndian()
tcStats := &tcStats{}
if err := binary.Read(buf, native, tcStats); err != nil {
return nil, err
}
stats := NewClassStatistics()
stats.Basic.Bytes = tcStats.Bytes
stats.Basic.Packets = tcStats.Packets
stats.Queue.Qlen = tcStats.Qlen
stats.Queue.Backlog = tcStats.Backlog
stats.Queue.Drops = tcStats.Drops
stats.Queue.Overlimits = tcStats.Overlimits
stats.RateEst.Bps = tcStats.Bps
stats.RateEst.Pps = tcStats.Pps
return stats, nil
}
func parseGnetStats(data []byte, gnetStats interface{}) error {
buf := &bytes.Buffer{}
buf.Write(data)
native := nl.NativeEndian()
return binary.Read(buf, native, gnetStats)
}
func parseTcStats2(data []byte) (*ClassStatistics, error) {
rtAttrs, err := nl.ParseRouteAttr(data)
if err != nil {
return nil, err
}
stats := NewClassStatistics()
for _, datum := range rtAttrs {
switch datum.Attr.Type {
case nl.TCA_STATS_BASIC:
if err := parseGnetStats(datum.Value, stats.Basic); err != nil {
return nil, fmt.Errorf("Failed to parse ClassStatistics.Basic with: %v\n%s",
err, hex.Dump(datum.Value))
}
case nl.TCA_STATS_QUEUE:
if err := parseGnetStats(datum.Value, stats.Queue); err != nil {
return nil, fmt.Errorf("Failed to parse ClassStatistics.Queue with: %v\n%s",
err, hex.Dump(datum.Value))
}
case nl.TCA_STATS_RATE_EST:
if err := parseGnetStats(datum.Value, stats.RateEst); err != nil {
return nil, fmt.Errorf("Failed to parse ClassStatistics.RateEst with: %v\n%s",
err, hex.Dump(datum.Value))
}
}
}
return stats, nil
}

View File

@ -6,9 +6,9 @@ import (
"errors"
"fmt"
"net"
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// ConntrackTableType Conntrack table for the netlink operation
@ -22,7 +22,11 @@ const (
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2
ConntrackExpectTable = 2
)
const (
// For Parsing Mark
TCP_PROTO = 6
UDP_PROTO = 17
)
const (
// backward compatibility with golang 1.6 which does not have io.SeekCurrent
seekCurrent = 1
@ -81,8 +85,8 @@ func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily)
// conntrack -F [table] Flush table
// The flush operation applies to all the family types
func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {
req := h.newConntrackRequest(table, syscall.AF_INET, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK)
_, err := req.Execute(syscall.NETLINK_NETFILTER, 0)
req := h.newConntrackRequest(table, unix.AF_INET, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK)
_, err := req.Execute(unix.NETLINK_NETFILTER, 0)
return err
}
@ -98,10 +102,10 @@ func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFami
for _, dataRaw := range res {
flow := parseRawData(dataRaw)
if match := filter.MatchConntrackFlow(flow); match {
req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK)
req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK)
// skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already
req2.AddRawData(dataRaw[4:])
req2.Execute(syscall.NETLINK_NETFILTER, 0)
req2.Execute(unix.NETLINK_NETFILTER, 0)
matched++
}
}
@ -123,34 +127,38 @@ func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily
}
func (h *Handle) dumpConntrackTable(table ConntrackTableType, family InetFamily) ([][]byte, error) {
req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, syscall.NLM_F_DUMP)
return req.Execute(syscall.NETLINK_NETFILTER, 0)
req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, unix.NLM_F_DUMP)
return req.Execute(unix.NETLINK_NETFILTER, 0)
}
// The full conntrack flow structure is very complicated and can be found in the file:
// http://git.netfilter.org/libnetfilter_conntrack/tree/include/internal/object.h
// For the time being, the structure below allows to parse and extract the base information of a flow
type ipTuple struct {
SrcIP net.IP
Bytes uint64
DstIP net.IP
Protocol uint8
SrcPort uint16
DstPort uint16
Packets uint64
Protocol uint8
SrcIP net.IP
SrcPort uint16
}
type ConntrackFlow struct {
FamilyType uint8
Forward ipTuple
Reverse ipTuple
Mark uint32
}
func (s *ConntrackFlow) String() string {
// conntrack cmd output:
// udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001
return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d\tsrc=%s dst=%s sport=%d dport=%d",
// 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",
nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol,
s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort,
s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort)
s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, s.Forward.Packets, s.Forward.Bytes,
s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Reverse.Packets, s.Reverse.Bytes,
s.Mark)
}
// This method parse the ip tuple structure
@ -160,7 +168,7 @@ func (s *ConntrackFlow) String() string {
// <len, NLA_F_NESTED|nl.CTA_TUPLE_PROTO, 1 byte for the protocol, 3 bytes of padding>
// <len, CTA_PROTO_SRC_PORT, 2 bytes for the source port, 2 bytes of padding>
// <len, CTA_PROTO_DST_PORT, 2 bytes for the source port, 2 bytes of padding>
func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) {
func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 {
for i := 0; i < 2; i++ {
_, t, _, v := parseNfAttrTLV(reader)
switch t {
@ -189,6 +197,7 @@ func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) {
// Skip some padding 2 byte
reader.Seek(2, seekCurrent)
}
return tpl.Protocol
}
func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) {
@ -214,8 +223,27 @@ func parseBERaw16(r *bytes.Reader, v *uint16) {
binary.Read(r, binary.BigEndian, v)
}
func parseBERaw64(r *bytes.Reader, v *uint64) {
binary.Read(r, binary.BigEndian, v)
}
func parseByteAndPacketCounters(r *bytes.Reader) (bytes, packets uint64) {
for i := 0; i < 2; i++ {
switch _, t, _ := parseNfAttrTL(r); t {
case nl.CTA_COUNTERS_BYTES:
parseBERaw64(r, &bytes)
case nl.CTA_COUNTERS_PACKETS:
parseBERaw64(r, &packets)
default:
return
}
}
return
}
func parseRawData(data []byte) *ConntrackFlow {
s := &ConntrackFlow{}
var proto uint8
// First there is the Nfgenmsg header
// consume only the family field
reader := bytes.NewReader(data)
@ -231,24 +259,39 @@ func parseRawData(data []byte) *ConntrackFlow {
// <len, NLA_F_NESTED|CTA_TUPLE_IP> 4 bytes
// flow information of the reverse flow
for reader.Len() > 0 {
nested, t, l := parseNfAttrTL(reader)
if nested && t == nl.CTA_TUPLE_ORIG {
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
parseIpTuple(reader, &s.Forward)
}
} else if nested && t == nl.CTA_TUPLE_REPLY {
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
parseIpTuple(reader, &s.Reverse)
// Got all the useful information stop parsing
break
} else {
// Header not recognized skip it
reader.Seek(int64(l), seekCurrent)
if nested, t, l := parseNfAttrTL(reader); nested {
switch t {
case nl.CTA_TUPLE_ORIG:
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
proto = parseIpTuple(reader, &s.Forward)
}
case nl.CTA_TUPLE_REPLY:
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
parseIpTuple(reader, &s.Reverse)
} else {
// Header not recognized skip it
reader.Seek(int64(l), seekCurrent)
}
case nl.CTA_COUNTERS_ORIG:
s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader)
case nl.CTA_COUNTERS_REPLY:
s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader)
}
}
}
if proto == TCP_PROTO {
reader.Seek(64, seekCurrent)
_, t, _, v := parseNfAttrTLV(reader)
if t == nl.CTA_MARK {
s.Mark = uint32(v[3])
}
} else if proto == UDP_PROTO {
reader.Seek(16, seekCurrent)
_, t, _, v := parseNfAttrTLV(reader)
if t == nl.CTA_MARK {
s.Mark = uint32(v[3])
}
}
return s
}
@ -266,7 +309,7 @@ func parseRawData(data []byte) *ConntrackFlow {
// Common parameters and options:
// -s, --src, --orig-src ip Source address from original direction
// -d, --dst, --orig-dst ip Destination address from original direction
// -r, --reply-src ip Source addres from reply direction
// -r, --reply-src ip Source address from reply direction
// -q, --reply-dst ip Destination address from reply direction
// -p, --protonum proto Layer 4 Protocol, eg. 'tcp'
// -f, --family proto Layer 3 Protocol, eg. 'ipv6'
@ -283,11 +326,14 @@ func parseRawData(data []byte) *ConntrackFlow {
type ConntrackFilterType uint8
const (
ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
ConntrackNatSrcIP // -src-nat ip Source NAT ip
ConntrackNatDstIP // -dst-nat ip Destination NAT ip
ConntrackNatAnyIP // -any-nat ip Source or destination NAT ip
ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
ConntrackReplySrcIP // --reply-src ip Reply Source IP
ConntrackReplyDstIP // --reply-dst ip Reply Destination IP
ConntrackReplyAnyIP // Match source or destination reply IP
ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP
ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instaed ConntrackReplyAnyIP
)
type CustomConntrackFilter interface {
@ -332,17 +378,17 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
}
// -src-nat ip Source NAT ip
if elem, found := f.ipFilter[ConntrackNatSrcIP]; match && found {
if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found {
match = match && elem.Equal(flow.Reverse.SrcIP)
}
// -dst-nat ip Destination NAT ip
if elem, found := f.ipFilter[ConntrackNatDstIP]; match && found {
if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found {
match = match && elem.Equal(flow.Reverse.DstIP)
}
// -any-nat ip Source or destination NAT ip
if elem, found := f.ipFilter[ConntrackNatAnyIP]; match && found {
// Match source or destination reply IP
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found {
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
}

View File

@ -2,8 +2,6 @@ package netlink
import (
"fmt"
"github.com/vishvananda/netlink/nl"
)
type Filter interface {
@ -19,7 +17,7 @@ type FilterAttrs struct {
Handle uint32
Parent uint32
Priority uint16 // lower is higher priority
Protocol uint16 // syscall.ETH_P_*
Protocol uint16 // unix.ETH_P_*
}
func (q FilterAttrs) String() string {
@ -184,14 +182,6 @@ func NewMirredAction(redirIndex int) *MirredAction {
}
}
// Constants used in TcU32Sel.Flags.
const (
TC_U32_TERMINAL = nl.TC_U32_TERMINAL
TC_U32_OFFSET = nl.TC_U32_OFFSET
TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
TC_U32_EAT = nl.TC_U32_EAT
)
// Sel of the U32 filters that contains multiple TcU32Key. This is the copy
// and the frontend representation of nl.TcU32Sel. It is serialized into canonical
// nl.TcU32Sel with the appropriate endianness.
@ -222,6 +212,8 @@ type TcU32Key struct {
type U32 struct {
FilterAttrs
ClassId uint32
Divisor uint32 // Divisor MUST be power of 2.
Hash uint32
RedirIndex int
Sel *TcU32Sel
Actions []Action
@ -235,6 +227,21 @@ func (filter *U32) Type() string {
return "u32"
}
// MatchAll filters match all packets
type MatchAll struct {
FilterAttrs
ClassId uint32
Actions []Action
}
func (filter *MatchAll) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
func (filter *MatchAll) Type() string {
return "matchall"
}
type FilterFwAttrs struct {
ClassId uint32
InDev string

View File

@ -9,6 +9,15 @@ import (
"unsafe"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// Constants used in TcU32Sel.Flags.
const (
TC_U32_TERMINAL = nl.TC_U32_TERMINAL
TC_U32_OFFSET = nl.TC_U32_OFFSET
TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
TC_U32_EAT = nl.TC_U32_EAT
)
// Fw filter filters on firewall marks
@ -47,7 +56,7 @@ func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) {
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 {
if CalcRtable(&police.Rate, rtab[:], rcellLog, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("TBF: failed to calculate rate table")
}
police.Burst = uint32(Xmittime(uint64(police.Rate.Rate), uint32(buffer)))
@ -56,7 +65,7 @@ func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) {
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 {
if CalcRtable(&police.PeakRate, ptab[:], pcellLog, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("POLICE: failed to calculate peak rate table")
}
}
@ -90,7 +99,7 @@ func FilterDel(filter Filter) error {
// FilterDel will delete a filter from the system.
// Equivalent to: `tc filter del $filter`
func (h *Handle) FilterDel(filter Filter) error {
req := h.newNetlinkRequest(syscall.RTM_DELTFILTER, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_DELTFILTER, unix.NLM_F_ACK)
base := filter.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
@ -101,7 +110,7 @@ func (h *Handle) FilterDel(filter Filter) error {
}
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -115,7 +124,7 @@ func FilterAdd(filter Filter) error {
// Equivalent to: `tc filter add $filter`
func (h *Handle) FilterAdd(filter Filter) error {
native = nl.NativeEndian()
req := h.newNetlinkRequest(syscall.RTM_NEWTFILTER, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
base := filter.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
@ -128,9 +137,11 @@ func (h *Handle) FilterAdd(filter Filter) error {
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if u32, ok := filter.(*U32); ok {
switch filter := filter.(type) {
case *U32:
// Convert TcU32Sel into nl.TcU32Sel as it is without copy.
sel := (*nl.TcU32Sel)(unsafe.Pointer(u32.Sel))
sel := (*nl.TcU32Sel)(unsafe.Pointer(filter.Sel))
if sel == nil {
// match all
sel = &nl.TcU32Sel{
@ -157,64 +168,81 @@ func (h *Handle) FilterAdd(filter Filter) error {
}
}
sel.Nkeys = uint8(len(sel.Keys))
nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
if u32.ClassId != 0 {
nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId))
options.AddRtAttr(nl.TCA_U32_SEL, sel.Serialize())
if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_U32_CLASSID, nl.Uint32Attr(filter.ClassId))
}
actionsAttr := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil)
if filter.Divisor != 0 {
if (filter.Divisor-1)&filter.Divisor != 0 {
return fmt.Errorf("illegal divisor %d. Must be a power of 2", filter.Divisor)
}
options.AddRtAttr(nl.TCA_U32_DIVISOR, nl.Uint32Attr(filter.Divisor))
}
if filter.Hash != 0 {
options.AddRtAttr(nl.TCA_U32_HASH, nl.Uint32Attr(filter.Hash))
}
actionsAttr := options.AddRtAttr(nl.TCA_U32_ACT, nil)
// backwards compatibility
if u32.RedirIndex != 0 {
u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...)
if filter.RedirIndex != 0 {
filter.Actions = append([]Action{NewMirredAction(filter.RedirIndex)}, filter.Actions...)
}
if err := EncodeActions(actionsAttr, u32.Actions); err != nil {
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
} else if fw, ok := filter.(*Fw); ok {
if fw.Mask != 0 {
case *Fw:
if filter.Mask != 0 {
b := make([]byte, 4)
native.PutUint32(b, fw.Mask)
nl.NewRtAttrChild(options, nl.TCA_FW_MASK, b)
native.PutUint32(b, filter.Mask)
options.AddRtAttr(nl.TCA_FW_MASK, b)
}
if fw.InDev != "" {
nl.NewRtAttrChild(options, nl.TCA_FW_INDEV, nl.ZeroTerminated(fw.InDev))
if filter.InDev != "" {
options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev))
}
if (fw.Police != nl.TcPolice{}) {
if (filter.Police != nl.TcPolice{}) {
police := nl.NewRtAttrChild(options, nl.TCA_FW_POLICE, nil)
nl.NewRtAttrChild(police, nl.TCA_POLICE_TBF, fw.Police.Serialize())
if (fw.Police.Rate != nl.TcRateSpec{}) {
payload := SerializeRtab(fw.Rtab)
nl.NewRtAttrChild(police, nl.TCA_POLICE_RATE, payload)
police := options.AddRtAttr(nl.TCA_FW_POLICE, nil)
police.AddRtAttr(nl.TCA_POLICE_TBF, filter.Police.Serialize())
if (filter.Police.Rate != nl.TcRateSpec{}) {
payload := SerializeRtab(filter.Rtab)
police.AddRtAttr(nl.TCA_POLICE_RATE, payload)
}
if (fw.Police.PeakRate != nl.TcRateSpec{}) {
payload := SerializeRtab(fw.Ptab)
nl.NewRtAttrChild(police, nl.TCA_POLICE_PEAKRATE, payload)
if (filter.Police.PeakRate != nl.TcRateSpec{}) {
payload := SerializeRtab(filter.Ptab)
police.AddRtAttr(nl.TCA_POLICE_PEAKRATE, payload)
}
}
if fw.ClassId != 0 {
if filter.ClassId != 0 {
b := make([]byte, 4)
native.PutUint32(b, fw.ClassId)
nl.NewRtAttrChild(options, nl.TCA_FW_CLASSID, b)
native.PutUint32(b, filter.ClassId)
options.AddRtAttr(nl.TCA_FW_CLASSID, b)
}
} else if bpf, ok := filter.(*BpfFilter); ok {
case *BpfFilter:
var bpfFlags uint32
if bpf.ClassId != 0 {
nl.NewRtAttrChild(options, nl.TCA_BPF_CLASSID, nl.Uint32Attr(bpf.ClassId))
if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_BPF_CLASSID, nl.Uint32Attr(filter.ClassId))
}
if bpf.Fd >= 0 {
nl.NewRtAttrChild(options, nl.TCA_BPF_FD, nl.Uint32Attr((uint32(bpf.Fd))))
if filter.Fd >= 0 {
options.AddRtAttr(nl.TCA_BPF_FD, nl.Uint32Attr((uint32(filter.Fd))))
}
if bpf.Name != "" {
nl.NewRtAttrChild(options, nl.TCA_BPF_NAME, nl.ZeroTerminated(bpf.Name))
if filter.Name != "" {
options.AddRtAttr(nl.TCA_BPF_NAME, nl.ZeroTerminated(filter.Name))
}
if bpf.DirectAction {
if filter.DirectAction {
bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT
}
nl.NewRtAttrChild(options, nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags))
options.AddRtAttr(nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags))
case *MatchAll:
actionsAttr := options.AddRtAttr(nl.TCA_MATCHALL_ACT, nil)
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId))
}
}
req.AddData(options)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -229,7 +257,7 @@ func FilterList(link Link, parent uint32) ([]Filter, error) {
// Equivalent to: `tc filter show`.
// Generally returns nothing if link and parent are not specified.
func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
req := h.newNetlinkRequest(syscall.RTM_GETTFILTER, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP)
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Parent: parent,
@ -241,7 +269,7 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
}
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTFILTER)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
if err != nil {
return nil, err
}
@ -277,6 +305,8 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
filter = &Fw{}
case "bpf":
filter = &BpfFilter{}
case "matchall":
filter = &MatchAll{}
default:
filter = &GenericFilter{FilterType: filterType}
}
@ -301,6 +331,11 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
if err != nil {
return nil, err
}
case "matchall":
detailed, err = parseMatchAllData(filter, data)
if err != nil {
return nil, err
}
default:
detailed = true
}
@ -340,34 +375,34 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
default:
return fmt.Errorf("unknown action type %s", action.Type())
case *MirredAction:
table := nl.NewRtAttrChild(attr, tabIndex, nil)
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred"))
aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil)
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
mirred := nl.TcMirred{
Eaction: int32(action.MirredAction),
Ifindex: uint32(action.Ifindex),
}
toTcGen(action.Attrs(), &mirred.TcGen)
nl.NewRtAttrChild(aopts, nl.TCA_MIRRED_PARMS, mirred.Serialize())
aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize())
case *BpfAction:
table := nl.NewRtAttrChild(attr, tabIndex, nil)
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf"))
aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil)
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
gen := nl.TcGen{}
toTcGen(action.Attrs(), &gen)
nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_PARMS, gen.Serialize())
nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd)))
nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name))
aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd)))
aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name))
case *GenericAction:
table := nl.NewRtAttrChild(attr, tabIndex, nil)
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("gact"))
aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil)
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("gact"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
gen := nl.TcGen{}
toTcGen(action.Attrs(), &gen)
nl.NewRtAttrChild(aopts, nl.TCA_GACT_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_GACT_PARMS, gen.Serialize())
}
}
return nil
@ -474,6 +509,10 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
}
case nl.TCA_U32_CLASSID:
u32.ClassId = native.Uint32(datum.Value)
case nl.TCA_U32_DIVISOR:
u32.Divisor = native.Uint32(datum.Value)
case nl.TCA_U32_HASH:
u32.Hash = native.Uint32(datum.Value)
}
}
return detailed, nil
@ -530,6 +569,28 @@ func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
return detailed, nil
}
func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
matchall := filter.(*MatchAll)
detailed := true
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_MATCHALL_CLASSID:
matchall.ClassId = native.Uint32(datum.Value[0:4])
case nl.TCA_MATCHALL_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return detailed, err
}
matchall.Actions, err = parseActions(tables)
if err != nil {
return detailed, err
}
}
}
return detailed, nil
}
func AlignToAtm(size uint) uint {
var linksize, cells int
cells = int(size / nl.ATM_CELL_PAYLOAD)
@ -552,7 +613,7 @@ func AdjustSize(sz uint, mpu uint, linklayer int) uint {
}
}
func CalcRtable(rate *nl.TcRateSpec, rtab [256]uint32, cellLog int, mtu uint32, linklayer int) int {
func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, linklayer int) int {
bps := rate.Rate
mpu := rate.Mpu
var sz uint

21
vendor/github.com/vishvananda/netlink/fou.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
package netlink
import (
"errors"
)
var (
// ErrAttrHeaderTruncated is returned when a netlink attribute's header is
// truncated.
ErrAttrHeaderTruncated = errors.New("attribute header truncated")
// ErrAttrBodyTruncated is returned when a netlink attribute's body is
// truncated.
ErrAttrBodyTruncated = errors.New("attribute body truncated")
)
type Fou struct {
Family int
Port int
Protocol int
EncapType int
}

215
vendor/github.com/vishvananda/netlink/fou_linux.go generated vendored Normal file
View File

@ -0,0 +1,215 @@
// +build linux
package netlink
import (
"encoding/binary"
"errors"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
const (
FOU_GENL_NAME = "fou"
)
const (
FOU_CMD_UNSPEC uint8 = iota
FOU_CMD_ADD
FOU_CMD_DEL
FOU_CMD_GET
FOU_CMD_MAX = FOU_CMD_GET
)
const (
FOU_ATTR_UNSPEC = iota
FOU_ATTR_PORT
FOU_ATTR_AF
FOU_ATTR_IPPROTO
FOU_ATTR_TYPE
FOU_ATTR_REMCSUM_NOPARTIAL
FOU_ATTR_MAX = FOU_ATTR_REMCSUM_NOPARTIAL
)
const (
FOU_ENCAP_UNSPEC = iota
FOU_ENCAP_DIRECT
FOU_ENCAP_GUE
FOU_ENCAP_MAX = FOU_ENCAP_GUE
)
var fouFamilyId int
func FouFamilyId() (int, error) {
if fouFamilyId != 0 {
return fouFamilyId, nil
}
fam, err := GenlFamilyGet(FOU_GENL_NAME)
if err != nil {
return -1, err
}
fouFamilyId = int(fam.ID)
return fouFamilyId, nil
}
func FouAdd(f Fou) error {
return pkgHandle.FouAdd(f)
}
func (h *Handle) FouAdd(f Fou) error {
fam_id, err := FouFamilyId()
if err != nil {
return err
}
// setting ip protocol conflicts with encapsulation type GUE
if f.EncapType == FOU_ENCAP_GUE && f.Protocol != 0 {
return errors.New("GUE encapsulation doesn't specify an IP protocol")
}
req := h.newNetlinkRequest(fam_id, unix.NLM_F_ACK)
// int to byte for port
bp := make([]byte, 2)
binary.BigEndian.PutUint16(bp[0:2], uint16(f.Port))
attrs := []*nl.RtAttr{
nl.NewRtAttr(FOU_ATTR_PORT, bp),
nl.NewRtAttr(FOU_ATTR_TYPE, []byte{uint8(f.EncapType)}),
nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(f.Family)}),
nl.NewRtAttr(FOU_ATTR_IPPROTO, []byte{uint8(f.Protocol)}),
}
raw := []byte{FOU_CMD_ADD, 1, 0, 0}
for _, a := range attrs {
raw = append(raw, a.Serialize()...)
}
req.AddRawData(raw)
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return err
}
return nil
}
func FouDel(f Fou) error {
return pkgHandle.FouDel(f)
}
func (h *Handle) FouDel(f Fou) error {
fam_id, err := FouFamilyId()
if err != nil {
return err
}
req := h.newNetlinkRequest(fam_id, unix.NLM_F_ACK)
// int to byte for port
bp := make([]byte, 2)
binary.BigEndian.PutUint16(bp[0:2], uint16(f.Port))
attrs := []*nl.RtAttr{
nl.NewRtAttr(FOU_ATTR_PORT, bp),
nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(f.Family)}),
}
raw := []byte{FOU_CMD_DEL, 1, 0, 0}
for _, a := range attrs {
raw = append(raw, a.Serialize()...)
}
req.AddRawData(raw)
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return err
}
return nil
}
func FouList(fam int) ([]Fou, error) {
return pkgHandle.FouList(fam)
}
func (h *Handle) FouList(fam int) ([]Fou, error) {
fam_id, err := FouFamilyId()
if err != nil {
return nil, err
}
req := h.newNetlinkRequest(fam_id, unix.NLM_F_DUMP)
attrs := []*nl.RtAttr{
nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(fam)}),
}
raw := []byte{FOU_CMD_GET, 1, 0, 0}
for _, a := range attrs {
raw = append(raw, a.Serialize()...)
}
req.AddRawData(raw)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
fous := make([]Fou, 0, len(msgs))
for _, m := range msgs {
f, err := deserializeFouMsg(m)
if err != nil {
return fous, err
}
fous = append(fous, f)
}
return fous, nil
}
func deserializeFouMsg(msg []byte) (Fou, error) {
// we'll skip to byte 4 to first attribute
msg = msg[3:]
var shift int
fou := Fou{}
for {
// attribute header is at least 16 bits
if len(msg) < 4 {
return fou, ErrAttrHeaderTruncated
}
lgt := int(binary.BigEndian.Uint16(msg[0:2]))
if len(msg) < lgt+4 {
return fou, ErrAttrBodyTruncated
}
attr := binary.BigEndian.Uint16(msg[2:4])
shift = lgt + 3
switch attr {
case FOU_ATTR_AF:
fou.Family = int(msg[5])
case FOU_ATTR_PORT:
fou.Port = int(binary.BigEndian.Uint16(msg[5:7]))
// port is 2 bytes
shift = lgt + 2
case FOU_ATTR_IPPROTO:
fou.Protocol = int(msg[5])
case FOU_ATTR_TYPE:
fou.EncapType = int(msg[5])
}
msg = msg[shift:]
if len(msg) < 4 {
break
}
}
return fou, nil
}

View File

@ -0,0 +1,15 @@
// +build !linux
package netlink
func FouAdd(f Fou) error {
return ErrNotImplemented
}
func FouDel(f Fou) error {
return ErrNotImplemented
}
func FouList(fam int) ([]Fou, error) {
return nil, ErrNotImplemented
}

View File

@ -5,6 +5,7 @@ import (
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
type GenlOp struct {
@ -130,9 +131,9 @@ func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
Command: nl.GENL_CTRL_CMD_GETFAMILY,
Version: nl.GENL_CTRL_VERSION,
}
req := h.newNetlinkRequest(nl.GENL_ID_CTRL, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(nl.GENL_ID_CTRL, unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
@ -151,7 +152,7 @@ func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
req := h.newNetlinkRequest(nl.GENL_ID_CTRL, 0)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_CTRL_ATTR_FAMILY_NAME, nl.ZeroTerminated(name)))
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}

View File

@ -7,6 +7,7 @@ import (
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
type PDP struct {
@ -82,9 +83,9 @@ func (h *Handle) GTPPDPList() ([]*PDP, error) {
Command: nl.GENL_GTP_CMD_GETPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
@ -96,7 +97,7 @@ func GTPPDPList() ([]*PDP, error) {
}
func gtpPDPGet(req *nl.NetlinkRequest) (*PDP, error) {
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
@ -182,7 +183,7 @@ func (h *Handle) GTPPDPAdd(link Link, pdp *PDP) error {
Command: nl.GENL_GTP_CMD_NEWPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_EXCL|unix.NLM_F_ACK)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
@ -199,7 +200,7 @@ func (h *Handle) GTPPDPAdd(link Link, pdp *PDP) error {
default:
return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
}
_, err = req.Execute(syscall.NETLINK_GENERIC, 0)
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
return err
}
@ -216,7 +217,7 @@ func (h *Handle) GTPPDPDel(link Link, pdp *PDP) error {
Command: nl.GENL_GTP_CMD_DELPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_EXCL|unix.NLM_F_ACK)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
@ -229,7 +230,7 @@ func (h *Handle) GTPPDPDel(link Link, pdp *PDP) error {
default:
return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
}
_, err = req.Execute(syscall.NETLINK_GENERIC, 0)
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
return err
}

View File

@ -2,11 +2,11 @@ package netlink
import (
"fmt"
"syscall"
"time"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
// Empty handle used by the netlink package methods
@ -43,14 +43,29 @@ func (h *Handle) SetSocketTimeout(to time.Duration) error {
if to < time.Microsecond {
return fmt.Errorf("invalid timeout, minimul value is %s", time.Microsecond)
}
tv := syscall.NsecToTimeval(to.Nanoseconds())
tv := unix.NsecToTimeval(to.Nanoseconds())
for _, sh := range h.sockets {
fd := sh.Socket.GetFd()
err := syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)
if err != nil {
if err := sh.Socket.SetSendTimeout(&tv); err != nil {
return err
}
err = syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &tv)
if err := sh.Socket.SetReceiveTimeout(&tv); err != nil {
return err
}
}
return nil
}
// SetSocketReceiveBufferSize sets the receive buffer size for each
// socket in the netlink handle. The maximum value is capped by
// /proc/sys/net/core/rmem_max.
func (h *Handle) SetSocketReceiveBufferSize(size int, force bool) error {
opt := unix.SO_RCVBUF
if force {
opt = unix.SO_RCVBUFFORCE
}
for _, sh := range h.sockets {
fd := sh.Socket.GetFd()
err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, opt, size)
if err != nil {
return err
}
@ -58,6 +73,24 @@ func (h *Handle) SetSocketTimeout(to time.Duration) error {
return nil
}
// GetSocketReceiveBufferSize gets the receiver buffer size for each
// socket in the netlink handle. The retrieved value should be the
// double to the one set for SetSocketReceiveBufferSize.
func (h *Handle) GetSocketReceiveBufferSize() ([]int, error) {
results := make([]int, len(h.sockets))
i := 0
for _, sh := range h.sockets {
fd := sh.Socket.GetFd()
size, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF)
if err != nil {
return nil, err
}
results[i] = size
i++
}
return results, nil
}
// NewHandle returns a netlink handle on the network namespace
// specified by ns. If ns=netns.None(), current network namespace
// will be assumed
@ -101,10 +134,10 @@ func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest {
return nl.NewNetlinkRequest(proto, flags)
}
return &nl.NetlinkRequest{
NlMsghdr: syscall.NlMsghdr{
Len: uint32(syscall.SizeofNlMsghdr),
NlMsghdr: unix.NlMsghdr{
Len: uint32(unix.SizeofNlMsghdr),
Type: uint16(proto),
Flags: syscall.NLM_F_REQUEST | uint16(flags),
Flags: unix.NLM_F_REQUEST | uint16(flags),
},
Sockets: h.sockets,
}

View File

@ -145,6 +145,10 @@ func (h *Handle) LinkSetFlood(link Link, mode bool) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetTxQLen(link Link, qlen int) error {
return ErrNotImplemented
}
func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error {
return ErrNotImplemented
}
@ -216,3 +220,39 @@ func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
return nil, ErrNotImplemented
}
func (h *Handle) RouteAdd(route *Route) error {
return ErrNotImplemented
}
func (h *Handle) RouteDel(route *Route) error {
return ErrNotImplemented
}
func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
return nil, ErrNotImplemented
}
func (h *Handle) RouteList(link Link, family int) ([]Route, error) {
return nil, ErrNotImplemented
}
func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
return nil, ErrNotImplemented
}
func (h *Handle) RouteReplace(route *Route) error {
return ErrNotImplemented
}
func (h *Handle) RuleAdd(rule *Rule) error {
return ErrNotImplemented
}
func (h *Handle) RuleDel(rule *Rule) error {
return ErrNotImplemented
}
func (h *Handle) RuleList(family int) ([]Rule, error) {
return nil, ErrNotImplemented
}

98
vendor/github.com/vishvananda/netlink/ioctl_linux.go generated vendored Normal file
View File

@ -0,0 +1,98 @@
package netlink
import (
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
// ioctl for statistics.
const (
// ETHTOOL_GSSET_INFO gets string set info
ETHTOOL_GSSET_INFO = 0x00000037
// SIOCETHTOOL is Ethtool interface
SIOCETHTOOL = 0x8946
// ETHTOOL_GSTRINGS gets specified string set
ETHTOOL_GSTRINGS = 0x0000001b
// ETHTOOL_GSTATS gets NIC-specific statistics
ETHTOOL_GSTATS = 0x0000001d
)
// string set id.
const (
// ETH_SS_TEST is self-test result names, for use with %ETHTOOL_TEST
ETH_SS_TEST = iota
// ETH_SS_STATS statistic names, for use with %ETHTOOL_GSTATS
ETH_SS_STATS
// ETH_SS_PRIV_FLAGS are driver private flag names
ETH_SS_PRIV_FLAGS
// _ETH_SS_NTUPLE_FILTERS is deprecated
_ETH_SS_NTUPLE_FILTERS
// ETH_SS_FEATURES are device feature names
ETH_SS_FEATURES
// ETH_SS_RSS_HASH_FUNCS is RSS hush function names
ETH_SS_RSS_HASH_FUNCS
)
// IfreqSlave is a struct for ioctl bond manipulation syscalls.
// It is used to assign slave to bond interface with Name.
type IfreqSlave struct {
Name [unix.IFNAMSIZ]byte
Slave [unix.IFNAMSIZ]byte
}
// Ifreq is a struct for ioctl ethernet manipulation syscalls.
type Ifreq struct {
Name [unix.IFNAMSIZ]byte
Data uintptr
}
// ethtoolSset is a string set information
type ethtoolSset struct {
cmd uint32
reserved uint32
mask uint64
data [1]uint32
}
// ethtoolGstrings is string set for data tagging
type ethtoolGstrings struct {
cmd uint32
stringSet uint32
length uint32
data [32]byte
}
type ethtoolStats struct {
cmd uint32
nStats uint32
data [1]uint64
}
// newIocltSlaveReq returns filled IfreqSlave with proper interface names
// It is used by ioctl to assign slave to bond master
func newIocltSlaveReq(slave, master string) *IfreqSlave {
ifreq := &IfreqSlave{}
copy(ifreq.Name[:unix.IFNAMSIZ-1], master)
copy(ifreq.Slave[:unix.IFNAMSIZ-1], slave)
return ifreq
}
// newIocltStringSetReq creates request to get interface string set
func newIocltStringSetReq(linkName string) (*Ifreq, *ethtoolSset) {
e := &ethtoolSset{
cmd: ETHTOOL_GSSET_INFO,
mask: 1 << ETH_SS_STATS,
}
ifreq := &Ifreq{Data: uintptr(unsafe.Pointer(e))}
copy(ifreq.Name[:unix.IFNAMSIZ-1], linkName)
return ifreq, e
}
// getSocketUDP returns file descriptor to new UDP socket
// It is used for communication with ioctl interface.
func getSocketUDP() (int, error) {
return syscall.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
}

View File

@ -3,6 +3,7 @@ package netlink
import (
"fmt"
"net"
"os"
)
// Link represents a link device from netlink. Shared link attributes
@ -37,6 +38,21 @@ type LinkAttrs struct {
EncapType string
Protinfo *Protinfo
OperState LinkOperState
NetNsID int
NumTxQueues int
NumRxQueues int
Vfs []VfInfo // virtual functions available on link
}
// VfInfo represents configuration of virtual function
type VfInfo struct {
ID int
Mac net.HardwareAddr
Vlan int
Qos int
TxRate int
Spoofchk bool
LinkState uint32
}
// LinkOperState represents the values of the IFLA_OPERSTATE link
@ -171,6 +187,7 @@ type LinkXdp struct {
Fd int
Attached bool
Flags uint32
ProgId uint32
}
// Device links cannot be created via netlink. These links
@ -218,6 +235,7 @@ type Bridge struct {
LinkAttrs
MulticastSnooping *bool
HelloTime *uint32
VlanFiltering *bool
}
func (bridge *Bridge) Attrs() *LinkAttrs {
@ -257,6 +275,9 @@ const (
type Macvlan struct {
LinkAttrs
Mode MacvlanMode
// MACAddrs is only populated for Macvlan SOURCE links
MACAddrs []net.HardwareAddr
}
func (macvlan *Macvlan) Attrs() *LinkAttrs {
@ -282,8 +303,11 @@ type TuntapFlag uint16
// Tuntap links created via /dev/tun/tap, but can be destroyed via netlink
type Tuntap struct {
LinkAttrs
Mode TuntapMode
Flags TuntapFlag
Mode TuntapMode
Flags TuntapFlag
NonPersist bool
Queues int
Fds []*os.File
}
func (tuntap *Tuntap) Attrs() *LinkAttrs {
@ -325,26 +349,28 @@ func (generic *GenericLink) Type() string {
type Vxlan struct {
LinkAttrs
VxlanId int
VtepDevIndex int
SrcAddr net.IP
Group net.IP
TTL int
TOS int
Learning bool
Proxy bool
RSC bool
L2miss bool
L3miss bool
UDPCSum bool
NoAge bool
GBP bool
FlowBased bool
Age int
Limit int
Port int
PortLow int
PortHigh int
VxlanId int
VtepDevIndex int
SrcAddr net.IP
Group net.IP
TTL int
TOS int
Learning bool
Proxy bool
RSC bool
L2miss bool
L3miss bool
UDPCSum bool
UDP6ZeroCSumTx bool
UDP6ZeroCSumRx bool
NoAge bool
GBP bool
FlowBased bool
Age int
Limit int
Port int
PortLow int
PortHigh int
}
func (vxlan *Vxlan) Attrs() *LinkAttrs {
@ -693,17 +719,25 @@ func (gretap *Gretap) Attrs() *LinkAttrs {
}
func (gretap *Gretap) Type() string {
if gretap.Local.To4() == nil {
return "ip6gretap"
}
return "gretap"
}
type Iptun struct {
LinkAttrs
Ttl uint8
Tos uint8
PMtuDisc uint8
Link uint32
Local net.IP
Remote net.IP
Ttl uint8
Tos uint8
PMtuDisc uint8
Link uint32
Local net.IP
Remote net.IP
EncapSport uint16
EncapDport uint16
EncapType uint16
EncapFlags uint16
FlowBased bool
}
func (iptun *Iptun) Attrs() *LinkAttrs {
@ -714,6 +748,28 @@ func (iptun *Iptun) Type() string {
return "ipip"
}
type Sittun struct {
LinkAttrs
Link uint32
Local net.IP
Remote net.IP
Ttl uint8
Tos uint8
PMtuDisc uint8
EncapType uint16
EncapFlags uint16
EncapSport uint16
EncapDport uint16
}
func (sittun *Sittun) Attrs() *LinkAttrs {
return &sittun.LinkAttrs
}
func (sittun *Sittun) Type() string {
return "sit"
}
type Vti struct {
LinkAttrs
IKey uint32
@ -727,10 +783,42 @@ func (vti *Vti) Attrs() *LinkAttrs {
return &vti.LinkAttrs
}
func (iptun *Vti) Type() string {
func (vti *Vti) Type() string {
if vti.Local.To4() == nil {
return "vti6"
}
return "vti"
}
type Gretun struct {
LinkAttrs
Link uint32
IFlags uint16
OFlags uint16
IKey uint32
OKey uint32
Local net.IP
Remote net.IP
Ttl uint8
Tos uint8
PMtuDisc uint8
EncapType uint16
EncapFlags uint16
EncapSport uint16
EncapDport uint16
}
func (gretun *Gretun) Attrs() *LinkAttrs {
return &gretun.LinkAttrs
}
func (gretun *Gretun) Type() string {
if gretun.Local.To4() == nil {
return "ip6gre"
}
return "gre"
}
type Vrf struct {
LinkAttrs
Table uint32
@ -763,7 +851,7 @@ func (gtp *GTP) Type() string {
// iproute2 supported devices;
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
// gre | gretap | ip6gre | ip6gretap | vti | nlmon |
// gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon |
// bond_slave | ipvlan
// LinkNotFoundError wraps the various not found errors when

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,18 @@ type Neigh struct {
Flags int
IP net.IP
HardwareAddr net.HardwareAddr
LLIPAddr net.IP //Used in the case of NHRP
Vlan int
VNI int
}
// String returns $ip/$hwaddr $label
func (neigh *Neigh) String() string {
return fmt.Sprintf("%s %s", neigh.IP, neigh.HardwareAddr)
}
// NeighUpdate is sent when a neighbor changes - type is RTM_NEWNEIGH or RTM_DELNEIGH.
type NeighUpdate struct {
Type uint16
Neigh
}

View File

@ -6,6 +6,8 @@ import (
"unsafe"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
const (
@ -73,7 +75,7 @@ func NeighAdd(neigh *Neigh) error {
// NeighAdd will add an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh add ....`
func (h *Handle) NeighAdd(neigh *Neigh) error {
return h.neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL)
return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
}
// NeighSet will add or replace an IP to MAC mapping to the ARP table
@ -85,7 +87,7 @@ func NeighSet(neigh *Neigh) error {
// NeighSet will add or replace an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh replace....`
func (h *Handle) NeighSet(neigh *Neigh) error {
return h.neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE)
return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
}
// NeighAppend will append an entry to FDB
@ -97,7 +99,7 @@ func NeighAppend(neigh *Neigh) error {
// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func (h *Handle) NeighAppend(neigh *Neigh) error {
return h.neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_APPEND)
return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
}
// NeighAppend will append an entry to FDB
@ -109,7 +111,7 @@ func neighAdd(neigh *Neigh, mode int) error {
// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
req := h.newNetlinkRequest(syscall.RTM_NEWNEIGH, mode|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
return neighHandle(neigh, req)
}
@ -122,12 +124,13 @@ func NeighDel(neigh *Neigh) error {
// NeighDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func (h *Handle) NeighDel(neigh *Neigh) error {
req := h.newNetlinkRequest(syscall.RTM_DELNEIGH, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
return neighHandle(neigh, req)
}
func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
var family int
if neigh.Family > 0 {
family = neigh.Family
} else {
@ -151,12 +154,25 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
dstData := nl.NewRtAttr(NDA_DST, ipData)
req.AddData(dstData)
if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
if neigh.LLIPAddr != nil {
llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
req.AddData(llIPData)
} else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
req.AddData(hwData)
}
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
if neigh.Vlan != 0 {
vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
req.AddData(vlanData)
}
if neigh.VNI != 0 {
vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
req.AddData(vniData)
}
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -189,7 +205,7 @@ func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
}
func (h *Handle) neighList(linkIndex, family, flags int) ([]Neigh, error) {
req := h.newNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
msg := Ndmsg{
Family: uint8(family),
Index: uint32(linkIndex),
@ -197,7 +213,7 @@ func (h *Handle) neighList(linkIndex, family, flags int) ([]Neigh, error) {
}
req.AddData(&msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWNEIGH)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
if err != nil {
return nil, err
}
@ -237,14 +253,130 @@ func NeighDeserialize(m []byte) (*Neigh, error) {
return nil, err
}
// This should be cached for perfomance
// once per table dump
link, err := LinkByIndex(neigh.LinkIndex)
if err != nil {
return nil, err
}
encapType := link.Attrs().EncapType
for _, attr := range attrs {
switch attr.Attr.Type {
case NDA_DST:
neigh.IP = net.IP(attr.Value)
case NDA_LLADDR:
neigh.HardwareAddr = net.HardwareAddr(attr.Value)
// BUG: Is this a bug in the netlink library?
// #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
// #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
attrLen := attr.Attr.Len - unix.SizeofRtAttr
if attrLen == 4 && (encapType == "ipip" ||
encapType == "sit" ||
encapType == "gre") {
neigh.LLIPAddr = net.IP(attr.Value)
} else if attrLen == 16 &&
encapType == "tunnel6" {
neigh.IP = net.IP(attr.Value)
} else {
neigh.HardwareAddr = net.HardwareAddr(attr.Value)
}
case NDA_VLAN:
neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
case NDA_VNI:
neigh.VNI = int(native.Uint32(attr.Value[0:4]))
}
}
return &neigh, nil
}
// NeighSubscribe takes a chan down which notifications will be sent
// when neighbors are added or deleted. Close the 'done' chan to stop subscription.
func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}
// NeighSubscribeAt works like NeighSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
return neighSubscribeAt(ns, netns.None(), ch, done, nil, false)
}
// NeighSubscribeOptions contains a set of options to use with
// NeighSubscribeWithOptions.
type NeighSubscribeOptions struct {
Namespace *netns.NsHandle
ErrorCallback func(error)
ListExisting bool
}
// NeighSubscribeWithOptions work like NeighSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
if options.Namespace == nil {
none := netns.None()
options.Namespace = &none
}
return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}
func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
if err != nil {
return err
}
if done != nil {
go func() {
<-done
s.Close()
}()
}
if listExisting {
req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(infmsg)
if err := s.Send(req); err != nil {
return err
}
}
go func() {
defer close(ch)
for {
msgs, err := s.Receive()
if err != nil {
if cberr != nil {
cberr(err)
}
return
}
for _, m := range msgs {
if m.Header.Type == unix.NLMSG_DONE {
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
}
return
}
neigh, err := NeighDeserialize(m.Data)
if err != nil {
if cberr != nil {
cberr(err)
}
return
}
ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
}
}
}()
return nil
}

View File

@ -27,7 +27,8 @@ func ParseIPNet(s string) (*net.IPNet, error) {
if err != nil {
return nil, err
}
return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil
ipNet.IP = ip
return ipNet, nil
}
// NewIPNet generates an IPNet from an ip address using a netmask of 32 or 128.

View File

@ -108,6 +108,10 @@ func LinkSetFlood(link Link, mode bool) error {
return ErrNotImplemented
}
func LinkSetTxQLen(link Link, qlen int) error {
return ErrNotImplemented
}
func LinkAdd(link Link) error {
return ErrNotImplemented
}

141
vendor/github.com/vishvananda/netlink/netns_linux.go generated vendored Normal file
View File

@ -0,0 +1,141 @@
package netlink
// Network namespace ID functions
//
// The kernel has a weird concept called the network namespace ID.
// This is different from the file reference in proc (and any bind-mounted
// namespaces, etc.)
//
// Instead, namespaces can be assigned a numeric ID at any time. Once set,
// the ID is fixed. The ID can either be set manually by the user, or
// automatically, triggered by certain kernel actions. The most common kernel
// action that triggers namespace ID creation is moving one end of a veth pair
// in to that namespace.
import (
"fmt"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// These can be replaced by the values from sys/unix when it is next released.
const (
_ = iota
NETNSA_NSID
NETNSA_PID
NETNSA_FD
)
// GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id).
// Returns -1 if the namespace does not have an ID set.
func (h *Handle) GetNetNsIdByPid(pid int) (int, error) {
return h.getNetNsId(NETNSA_PID, uint32(pid))
}
// GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id).
// Returns -1 if the namespace does not have an ID set.
func GetNetNsIdByPid(pid int) (int, error) {
return pkgHandle.GetNetNsIdByPid(pid)
}
// SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id).
// The ID can only be set for namespaces without an ID already set.
func (h *Handle) SetNetNsIdByPid(pid, nsid int) error {
return h.setNetNsId(NETNSA_PID, uint32(pid), uint32(nsid))
}
// SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id).
// The ID can only be set for namespaces without an ID already set.
func SetNetNsIdByPid(pid, nsid int) error {
return pkgHandle.SetNetNsIdByPid(pid, nsid)
}
// GetNetNsIdByPid looks up the network namespace ID for a given fd.
// fd must be an open file descriptor to a namespace file.
// Returns -1 if the namespace does not have an ID set.
func (h *Handle) GetNetNsIdByFd(fd int) (int, error) {
return h.getNetNsId(NETNSA_FD, uint32(fd))
}
// GetNetNsIdByPid looks up the network namespace ID for a given fd.
// fd must be an open file descriptor to a namespace file.
// Returns -1 if the namespace does not have an ID set.
func GetNetNsIdByFd(fd int) (int, error) {
return pkgHandle.GetNetNsIdByFd(fd)
}
// SetNetNSIdByFd sets the ID of the network namespace for a given fd.
// fd must be an open file descriptor to a namespace file.
// The ID can only be set for namespaces without an ID already set.
func (h *Handle) SetNetNsIdByFd(fd, nsid int) error {
return h.setNetNsId(NETNSA_FD, uint32(fd), uint32(nsid))
}
// SetNetNSIdByFd sets the ID of the network namespace for a given fd.
// fd must be an open file descriptor to a namespace file.
// The ID can only be set for namespaces without an ID already set.
func SetNetNsIdByFd(fd, nsid int) error {
return pkgHandle.SetNetNsIdByFd(fd, nsid)
}
// getNetNsId requests the netnsid for a given type-val pair
// type should be either NETNSA_PID or NETNSA_FD
func (h *Handle) getNetNsId(attrType int, val uint32) (int, error) {
req := h.newNetlinkRequest(unix.RTM_GETNSID, unix.NLM_F_REQUEST)
rtgen := nl.NewRtGenMsg()
req.AddData(rtgen)
b := make([]byte, 4, 4)
native.PutUint32(b, val)
attr := nl.NewRtAttr(attrType, b)
req.AddData(attr)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID)
if err != nil {
return 0, err
}
for _, m := range msgs {
msg := nl.DeserializeRtGenMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return 0, err
}
for _, attr := range attrs {
switch attr.Attr.Type {
case NETNSA_NSID:
return int(int32(native.Uint32(attr.Value))), nil
}
}
}
return 0, fmt.Errorf("unexpected empty result")
}
// setNetNsId sets the netnsid for a given type-val pair
// type should be either NETNSA_PID or NETNSA_FD
// The ID can only be set for namespaces without an ID already set
func (h *Handle) setNetNsId(attrType int, val uint32, newnsid uint32) error {
req := h.newNetlinkRequest(unix.RTM_NEWNSID, unix.NLM_F_REQUEST|unix.NLM_F_ACK)
rtgen := nl.NewRtGenMsg()
req.AddData(rtgen)
b := make([]byte, 4, 4)
native.PutUint32(b, val)
attr := nl.NewRtAttr(attrType, b)
req.AddData(attr)
b1 := make([]byte, 4, 4)
native.PutUint32(b1, newnsid)
attr1 := nl.NewRtAttr(NETNSA_NSID, b1)
req.AddData(attr1)
_, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID)
return err
}

View File

@ -0,0 +1,19 @@
// +build !linux
package netlink
func GetNetNsIdByPid(pid int) (int, error) {
return 0, ErrNotImplemented
}
func SetNetNsIdByPid(pid, nsid int) error {
return ErrNotImplemented
}
func GetNetNsIdByFd(fd int) (int, error) {
return 0, ErrNotImplemented
}
func SetNetNsIdByFd(fd, nsid int) error {
return ErrNotImplemented
}

View File

@ -1,17 +1,18 @@
package nl
import (
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
type IfAddrmsg struct {
syscall.IfAddrmsg
unix.IfAddrmsg
}
func NewIfAddrmsg(family int) *IfAddrmsg {
return &IfAddrmsg{
IfAddrmsg: syscall.IfAddrmsg{
IfAddrmsg: unix.IfAddrmsg{
Family: uint8(family),
},
}
@ -35,15 +36,15 @@ func NewIfAddrmsg(family int) *IfAddrmsg {
// SizeofIfAddrmsg = 0x8
func DeserializeIfAddrmsg(b []byte) *IfAddrmsg {
return (*IfAddrmsg)(unsafe.Pointer(&b[0:syscall.SizeofIfAddrmsg][0]))
return (*IfAddrmsg)(unsafe.Pointer(&b[0:unix.SizeofIfAddrmsg][0]))
}
func (msg *IfAddrmsg) Serialize() []byte {
return (*(*[syscall.SizeofIfAddrmsg]byte)(unsafe.Pointer(msg)))[:]
return (*(*[unix.SizeofIfAddrmsg]byte)(unsafe.Pointer(msg)))[:]
}
func (msg *IfAddrmsg) Len() int {
return syscall.SizeofIfAddrmsg
return unix.SizeofIfAddrmsg
}
// struct ifa_cacheinfo {

View File

@ -11,8 +11,8 @@ const (
/* Bridge Flags */
const (
BRIDGE_FLAGS_MASTER = iota /* Bridge command to/from master */
BRIDGE_FLAGS_SELF /* Bridge command to/from lowerdev */
BRIDGE_FLAGS_MASTER = iota + 1 /* Bridge command to/from master */
BRIDGE_FLAGS_SELF /* Bridge command to/from lowerdev */
)
/* Bridge management nested attributes

View File

@ -76,12 +76,14 @@ const (
// __CTA_MAX
// };
const (
CTA_TUPLE_ORIG = 1
CTA_TUPLE_REPLY = 2
CTA_STATUS = 3
CTA_TIMEOUT = 8
CTA_MARK = 9
CTA_PROTOINFO = 4
CTA_TUPLE_ORIG = 1
CTA_TUPLE_REPLY = 2
CTA_STATUS = 3
CTA_TIMEOUT = 7
CTA_MARK = 8
CTA_COUNTERS_ORIG = 9
CTA_COUNTERS_REPLY = 10
CTA_PROTOINFO = 4
)
// enum ctattr_tuple {
@ -163,6 +165,21 @@ const (
CTA_PROTOINFO_TCP_FLAGS_REPLY = 5
)
// enum ctattr_counters {
// CTA_COUNTERS_UNSPEC,
// CTA_COUNTERS_PACKETS, /* 64bit counters */
// CTA_COUNTERS_BYTES, /* 64bit counters */
// CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */
// CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */
// CTA_COUNTERS_PAD,
// __CTA_COUNTERS_M
// };
// #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
const (
CTA_COUNTERS_PACKETS = 1
CTA_COUNTERS_BYTES = 2
)
// /* General form of address family dependent message.
// */
// struct nfgenmsg {

View File

@ -1,35 +1,11 @@
package nl
import (
"syscall"
"unsafe"
)
const (
DEFAULT_CHANGE = 0xFFFFFFFF
// doesn't exist in syscall
IFLA_VFINFO_LIST = syscall.IFLA_IFALIAS + 1 + iota
IFLA_STATS64
IFLA_VF_PORTS
IFLA_PORT_SELF
IFLA_AF_SPEC
IFLA_GROUP
IFLA_NET_NS_FD
IFLA_EXT_MASK
IFLA_PROMISCUITY
IFLA_NUM_TX_QUEUES
IFLA_NUM_RX_QUEUES
IFLA_CARRIER
IFLA_PHYS_PORT_ID
IFLA_CARRIER_CHANGES
IFLA_PHYS_SWITCH_ID
IFLA_LINK_NETNSID
IFLA_PHYS_PORT_NAME
IFLA_PROTO_DOWN
IFLA_GSO_MAX_SEGS
IFLA_GSO_MAX_SIZE
IFLA_PAD
IFLA_XDP
)
const (
@ -118,6 +94,10 @@ const (
IFLA_MACVLAN_UNSPEC = iota
IFLA_MACVLAN_MODE
IFLA_MACVLAN_FLAGS
IFLA_MACVLAN_MACADDR_MODE
IFLA_MACVLAN_MACADDR
IFLA_MACVLAN_MACADDR_DATA
IFLA_MACVLAN_MACADDR_COUNT
IFLA_MACVLAN_MAX = IFLA_MACVLAN_FLAGS
)
@ -129,6 +109,13 @@ const (
MACVLAN_MODE_SOURCE = 16
)
const (
MACVLAN_MACADDR_ADD = iota
MACVLAN_MACADDR_DEL
MACVLAN_MACADDR_FLUSH
MACVLAN_MACADDR_SET
)
const (
IFLA_BOND_UNSPEC = iota
IFLA_BOND_MODE
@ -230,8 +217,11 @@ const (
IFLA_VF_RSS_QUERY_EN /* RSS Redirection Table and Hash Key query
* on/off switch
*/
IFLA_VF_STATS /* network device statistics */
IFLA_VF_MAX = IFLA_VF_STATS
IFLA_VF_STATS /* network device statistics */
IFLA_VF_TRUST /* Trust state of VF */
IFLA_VF_IB_NODE_GUID /* VF Infiniband node GUID */
IFLA_VF_IB_PORT_GUID /* VF Infiniband port GUID */
IFLA_VF_MAX = IFLA_VF_IB_PORT_GUID
)
const (
@ -259,6 +249,8 @@ const (
SizeofVfSpoofchk = 0x08
SizeofVfLinkState = 0x08
SizeofVfRssQueryEn = 0x08
SizeofVfTrust = 0x08
SizeofVfGUID = 0x10
)
// struct ifla_vf_mac {
@ -419,12 +411,66 @@ func (msg *VfRssQueryEn) Serialize() []byte {
return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_trust {
// __u32 vf;
// __u32 setting;
// };
type VfTrust struct {
Vf uint32
Setting uint32
}
func (msg *VfTrust) Len() int {
return SizeofVfTrust
}
func DeserializeVfTrust(b []byte) *VfTrust {
return (*VfTrust)(unsafe.Pointer(&b[0:SizeofVfTrust][0]))
}
func (msg *VfTrust) Serialize() []byte {
return (*(*[SizeofVfTrust]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_guid {
// __u32 vf;
// __u32 rsvd;
// __u64 guid;
// };
type VfGUID struct {
Vf uint32
Rsvd uint32
GUID uint64
}
func (msg *VfGUID) Len() int {
return SizeofVfGUID
}
func DeserializeVfGUID(b []byte) *VfGUID {
return (*VfGUID)(unsafe.Pointer(&b[0:SizeofVfGUID][0]))
}
func (msg *VfGUID) Serialize() []byte {
return (*(*[SizeofVfGUID]byte)(unsafe.Pointer(msg)))[:]
}
const (
XDP_FLAGS_UPDATE_IF_NOEXIST = 1 << iota
XDP_FLAGS_SKB_MODE
XDP_FLAGS_DRV_MODE
XDP_FLAGS_MASK = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE
)
const (
IFLA_XDP_UNSPEC = iota
IFLA_XDP_FD /* fd of xdp program to attach, or -1 to remove */
IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */
IFLA_XDP_FLAGS /* xdp prog related flags */
IFLA_XDP_MAX = IFLA_XDP_FLAGS
IFLA_XDP_PROG_ID /* xdp prog id */
IFLA_XDP_MAX = IFLA_XDP_PROG_ID
)
const (
@ -443,7 +489,12 @@ const (
IFLA_IPTUN_6RD_RELAY_PREFIX
IFLA_IPTUN_6RD_PREFIXLEN
IFLA_IPTUN_6RD_RELAY_PREFIXLEN
IFLA_IPTUN_MAX = IFLA_IPTUN_6RD_RELAY_PREFIXLEN
IFLA_IPTUN_ENCAP_TYPE
IFLA_IPTUN_ENCAP_FLAGS
IFLA_IPTUN_ENCAP_SPORT
IFLA_IPTUN_ENCAP_DPORT
IFLA_IPTUN_COLLECT_METADATA
IFLA_IPTUN_MAX = IFLA_IPTUN_COLLECT_METADATA
)
const (

View File

@ -13,18 +13,23 @@ import (
"unsafe"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
const (
// Family type definitions
FAMILY_ALL = syscall.AF_UNSPEC
FAMILY_V4 = syscall.AF_INET
FAMILY_V6 = syscall.AF_INET6
FAMILY_ALL = unix.AF_UNSPEC
FAMILY_V4 = unix.AF_INET
FAMILY_V6 = unix.AF_INET6
FAMILY_MPLS = AF_MPLS
// Arbitrary set value (greater than default 4k) to allow receiving
// from kernel more verbose messages e.g. for statistics,
// tc rules or filters, or other more memory requiring data.
RECEIVE_BUFFER_SIZE = 65536
)
// SupportedNlFamilies contains the list of netlink families this netlink package supports
var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM, syscall.NETLINK_NETFILTER}
var SupportedNlFamilies = []int{unix.NETLINK_ROUTE, unix.NETLINK_XFRM, unix.NETLINK_NETFILTER}
var nextSeqNr uint32
@ -77,161 +82,161 @@ type NetlinkRequestData interface {
// IfInfomsg is related to links, but it is used for list requests as well
type IfInfomsg struct {
syscall.IfInfomsg
unix.IfInfomsg
}
// Create an IfInfomsg with family specified
func NewIfInfomsg(family int) *IfInfomsg {
return &IfInfomsg{
IfInfomsg: syscall.IfInfomsg{
IfInfomsg: unix.IfInfomsg{
Family: uint8(family),
},
}
}
func DeserializeIfInfomsg(b []byte) *IfInfomsg {
return (*IfInfomsg)(unsafe.Pointer(&b[0:syscall.SizeofIfInfomsg][0]))
return (*IfInfomsg)(unsafe.Pointer(&b[0:unix.SizeofIfInfomsg][0]))
}
func (msg *IfInfomsg) Serialize() []byte {
return (*(*[syscall.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:]
return (*(*[unix.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:]
}
func (msg *IfInfomsg) Len() int {
return syscall.SizeofIfInfomsg
return unix.SizeofIfInfomsg
}
func (msg *IfInfomsg) EncapType() string {
switch msg.Type {
case 0:
return "generic"
case syscall.ARPHRD_ETHER:
case unix.ARPHRD_ETHER:
return "ether"
case syscall.ARPHRD_EETHER:
case unix.ARPHRD_EETHER:
return "eether"
case syscall.ARPHRD_AX25:
case unix.ARPHRD_AX25:
return "ax25"
case syscall.ARPHRD_PRONET:
case unix.ARPHRD_PRONET:
return "pronet"
case syscall.ARPHRD_CHAOS:
case unix.ARPHRD_CHAOS:
return "chaos"
case syscall.ARPHRD_IEEE802:
case unix.ARPHRD_IEEE802:
return "ieee802"
case syscall.ARPHRD_ARCNET:
case unix.ARPHRD_ARCNET:
return "arcnet"
case syscall.ARPHRD_APPLETLK:
case unix.ARPHRD_APPLETLK:
return "atalk"
case syscall.ARPHRD_DLCI:
case unix.ARPHRD_DLCI:
return "dlci"
case syscall.ARPHRD_ATM:
case unix.ARPHRD_ATM:
return "atm"
case syscall.ARPHRD_METRICOM:
case unix.ARPHRD_METRICOM:
return "metricom"
case syscall.ARPHRD_IEEE1394:
case unix.ARPHRD_IEEE1394:
return "ieee1394"
case syscall.ARPHRD_INFINIBAND:
case unix.ARPHRD_INFINIBAND:
return "infiniband"
case syscall.ARPHRD_SLIP:
case unix.ARPHRD_SLIP:
return "slip"
case syscall.ARPHRD_CSLIP:
case unix.ARPHRD_CSLIP:
return "cslip"
case syscall.ARPHRD_SLIP6:
case unix.ARPHRD_SLIP6:
return "slip6"
case syscall.ARPHRD_CSLIP6:
case unix.ARPHRD_CSLIP6:
return "cslip6"
case syscall.ARPHRD_RSRVD:
case unix.ARPHRD_RSRVD:
return "rsrvd"
case syscall.ARPHRD_ADAPT:
case unix.ARPHRD_ADAPT:
return "adapt"
case syscall.ARPHRD_ROSE:
case unix.ARPHRD_ROSE:
return "rose"
case syscall.ARPHRD_X25:
case unix.ARPHRD_X25:
return "x25"
case syscall.ARPHRD_HWX25:
case unix.ARPHRD_HWX25:
return "hwx25"
case syscall.ARPHRD_PPP:
case unix.ARPHRD_PPP:
return "ppp"
case syscall.ARPHRD_HDLC:
case unix.ARPHRD_HDLC:
return "hdlc"
case syscall.ARPHRD_LAPB:
case unix.ARPHRD_LAPB:
return "lapb"
case syscall.ARPHRD_DDCMP:
case unix.ARPHRD_DDCMP:
return "ddcmp"
case syscall.ARPHRD_RAWHDLC:
case unix.ARPHRD_RAWHDLC:
return "rawhdlc"
case syscall.ARPHRD_TUNNEL:
case unix.ARPHRD_TUNNEL:
return "ipip"
case syscall.ARPHRD_TUNNEL6:
case unix.ARPHRD_TUNNEL6:
return "tunnel6"
case syscall.ARPHRD_FRAD:
case unix.ARPHRD_FRAD:
return "frad"
case syscall.ARPHRD_SKIP:
case unix.ARPHRD_SKIP:
return "skip"
case syscall.ARPHRD_LOOPBACK:
case unix.ARPHRD_LOOPBACK:
return "loopback"
case syscall.ARPHRD_LOCALTLK:
case unix.ARPHRD_LOCALTLK:
return "ltalk"
case syscall.ARPHRD_FDDI:
case unix.ARPHRD_FDDI:
return "fddi"
case syscall.ARPHRD_BIF:
case unix.ARPHRD_BIF:
return "bif"
case syscall.ARPHRD_SIT:
case unix.ARPHRD_SIT:
return "sit"
case syscall.ARPHRD_IPDDP:
case unix.ARPHRD_IPDDP:
return "ip/ddp"
case syscall.ARPHRD_IPGRE:
case unix.ARPHRD_IPGRE:
return "gre"
case syscall.ARPHRD_PIMREG:
case unix.ARPHRD_PIMREG:
return "pimreg"
case syscall.ARPHRD_HIPPI:
case unix.ARPHRD_HIPPI:
return "hippi"
case syscall.ARPHRD_ASH:
case unix.ARPHRD_ASH:
return "ash"
case syscall.ARPHRD_ECONET:
case unix.ARPHRD_ECONET:
return "econet"
case syscall.ARPHRD_IRDA:
case unix.ARPHRD_IRDA:
return "irda"
case syscall.ARPHRD_FCPP:
case unix.ARPHRD_FCPP:
return "fcpp"
case syscall.ARPHRD_FCAL:
case unix.ARPHRD_FCAL:
return "fcal"
case syscall.ARPHRD_FCPL:
case unix.ARPHRD_FCPL:
return "fcpl"
case syscall.ARPHRD_FCFABRIC:
case unix.ARPHRD_FCFABRIC:
return "fcfb0"
case syscall.ARPHRD_FCFABRIC + 1:
case unix.ARPHRD_FCFABRIC + 1:
return "fcfb1"
case syscall.ARPHRD_FCFABRIC + 2:
case unix.ARPHRD_FCFABRIC + 2:
return "fcfb2"
case syscall.ARPHRD_FCFABRIC + 3:
case unix.ARPHRD_FCFABRIC + 3:
return "fcfb3"
case syscall.ARPHRD_FCFABRIC + 4:
case unix.ARPHRD_FCFABRIC + 4:
return "fcfb4"
case syscall.ARPHRD_FCFABRIC + 5:
case unix.ARPHRD_FCFABRIC + 5:
return "fcfb5"
case syscall.ARPHRD_FCFABRIC + 6:
case unix.ARPHRD_FCFABRIC + 6:
return "fcfb6"
case syscall.ARPHRD_FCFABRIC + 7:
case unix.ARPHRD_FCFABRIC + 7:
return "fcfb7"
case syscall.ARPHRD_FCFABRIC + 8:
case unix.ARPHRD_FCFABRIC + 8:
return "fcfb8"
case syscall.ARPHRD_FCFABRIC + 9:
case unix.ARPHRD_FCFABRIC + 9:
return "fcfb9"
case syscall.ARPHRD_FCFABRIC + 10:
case unix.ARPHRD_FCFABRIC + 10:
return "fcfb10"
case syscall.ARPHRD_FCFABRIC + 11:
case unix.ARPHRD_FCFABRIC + 11:
return "fcfb11"
case syscall.ARPHRD_FCFABRIC + 12:
case unix.ARPHRD_FCFABRIC + 12:
return "fcfb12"
case syscall.ARPHRD_IEEE802_TR:
case unix.ARPHRD_IEEE802_TR:
return "tr"
case syscall.ARPHRD_IEEE80211:
case unix.ARPHRD_IEEE80211:
return "ieee802.11"
case syscall.ARPHRD_IEEE80211_PRISM:
case unix.ARPHRD_IEEE80211_PRISM:
return "ieee802.11/prism"
case syscall.ARPHRD_IEEE80211_RADIOTAP:
case unix.ARPHRD_IEEE80211_RADIOTAP:
return "ieee802.11/radiotap"
case syscall.ARPHRD_IEEE802154:
case unix.ARPHRD_IEEE802154:
return "ieee802.15.4"
case 65534:
@ -243,7 +248,7 @@ func (msg *IfInfomsg) EncapType() string {
}
func rtaAlignOf(attrlen int) int {
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
return (attrlen + unix.RTA_ALIGNTO - 1) & ^(unix.RTA_ALIGNTO - 1)
}
func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
@ -254,7 +259,7 @@ func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
// Extend RtAttr to handle data and children
type RtAttr struct {
syscall.RtAttr
unix.RtAttr
Data []byte
children []NetlinkRequestData
}
@ -262,7 +267,7 @@ type RtAttr struct {
// Create a new Extended RtAttr object
func NewRtAttr(attrType int, data []byte) *RtAttr {
return &RtAttr{
RtAttr: syscall.RtAttr{
RtAttr: unix.RtAttr{
Type: uint16(attrType),
},
children: []NetlinkRequestData{},
@ -270,23 +275,35 @@ func NewRtAttr(attrType int, data []byte) *RtAttr {
}
}
// Create a new RtAttr obj anc add it as a child of an existing object
// NewRtAttrChild adds an RtAttr as a child to the parent and returns the new attribute
//
// Deprecated: Use AddRtAttr() on the parent object
func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
return parent.AddRtAttr(attrType, data)
}
// AddRtAttr adds an RtAttr as a child and returns the new attribute
func (a *RtAttr) AddRtAttr(attrType int, data []byte) *RtAttr {
attr := NewRtAttr(attrType, data)
parent.children = append(parent.children, attr)
a.children = append(a.children, attr)
return attr
}
// AddChild adds an existing NetlinkRequestData as a child.
func (a *RtAttr) AddChild(attr NetlinkRequestData) {
a.children = append(a.children, attr)
}
func (a *RtAttr) Len() int {
if len(a.children) == 0 {
return (syscall.SizeofRtAttr + len(a.Data))
return (unix.SizeofRtAttr + len(a.Data))
}
l := 0
for _, child := range a.children {
l += rtaAlignOf(child.Len())
}
l += syscall.SizeofRtAttr
l += unix.SizeofRtAttr
return rtaAlignOf(l + len(a.Data))
}
@ -319,7 +336,7 @@ func (a *RtAttr) Serialize() []byte {
}
type NetlinkRequest struct {
syscall.NlMsghdr
unix.NlMsghdr
Data []NetlinkRequestData
RawData []byte
Sockets map[int]*SocketHandle
@ -327,7 +344,7 @@ type NetlinkRequest struct {
// Serialize the Netlink Request into a byte array
func (req *NetlinkRequest) Serialize() []byte {
length := syscall.SizeofNlMsghdr
length := unix.SizeofNlMsghdr
dataBytes := make([][]byte, len(req.Data))
for i, data := range req.Data {
dataBytes[i] = data.Serialize()
@ -337,8 +354,8 @@ func (req *NetlinkRequest) Serialize() []byte {
req.Len = uint32(length)
b := make([]byte, length)
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
next := syscall.SizeofNlMsghdr
hdr := (*(*[unix.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
next := unix.SizeofNlMsghdr
copy(b[0:next], hdr)
for _, data := range dataBytes {
for _, dataByte := range data {
@ -354,16 +371,12 @@ func (req *NetlinkRequest) Serialize() []byte {
}
func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
if data != nil {
req.Data = append(req.Data, data)
}
req.Data = append(req.Data, data)
}
// AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization
func (req *NetlinkRequest) AddRawData(data []byte) {
if data != nil {
req.RawData = append(req.RawData, data...)
}
req.RawData = append(req.RawData, data...)
}
// Execute the request against a the given sockType.
@ -421,10 +434,10 @@ done:
if m.Header.Pid != pid {
return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
}
if m.Header.Type == syscall.NLMSG_DONE {
if m.Header.Type == unix.NLMSG_DONE {
break done
}
if m.Header.Type == syscall.NLMSG_ERROR {
if m.Header.Type == unix.NLMSG_ERROR {
native := NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
@ -436,7 +449,7 @@ done:
continue
}
res = append(res, m.Data)
if m.Header.Flags&syscall.NLM_F_MULTI == 0 {
if m.Header.Flags&unix.NLM_F_MULTI == 0 {
break done
}
}
@ -449,10 +462,10 @@ done:
// the message is serialized
func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
return &NetlinkRequest{
NlMsghdr: syscall.NlMsghdr{
Len: uint32(syscall.SizeofNlMsghdr),
NlMsghdr: unix.NlMsghdr{
Len: uint32(unix.SizeofNlMsghdr),
Type: uint16(proto),
Flags: syscall.NLM_F_REQUEST | uint16(flags),
Flags: unix.NLM_F_REQUEST | uint16(flags),
Seq: atomic.AddUint32(&nextSeqNr, 1),
},
}
@ -460,21 +473,21 @@ func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
type NetlinkSocket struct {
fd int32
lsa syscall.SockaddrNetlink
lsa unix.SockaddrNetlink
sync.Mutex
}
func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, protocol)
fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, protocol)
if err != nil {
return nil, err
}
s := &NetlinkSocket{
fd: int32(fd),
}
s.lsa.Family = syscall.AF_NETLINK
if err := syscall.Bind(fd, &s.lsa); err != nil {
syscall.Close(fd)
s.lsa.Family = unix.AF_NETLINK
if err := unix.Bind(fd, &s.lsa); err != nil {
unix.Close(fd)
return nil, err
}
@ -551,21 +564,21 @@ func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) {
// Returns the netlink socket on which Receive() method can be called
// to retrieve the messages from the kernel.
func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, protocol)
if err != nil {
return nil, err
}
s := &NetlinkSocket{
fd: int32(fd),
}
s.lsa.Family = syscall.AF_NETLINK
s.lsa.Family = unix.AF_NETLINK
for _, g := range groups {
s.lsa.Groups |= (1 << (g - 1))
}
if err := syscall.Bind(fd, &s.lsa); err != nil {
syscall.Close(fd)
if err := unix.Bind(fd, &s.lsa); err != nil {
unix.Close(fd)
return nil, err
}
@ -586,7 +599,7 @@ func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*Ne
func (s *NetlinkSocket) Close() {
fd := int(atomic.SwapInt32(&s.fd, -1))
syscall.Close(fd)
unix.Close(fd)
}
func (s *NetlinkSocket) GetFd() int {
@ -598,7 +611,7 @@ func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
if fd < 0 {
return fmt.Errorf("Send called on a closed socket")
}
if err := syscall.Sendto(fd, request.Serialize(), 0, &s.lsa); err != nil {
if err := unix.Sendto(fd, request.Serialize(), 0, &s.lsa); err != nil {
return err
}
return nil
@ -609,26 +622,40 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
if fd < 0 {
return nil, fmt.Errorf("Receive called on a closed socket")
}
rb := make([]byte, syscall.Getpagesize())
nr, _, err := syscall.Recvfrom(fd, rb, 0)
rb := make([]byte, RECEIVE_BUFFER_SIZE)
nr, _, err := unix.Recvfrom(fd, rb, 0)
if err != nil {
return nil, err
}
if nr < syscall.NLMSG_HDRLEN {
if nr < unix.NLMSG_HDRLEN {
return nil, fmt.Errorf("Got short response from netlink")
}
rb = rb[:nr]
return syscall.ParseNetlinkMessage(rb)
}
// SetSendTimeout allows to set a send timeout on the socket
func (s *NetlinkSocket) SetSendTimeout(timeout *unix.Timeval) error {
// Set a send timeout of SOCKET_SEND_TIMEOUT, this will allow the Send to periodically unblock and avoid that a routine
// remains stuck on a send on a closed fd
return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_SNDTIMEO, timeout)
}
// SetReceiveTimeout allows to set a receive timeout on the socket
func (s *NetlinkSocket) SetReceiveTimeout(timeout *unix.Timeval) error {
// Set a read timeout of SOCKET_READ_TIMEOUT, this will allow the Read to periodically unblock and avoid that a routine
// remains stuck on a recvmsg on a closed fd
return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, timeout)
}
func (s *NetlinkSocket) GetPid() (uint32, error) {
fd := int(atomic.LoadInt32(&s.fd))
lsa, err := syscall.Getsockname(fd)
lsa, err := unix.Getsockname(fd)
if err != nil {
return 0, err
}
switch v := lsa.(type) {
case *syscall.SockaddrNetlink:
case *unix.SockaddrNetlink:
return v.Pid, nil
}
return 0, fmt.Errorf("Wrong socket type")
@ -683,24 +710,24 @@ func Uint64Attr(v uint64) []byte {
func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) {
var attrs []syscall.NetlinkRouteAttr
for len(b) >= syscall.SizeofRtAttr {
for len(b) >= unix.SizeofRtAttr {
a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
if err != nil {
return nil, err
}
ra := syscall.NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-syscall.SizeofRtAttr]}
ra := syscall.NetlinkRouteAttr{Attr: syscall.RtAttr(*a), Value: vbuf[:int(a.Len)-unix.SizeofRtAttr]}
attrs = append(attrs, ra)
b = b[alen:]
}
return attrs, nil
}
func netlinkRouteAttrAndValue(b []byte) (*syscall.RtAttr, []byte, int, error) {
a := (*syscall.RtAttr)(unsafe.Pointer(&b[0]))
if int(a.Len) < syscall.SizeofRtAttr || int(a.Len) > len(b) {
return nil, nil, 0, syscall.EINVAL
func netlinkRouteAttrAndValue(b []byte) (*unix.RtAttr, []byte, int, error) {
a := (*unix.RtAttr)(unsafe.Pointer(&b[0]))
if int(a.Len) < unix.SizeofRtAttr || int(a.Len) > len(b) {
return nil, nil, 0, unix.EINVAL
}
return a, b[syscall.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
return a, b[unix.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
}
// SocketHandle contains the netlink socket and the associated

View File

@ -0,0 +1,31 @@
package nl
const (
RDMA_NL_GET_CLIENT_SHIFT = 10
)
const (
RDMA_NL_NLDEV = 5
)
const (
RDMA_NLDEV_CMD_GET = 1
RDMA_NLDEV_CMD_SET = 2
)
const (
RDMA_NLDEV_ATTR_DEV_INDEX = 1
RDMA_NLDEV_ATTR_DEV_NAME = 2
RDMA_NLDEV_ATTR_PORT_INDEX = 3
RDMA_NLDEV_ATTR_CAP_FLAGS = 4
RDMA_NLDEV_ATTR_FW_VERSION = 5
RDMA_NLDEV_ATTR_NODE_GUID = 6
RDMA_NLDEV_ATTR_SYS_IMAGE_GUID = 7
RDMA_NLDEV_ATTR_SUBNET_PREFIX = 8
RDMA_NLDEV_ATTR_LID = 9
RDMA_NLDEV_ATTR_SM_LID = 10
RDMA_NLDEV_ATTR_LMC = 11
RDMA_NLDEV_ATTR_PORT_STATE = 12
RDMA_NLDEV_ATTR_PORT_PHYS_STATE = 13
RDMA_NLDEV_ATTR_DEV_NODE_TYPE = 14
)

View File

@ -1,65 +1,66 @@
package nl
import (
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
type RtMsg struct {
syscall.RtMsg
unix.RtMsg
}
func NewRtMsg() *RtMsg {
return &RtMsg{
RtMsg: syscall.RtMsg{
Table: syscall.RT_TABLE_MAIN,
Scope: syscall.RT_SCOPE_UNIVERSE,
Protocol: syscall.RTPROT_BOOT,
Type: syscall.RTN_UNICAST,
RtMsg: unix.RtMsg{
Table: unix.RT_TABLE_MAIN,
Scope: unix.RT_SCOPE_UNIVERSE,
Protocol: unix.RTPROT_BOOT,
Type: unix.RTN_UNICAST,
},
}
}
func NewRtDelMsg() *RtMsg {
return &RtMsg{
RtMsg: syscall.RtMsg{
Table: syscall.RT_TABLE_MAIN,
Scope: syscall.RT_SCOPE_NOWHERE,
RtMsg: unix.RtMsg{
Table: unix.RT_TABLE_MAIN,
Scope: unix.RT_SCOPE_NOWHERE,
},
}
}
func (msg *RtMsg) Len() int {
return syscall.SizeofRtMsg
return unix.SizeofRtMsg
}
func DeserializeRtMsg(b []byte) *RtMsg {
return (*RtMsg)(unsafe.Pointer(&b[0:syscall.SizeofRtMsg][0]))
return (*RtMsg)(unsafe.Pointer(&b[0:unix.SizeofRtMsg][0]))
}
func (msg *RtMsg) Serialize() []byte {
return (*(*[syscall.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:]
return (*(*[unix.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:]
}
type RtNexthop struct {
syscall.RtNexthop
unix.RtNexthop
Children []NetlinkRequestData
}
func DeserializeRtNexthop(b []byte) *RtNexthop {
return (*RtNexthop)(unsafe.Pointer(&b[0:syscall.SizeofRtNexthop][0]))
return (*RtNexthop)(unsafe.Pointer(&b[0:unix.SizeofRtNexthop][0]))
}
func (msg *RtNexthop) Len() int {
if len(msg.Children) == 0 {
return syscall.SizeofRtNexthop
return unix.SizeofRtNexthop
}
l := 0
for _, child := range msg.Children {
l += rtaAlignOf(child.Len())
}
l += syscall.SizeofRtNexthop
l += unix.SizeofRtNexthop
return rtaAlignOf(l)
}
@ -67,8 +68,8 @@ func (msg *RtNexthop) Serialize() []byte {
length := msg.Len()
msg.RtNexthop.Len = uint16(length)
buf := make([]byte, length)
copy(buf, (*(*[syscall.SizeofRtNexthop]byte)(unsafe.Pointer(msg)))[:])
next := rtaAlignOf(syscall.SizeofRtNexthop)
copy(buf, (*(*[unix.SizeofRtNexthop]byte)(unsafe.Pointer(msg)))[:])
next := rtaAlignOf(unix.SizeofRtNexthop)
if len(msg.Children) > 0 {
for _, child := range msg.Children {
childBuf := child.Serialize()
@ -78,3 +79,29 @@ func (msg *RtNexthop) Serialize() []byte {
}
return buf
}
type RtGenMsg struct {
unix.RtGenmsg
}
func NewRtGenMsg() *RtGenMsg {
return &RtGenMsg{
RtGenmsg: unix.RtGenmsg{
Family: unix.AF_UNSPEC,
},
}
}
func (msg *RtGenMsg) Len() int {
return rtaAlignOf(unix.SizeofRtGenmsg)
}
func DeserializeRtGenMsg(b []byte) *RtGenMsg {
return &RtGenMsg{RtGenmsg: unix.RtGenmsg{Family: b[0]}}
}
func (msg *RtGenMsg) Serialize() []byte {
out := make([]byte, msg.Len())
out[0] = msg.Family
return out
}

154
vendor/github.com/vishvananda/netlink/nl/seg6_linux.go generated vendored Normal file
View File

@ -0,0 +1,154 @@
package nl
import (
"errors"
"fmt"
"net"
)
type IPv6SrHdr struct {
nextHdr uint8
hdrLen uint8
routingType uint8
segmentsLeft uint8
firstSegment uint8
flags uint8
reserved uint16
Segments []net.IP
}
func (s1 *IPv6SrHdr) Equal(s2 IPv6SrHdr) bool {
if len(s1.Segments) != len(s2.Segments) {
return false
}
for i := range s1.Segments {
if s1.Segments[i].Equal(s2.Segments[i]) != true {
return false
}
}
return s1.nextHdr == s2.nextHdr &&
s1.hdrLen == s2.hdrLen &&
s1.routingType == s2.routingType &&
s1.segmentsLeft == s2.segmentsLeft &&
s1.firstSegment == s2.firstSegment &&
s1.flags == s2.flags
// reserved doesn't need to be identical.
}
// seg6 encap mode
const (
SEG6_IPTUN_MODE_INLINE = iota
SEG6_IPTUN_MODE_ENCAP
)
// number of nested RTATTR
// from include/uapi/linux/seg6_iptunnel.h
const (
SEG6_IPTUNNEL_UNSPEC = iota
SEG6_IPTUNNEL_SRH
__SEG6_IPTUNNEL_MAX
)
const (
SEG6_IPTUNNEL_MAX = __SEG6_IPTUNNEL_MAX - 1
)
func EncodeSEG6Encap(mode int, segments []net.IP) ([]byte, error) {
nsegs := len(segments) // nsegs: number of segments
if nsegs == 0 {
return nil, errors.New("EncodeSEG6Encap: No Segment in srh")
}
b := make([]byte, 12, 12+len(segments)*16)
native := NativeEndian()
native.PutUint32(b, uint32(mode))
b[4] = 0 // srh.nextHdr (0 when calling netlink)
b[5] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit)
b[6] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA)
b[7] = uint8(nsegs - 1) // srh.segmentsLeft
b[8] = uint8(nsegs - 1) // srh.firstSegment
b[9] = 0 // srh.flags (SR6_FLAG1_HMAC for srh_hmac)
// srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07
native.PutUint16(b[10:], 0) // srh.reserved
for _, netIP := range segments {
b = append(b, netIP...) // srh.Segments
}
return b, nil
}
func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) {
native := NativeEndian()
mode := int(native.Uint32(buf))
srh := IPv6SrHdr{
nextHdr: buf[4],
hdrLen: buf[5],
routingType: buf[6],
segmentsLeft: buf[7],
firstSegment: buf[8],
flags: buf[9],
reserved: native.Uint16(buf[10:12]),
}
buf = buf[12:]
if len(buf)%16 != 0 {
err := fmt.Errorf("DecodeSEG6Encap: error parsing Segment List (buf len: %d)\n", len(buf))
return mode, nil, err
}
for len(buf) > 0 {
srh.Segments = append(srh.Segments, net.IP(buf[:16]))
buf = buf[16:]
}
return mode, srh.Segments, nil
}
func DecodeSEG6Srh(buf []byte) ([]net.IP, error) {
native := NativeEndian()
srh := IPv6SrHdr{
nextHdr: buf[0],
hdrLen: buf[1],
routingType: buf[2],
segmentsLeft: buf[3],
firstSegment: buf[4],
flags: buf[5],
reserved: native.Uint16(buf[6:8]),
}
buf = buf[8:]
if len(buf)%16 != 0 {
err := fmt.Errorf("DecodeSEG6Srh: error parsing Segment List (buf len: %d)", len(buf))
return nil, err
}
for len(buf) > 0 {
srh.Segments = append(srh.Segments, net.IP(buf[:16]))
buf = buf[16:]
}
return srh.Segments, nil
}
func EncodeSEG6Srh(segments []net.IP) ([]byte, error) {
nsegs := len(segments) // nsegs: number of segments
if nsegs == 0 {
return nil, errors.New("EncodeSEG6Srh: No Segments")
}
b := make([]byte, 8, 8+len(segments)*16)
native := NativeEndian()
b[0] = 0 // srh.nextHdr (0 when calling netlink)
b[1] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit)
b[2] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA)
b[3] = uint8(nsegs - 1) // srh.segmentsLeft
b[4] = uint8(nsegs - 1) // srh.firstSegment
b[5] = 0 // srh.flags (SR6_FLAG1_HMAC for srh_hmac)
// srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07
native.PutUint16(b[6:], 0) // srh.reserved
for _, netIP := range segments {
b = append(b, netIP...) // srh.Segments
}
return b, nil
}
// Helper functions
func SEG6EncapModeString(mode int) string {
switch mode {
case SEG6_IPTUN_MODE_INLINE:
return "inline"
case SEG6_IPTUN_MODE_ENCAP:
return "encap"
}
return "unknown"
}

View File

@ -0,0 +1,76 @@
package nl
import ()
// seg6local parameters
const (
SEG6_LOCAL_UNSPEC = iota
SEG6_LOCAL_ACTION
SEG6_LOCAL_SRH
SEG6_LOCAL_TABLE
SEG6_LOCAL_NH4
SEG6_LOCAL_NH6
SEG6_LOCAL_IIF
SEG6_LOCAL_OIF
__SEG6_LOCAL_MAX
)
const (
SEG6_LOCAL_MAX = __SEG6_LOCAL_MAX
)
// seg6local actions
const (
SEG6_LOCAL_ACTION_END = iota + 1 // 1
SEG6_LOCAL_ACTION_END_X // 2
SEG6_LOCAL_ACTION_END_T // 3
SEG6_LOCAL_ACTION_END_DX2 // 4
SEG6_LOCAL_ACTION_END_DX6 // 5
SEG6_LOCAL_ACTION_END_DX4 // 6
SEG6_LOCAL_ACTION_END_DT6 // 7
SEG6_LOCAL_ACTION_END_DT4 // 8
SEG6_LOCAL_ACTION_END_B6 // 9
SEG6_LOCAL_ACTION_END_B6_ENCAPS // 10
SEG6_LOCAL_ACTION_END_BM // 11
SEG6_LOCAL_ACTION_END_S // 12
SEG6_LOCAL_ACTION_END_AS // 13
SEG6_LOCAL_ACTION_END_AM // 14
__SEG6_LOCAL_ACTION_MAX
)
const (
SEG6_LOCAL_ACTION_MAX = __SEG6_LOCAL_ACTION_MAX - 1
)
// Helper functions
func SEG6LocalActionString(action int) string {
switch action {
case SEG6_LOCAL_ACTION_END:
return "End"
case SEG6_LOCAL_ACTION_END_X:
return "End.X"
case SEG6_LOCAL_ACTION_END_T:
return "End.T"
case SEG6_LOCAL_ACTION_END_DX2:
return "End.DX2"
case SEG6_LOCAL_ACTION_END_DX6:
return "End.DX6"
case SEG6_LOCAL_ACTION_END_DX4:
return "End.DX4"
case SEG6_LOCAL_ACTION_END_DT6:
return "End.DT6"
case SEG6_LOCAL_ACTION_END_DT4:
return "End.DT4"
case SEG6_LOCAL_ACTION_END_B6:
return "End.B6"
case SEG6_LOCAL_ACTION_END_B6_ENCAPS:
return "End.B6.Encaps"
case SEG6_LOCAL_ACTION_END_BM:
return "End.BM"
case SEG6_LOCAL_ACTION_END_S:
return "End.S"
case SEG6_LOCAL_ACTION_END_AS:
return "End.AS"
case SEG6_LOCAL_ACTION_END_AM:
return "End.AM"
}
return "unknown"
}

View File

@ -65,4 +65,15 @@ const (
LWTUNNEL_ENCAP_IP
LWTUNNEL_ENCAP_ILA
LWTUNNEL_ENCAP_IP6
LWTUNNEL_ENCAP_SEG6
LWTUNNEL_ENCAP_BPF
LWTUNNEL_ENCAP_SEG6_LOCAL
)
// routing header types
const (
IPV6_SRCRT_STRICT = 0x01 // Deprecated; will be removed
IPV6_SRCRT_TYPE_0 = 0 // Deprecated; will be removed
IPV6_SRCRT_TYPE_2 = 2 // IPv6 type 2 Routing Header
IPV6_SRCRT_TYPE_4 = 4 // Segment Routing with IPv6
)

View File

@ -1,6 +1,7 @@
package nl
import (
"encoding/binary"
"unsafe"
)
@ -64,6 +65,15 @@ const (
TCA_PRIO_MAX = TCA_PRIO_MQ
)
const (
TCA_STATS_UNSPEC = iota
TCA_STATS_BASIC
TCA_STATS_RATE_EST
TCA_STATS_QUEUE
TCA_STATS_APP
TCA_STATS_MAX = TCA_STATS_APP
)
const (
SizeofTcMsg = 0x14
SizeofTcActionMsg = 0x04
@ -412,6 +422,57 @@ func (x *TcHtbGlob) Serialize() []byte {
return (*(*[SizeofTcHtbGlob]byte)(unsafe.Pointer(x)))[:]
}
// HFSC
type Curve struct {
m1 uint32
d uint32
m2 uint32
}
type HfscCopt struct {
Rsc Curve
Fsc Curve
Usc Curve
}
func (c *Curve) Attrs() (uint32, uint32, uint32) {
return c.m1, c.d, c.m2
}
func (c *Curve) Set(m1 uint32, d uint32, m2 uint32) {
c.m1 = m1
c.d = d
c.m2 = m2
}
func DeserializeHfscCurve(b []byte) *Curve {
return &Curve{
m1: binary.LittleEndian.Uint32(b[0:4]),
d: binary.LittleEndian.Uint32(b[4:8]),
m2: binary.LittleEndian.Uint32(b[8:12]),
}
}
func SerializeHfscCurve(c *Curve) (b []byte) {
t := make([]byte, binary.MaxVarintLen32)
binary.LittleEndian.PutUint32(t, c.m1)
b = append(b, t[:4]...)
binary.LittleEndian.PutUint32(t, c.d)
b = append(b, t[:4]...)
binary.LittleEndian.PutUint32(t, c.m2)
b = append(b, t[:4]...)
return b
}
type TcHfscOpt struct {
Defcls uint16
}
func (x *TcHfscOpt) Serialize() []byte {
return (*(*[2]byte)(unsafe.Pointer(x)))[:]
}
const (
TCA_U32_UNSPEC = iota
TCA_U32_CLASSID
@ -673,3 +734,45 @@ const (
TCA_FW_MASK
TCA_FW_MAX = TCA_FW_MASK
)
const (
TCA_MATCHALL_UNSPEC = iota
TCA_MATCHALL_CLASSID
TCA_MATCHALL_ACT
TCA_MATCHALL_FLAGS
)
const (
TCA_FQ_UNSPEC = iota
TCA_FQ_PLIMIT // limit of total number of packets in queue
TCA_FQ_FLOW_PLIMIT // limit of packets per flow
TCA_FQ_QUANTUM // RR quantum
TCA_FQ_INITIAL_QUANTUM // RR quantum for new flow
TCA_FQ_RATE_ENABLE // enable/disable rate limiting
TCA_FQ_FLOW_DEFAULT_RATE // obsolete do not use
TCA_FQ_FLOW_MAX_RATE // per flow max rate
TCA_FQ_BUCKETS_LOG // log2(number of buckets)
TCA_FQ_FLOW_REFILL_DELAY // flow credit refill delay in usec
TCA_FQ_ORPHAN_MASK // mask applied to orphaned skb hashes
TCA_FQ_LOW_RATE_THRESHOLD // per packet delay under this rate
)
const (
TCA_FQ_CODEL_UNSPEC = iota
TCA_FQ_CODEL_TARGET
TCA_FQ_CODEL_LIMIT
TCA_FQ_CODEL_INTERVAL
TCA_FQ_CODEL_ECN
TCA_FQ_CODEL_FLOWS
TCA_FQ_CODEL_QUANTUM
TCA_FQ_CODEL_CE_THRESHOLD
TCA_FQ_CODEL_DROP_BATCH_SIZE
TCA_FQ_CODEL_MEMORY_LIMIT
)
const (
TCA_HFSC_UNSPEC = iota
TCA_HFSC_RSC
TCA_HFSC_FSC
TCA_HFSC_USC
)

View File

@ -18,6 +18,10 @@ type Protinfo struct {
// String returns a list of enabled flags
func (prot *Protinfo) String() string {
if prot == nil {
return "<nil>"
}
var boolStrings []string
if prot.Hairpin {
boolStrings = append(boolStrings, "Hairpin")

View File

@ -5,6 +5,7 @@ import (
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
func LinkGetProtinfo(link Link) (Protinfo, error) {
@ -15,10 +16,10 @@ func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) {
base := link.Attrs()
h.ensureIndex(base)
var pi Protinfo
req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0)
msgs, err := req.Execute(unix.NETLINK_ROUTE, 0)
if err != nil {
return pi, err
}
@ -33,14 +34,14 @@ func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) {
return pi, err
}
for _, attr := range attrs {
if attr.Attr.Type != syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED {
if attr.Attr.Type != unix.IFLA_PROTINFO|unix.NLA_F_NESTED {
continue
}
infos, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return pi, err
}
pi = *parseProtinfo(infos)
pi = parseProtinfo(infos)
return pi, nil
}
@ -48,8 +49,7 @@ func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) {
return pi, fmt.Errorf("Device with index %d not found", base.Index)
}
func parseProtinfo(infos []syscall.NetlinkRouteAttr) *Protinfo {
var pi Protinfo
func parseProtinfo(infos []syscall.NetlinkRouteAttr) (pi Protinfo) {
for _, info := range infos {
switch info.Attr.Type {
case nl.IFLA_BRPORT_MODE:
@ -70,5 +70,5 @@ func parseProtinfo(infos []syscall.NetlinkRouteAttr) *Protinfo {
pi.ProxyArpWiFi = byteToBool(info.Value[0])
}
}
return &pi
return
}

View File

@ -176,6 +176,13 @@ type Netem struct {
CorruptCorr uint32
}
func (netem *Netem) String() string {
return fmt.Sprintf(
"{Latency: %v, Limit: %v, Loss: %v, Gap: %v, Duplicate: %v, Jitter: %v}",
netem.Latency, netem.Limit, netem.Loss, netem.Gap, netem.Duplicate, netem.Jitter,
)
}
func (qdisc *Netem) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
@ -230,3 +237,104 @@ func (qdisc *GenericQdisc) Attrs() *QdiscAttrs {
func (qdisc *GenericQdisc) Type() string {
return qdisc.QdiscType
}
type Hfsc struct {
QdiscAttrs
Defcls uint16
}
func NewHfsc(attrs QdiscAttrs) *Hfsc {
return &Hfsc{
QdiscAttrs: attrs,
Defcls: 1,
}
}
func (hfsc *Hfsc) Attrs() *QdiscAttrs {
return &hfsc.QdiscAttrs
}
func (hfsc *Hfsc) Type() string {
return "hfsc"
}
func (hfsc *Hfsc) String() string {
return fmt.Sprintf(
"{%v -- default: %d}",
hfsc.Attrs(), hfsc.Defcls,
)
}
// Fq is a classless packet scheduler meant to be mostly used for locally generated traffic.
type Fq struct {
QdiscAttrs
PacketLimit uint32
FlowPacketLimit uint32
// In bytes
Quantum uint32
InitialQuantum uint32
// called RateEnable under the hood
Pacing uint32
FlowDefaultRate uint32
FlowMaxRate uint32
// called BucketsLog under the hood
Buckets uint32
FlowRefillDelay uint32
LowRateThreshold uint32
}
func (fq *Fq) String() string {
return fmt.Sprintf(
"{PacketLimit: %v, FlowPacketLimit: %v, Quantum: %v, InitalQuantum: %v, Pacing: %v, FlowDefaultRate: %v, FlowMaxRate: %v, Buckets: %v, FlowRefillDelay: %v, LowRateTreshold: %v}",
fq.PacketLimit, fq.FlowPacketLimit, fq.Quantum, fq.InitialQuantum, fq.Pacing, fq.FlowDefaultRate, fq.FlowMaxRate, fq.Buckets, fq.FlowRefillDelay, fq.LowRateThreshold,
)
}
func NewFq(attrs QdiscAttrs) *Fq {
return &Fq{
QdiscAttrs: attrs,
Pacing: 1,
}
}
func (qdisc *Fq) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *Fq) Type() string {
return "fq"
}
// FQ_Codel (Fair Queuing Controlled Delay) is queuing discipline that combines Fair Queuing with the CoDel AQM scheme.
type FqCodel struct {
QdiscAttrs
Target uint32
Limit uint32
Interval uint32
ECN uint32
Flows uint32
Quantum uint32
// There are some more attributes here, but support for them seems not ubiquitous
}
func (fqcodel *FqCodel) String() string {
return fmt.Sprintf(
"{%v -- Target: %v, Limit: %v, Interval: %v, ECM: %v, Flows: %v, Quantum: %v}",
fqcodel.Attrs(), fqcodel.Target, fqcodel.Limit, fqcodel.Interval, fqcodel.ECN, fqcodel.Flows, fqcodel.Quantum,
)
}
func NewFqCodel(attrs QdiscAttrs) *FqCodel {
return &FqCodel{
QdiscAttrs: attrs,
ECN: 1,
}
}
func (qdisc *FqCodel) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *FqCodel) Type() string {
return "fq_codel"
}

View File

@ -8,6 +8,7 @@ import (
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// NOTE function is here because it uses other linux functions
@ -84,7 +85,7 @@ func QdiscDel(qdisc Qdisc) error {
// QdiscDel will delete a qdisc from the system.
// Equivalent to: `tc qdisc del $qdisc`
func (h *Handle) QdiscDel(qdisc Qdisc) error {
return h.qdiscModify(syscall.RTM_DELQDISC, 0, qdisc)
return h.qdiscModify(unix.RTM_DELQDISC, 0, qdisc)
}
// QdiscChange will change a qdisc in place
@ -98,7 +99,7 @@ func QdiscChange(qdisc Qdisc) error {
// Equivalent to: `tc qdisc change $qdisc`
// The parent and handle MUST NOT be changed.
func (h *Handle) QdiscChange(qdisc Qdisc) error {
return h.qdiscModify(syscall.RTM_NEWQDISC, 0, qdisc)
return h.qdiscModify(unix.RTM_NEWQDISC, 0, qdisc)
}
// QdiscReplace will replace a qdisc to the system.
@ -113,8 +114,8 @@ func QdiscReplace(qdisc Qdisc) error {
// The handle MUST change.
func (h *Handle) QdiscReplace(qdisc Qdisc) error {
return h.qdiscModify(
syscall.RTM_NEWQDISC,
syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE,
unix.RTM_NEWQDISC,
unix.NLM_F_CREATE|unix.NLM_F_REPLACE,
qdisc)
}
@ -128,13 +129,13 @@ func QdiscAdd(qdisc Qdisc) error {
// Equivalent to: `tc qdisc add $qdisc`
func (h *Handle) QdiscAdd(qdisc Qdisc) error {
return h.qdiscModify(
syscall.RTM_NEWQDISC,
syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
unix.RTM_NEWQDISC,
unix.NLM_F_CREATE|unix.NLM_F_EXCL,
qdisc)
}
func (h *Handle) qdiscModify(cmd, flags int, qdisc Qdisc) error {
req := h.newNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
base := qdisc.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
@ -145,13 +146,13 @@ func (h *Handle) qdiscModify(cmd, flags int, qdisc Qdisc) error {
req.AddData(msg)
// When deleting don't bother building the rest of the netlink payload
if cmd != syscall.RTM_DELQDISC {
if cmd != unix.RTM_DELQDISC {
if err := qdiscPayload(req, qdisc); err != nil {
return err
}
}
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -160,78 +161,130 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if prio, ok := qdisc.(*Prio); ok {
switch qdisc := qdisc.(type) {
case *Prio:
tcmap := nl.TcPrioMap{
Bands: int32(prio.Bands),
Priomap: prio.PriorityMap,
Bands: int32(qdisc.Bands),
Priomap: qdisc.PriorityMap,
}
options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize())
} else if tbf, ok := qdisc.(*Tbf); ok {
case *Tbf:
opt := nl.TcTbfQopt{}
opt.Rate.Rate = uint32(tbf.Rate)
opt.Peakrate.Rate = uint32(tbf.Peakrate)
opt.Limit = tbf.Limit
opt.Buffer = tbf.Buffer
nl.NewRtAttrChild(options, nl.TCA_TBF_PARMS, opt.Serialize())
if tbf.Rate >= uint64(1<<32) {
nl.NewRtAttrChild(options, nl.TCA_TBF_RATE64, nl.Uint64Attr(tbf.Rate))
opt.Rate.Rate = uint32(qdisc.Rate)
opt.Peakrate.Rate = uint32(qdisc.Peakrate)
opt.Limit = qdisc.Limit
opt.Buffer = qdisc.Buffer
options.AddRtAttr(nl.TCA_TBF_PARMS, opt.Serialize())
if qdisc.Rate >= uint64(1<<32) {
options.AddRtAttr(nl.TCA_TBF_RATE64, nl.Uint64Attr(qdisc.Rate))
}
if tbf.Peakrate >= uint64(1<<32) {
nl.NewRtAttrChild(options, nl.TCA_TBF_PRATE64, nl.Uint64Attr(tbf.Peakrate))
if qdisc.Peakrate >= uint64(1<<32) {
options.AddRtAttr(nl.TCA_TBF_PRATE64, nl.Uint64Attr(qdisc.Peakrate))
}
if tbf.Peakrate > 0 {
nl.NewRtAttrChild(options, nl.TCA_TBF_PBURST, nl.Uint32Attr(tbf.Minburst))
if qdisc.Peakrate > 0 {
options.AddRtAttr(nl.TCA_TBF_PBURST, nl.Uint32Attr(qdisc.Minburst))
}
} else if htb, ok := qdisc.(*Htb); ok {
case *Htb:
opt := nl.TcHtbGlob{}
opt.Version = htb.Version
opt.Rate2Quantum = htb.Rate2Quantum
opt.Defcls = htb.Defcls
opt.Version = qdisc.Version
opt.Rate2Quantum = qdisc.Rate2Quantum
opt.Defcls = qdisc.Defcls
// TODO: Handle Debug properly. For now default to 0
opt.Debug = htb.Debug
opt.DirectPkts = htb.DirectPkts
nl.NewRtAttrChild(options, nl.TCA_HTB_INIT, opt.Serialize())
// nl.NewRtAttrChild(options, nl.TCA_HTB_DIRECT_QLEN, opt.Serialize())
} else if netem, ok := qdisc.(*Netem); ok {
opt.Debug = qdisc.Debug
opt.DirectPkts = qdisc.DirectPkts
options.AddRtAttr(nl.TCA_HTB_INIT, opt.Serialize())
// options.AddRtAttr(nl.TCA_HTB_DIRECT_QLEN, opt.Serialize())
case *Hfsc:
opt := nl.TcHfscOpt{}
opt.Defcls = qdisc.Defcls
options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
case *Netem:
opt := nl.TcNetemQopt{}
opt.Latency = netem.Latency
opt.Limit = netem.Limit
opt.Loss = netem.Loss
opt.Gap = netem.Gap
opt.Duplicate = netem.Duplicate
opt.Jitter = netem.Jitter
opt.Latency = qdisc.Latency
opt.Limit = qdisc.Limit
opt.Loss = qdisc.Loss
opt.Gap = qdisc.Gap
opt.Duplicate = qdisc.Duplicate
opt.Jitter = qdisc.Jitter
options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
// Correlation
corr := nl.TcNetemCorr{}
corr.DelayCorr = netem.DelayCorr
corr.LossCorr = netem.LossCorr
corr.DupCorr = netem.DuplicateCorr
corr.DelayCorr = qdisc.DelayCorr
corr.LossCorr = qdisc.LossCorr
corr.DupCorr = qdisc.DuplicateCorr
if corr.DelayCorr > 0 || corr.LossCorr > 0 || corr.DupCorr > 0 {
nl.NewRtAttrChild(options, nl.TCA_NETEM_CORR, corr.Serialize())
options.AddRtAttr(nl.TCA_NETEM_CORR, corr.Serialize())
}
// Corruption
corruption := nl.TcNetemCorrupt{}
corruption.Probability = netem.CorruptProb
corruption.Correlation = netem.CorruptCorr
corruption.Probability = qdisc.CorruptProb
corruption.Correlation = qdisc.CorruptCorr
if corruption.Probability > 0 {
nl.NewRtAttrChild(options, nl.TCA_NETEM_CORRUPT, corruption.Serialize())
options.AddRtAttr(nl.TCA_NETEM_CORRUPT, corruption.Serialize())
}
// Reorder
reorder := nl.TcNetemReorder{}
reorder.Probability = netem.ReorderProb
reorder.Correlation = netem.ReorderCorr
reorder.Probability = qdisc.ReorderProb
reorder.Correlation = qdisc.ReorderCorr
if reorder.Probability > 0 {
nl.NewRtAttrChild(options, nl.TCA_NETEM_REORDER, reorder.Serialize())
options.AddRtAttr(nl.TCA_NETEM_REORDER, reorder.Serialize())
}
} else if _, ok := qdisc.(*Ingress); ok {
case *Ingress:
// ingress filters must use the proper handle
if qdisc.Attrs().Parent != HANDLE_INGRESS {
return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS")
}
case *FqCodel:
options.AddRtAttr(nl.TCA_FQ_CODEL_ECN, nl.Uint32Attr((uint32(qdisc.ECN))))
if qdisc.Limit > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_LIMIT, nl.Uint32Attr((uint32(qdisc.Limit))))
}
if qdisc.Interval > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_INTERVAL, nl.Uint32Attr((uint32(qdisc.Interval))))
}
if qdisc.Flows > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_FLOWS, nl.Uint32Attr((uint32(qdisc.Flows))))
}
if qdisc.Quantum > 0 {
options.AddRtAttr(nl.TCA_FQ_CODEL_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum))))
}
case *Fq:
options.AddRtAttr(nl.TCA_FQ_RATE_ENABLE, nl.Uint32Attr((uint32(qdisc.Pacing))))
if qdisc.Buckets > 0 {
options.AddRtAttr(nl.TCA_FQ_BUCKETS_LOG, nl.Uint32Attr((uint32(qdisc.Buckets))))
}
if qdisc.LowRateThreshold > 0 {
options.AddRtAttr(nl.TCA_FQ_LOW_RATE_THRESHOLD, nl.Uint32Attr((uint32(qdisc.LowRateThreshold))))
}
if qdisc.Quantum > 0 {
options.AddRtAttr(nl.TCA_FQ_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum))))
}
if qdisc.InitialQuantum > 0 {
options.AddRtAttr(nl.TCA_FQ_INITIAL_QUANTUM, nl.Uint32Attr((uint32(qdisc.InitialQuantum))))
}
if qdisc.FlowRefillDelay > 0 {
options.AddRtAttr(nl.TCA_FQ_FLOW_REFILL_DELAY, nl.Uint32Attr((uint32(qdisc.FlowRefillDelay))))
}
if qdisc.FlowPacketLimit > 0 {
options.AddRtAttr(nl.TCA_FQ_FLOW_PLIMIT, nl.Uint32Attr((uint32(qdisc.FlowPacketLimit))))
}
if qdisc.FlowMaxRate > 0 {
options.AddRtAttr(nl.TCA_FQ_FLOW_MAX_RATE, nl.Uint32Attr((uint32(qdisc.FlowMaxRate))))
}
if qdisc.FlowDefaultRate > 0 {
options.AddRtAttr(nl.TCA_FQ_FLOW_DEFAULT_RATE, nl.Uint32Attr((uint32(qdisc.FlowDefaultRate))))
}
default:
options = nil
}
req.AddData(options)
if options != nil {
req.AddData(options)
}
return nil
}
@ -246,7 +299,7 @@ func QdiscList(link Link) ([]Qdisc, error) {
// Equivalent to: `tc qdisc show`.
// The list can be filtered by link.
func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
req := h.newNetlinkRequest(syscall.RTM_GETQDISC, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(unix.RTM_GETQDISC, unix.NLM_F_DUMP)
index := int32(0)
if link != nil {
base := link.Attrs()
@ -259,7 +312,7 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
}
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWQDISC)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC)
if err != nil {
return nil, err
}
@ -301,6 +354,12 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
qdisc = &Ingress{}
case "htb":
qdisc = &Htb{}
case "fq":
qdisc = &Fq{}
case "hfsc":
qdisc = &Hfsc{}
case "fq_codel":
qdisc = &FqCodel{}
case "netem":
qdisc = &Netem{}
default:
@ -326,6 +385,10 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
if err := parseTbfData(qdisc, data); err != nil {
return nil, err
}
case "hfsc":
if err := parseHfscData(qdisc, attr.Value); err != nil {
return nil, err
}
case "htb":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
@ -334,6 +397,22 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
if err := parseHtbData(qdisc, data); err != nil {
return nil, err
}
case "fq":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
if err := parseFqData(qdisc, data); err != nil {
return nil, err
}
case "fq_codel":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
if err := parseFqCodelData(qdisc, data); err != nil {
return nil, err
}
case "netem":
if err := parseNetemData(qdisc, attr.Value); err != nil {
return nil, err
@ -386,6 +465,68 @@ func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
return nil
}
func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
fqCodel := qdisc.(*FqCodel)
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_FQ_CODEL_TARGET:
fqCodel.Target = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_LIMIT:
fqCodel.Limit = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_INTERVAL:
fqCodel.Interval = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_ECN:
fqCodel.ECN = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_FLOWS:
fqCodel.Flows = native.Uint32(datum.Value)
case nl.TCA_FQ_CODEL_QUANTUM:
fqCodel.Quantum = native.Uint32(datum.Value)
}
}
return nil
}
func parseHfscData(qdisc Qdisc, data []byte) error {
Hfsc := qdisc.(*Hfsc)
native = nl.NativeEndian()
Hfsc.Defcls = native.Uint16(data)
return nil
}
func parseFqData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
fq := qdisc.(*Fq)
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_FQ_BUCKETS_LOG:
fq.Buckets = native.Uint32(datum.Value)
case nl.TCA_FQ_LOW_RATE_THRESHOLD:
fq.LowRateThreshold = native.Uint32(datum.Value)
case nl.TCA_FQ_QUANTUM:
fq.Quantum = native.Uint32(datum.Value)
case nl.TCA_FQ_RATE_ENABLE:
fq.Pacing = native.Uint32(datum.Value)
case nl.TCA_FQ_INITIAL_QUANTUM:
fq.InitialQuantum = native.Uint32(datum.Value)
case nl.TCA_FQ_ORPHAN_MASK:
// TODO
case nl.TCA_FQ_FLOW_REFILL_DELAY:
fq.FlowRefillDelay = native.Uint32(datum.Value)
case nl.TCA_FQ_FLOW_PLIMIT:
fq.FlowPacketLimit = native.Uint32(datum.Value)
case nl.TCA_FQ_PLIMIT:
fq.PacketLimit = native.Uint32(datum.Value)
case nl.TCA_FQ_FLOW_MAX_RATE:
fq.FlowMaxRate = native.Uint32(datum.Value)
case nl.TCA_FQ_FLOW_DEFAULT_RATE:
fq.FlowDefaultRate = native.Uint32(datum.Value)
}
}
return nil
}
func parseNetemData(qdisc Qdisc, value []byte) error {
netem := qdisc.(*Netem)
opt := nl.DeserializeTcNetemQopt(value)

View File

@ -0,0 +1,145 @@
package netlink
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// LinkAttrs represents data shared by most link types
type RdmaLinkAttrs struct {
Index uint32
Name string
FirmwareVersion string
NodeGuid string
SysImageGuid string
}
// Link represents a rdma device from netlink.
type RdmaLink struct {
Attrs RdmaLinkAttrs
}
func getProtoField(clientType int, op int) int {
return ((clientType << nl.RDMA_NL_GET_CLIENT_SHIFT) | op)
}
func uint64ToGuidString(guid uint64) string {
//Convert to byte array
sysGuidBytes := new(bytes.Buffer)
binary.Write(sysGuidBytes, binary.LittleEndian, guid)
//Convert to HardwareAddr
sysGuidNet := net.HardwareAddr(sysGuidBytes.Bytes())
//Get the String
return sysGuidNet.String()
}
func executeOneGetRdmaLink(data []byte) (*RdmaLink, error) {
link := RdmaLink{}
reader := bytes.NewReader(data)
for reader.Len() >= 4 {
_, attrType, len, value := parseNfAttrTLV(reader)
switch attrType {
case nl.RDMA_NLDEV_ATTR_DEV_INDEX:
var Index uint32
r := bytes.NewReader(value)
binary.Read(r, nl.NativeEndian(), &Index)
link.Attrs.Index = Index
case nl.RDMA_NLDEV_ATTR_DEV_NAME:
link.Attrs.Name = string(value[0 : len-1])
case nl.RDMA_NLDEV_ATTR_FW_VERSION:
link.Attrs.FirmwareVersion = string(value[0 : len-1])
case nl.RDMA_NLDEV_ATTR_NODE_GUID:
var guid uint64
r := bytes.NewReader(value)
binary.Read(r, nl.NativeEndian(), &guid)
link.Attrs.NodeGuid = uint64ToGuidString(guid)
case nl.RDMA_NLDEV_ATTR_SYS_IMAGE_GUID:
var sysGuid uint64
r := bytes.NewReader(value)
binary.Read(r, nl.NativeEndian(), &sysGuid)
link.Attrs.SysImageGuid = uint64ToGuidString(sysGuid)
}
if (len % 4) != 0 {
// Skip pad bytes
reader.Seek(int64(4-(len%4)), seekCurrent)
}
}
return &link, nil
}
func execRdmaGetLink(req *nl.NetlinkRequest, name string) (*RdmaLink, error) {
msgs, err := req.Execute(unix.NETLINK_RDMA, 0)
if err != nil {
return nil, err
}
for _, m := range msgs {
link, err := executeOneGetRdmaLink(m)
if err != nil {
return nil, err
}
if link.Attrs.Name == name {
return link, nil
}
}
return nil, fmt.Errorf("Rdma device %v not found", name)
}
func execRdmaSetLink(req *nl.NetlinkRequest) error {
_, err := req.Execute(unix.NETLINK_RDMA, 0)
return err
}
// RdmaLinkByName finds a link by name and returns a pointer to the object if
// found and nil error, otherwise returns error code.
func RdmaLinkByName(name string) (*RdmaLink, error) {
return pkgHandle.RdmaLinkByName(name)
}
// RdmaLinkByName finds a link by name and returns a pointer to the object if
// found and nil error, otherwise returns error code.
func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) {
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET)
req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP)
return execRdmaGetLink(req, name)
}
// RdmaLinkSetName sets the name of the rdma link device. Return nil on success
// or error otherwise.
// Equivalent to: `rdma dev set $old_devname name $name`
func RdmaLinkSetName(link *RdmaLink, name string) error {
return pkgHandle.RdmaLinkSetName(link, name)
}
// RdmaLinkSetName sets the name of the rdma link device. Return nil on success
// or error otherwise.
// Equivalent to: `rdma dev set $old_devname name $name`
func (h *Handle) RdmaLinkSetName(link *RdmaLink, name string) error {
proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET)
req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
b := make([]byte, 4)
native.PutUint32(b, uint32(link.Attrs.Index))
data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b)
req.AddData(data)
b = make([]byte, len(name)+1)
copy(b, name)
data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, b)
req.AddData(data)
return execRdmaSetLink(req)
}

View File

@ -16,6 +16,7 @@ type Destination interface {
Decode([]byte) error
Encode() ([]byte, error)
String() string
Equal(Destination) bool
}
type Encap interface {
@ -23,6 +24,7 @@ type Encap interface {
Decode([]byte) error
Encode() ([]byte, error)
String() string
Equal(Encap) bool
}
// Route represents a netlink route.
@ -43,6 +45,9 @@ type Route struct {
MPLSDst *int
NewDst Destination
Encap Encap
MTU int
AdvMSS int
Hoplimit int
}
func (r Route) String() string {
@ -72,6 +77,26 @@ func (r Route) String() string {
return fmt.Sprintf("{%s}", strings.Join(elems, " "))
}
func (r Route) Equal(x Route) bool {
return r.LinkIndex == x.LinkIndex &&
r.ILinkIndex == x.ILinkIndex &&
r.Scope == x.Scope &&
ipNetEqual(r.Dst, x.Dst) &&
r.Src.Equal(x.Src) &&
r.Gw.Equal(x.Gw) &&
nexthopInfoSlice(r.MultiPath).Equal(x.MultiPath) &&
r.Protocol == x.Protocol &&
r.Priority == x.Priority &&
r.Table == x.Table &&
r.Type == x.Type &&
r.Tos == x.Tos &&
r.Hoplimit == x.Hoplimit &&
r.Flags == x.Flags &&
(r.MPLSDst == x.MPLSDst || (r.MPLSDst != nil && x.MPLSDst != nil && *r.MPLSDst == *x.MPLSDst)) &&
(r.NewDst == x.NewDst || (r.NewDst != nil && r.NewDst.Equal(x.NewDst))) &&
(r.Encap == x.Encap || (r.Encap != nil && r.Encap.Equal(x.Encap)))
}
func (r *Route) SetFlag(flag NextHopFlag) {
r.Flags |= int(flag)
}
@ -110,7 +135,46 @@ func (n *NexthopInfo) String() string {
elems = append(elems, fmt.Sprintf("Encap: %s", n.Encap))
}
elems = append(elems, fmt.Sprintf("Weight: %d", n.Hops+1))
elems = append(elems, fmt.Sprintf("Gw: %d", n.Gw))
elems = append(elems, fmt.Sprintf("Gw: %s", n.Gw))
elems = append(elems, fmt.Sprintf("Flags: %s", n.ListFlags()))
return fmt.Sprintf("{%s}", strings.Join(elems, " "))
}
func (n NexthopInfo) Equal(x NexthopInfo) bool {
return n.LinkIndex == x.LinkIndex &&
n.Hops == x.Hops &&
n.Gw.Equal(x.Gw) &&
n.Flags == x.Flags &&
(n.NewDst == x.NewDst || (n.NewDst != nil && n.NewDst.Equal(x.NewDst))) &&
(n.Encap == x.Encap || (n.Encap != nil && n.Encap.Equal(x.Encap)))
}
type nexthopInfoSlice []*NexthopInfo
func (n nexthopInfoSlice) Equal(x []*NexthopInfo) bool {
if len(n) != len(x) {
return false
}
for i := range n {
if n[i] == nil || x[i] == nil {
return false
}
if !n[i].Equal(*x[i]) {
return false
}
}
return true
}
// ipNetEqual returns true iff both IPNet are equal
func ipNetEqual(ipn1 *net.IPNet, ipn2 *net.IPNet) bool {
if ipn1 == ipn2 {
return true
}
if ipn1 == nil || ipn2 == nil {
return false
}
m1, _ := ipn1.Mask.Size()
m2, _ := ipn2.Mask.Size()
return m1 == m2 && ipn1.IP.Equal(ipn2.IP)
}

View File

@ -8,16 +8,17 @@ import (
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
// RtAttr is shared so it is in netlink_linux.go
const (
SCOPE_UNIVERSE Scope = syscall.RT_SCOPE_UNIVERSE
SCOPE_SITE Scope = syscall.RT_SCOPE_SITE
SCOPE_LINK Scope = syscall.RT_SCOPE_LINK
SCOPE_HOST Scope = syscall.RT_SCOPE_HOST
SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE
SCOPE_UNIVERSE Scope = unix.RT_SCOPE_UNIVERSE
SCOPE_SITE Scope = unix.RT_SCOPE_SITE
SCOPE_LINK Scope = unix.RT_SCOPE_LINK
SCOPE_HOST Scope = unix.RT_SCOPE_HOST
SCOPE_NOWHERE Scope = unix.RT_SCOPE_NOWHERE
)
const (
@ -31,11 +32,12 @@ const (
RT_FILTER_SRC
RT_FILTER_GW
RT_FILTER_TABLE
RT_FILTER_HOPLIMIT
)
const (
FLAG_ONLINK NextHopFlag = syscall.RTNH_F_ONLINK
FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE
FLAG_ONLINK NextHopFlag = unix.RTNH_F_ONLINK
FLAG_PERVASIVE NextHopFlag = unix.RTNH_F_PERVASIVE
)
var testFlags = []flagString{
@ -86,6 +88,34 @@ func (d *MPLSDestination) String() string {
return strings.Join(s, "/")
}
func (d *MPLSDestination) Equal(x Destination) bool {
o, ok := x.(*MPLSDestination)
if !ok {
return false
}
if d == nil && o == nil {
return true
}
if d == nil || o == nil {
return false
}
if d.Labels == nil && o.Labels == nil {
return true
}
if d.Labels == nil || o.Labels == nil {
return false
}
if len(d.Labels) != len(o.Labels) {
return false
}
for i := range d.Labels {
if d.Labels[i] != o.Labels[i] {
return false
}
}
return true
}
type MPLSEncap struct {
Labels []int
}
@ -96,17 +126,17 @@ func (e *MPLSEncap) Type() int {
func (e *MPLSEncap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("Lack of bytes")
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
l := native.Uint16(buf)
if len(buf) < int(l) {
return fmt.Errorf("Lack of bytes")
return fmt.Errorf("lack of bytes")
}
buf = buf[:l]
typ := native.Uint16(buf[2:])
if typ != nl.MPLS_IPTUNNEL_DST {
return fmt.Errorf("Unknown MPLS Encap Type: %d", typ)
return fmt.Errorf("unknown MPLS Encap Type: %d", typ)
}
e.Labels = nl.DecodeMPLSStack(buf[4:])
return nil
@ -129,6 +159,290 @@ func (e *MPLSEncap) String() string {
return strings.Join(s, "/")
}
func (e *MPLSEncap) Equal(x Encap) bool {
o, ok := x.(*MPLSEncap)
if !ok {
return false
}
if e == nil && o == nil {
return true
}
if e == nil || o == nil {
return false
}
if e.Labels == nil && o.Labels == nil {
return true
}
if e.Labels == nil || o.Labels == nil {
return false
}
if len(e.Labels) != len(o.Labels) {
return false
}
for i := range e.Labels {
if e.Labels[i] != o.Labels[i] {
return false
}
}
return true
}
// SEG6 definitions
type SEG6Encap struct {
Mode int
Segments []net.IP
}
func (e *SEG6Encap) Type() int {
return nl.LWTUNNEL_ENCAP_SEG6
}
func (e *SEG6Encap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
// Get Length(l) & Type(typ) : 2 + 2 bytes
l := native.Uint16(buf)
if len(buf) < int(l) {
return fmt.Errorf("lack of bytes")
}
buf = buf[:l] // make sure buf size upper limit is Length
typ := native.Uint16(buf[2:])
// LWTUNNEL_ENCAP_SEG6 has only one attr type SEG6_IPTUNNEL_SRH
if typ != nl.SEG6_IPTUNNEL_SRH {
return fmt.Errorf("unknown SEG6 Type: %d", typ)
}
var err error
e.Mode, e.Segments, err = nl.DecodeSEG6Encap(buf[4:])
return err
}
func (e *SEG6Encap) Encode() ([]byte, error) {
s, err := nl.EncodeSEG6Encap(e.Mode, e.Segments)
native := nl.NativeEndian()
hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.SEG6_IPTUNNEL_SRH)
return append(hdr, s...), err
}
func (e *SEG6Encap) String() string {
segs := make([]string, 0, len(e.Segments))
// append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- {
segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1]))
}
str := fmt.Sprintf("mode %s segs %d [ %s ]", nl.SEG6EncapModeString(e.Mode),
len(e.Segments), strings.Join(segs, " "))
return str
}
func (e *SEG6Encap) Equal(x Encap) bool {
o, ok := x.(*SEG6Encap)
if !ok {
return false
}
if e == o {
return true
}
if e == nil || o == nil {
return false
}
if e.Mode != o.Mode {
return false
}
if len(e.Segments) != len(o.Segments) {
return false
}
for i := range e.Segments {
if !e.Segments[i].Equal(o.Segments[i]) {
return false
}
}
return true
}
// SEG6Local definitions
type SEG6LocalEncap struct {
Flags [nl.SEG6_LOCAL_MAX]bool
Action int
Segments []net.IP // from SRH in seg6_local_lwt
Table int // table id for End.T and End.DT6
InAddr net.IP
In6Addr net.IP
Iif int
Oif int
}
func (e *SEG6LocalEncap) Type() int {
return nl.LWTUNNEL_ENCAP_SEG6_LOCAL
}
func (e *SEG6LocalEncap) Decode(buf []byte) error {
attrs, err := nl.ParseRouteAttr(buf)
if err != nil {
return err
}
native := nl.NativeEndian()
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.SEG6_LOCAL_ACTION:
e.Action = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_ACTION] = true
case nl.SEG6_LOCAL_SRH:
e.Segments, err = nl.DecodeSEG6Srh(attr.Value[:])
e.Flags[nl.SEG6_LOCAL_SRH] = true
case nl.SEG6_LOCAL_TABLE:
e.Table = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_TABLE] = true
case nl.SEG6_LOCAL_NH4:
e.InAddr = net.IP(attr.Value[0:4])
e.Flags[nl.SEG6_LOCAL_NH4] = true
case nl.SEG6_LOCAL_NH6:
e.In6Addr = net.IP(attr.Value[0:16])
e.Flags[nl.SEG6_LOCAL_NH6] = true
case nl.SEG6_LOCAL_IIF:
e.Iif = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_IIF] = true
case nl.SEG6_LOCAL_OIF:
e.Oif = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_OIF] = true
}
}
return err
}
func (e *SEG6LocalEncap) Encode() ([]byte, error) {
var err error
native := nl.NativeEndian()
res := make([]byte, 8)
native.PutUint16(res, 8) // length
native.PutUint16(res[2:], nl.SEG6_LOCAL_ACTION)
native.PutUint32(res[4:], uint32(e.Action))
if e.Flags[nl.SEG6_LOCAL_SRH] {
srh, err := nl.EncodeSEG6Srh(e.Segments)
if err != nil {
return nil, err
}
attr := make([]byte, 4)
native.PutUint16(attr, uint16(len(srh)+4))
native.PutUint16(attr[2:], nl.SEG6_LOCAL_SRH)
attr = append(attr, srh...)
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_TABLE] {
attr := make([]byte, 8)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_TABLE)
native.PutUint32(attr[4:], uint32(e.Table))
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_NH4] {
attr := make([]byte, 4)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH4)
ipv4 := e.InAddr.To4()
if ipv4 == nil {
err = fmt.Errorf("SEG6_LOCAL_NH4 has invalid IPv4 address")
return nil, err
}
attr = append(attr, ipv4...)
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_NH6] {
attr := make([]byte, 4)
native.PutUint16(attr, 20)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH6)
attr = append(attr, e.In6Addr...)
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_IIF] {
attr := make([]byte, 8)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_IIF)
native.PutUint32(attr[4:], uint32(e.Iif))
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_OIF] {
attr := make([]byte, 8)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_OIF)
native.PutUint32(attr[4:], uint32(e.Oif))
res = append(res, attr...)
}
return res, err
}
func (e *SEG6LocalEncap) String() string {
strs := make([]string, 0, nl.SEG6_LOCAL_MAX)
strs = append(strs, fmt.Sprintf("action %s", nl.SEG6LocalActionString(e.Action)))
if e.Flags[nl.SEG6_LOCAL_TABLE] {
strs = append(strs, fmt.Sprintf("table %d", e.Table))
}
if e.Flags[nl.SEG6_LOCAL_NH4] {
strs = append(strs, fmt.Sprintf("nh4 %s", e.InAddr))
}
if e.Flags[nl.SEG6_LOCAL_NH6] {
strs = append(strs, fmt.Sprintf("nh6 %s", e.In6Addr))
}
if e.Flags[nl.SEG6_LOCAL_IIF] {
link, err := LinkByIndex(e.Iif)
if err != nil {
strs = append(strs, fmt.Sprintf("iif %d", e.Iif))
} else {
strs = append(strs, fmt.Sprintf("iif %s", link.Attrs().Name))
}
}
if e.Flags[nl.SEG6_LOCAL_OIF] {
link, err := LinkByIndex(e.Oif)
if err != nil {
strs = append(strs, fmt.Sprintf("oif %d", e.Oif))
} else {
strs = append(strs, fmt.Sprintf("oif %s", link.Attrs().Name))
}
}
if e.Flags[nl.SEG6_LOCAL_SRH] {
segs := make([]string, 0, len(e.Segments))
//append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- {
segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1]))
}
strs = append(strs, fmt.Sprintf("segs %d [ %s ]", len(e.Segments), strings.Join(segs, " ")))
}
return strings.Join(strs, " ")
}
func (e *SEG6LocalEncap) Equal(x Encap) bool {
o, ok := x.(*SEG6LocalEncap)
if !ok {
return false
}
if e == o {
return true
}
if e == nil || o == nil {
return false
}
// compare all arrays first
for i := range e.Flags {
if e.Flags[i] != o.Flags[i] {
return false
}
}
if len(e.Segments) != len(o.Segments) {
return false
}
for i := range e.Segments {
if !e.Segments[i].Equal(o.Segments[i]) {
return false
}
}
// compare values
if !e.InAddr.Equal(o.InAddr) || !e.In6Addr.Equal(o.In6Addr) {
return false
}
if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif {
return false
}
return true
}
// RouteAdd will add a route to the system.
// Equivalent to: `ip route add $route`
func RouteAdd(route *Route) error {
@ -138,8 +452,8 @@ func RouteAdd(route *Route) error {
// RouteAdd will add a route to the system.
// Equivalent to: `ip route add $route`
func (h *Handle) RouteAdd(route *Route) error {
flags := syscall.NLM_F_CREATE | syscall.NLM_F_EXCL | syscall.NLM_F_ACK
req := h.newNetlinkRequest(syscall.RTM_NEWROUTE, flags)
flags := unix.NLM_F_CREATE | unix.NLM_F_EXCL | unix.NLM_F_ACK
req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags)
return h.routeHandle(route, req, nl.NewRtMsg())
}
@ -152,8 +466,8 @@ func RouteReplace(route *Route) error {
// RouteReplace will add a route to the system.
// Equivalent to: `ip route replace $route`
func (h *Handle) RouteReplace(route *Route) error {
flags := syscall.NLM_F_CREATE | syscall.NLM_F_REPLACE | syscall.NLM_F_ACK
req := h.newNetlinkRequest(syscall.RTM_NEWROUTE, flags)
flags := unix.NLM_F_CREATE | unix.NLM_F_REPLACE | unix.NLM_F_ACK
req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags)
return h.routeHandle(route, req, nl.NewRtMsg())
}
@ -166,7 +480,7 @@ func RouteDel(route *Route) error {
// RouteDel will delete a route from the system.
// Equivalent to: `ip route del $route`
func (h *Handle) RouteDel(route *Route) error {
req := h.newNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_DELROUTE, unix.NLM_F_ACK)
return h.routeHandle(route, req, nl.NewRtDelMsg())
}
@ -189,12 +503,12 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
} else {
dstData = route.Dst.IP.To16()
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, dstData))
} else if route.MPLSDst != nil {
family = nl.FAMILY_MPLS
msg.Dst_len = uint8(20)
msg.Type = syscall.RTN_UNICAST
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, nl.EncodeMPLSStack(*route.MPLSDst)))
msg.Type = unix.RTN_UNICAST
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, nl.EncodeMPLSStack(*route.MPLSDst)))
}
if route.NewDst != nil {
@ -232,7 +546,7 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
srcData = route.Src.To16()
}
// The commonly used src ip for routes is actually PREFSRC
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PREFSRC, srcData))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PREFSRC, srcData))
}
if route.Gw != nil {
@ -247,14 +561,14 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
} else {
gwData = route.Gw.To16()
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_GATEWAY, gwData))
}
if len(route.MultiPath) > 0 {
buf := []byte{}
for _, nh := range route.MultiPath {
rtnh := &nl.RtNexthop{
RtNexthop: syscall.RtNexthop{
RtNexthop: unix.RtNexthop{
Hops: uint8(nh.Hops),
Ifindex: int32(nh.LinkIndex),
Flags: uint8(nh.Flags),
@ -267,9 +581,9 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
}
if gwFamily == FAMILY_V4 {
children = append(children, nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To4())))
children = append(children, nl.NewRtAttr(unix.RTA_GATEWAY, []byte(nh.Gw.To4())))
} else {
children = append(children, nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To16())))
children = append(children, nl.NewRtAttr(unix.RTA_GATEWAY, []byte(nh.Gw.To16())))
}
}
if nh.NewDst != nil {
@ -295,15 +609,15 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
rtnh.Children = children
buf = append(buf, rtnh.Serialize()...)
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_MULTIPATH, buf))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_MULTIPATH, buf))
}
if route.Table > 0 {
if route.Table >= 256 {
msg.Table = syscall.RT_TABLE_UNSPEC
msg.Table = unix.RT_TABLE_UNSPEC
b := make([]byte, 4)
native.PutUint32(b, uint32(route.Table))
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_TABLE, b))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_TABLE, b))
} else {
msg.Table = uint8(route.Table)
}
@ -312,7 +626,7 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
if route.Priority > 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(route.Priority))
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PRIORITY, b))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PRIORITY, b))
}
if route.Tos > 0 {
msg.Tos = uint8(route.Tos)
@ -324,6 +638,29 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
msg.Type = uint8(route.Type)
}
var metrics []*nl.RtAttr
// TODO: support other rta_metric values
if route.MTU > 0 {
b := nl.Uint32Attr(uint32(route.MTU))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_MTU, b))
}
if route.AdvMSS > 0 {
b := nl.Uint32Attr(uint32(route.AdvMSS))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_ADVMSS, b))
}
if route.Hoplimit > 0 {
b := nl.Uint32Attr(uint32(route.Hoplimit))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_HOPLIMIT, b))
}
if metrics != nil {
attr := nl.NewRtAttr(unix.RTA_METRICS, nil)
for _, metric := range metrics {
attr.AddChild(metric)
}
rtAttrs = append(rtAttrs, attr)
}
msg.Flags = uint32(route.Flags)
msg.Scope = uint8(route.Scope)
msg.Family = uint8(family)
@ -338,9 +675,9 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
)
native.PutUint32(b, uint32(route.LinkIndex))
req.AddData(nl.NewRtAttr(syscall.RTA_OIF, b))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -373,11 +710,11 @@ func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, e
// RouteListFiltered gets a list of routes in the system filtered with specified rules.
// All rules must be defined in RouteFilter struct
func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
req := h.newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(family)
req.AddData(infmsg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE)
if err != nil {
return nil, err
}
@ -385,11 +722,11 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
if msg.Flags&syscall.RTM_F_CLONED != 0 {
if msg.Flags&unix.RTM_F_CLONED != 0 {
// Ignore cloned routes
continue
}
if msg.Table != syscall.RT_TABLE_MAIN {
if msg.Table != unix.RT_TABLE_MAIN {
if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 {
// Ignore non-main tables
continue
@ -401,7 +738,7 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
}
if filter != nil {
switch {
case filterMask&RT_FILTER_TABLE != 0 && filter.Table != syscall.RT_TABLE_UNSPEC && route.Table != filter.Table:
case filterMask&RT_FILTER_TABLE != 0 && filter.Table != unix.RT_TABLE_UNSPEC && route.Table != filter.Table:
continue
case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
continue
@ -421,21 +758,12 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
continue
case filterMask&RT_FILTER_DST != 0:
if filter.MPLSDst == nil || route.MPLSDst == nil || (*filter.MPLSDst) != (*route.MPLSDst) {
if filter.Dst == nil {
if route.Dst != nil {
continue
}
} else {
if route.Dst == nil {
continue
}
aMaskLen, aMaskBits := route.Dst.Mask.Size()
bMaskLen, bMaskBits := filter.Dst.Mask.Size()
if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) {
continue
}
if !ipNetEqual(route.Dst, filter.Dst) {
continue
}
}
case filterMask&RT_FILTER_HOPLIMIT != 0 && route.Hoplimit != filter.Hoplimit:
continue
}
}
res = append(res, route)
@ -463,11 +791,11 @@ func deserializeRoute(m []byte) (Route, error) {
var encap, encapType syscall.NetlinkRouteAttr
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.RTA_GATEWAY:
case unix.RTA_GATEWAY:
route.Gw = net.IP(attr.Value)
case syscall.RTA_PREFSRC:
case unix.RTA_PREFSRC:
route.Src = net.IP(attr.Value)
case syscall.RTA_DST:
case unix.RTA_DST:
if msg.Family == nl.FAMILY_MPLS {
stack := nl.DecodeMPLSStack(attr.Value)
if len(stack) == 0 || len(stack) > 1 {
@ -480,36 +808,36 @@ func deserializeRoute(m []byte) (Route, error) {
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
}
}
case syscall.RTA_OIF:
case unix.RTA_OIF:
route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
case syscall.RTA_IIF:
case unix.RTA_IIF:
route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
case syscall.RTA_PRIORITY:
case unix.RTA_PRIORITY:
route.Priority = int(native.Uint32(attr.Value[0:4]))
case syscall.RTA_TABLE:
case unix.RTA_TABLE:
route.Table = int(native.Uint32(attr.Value[0:4]))
case syscall.RTA_MULTIPATH:
case unix.RTA_MULTIPATH:
parseRtNexthop := func(value []byte) (*NexthopInfo, []byte, error) {
if len(value) < syscall.SizeofRtNexthop {
return nil, nil, fmt.Errorf("Lack of bytes")
if len(value) < unix.SizeofRtNexthop {
return nil, nil, fmt.Errorf("lack of bytes")
}
nh := nl.DeserializeRtNexthop(value)
if len(value) < int(nh.RtNexthop.Len) {
return nil, nil, fmt.Errorf("Lack of bytes")
return nil, nil, fmt.Errorf("lack of bytes")
}
info := &NexthopInfo{
LinkIndex: int(nh.RtNexthop.Ifindex),
Hops: int(nh.RtNexthop.Hops),
Flags: int(nh.RtNexthop.Flags),
}
attrs, err := nl.ParseRouteAttr(value[syscall.SizeofRtNexthop:int(nh.RtNexthop.Len)])
attrs, err := nl.ParseRouteAttr(value[unix.SizeofRtNexthop:int(nh.RtNexthop.Len)])
if err != nil {
return nil, nil, err
}
var encap, encapType syscall.NetlinkRouteAttr
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.RTA_GATEWAY:
case unix.RTA_GATEWAY:
info.Gw = net.IP(attr.Value)
case nl.RTA_NEWDST:
var d Destination
@ -566,6 +894,21 @@ func deserializeRoute(m []byte) (Route, error) {
encapType = attr
case nl.RTA_ENCAP:
encap = attr
case unix.RTA_METRICS:
metrics, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return route, err
}
for _, metric := range metrics {
switch metric.Attr.Type {
case unix.RTAX_MTU:
route.MTU = int(native.Uint32(metric.Value[0:4]))
case unix.RTAX_ADVMSS:
route.AdvMSS = int(native.Uint32(metric.Value[0:4]))
case unix.RTAX_HOPLIMIT:
route.Hoplimit = int(native.Uint32(metric.Value[0:4]))
}
}
}
}
@ -578,6 +921,16 @@ func deserializeRoute(m []byte) (Route, error) {
if err := e.Decode(encap.Value); err != nil {
return route, err
}
case nl.LWTUNNEL_ENCAP_SEG6:
e = &SEG6Encap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
case nl.LWTUNNEL_ENCAP_SEG6_LOCAL:
e = &SEG6LocalEncap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
}
route.Encap = e
}
@ -594,7 +947,7 @@ func RouteGet(destination net.IP) ([]Route, error) {
// RouteGet gets a route to a specific destination from the host system.
// Equivalent to: 'ip route get'.
func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
req := h.newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST)
req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_REQUEST)
family := nl.GetIPFamily(destination)
var destinationData []byte
var bitlen uint8
@ -610,10 +963,10 @@ func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
msg.Dst_len = bitlen
req.AddData(msg)
rtaDst := nl.NewRtAttr(syscall.RTA_DST, destinationData)
rtaDst := nl.NewRtAttr(unix.RTA_DST, destinationData)
req.AddData(rtaDst)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE)
if err != nil {
return nil, err
}
@ -633,17 +986,36 @@ func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
// RouteSubscribe takes a chan down which notifications will be sent
// when routes are added or deleted. Close the 'done' chan to stop subscription.
func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error {
return routeSubscribeAt(netns.None(), netns.None(), ch, done)
return routeSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}
// RouteSubscribeAt works like RouteSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
return routeSubscribeAt(ns, netns.None(), ch, done)
return routeSubscribeAt(ns, netns.None(), ch, done, nil, false)
}
func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE)
// RouteSubscribeOptions contains a set of options to use with
// RouteSubscribeWithOptions.
type RouteSubscribeOptions struct {
Namespace *netns.NsHandle
ErrorCallback func(error)
ListExisting bool
}
// RouteSubscribeWithOptions work like RouteSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
func RouteSubscribeWithOptions(ch chan<- RouteUpdate, done <-chan struct{}, options RouteSubscribeOptions) error {
if options.Namespace == nil {
none := netns.None()
options.Namespace = &none
}
return routeSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}
func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_ROUTE, unix.RTNLGRP_IPV6_ROUTE)
if err != nil {
return err
}
@ -653,16 +1025,45 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
s.Close()
}()
}
if listExisting {
req := pkgHandle.newNetlinkRequest(unix.RTM_GETROUTE,
unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(infmsg)
if err := s.Send(req); err != nil {
return err
}
}
go func() {
defer close(ch)
for {
msgs, err := s.Receive()
if err != nil {
if cberr != nil {
cberr(err)
}
return
}
for _, m := range msgs {
if m.Header.Type == unix.NLMSG_DONE {
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
}
return
}
route, err := deserializeRoute(m.Data)
if err != nil {
if cberr != nil {
cberr(err)
}
return
}
ch <- RouteUpdate{Type: m.Header.Type, Route: route}

View File

@ -8,6 +8,7 @@ import (
// Rule represents a netlink rule.
type Rule struct {
Priority int
Family int
Table int
Mark int
Mask int
@ -20,6 +21,7 @@ type Rule struct {
OifName string
SuppressIfgroup int
SuppressPrefixlen int
Invert bool
}
func (r Rule) String() string {

View File

@ -3,11 +3,13 @@ package netlink
import (
"fmt"
"net"
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
const FibRuleInvert = 0x2
// RuleAdd adds a rule to the system.
// Equivalent to: ip rule add
func RuleAdd(rule *Rule) error {
@ -17,7 +19,7 @@ func RuleAdd(rule *Rule) error {
// RuleAdd adds a rule to the system.
// Equivalent to: ip rule add
func (h *Handle) RuleAdd(rule *Rule) error {
req := h.newNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_NEWRULE, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
return ruleHandle(rule, req)
}
@ -30,15 +32,31 @@ func RuleDel(rule *Rule) error {
// RuleDel deletes a rule from the system.
// Equivalent to: ip rule del
func (h *Handle) RuleDel(rule *Rule) error {
req := h.newNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(unix.RTM_DELRULE, unix.NLM_F_ACK)
return ruleHandle(rule, req)
}
func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
msg := nl.NewRtMsg()
msg.Family = syscall.AF_INET
var dstFamily uint8
msg.Family = unix.AF_INET
msg.Protocol = unix.RTPROT_BOOT
msg.Scope = unix.RT_SCOPE_UNIVERSE
msg.Table = unix.RT_TABLE_UNSPEC
msg.Type = unix.RTN_UNSPEC
if req.NlMsghdr.Flags&unix.NLM_F_CREATE > 0 {
msg.Type = unix.RTN_UNICAST
}
if rule.Invert {
msg.Flags |= FibRuleInvert
}
if rule.Family != 0 {
msg.Family = uint8(rule.Family)
}
if rule.Table >= 0 && rule.Table < 256 {
msg.Table = uint8(rule.Table)
}
var dstFamily uint8
var rtAttrs []*nl.RtAttr
if rule.Dst != nil && rule.Dst.IP != nil {
dstLen, _ := rule.Dst.Mask.Size()
@ -46,12 +64,12 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP))
dstFamily = msg.Family
var dstData []byte
if msg.Family == syscall.AF_INET {
if msg.Family == unix.AF_INET {
dstData = rule.Dst.IP.To4()
} else {
dstData = rule.Dst.IP.To16()
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, dstData))
}
if rule.Src != nil && rule.Src.IP != nil {
@ -62,19 +80,12 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
srcLen, _ := rule.Src.Mask.Size()
msg.Src_len = uint8(srcLen)
var srcData []byte
if msg.Family == syscall.AF_INET {
if msg.Family == unix.AF_INET {
srcData = rule.Src.IP.To4()
} else {
srcData = rule.Src.IP.To16()
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_SRC, srcData))
}
if rule.Table >= 0 {
msg.Table = uint8(rule.Table)
if rule.Table >= 256 {
msg.Table = syscall.RT_TABLE_UNSPEC
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_SRC, srcData))
}
req.AddData(msg)
@ -139,7 +150,7 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
}
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
@ -152,11 +163,11 @@ func RuleList(family int) ([]Rule, error) {
// RuleList lists rules in the system.
// Equivalent to: ip rule list
func (h *Handle) RuleList(family int) ([]Rule, error) {
req := h.newNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST)
req := h.newNetlinkRequest(unix.RTM_GETRULE, unix.NLM_F_DUMP|unix.NLM_F_REQUEST)
msg := nl.NewIfInfomsg(family)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWRULE)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWRULE)
if err != nil {
return nil, err
}
@ -172,9 +183,11 @@ func (h *Handle) RuleList(family int) ([]Rule, error) {
rule := NewRule()
rule.Invert = msg.Flags&FibRuleInvert > 0
for j := range attrs {
switch attrs[j].Attr.Type {
case syscall.RTA_TABLE:
case unix.RTA_TABLE:
rule.Table = int(native.Uint32(attrs[j].Value[0:4]))
case nl.FRA_SRC:
rule.Src = &net.IPNet{

View File

@ -4,9 +4,9 @@ import (
"errors"
"fmt"
"net"
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
const (
@ -123,15 +123,15 @@ func SocketGet(local, remote net.Addr) (*Socket, error) {
return nil, ErrNotImplemented
}
s, err := nl.Subscribe(syscall.NETLINK_INET_DIAG)
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
if err != nil {
return nil, err
}
defer s.Close()
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, 0)
req.AddData(&socketRequest{
Family: syscall.AF_INET,
Protocol: syscall.IPPROTO_TCP,
Family: unix.AF_INET,
Protocol: unix.IPPROTO_TCP,
ID: SocketID{
SourcePort: uint16(localTCP.Port),
DestinationPort: uint16(remoteTCP.Port),

View File

@ -2,19 +2,20 @@ package netlink
import (
"fmt"
"syscall"
"golang.org/x/sys/unix"
)
// Proto is an enum representing an ipsec protocol.
type Proto uint8
const (
XFRM_PROTO_ROUTE2 Proto = syscall.IPPROTO_ROUTING
XFRM_PROTO_ESP Proto = syscall.IPPROTO_ESP
XFRM_PROTO_AH Proto = syscall.IPPROTO_AH
XFRM_PROTO_HAO Proto = syscall.IPPROTO_DSTOPTS
XFRM_PROTO_ROUTE2 Proto = unix.IPPROTO_ROUTING
XFRM_PROTO_ESP Proto = unix.IPPROTO_ESP
XFRM_PROTO_AH Proto = unix.IPPROTO_AH
XFRM_PROTO_HAO Proto = unix.IPPROTO_DSTOPTS
XFRM_PROTO_COMP Proto = 0x6c // NOTE not defined on darwin
XFRM_PROTO_IPSEC_ANY Proto = syscall.IPPROTO_RAW
XFRM_PROTO_IPSEC_ANY Proto = unix.IPPROTO_RAW
)
func (p Proto) String() string {

View File

@ -2,11 +2,10 @@ package netlink
import (
"fmt"
"syscall"
"github.com/vishvananda/netns"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
type XfrmMsg interface {
@ -39,7 +38,7 @@ func XfrmMonitor(ch chan<- XfrmMsg, done <-chan struct{}, errorChan chan<- error
if err != nil {
return nil
}
s, err := nl.SubscribeAt(netns.None(), netns.None(), syscall.NETLINK_XFRM, groups...)
s, err := nl.SubscribeAt(netns.None(), netns.None(), unix.NETLINK_XFRM, groups...)
if err != nil {
return err
}

View File

@ -35,6 +35,25 @@ func (d Dir) String() string {
return fmt.Sprintf("socket %d", d-XFRM_SOCKET_IN)
}
// PolicyAction is an enum representing an ipsec policy action.
type PolicyAction uint8
const (
XFRM_POLICY_ALLOW PolicyAction = 0
XFRM_POLICY_BLOCK PolicyAction = 1
)
func (a PolicyAction) String() string {
switch a {
case XFRM_POLICY_ALLOW:
return "allow"
case XFRM_POLICY_BLOCK:
return "block"
default:
return fmt.Sprintf("action %d", a)
}
}
// XfrmPolicyTmpl encapsulates a rule for the base addresses of an ipsec
// policy. These rules are matched with XfrmState to determine encryption
// and authentication algorithms.
@ -64,11 +83,13 @@ type XfrmPolicy struct {
Dir Dir
Priority int
Index int
Action PolicyAction
Ifindex int
Mark *XfrmMark
Tmpls []XfrmPolicyTmpl
}
func (p XfrmPolicy) String() string {
return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, DstPort: %d, SrcPort: %d, Dir: %s, Priority: %d, Index: %d, Mark: %s, Tmpls: %s}",
p.Dst, p.Src, p.Proto, p.DstPort, p.SrcPort, p.Dir, p.Priority, p.Index, p.Mark, p.Tmpls)
return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, DstPort: %d, SrcPort: %d, Dir: %s, Priority: %d, Index: %d, Action: %s, Ifindex: %d, Mark: %s, Tmpls: %s}",
p.Dst, p.Src, p.Proto, p.DstPort, p.SrcPort, p.Dir, p.Priority, p.Index, p.Action, p.Ifindex, p.Mark, p.Tmpls)
}

View File

@ -1,9 +1,8 @@
package netlink
import (
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) {
@ -28,6 +27,7 @@ func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) {
if sel.Sport != 0 {
sel.SportMask = ^uint16(0)
}
sel.Ifindex = int32(policy.Ifindex)
}
// XfrmPolicyAdd will add an xfrm policy to the system.
@ -55,13 +55,14 @@ func (h *Handle) XfrmPolicyUpdate(policy *XfrmPolicy) error {
}
func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error {
req := h.newNetlinkRequest(nlProto, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(nlProto, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
msg := &nl.XfrmUserpolicyInfo{}
selFromPolicy(&msg.Sel, policy)
msg.Priority = uint32(policy.Priority)
msg.Index = uint32(policy.Index)
msg.Dir = uint8(policy.Dir)
msg.Action = uint8(policy.Action)
msg.Lft.SoftByteLimit = nl.XFRM_INF
msg.Lft.HardByteLimit = nl.XFRM_INF
msg.Lft.SoftPacketLimit = nl.XFRM_INF
@ -91,7 +92,7 @@ func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error {
req.AddData(out)
}
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
_, err := req.Execute(unix.NETLINK_XFRM, 0)
return err
}
@ -121,12 +122,12 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
// Equivalent to: `ip xfrm policy show`.
// The list can be filtered by ip family.
func (h *Handle) XfrmPolicyList(family int) ([]XfrmPolicy, error) {
req := h.newNetlinkRequest(nl.XFRM_MSG_GETPOLICY, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(nl.XFRM_MSG_GETPOLICY, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(family)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY)
msgs, err := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY)
if err != nil {
return nil, err
}
@ -165,13 +166,13 @@ func XfrmPolicyFlush() error {
// XfrmPolicyFlush will flush the policies on the system.
// Equivalent to: `ip xfrm policy flush`
func (h *Handle) XfrmPolicyFlush() error {
req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHPOLICY, syscall.NLM_F_ACK)
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHPOLICY, unix.NLM_F_ACK)
_, err := req.Execute(unix.NETLINK_XFRM, 0)
return err
}
func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPolicy, error) {
req := h.newNetlinkRequest(nlProto, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(nlProto, unix.NLM_F_ACK)
msg := &nl.XfrmUserpolicyId{}
selFromPolicy(&msg.Sel, policy)
@ -189,7 +190,7 @@ func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPo
resType = 0
}
msgs, err := req.Execute(syscall.NETLINK_XFRM, uint16(resType))
msgs, err := req.Execute(unix.NETLINK_XFRM, uint16(resType))
if err != nil {
return nil, err
}
@ -198,12 +199,7 @@ func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPo
return nil, err
}
p, err := parseXfrmPolicy(msgs[0], FAMILY_ALL)
if err != nil {
return nil, err
}
return p, nil
return parseXfrmPolicy(msgs[0], FAMILY_ALL)
}
func parseXfrmPolicy(m []byte, family int) (*XfrmPolicy, error) {
@ -221,9 +217,11 @@ func parseXfrmPolicy(m []byte, family int) (*XfrmPolicy, error) {
policy.Proto = Proto(msg.Sel.Proto)
policy.DstPort = int(nl.Swap16(msg.Sel.Dport))
policy.SrcPort = int(nl.Swap16(msg.Sel.Sport))
policy.Ifindex = int(msg.Sel.Ifindex)
policy.Priority = int(msg.Priority)
policy.Index = int(msg.Index)
policy.Dir = Dir(msg.Dir)
policy.Action = PolicyAction(msg.Action)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {

View File

@ -3,6 +3,7 @@ package netlink
import (
"fmt"
"net"
"time"
)
// XfrmStateAlgo represents the algorithm to use for the ipsec encryption.
@ -67,6 +68,19 @@ type XfrmStateLimits struct {
TimeUseHard uint64
}
// XfrmStateStats represents the current number of bytes/packets
// processed by this State, the State's installation and first use
// time and the replay window counters.
type XfrmStateStats struct {
ReplayWindow uint32
Replay uint32
Failed uint32
Bytes uint64
Packets uint64
AddTime uint64
UseTime uint64
}
// XfrmState represents the state of an ipsec policy. It optionally
// contains an XfrmStateAlgo for encryption and one for authentication.
type XfrmState struct {
@ -78,6 +92,7 @@ type XfrmState struct {
Reqid int
ReplayWindow int
Limits XfrmStateLimits
Statistics XfrmStateStats
Mark *XfrmMark
Auth *XfrmStateAlgo
Crypt *XfrmStateAlgo
@ -94,10 +109,16 @@ func (sa XfrmState) Print(stats bool) string {
if !stats {
return sa.String()
}
return fmt.Sprintf("%s, ByteSoft: %s, ByteHard: %s, PacketSoft: %s, PacketHard: %s, TimeSoft: %d, TimeHard: %d, TimeUseSoft: %d, TimeUseHard: %d",
at := time.Unix(int64(sa.Statistics.AddTime), 0).Format(time.UnixDate)
ut := "-"
if sa.Statistics.UseTime > 0 {
ut = time.Unix(int64(sa.Statistics.UseTime), 0).Format(time.UnixDate)
}
return fmt.Sprintf("%s, ByteSoft: %s, ByteHard: %s, PacketSoft: %s, PacketHard: %s, TimeSoft: %d, TimeHard: %d, TimeUseSoft: %d, TimeUseHard: %d, Bytes: %d, Packets: %d, "+
"AddTime: %s, UseTime: %s, ReplayWindow: %d, Replay: %d, Failed: %d",
sa.String(), printLimit(sa.Limits.ByteSoft), printLimit(sa.Limits.ByteHard), printLimit(sa.Limits.PacketSoft), printLimit(sa.Limits.PacketHard),
sa.Limits.TimeSoft, sa.Limits.TimeHard, sa.Limits.TimeUseSoft, sa.Limits.TimeUseHard)
sa.Limits.TimeSoft, sa.Limits.TimeHard, sa.Limits.TimeUseSoft, sa.Limits.TimeUseHard, sa.Statistics.Bytes, sa.Statistics.Packets, at, ut,
sa.Statistics.ReplayWindow, sa.Statistics.Replay, sa.Statistics.Failed)
}
func printLimit(lmt uint64) string {

View File

@ -2,10 +2,10 @@ package netlink
import (
"fmt"
"syscall"
"unsafe"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
func writeStateAlgo(a *XfrmStateAlgo) []byte {
@ -69,8 +69,10 @@ func writeReplayEsn(replayWindow int) []byte {
ReplayWindow: uint32(replayWindow),
}
// taken from iproute2/ip/xfrm_state.c:
replayEsn.BmpLen = uint32((replayWindow + (4 * 8) - 1) / (4 * 8))
// Linux stores the bitmap to identify the already received sequence packets in blocks of uint32 elements.
// Therefore bitmap length is the minimum number of uint32 elements needed. The following is a ceiling operation.
bytesPerElem := int(unsafe.Sizeof(replayEsn.BmpLen)) // Any uint32 variable is good for this
replayEsn.BmpLen = uint32((replayWindow + (bytesPerElem * 8) - 1) / (bytesPerElem * 8))
return replayEsn.Serialize()
}
@ -111,7 +113,7 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
if state.Spi == 0 {
return fmt.Errorf("Spi must be set when adding xfrm state.")
}
req := h.newNetlinkRequest(nlProto, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req := h.newNetlinkRequest(nlProto, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
msg := xfrmUsersaInfoFromXfrmState(state)
@ -157,13 +159,13 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
req.AddData(out)
}
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
_, err := req.Execute(unix.NETLINK_XFRM, 0)
return err
}
func (h *Handle) xfrmStateAllocSpi(state *XfrmState) (*XfrmState, error) {
req := h.newNetlinkRequest(nl.XFRM_MSG_ALLOCSPI,
syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
msg := &nl.XfrmUserSpiInfo{}
msg.XfrmUsersaInfo = *(xfrmUsersaInfoFromXfrmState(state))
@ -177,17 +179,12 @@ func (h *Handle) xfrmStateAllocSpi(state *XfrmState) (*XfrmState, error) {
req.AddData(out)
}
msgs, err := req.Execute(syscall.NETLINK_XFRM, 0)
msgs, err := req.Execute(unix.NETLINK_XFRM, 0)
if err != nil {
return nil, err
}
s, err := parseXfrmState(msgs[0], FAMILY_ALL)
if err != nil {
return nil, err
}
return s, err
return parseXfrmState(msgs[0], FAMILY_ALL)
}
// XfrmStateDel will delete an xfrm state from the system. Note that
@ -216,9 +213,9 @@ func XfrmStateList(family int) ([]XfrmState, error) {
// Equivalent to: `ip xfrm state show`.
// The list can be filtered by ip family.
func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) {
req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP)
req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, unix.NLM_F_DUMP)
msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
msgs, err := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
if err != nil {
return nil, err
}
@ -255,7 +252,7 @@ func (h *Handle) XfrmStateGet(state *XfrmState) (*XfrmState, error) {
}
func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState, error) {
req := h.newNetlinkRequest(nlProto, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(nlProto, unix.NLM_F_ACK)
msg := &nl.XfrmUsersaId{}
msg.Family = uint16(nl.GetIPFamily(state.Dst))
@ -278,7 +275,7 @@ func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState
resType = 0
}
msgs, err := req.Execute(syscall.NETLINK_XFRM, uint16(resType))
msgs, err := req.Execute(unix.NETLINK_XFRM, uint16(resType))
if err != nil {
return nil, err
}
@ -308,6 +305,7 @@ func xfrmStateFromXfrmUsersaInfo(msg *nl.XfrmUsersaInfo) *XfrmState {
state.Reqid = int(msg.Reqid)
state.ReplayWindow = int(msg.ReplayWindow)
lftToLimits(&msg.Lft, &state.Limits)
curToStats(&msg.Curlft, &msg.Stats, &state.Statistics)
return &state
}
@ -386,16 +384,12 @@ func XfrmStateFlush(proto Proto) error {
// proto = 0 means any transformation protocols
// Equivalent to: `ip xfrm state flush [ proto XFRM-PROTO ]`
func (h *Handle) XfrmStateFlush(proto Proto) error {
req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHSA, syscall.NLM_F_ACK)
req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHSA, unix.NLM_F_ACK)
req.AddData(&nl.XfrmUsersaFlush{Proto: uint8(proto)})
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
if err != nil {
return err
}
return nil
_, err := req.Execute(unix.NETLINK_XFRM, 0)
return err
}
func limitsToLft(lmts XfrmStateLimits, lft *nl.XfrmLifetimeCfg) {
@ -429,6 +423,16 @@ func lftToLimits(lft *nl.XfrmLifetimeCfg, lmts *XfrmStateLimits) {
*lmts = *(*XfrmStateLimits)(unsafe.Pointer(lft))
}
func curToStats(cur *nl.XfrmLifetimeCur, wstats *nl.XfrmStats, stats *XfrmStateStats) {
stats.Bytes = cur.Bytes
stats.Packets = cur.Packets
stats.AddTime = cur.AddTime
stats.UseTime = cur.UseTime
stats.ReplayWindow = wstats.ReplayWindow
stats.Replay = wstats.Replay
stats.Failed = wstats.IntegrityFailed
}
func xfrmUsersaInfoFromXfrmState(state *XfrmState) *nl.XfrmUsersaInfo {
msg := &nl.XfrmUsersaInfo{}
msg.Family = uint16(nl.GetIPFamily(state.Dst))

View File

@ -138,7 +138,9 @@ func getThisCgroup(cgroupType string) (string, error) {
return "", fmt.Errorf("docker pid not found in /var/run/docker.pid")
}
pid, err := strconv.Atoi(result[0])
if err != nil {
return "", err
}
output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid))
if err != nil {
return "", err
@ -186,6 +188,12 @@ func getPidForContainer(id string) (int, error) {
filepath.Join(cgroupRoot, "system.slice", "docker-"+id+".scope", "tasks"),
// Even more recent docker versions under cgroup/systemd/docker/<id>/
filepath.Join(cgroupRoot, "..", "systemd", "docker", id, "tasks"),
// Kubernetes with docker and CNI is even more different
filepath.Join(cgroupRoot, "..", "systemd", "kubepods", "*", "pod*", id, "tasks"),
// Another flavor of containers location in recent kubernetes 1.11+
filepath.Join(cgroupRoot, cgroupThis, "kubepods.slice", "kubepods-besteffort.slice", "*", "docker-"+id+".scope", "tasks"),
// When runs inside of a container with recent kubernetes 1.11+
filepath.Join(cgroupRoot, "kubepods.slice", "kubepods-besteffort.slice", "*", "docker-"+id+".scope", "tasks"),
}
var filename string