Merge pull request #679 from clearlinux/master

Update netlink library.
This commit is contained in:
aboch 2015-12-07 15:25:06 -08:00
commit 5359d01a51
30 changed files with 1989 additions and 1847 deletions

View File

@ -216,11 +216,11 @@
},
{
"ImportPath": "github.com/vishvananda/netlink",
"Rev": "4b5dce31de6d42af5bb9811c6d265472199e0fec"
"Rev": "8e810149a2e531fed9b837c0c7d8a8922d2bedf7"
},
{
"ImportPath": "github.com/vishvananda/netns",
"Rev": "493029407eeb434d0c2d44e02ea072ff2488d322"
"Rev": "604eaf189ee867d8c147fafc28def2394e878d25"
},
{
"ImportPath": "golang.org/x/net/context",

View File

@ -1,45 +0,0 @@
package netlink
import (
"testing"
)
func TestAddrAddDel(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
link, err := LinkByName("lo")
if err != nil {
t.Fatal(err)
}
addr, err := ParseAddr("127.1.1.1/24 local")
if err != nil {
t.Fatal(err)
}
if err = AddrAdd(link, addr); err != nil {
t.Fatal(err)
}
addrs, err := AddrList(link, FAMILY_ALL)
if err != nil {
t.Fatal(err)
}
if len(addrs) != 1 || !addr.Equal(addrs[0]) || addrs[0].Label != addr.Label {
t.Fatal("Address not added properly")
}
if err = AddrDel(link, addr); err != nil {
t.Fatal(err)
}
addrs, err = AddrList(link, FAMILY_ALL)
if err != nil {
t.Fatal(err)
}
if len(addrs) != 0 {
t.Fatal("Address not removed properly")
}
}

View File

@ -0,0 +1,110 @@
package netlink
import (
"fmt"
)
type Class interface {
Attrs() *ClassAttrs
Type() string
}
// Class 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
}
func (q ClassAttrs) String() string {
return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Leaf: %s}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Leaf)
}
type HtbClassAttrs struct {
// TODO handle all attributes
Rate uint64
Ceil uint64
Buffer uint32
Cbuffer uint32
Quantum uint32
Level uint32
Prio uint32
}
func (q HtbClassAttrs) String() string {
return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer)
}
// Htb class
type HtbClass struct {
ClassAttrs
Rate uint64
Ceil uint64
Buffer uint32
Cbuffer uint32
Quantum uint32
Level uint32
Prio uint32
}
func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
mtu := 1600
rate := cattrs.Rate / 8
ceil := cattrs.Ceil / 8
buffer := cattrs.Buffer
cbuffer := cattrs.Cbuffer
if ceil == 0 {
ceil = rate
}
if buffer == 0 {
buffer = uint32(float64(rate)/Hz() + float64(mtu))
}
buffer = uint32(Xmittime(rate, buffer))
if cbuffer == 0 {
cbuffer = uint32(float64(ceil)/Hz() + float64(mtu))
}
cbuffer = uint32(Xmittime(ceil, cbuffer))
return &HtbClass{
ClassAttrs: attrs,
Rate: rate,
Ceil: ceil,
Buffer: buffer,
Cbuffer: cbuffer,
Quantum: 10,
Level: 0,
Prio: 0,
}
}
func (q HtbClass) String() string {
return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer)
}
func (class *HtbClass) Attrs() *ClassAttrs {
return &class.ClassAttrs
}
func (class *HtbClass) Type() string {
return "htb"
}
// GenericClass classes represent types that are not currently understood
// by this netlink library.
type GenericClass struct {
ClassAttrs
ClassType string
}
func (class *GenericClass) Attrs() *ClassAttrs {
return &class.ClassAttrs
}
func (class *GenericClass) Type() string {
return class.ClassType
}

View File

@ -0,0 +1,144 @@
package netlink
import (
"syscall"
"github.com/vishvananda/netlink/nl"
)
// ClassDel will delete a class from the system.
// Equivalent to: `tc class del $class`
func ClassDel(class Class) error {
req := nl.NewNetlinkRequest(syscall.RTM_DELTCLASS, syscall.NLM_F_ACK)
base := class.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
}
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// ClassAdd will add a class to the system.
// Equivalent to: `tc class add $class`
func ClassAdd(class Class) error {
req := nl.NewNetlinkRequest(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
base := class.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
}
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if htb, ok := class.(*HtbClass); ok {
opt := nl.TcHtbCopt{}
opt.Rate.Rate = uint32(htb.Rate)
opt.Ceil.Rate = uint32(htb.Ceil)
opt.Buffer = htb.Buffer
opt.Cbuffer = htb.Cbuffer
opt.Quantum = htb.Quantum
opt.Level = htb.Level
opt.Prio = htb.Prio
// TODO: Handle Debug properly. For now default to 0
nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize())
}
req.AddData(options)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`.
// Generally retunrs nothing if link and parent are not specified.
func ClassList(link Link, parent uint32) ([]Class, error) {
req := nl.NewNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP)
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Parent: parent,
}
if link != nil {
base := link.Attrs()
ensureIndex(base)
msg.Ifindex = int32(base.Index)
}
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTCLASS)
if err != nil {
return nil, err
}
var res []Class
for _, m := range msgs {
msg := nl.DeserializeTcMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
base := ClassAttrs{
LinkIndex: int(msg.Ifindex),
Handle: msg.Handle,
Parent: msg.Parent,
}
var class Class
classType := ""
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.TCA_KIND:
classType = string(attr.Value[:len(attr.Value)-1])
switch classType {
case "htb":
class = &HtbClass{}
default:
class = &GenericClass{ClassType: classType}
}
case nl.TCA_OPTIONS:
switch classType {
case "htb":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
_, err = parseHtbClassData(class, data)
if err != nil {
return nil, err
}
}
}
}
*class.Attrs() = base
res = append(res, class)
}
return res, nil
}
func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
htb := class.(*HtbClass)
detailed := false
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_HTB_PARMS:
opt := nl.DeserializeTcHtbCopt(datum.Value)
htb.Rate = uint64(opt.Rate.Rate)
htb.Ceil = uint64(opt.Ceil.Rate)
htb.Buffer = opt.Buffer
htb.Cbuffer = opt.Cbuffer
htb.Quantum = opt.Quantum
htb.Level = opt.Level
htb.Prio = opt.Prio
}
}
return detailed, nil
}

View File

@ -0,0 +1,140 @@
package netlink
import (
"errors"
"fmt"
"github.com/vishvananda/netlink/nl"
)
type Filter interface {
Attrs() *FilterAttrs
Type() string
}
// Filter represents a netlink filter. 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 FilterAttrs struct {
LinkIndex int
Handle uint32
Parent uint32
Priority uint16 // lower is higher priority
Protocol uint16 // syscall.ETH_P_*
}
func (q FilterAttrs) String() string {
return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Priority: %d, Protocol: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Priority, q.Protocol)
}
// U32 filters on many packet related properties
type U32 struct {
FilterAttrs
// Currently only supports redirecting to another interface
RedirIndex int
}
func (filter *U32) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
func (filter *U32) Type() string {
return "u32"
}
type FilterFwAttrs struct {
ClassId uint32
InDev string
Mask uint32
Index uint32
Buffer uint32
Mtu uint32
Mpu uint16
Rate uint32
AvRate uint32
PeakRate uint32
Action int
Overhead uint16
LinkLayer int
}
// FwFilter filters on firewall marks
type Fw struct {
FilterAttrs
ClassId uint32
Police nl.TcPolice
InDev string
// TODO Action
Mask uint32
AvRate uint32
Rtab [256]uint32
Ptab [256]uint32
}
func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) {
var rtab [256]uint32
var ptab [256]uint32
rcell_log := -1
pcell_log := -1
avrate := fattrs.AvRate / 8
police := nl.TcPolice{}
police.Rate.Rate = fattrs.Rate / 8
police.PeakRate.Rate = fattrs.PeakRate / 8
buffer := fattrs.Buffer
linklayer := nl.LINKLAYER_ETHERNET
if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC {
linklayer = fattrs.LinkLayer
}
police.Action = int32(fattrs.Action)
if police.Rate.Rate != 0 {
police.Rate.Mpu = fattrs.Mpu
police.Rate.Overhead = fattrs.Overhead
if CalcRtable(&police.Rate, rtab, rcell_log, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("TBF: failed to calculate rate table.")
}
police.Burst = uint32(Xmittime(uint64(police.Rate.Rate), uint32(buffer)))
}
police.Mtu = fattrs.Mtu
if police.PeakRate.Rate != 0 {
police.PeakRate.Mpu = fattrs.Mpu
police.PeakRate.Overhead = fattrs.Overhead
if CalcRtable(&police.PeakRate, ptab, pcell_log, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("POLICE: failed to calculate peak rate table.")
}
}
return &Fw{
FilterAttrs: attrs,
ClassId: fattrs.ClassId,
InDev: fattrs.InDev,
Mask: fattrs.Mask,
Police: police,
AvRate: avrate,
Rtab: rtab,
Ptab: ptab,
}, nil
}
func (filter *Fw) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
func (filter *Fw) Type() string {
return "fw"
}
// GenericFilter filters represent types that are not currently understood
// by this netlink library.
type GenericFilter struct {
FilterAttrs
FilterType string
}
func (filter *GenericFilter) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
func (filter *GenericFilter) Type() string {
return filter.FilterType
}

View File

@ -0,0 +1,322 @@
package netlink
import (
"bytes"
"encoding/binary"
"fmt"
"syscall"
"github.com/vishvananda/netlink/nl"
)
// FilterDel will delete a filter from the system.
// Equivalent to: `tc filter del $filter`
func FilterDel(filter Filter) error {
req := nl.NewNetlinkRequest(syscall.RTM_DELTFILTER, syscall.NLM_F_ACK)
base := filter.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
}
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// FilterAdd will add a filter to the system.
// Equivalent to: `tc filter add $filter`
func FilterAdd(filter Filter) error {
native = nl.NativeEndian()
req := nl.NewNetlinkRequest(syscall.RTM_NEWTFILTER, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
base := filter.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
}
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if u32, ok := filter.(*U32); ok {
// match all
sel := nl.TcU32Sel{
Nkeys: 1,
Flags: nl.TC_U32_TERMINAL,
}
sel.Keys = append(sel.Keys, nl.TcU32Key{})
nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
actions := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil)
table := nl.NewRtAttrChild(actions, nl.TCA_ACT_TAB, nil)
nl.NewRtAttrChild(table, nl.TCA_KIND, nl.ZeroTerminated("mirred"))
// redirect to other interface
mir := nl.TcMirred{
Action: nl.TC_ACT_STOLEN,
Eaction: nl.TCA_EGRESS_REDIR,
Ifindex: uint32(u32.RedirIndex),
}
aopts := nl.NewRtAttrChild(table, nl.TCA_OPTIONS, nil)
nl.NewRtAttrChild(aopts, nl.TCA_MIRRED_PARMS, mir.Serialize())
} else if fw, ok := filter.(*Fw); ok {
if fw.Mask != 0 {
b := make([]byte, 4)
native.PutUint32(b, fw.Mask)
nl.NewRtAttrChild(options, nl.TCA_FW_MASK, b)
}
if fw.InDev != "" {
nl.NewRtAttrChild(options, nl.TCA_FW_INDEV, nl.ZeroTerminated(fw.InDev))
}
if (fw.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)
}
if (fw.Police.PeakRate != nl.TcRateSpec{}) {
payload := SerializeRtab(fw.Ptab)
nl.NewRtAttrChild(police, nl.TCA_POLICE_PEAKRATE, payload)
}
}
if fw.ClassId != 0 {
b := make([]byte, 4)
native.PutUint32(b, fw.ClassId)
nl.NewRtAttrChild(options, nl.TCA_FW_CLASSID, b)
}
}
req.AddData(options)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`.
// Generally retunrs nothing if link and parent are not specified.
func FilterList(link Link, parent uint32) ([]Filter, error) {
req := nl.NewNetlinkRequest(syscall.RTM_GETTFILTER, syscall.NLM_F_DUMP)
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Parent: parent,
}
if link != nil {
base := link.Attrs()
ensureIndex(base)
msg.Ifindex = int32(base.Index)
}
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTFILTER)
if err != nil {
return nil, err
}
var res []Filter
for _, m := range msgs {
msg := nl.DeserializeTcMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
base := FilterAttrs{
LinkIndex: int(msg.Ifindex),
Handle: msg.Handle,
Parent: msg.Parent,
}
base.Priority, base.Protocol = MajorMinor(msg.Info)
base.Protocol = nl.Swap16(base.Protocol)
var filter Filter
filterType := ""
detailed := false
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.TCA_KIND:
filterType = string(attr.Value[:len(attr.Value)-1])
switch filterType {
case "u32":
filter = &U32{}
case "fw":
filter = &Fw{}
default:
filter = &GenericFilter{FilterType: filterType}
}
case nl.TCA_OPTIONS:
switch filterType {
case "u32":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
detailed, err = parseU32Data(filter, data)
if err != nil {
return nil, err
}
case "fw":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
detailed, err = parseFwData(filter, data)
if err != nil {
return nil, err
}
}
}
}
// only return the detailed version of the filter
if detailed {
*filter.Attrs() = base
res = append(res, filter)
}
}
return res, nil
}
func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
u32 := filter.(*U32)
detailed := false
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_U32_SEL:
detailed = true
sel := nl.DeserializeTcU32Sel(datum.Value)
// only parse if we have a very basic redirect
if sel.Flags&nl.TC_U32_TERMINAL == 0 || sel.Nkeys != 1 {
return detailed, nil
}
case nl.TCA_U32_ACT:
table, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return detailed, err
}
if len(table) != 1 || table[0].Attr.Type != nl.TCA_ACT_TAB {
return detailed, fmt.Errorf("Action table not formed properly")
}
aattrs, err := nl.ParseRouteAttr(table[0].Value)
for _, aattr := range aattrs {
switch aattr.Attr.Type {
case nl.TCA_KIND:
actionType := string(aattr.Value[:len(aattr.Value)-1])
// only parse if the action is mirred
if actionType != "mirred" {
return detailed, nil
}
case nl.TCA_OPTIONS:
adata, err := nl.ParseRouteAttr(aattr.Value)
if err != nil {
return detailed, err
}
for _, adatum := range adata {
switch adatum.Attr.Type {
case nl.TCA_MIRRED_PARMS:
mir := nl.DeserializeTcMirred(adatum.Value)
u32.RedirIndex = int(mir.Ifindex)
}
}
}
}
}
}
return detailed, nil
}
func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
fw := filter.(*Fw)
detailed := true
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_FW_MASK:
fw.Mask = native.Uint32(datum.Value[0:4])
case nl.TCA_FW_CLASSID:
fw.ClassId = native.Uint32(datum.Value[0:4])
case nl.TCA_FW_INDEV:
fw.InDev = string(datum.Value[:len(datum.Value)-1])
case nl.TCA_FW_POLICE:
adata, _ := nl.ParseRouteAttr(datum.Value)
for _, aattr := range adata {
switch aattr.Attr.Type {
case nl.TCA_POLICE_TBF:
fw.Police = *nl.DeserializeTcPolice(aattr.Value)
case nl.TCA_POLICE_RATE:
fw.Rtab = DeserializeRtab(aattr.Value)
case nl.TCA_POLICE_PEAKRATE:
fw.Ptab = DeserializeRtab(aattr.Value)
}
}
}
}
return detailed, nil
}
func AlignToAtm(size uint) uint {
var linksize, cells int
cells = int(size / nl.ATM_CELL_PAYLOAD)
if (size % nl.ATM_CELL_PAYLOAD) > 0 {
cells++
}
linksize = cells * nl.ATM_CELL_SIZE
return uint(linksize)
}
func AdjustSize(sz uint, mpu uint, linklayer int) uint {
if sz < mpu {
sz = mpu
}
switch linklayer {
case nl.LINKLAYER_ATM:
return AlignToAtm(sz)
default:
return sz
}
}
func CalcRtable(rate *nl.TcRateSpec, rtab [256]uint32, cell_log int, mtu uint32, linklayer int) int {
bps := rate.Rate
mpu := rate.Mpu
var sz uint
if mtu == 0 {
mtu = 2047
}
if cell_log < 0 {
cell_log = 0
for (mtu >> uint(cell_log)) > 255 {
cell_log++
}
}
for i := 0; i < 256; i++ {
sz = AdjustSize(uint((i+1)<<uint32(cell_log)), uint(mpu), linklayer)
rtab[i] = uint32(Xmittime(uint64(bps), uint32(sz)))
}
rate.CellAlign = -1
rate.CellLog = uint8(cell_log)
rate.Linklayer = uint8(linklayer & nl.TC_LINKLAYER_MASK)
return cell_log
}
func DeserializeRtab(b []byte) [256]uint32 {
var rtab [256]uint32
native := nl.NativeEndian()
r := bytes.NewReader(b)
_ = binary.Read(r, native, &rtab)
return rtab
}
func SerializeRtab(rtab [256]uint32) []byte {
native := nl.NativeEndian()
var w bytes.Buffer
_ = binary.Write(&w, native, rtab)
return w.Bytes()
}

