Skip to content

Commit

Permalink
arp and vxlan support added
Browse files Browse the repository at this point in the history
  • Loading branch information
Eugene Yakubovich committed Oct 13, 2014
1 parent f87c54f commit 33e8718
Show file tree
Hide file tree
Showing 9 changed files with 656 additions and 2 deletions.
29 changes: 29 additions & 0 deletions link.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,35 @@ func (generic *Generic) Type() string {
return generic.LinkType
}

type Vxlan struct {
LinkAttrs
VxlanId int
VtepDevIndex int
SrcAddr net.IP
Group net.IP
TTL int
TOS int
Learning bool
Proxy bool
RSC bool
L2miss bool
L3miss bool
NoAge bool
Age int
Limit int
Port int
PortLow int
PortHigh int
}

func (vxlan *Vxlan) Attrs() *LinkAttrs {
return &vxlan.LinkAttrs
}

func (vxlan *Vxlan) Type() string {
return "vxlan"
}

// iproute2 supported devices;
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
// can | bridge | bond | ipoib | ip6tnl | ipip | sit |
Expand Down
127 changes: 126 additions & 1 deletion link_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package netlink

import (
"bytes"
"encoding/binary"
"fmt"
"net"
Expand Down Expand Up @@ -175,6 +176,76 @@ func LinkSetNsFd(link Link, fd int) error {
return err
}

func boolAttr(val bool) []byte {
var v uint8
if val {
v = 1
}
return nl.Uint8Attr(v)
}

type vxlanPortRange struct {
Lo, Hi uint16
}

func addVxlanAttrs(vxlan* Vxlan, linkInfo *nl.RtAttr) {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_ID, nl.Uint32Attr(uint32(vxlan.VxlanId)))
if vxlan.ParentIndex != 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LINK, nl.Uint32Attr(uint32(vxlan.VtepDevIndex)))
}
if vxlan.SrcAddr != nil {
ip := vxlan.SrcAddr.To4()
if ip != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL, []byte(ip))
} else {
ip = vxlan.SrcAddr.To16()
if ip != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL6, []byte(ip))
}
}
}
if vxlan.Group != nil {
group := vxlan.Group.To4()
if group != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP, []byte(group))
} else {
group = vxlan.Group.To16()
if group != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP6, []byte(group))
}
}
}

nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TTL, nl.Uint8Attr(uint8(vxlan.TTL)))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TOS, nl.Uint8Attr(uint8(vxlan.TOS)))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LEARNING, boolAttr(vxlan.Learning))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PROXY, boolAttr(vxlan.Proxy))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_RSC, boolAttr(vxlan.RSC))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L2MISS, boolAttr(vxlan.L2miss))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L3MISS, boolAttr(vxlan.L3miss))

if vxlan.NoAge {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
} else if vxlan.Age > 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(uint32(vxlan.Age)))
}
if vxlan.Limit > 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit)))
}
if vxlan.Port > 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT, nl.Uint16Attr(uint16(vxlan.Port)))
}
if vxlan.PortLow > 0 || vxlan.PortHigh > 0 {
pr := vxlanPortRange{ uint16(vxlan.PortLow), uint16(vxlan.PortHigh) }

buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, &pr)

nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT_RANGE, buf.Bytes())
}
}

// LinkAdd adds a new link device. The type and features of the device
// are taken fromt the parameters in the link object.
// Equivalent to: `ip link add $link`
Expand Down Expand Up @@ -217,6 +288,8 @@ func LinkAdd(link Link) error {
peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil)
nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC)
nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName))
} else if vxlan, ok := link.(*Vxlan); ok {
addVxlanAttrs(vxlan, linkInfo)
}

req.AddData(linkInfo)
Expand Down Expand Up @@ -333,6 +406,8 @@ func LinkList() ([]Link, error) {
link = &Vlan{}
case "veth":
link = &Veth{}
case "vxlan":
link = &Vxlan{}
default:
link = &Generic{LinkType: linkType}
}
Expand All @@ -344,6 +419,8 @@ func LinkList() ([]Link, error) {
switch linkType {
case "vlan":
parseVlanData(link, data, native)
case "vxlan":
parseVxlanData(link, data, native)
}
}
}
Expand Down Expand Up @@ -379,7 +456,7 @@ func LinkList() ([]Link, error) {
}

func parseVlanData(link Link, data []syscall.NetlinkRouteAttr, native binary.ByteOrder) {
vlan, _ := link.(*Vlan)
vlan := link.(*Vlan)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_VLAN_ID:
Expand All @@ -388,6 +465,54 @@ func parseVlanData(link Link, data []syscall.NetlinkRouteAttr, native binary.Byt
}
}

