mirror of
https://github.com/vishvananda/netlink
synced 2025-01-31 20:26:55 +00:00
Resolved upstream merge conflicts
This commit is contained in:
commit
40ccc37ce5
2
addr.go
2
addr.go
@ -11,6 +11,8 @@ import (
|
||||
type Addr struct {
|
||||
*net.IPNet
|
||||
Label string
|
||||
Flags int
|
||||
Scope int
|
||||
}
|
||||
|
||||
// String returns $ip/$netmask $label
|
||||
|
@ -9,6 +9,9 @@ import (
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// IFA_FLAGS is a u32 attribute.
|
||||
const IFA_FLAGS = 0x8
|
||||
|
||||
// AddrAdd will add an IP address to a link device.
|
||||
// Equivalent to: `ip addr add $addr dev $link`
|
||||
func AddrAdd(link Link, addr *Addr) error {
|
||||
@ -35,6 +38,7 @@ func 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()
|
||||
msg.Prefixlen = uint8(prefixlen)
|
||||
req.AddData(msg)
|
||||
@ -52,6 +56,13 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
|
||||
addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData)
|
||||
req.AddData(addressData)
|
||||
|
||||
if addr.Flags != 0 {
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(addr.Flags))
|
||||
flagsData := nl.NewRtAttr(IFA_FLAGS, b)
|
||||
req.AddData(flagsData)
|
||||
}
|
||||
|
||||
if addr.Label != "" {
|
||||
labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
|
||||
req.AddData(labelData)
|
||||
@ -111,6 +122,8 @@ func AddrList(link Link, family int) ([]Addr, error) {
|
||||
}
|
||||
case syscall.IFA_LABEL:
|
||||
addr.Label = string(attr.Value[:len(attr.Value)-1])
|
||||
case IFA_FLAGS:
|
||||
addr.Flags = int(native.Uint32(attr.Value[0:4]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,6 +133,7 @@ func AddrList(link Link, family int) ([]Addr, error) {
|
||||
} else {
|
||||
addr.IPNet = dst
|
||||
}
|
||||
addr.Scope = int(msg.Scope)
|
||||
|
||||
res = append(res, addr)
|
||||
}
|
||||
|
91
addr_test.go
91
addr_test.go
@ -1,10 +1,39 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddrAddDel(t *testing.T) {
|
||||
func TestAddr(t *testing.T) {
|
||||
var address = &net.IPNet{net.IPv4(127, 0, 0, 2), net.CIDRMask(24, 32)}
|
||||
var addrTests = []struct {
|
||||
addr *Addr
|
||||
expected *Addr
|
||||
}{
|
||||
{
|
||||
&Addr{IPNet: address},
|
||||
&Addr{IPNet: address, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
|
||||
},
|
||||
{
|
||||
&Addr{IPNet: address, Label: "local"},
|
||||
&Addr{IPNet: address, Label: "local", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
|
||||
},
|
||||
{
|
||||
&Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC},
|
||||
&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
|
||||
},
|
||||
{
|
||||
&Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED},
|
||||
&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
|
||||
},
|
||||
{
|
||||
&Addr{IPNet: address, Scope: syscall.RT_SCOPE_NOWHERE},
|
||||
&Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_NOWHERE},
|
||||
},
|
||||
}
|
||||
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
|
||||
@ -13,33 +42,47 @@ func TestAddrAddDel(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addr, err := ParseAddr("127.1.1.1/24 local")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, tt := range addrTests {
|
||||
if err = AddrAdd(link, tt.addr); 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)
|
||||
}
|
||||
|
||||
addrs, err := AddrList(link, FAMILY_ALL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(addrs) != 1 {
|
||||
t.Fatal("Address not added properly")
|
||||
}
|
||||
|
||||
if len(addrs) != 1 || !addr.Equal(addrs[0]) || addrs[0].Label != addr.Label {
|
||||
t.Fatal("Address not added properly")
|
||||
}
|
||||
if !addrs[0].Equal(*tt.expected) {
|
||||
t.Fatalf("Address ip no set properly, got=%s, expected=%s", addrs[0], tt.expected)
|
||||
}
|
||||
|
||||
if err = AddrDel(link, addr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
addrs, err = AddrList(link, FAMILY_ALL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if addrs[0].Label != tt.expected.Label {
|
||||
t.Fatalf("Address label not set properly, got=%s, expected=%s", addrs[0].Label, tt.expected.Label)
|
||||
}
|
||||
|
||||
if len(addrs) != 0 {
|
||||
t.Fatal("Address not removed properly")
|
||||
if addrs[0].Flags != tt.expected.Flags {
|
||||
t.Fatalf("Address flags not set properly, got=%d, expected=%d", addrs[0].Flags, tt.expected.Flags)
|
||||
}
|
||||
|
||||
if addrs[0].Scope != tt.expected.Scope {
|
||||
t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope)
|
||||
}
|
||||
|
||||
if err = AddrDel(link, tt.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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
284
link.go
284
link.go
@ -1,6 +1,7 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
@ -241,6 +242,289 @@ func (ipvlan *IPVlan) Type() string {
|
||||
return "ipvlan"
|
||||
}
|
||||
|
||||
// BondMode type
|
||||
type BondMode int
|
||||
|
||||
func (b BondMode) String() string {
|
||||
s, ok := bondModeToString[b]
|
||||
if !ok {
|
||||
return fmt.Sprintf("BondMode(%d)", b)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// StringToBondMode returns bond mode, or uknonw is the s is invalid.
|
||||
func StringToBondMode(s string) BondMode {
|
||||
mode, ok := StringToBondModeMap[s]
|
||||
if !ok {
|
||||
return BOND_MODE_UNKNOWN
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
// Possible BondMode
|
||||
const (
|
||||
BOND_MODE_802_3AD BondMode = iota
|
||||
BOND_MODE_BALANCE_RR
|
||||
BOND_MODE_ACTIVE_BACKUP
|
||||
BOND_MODE_BALANCE_XOR
|
||||
BOND_MODE_BROADCAST
|
||||
BOND_MODE_BALANCE_TLB
|
||||
BOND_MODE_BALANCE_ALB
|
||||
BOND_MODE_UNKNOWN
|
||||
)
|
||||
|
||||
var bondModeToString = map[BondMode]string{
|
||||
BOND_MODE_802_3AD: "802.3ad",
|
||||
BOND_MODE_BALANCE_RR: "balance-rr",
|
||||
BOND_MODE_ACTIVE_BACKUP: "active-backup",
|
||||
BOND_MODE_BALANCE_XOR: "balance-xor",
|
||||
BOND_MODE_BROADCAST: "broadcast",
|
||||
BOND_MODE_BALANCE_TLB: "balance-tlb",
|
||||
BOND_MODE_BALANCE_ALB: "balance-alb",
|
||||
}
|
||||
var StringToBondModeMap = map[string]BondMode{
|
||||
"802.3ad": BOND_MODE_802_3AD,
|
||||
"balance-rr": BOND_MODE_BALANCE_RR,
|
||||
"active-backup": BOND_MODE_ACTIVE_BACKUP,
|
||||
"balance-xor": BOND_MODE_BALANCE_XOR,
|
||||
"broadcast": BOND_MODE_BROADCAST,
|
||||
"balance-tlb": BOND_MODE_BALANCE_TLB,
|
||||
"balance-alb": BOND_MODE_BALANCE_ALB,
|
||||
}
|
||||
|
||||
// BondArpValidate type
|
||||
type BondArpValidate int
|
||||
|
||||
// Possible BondArpValidate value
|
||||
const (
|
||||
BOND_ARP_VALIDATE_NONE BondArpValidate = iota
|
||||
BOND_ARP_VALIDATE_ACTIVE
|
||||
BOND_ARP_VALIDATE_BACKUP
|
||||
BOND_ARP_VALIDATE_ALL
|
||||
)
|
||||
|
||||
// BondPrimaryReselect type
|
||||
type BondPrimaryReselect int
|
||||
|
||||
// Possible BondPrimaryReselect value
|
||||
const (
|
||||
BOND_PRIMARY_RESELECT_ALWAYS BondPrimaryReselect = iota
|
||||
BOND_PRIMARY_RESELECT_BETTER
|
||||
BOND_PRIMARY_RESELECT_FAILURE
|
||||
)
|
||||
|
||||
// BondArpAllTargets type
|
||||
type BondArpAllTargets int
|
||||
|
||||
// Possible BondArpAllTargets value
|
||||
const (
|
||||
BOND_ARP_ALL_TARGETS_ANY BondArpAllTargets = iota
|
||||
BOND_ARP_ALL_TARGETS_ALL
|
||||
)
|
||||
|
||||
// BondFailOverMac type
|
||||
type BondFailOverMac int
|
||||
|
||||
// Possible BondFailOverMac value
|
||||
const (
|
||||
BOND_FAIL_OVER_MAC_NONE BondFailOverMac = iota
|
||||
BOND_FAIL_OVER_MAC_ACTIVE
|
||||
BOND_FAIL_OVER_MAC_FOLLOW
|
||||
)
|
||||
|
||||
// BondXmitHashPolicy type
|
||||
type BondXmitHashPolicy int
|
||||
|
||||
func (b BondXmitHashPolicy) String() string {
|
||||
s, ok := bondXmitHashPolicyToString[b]
|
||||
if !ok {
|
||||
return fmt.Sprintf("XmitHashPolicy(%d)", b)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// StringToBondXmitHashPolicy returns bond lacp arte, or uknonw is the s is invalid.
|
||||
func StringToBondXmitHashPolicy(s string) BondXmitHashPolicy {
|
||||
lacp, ok := StringToBondXmitHashPolicyMap[s]
|
||||
if !ok {
|
||||
return BOND_XMIT_HASH_POLICY_UNKNOWN
|
||||
}
|
||||
return lacp
|
||||
}
|
||||
|
||||
// Possible BondXmitHashPolicy value
|
||||
const (
|
||||
BOND_XMIT_HASH_POLICY_LAYER2 BondXmitHashPolicy = iota
|
||||
BOND_XMIT_HASH_POLICY_LAYER3_4
|
||||
BOND_XMIT_HASH_POLICY_LAYER2_3
|
||||
BOND_XMIT_HASH_POLICY_ENCAP2_3
|
||||
BOND_XMIT_HASH_POLICY_ENCAP3_4
|
||||
BOND_XMIT_HASH_POLICY_UNKNOWN
|
||||
)
|
||||
|
||||
var bondXmitHashPolicyToString = map[BondXmitHashPolicy]string{
|
||||
BOND_XMIT_HASH_POLICY_LAYER2: "layer2",
|
||||
BOND_XMIT_HASH_POLICY_LAYER3_4: "layer3+4",
|
||||
BOND_XMIT_HASH_POLICY_LAYER2_3: "layer2+3",
|
||||
BOND_XMIT_HASH_POLICY_ENCAP2_3: "encap2+3",
|
||||
BOND_XMIT_HASH_POLICY_ENCAP3_4: "encap3+4",
|
||||
}
|
||||
var StringToBondXmitHashPolicyMap = map[string]BondXmitHashPolicy{
|
||||
"layer2": BOND_XMIT_HASH_POLICY_LAYER2,
|
||||
"layer3+4": BOND_XMIT_HASH_POLICY_LAYER3_4,
|
||||
"layer2+3": BOND_XMIT_HASH_POLICY_LAYER2_3,
|
||||
"encap2+3": BOND_XMIT_HASH_POLICY_ENCAP2_3,
|
||||
"encap3+4": BOND_XMIT_HASH_POLICY_ENCAP3_4,
|
||||
}
|
||||
|
||||
// BondLacpRate type
|
||||
type BondLacpRate int
|
||||
|
||||
func (b BondLacpRate) String() string {
|
||||
s, ok := bondLacpRateToString[b]
|
||||
if !ok {
|
||||
return fmt.Sprintf("LacpRate(%d)", b)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// StringToBondLacpRate returns bond lacp arte, or uknonw is the s is invalid.
|
||||
func StringToBondLacpRate(s string) BondLacpRate {
|
||||
lacp, ok := StringToBondLacpRateMap[s]
|
||||
if !ok {
|
||||
return BOND_LACP_RATE_UNKNOWN
|
||||
}
|
||||
return lacp
|
||||
}
|
||||
|
||||
// Possible BondLacpRate value
|
||||
const (
|
||||
BOND_LACP_RATE_SLOW BondLacpRate = iota
|
||||
BOND_LACP_RATE_FAST
|
||||
BOND_LACP_RATE_UNKNOWN
|
||||
)
|
||||
|
||||
var bondLacpRateToString = map[BondLacpRate]string{
|
||||
BOND_LACP_RATE_SLOW: "slow",
|
||||
BOND_LACP_RATE_FAST: "fast",
|
||||
}
|
||||
var StringToBondLacpRateMap = map[string]BondLacpRate{
|
||||
"slow": BOND_LACP_RATE_SLOW,
|
||||
"fast": BOND_LACP_RATE_FAST,
|
||||
}
|
||||
|
||||
// BondAdSelect type
|
||||
type BondAdSelect int
|
||||
|
||||
// Possible BondAdSelect value
|
||||
const (
|
||||
BOND_AD_SELECT_STABLE BondAdSelect = iota
|
||||
BOND_AD_SELECT_BANDWIDTH
|
||||
BOND_AD_SELECT_COUNT
|
||||
)
|
||||
|
||||
// BondAdInfo
|
||||
type BondAdInfo struct {
|
||||
AggregatorId int
|
||||
NumPorts int
|
||||
ActorKey int
|
||||
PartnerKey int
|
||||
PartnerMac net.HardwareAddr
|
||||
}
|
||||
|
||||
// Bond representation
|
||||
type Bond struct {
|
||||
LinkAttrs
|
||||
Mode BondMode
|
||||
ActiveSlave int
|
||||
Miimon int
|
||||
UpDelay int
|
||||
DownDelay int
|
||||
UseCarrier int
|
||||
ArpInterval int
|
||||
ArpIpTargets []net.IP
|
||||
ArpValidate BondArpValidate
|
||||
ArpAllTargets BondArpAllTargets
|
||||
Primary int
|
||||
PrimaryReselect BondPrimaryReselect
|
||||
FailOverMac BondFailOverMac
|
||||
XmitHashPolicy BondXmitHashPolicy
|
||||
ResendIgmp int
|
||||
NumPeerNotif int
|
||||
AllSlavesActive int
|
||||
MinLinks int
|
||||
LpInterval int
|
||||
PackersPerSlave int
|
||||
LacpRate BondLacpRate
|
||||
AdSelect BondAdSelect
|
||||
// looking at iproute tool AdInfo can only be retrived. It can't be set.
|
||||
AdInfo *BondAdInfo
|
||||
}
|
||||
|
||||
func NewLinkBond(atr LinkAttrs) *Bond {
|
||||
return &Bond{
|
||||
LinkAttrs: atr,
|
||||
Mode: -1,
|
||||
ActiveSlave: -1,
|
||||
Miimon: -1,
|
||||
UpDelay: -1,
|
||||
DownDelay: -1,
|
||||
UseCarrier: -1,
|
||||
ArpInterval: -1,
|
||||
ArpIpTargets: nil,
|
||||
ArpValidate: -1,
|
||||
ArpAllTargets: -1,
|
||||
Primary: -1,
|
||||
PrimaryReselect: -1,
|
||||
FailOverMac: -1,
|
||||
XmitHashPolicy: -1,
|
||||
ResendIgmp: -1,
|
||||
NumPeerNotif: -1,
|
||||
AllSlavesActive: -1,
|
||||
MinLinks: -1,
|
||||
LpInterval: -1,
|
||||
PackersPerSlave: -1,
|
||||
LacpRate: -1,
|
||||
AdSelect: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Flag mask for bond options. Bond.Flagmask must be set to on for option to work.
|
||||
const (
|
||||
BOND_MODE_MASK uint64 = 1 << (1 + iota)
|
||||
BOND_ACTIVE_SLAVE_MASK
|
||||
BOND_MIIMON_MASK
|
||||
BOND_UPDELAY_MASK
|
||||
BOND_DOWNDELAY_MASK
|
||||
BOND_USE_CARRIER_MASK
|
||||
BOND_ARP_INTERVAL_MASK
|
||||
BOND_ARP_VALIDATE_MASK
|
||||
BOND_ARP_ALL_TARGETS_MASK
|
||||
BOND_PRIMARY_MASK
|
||||
BOND_PRIMARY_RESELECT_MASK
|
||||
BOND_FAIL_OVER_MAC_MASK
|
||||
BOND_XMIT_HASH_POLICY_MASK
|
||||
BOND_RESEND_IGMP_MASK
|
||||
BOND_NUM_PEER_NOTIF_MASK
|
||||
BOND_ALL_SLAVES_ACTIVE_MASK
|
||||
BOND_MIN_LINKS_MASK
|
||||
BOND_LP_INTERVAL_MASK
|
||||
BOND_PACKETS_PER_SLAVE_MASK
|
||||
BOND_LACP_RATE_MASK
|
||||
BOND_AD_SELECT_MASK
|
||||
)
|
||||
|
||||
// Attrs implementation.
|
||||
func (bond *Bond) Attrs() *LinkAttrs {
|
||||
return &bond.LinkAttrs
|
||||
}
|
||||
|
||||
// Type implementation fro Vxlan.
|
||||
func (bond *Bond) Type() string {
|
||||
return "bond"
|
||||
}
|
||||
|
||||
// GreTap devices must specify LocalIP and RemoteIP on create
|
||||
type Gretap struct {
|
||||
LinkAttrs
|
||||
|
275
link_linux.go
275
link_linux.go
@ -151,9 +151,18 @@ func LinkSetMaster(link Link, master *Bridge) error {
|
||||
ensureIndex(masterBase)
|
||||
index = masterBase.Index
|
||||
}
|
||||
if index <= 0 {
|
||||
return fmt.Errorf("Device does not exist")
|
||||
}
|
||||
return LinkSetMasterByIndex(link, index)
|
||||
}
|
||||
|
||||
// LinkSetNoMaster removes the master of the link device.
|
||||
// Equivalent to: `ip link set $link nomaster`
|
||||
func LinkSetNoMaster(link Link) error {
|
||||
return LinkSetMasterByIndex(link, 0)
|
||||
}
|
||||
|
||||
// LinkSetMasterByIndex sets the master of the link device.
|
||||
// Equivalent to: `ip link set $link master $master`
|
||||
func LinkSetMasterByIndex(link Link, masterIndex int) error {
|
||||
@ -293,61 +302,85 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
|
||||
}
|
||||
}
|
||||
|
||||
func htonl(val uint32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(bytes, val)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func htons(val uint16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(bytes, val)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
|
||||
|
||||
func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_IKEY, htonl(gretap.Key))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_OKEY, htonl(gretap.Key))
|
||||
|
||||
ip := gretap.LocalIP.To4()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip))
|
||||
if bond.Mode >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_MODE, nl.Uint8Attr(uint8(bond.Mode)))
|
||||
}
|
||||
ip = gretap.RemoteIP.To4()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_REMOTE, []byte(ip))
|
||||
if bond.ActiveSlave >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_ACTIVE_SLAVE, nl.Uint32Attr(uint32(bond.ActiveSlave)))
|
||||
}
|
||||
if bond.Miimon >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_MIIMON, nl.Uint32Attr(uint32(bond.Miimon)))
|
||||
}
|
||||
if bond.UpDelay >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_UPDELAY, nl.Uint32Attr(uint32(bond.UpDelay)))
|
||||
}
|
||||
if bond.DownDelay >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_DOWNDELAY, nl.Uint32Attr(uint32(bond.DownDelay)))
|
||||
}
|
||||
if bond.UseCarrier >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_USE_CARRIER, nl.Uint8Attr(uint8(bond.UseCarrier)))
|
||||
}
|
||||
if bond.ArpInterval >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_INTERVAL, nl.Uint32Attr(uint32(bond.ArpInterval)))
|
||||
}
|
||||
if bond.ArpIpTargets != nil {
|
||||
msg := nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_IP_TARGET, nil)
|
||||
for i := range bond.ArpIpTargets {
|
||||
ip := bond.ArpIpTargets[i].To4()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(msg, i, []byte(ip))
|
||||
continue
|
||||
}
|
||||
ip = bond.ArpIpTargets[i].To16()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(msg, i, []byte(ip))
|
||||
}
|
||||
}
|
||||
}
|
||||
if bond.ArpValidate >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_VALIDATE, nl.Uint32Attr(uint32(bond.ArpValidate)))
|
||||
}
|
||||
if bond.ArpAllTargets >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_ALL_TARGETS, nl.Uint32Attr(uint32(bond.ArpAllTargets)))
|
||||
}
|
||||
if bond.Primary >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY, nl.Uint32Attr(uint32(bond.Primary)))
|
||||
}
|
||||
if bond.PrimaryReselect >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY_RESELECT, nl.Uint8Attr(uint8(bond.PrimaryReselect)))
|
||||
}
|
||||
if bond.FailOverMac >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_FAIL_OVER_MAC, nl.Uint8Attr(uint8(bond.FailOverMac)))
|
||||
}
|
||||
if bond.XmitHashPolicy >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_XMIT_HASH_POLICY, nl.Uint8Attr(uint8(bond.XmitHashPolicy)))
|
||||
}
|
||||
if bond.ResendIgmp >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_RESEND_IGMP, nl.Uint32Attr(uint32(bond.ResendIgmp)))
|
||||
}
|
||||
if bond.NumPeerNotif >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_NUM_PEER_NOTIF, nl.Uint8Attr(uint8(bond.NumPeerNotif)))
|
||||
}
|
||||
if bond.AllSlavesActive >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_ALL_SLAVES_ACTIVE, nl.Uint8Attr(uint8(bond.AllSlavesActive)))
|
||||
}
|
||||
if bond.MinLinks >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_MIN_LINKS, nl.Uint32Attr(uint32(bond.MinLinks)))
|
||||
}
|
||||
if bond.LpInterval >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_LP_INTERVAL, nl.Uint32Attr(uint32(bond.LpInterval)))
|
||||
}
|
||||
if bond.PackersPerSlave >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_PACKETS_PER_SLAVE, nl.Uint32Attr(uint32(bond.PackersPerSlave)))
|
||||
}
|
||||
if bond.LacpRate >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_LACP_RATE, nl.Uint8Attr(uint8(bond.LacpRate)))
|
||||
}
|
||||
if bond.AdSelect >= 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_SELECT, nl.Uint8Attr(uint8(bond.AdSelect)))
|
||||
}
|
||||
|
||||
iflags := uint16(nl.GRE_KEY)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_IFLAGS, htons(iflags))
|
||||
|
||||
oflags := uint16(nl.GRE_KEY)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_OFLAGS, htons(oflags))
|
||||
|
||||
// Use sane defaults for remaining parameters
|
||||
|
||||
//grelink := 0
|
||||
//if grelink != 0 {
|
||||
// nl.NewRtAttrChild(data, nl.IFLA_GRE_LINK, nl.Uint32Attr(uint32(grelink)))
|
||||
//}
|
||||
pmtudisc := 1
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_PMTUDISC, nl.Uint8Attr(uint8(pmtudisc)))
|
||||
ttl := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_TTL, nl.Uint8Attr(uint8(ttl)))
|
||||
tos := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_TOS, nl.Uint8Attr(uint8(tos)))
|
||||
encaptype := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_TYPE, nl.Uint16Attr(uint16(encaptype)))
|
||||
encapflags := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_FLAGS, nl.Uint16Attr(uint16(encapflags)))
|
||||
|
||||
encapsport := uint16(0)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_SPORT, htons(encapsport))
|
||||
encapdport := uint16(0)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_DPORT, htons(encapdport))
|
||||
}
|
||||
|
||||
// LinkAdd adds a new link device. The type and features of the device
|
||||
@ -403,6 +436,27 @@ func LinkAdd(link Link) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
// TODO: make it shorter
|
||||
if base.Flags&net.FlagUp != 0 {
|
||||
msg.Change = syscall.IFF_UP
|
||||
msg.Flags = syscall.IFF_UP
|
||||
}
|
||||
if base.Flags&net.FlagBroadcast != 0 {
|
||||
msg.Change |= syscall.IFF_BROADCAST
|
||||
msg.Flags |= syscall.IFF_BROADCAST
|
||||
}
|
||||
if base.Flags&net.FlagLoopback != 0 {
|
||||
msg.Change |= syscall.IFF_LOOPBACK
|
||||
msg.Flags |= syscall.IFF_LOOPBACK
|
||||
}
|
||||
if base.Flags&net.FlagPointToPoint != 0 {
|
||||
msg.Change |= syscall.IFF_POINTOPOINT
|
||||
msg.Flags |= syscall.IFF_POINTOPOINT
|
||||
}
|
||||
if base.Flags&net.FlagMulticast != 0 {
|
||||
msg.Change |= syscall.IFF_MULTICAST
|
||||
msg.Flags |= syscall.IFF_MULTICAST
|
||||
}
|
||||
req.AddData(msg)
|
||||
|
||||
if base.ParentIndex != 0 {
|
||||
@ -463,6 +517,8 @@ func LinkAdd(link Link) error {
|
||||
|
||||
} else if vxlan, ok := link.(*Vxlan); ok {
|
||||
addVxlanAttrs(vxlan, linkInfo)
|
||||
} else if bond, ok := link.(*Bond); ok {
|
||||
addBondAttrs(bond, linkInfo)
|
||||
} else if ipv, ok := link.(*IPVlan); ok {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
|
||||
@ -660,6 +716,8 @@ func linkDeserialize(m []byte) (Link, error) {
|
||||
link = &Veth{}
|
||||
case "vxlan":
|
||||
link = &Vxlan{}
|
||||
case "bond":
|
||||
link = &Bond{}
|
||||
case "ipvlan":
|
||||
link = &IPVlan{}
|
||||
case "macvlan":
|
||||
@ -681,6 +739,8 @@ func linkDeserialize(m []byte) (Link, error) {
|
||||
parseVlanData(link, data)
|
||||
case "vxlan":
|
||||
parseVxlanData(link, data)
|
||||
case "bond":
|
||||
parseBondData(link, data)
|
||||
case "ipvlan":
|
||||
parseIPVlanData(link, data)
|
||||
case "macvlan":
|
||||
@ -893,6 +953,60 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
}
|
||||
}
|
||||
|
||||
func parseBondData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
bond := NewBond(LinkAttrs{})
|
||||
for i := range data {
|
||||
switch data[i].Attr.Type {
|
||||
case nl.IFLA_BOND_MODE:
|
||||
bond.Mode = BondMode(data[i].Value[0])
|
||||
case nl.IFLA_BOND_ACTIVE_SLAVE:
|
||||
bond.ActiveSlave = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_MIIMON:
|
||||
bond.Miimon = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_UPDELAY:
|
||||
bond.UpDelay = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_DOWNDELAY:
|
||||
bond.DownDelay = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_USE_CARRIER:
|
||||
bond.UseCarrier = int(data[i].Value[0])
|
||||
case nl.IFLA_BOND_ARP_INTERVAL:
|
||||
bond.ArpInterval = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_ARP_IP_TARGET:
|
||||
// TODO: implement
|
||||
case nl.IFLA_BOND_ARP_VALIDATE:
|
||||
bond.ArpValidate = BondArpValidate(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_ARP_ALL_TARGETS:
|
||||
bond.ArpAllTargets = BondArpAllTargets(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_PRIMARY:
|
||||
bond.Primary = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_PRIMARY_RESELECT:
|
||||
bond.PrimaryReselect = BondPrimaryReselect(data[i].Value[0])
|
||||
case nl.IFLA_BOND_FAIL_OVER_MAC:
|
||||
bond.FailOverMac = BondFailOverMac(data[i].Value[0])
|
||||
case nl.IFLA_BOND_XMIT_HASH_POLICY:
|
||||
bond.XmitHashPolicy = BondXmitHashPolicy(data[i].Value[0])
|
||||
case nl.IFLA_BOND_RESEND_IGMP:
|
||||
bond.ResendIgmp = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_NUM_PEER_NOTIF:
|
||||
bond.NumPeerNotif = int(data[i].Value[0])
|
||||
case nl.IFLA_BOND_ALL_SLAVES_ACTIVE:
|
||||
bond.AllSlavesActive = int(data[i].Value[0])
|
||||
case nl.IFLA_BOND_MIN_LINKS:
|
||||
bond.MinLinks = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_LP_INTERVAL:
|
||||
bond.LpInterval = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_PACKETS_PER_SLAVE:
|
||||
bond.PackersPerSlave = int(native.Uint32(data[i].Value[0:4]))
|
||||
case nl.IFLA_BOND_AD_LACP_RATE:
|
||||
bond.LacpRate = BondLacpRate(data[i].Value[0])
|
||||
case nl.IFLA_BOND_AD_SELECT:
|
||||
bond.AdSelect = BondAdSelect(data[i].Value[0])
|
||||
case nl.IFLA_BOND_AD_INFO:
|
||||
// TODO: implement
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
ipv := link.(*IPVlan)
|
||||
for _, datum := range data {
|
||||
@ -949,3 +1063,60 @@ func linkFlags(rawFlags uint32) net.Flags {
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func htonl(val uint32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(bytes, val)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func htons(val uint16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(bytes, val)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
|
||||
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_IKEY, htonl(gretap.Key))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_OKEY, htonl(gretap.Key))
|
||||
|
||||
ip := gretap.LocalIP.To4()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip))
|
||||
}
|
||||
ip = gretap.RemoteIP.To4()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_REMOTE, []byte(ip))
|
||||
}
|
||||
|
||||
iflags := uint16(nl.GRE_KEY)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_IFLAGS, htons(iflags))
|
||||
|
||||
oflags := uint16(nl.GRE_KEY)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_OFLAGS, htons(oflags))
|
||||
|
||||
// Use sane defaults for remaining parameters
|
||||
|
||||
//grelink := 0
|
||||
//if grelink != 0 {
|
||||
// nl.NewRtAttrChild(data, nl.IFLA_GRE_LINK, nl.Uint32Attr(uint32(grelink)))
|
||||
//}
|
||||
pmtudisc := 1
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_PMTUDISC, nl.Uint8Attr(uint8(pmtudisc)))
|
||||
ttl := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_TTL, nl.Uint8Attr(uint8(ttl)))
|
||||
tos := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_TOS, nl.Uint8Attr(uint8(tos)))
|
||||
encaptype := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_TYPE, nl.Uint16Attr(uint16(encaptype)))
|
||||
encapflags := 0
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_FLAGS, nl.Uint16Attr(uint16(encapflags)))
|
||||
|
||||
encapsport := uint16(0)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_SPORT, htons(encapsport))
|
||||
encapdport := uint16(0)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_DPORT, htons(encapdport))
|
||||
}
|
||||
|
15
link_test.go
15
link_test.go
@ -273,6 +273,13 @@ func TestLinkAddDelVeth(t *testing.T) {
|
||||
testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"})
|
||||
}
|
||||
|
||||
func TestLinkAddDelBond(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
|
||||
testLinkAddDel(t, NewBond(LinkAttrs{Name: "foo"}))
|
||||
}
|
||||
|
||||
func TestLinkAddVethWithDefaultTxQLen(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
@ -400,6 +407,12 @@ func TestLinkSetUnsetResetMaster(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nonexistsmaster := &Bridge{LinkAttrs{Name: "foobar"}}
|
||||
|
||||
if err := LinkSetMaster(slave, nonexistsmaster); err == nil {
|
||||
t.Fatal("error expected")
|
||||
}
|
||||
|
||||
if err := LinkSetMaster(slave, master); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -426,7 +439,7 @@ func TestLinkSetUnsetResetMaster(t *testing.T) {
|
||||
t.Fatal("Master not reset properly")
|
||||
}
|
||||
|
||||
if err := LinkSetMaster(slave, nil); err != nil {
|
||||
if err := LinkSetNoMaster(slave); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,52 @@ const (
|
||||
MACVLAN_MODE_SOURCE = 16
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_BOND_UNSPEC = iota
|
||||
IFLA_BOND_MODE
|
||||
IFLA_BOND_ACTIVE_SLAVE
|
||||
IFLA_BOND_MIIMON
|
||||
IFLA_BOND_UPDELAY
|
||||
IFLA_BOND_DOWNDELAY
|
||||
IFLA_BOND_USE_CARRIER
|
||||
IFLA_BOND_ARP_INTERVAL
|
||||
IFLA_BOND_ARP_IP_TARGET
|
||||
IFLA_BOND_ARP_VALIDATE
|
||||
IFLA_BOND_ARP_ALL_TARGETS
|
||||
IFLA_BOND_PRIMARY
|
||||
IFLA_BOND_PRIMARY_RESELECT
|
||||
IFLA_BOND_FAIL_OVER_MAC
|
||||
IFLA_BOND_XMIT_HASH_POLICY
|
||||
IFLA_BOND_RESEND_IGMP
|
||||
IFLA_BOND_NUM_PEER_NOTIF
|
||||
IFLA_BOND_ALL_SLAVES_ACTIVE
|
||||
IFLA_BOND_MIN_LINKS
|
||||
IFLA_BOND_LP_INTERVAL
|
||||
IFLA_BOND_PACKETS_PER_SLAVE
|
||||
IFLA_BOND_AD_LACP_RATE
|
||||
IFLA_BOND_AD_SELECT
|
||||
IFLA_BOND_AD_INFO
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_BOND_AD_INFO_UNSPEC = iota
|
||||
IFLA_BOND_AD_INFO_AGGREGATOR
|
||||
IFLA_BOND_AD_INFO_NUM_PORTS
|
||||
IFLA_BOND_AD_INFO_ACTOR_KEY
|
||||
IFLA_BOND_AD_INFO_PARTNER_KEY
|
||||
IFLA_BOND_AD_INFO_PARTNER_MAC
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_BOND_SLAVE_UNSPEC = iota
|
||||
IFLA_BOND_SLAVE_STATE
|
||||
IFLA_BOND_SLAVE_MII_STATUS
|
||||
IFLA_BOND_SLAVE_LINK_FAILURE_COUNT
|
||||
IFLA_BOND_SLAVE_PERM_HWADDR
|
||||
IFLA_BOND_SLAVE_QUEUE_ID
|
||||
IFLA_BOND_SLAVE_AD_AGGREGATOR_ID
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_GRE_UNSPEC = iota
|
||||
IFLA_GRE_LINK
|
||||
|
37
nl/syscall.go
Normal file
37
nl/syscall.go
Normal file
@ -0,0 +1,37 @@
|
||||
package nl
|
||||
|
||||
// syscall package lack of rule atributes type.
|
||||
// Thus there are defined below
|
||||
const (
|
||||
FRA_UNSPEC = iota
|
||||
FRA_DST /* destination address */
|
||||
FRA_SRC /* source address */
|
||||
FRA_IIFNAME /* interface name */
|
||||
FRA_GOTO /* target to jump to (FR_ACT_GOTO) */
|
||||
FRA_UNUSED2
|
||||
FRA_PRIORITY /* priority/preference */
|
||||
FRA_UNUSED3
|
||||
FRA_UNUSED4
|
||||
FRA_UNUSED5
|
||||
FRA_FWMARK /* mark */
|
||||
FRA_FLOW /* flow/class id */
|
||||
FRA_TUN_ID
|
||||
FRA_SUPPRESS_IFGROUP
|
||||
FRA_SUPPRESS_PREFIXLEN
|
||||
FRA_TABLE /* Extended table id */
|
||||
FRA_FWMASK /* mask for netfilter mark */
|
||||
FRA_OIFNAME
|
||||
)
|
||||
|
||||
// ip rule netlink request types
|
||||
const (
|
||||
FR_ACT_UNSPEC = iota
|
||||
FR_ACT_TO_TBL /* Pass to fixed table */
|
||||
FR_ACT_GOTO /* Jump to another rule */
|
||||
FR_ACT_NOP /* No operation */
|
||||
FR_ACT_RES3
|
||||
FR_ACT_RES4
|
||||
FR_ACT_BLACKHOLE /* Drop without notification */
|
||||
FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */
|
||||
FR_ACT_PROHIBIT /* Drop with EACCES */
|
||||
)
|
23
route.go
23
route.go
@ -24,17 +24,20 @@ const (
|
||||
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
|
||||
// currently not supported.
|
||||
// Route represents a netlink route.
|
||||
type Route struct {
|
||||
LinkIndex int
|
||||
Scope Scope
|
||||
Dst *net.IPNet
|
||||
Src net.IP
|
||||
Gw net.IP
|
||||
Flags int
|
||||
LinkIndex int
|
||||
ILinkIndex int
|
||||
Scope Scope
|
||||
Dst *net.IPNet
|
||||
Src net.IP
|
||||
Gw net.IP
|
||||
Protocol int
|
||||
Priority int
|
||||
Table int
|
||||
Type int
|
||||
Tos int
|
||||
Flags int
|
||||
}
|
||||
|
||||
func (r Route) String() string {
|
||||
|
132
route_linux.go
132
route_linux.go
@ -10,6 +10,19 @@ import (
|
||||
|
||||
// RtAttr is shared so it is in netlink_linux.go
|
||||
|
||||
const (
|
||||
RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
|
||||
RT_FILTER_SCOPE
|
||||
RT_FILTER_TYPE
|
||||
RT_FILTER_TOS
|
||||
RT_FILTER_IIF
|
||||
RT_FILTER_OIF
|
||||
RT_FILTER_DST
|
||||
RT_FILTER_SRC
|
||||
RT_FILTER_GW
|
||||
RT_FILTER_TABLE
|
||||
)
|
||||
|
||||
// RouteAdd will add a route to the system.
|
||||
// Equivalent to: `ip route add $route`
|
||||
func RouteAdd(route *Route) error {
|
||||
@ -29,8 +42,6 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
|
||||
return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
|
||||
}
|
||||
|
||||
msg.Scope = uint8(route.Scope)
|
||||
msg.Flags = uint32(route.Flags)
|
||||
family := -1
|
||||
var rtAttrs []*nl.RtAttr
|
||||
|
||||
@ -79,8 +90,34 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData))
|
||||
}
|
||||
|
||||
msg.Family = uint8(family)
|
||||
if route.Table > 0 {
|
||||
if route.Table >= 256 {
|
||||
msg.Table = syscall.RT_TABLE_UNSPEC
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(route.Table))
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_TABLE, b))
|
||||
} else {
|
||||
msg.Table = uint8(route.Table)
|
||||
}
|
||||
}
|
||||
|
||||
if route.Priority > 0 {
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(route.Priority))
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PRIORITY, b))
|
||||
}
|
||||
if route.Tos > 0 {
|
||||
msg.Tos = uint8(route.Tos)
|
||||
}
|
||||
if route.Protocol > 0 {
|
||||
msg.Protocol = uint8(route.Protocol)
|
||||
}
|
||||
if route.Type > 0 {
|
||||
msg.Type = uint8(route.Type)
|
||||
}
|
||||
|
||||
msg.Scope = uint8(route.Scope)
|
||||
msg.Family = uint8(family)
|
||||
req.AddData(msg)
|
||||
for _, attr := range rtAttrs {
|
||||
req.AddData(attr)
|
||||
@ -102,61 +139,95 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
|
||||
// Equivalent to: `ip route show`.
|
||||
// The list can be filtered by link and ip family.
|
||||
func RouteList(link Link, family int) ([]Route, error) {
|
||||
var routeFilter *Route
|
||||
if link != nil {
|
||||
routeFilter = &Route{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
}
|
||||
}
|
||||
return RouteListFiltered(family, routeFilter, RT_FILTER_OIF)
|
||||
}
|
||||
|
||||
// RouteListFiltered gets a list of routes in the system filtered with specified rules.
|
||||
// All rules must be defined in RouteFilter struct
|
||||
func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
|
||||
msg := nl.NewIfInfomsg(family)
|
||||
req.AddData(msg)
|
||||
infmsg := nl.NewIfInfomsg(family)
|
||||
req.AddData(infmsg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := 0
|
||||
if link != nil {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
index = base.Index
|
||||
}
|
||||
|
||||
var res []Route
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeRtMsg(m)
|
||||
|
||||
if msg.Flags&syscall.RTM_F_CLONED != 0 {
|
||||
// Ignore cloned routes
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Table != syscall.RT_TABLE_MAIN {
|
||||
// Ignore non-main tables
|
||||
continue
|
||||
if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 {
|
||||
// Ignore non-main tables
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
route, err := deserializeRoute(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if link != nil && route.LinkIndex != index {
|
||||
// Ignore routes from other interfaces
|
||||
continue
|
||||
if filter != nil {
|
||||
switch {
|
||||
case filterMask&RT_FILTER_TABLE != 0 && route.Table != filter.Table:
|
||||
continue
|
||||
case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
|
||||
continue
|
||||
case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope:
|
||||
continue
|
||||
case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type:
|
||||
continue
|
||||
case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos:
|
||||
continue
|
||||
case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex:
|
||||
continue
|
||||
case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex:
|
||||
continue
|
||||
case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw):
|
||||
continue
|
||||
case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
|
||||
continue
|
||||
case filterMask&RT_FILTER_DST != 0 && filter.Dst != nil:
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
res = append(res, route)
|
||||
}
|
||||
|
||||
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
|
||||
return Route{}, err
|
||||
}
|
||||
route := Route{
|
||||
Scope: Scope(msg.Scope),
|
||||
Protocol: int(msg.Protocol),
|
||||
Table: int(msg.Table),
|
||||
Type: int(msg.Type),
|
||||
Tos: int(msg.Tos),
|
||||
Flags: int(msg.Flags),
|
||||
}
|
||||
route.Scope = Scope(msg.Scope)
|
||||
route.Flags = int(msg.Flags)
|
||||
|
||||
native := nl.NativeEndian()
|
||||
for _, attr := range attrs {
|
||||
@ -171,8 +242,13 @@ func deserializeRoute(m []byte) (Route, error) {
|
||||
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
|
||||
route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
|
||||
case syscall.RTA_IIF:
|
||||
route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
|
||||
case syscall.RTA_PRIORITY:
|
||||
route.Priority = int(native.Uint32(attr.Value[0:4]))
|
||||
case syscall.RTA_TABLE:
|
||||
route.Table = int(native.Uint32(attr.Value[0:4]))
|
||||
}
|
||||
}
|
||||
return route, nil
|
||||
|
102
route_test.go
102
route_test.go
@ -18,17 +18,19 @@ func TestRouteAddDel(t *testing.T) {
|
||||
}
|
||||
|
||||
// bring the interface up
|
||||
if err = LinkSetUp(link); err != nil {
|
||||
if err := LinkSetUp(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// add a gateway route
|
||||
_, dst, err := net.ParseCIDR("192.168.0.0/24")
|
||||
dst := &net.IPNet{
|
||||
IP: net.IPv4(192, 168, 0, 0),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
}
|
||||
|
||||
ip := net.ParseIP("127.1.1.1")
|
||||
ip := net.IPv4(127, 1, 1, 1)
|
||||
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
||||
err = RouteAdd(&route)
|
||||
if err != nil {
|
||||
if err := RouteAdd(&route); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
routes, err := RouteList(link, FAMILY_V4)
|
||||
@ -36,10 +38,10 @@ func TestRouteAddDel(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(routes) != 1 {
|
||||
t.Fatal("Link not added properly")
|
||||
t.Fatal("Route not added properly")
|
||||
}
|
||||
|
||||
dstIP := net.ParseIP("192.168.0.42")
|
||||
dstIP := net.IPv4(192, 168, 0, 42)
|
||||
routeToDstIP, err := RouteGet(dstIP)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -48,12 +50,9 @@ func TestRouteAddDel(t *testing.T) {
|
||||
if len(routeToDstIP) == 0 {
|
||||
t.Fatal("Default route not present")
|
||||
}
|
||||
|
||||
err = RouteDel(&route)
|
||||
if err != nil {
|
||||
if err := RouteDel(&route); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
routes, err = RouteList(link, FAMILY_V4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -122,25 +121,90 @@ func TestRouteSubscribe(t *testing.T) {
|
||||
}
|
||||
|
||||
// add a gateway route
|
||||
_, dst, err := net.ParseCIDR("192.168.0.0/24")
|
||||
dst := &net.IPNet{
|
||||
IP: net.IPv4(192, 168, 0, 0),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
}
|
||||
|
||||
ip := net.ParseIP("127.1.1.1")
|
||||
ip := net.IPv4(127, 1, 1, 1)
|
||||
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
||||
err = RouteAdd(&route)
|
||||
if err != nil {
|
||||
if err := RouteAdd(&route); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !expectRouteUpdate(ch, syscall.RTM_NEWROUTE, dst.IP) {
|
||||
t.Fatal("Add update not received as expected")
|
||||
}
|
||||
|
||||
err = RouteDel(&route)
|
||||
if err != nil {
|
||||
if err := RouteDel(&route); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !expectRouteUpdate(ch, syscall.RTM_DELROUTE, dst.IP) {
|
||||
t.Fatal("Del update not received as expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteExtraFields(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 := &net.IPNet{
|
||||
IP: net.IPv4(1, 1, 1, 1),
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
}
|
||||
|
||||
src := net.IPv4(127, 3, 3, 3)
|
||||
route := Route{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Dst: dst,
|
||||
Src: src,
|
||||
Scope: syscall.RT_SCOPE_LINK,
|
||||
Priority: 13,
|
||||
Table: syscall.RT_TABLE_MAIN,
|
||||
Type: syscall.RTN_UNICAST,
|
||||
Tos: 14,
|
||||
}
|
||||
if err := RouteAdd(&route); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
routes, err := RouteListFiltered(FAMILY_V4, &Route{
|
||||
Dst: dst,
|
||||
Src: src,
|
||||
Scope: syscall.RT_SCOPE_LINK,
|
||||
Table: syscall.RT_TABLE_MAIN,
|
||||
Type: syscall.RTN_UNICAST,
|
||||
Tos: 14,
|
||||
}, RT_FILTER_DST|RT_FILTER_SRC|RT_FILTER_SCOPE|RT_FILTER_TABLE|RT_FILTER_TYPE|RT_FILTER_TOS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(routes) != 1 {
|
||||
t.Fatal("Route not added properly")
|
||||
}
|
||||
|
||||
if routes[0].Scope != syscall.RT_SCOPE_LINK {
|
||||
t.Fatal("Invalid Scope. Route not added properly")
|
||||
}
|
||||
if routes[0].Priority != 13 {
|
||||
t.Fatal("Invalid Priority. Route not added properly")
|
||||
}
|
||||
if routes[0].Table != syscall.RT_TABLE_MAIN {
|
||||
t.Fatal("Invalid Scope. Route not added properly")
|
||||
}
|
||||
if routes[0].Type != syscall.RTN_UNICAST {
|
||||
t.Fatal("Invalid Type. Route not added properly")
|
||||
}
|
||||
if routes[0].Tos != 14 {
|
||||
t.Fatal("Invalid Tos. Route not added properly")
|
||||
}
|
||||
}
|
||||
|
43
rule.go
Normal file
43
rule.go
Normal file
@ -0,0 +1,43 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// Rule represents a netlink rule.
|
||||
type Rule struct {
|
||||
*nl.RtMsg
|
||||
Priority int
|
||||
Table int
|
||||
Mark int
|
||||
Mask int
|
||||
TunID uint
|
||||
Goto int
|
||||
Src *net.IPNet
|
||||
Dst *net.IPNet
|
||||
Flow int
|
||||
IifName string
|
||||
OifName string
|
||||
SuppressIfgroup int
|
||||
SuppressPrefixlen int
|
||||
}
|
||||
|
||||
func (r Rule) String() string {
|
||||
return fmt.Sprintf("ip rule %d: from %s table %d", r.Priority, r.Src, r.Table)
|
||||
}
|
||||
|
||||
// NewRule return empty rules.
|
||||
func NewRule() *Rule {
|
||||
return &Rule{
|
||||
SuppressIfgroup: -1,
|
||||
SuppressPrefixlen: -1,
|
||||
Priority: -1,
|
||||
Mark: -1,
|
||||
Mask: -1,
|
||||
Goto: -1,
|
||||
Flow: -1,
|
||||
}
|
||||
}
|
198
rule_linux.go
Normal file
198
rule_linux.go
Normal file
@ -0,0 +1,198 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// RuleAdd adds a rule to the system.
|
||||
// Equivalent to: ip rule add
|
||||
func RuleAdd(rule *Rule) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
return ruleHandle(rule, req)
|
||||
}
|
||||
|
||||
// RuleDel deletes a rule from the system.
|
||||
// Equivalent to: ip rule del
|
||||
func RuleDel(rule *Rule) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.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
|
||||
|
||||
var rtAttrs []*nl.RtAttr
|
||||
if rule.Dst != nil && rule.Dst.IP != nil {
|
||||
dstLen, _ := rule.Dst.Mask.Size()
|
||||
msg.Dst_len = uint8(dstLen)
|
||||
msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP))
|
||||
dstFamily = msg.Family
|
||||
var dstData []byte
|
||||
if msg.Family == syscall.AF_INET {
|
||||
dstData = rule.Dst.IP.To4()
|
||||
} else {
|
||||
dstData = rule.Dst.IP.To16()
|
||||
}
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
|
||||
}
|
||||
|
||||
if rule.Src != nil && rule.Src.IP != nil {
|
||||
msg.Family = uint8(nl.GetIPFamily(rule.Src.IP))
|
||||
if dstFamily != 0 && dstFamily != msg.Family {
|
||||
return fmt.Errorf("source and destination ip are not the same IP family")
|
||||
}
|
||||
srcLen, _ := rule.Src.Mask.Size()
|
||||
msg.Src_len = uint8(srcLen)
|
||||
var srcData []byte
|
||||
if msg.Family == syscall.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
|
||||
}
|
||||
}
|
||||
|
||||
req.AddData(msg)
|
||||
for i := range rtAttrs {
|
||||
req.AddData(rtAttrs[i])
|
||||
}
|
||||
|
||||
var (
|
||||
b = make([]byte, 4)
|
||||
native = nl.NativeEndian()
|
||||
)
|
||||
|
||||
if rule.Priority >= 0 {
|
||||
native.PutUint32(b, uint32(rule.Priority))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b))
|
||||
}
|
||||
if rule.Mark >= 0 {
|
||||
native.PutUint32(b, uint32(rule.Mark))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b))
|
||||
}
|
||||
if rule.Mask >= 0 {
|
||||
native.PutUint32(b, uint32(rule.Mask))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b))
|
||||
}
|
||||
if rule.Flow >= 0 {
|
||||
native.PutUint32(b, uint32(rule.Flow))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b))
|
||||
}
|
||||
if rule.TunID > 0 {
|
||||
native.PutUint32(b, uint32(rule.TunID))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b))
|
||||
}
|
||||
if rule.Table >= 256 {
|
||||
native.PutUint32(b, uint32(rule.Table))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b))
|
||||
}
|
||||
if msg.Table > 0 {
|
||||
if rule.SuppressPrefixlen >= 0 {
|
||||
native.PutUint32(b, uint32(rule.SuppressPrefixlen))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b))
|
||||
}
|
||||
if rule.SuppressIfgroup >= 0 {
|
||||
native.PutUint32(b, uint32(rule.SuppressIfgroup))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b))
|
||||
}
|
||||
}
|
||||
if rule.IifName != "" {
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName)))
|
||||
}
|
||||
if rule.OifName != "" {
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName)))
|
||||
}
|
||||
if rule.Goto >= 0 {
|
||||
msg.Type = nl.FR_ACT_NOP
|
||||
native.PutUint32(b, uint32(rule.Goto))
|
||||
req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
|
||||
}
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// RuleList lists rules in the system.
|
||||
// Equivalent to: ip rule list
|
||||
func RuleList(family int) ([]Rule, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST)
|
||||
msg := nl.NewIfInfomsg(family)
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWRULE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
native := nl.NativeEndian()
|
||||
var res = make([]Rule, 0)
|
||||
for i := range msgs {
|
||||
msg := nl.DeserializeRtMsg(msgs[i])
|
||||
attrs, err := nl.ParseRouteAttr(msgs[i][msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rule := NewRule()
|
||||
rule.RtMsg = msg
|
||||
|
||||
for j := range attrs {
|
||||
switch attrs[j].Attr.Type {
|
||||
case syscall.RTA_TABLE:
|
||||
rule.Table = int(native.Uint32(attrs[j].Value[0:4]))
|
||||
case nl.FRA_SRC:
|
||||
rule.Src = &net.IPNet{
|
||||
IP: attrs[j].Value,
|
||||
Mask: net.CIDRMask(int(msg.Src_len), 8*len(attrs[j].Value)),
|
||||
}
|
||||
case nl.FRA_DST:
|
||||
rule.Dst = &net.IPNet{
|
||||
IP: attrs[j].Value,
|
||||
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attrs[j].Value)),
|
||||
}
|
||||
case nl.FRA_FWMARK:
|
||||
rule.Mark = int(native.Uint32(attrs[j].Value[0:4]))
|
||||
case nl.FRA_FWMASK:
|
||||
rule.Mask = int(native.Uint32(attrs[j].Value[0:4]))
|
||||
case nl.FRA_TUN_ID:
|
||||
rule.TunID = uint(native.Uint64(attrs[j].Value[0:4]))
|
||||
case nl.FRA_IIFNAME:
|
||||
rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
|
||||
case nl.FRA_OIFNAME:
|
||||
rule.OifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
|
||||
case nl.FRA_SUPPRESS_PREFIXLEN:
|
||||
i := native.Uint32(attrs[j].Value[0:4])
|
||||
if i != 0xffffffff {
|
||||
rule.SuppressPrefixlen = int(i)
|
||||
}
|
||||
case nl.FRA_SUPPRESS_IFGROUP:
|
||||
i := native.Uint32(attrs[j].Value[0:4])
|
||||
if i != 0xffffffff {
|
||||
rule.SuppressIfgroup = int(i)
|
||||
}
|
||||
case nl.FRA_FLOW:
|
||||
rule.Flow = int(native.Uint32(attrs[j].Value[0:4]))
|
||||
case nl.FRA_GOTO:
|
||||
rule.Goto = int(native.Uint32(attrs[j].Value[0:4]))
|
||||
case nl.FRA_PRIORITY:
|
||||
rule.Priority = int(native.Uint32(attrs[j].Value[0:4]))
|
||||
}
|
||||
}
|
||||
res = append(res, *rule)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
66
rule_test.go
Normal file
66
rule_test.go
Normal file
@ -0,0 +1,66 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRuleAddDel(t *testing.T) {
|
||||
srcNet := &net.IPNet{IP: net.IPv4(172, 16, 0, 1), Mask: net.CIDRMask(16, 32)}
|
||||
dstNet := &net.IPNet{IP: net.IPv4(172, 16, 1, 1), Mask: net.CIDRMask(24, 32)}
|
||||
|
||||
rules_begin, err := RuleList(syscall.AF_INET)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule := NewRule()
|
||||
rule.Table = syscall.RT_TABLE_MAIN
|
||||
rule.Src = srcNet
|
||||
rule.Dst = dstNet
|
||||
rule.Priority = 5
|
||||
rule.OifName = "lo"
|
||||
rule.IifName = "lo"
|
||||
if err := RuleAdd(rule); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rules, err := RuleList(syscall.AF_INET)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(rules) != len(rules_begin)+1 {
|
||||
t.Fatal("Rule not added properly")
|
||||
}
|
||||
|
||||
// find this rule
|
||||
var found bool
|
||||
for i := range rules {
|
||||
if rules[i].Table == rule.Table &&
|
||||
rules[i].Src != nil && rules[i].Src.String() == srcNet.String() &&
|
||||
rules[i].Dst != nil && rules[i].Dst.String() == dstNet.String() &&
|
||||
rules[i].OifName == rule.OifName &&
|
||||
rules[i].Priority == rule.Priority &&
|
||||
rules[i].IifName == rule.IifName {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("Rule has diffrent options than one added")
|
||||
}
|
||||
|
||||
if err := RuleDel(rule); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rules_end, err := RuleList(syscall.AF_INET)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(rules_end) != len(rules_begin) {
|
||||
t.Fatal("Rule not removed properly")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user