View File

@ -1,6 +1,9 @@
package netlink
import "net"
import (
"net"
"syscall"
)
// Link represents a link device from netlink. Shared link attributes
// like name may be retrieved using the Attrs() method. Unique data
@ -62,6 +65,19 @@ func (dummy *Dummy) Type() string {
return "dummy"
}
// Ifb links are advanced dummy devices for packet filtering
type Ifb struct {
LinkAttrs
}
func (ifb *Ifb) Attrs() *LinkAttrs {
return &ifb.LinkAttrs
}
func (ifb *Ifb) Type() string {
return "ifb"
}
// Bridge links are simple linux bridges
type Bridge struct {
LinkAttrs
@ -114,6 +130,36 @@ func (macvlan *Macvlan) Type() string {
return "macvlan"
}
// Macvtap - macvtap is a virtual interfaces based on macvlan
type Macvtap struct {
Macvlan
}
func (macvtap Macvtap) Type() string {
return "macvtap"
}
type TuntapMode uint16
const (
TUNTAP_MODE_TUN TuntapMode = syscall.IFF_TUN
TUNTAP_MODE_TAP TuntapMode = syscall.IFF_TAP
)
// Tuntap links created via /dev/tun/tap, but can be destroyed via netlink
type Tuntap struct {
LinkAttrs
Mode TuntapMode
}
func (tuntap *Tuntap) Attrs() *LinkAttrs {
return &tuntap.LinkAttrs
}
func (tuntap *Tuntap) Type() string {
return "tuntap"
}
// Veth devices must specify PeerName on create
type Veth struct {
LinkAttrs
@ -128,18 +174,18 @@ func (veth *Veth) Type() string {
return "veth"
}
// Generic links represent types that are not currently understood
// GenericLink links represent types that are not currently understood
// by this netlink library.
type Generic struct {
type GenericLink struct {
LinkAttrs
LinkType string
}
func (generic *Generic) Attrs() *LinkAttrs {
func (generic *GenericLink) Attrs() *LinkAttrs {
return &generic.LinkAttrs
}
func (generic *Generic) Type() string {
func (generic *GenericLink) Type() string {
return generic.LinkType
}

View File

@ -5,7 +5,9 @@ import (
"encoding/binary"
"fmt"
"net"
"os"
"syscall"
"unsafe"
"github.com/vishvananda/netlink/nl"
)
@ -285,6 +287,44 @@ func LinkAdd(link Link) error {
return fmt.Errorf("LinkAttrs.Name cannot be empty!")
}
if tuntap, ok := link.(*Tuntap); ok {
// TODO: support user
// TODO: support group
// TODO: support non- one_queue
// TODO: support pi | vnet_hdr | multi_queue
// TODO: support non- exclusive
// TODO: support non- persistent
if tuntap.Mode < syscall.IFF_TUN || tuntap.Mode > syscall.IFF_TAP {
return fmt.Errorf("Tuntap.Mode %v unknown!", tuntap.Mode)
}
file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
if err != nil {
return err
}
defer file.Close()
var req ifReq
req.Flags |= syscall.IFF_ONE_QUEUE
req.Flags |= syscall.IFF_TUN_EXCL
copy(req.Name[:15], base.Name)
req.Flags |= uint16(tuntap.Mode)
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&req)))
if errno != 0 {
return fmt.Errorf("Tuntap IOCTL TUNSETIFF failed, errno %v", errno)
}
_, _, errno = syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), uintptr(syscall.TUNSETPERSIST), 1)
if errno != 0 {
return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno)
}
ensureIndex(base)
// can't set master during create, so set it afterwards
if base.MasterIndex != 0 {
// TODO: verify MasterIndex is actually a bridge?
return LinkSetMasterByIndex(link, base.MasterIndex)
}
return nil
}
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
@ -493,6 +533,8 @@ func linkDeserialize(m []byte) (Link, error) {
switch linkType {
case "dummy":
link = &Dummy{}
case "ifb":
link = &Ifb{}
case "bridge":
link = &Bridge{}
case "vlan":
@ -505,8 +547,10 @@ func linkDeserialize(m []byte) (Link, error) {
link = &IPVlan{}
case "macvlan":
link = &Macvlan{}
case "macvtap":
link = &Macvtap{}
default:
link = &Generic{LinkType: linkType}
link = &GenericLink{LinkType: linkType}
}
case nl.IFLA_INFO_DATA:
data, err := nl.ParseRouteAttr(info.Value)
@ -522,6 +566,8 @@ func linkDeserialize(m []byte) (Link, error) {
parseIPVlanData(link, data)
case "macvlan":
parseMacvlanData(link, data)
case "macvtap":
parseMacvtapData(link, data)
}
}
}
@ -583,6 +629,46 @@ func LinkList() ([]Link, error) {
return res, nil
}
// LinkUpdate is used to pass information back from LinkSubscribe()
type LinkUpdate struct {
nl.IfInfomsg
Link
}
// LinkSubscribe takes a chan down which notifications will be sent
// when links change. Close the 'done' chan to stop subscription.
func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error {
s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
if err != nil {
return err
}
if done != nil {
go func() {
<-done
s.Close()
}()
}
go func() {
defer close(ch)
for {
msgs, err := s.Receive()
if err != nil {
return
}
for _, m := range msgs {
ifmsg := nl.DeserializeIfInfomsg(m.Data)
link, err := linkDeserialize(m.Data)
if err != nil {
return
}
ch <- LinkUpdate{IfInfomsg: *ifmsg, Link: link}
}
}
}()
return nil
}
func LinkSetHairpin(link Link, mode bool) error {
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE)
}
@ -696,6 +782,11 @@ func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
}
}
func parseMacvtapData(link Link, data []syscall.NetlinkRouteAttr) {
macv := link.(*Macvtap)
parseMacvlanData(&macv.Macvlan, data)
}
func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) {
macv := link.(*Macvlan)
for _, datum := range data {

View File

@ -1,643 +0,0 @@
package netlink
import (
"bytes"
"net"
"testing"
"github.com/vishvananda/netns"
)
const (
testTxQLen int = 100
defaultTxQLen int = 1000
)
func testLinkAddDel(t *testing.T, link Link) {
links, err := LinkList()
if err != nil {
t.Fatal(err)
}
num := len(links)
if err := LinkAdd(link); err != nil {
t.Fatal(err)
}
base := link.Attrs()
result, err := LinkByName(base.Name)
if err != nil {
t.Fatal(err)
}
rBase := result.Attrs()
if vlan, ok := link.(*Vlan); ok {
other, ok := result.(*Vlan)
if !ok {
t.Fatal("Result of create is not a vlan")
}
if vlan.VlanId != other.VlanId {
t.Fatal("Link.VlanId id doesn't match")
}
}
if rBase.ParentIndex == 0 && base.ParentIndex != 0 {
t.Fatal("Created link doesn't have a Parent but it should")
} else if rBase.ParentIndex != 0 && base.ParentIndex == 0 {
t.Fatal("Created link has a Parent but it shouldn't")
} else if rBase.ParentIndex != 0 && base.ParentIndex != 0 {
if rBase.ParentIndex != base.ParentIndex {
t.Fatal("Link.ParentIndex doesn't match")
}
}
if veth, ok := result.(*Veth); ok {
if rBase.TxQLen != base.TxQLen {
t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen)
}
if rBase.MTU != base.MTU {
t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU)
}
if veth.PeerName != "" {
var peer *Veth
other, err := LinkByName(veth.PeerName)
if err != nil {
t.Fatalf("Peer %s not created", veth.PeerName)
}
if peer, ok = other.(*Veth); !ok {
t.Fatalf("Peer %s is incorrect type", veth.PeerName)
}
if peer.TxQLen != testTxQLen {
t.Fatalf("TxQLen of peer is %d, should be %d", peer.TxQLen, testTxQLen)
}
}
}
if vxlan, ok := link.(*Vxlan); ok {
other, ok := result.(*Vxlan)
if !ok {
t.Fatal("Result of create is not a vxlan")
}
compareVxlan(t, vxlan, other)
}
if ipv, ok := link.(*IPVlan); ok {
other, ok := result.(*IPVlan)
if !ok {
t.Fatal("Result of create is not a ipvlan")
}
if ipv.Mode != other.Mode {
t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, ipv.Mode)
}
}
if macv, ok := link.(*Macvlan); ok {
other, ok := result.(*Macvlan)
if !ok {
t.Fatal("Result of create is not a macvlan")
}
if macv.Mode != other.Mode {
t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode)
}
}
if err = LinkDel(link); err != nil {
t.Fatal(err)
}
links, err = LinkList()
if err != nil {
t.Fatal(err)
}
if len(links) != num {
t.Fatal("Link not removed properly")
}
}
func compareVxlan(t *testing.T, expected, actual *Vxlan) {
if actual.VxlanId != expected.VxlanId {
t.Fatal("Vxlan.VxlanId doesn't match")
}
if expected.SrcAddr != nil && !actual.SrcAddr.Equal(expected.SrcAddr) {
t.Fatal("Vxlan.SrcAddr doesn't match")
}
if expected.Group != nil && !actual.Group.Equal(expected.Group) {
t.Fatal("Vxlan.Group doesn't match")
}
if expected.TTL != -1 && actual.TTL != expected.TTL {
t.Fatal("Vxlan.TTL doesn't match")
}
if expected.TOS != -1 && actual.TOS != expected.TOS {
t.Fatal("Vxlan.TOS doesn't match")
}
if actual.Learning != expected.Learning {
t.Fatal("Vxlan.Learning doesn't match")
}
if actual.Proxy != expected.Proxy {
t.Fatal("Vxlan.Proxy doesn't match")
}
if actual.RSC != expected.RSC {
t.Fatal("Vxlan.RSC doesn't match")
}
if actual.L2miss != expected.L2miss {
t.Fatal("Vxlan.L2miss doesn't match")
}
if actual.L3miss != expected.L3miss {
t.Fatal("Vxlan.L3miss doesn't match")
}
if actual.GBP != expected.GBP {
t.Fatal("Vxlan.GBP doesn't match")
}
if expected.NoAge {
if !actual.NoAge {
t.Fatal("Vxlan.NoAge doesn't match")
}
} else if expected.Age > 0 && actual.Age != expected.Age {
t.Fatal("Vxlan.Age doesn't match")
}
if expected.Limit > 0 && actual.Limit != expected.Limit {
t.Fatal("Vxlan.Limit doesn't match")
}
if expected.Port > 0 && actual.Port != expected.Port {
t.Fatal("Vxlan.Port doesn't match")
}
if expected.PortLow > 0 || expected.PortHigh > 0 {
if actual.PortLow != expected.PortLow {
t.Fatal("Vxlan.PortLow doesn't match")
}
if actual.PortHigh != expected.PortHigh {
t.Fatal("Vxlan.PortHigh doesn't match")
}
}
}
func TestLinkAddDelDummy(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo"}})
}
func TestLinkAddDelBridge(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
testLinkAddDel(t, &Bridge{LinkAttrs{Name: "foo", MTU: 1400}})
}
func TestLinkAddDelVlan(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
testLinkAddDel(t, &Vlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, 900})
if err := LinkDel(parent); err != nil {
t.Fatal(err)
}
}
func TestLinkAddDelMacvlan(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
testLinkAddDel(t, &Macvlan{
LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
Mode: MACVLAN_MODE_PRIVATE,
})
if err := LinkDel(parent); err != nil {
t.Fatal(err)
}
}
func TestLinkAddDelVeth(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"})
}
func TestLinkAddVethWithDefaultTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
if err := LinkAdd(veth); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if veth, ok := link.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != defaultTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
}
}
peer, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if veth, ok := peer.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != defaultTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
}
}
}
func TestLinkAddVethWithZeroTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
la.TxQLen = 0
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
if err := LinkAdd(veth); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if veth, ok := link.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != 0 {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
}
}
peer, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if veth, ok := peer.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != 0 {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
}
}
}
func TestLinkAddDummyWithTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
la.TxQLen = 1500
dummy := &Dummy{LinkAttrs: la}
if err := LinkAdd(dummy); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if dummy, ok := link.(*Dummy); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if dummy.TxQLen != 1500 {
t.Fatalf("TxQLen is %d, should be %d", dummy.TxQLen, 1500)
}
}
}
func TestLinkAddDelBridgeMaster(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
master := &Bridge{LinkAttrs{Name: "foo"}}
if err := LinkAdd(master); err != nil {
t.Fatal(err)
}
testLinkAddDel(t, &Dummy{LinkAttrs{Name: "bar", MasterIndex: master.Attrs().Index}})
if err := LinkDel(master); err != nil {
t.Fatal(err)
}
}
func TestLinkSetUnsetResetMaster(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
master := &Bridge{LinkAttrs{Name: "foo"}}
if err := LinkAdd(master); err != nil {
t.Fatal(err)
}
newmaster := &Bridge{LinkAttrs{Name: "bar"}}
if err := LinkAdd(newmaster); err != nil {
t.Fatal(err)
}
slave := &Dummy{LinkAttrs{Name: "baz"}}
if err := LinkAdd(slave); err != nil {
t.Fatal(err)
}
if err := LinkSetMaster(slave, master); err != nil {
t.Fatal(err)
}
link, err := LinkByName("baz")
if err != nil {
t.Fatal(err)
}
if link.Attrs().MasterIndex != master.Attrs().Index {
t.Fatal("Master not set properly")
}
if err := LinkSetMaster(slave, newmaster); err != nil {
t.Fatal(err)
}
link, err = LinkByName("baz")
if err != nil {
t.Fatal(err)
}
if link.Attrs().MasterIndex != newmaster.Attrs().Index {
t.Fatal("Master not reset properly")
}
if err := LinkSetMaster(slave, nil); err != nil {
t.Fatal(err)
}
link, err = LinkByName("baz")
if err != nil {
t.Fatal(err)
}
if link.Attrs().MasterIndex != 0 {
t.Fatal("Master not unset properly")
}
if err := LinkDel(slave); err != nil {
t.Fatal(err)
}
if err := LinkDel(newmaster); err != nil {
t.Fatal(err)
}
if err := LinkDel(master); err != nil {
t.Fatal(err)
}
}
func TestLinkSetNs(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
basens, err := netns.Get()
if err != nil {
t.Fatal("Failed to get basens")
}
defer basens.Close()
newns, err := netns.New()
if err != nil {
t.Fatal("Failed to create newns")
}
defer newns.Close()
link := &Veth{LinkAttrs{Name: "foo"}, "bar"}
if err := LinkAdd(link); err != nil {
t.Fatal(err)
}
peer, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
LinkSetNsFd(peer, int(basens))
if err != nil {
t.Fatal("Failed to set newns for link")
}
_, err = LinkByName("bar")
if err == nil {
t.Fatal("Link bar is still in newns")
}
err = netns.Set(basens)
if err != nil {
t.Fatal("Failed to set basens")
}
peer, err = LinkByName("bar")
if err != nil {
t.Fatal("Link is not in basens")
}
if err := LinkDel(peer); err != nil {
t.Fatal(err)
}
err = netns.Set(newns)
if err != nil {
t.Fatal("Failed to set newns")
}
_, err = LinkByName("foo")
if err == nil {
t.Fatal("Other half of veth pair not deleted")
}
}
func TestLinkAddDelVxlan(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{
LinkAttrs{Name: "foo"},
}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
vxlan := Vxlan{
LinkAttrs: LinkAttrs{
Name: "bar",
},
VxlanId: 10,
VtepDevIndex: parent.Index,
Learning: true,
L2miss: true,
L3miss: true,
}
testLinkAddDel(t, &vxlan)
if err := LinkDel(parent); err != nil {
t.Fatal(err)
}
}
func TestLinkAddDelIPVlanL2(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
ipv := IPVlan{
LinkAttrs: LinkAttrs{
Name: "bar",
ParentIndex: parent.Index,
},
Mode: IPVLAN_MODE_L2,
}
testLinkAddDel(t, &ipv)
}
func TestLinkAddDelIPVlanL3(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
ipv := IPVlan{
LinkAttrs: LinkAttrs{
Name: "bar",
ParentIndex: parent.Index,
},
Mode: IPVLAN_MODE_L3,
}
testLinkAddDel(t, &ipv)
}
func TestLinkAddDelIPVlanNoParent(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
ipv := IPVlan{
LinkAttrs: LinkAttrs{
Name: "bar",
},
Mode: IPVLAN_MODE_L3,
}
err := LinkAdd(&ipv)
if err == nil {
t.Fatal("Add should fail if ipvlan creating without ParentIndex")
}
if err.Error() != "Can't create ipvlan link without ParentIndex" {
t.Fatalf("Error should be about missing ParentIndex, got %q", err)
}
}
func TestLinkByIndex(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
dummy := &Dummy{LinkAttrs{Name: "dummy"}}
if err := LinkAdd(dummy); err != nil {
t.Fatal(err)
}
found, err := LinkByIndex(dummy.Index)
if err != nil {
t.Fatal(err)
}
if found.Attrs().Index != dummy.Attrs().Index {
t.Fatalf("Indices don't match: %v != %v", found.Attrs().Index, dummy.Attrs().Index)
}
LinkDel(dummy)
// test not found
_, err = LinkByIndex(dummy.Attrs().Index)
if err == nil {
t.Fatalf("LinkByIndex(%v) found deleted link", err)
}
}
func TestLinkSet(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
iface := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(iface); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
err = LinkSetName(link, "bar")
if err != nil {
t.Fatalf("Could not change interface name: %v", err)
}
link, err = LinkByName("bar")
if err != nil {
t.Fatalf("Interface name not changed: %v", err)
}
err = LinkSetMTU(link, 1400)
if err != nil {
t.Fatalf("Could not set MTU: %v", err)
}
link, err = LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if link.Attrs().MTU != 1400 {
t.Fatal("MTU not changed!")
}
addr, err := net.ParseMAC("00:12:34:56:78:AB")
if err != nil {
t.Fatal(err)
}
err = LinkSetHardwareAddr(link, addr)
if err != nil {
t.Fatal(err)
}
link, err = LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(link.Attrs().HardwareAddr, addr) {
t.Fatalf("hardware address not changed!")
}
}