func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr, native binary.ByteOrder) {
vxlan := link.(*Vxlan)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_VXLAN_ID:
vxlan.VxlanId = int(native.Uint32(datum.Value[0:4]))
case nl.IFLA_VXLAN_LINK:
vxlan.VtepDevIndex = int(native.Uint32(datum.Value[0:4]))
case nl.IFLA_VXLAN_LOCAL:
vxlan.SrcAddr = net.IP(datum.Value[0:4])
case nl.IFLA_VXLAN_LOCAL6:
vxlan.SrcAddr = net.IP(datum.Value[0:16])
case nl.IFLA_VXLAN_GROUP:
vxlan.Group = net.IP(datum.Value[0:4])
case nl.IFLA_VXLAN_GROUP6:
vxlan.Group = net.IP(datum.Value[0:16])
case nl.IFLA_VXLAN_TTL:
vxlan.TTL = int(datum.Value[0])
case nl.IFLA_VXLAN_TOS:
vxlan.TOS = int(datum.Value[0])
case nl.IFLA_VXLAN_LEARNING:
vxlan.Learning = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_PROXY:
vxlan.Proxy = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_RSC:
vxlan.RSC = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_L2MISS:
vxlan.L2miss = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_L3MISS:
vxlan.L3miss = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_AGEING:
vxlan.Age = int(native.Uint32(datum.Value[0:4]))
vxlan.NoAge = vxlan.Age == 0
case nl.IFLA_VXLAN_LIMIT:
vxlan.Limit = int(native.Uint32(datum.Value[0:4]))
case nl.IFLA_VXLAN_PORT:
vxlan.Port = int(native.Uint16(datum.Value[0:2]))
case nl.IFLA_VXLAN_PORT_RANGE:
buf := bytes.NewBuffer(datum.Value[0:4])
var pr vxlanPortRange
if binary.Read(buf, binary.BigEndian, &pr) != nil {
vxlan.PortLow = int(pr.Lo)
vxlan.PortHigh = int(pr.Hi)
}
}
}
}

// copied from pkg/net_linux.go
func linkFlags(rawFlags uint32) net.Flags {
var f net.Flags
Expand Down
91 changes: 91 additions & 0 deletions link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}

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 err = LinkDel(link); err != nil {
t.Fatal(err)
}
Expand All @@ -71,6 +79,61 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}

func compareVxlan(t *testing.T, expected, actual *Vxlan) {

if actual.VxlanId != expected.VxlanId {
t.Fatalf("Vxlan.VxlanId doesn't match")
}
if expected.SrcAddr != nil && !actual.SrcAddr.Equal(expected.SrcAddr) {
t.Fatalf("Vxlan.SrcAddr doesn't match")
}
if expected.Group != nil && !actual.Group.Equal(expected.Group) {
t.Fatalf("Vxlan.Group doesn't match")
}
if expected.TTL != -1 && actual.TTL != expected.TTL {
t.Fatalf("Vxlan.TTL doesn't match")
}
if expected.TOS != -1 && actual.TOS != expected.TOS {
t.Fatalf("Vxlan.TOS doesn't match")
}
if actual.Learning != expected.Learning {
t.Fatalf("Vxlan.Learning doesn't match")
}
if actual.Proxy != expected.Proxy {
t.Fatalf("Vxlan.Proxy doesn't match")
}
if actual.RSC != expected.RSC {
t.Fatalf("Vxlan.RSC doesn't match", actual, expected)
}
if actual.L2miss != expected.L2miss {
t.Fatalf("Vxlan.L2miss doesn't match")
}
if actual.L3miss != expected.L3miss {
t.Fatalf("Vxlan.L3miss doesn't match")
}
if expected.NoAge {
if !actual.NoAge {
t.Fatalf("Vxlan.NoAge doesn't match")
}
} else if expected.Age > 0 && actual.Age != expected.Age {
t.Fatalf("Vxlan.Age doesn't match")
}
if expected.Limit > 0 && actual.Limit != expected.Limit {
t.Fatalf("Vxlan.Limit doesn't match")
}
if expected.Port > 0 && actual.Port != expected.Port {
t.Fatalf("Vxlan.Port doesn't match")
}
if expected.PortLow > 0 || expected.PortHigh > 0 {
if actual.PortLow != expected.PortLow {
t.Fatalf("Vxlan.PortLow doesn't match")
}
if actual.PortHigh != expected.PortHigh {
t.Fatalf("Vxlan.PortHigh doesn't match")
}
}
}

func TestLinkAddDelDummy(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
Expand Down Expand Up @@ -270,3 +333,31 @@ func TestLinkSetNs(t *testing.T) {
}

}

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)
}
}
22 changes: 22 additions & 0 deletions neigh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package netlink

import (
"fmt"
"net"
)

// Neigh represents a link layer neighbor from netlink.
type Neigh struct {
Link Link
Family int
State int
Type int
Flags int
IP net.IP
HardwareAddr net.HardwareAddr
}

// String returns $ip/$hwaddr $label
func (neigh *Neigh) String() string {
return fmt.Sprintf("%s %s", neigh.IP, neigh.HardwareAddr)
}
Loading

0 comments on commit 33e8718

Please sign in to comment.