View File

@ -0,0 +1,14 @@
package netlink
// ideally golang.org/x/sys/unix would define IfReq but it only has
// IFNAMSIZ, hence this minimalistic implementation
const (
SizeOfIfReq = 40
IFNAMSIZ = 16
)
type ifReq struct {
Name [IFNAMSIZ]byte
Flags uint16
pad [SizeOfIfReq - IFNAMSIZ - 2]byte
}

View File

@ -1,104 +0,0 @@
package netlink
import (
"net"
"testing"
)
type arpEntry struct {
ip net.IP
mac net.HardwareAddr
}
func parseMAC(s string) net.HardwareAddr {
m, err := net.ParseMAC(s)
if err != nil {
panic(err)
}
return m
}
func dumpContains(dump []Neigh, e arpEntry) bool {
for _, n := range dump {
if n.IP.Equal(e.ip) && (n.State&NUD_INCOMPLETE) == 0 {
return true
}
}
return false
}
func TestNeighAddDel(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
dummy := Dummy{LinkAttrs{Name: "neigh0"}}
if err := LinkAdd(&dummy); err != nil {
t.Fatal(err)
}
ensureIndex(dummy.Attrs())
arpTable := []arpEntry{
{net.ParseIP("10.99.0.1"), parseMAC("aa:bb:cc:dd:00:01")},
{net.ParseIP("10.99.0.2"), parseMAC("aa:bb:cc:dd:00:02")},
{net.ParseIP("10.99.0.3"), parseMAC("aa:bb:cc:dd:00:03")},
{net.ParseIP("10.99.0.4"), parseMAC("aa:bb:cc:dd:00:04")},
{net.ParseIP("10.99.0.5"), parseMAC("aa:bb:cc:dd:00:05")},
}
// Add the arpTable
for _, entry := range arpTable {
err := NeighAdd(&Neigh{
LinkIndex: dummy.Index,
State: NUD_REACHABLE,
IP: entry.ip,
HardwareAddr: entry.mac,
})
if err != nil {
t.Errorf("Failed to NeighAdd: %v", err)
}
}
// Dump and see that all added entries are there
dump, err := NeighList(dummy.Index, 0)
if err != nil {
t.Errorf("Failed to NeighList: %v", err)
}
for _, entry := range arpTable {
if !dumpContains(dump, entry) {
t.Errorf("Dump does not contain: %v", entry)
}
}
// Delete the arpTable
for _, entry := range arpTable {
err := NeighDel(&Neigh{
LinkIndex: dummy.Index,
IP: entry.ip,
HardwareAddr: entry.mac,
})
if err != nil {
t.Errorf("Failed to NeighDel: %v", err)
}
}
// TODO: seems not working because of cache
//// Dump and see that none of deleted entries are there
//dump, err = NeighList(dummy.Index, 0)
//if err != nil {
//t.Errorf("Failed to NeighList: %v", err)
//}
//for _, entry := range arpTable {
//if dumpContains(dump, entry) {
//t.Errorf("Dump contains: %v", entry)
//}
//}
if err := LinkDel(&dummy); err != nil {
t.Fatal(err)
}
}

View File

@ -1,34 +0,0 @@
package netlink
import (
"log"
"os"
"runtime"
"testing"
"github.com/vishvananda/netns"
)
type tearDownNetlinkTest func()
func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest {
if os.Getuid() != 0 {
msg := "Skipped test because it requires root privileges."
log.Printf(msg)
t.Skip(msg)
}
// new temporary namespace so we don't pollute the host
// lock thread since the namespace is thread local
runtime.LockOSThread()
var err error
ns, err := netns.New()
if err != nil {
t.Fatal("Failed to create newns", ns)
}
return func() {
ns.Close()
runtime.UnlockOSThread()
}
}

View File

@ -1,39 +0,0 @@
package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"syscall"
"testing"
)
func (msg *IfAddrmsg) write(b []byte) {
native := NativeEndian()
b[0] = msg.Family
b[1] = msg.Prefixlen
b[2] = msg.Flags
b[3] = msg.Scope
native.PutUint32(b[4:8], msg.Index)
}
func (msg *IfAddrmsg) serializeSafe() []byte {
len := syscall.SizeofIfAddrmsg
b := make([]byte, len)
msg.write(b)
return b
}
func deserializeIfAddrmsgSafe(b []byte) *IfAddrmsg {
var msg = IfAddrmsg{}
binary.Read(bytes.NewReader(b[0:syscall.SizeofIfAddrmsg]), NativeEndian(), &msg)
return &msg
}
func TestIfAddrmsgDeserializeSerialize(t *testing.T) {
var orig = make([]byte, syscall.SizeofIfAddrmsg)
rand.Read(orig)
safemsg := deserializeIfAddrmsgSafe(orig)
msg := DeserializeIfAddrmsg(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

View File

@ -142,7 +142,7 @@ func (a *RtAttr) Len() int {
}
// Serialize the RtAttr into a byte array
// This can't ust unsafe.cast because it must iterate through children.
// This can't just unsafe.cast because it must iterate through children.
func (a *RtAttr) Serialize() []byte {
native := NativeEndian()

View File

@ -1,60 +0,0 @@
package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"reflect"
"syscall"
"testing"
)
type testSerializer interface {
serializeSafe() []byte
Serialize() []byte
}
func testDeserializeSerialize(t *testing.T, orig []byte, safemsg testSerializer, msg testSerializer) {
if !reflect.DeepEqual(safemsg, msg) {
t.Fatal("Deserialization failed.\n", safemsg, "\n", msg)
}
safe := msg.serializeSafe()
if !bytes.Equal(safe, orig) {
t.Fatal("Safe serialization failed.\n", safe, "\n", orig)
}
b := msg.Serialize()
if !bytes.Equal(b, safe) {
t.Fatal("Serialization failed.\n", b, "\n", safe)
}
}
func (msg *IfInfomsg) write(b []byte) {
native := NativeEndian()
b[0] = msg.Family
b[1] = msg.X__ifi_pad
native.PutUint16(b[2:4], msg.Type)
native.PutUint32(b[4:8], uint32(msg.Index))
native.PutUint32(b[8:12], msg.Flags)
native.PutUint32(b[12:16], msg.Change)
}
func (msg *IfInfomsg) serializeSafe() []byte {
length := syscall.SizeofIfInfomsg
b := make([]byte, length)
msg.write(b)
return b
}
func deserializeIfInfomsgSafe(b []byte) *IfInfomsg {
var msg = IfInfomsg{}
binary.Read(bytes.NewReader(b[0:syscall.SizeofIfInfomsg]), NativeEndian(), &msg)
return &msg
}
func TestIfInfomsgDeserializeSerialize(t *testing.T) {
var orig = make([]byte, syscall.SizeofIfInfomsg)
rand.Read(orig)
safemsg := deserializeIfInfomsgSafe(orig)
msg := DeserializeIfInfomsg(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

View File

@ -1,43 +0,0 @@
package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"syscall"
"testing"
)
func (msg *RtMsg) write(b []byte) {
native := NativeEndian()
b[0] = msg.Family
b[1] = msg.Dst_len
b[2] = msg.Src_len
b[3] = msg.Tos
b[4] = msg.Table
b[5] = msg.Protocol
b[6] = msg.Scope
b[7] = msg.Type
native.PutUint32(b[8:12], msg.Flags)
}
func (msg *RtMsg) serializeSafe() []byte {
len := syscall.SizeofRtMsg
b := make([]byte, len)
msg.write(b)
return b
}
func deserializeRtMsgSafe(b []byte) *RtMsg {
var msg = RtMsg{}
binary.Read(bytes.NewReader(b[0:syscall.SizeofRtMsg]), NativeEndian(), &msg)
return &msg
}
func TestRtMsgDeserializeSerialize(t *testing.T) {
var orig = make([]byte, syscall.SizeofRtMsg)
rand.Read(orig)
safemsg := deserializeRtMsgSafe(orig)
msg := DeserializeRtMsg(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

View File

@ -0,0 +1,508 @@
package nl
import (
"unsafe"
)
// LinkLayer
const (
LINKLAYER_UNSPEC = iota
LINKLAYER_ETHERNET
LINKLAYER_ATM
)
// ATM
const (
ATM_CELL_PAYLOAD = 48
ATM_CELL_SIZE = 53
)
const TC_LINKLAYER_MASK = 0x0F
// Police
const (
TCA_POLICE_UNSPEC = iota
TCA_POLICE_TBF
TCA_POLICE_RATE
TCA_POLICE_PEAKRATE
TCA_POLICE_AVRATE
TCA_POLICE_RESULT
TCA_POLICE_MAX = TCA_POLICE_RESULT
)
// Message types
const (
TCA_UNSPEC = iota
TCA_KIND
TCA_OPTIONS
TCA_STATS
TCA_XSTATS
TCA_RATE
TCA_FCNT
TCA_STATS2
TCA_STAB
TCA_MAX = TCA_STAB
)
const (
TCA_ACT_TAB = 1
TCAA_MAX = 1
)
const (
TCA_PRIO_UNSPEC = iota
TCA_PRIO_MQ
TCA_PRIO_MAX = TCA_PRIO_MQ
)
const (
SizeofTcMsg = 0x14
SizeofTcActionMsg = 0x04
SizeofTcPrioMap = 0x14
SizeofTcRateSpec = 0x0c
SizeofTcTbfQopt = 2*SizeofTcRateSpec + 0x0c
SizeofTcHtbCopt = 2*SizeofTcRateSpec + 0x14
SizeofTcHtbGlob = 0x14
SizeofTcU32Key = 0x10
SizeofTcU32Sel = 0x10 // without keys
SizeofTcMirred = 0x1c
SizeofTcPolice = 2*SizeofTcRateSpec + 0x20
)
// struct tcmsg {
// unsigned char tcm_family;
// unsigned char tcm__pad1;
// unsigned short tcm__pad2;
// int tcm_ifindex;
// __u32 tcm_handle;
// __u32 tcm_parent;
// __u32 tcm_info;
// };
type TcMsg struct {
Family uint8
Pad [3]byte
Ifindex int32
Handle uint32
Parent uint32
Info uint32
}
func (msg *TcMsg) Len() int {
return SizeofTcMsg
}
func DeserializeTcMsg(b []byte) *TcMsg {
return (*TcMsg)(unsafe.Pointer(&b[0:SizeofTcMsg][0]))
}
func (x *TcMsg) Serialize() []byte {
return (*(*[SizeofTcMsg]byte)(unsafe.Pointer(x)))[:]
}
// struct tcamsg {
// unsigned char tca_family;
// unsigned char tca__pad1;
// unsigned short tca__pad2;
// };
type TcActionMsg struct {
Family uint8
Pad [3]byte
}
func (msg *TcActionMsg) Len() int {
return SizeofTcActionMsg
}
func DeserializeTcActionMsg(b []byte) *TcActionMsg {
return (*TcActionMsg)(unsafe.Pointer(&b[0:SizeofTcActionMsg][0]))
}
func (x *TcActionMsg) Serialize() []byte {
return (*(*[SizeofTcActionMsg]byte)(unsafe.Pointer(x)))[:]
}
const (
TC_PRIO_MAX = 15
)
// struct tc_prio_qopt {
// int bands; /* Number of bands */
// __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
// };
type TcPrioMap struct {
Bands int32
Priomap [TC_PRIO_MAX + 1]uint8
}
func (msg *TcPrioMap) Len() int {
return SizeofTcPrioMap
}
func DeserializeTcPrioMap(b []byte) *TcPrioMap {
return (*TcPrioMap)(unsafe.Pointer(&b[0:SizeofTcPrioMap][0]))
}
func (x *TcPrioMap) Serialize() []byte {
return (*(*[SizeofTcPrioMap]byte)(unsafe.Pointer(x)))[:]
}
const (
TCA_TBF_UNSPEC = iota
TCA_TBF_PARMS
TCA_TBF_RTAB
TCA_TBF_PTAB
TCA_TBF_RATE64
TCA_TBF_PRATE64
TCA_TBF_BURST
TCA_TBF_PBURST
TCA_TBF_MAX = TCA_TBF_PBURST
)
// struct tc_ratespec {
// unsigned char cell_log;
// __u8 linklayer; /* lower 4 bits */
// unsigned short overhead;
// short cell_align;
// unsigned short mpu;
// __u32 rate;
// };
type TcRateSpec struct {
CellLog uint8
Linklayer uint8
Overhead uint16
CellAlign int16
Mpu uint16
Rate uint32
}
func (msg *TcRateSpec) Len() int {
return SizeofTcRateSpec
}
func DeserializeTcRateSpec(b []byte) *TcRateSpec {
return (*TcRateSpec)(unsafe.Pointer(&b[0:SizeofTcRateSpec][0]))
}
func (x *TcRateSpec) Serialize() []byte {
return (*(*[SizeofTcRateSpec]byte)(unsafe.Pointer(x)))[:]
}
// struct tc_tbf_qopt {
// struct tc_ratespec rate;
// struct tc_ratespec peakrate;
// __u32 limit;
// __u32 buffer;
// __u32 mtu;
// };
type TcTbfQopt struct {
Rate TcRateSpec
Peakrate TcRateSpec
Limit uint32
Buffer uint32
Mtu uint32
}
func (msg *TcTbfQopt) Len() int {
return SizeofTcTbfQopt
}
func DeserializeTcTbfQopt(b []byte) *TcTbfQopt {
return (*TcTbfQopt)(unsafe.Pointer(&b[0:SizeofTcTbfQopt][0]))
}
func (x *TcTbfQopt) Serialize() []byte {
return (*(*[SizeofTcTbfQopt]byte)(unsafe.Pointer(x)))[:]
}
const (
TCA_HTB_UNSPEC = iota
TCA_HTB_PARMS
TCA_HTB_INIT
TCA_HTB_CTAB
TCA_HTB_RTAB
TCA_HTB_DIRECT_QLEN
TCA_HTB_RATE64
TCA_HTB_CEIL64
TCA_HTB_MAX = TCA_HTB_CEIL64
)
//struct tc_htb_opt {
// struct tc_ratespec rate;
// struct tc_ratespec ceil;
// __u32 buffer;
// __u32 cbuffer;
// __u32 quantum;
// __u32 level; /* out only */
// __u32 prio;
//};
type TcHtbCopt struct {
Rate TcRateSpec
Ceil TcRateSpec
Buffer uint32
Cbuffer uint32
Quantum uint32
Level uint32
Prio uint32
}
func (msg *TcHtbCopt) Len() int {
return SizeofTcHtbCopt
}
func DeserializeTcHtbCopt(b []byte) *TcHtbCopt {
return (*TcHtbCopt)(unsafe.Pointer(&b[0:SizeofTcHtbCopt][0]))
}
func (x *TcHtbCopt) Serialize() []byte {
return (*(*[SizeofTcHtbCopt]byte)(unsafe.Pointer(x)))[:]
}
type TcHtbGlob struct {
Version uint32
Rate2Quantum uint32
Defcls uint32
Debug uint32
DirectPkts uint32
}
func (msg *TcHtbGlob) Len() int {
return SizeofTcHtbGlob
}
func DeserializeTcHtbGlob(b []byte) *TcHtbGlob {
return (*TcHtbGlob)(unsafe.Pointer(&b[0:SizeofTcHtbGlob][0]))
}
func (x *TcHtbGlob) Serialize() []byte {
return (*(*[SizeofTcHtbGlob]byte)(unsafe.Pointer(x)))[:]
}
const (
TCA_U32_UNSPEC = iota
TCA_U32_CLASSID
TCA_U32_HASH
TCA_U32_LINK
TCA_U32_DIVISOR
TCA_U32_SEL
TCA_U32_POLICE
TCA_U32_ACT
TCA_U32_INDEV
TCA_U32_PCNT
TCA_U32_MARK
TCA_U32_MAX = TCA_U32_MARK
)
// struct tc_u32_key {
// __be32 mask;
// __be32 val;
// int off;
// int offmask;
// };
type TcU32Key struct {
Mask uint32 // big endian
Val uint32 // big endian
Off int32
OffMask int32
}
func (msg *TcU32Key) Len() int {
return SizeofTcU32Key
}
func DeserializeTcU32Key(b []byte) *TcU32Key {
return (*TcU32Key)(unsafe.Pointer(&b[0:SizeofTcU32Key][0]))
}
func (x *TcU32Key) Serialize() []byte {
return (*(*[SizeofTcU32Key]byte)(unsafe.Pointer(x)))[:]
}
// struct tc_u32_sel {
// unsigned char flags;
// unsigned char offshift;
// unsigned char nkeys;
//
// __be16 offmask;
// __u16 off;
// short offoff;
//
// short hoff;
// __be32 hmask;
// struct tc_u32_key keys[0];
// };
const (
TC_U32_TERMINAL = 1 << iota
TC_U32_OFFSET = 1 << iota
TC_U32_VAROFFSET = 1 << iota
TC_U32_EAT = 1 << iota
)
type TcU32Sel struct {
Flags uint8
Offshift uint8
Nkeys uint8
Pad uint8
Offmask uint16 // big endian
Off uint16
Offoff int16
Hoff int16
Hmask uint32 // big endian
Keys []TcU32Key
}
func (msg *TcU32Sel) Len() int {
return SizeofTcU32Sel + int(msg.Nkeys)*SizeofTcU32Key
}
func DeserializeTcU32Sel(b []byte) *TcU32Sel {
x := &TcU32Sel{}
copy((*(*[SizeofTcU32Sel]byte)(unsafe.Pointer(x)))[:], b)
next := SizeofTcU32Sel
var i uint8
for i = 0; i < x.Nkeys; i++ {
x.Keys = append(x.Keys, *DeserializeTcU32Key(b[next:]))
next += SizeofTcU32Key
}
return x
}
func (x *TcU32Sel) Serialize() []byte {
// This can't just unsafe.cast because it must iterate through keys.
buf := make([]byte, x.Len())
copy(buf, (*(*[SizeofTcU32Sel]byte)(unsafe.Pointer(x)))[:])
next := SizeofTcU32Sel
for _, key := range x.Keys {
keyBuf := key.Serialize()
copy(buf[next:], keyBuf)
next += SizeofTcU32Key
}
return buf
}
const (
TCA_ACT_MIRRED = 8
)
const (
TCA_MIRRED_UNSPEC = iota
TCA_MIRRED_TM
TCA_MIRRED_PARMS
TCA_MIRRED_MAX = TCA_MIRRED_PARMS
)
const (
TCA_EGRESS_REDIR = 1 /* packet redirect to EGRESS*/
TCA_EGRESS_MIRROR = 2 /* mirror packet to EGRESS */
TCA_INGRESS_REDIR = 3 /* packet redirect to INGRESS*/
TCA_INGRESS_MIRROR = 4 /* mirror packet to INGRESS */
)
const (
TC_ACT_UNSPEC = int32(-1)
TC_ACT_OK = 0
TC_ACT_RECLASSIFY = 1
TC_ACT_SHOT = 2
TC_ACT_PIPE = 3
TC_ACT_STOLEN = 4
TC_ACT_QUEUED = 5
TC_ACT_REPEAT = 6
TC_ACT_JUMP = 0x10000000
)
// #define tc_gen \
// __u32 index; \
// __u32 capab; \
// int action; \
// int refcnt; \
// int bindcnt
// struct tc_mirred {
// tc_gen;
// int eaction; /* one of IN/EGRESS_MIRROR/REDIR */
// __u32 ifindex; /* ifindex of egress port */
// };
type TcMirred struct {
Index uint32
Capab uint32
Action int32
Refcnt int32
Bindcnt int32
Eaction int32
Ifindex uint32
}
func (msg *TcMirred) Len() int {
return SizeofTcMirred
}
func DeserializeTcMirred(b []byte) *TcMirred {
return (*TcMirred)(unsafe.Pointer(&b[0:SizeofTcMirred][0]))
}
func (x *TcMirred) Serialize() []byte {
return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:]
}
const (
TC_POLICE_UNSPEC = TC_ACT_UNSPEC
TC_POLICE_OK = TC_ACT_OK
TC_POLICE_RECLASSIFY = TC_ACT_RECLASSIFY
TC_POLICE_SHOT = TC_ACT_SHOT
TC_POLICE_PIPE = TC_ACT_PIPE
)
// struct tc_police {
// __u32 index;
// int action;
// __u32 limit;
// __u32 burst;
// __u32 mtu;
// struct tc_ratespec rate;
// struct tc_ratespec peakrate;
// int refcnt;
// int bindcnt;
// __u32 capab;
// };
type TcPolice struct {
Index uint32
Action int32
Limit uint32
Burst uint32
Mtu uint32
Rate TcRateSpec
PeakRate TcRateSpec
Refcnt int32
Bindcnt int32
Capab uint32
}
func (msg *TcPolice) Len() int {
return SizeofTcPolice
}
func DeserializeTcPolice(b []byte) *TcPolice {
return (*TcPolice)(unsafe.Pointer(&b[0:SizeofTcPolice][0]))
}
func (x *TcPolice) Serialize() []byte {
return (*(*[SizeofTcPolice]byte)(unsafe.Pointer(x)))[:]
}
const (
TCA_FW_UNSPEC = iota
TCA_FW_CLASSID
TCA_FW_POLICE
TCA_FW_INDEV
TCA_FW_ACT
TCA_FW_MASK
TCA_FW_MAX = TCA_FW_MASK
)

View File

@ -1,161 +0,0 @@
package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"testing"
)
func (msg *XfrmAddress) write(b []byte) {
copy(b[0:SizeofXfrmAddress], msg[:])
}
func (msg *XfrmAddress) serializeSafe() []byte {
b := make([]byte, SizeofXfrmAddress)
msg.write(b)
return b
}
func deserializeXfrmAddressSafe(b []byte) *XfrmAddress {
var msg = XfrmAddress{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmAddress]), NativeEndian(), &msg)
return &msg
}
func TestXfrmAddressDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmAddress)
rand.Read(orig)
safemsg := deserializeXfrmAddressSafe(orig)
msg := DeserializeXfrmAddress(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmSelector) write(b []byte) {
const AddrEnd = SizeofXfrmAddress * 2
native := NativeEndian()
msg.Daddr.write(b[0:SizeofXfrmAddress])
msg.Saddr.write(b[SizeofXfrmAddress:AddrEnd])
native.PutUint16(b[AddrEnd:AddrEnd+2], msg.Dport)
native.PutUint16(b[AddrEnd+2:AddrEnd+4], msg.DportMask)
native.PutUint16(b[AddrEnd+4:AddrEnd+6], msg.Sport)
native.PutUint16(b[AddrEnd+6:AddrEnd+8], msg.SportMask)
native.PutUint16(b[AddrEnd+8:AddrEnd+10], msg.Family)
b[AddrEnd+10] = msg.PrefixlenD
b[AddrEnd+11] = msg.PrefixlenS
b[AddrEnd+12] = msg.Proto
copy(b[AddrEnd+13:AddrEnd+16], msg.Pad[:])
native.PutUint32(b[AddrEnd+16:AddrEnd+20], uint32(msg.Ifindex))
native.PutUint32(b[AddrEnd+20:AddrEnd+24], msg.User)
}
func (msg *XfrmSelector) serializeSafe() []byte {
length := SizeofXfrmSelector
b := make([]byte, length)
msg.write(b)
return b
}
func deserializeXfrmSelectorSafe(b []byte) *XfrmSelector {
var msg = XfrmSelector{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmSelector]), NativeEndian(), &msg)
return &msg
}
func TestXfrmSelectorDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmSelector)
rand.Read(orig)
safemsg := deserializeXfrmSelectorSafe(orig)
msg := DeserializeXfrmSelector(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmLifetimeCfg) write(b []byte) {
native := NativeEndian()
native.PutUint64(b[0:8], msg.SoftByteLimit)
native.PutUint64(b[8:16], msg.HardByteLimit)
native.PutUint64(b[16:24], msg.SoftPacketLimit)
native.PutUint64(b[24:32], msg.HardPacketLimit)
native.PutUint64(b[32:40], msg.SoftAddExpiresSeconds)
native.PutUint64(b[40:48], msg.HardAddExpiresSeconds)
native.PutUint64(b[48:56], msg.SoftUseExpiresSeconds)
native.PutUint64(b[56:64], msg.HardUseExpiresSeconds)
}
func (msg *XfrmLifetimeCfg) serializeSafe() []byte {
length := SizeofXfrmLifetimeCfg
b := make([]byte, length)
msg.write(b)
return b
}
func deserializeXfrmLifetimeCfgSafe(b []byte) *XfrmLifetimeCfg {
var msg = XfrmLifetimeCfg{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCfg]), NativeEndian(), &msg)
return &msg
}
func TestXfrmLifetimeCfgDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmLifetimeCfg)
rand.Read(orig)
safemsg := deserializeXfrmLifetimeCfgSafe(orig)
msg := DeserializeXfrmLifetimeCfg(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmLifetimeCur) write(b []byte) {
native := NativeEndian()
native.PutUint64(b[0:8], msg.Bytes)
native.PutUint64(b[8:16], msg.Packets)
native.PutUint64(b[16:24], msg.AddTime)
native.PutUint64(b[24:32], msg.UseTime)
}
func (msg *XfrmLifetimeCur) serializeSafe() []byte {
length := SizeofXfrmLifetimeCur
b := make([]byte, length)
msg.write(b)
return b
}
func deserializeXfrmLifetimeCurSafe(b []byte) *XfrmLifetimeCur {
var msg = XfrmLifetimeCur{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCur]), NativeEndian(), &msg)
return &msg
}
func TestXfrmLifetimeCurDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmLifetimeCur)
rand.Read(orig)
safemsg := deserializeXfrmLifetimeCurSafe(orig)
msg := DeserializeXfrmLifetimeCur(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmId) write(b []byte) {
native := NativeEndian()
msg.Daddr.write(b[0:SizeofXfrmAddress])
native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi)
b[SizeofXfrmAddress+4] = msg.Proto
copy(b[SizeofXfrmAddress+5:SizeofXfrmAddress+8], msg.Pad[:])
}
func (msg *XfrmId) serializeSafe() []byte {
b := make([]byte, SizeofXfrmId)
msg.write(b)
return b
}
func deserializeXfrmIdSafe(b []byte) *XfrmId {
var msg = XfrmId{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmId]), NativeEndian(), &msg)
return &msg
}
func TestXfrmIdDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmId)
rand.Read(orig)
safemsg := deserializeXfrmIdSafe(orig)
msg := DeserializeXfrmId(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

View File

@ -1,109 +0,0 @@
package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"testing"
)
func (msg *XfrmUserpolicyId) write(b []byte) {
native := NativeEndian()
msg.Sel.write(b[0:SizeofXfrmSelector])
native.PutUint32(b[SizeofXfrmSelector:SizeofXfrmSelector+4], msg.Index)
b[SizeofXfrmSelector+4] = msg.Dir
copy(b[SizeofXfrmSelector+5:SizeofXfrmSelector+8], msg.Pad[:])
}
func (msg *XfrmUserpolicyId) serializeSafe() []byte {
b := make([]byte, SizeofXfrmUserpolicyId)
msg.write(b)
return b
}
func deserializeXfrmUserpolicyIdSafe(b []byte) *XfrmUserpolicyId {
var msg = XfrmUserpolicyId{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyId]), NativeEndian(), &msg)
return &msg
}
func TestXfrmUserpolicyIdDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmUserpolicyId)
rand.Read(orig)
safemsg := deserializeXfrmUserpolicyIdSafe(orig)
msg := DeserializeXfrmUserpolicyId(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmUserpolicyInfo) write(b []byte) {
const CfgEnd = SizeofXfrmSelector + SizeofXfrmLifetimeCfg
const CurEnd = CfgEnd + SizeofXfrmLifetimeCur
native := NativeEndian()
msg.Sel.write(b[0:SizeofXfrmSelector])
msg.Lft.write(b[SizeofXfrmSelector:CfgEnd])
msg.Curlft.write(b[CfgEnd:CurEnd])
native.PutUint32(b[CurEnd:CurEnd+4], msg.Priority)
native.PutUint32(b[CurEnd+4:CurEnd+8], msg.Index)
b[CurEnd+8] = msg.Dir
b[CurEnd+9] = msg.Action
b[CurEnd+10] = msg.Flags
b[CurEnd+11] = msg.Share
copy(b[CurEnd+12:CurEnd+16], msg.Pad[:])
}
func (msg *XfrmUserpolicyInfo) serializeSafe() []byte {
b := make([]byte, SizeofXfrmUserpolicyInfo)
msg.write(b)
return b
}
func deserializeXfrmUserpolicyInfoSafe(b []byte) *XfrmUserpolicyInfo {
var msg = XfrmUserpolicyInfo{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyInfo]), NativeEndian(), &msg)
return &msg
}
func TestXfrmUserpolicyInfoDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmUserpolicyInfo)
rand.Read(orig)
safemsg := deserializeXfrmUserpolicyInfoSafe(orig)
msg := DeserializeXfrmUserpolicyInfo(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmUserTmpl) write(b []byte) {
const AddrEnd = SizeofXfrmId + 4 + SizeofXfrmAddress
native := NativeEndian()
msg.XfrmId.write(b[0:SizeofXfrmId])
native.PutUint16(b[SizeofXfrmId:SizeofXfrmId+2], msg.Family)
copy(b[SizeofXfrmId+2:SizeofXfrmId+4], msg.Pad1[:])
msg.Saddr.write(b[SizeofXfrmId+4 : AddrEnd])
native.PutUint32(b[AddrEnd:AddrEnd+4], msg.Reqid)
b[AddrEnd+4] = msg.Mode
b[AddrEnd+5] = msg.Share
b[AddrEnd+6] = msg.Optional
b[AddrEnd+7] = msg.Pad2
native.PutUint32(b[AddrEnd+8:AddrEnd+12], msg.Aalgos)
native.PutUint32(b[AddrEnd+12:AddrEnd+16], msg.Ealgos)
native.PutUint32(b[AddrEnd+16:AddrEnd+20], msg.Calgos)
}
func (msg *XfrmUserTmpl) serializeSafe() []byte {
b := make([]byte, SizeofXfrmUserTmpl)
msg.write(b)
return b
}
func deserializeXfrmUserTmplSafe(b []byte) *XfrmUserTmpl {
var msg = XfrmUserTmpl{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmUserTmpl]), NativeEndian(), &msg)
return &msg
}
func TestXfrmUserTmplDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmUserTmpl)
rand.Read(orig)
safemsg := deserializeXfrmUserTmplSafe(orig)
msg := DeserializeXfrmUserTmpl(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

View File

@ -1,207 +0,0 @@
package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"testing"
)
func (msg *XfrmUsersaId) write(b []byte) {
native := NativeEndian()
msg.Daddr.write(b[0:SizeofXfrmAddress])
native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi)
native.PutUint16(b[SizeofXfrmAddress+4:SizeofXfrmAddress+6], msg.Family)
b[SizeofXfrmAddress+6] = msg.Proto
b[SizeofXfrmAddress+7] = msg.Pad
}
func (msg *XfrmUsersaId) serializeSafe() []byte {
b := make([]byte, SizeofXfrmUsersaId)
msg.write(b)
return b
}
func deserializeXfrmUsersaIdSafe(b []byte) *XfrmUsersaId {
var msg = XfrmUsersaId{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaId]), NativeEndian(), &msg)
return &msg
}
func TestXfrmUsersaIdDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmUsersaId)
rand.Read(orig)
safemsg := deserializeXfrmUsersaIdSafe(orig)
msg := DeserializeXfrmUsersaId(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmStats) write(b []byte) {
native := NativeEndian()
native.PutUint32(b[0:4], msg.ReplayWindow)
native.PutUint32(b[4:8], msg.Replay)
native.PutUint32(b[8:12], msg.IntegrityFailed)
}
func (msg *XfrmStats) serializeSafe() []byte {
b := make([]byte, SizeofXfrmStats)
msg.write(b)
return b
}
func deserializeXfrmStatsSafe(b []byte) *XfrmStats {
var msg = XfrmStats{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmStats]), NativeEndian(), &msg)
return &msg
}
func TestXfrmStatsDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmStats)
rand.Read(orig)
safemsg := deserializeXfrmStatsSafe(orig)
msg := DeserializeXfrmStats(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmUsersaInfo) write(b []byte) {
const IdEnd = SizeofXfrmSelector + SizeofXfrmId
const AddressEnd = IdEnd + SizeofXfrmAddress
const CfgEnd = AddressEnd + SizeofXfrmLifetimeCfg
const CurEnd = CfgEnd + SizeofXfrmLifetimeCur
const StatsEnd = CurEnd + SizeofXfrmStats
native := NativeEndian()
msg.Sel.write(b[0:SizeofXfrmSelector])
msg.Id.write(b[SizeofXfrmSelector:IdEnd])
msg.Saddr.write(b[IdEnd:AddressEnd])
msg.Lft.write(b[AddressEnd:CfgEnd])
msg.Curlft.write(b[CfgEnd:CurEnd])
msg.Stats.write(b[CurEnd:StatsEnd])
native.PutUint32(b[StatsEnd:StatsEnd+4], msg.Seq)
native.PutUint32(b[StatsEnd+4:StatsEnd+8], msg.Reqid)
native.PutUint16(b[StatsEnd+8:StatsEnd+10], msg.Family)
b[StatsEnd+10] = msg.Mode
b[StatsEnd+11] = msg.ReplayWindow
b[StatsEnd+12] = msg.Flags
copy(b[StatsEnd+13:StatsEnd+20], msg.Pad[:])
}
func (msg *XfrmUsersaInfo) serializeSafe() []byte {
b := make([]byte, SizeofXfrmUsersaInfo)
msg.write(b)
return b
}
func deserializeXfrmUsersaInfoSafe(b []byte) *XfrmUsersaInfo {
var msg = XfrmUsersaInfo{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaInfo]), NativeEndian(), &msg)
return &msg
}
func TestXfrmUsersaInfoDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmUsersaInfo)
rand.Read(orig)
safemsg := deserializeXfrmUsersaInfoSafe(orig)
msg := DeserializeXfrmUsersaInfo(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmAlgo) write(b []byte) {
native := NativeEndian()
copy(b[0:64], msg.AlgName[:])
native.PutUint32(b[64:68], msg.AlgKeyLen)
copy(b[68:msg.Len()], msg.AlgKey[:])
}
func (msg *XfrmAlgo) serializeSafe() []byte {
b := make([]byte, msg.Len())
msg.write(b)
return b
}
func deserializeXfrmAlgoSafe(b []byte) *XfrmAlgo {
var msg = XfrmAlgo{}
copy(msg.AlgName[:], b[0:64])
binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
msg.AlgKey = b[68:msg.Len()]
return &msg
}
func TestXfrmAlgoDeserializeSerialize(t *testing.T) {
// use a 32 byte key len
var orig = make([]byte, SizeofXfrmAlgo+32)
rand.Read(orig)
// set the key len to 256 bits
orig[64] = 0
orig[65] = 1
orig[66] = 0
orig[67] = 0
safemsg := deserializeXfrmAlgoSafe(orig)
msg := DeserializeXfrmAlgo(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmAlgoAuth) write(b []byte) {
native := NativeEndian()
copy(b[0:64], msg.AlgName[:])
native.PutUint32(b[64:68], msg.AlgKeyLen)
native.PutUint32(b[68:72], msg.AlgTruncLen)
copy(b[72:msg.Len()], msg.AlgKey[:])
}
func (msg *XfrmAlgoAuth) serializeSafe() []byte {
b := make([]byte, msg.Len())
msg.write(b)
return b
}
func deserializeXfrmAlgoAuthSafe(b []byte) *XfrmAlgoAuth {
var msg = XfrmAlgoAuth{}
copy(msg.AlgName[:], b[0:64])
binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgTruncLen)
msg.AlgKey = b[72:msg.Len()]
return &msg
}
func TestXfrmAlgoAuthDeserializeSerialize(t *testing.T) {
// use a 32 byte key len
var orig = make([]byte, SizeofXfrmAlgoAuth+32)
rand.Read(orig)
// set the key len to 256 bits
orig[64] = 0
orig[65] = 1
orig[66] = 0
orig[67] = 0
safemsg := deserializeXfrmAlgoAuthSafe(orig)
msg := DeserializeXfrmAlgoAuth(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmEncapTmpl) write(b []byte) {
native := NativeEndian()
native.PutUint16(b[0:2], msg.EncapType)
native.PutUint16(b[2:4], msg.EncapSport)
native.PutUint16(b[4:6], msg.EncapDport)
copy(b[6:8], msg.Pad[:])
msg.EncapOa.write(b[8:SizeofXfrmAddress])
}
func (msg *XfrmEncapTmpl) serializeSafe() []byte {
b := make([]byte, SizeofXfrmEncapTmpl)
msg.write(b)
return b
}
func deserializeXfrmEncapTmplSafe(b []byte) *XfrmEncapTmpl {
var msg = XfrmEncapTmpl{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmEncapTmpl]), NativeEndian(), &msg)
return &msg
}
func TestXfrmEncapTmplDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmEncapTmpl)
rand.Read(orig)
safemsg := deserializeXfrmEncapTmplSafe(orig)
msg := DeserializeXfrmEncapTmpl(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

View File

@ -1,98 +0,0 @@
package netlink
import "testing"
func TestProtinfo(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
master := &Bridge{LinkAttrs{Name: "foo"}}
if err := LinkAdd(master); err != nil {
t.Fatal(err)
}
iface1 := &Dummy{LinkAttrs{Name: "bar1", MasterIndex: master.Index}}
iface2 := &Dummy{LinkAttrs{Name: "bar2", MasterIndex: master.Index}}
iface3 := &Dummy{LinkAttrs{Name: "bar3"}}
if err := LinkAdd(iface1); err != nil {
t.Fatal(err)
}
if err := LinkAdd(iface2); err != nil {
t.Fatal(err)
}
if err := LinkAdd(iface3); err != nil {
t.Fatal(err)
}
oldpi1, err := LinkGetProtinfo(iface1)
if err != nil {
t.Fatal(err)
}
oldpi2, err := LinkGetProtinfo(iface2)
if err != nil {
t.Fatal(err)
}
if err := LinkSetHairpin(iface1, true); err != nil {
t.Fatal(err)
}
if err := LinkSetRootBlock(iface1, true); err != nil {
t.Fatal(err)
}
pi1, err := LinkGetProtinfo(iface1)
if err != nil {
t.Fatal(err)
}
if !pi1.Hairpin {
t.Fatalf("Hairpin mode is not enabled for %s, but should", iface1.Name)
}
if !pi1.RootBlock {
t.Fatalf("RootBlock is not enabled for %s, but should", iface1.Name)
}
if pi1.Guard != oldpi1.Guard {
t.Fatalf("Guard field was changed for %s but shouldn't", iface1.Name)
}
if pi1.FastLeave != oldpi1.FastLeave {
t.Fatalf("FastLeave field was changed for %s but shouldn't", iface1.Name)
}
if pi1.Learning != oldpi1.Learning {
t.Fatalf("Learning field was changed for %s but shouldn't", iface1.Name)
}
if pi1.Flood != oldpi1.Flood {
t.Fatalf("Flood field was changed for %s but shouldn't", iface1.Name)
}
if err := LinkSetGuard(iface2, true); err != nil {
t.Fatal(err)
}
if err := LinkSetLearning(iface2, false); err != nil {
t.Fatal(err)
}
pi2, err := LinkGetProtinfo(iface2)
if err != nil {
t.Fatal(err)
}
if pi2.Hairpin {
t.Fatalf("Hairpin mode is enabled for %s, but shouldn't", iface2.Name)
}
if !pi2.Guard {
t.Fatalf("Guard is not enabled for %s, but should", iface2.Name)
}
if pi2.Learning {
t.Fatalf("Learning is enabled for %s, but shouldn't", iface2.Name)
}
if pi2.RootBlock != oldpi2.RootBlock {
t.Fatalf("RootBlock field was changed for %s but shouldn't", iface2.Name)
}
if pi2.FastLeave != oldpi2.FastLeave {
t.Fatalf("FastLeave field was changed for %s but shouldn't", iface2.Name)
}
if pi2.Flood != oldpi2.Flood {
t.Fatalf("Flood field was changed for %s but shouldn't", iface2.Name)
}
if err := LinkSetHairpin(iface3, true); err == nil || err.Error() != "operation not supported" {
t.Fatalf("Set protinfo attrs for link without master is not supported, but err: %s", err)
}
}

View File

@ -0,0 +1,167 @@
package netlink
import (
"fmt"
)
const (
HANDLE_NONE = 0
HANDLE_INGRESS = 0xFFFFFFF1
HANDLE_ROOT = 0xFFFFFFFF
PRIORITY_MAP_LEN = 16
)
type Qdisc interface {
Attrs() *QdiscAttrs
Type() string
}
// Qdisc represents a netlink qdisc. A qdisc is associated with a link,
// has a handle, a parent and a refcnt. The root qdisc of a device should
// have parent == HANDLE_ROOT.
type QdiscAttrs struct {
LinkIndex int
Handle uint32
Parent uint32
Refcnt uint32 // read only
}
func (q QdiscAttrs) String() string {
return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Refcnt: %s}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Refcnt)
}
func MakeHandle(major, minor uint16) uint32 {
return (uint32(major) << 16) | uint32(minor)
}
func MajorMinor(handle uint32) (uint16, uint16) {
return uint16((handle & 0xFFFF0000) >> 16), uint16(handle & 0x0000FFFFF)
}
func HandleStr(handle uint32) string {
switch handle {
case HANDLE_NONE:
return "none"
case HANDLE_INGRESS:
return "ingress"
case HANDLE_ROOT:
return "root"
default:
major, minor := MajorMinor(handle)
return fmt.Sprintf("%x:%x", major, minor)
}
}
// PfifoFast is the default qdisc created by the kernel if one has not
// been defined for the interface
type PfifoFast struct {
QdiscAttrs
Bands uint8
PriorityMap [PRIORITY_MAP_LEN]uint8
}
func (qdisc *PfifoFast) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *PfifoFast) Type() string {
return "pfifo_fast"
}
// Prio is a basic qdisc that works just like PfifoFast
type Prio struct {
QdiscAttrs
Bands uint8
PriorityMap [PRIORITY_MAP_LEN]uint8
}
func NewPrio(attrs QdiscAttrs) *Prio {
return &Prio{
QdiscAttrs: attrs,
Bands: 3,
PriorityMap: [PRIORITY_MAP_LEN]uint8{1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
}
}
func (qdisc *Prio) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *Prio) Type() string {
return "prio"
}
// Htb is a classful qdisc that rate limits based on tokens
type Htb struct {
QdiscAttrs
Version uint32
Rate2Quantum uint32
Defcls uint32
Debug uint32
DirectPkts uint32
}
func NewHtb(attrs QdiscAttrs) *Htb {
return &Htb{
QdiscAttrs: attrs,
Version: 3,
Defcls: 0,
Rate2Quantum: 10,
Debug: 0,
DirectPkts: 0,
}
}
func (qdisc *Htb) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *Htb) Type() string {
return "htb"
}
// Tbf is a classless qdisc that rate limits based on tokens
type Tbf struct {
QdiscAttrs
// TODO: handle 64bit rate properly
Rate uint64
Limit uint32
Buffer uint32
// TODO: handle other settings
}
func (qdisc *Tbf) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *Tbf) Type() string {
return "tbf"
}
// Ingress is a qdisc for adding ingress filters
type Ingress struct {
QdiscAttrs
}
func (qdisc *Ingress) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *Ingress) Type() string {
return "ingress"
}
// GenericQdisc qdiscs represent types that are not currently understood
// by this netlink library.
type GenericQdisc struct {
QdiscAttrs
QdiscType string
}
func (qdisc *GenericQdisc) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}
func (qdisc *GenericQdisc) Type() string {
return qdisc.QdiscType
}

View File

@ -0,0 +1,316 @@
package netlink
import (
"fmt"
"io/ioutil"
"strconv"
"strings"
"syscall"
"github.com/vishvananda/netlink/nl"
)
// QdiscDel will delete a qdisc from the system.
// Equivalent to: `tc qdisc del $qdisc`
func QdiscDel(qdisc Qdisc) error {
req := nl.NewNetlinkRequest(syscall.RTM_DELQDISC, syscall.NLM_F_ACK)
base := qdisc.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
}
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// QdiscAdd will add a qdisc to the system.
// Equivalent to: `tc qdisc add $qdisc`
func QdiscAdd(qdisc Qdisc) error {
req := nl.NewNetlinkRequest(syscall.RTM_NEWQDISC, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
base := qdisc.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
}
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if prio, ok := qdisc.(*Prio); ok {
tcmap := nl.TcPrioMap{
Bands: int32(prio.Bands),
Priomap: prio.PriorityMap,
}
options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize())
} else if tbf, ok := qdisc.(*Tbf); ok {
opt := nl.TcTbfQopt{}
// TODO: handle rate > uint32
opt.Rate.Rate = uint32(tbf.Rate)
opt.Limit = tbf.Limit
opt.Buffer = tbf.Buffer
nl.NewRtAttrChild(options, nl.TCA_TBF_PARMS, opt.Serialize())
} else if htb, ok := qdisc.(*Htb); ok {
opt := nl.TcHtbGlob{}
opt.Version = htb.Version
opt.Rate2Quantum = htb.Rate2Quantum
opt.Defcls = htb.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 _, ok := qdisc.(*Ingress); ok {
// ingress filters must use the proper handle
if msg.Parent != HANDLE_INGRESS {
return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS")
}
}
req.AddData(options)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// QdiscList gets a list of qdiscs in the system.
// Equivalent to: `tc qdisc show`.
// The list can be filtered by link.
func QdiscList(link Link) ([]Qdisc, error) {
req := nl.NewNetlinkRequest(syscall.RTM_GETQDISC, syscall.NLM_F_DUMP)
index := int32(0)
if link != nil {
base := link.Attrs()
ensureIndex(base)
index = int32(base.Index)
}
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: index,
}
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWQDISC)
if err != nil {
return nil, err
}
var res []Qdisc
for _, m := range msgs {
msg := nl.DeserializeTcMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
// skip qdiscs from other interfaces
if link != nil && msg.Ifindex != index {
continue
}
base := QdiscAttrs{
LinkIndex: int(msg.Ifindex),
Handle: msg.Handle,
Parent: msg.Parent,
Refcnt: msg.Info,
}
var qdisc Qdisc
qdiscType := ""
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.TCA_KIND:
qdiscType = string(attr.Value[:len(attr.Value)-1])
switch qdiscType {
case "pfifo_fast":
qdisc = &PfifoFast{}
case "prio":
qdisc = &Prio{}
case "tbf":
qdisc = &Tbf{}
case "ingress":
qdisc = &Ingress{}
case "htb":
qdisc = &Htb{}
default:
qdisc = &GenericQdisc{QdiscType: qdiscType}
}
case nl.TCA_OPTIONS:
switch qdiscType {
case "pfifo_fast":
// pfifo returns TcPrioMap directly without wrapping it in rtattr
if err := parsePfifoFastData(qdisc, attr.Value); err != nil {
return nil, err
}
case "prio":
// prio returns TcPrioMap directly without wrapping it in rtattr
if err := parsePrioData(qdisc, attr.Value); err != nil {
return nil, err
}
case "tbf":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
if err := parseTbfData(qdisc, data); err != nil {
return nil, err
}
case "htb":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
if err := parseHtbData(qdisc, data); err != nil {
return nil, err
}
// no options for ingress
}
}
}
*qdisc.Attrs() = base
res = append(res, qdisc)
}
return res, nil
}
func parsePfifoFastData(qdisc Qdisc, value []byte) error {
pfifo := qdisc.(*PfifoFast)
tcmap := nl.DeserializeTcPrioMap(value)
pfifo.PriorityMap = tcmap.Priomap
pfifo.Bands = uint8(tcmap.Bands)
return nil
}
func parsePrioData(qdisc Qdisc, value []byte) error {
prio := qdisc.(*Prio)
tcmap := nl.DeserializeTcPrioMap(value)
prio.PriorityMap = tcmap.Priomap
prio.Bands = uint8(tcmap.Bands)
return nil
}
func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
htb := qdisc.(*Htb)
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_HTB_INIT:
opt := nl.DeserializeTcHtbGlob(datum.Value)
htb.Version = opt.Version
htb.Rate2Quantum = opt.Rate2Quantum
htb.Defcls = opt.Defcls
htb.Debug = opt.Debug
htb.DirectPkts = opt.DirectPkts
case nl.TCA_HTB_DIRECT_QLEN:
// TODO
//htb.DirectQlen = native.uint32(datum.Value)
}
}
return nil
}
func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
native = nl.NativeEndian()
tbf := qdisc.(*Tbf)
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_TBF_PARMS:
opt := nl.DeserializeTcTbfQopt(datum.Value)
tbf.Rate = uint64(opt.Rate.Rate)
tbf.Limit = opt.Limit
tbf.Buffer = opt.Buffer
case nl.TCA_TBF_RATE64:
tbf.Rate = native.Uint64(datum.Value[0:4])
}
}
return nil
}
const (
TIME_UNITS_PER_SEC = 1000000
)
var (
tickInUsec float64 = 0.0
clockFactor float64 = 0.0
hz float64 = 0.0
)
func initClock() {
data, err := ioutil.ReadFile("/proc/net/psched")
if err != nil {
return
}
parts := strings.Split(strings.TrimSpace(string(data)), " ")
if len(parts) < 3 {
return
}
var vals [3]uint64
for i := range vals {
val, err := strconv.ParseUint(parts[i], 16, 32)
if err != nil {
return
}
vals[i] = val
}
// compatibility
if vals[2] == 1000000000 {
vals[0] = vals[1]
}
clockFactor = float64(vals[2]) / TIME_UNITS_PER_SEC
tickInUsec = float64(vals[0]) / float64(vals[1]) * clockFactor
hz = float64(vals[0])
}
func TickInUsec() float64 {
if tickInUsec == 0.0 {
initClock()
}
return tickInUsec
}
func ClockFactor() float64 {
if clockFactor == 0.0 {
initClock()
}
return clockFactor
}
func Hz() float64 {
if hz == 0.0 {
initClock()
}
return hz
}
func time2Tick(time uint32) uint32 {
return uint32(float64(time) * TickInUsec())
}
func tick2Time(tick uint32) uint32 {
return uint32(float64(tick) / TickInUsec())
}
func time2Ktime(time uint32) uint32 {
return uint32(float64(time) * ClockFactor())
}
func ktime2Time(ktime uint32) uint32 {
return uint32(float64(ktime) / ClockFactor())
}
func burst(rate uint64, buffer uint32) uint32 {
return uint32(float64(rate) * float64(tick2Time(buffer)) / TIME_UNITS_PER_SEC)
}
func latency(rate uint64, limit, buffer uint32) float64 {
return TIME_UNITS_PER_SEC*(float64(limit)/float64(rate)) - float64(tick2Time(buffer))
}
func Xmittime(rate uint64, size uint32) float64 {
return TickInUsec() * TIME_UNITS_PER_SEC * (float64(size) / float64(rate))
}

View File

@ -17,6 +17,13 @@ const (
SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE
)
type NextHopFlag int
const (
FLAG_ONLINK NextHopFlag = syscall.RTNH_F_ONLINK
FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE
)
// Route represents a netlink route. A route is associated with a link,
// has a destination network, an optional source ip, and optional
// gateway. Advanced route parameters and non-main routing tables are
@ -27,9 +34,44 @@ type Route struct {
Dst *net.IPNet
Src net.IP
Gw net.IP
Flags int
}
func (r Route) String() string {
return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s}", r.LinkIndex, r.Dst,
r.Src, r.Gw)
return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s Flags: %s}", r.LinkIndex, r.Dst,
r.Src, r.Gw, r.ListFlags())
}
func (r *Route) SetFlag(flag NextHopFlag) {
r.Flags |= int(flag)
}
func (r *Route) ClearFlag(flag NextHopFlag) {
r.Flags &^= int(flag)
}
type flagString struct {
f NextHopFlag
s string
}
var testFlags = []flagString{
flagString{f: FLAG_ONLINK, s: "onlink"},
flagString{f: FLAG_PERVASIVE, s: "pervasive"},
}
func (r *Route) ListFlags() []string {
var flags []string
for _, tf := range testFlags {
if r.Flags&int(tf.f) != 0 {
flags = append(flags, tf.s)
}
}
return flags
}
// RouteUpdate is sent when a route changes - type is RTM_NEWROUTE or RTM_DELROUTE
type RouteUpdate struct {
Type uint16
Route
}

View File

@ -17,7 +17,7 @@ func RouteAdd(route *Route) error {
return routeHandle(route, req, nl.NewRtMsg())
}
// RouteAdd will delete a route from the system.
// RouteDel will delete a route from the system.
// Equivalent to: `ip route del $route`
func RouteDel(route *Route) error {
req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK)
@ -30,6 +30,7 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
}
msg.Scope = uint8(route.Scope)
msg.Flags = uint32(route.Flags)
family := -1
var rtAttrs []*nl.RtAttr
@ -117,7 +118,6 @@ func RouteList(link Link, family int) ([]Route, error) {
index = base.Index
}
native := nl.NativeEndian()
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
@ -132,31 +132,14 @@ func RouteList(link Link, family int) ([]Route, error) {
continue
}
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
route, err := deserializeRoute(m)
if err != nil {
return nil, err
}
route := Route{Scope: Scope(msg.Scope)}
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.RTA_GATEWAY:
route.Gw = net.IP(attr.Value)
case syscall.RTA_PREFSRC:
route.Src = net.IP(attr.Value)
case syscall.RTA_DST:
route.Dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
}
case syscall.RTA_OIF:
routeIndex := int(native.Uint32(attr.Value[0:4]))
if link != nil && routeIndex != index {
// Ignore routes from other interfaces
continue
}
route.LinkIndex = routeIndex
}
if link != nil && route.LinkIndex != index {
// Ignore routes from other interfaces
continue
}
res = append(res, route)
}
@ -164,6 +147,37 @@ func RouteList(link Link, family int) ([]Route, error) {
return res, nil
}
// deserializeRoute decodes a binary netlink message into a Route struct
func deserializeRoute(m []byte) (Route, error) {
route := Route{}
msg := nl.DeserializeRtMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return route, err
}
route.Scope = Scope(msg.Scope)
route.Flags = int(msg.Flags)
native := nl.NativeEndian()
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.RTA_GATEWAY:
route.Gw = net.IP(attr.Value)
case syscall.RTA_PREFSRC:
route.Src = net.IP(attr.Value)
case syscall.RTA_DST:
route.Dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
}
case syscall.RTA_OIF:
routeIndex := int(native.Uint32(attr.Value[0:4]))
route.LinkIndex = routeIndex
}
}
return route, nil
}
// RouteGet gets a route to a specific destination from the host system.
// Equivalent to: 'ip route get'.
func RouteGet(destination net.IP) ([]Route, error) {
@ -191,34 +205,47 @@ func RouteGet(destination net.IP) ([]Route, error) {
return nil, err
}
native := nl.NativeEndian()
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
route, err := deserializeRoute(m)
if err != nil {
return nil, err
}
route := Route{}
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.RTA_GATEWAY:
route.Gw = net.IP(attr.Value)
case syscall.RTA_PREFSRC:
route.Src = net.IP(attr.Value)
case syscall.RTA_DST:
route.Dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
}
case syscall.RTA_OIF:
routeIndex := int(native.Uint32(attr.Value[0:4]))
route.LinkIndex = routeIndex
}
}
res = append(res, route)
}
return res, nil
}
// 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 {
s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE)
if err != nil {
return err
}
if done != nil {
go func() {
<-done
s.Close()
}()
}
go func() {
defer close(ch)
for {
msgs, err := s.Receive()
if err != nil {
return
}
for _, m := range msgs {
route, err := deserializeRoute(m.Data)
if err != nil {
return
}
ch <- RouteUpdate{Type: m.Header.Type, Route: route}
}
}
}()
return nil
}

View File

@ -1,84 +0,0 @@
package netlink
import (
"net"
"testing"
)
func TestRouteAddDel(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
// get loopback interface
link, err := LinkByName("lo")
if err != nil {
t.Fatal(err)
}
// bring the interface up
if err = LinkSetUp(link); err != nil {
t.Fatal(err)
}
// add a gateway route
_, dst, err := net.ParseCIDR("192.168.0.0/24")
ip := net.ParseIP("127.1.1.1")
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
err = RouteAdd(&route)
if err != nil {
t.Fatal(err)
}
routes, err := RouteList(link, FAMILY_V4)
if err != nil {
t.Fatal(err)
}
if len(routes) != 1 {
t.Fatal("Link not added properly")
}
dstIP := net.ParseIP("192.168.0.42")
routeToDstIP, err := RouteGet(dstIP)
if err != nil {
t.Fatal(err)
}
if len(routeToDstIP) == 0 {
t.Fatal("Default route not present")
}
err = RouteDel(&route)
if err != nil {
t.Fatal(err)
}
routes, err = RouteList(link, FAMILY_V4)
if err != nil {
t.Fatal(err)
}
if len(routes) != 0 {
t.Fatal("Route not removed properly")
}
}
func TestRouteAddIncomplete(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
// get loopback interface
link, err := LinkByName("lo")
if err != nil {
t.Fatal(err)
}
// bring the interface up
if err = LinkSetUp(link); err != nil {
t.Fatal(err)
}
route := Route{LinkIndex: link.Attrs().Index}
if err := RouteAdd(&route); err == nil {
t.Fatal("Adding incomplete route should fail")
}
}

View File

@ -1,49 +0,0 @@
package netlink
import (
"net"
"testing"
)
func TestXfrmPolicyAddDel(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
src, _ := ParseIPNet("127.1.1.1/32")
dst, _ := ParseIPNet("127.1.1.2/32")
policy := XfrmPolicy{
Src: src,
Dst: dst,
Dir: XFRM_DIR_OUT,
}
tmpl := XfrmPolicyTmpl{
Src: net.ParseIP("127.0.0.1"),
Dst: net.ParseIP("127.0.0.2"),
Proto: XFRM_PROTO_ESP,
Mode: XFRM_MODE_TUNNEL,
}
policy.Tmpls = append(policy.Tmpls, tmpl)
if err := XfrmPolicyAdd(&policy); err != nil {
t.Fatal(err)
}
policies, err := XfrmPolicyList(FAMILY_ALL)
if err != nil {
t.Fatal(err)
}
if len(policies) != 1 {
t.Fatal("Policy not added properly")
}
if err = XfrmPolicyDel(&policy); err != nil {
t.Fatal(err)
}
policies, err = XfrmPolicyList(FAMILY_ALL)
if err != nil {
t.Fatal(err)
}
if len(policies) != 0 {
t.Fatal("Policy not removed properly")
}
}

View File

@ -1,50 +0,0 @@
package netlink
import (
"net"
"testing"
)
func TestXfrmStateAddDel(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
state := XfrmState{
Src: net.ParseIP("127.0.0.1"),
Dst: net.ParseIP("127.0.0.2"),
Proto: XFRM_PROTO_ESP,
Mode: XFRM_MODE_TUNNEL,
Spi: 1,
Auth: &XfrmStateAlgo{
Name: "hmac(sha256)",
Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"),
},
Crypt: &XfrmStateAlgo{
Name: "cbc(aes)",
Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"),
},
}
if err := XfrmStateAdd(&state); err != nil {
t.Fatal(err)
}
policies, err := XfrmStateList(FAMILY_ALL)
if err != nil {
t.Fatal(err)
}
if len(policies) != 1 {
t.Fatal("State not added properly")
}
if err = XfrmStateDel(&state); err != nil {
t.Fatal(err)
}
policies, err = XfrmStateList(FAMILY_ALL)
if err != nil {
t.Fatal(err)
}
if len(policies) != 0 {
t.Fatal("State not removed properly")
}
}

View File

@ -0,0 +1,7 @@
// +build linux,s390x
package netns
const (
SYS_SETNS = 339
)

View File

@ -1,66 +0,0 @@
package netns
import (
"runtime"
"sync"
"testing"
)
func TestGetNewSetDelete(t *testing.T) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := Get()
if err != nil {
t.Fatal(err)
}
newns, err := New()
if err != nil {
t.Fatal(err)
}
if origns.Equal(newns) {
t.Fatal("New ns failed")
}
if err := Set(origns); err != nil {
t.Fatal(err)
}
newns.Close()
if newns.IsOpen() {
t.Fatal("newns still open after close", newns)
}
ns, err := Get()
if err != nil {
t.Fatal(err)
}
if !ns.Equal(origns) {
t.Fatal("Reset ns failed", origns, newns, ns)
}
}
func TestNone(t *testing.T) {
ns := None()
if ns.IsOpen() {
t.Fatal("None ns is open", ns)
}
}
func TestThreaded(t *testing.T) {
ncpu := runtime.GOMAXPROCS(-1)
if ncpu < 2 {
t.Skip("-cpu=2 or larger required")
}
// Lock this thread simply to ensure other threads get used.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
wg := &sync.WaitGroup{}
for i := 0; i < ncpu; i++ {
wg.Add(1)
go func() {
defer wg.Done()
TestGetNewSetDelete(t)
}()
}
wg.Wait()
}