mirror of https://github.com/vishvananda/netlink
support MPLS
$ ip -M route add 100 dev eth0 $ ip -M route add 100 as to 200/300 dev eth0 $ ip -M route add 100 nexthop dev eth0 as to 200 \ nexthop dev eth1 as to 300 $ ip route add 10.10.0.0/24 encap mpls 200/300 dev eth0 $ ip route add 10.0.0.0/24 nexthop encap mpls 200 dev eth0 \ nexthop encap mpls 300 dev eth1 Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
This commit is contained in:
parent
dedc638a06
commit
fe3b5664d2
|
@ -4,7 +4,8 @@ import "github.com/vishvananda/netlink/nl"
|
||||||
|
|
||||||
// Family type definitions
|
// Family type definitions
|
||||||
const (
|
const (
|
||||||
FAMILY_ALL = nl.FAMILY_ALL
|
FAMILY_ALL = nl.FAMILY_ALL
|
||||||
FAMILY_V4 = nl.FAMILY_V4
|
FAMILY_V4 = nl.FAMILY_V4
|
||||||
FAMILY_V6 = nl.FAMILY_V6
|
FAMILY_V6 = nl.FAMILY_V6
|
||||||
|
FAMILY_MPLS = nl.FAMILY_MPLS
|
||||||
)
|
)
|
||||||
|
|
|
@ -36,3 +36,23 @@ func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest {
|
||||||
runtime.UnlockOSThread()
|
runtime.UnlockOSThread()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setUpMPLSNetlinkTest(t *testing.T) tearDownNetlinkTest {
|
||||||
|
if _, err := os.Stat("/proc/sys/net/mpls/platform_labels"); err != nil {
|
||||||
|
msg := "Skipped test because it requires MPLS support."
|
||||||
|
log.Printf(msg)
|
||||||
|
t.Skip(msg)
|
||||||
|
}
|
||||||
|
f := setUpNetlinkTest(t)
|
||||||
|
setUpF := func(path, value string) {
|
||||||
|
file, err := os.Create(path)
|
||||||
|
defer file.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to open %s: %s", path, err)
|
||||||
|
}
|
||||||
|
file.WriteString(value)
|
||||||
|
}
|
||||||
|
setUpF("/proc/sys/net/mpls/platform_labels", "1024")
|
||||||
|
setUpF("/proc/sys/net/mpls/conf/lo/input", "1")
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package nl
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
const (
|
||||||
|
MPLS_LS_LABEL_SHIFT = 12
|
||||||
|
MPLS_LS_S_SHIFT = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
func EncodeMPLSStack(labels ...int) []byte {
|
||||||
|
b := make([]byte, 4*len(labels))
|
||||||
|
for idx, label := range labels {
|
||||||
|
l := label << MPLS_LS_LABEL_SHIFT
|
||||||
|
if idx == len(labels)-1 {
|
||||||
|
l |= 1 << MPLS_LS_S_SHIFT
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint32(b[idx*4:], uint32(l))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeMPLSStack(buf []byte) []int {
|
||||||
|
if len(buf)%4 != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
stack := make([]int, 0, len(buf)/4)
|
||||||
|
for len(buf) > 0 {
|
||||||
|
l := binary.BigEndian.Uint32(buf[:4])
|
||||||
|
buf = buf[4:]
|
||||||
|
stack = append(stack, int(l)>>MPLS_LS_LABEL_SHIFT)
|
||||||
|
if (l>>MPLS_LS_S_SHIFT)&1 > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack
|
||||||
|
}
|
|
@ -17,9 +17,10 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Family type definitions
|
// Family type definitions
|
||||||
FAMILY_ALL = syscall.AF_UNSPEC
|
FAMILY_ALL = syscall.AF_UNSPEC
|
||||||
FAMILY_V4 = syscall.AF_INET
|
FAMILY_V4 = syscall.AF_INET
|
||||||
FAMILY_V6 = syscall.AF_INET6
|
FAMILY_V6 = syscall.AF_INET6
|
||||||
|
FAMILY_MPLS = AF_MPLS
|
||||||
)
|
)
|
||||||
|
|
||||||
// SupportedNlFamilies contains the list of netlink families this netlink package supports
|
// SupportedNlFamilies contains the list of netlink families this netlink package supports
|
||||||
|
|
|
@ -43,12 +43,38 @@ func (msg *RtMsg) Serialize() []byte {
|
||||||
|
|
||||||
type RtNexthop struct {
|
type RtNexthop struct {
|
||||||
syscall.RtNexthop
|
syscall.RtNexthop
|
||||||
|
Children []NetlinkRequestData
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeserializeRtNexthop(b []byte) *RtNexthop {
|
func DeserializeRtNexthop(b []byte) *RtNexthop {
|
||||||
return (*RtNexthop)(unsafe.Pointer(&b[0:syscall.SizeofRtNexthop][0]))
|
return (*RtNexthop)(unsafe.Pointer(&b[0:syscall.SizeofRtNexthop][0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *RtNexthop) Serialize() []byte {
|
func (msg *RtNexthop) Len() int {
|
||||||
return (*(*[syscall.SizeofRtNexthop]byte)(unsafe.Pointer(msg)))[:]
|
if len(msg.Children) == 0 {
|
||||||
|
return syscall.SizeofRtNexthop
|
||||||
|
}
|
||||||
|
|
||||||
|
l := 0
|
||||||
|
for _, child := range msg.Children {
|
||||||
|
l += rtaAlignOf(child.Len())
|
||||||
|
}
|
||||||
|
l += syscall.SizeofRtNexthop
|
||||||
|
return rtaAlignOf(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *RtNexthop) Serialize() []byte {
|
||||||
|
length := msg.Len()
|
||||||
|
msg.RtNexthop.Len = uint16(length)
|
||||||
|
buf := make([]byte, length)
|
||||||
|
copy(buf, (*(*[syscall.SizeofRtNexthop]byte)(unsafe.Pointer(msg)))[:])
|
||||||
|
next := rtaAlignOf(syscall.SizeofRtNexthop)
|
||||||
|
if len(msg.Children) > 0 {
|
||||||
|
for _, child := range msg.Children {
|
||||||
|
childBuf := child.Serialize()
|
||||||
|
copy(buf[next:], childBuf)
|
||||||
|
next += rtaAlignOf(len(childBuf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,3 +41,28 @@ const (
|
||||||
SOCK_DIAG_BY_FAMILY = 20 /* linux.sock_diag.h */
|
SOCK_DIAG_BY_FAMILY = 20 /* linux.sock_diag.h */
|
||||||
TCPDIAG_NOCOOKIE = 0xFFFFFFFF /* TCPDIAG_NOCOOKIE in net/ipv4/tcp_diag.h*/
|
TCPDIAG_NOCOOKIE = 0xFFFFFFFF /* TCPDIAG_NOCOOKIE in net/ipv4/tcp_diag.h*/
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AF_MPLS = 28
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RTA_NEWDST = 0x13
|
||||||
|
RTA_ENCAP_TYPE = 0x15
|
||||||
|
RTA_ENCAP = 0x16
|
||||||
|
)
|
||||||
|
|
||||||
|
// RTA_ENCAP subtype
|
||||||
|
const (
|
||||||
|
MPLS_IPTUNNEL_UNSPEC = iota
|
||||||
|
MPLS_IPTUNNEL_DST
|
||||||
|
)
|
||||||
|
|
||||||
|
// light weight tunnel encap types
|
||||||
|
const (
|
||||||
|
LWTUNNEL_ENCAP_NONE = iota
|
||||||
|
LWTUNNEL_ENCAP_MPLS
|
||||||
|
LWTUNNEL_ENCAP_IP
|
||||||
|
LWTUNNEL_ENCAP_ILA
|
||||||
|
LWTUNNEL_ENCAP_IP6
|
||||||
|
)
|
||||||
|
|
61
route.go
61
route.go
|
@ -3,6 +3,7 @@ package netlink
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scope is an enum representing a route scope.
|
// Scope is an enum representing a route scope.
|
||||||
|
@ -10,6 +11,20 @@ type Scope uint8
|
||||||
|
|
||||||
type NextHopFlag int
|
type NextHopFlag int
|
||||||
|
|
||||||
|
type Destination interface {
|
||||||
|
Family() int
|
||||||
|
Decode([]byte) error
|
||||||
|
Encode() ([]byte, error)
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encap interface {
|
||||||
|
Type() int
|
||||||
|
Decode([]byte) error
|
||||||
|
Encode() ([]byte, error)
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
// Route represents a netlink route.
|
// Route represents a netlink route.
|
||||||
type Route struct {
|
type Route struct {
|
||||||
LinkIndex int
|
LinkIndex int
|
||||||
|
@ -25,15 +40,36 @@ type Route struct {
|
||||||
Type int
|
Type int
|
||||||
Tos int
|
Tos int
|
||||||
Flags int
|
Flags int
|
||||||
|
MPLSDst *int
|
||||||
|
NewDst Destination
|
||||||
|
Encap Encap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Route) String() string {
|
func (r Route) String() string {
|
||||||
if len(r.MultiPath) > 0 {
|
elems := []string{}
|
||||||
return fmt.Sprintf("{Dst: %s Src: %s Gw: %s Flags: %s Table: %d}", r.Dst,
|
if len(r.MultiPath) == 0 {
|
||||||
r.Src, r.MultiPath, r.ListFlags(), r.Table)
|
elems = append(elems, fmt.Sprintf("Ifindex: %d", r.LinkIndex))
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s Flags: %s Table: %d}", r.LinkIndex, r.Dst,
|
if r.MPLSDst != nil {
|
||||||
r.Src, r.Gw, r.ListFlags(), r.Table)
|
elems = append(elems, fmt.Sprintf("Dst: %d", r.MPLSDst))
|
||||||
|
} else {
|
||||||
|
elems = append(elems, fmt.Sprintf("Dst: %s", r.Dst))
|
||||||
|
}
|
||||||
|
if r.NewDst != nil {
|
||||||
|
elems = append(elems, fmt.Sprintf("NewDst: %s", r.NewDst))
|
||||||
|
}
|
||||||
|
if r.Encap != nil {
|
||||||
|
elems = append(elems, fmt.Sprintf("Encap: %s", r.Encap))
|
||||||
|
}
|
||||||
|
elems = append(elems, fmt.Sprintf("Src: %s", r.Src))
|
||||||
|
if len(r.MultiPath) > 0 {
|
||||||
|
elems = append(elems, fmt.Sprintf("Gw: %s", r.MultiPath))
|
||||||
|
} else {
|
||||||
|
elems = append(elems, fmt.Sprintf("Gw: %s", r.Gw))
|
||||||
|
}
|
||||||
|
elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags()))
|
||||||
|
elems = append(elems, fmt.Sprintf("Table: %d", r.Table))
|
||||||
|
return fmt.Sprintf("{%s}", strings.Join(elems, " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Route) SetFlag(flag NextHopFlag) {
|
func (r *Route) SetFlag(flag NextHopFlag) {
|
||||||
|
@ -60,8 +96,21 @@ type NexthopInfo struct {
|
||||||
Hops int
|
Hops int
|
||||||
Gw net.IP
|
Gw net.IP
|
||||||
Flags int
|
Flags int
|
||||||
|
NewDst Destination
|
||||||
|
Encap Encap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NexthopInfo) String() string {
|
func (n *NexthopInfo) String() string {
|
||||||
return fmt.Sprintf("{Ifindex: %d Weight: %d Gw: %s Flags: %s}", n.LinkIndex, n.Hops+1, n.Gw, n.ListFlags())
|
elems := []string{}
|
||||||
|
elems = append(elems, fmt.Sprintf("Ifindex: %d", n.LinkIndex))
|
||||||
|
if n.NewDst != nil {
|
||||||
|
elems = append(elems, fmt.Sprintf("NewDst: %s", n.NewDst))
|
||||||
|
}
|
||||||
|
if n.Encap != nil {
|
||||||
|
elems = append(elems, fmt.Sprintf("Encap: %s", n.Encap))
|
||||||
|
}
|
||||||
|
elems = append(elems, fmt.Sprintf("Weight: %d", n.Hops+1))
|
||||||
|
elems = append(elems, fmt.Sprintf("Gw: %d", n.Gw))
|
||||||
|
elems = append(elems, fmt.Sprintf("Flags: %s", n.ListFlags()))
|
||||||
|
return fmt.Sprintf("{%s}", strings.Join(elems, " "))
|
||||||
}
|
}
|
||||||
|
|
228
route_linux.go
228
route_linux.go
|
@ -3,6 +3,7 @@ package netlink
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink/nl"
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
@ -60,6 +61,74 @@ func (n *NexthopInfo) ListFlags() []string {
|
||||||
return listFlags(n.Flags)
|
return listFlags(n.Flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MPLSDestination struct {
|
||||||
|
Labels []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MPLSDestination) Family() int {
|
||||||
|
return nl.FAMILY_MPLS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MPLSDestination) Decode(buf []byte) error {
|
||||||
|
d.Labels = nl.DecodeMPLSStack(buf)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MPLSDestination) Encode() ([]byte, error) {
|
||||||
|
return nl.EncodeMPLSStack(d.Labels...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MPLSDestination) String() string {
|
||||||
|
s := make([]string, 0, len(d.Labels))
|
||||||
|
for _, l := range d.Labels {
|
||||||
|
s = append(s, fmt.Sprintf("%d", l))
|
||||||
|
}
|
||||||
|
return strings.Join(s, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
type MPLSEncap struct {
|
||||||
|
Labels []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MPLSEncap) Type() int {
|
||||||
|
return nl.LWTUNNEL_ENCAP_MPLS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MPLSEncap) Decode(buf []byte) error {
|
||||||
|
if len(buf) < 4 {
|
||||||
|
return fmt.Errorf("Lack of bytes")
|
||||||
|
}
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
l := native.Uint16(buf)
|
||||||
|
if len(buf) < int(l) {
|
||||||
|
return fmt.Errorf("Lack of bytes")
|
||||||
|
}
|
||||||
|
buf = buf[:l]
|
||||||
|
typ := native.Uint16(buf[2:])
|
||||||
|
if typ != nl.MPLS_IPTUNNEL_DST {
|
||||||
|
return fmt.Errorf("Unknown MPLS Encap Type: %d", typ)
|
||||||
|
}
|
||||||
|
e.Labels = nl.DecodeMPLSStack(buf[4:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MPLSEncap) Encode() ([]byte, error) {
|
||||||
|
s := nl.EncodeMPLSStack(e.Labels...)
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
hdr := make([]byte, 4)
|
||||||
|
native.PutUint16(hdr, uint16(len(s)+4))
|
||||||
|
native.PutUint16(hdr[2:], nl.MPLS_IPTUNNEL_DST)
|
||||||
|
return append(hdr, s...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MPLSEncap) String() string {
|
||||||
|
s := make([]string, 0, len(e.Labels))
|
||||||
|
for _, l := range e.Labels {
|
||||||
|
s = append(s, fmt.Sprintf("%d", l))
|
||||||
|
}
|
||||||
|
return strings.Join(s, "/")
|
||||||
|
}
|
||||||
|
|
||||||
// RouteAdd will add a route to the system.
|
// RouteAdd will add a route to the system.
|
||||||
// Equivalent to: `ip route add $route`
|
// Equivalent to: `ip route add $route`
|
||||||
func RouteAdd(route *Route) error {
|
func RouteAdd(route *Route) error {
|
||||||
|
@ -102,7 +171,7 @@ func (h *Handle) RouteDel(route *Route) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
|
func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
|
||||||
if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil {
|
if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil && route.MPLSDst == nil {
|
||||||
return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
|
return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +190,33 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
|
||||||
dstData = route.Dst.IP.To16()
|
dstData = route.Dst.IP.To16()
|
||||||
}
|
}
|
||||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
|
||||||
|
} else if route.MPLSDst != nil {
|
||||||
|
family = nl.FAMILY_MPLS
|
||||||
|
msg.Dst_len = uint8(20)
|
||||||
|
msg.Type = syscall.RTN_UNICAST
|
||||||
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, nl.EncodeMPLSStack(*route.MPLSDst)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if route.NewDst != nil {
|
||||||
|
if family != -1 && family != route.NewDst.Family() {
|
||||||
|
return fmt.Errorf("new destination and destination are not the same address family")
|
||||||
|
}
|
||||||
|
buf, err := route.NewDst.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_NEWDST, buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
if route.Encap != nil {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
native.PutUint16(buf, uint16(route.Encap.Type()))
|
||||||
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_ENCAP_TYPE, buf))
|
||||||
|
buf, err := route.Encap.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_ENCAP, buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
if route.Src != nil {
|
if route.Src != nil {
|
||||||
|
@ -161,27 +257,43 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
|
||||||
RtNexthop: syscall.RtNexthop{
|
RtNexthop: syscall.RtNexthop{
|
||||||
Hops: uint8(nh.Hops),
|
Hops: uint8(nh.Hops),
|
||||||
Ifindex: int32(nh.LinkIndex),
|
Ifindex: int32(nh.LinkIndex),
|
||||||
Len: uint16(syscall.SizeofRtNexthop),
|
|
||||||
Flags: uint8(nh.Flags),
|
Flags: uint8(nh.Flags),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
var gwData []byte
|
children := []nl.NetlinkRequestData{}
|
||||||
if nh.Gw != nil {
|
if nh.Gw != nil {
|
||||||
gwFamily := nl.GetIPFamily(nh.Gw)
|
gwFamily := nl.GetIPFamily(nh.Gw)
|
||||||
if family != -1 && family != gwFamily {
|
if family != -1 && family != gwFamily {
|
||||||
return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
|
return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
|
||||||
}
|
}
|
||||||
var gw *nl.RtAttr
|
|
||||||
if gwFamily == FAMILY_V4 {
|
if gwFamily == FAMILY_V4 {
|
||||||
gw = nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To4()))
|
children = append(children, nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To4())))
|
||||||
} else {
|
} else {
|
||||||
gw = nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To16()))
|
children = append(children, nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To16())))
|
||||||
}
|
}
|
||||||
gwData = gw.Serialize()
|
|
||||||
rtnh.Len += uint16(len(gwData))
|
|
||||||
}
|
}
|
||||||
|
if nh.NewDst != nil {
|
||||||
|
if family != -1 && family != nh.NewDst.Family() {
|
||||||
|
return fmt.Errorf("new destination and destination are not the same address family")
|
||||||
|
}
|
||||||
|
buf, err := nh.NewDst.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
children = append(children, nl.NewRtAttr(nl.RTA_NEWDST, buf))
|
||||||
|
}
|
||||||
|
if nh.Encap != nil {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
native.PutUint16(buf, uint16(nh.Encap.Type()))
|
||||||
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_ENCAP_TYPE, buf))
|
||||||
|
buf, err := nh.Encap.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
children = append(children, nl.NewRtAttr(nl.RTA_ENCAP, buf))
|
||||||
|
}
|
||||||
|
rtnh.Children = children
|
||||||
buf = append(buf, rtnh.Serialize()...)
|
buf = append(buf, rtnh.Serialize()...)
|
||||||
buf = append(buf, gwData...)
|
|
||||||
}
|
}
|
||||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_MULTIPATH, buf))
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_MULTIPATH, buf))
|
||||||
}
|
}
|
||||||
|
@ -308,18 +420,20 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
|
||||||
case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
|
case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
|
||||||
continue
|
continue
|
||||||
case filterMask&RT_FILTER_DST != 0:
|
case filterMask&RT_FILTER_DST != 0:
|
||||||
if filter.Dst == nil {
|
if filter.MPLSDst == nil || route.MPLSDst == nil || (*filter.MPLSDst) != (*route.MPLSDst) {
|
||||||
if route.Dst != nil {
|
if filter.Dst == nil {
|
||||||
continue
|
if route.Dst != nil {
|
||||||
}
|
continue
|
||||||
} else {
|
}
|
||||||
if route.Dst == nil {
|
} else {
|
||||||
continue
|
if route.Dst == nil {
|
||||||
}
|
continue
|
||||||
aMaskLen, aMaskBits := route.Dst.Mask.Size()
|
}
|
||||||
bMaskLen, bMaskBits := filter.Dst.Mask.Size()
|
aMaskLen, aMaskBits := route.Dst.Mask.Size()
|
||||||
if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) {
|
bMaskLen, bMaskBits := filter.Dst.Mask.Size()
|
||||||
continue
|
if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,6 +460,7 @@ func deserializeRoute(m []byte) (Route, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
native := nl.NativeEndian()
|
native := nl.NativeEndian()
|
||||||
|
var encap, encapType syscall.NetlinkRouteAttr
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
switch attr.Attr.Type {
|
switch attr.Attr.Type {
|
||||||
case syscall.RTA_GATEWAY:
|
case syscall.RTA_GATEWAY:
|
||||||
|
@ -353,9 +468,17 @@ func deserializeRoute(m []byte) (Route, error) {
|
||||||
case syscall.RTA_PREFSRC:
|
case syscall.RTA_PREFSRC:
|
||||||
route.Src = net.IP(attr.Value)
|
route.Src = net.IP(attr.Value)
|
||||||
case syscall.RTA_DST:
|
case syscall.RTA_DST:
|
||||||
route.Dst = &net.IPNet{
|
if msg.Family == nl.FAMILY_MPLS {
|
||||||
IP: attr.Value,
|
stack := nl.DecodeMPLSStack(attr.Value)
|
||||||
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
|
if len(stack) == 0 || len(stack) > 1 {
|
||||||
|
return route, fmt.Errorf("invalid MPLS RTA_DST")
|
||||||
|
}
|
||||||
|
route.MPLSDst = &stack[0]
|
||||||
|
} else {
|
||||||
|
route.Dst = &net.IPNet{
|
||||||
|
IP: attr.Value,
|
||||||
|
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case syscall.RTA_OIF:
|
case syscall.RTA_OIF:
|
||||||
route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
|
route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
|
||||||
|
@ -383,12 +506,41 @@ func deserializeRoute(m []byte) (Route, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
var encap, encapType syscall.NetlinkRouteAttr
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
switch attr.Attr.Type {
|
switch attr.Attr.Type {
|
||||||
case syscall.RTA_GATEWAY:
|
case syscall.RTA_GATEWAY:
|
||||||
info.Gw = net.IP(attr.Value)
|
info.Gw = net.IP(attr.Value)
|
||||||
|
case nl.RTA_NEWDST:
|
||||||
|
var d Destination
|
||||||
|
switch msg.Family {
|
||||||
|
case nl.FAMILY_MPLS:
|
||||||
|
d = &MPLSDestination{}
|
||||||
|
}
|
||||||
|
if err := d.Decode(attr.Value); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
info.NewDst = d
|
||||||
|
case nl.RTA_ENCAP_TYPE:
|
||||||
|
encapType = attr
|
||||||
|
case nl.RTA_ENCAP:
|
||||||
|
encap = attr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(encap.Value) != 0 && len(encapType.Value) != 0 {
|
||||||
|
typ := int(native.Uint16(encapType.Value[0:2]))
|
||||||
|
var e Encap
|
||||||
|
switch typ {
|
||||||
|
case nl.LWTUNNEL_ENCAP_MPLS:
|
||||||
|
e = &MPLSEncap{}
|
||||||
|
if err := e.Decode(encap.Value); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.Encap = e
|
||||||
|
}
|
||||||
|
|
||||||
return info, value[int(nh.RtNexthop.Len):], nil
|
return info, value[int(nh.RtNexthop.Len):], nil
|
||||||
}
|
}
|
||||||
rest := attr.Value
|
rest := attr.Value
|
||||||
|
@ -400,8 +552,36 @@ func deserializeRoute(m []byte) (Route, error) {
|
||||||
route.MultiPath = append(route.MultiPath, info)
|
route.MultiPath = append(route.MultiPath, info)
|
||||||
rest = buf
|
rest = buf
|
||||||
}
|
}
|
||||||
|
case nl.RTA_NEWDST:
|
||||||
|
var d Destination
|
||||||
|
switch msg.Family {
|
||||||
|
case nl.FAMILY_MPLS:
|
||||||
|
d = &MPLSDestination{}
|
||||||
|
}
|
||||||
|
if err := d.Decode(attr.Value); err != nil {
|
||||||
|
return route, err
|
||||||
|
}
|
||||||
|
route.NewDst = d
|
||||||
|
case nl.RTA_ENCAP_TYPE:
|
||||||
|
encapType = attr
|
||||||
|
case nl.RTA_ENCAP:
|
||||||
|
encap = attr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(encap.Value) != 0 && len(encapType.Value) != 0 {
|
||||||
|
typ := int(native.Uint16(encapType.Value[0:2]))
|
||||||
|
var e Encap
|
||||||
|
switch typ {
|
||||||
|
case nl.LWTUNNEL_ENCAP_MPLS:
|
||||||
|
e = &MPLSEncap{}
|
||||||
|
if err := e.Decode(encap.Value); err != nil {
|
||||||
|
return route, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
route.Encap = e
|
||||||
|
}
|
||||||
|
|
||||||
return route, nil
|
return route, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,3 +451,50 @@ func TestFilterDefaultRoute(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMPLSRouteAddDel(t *testing.T) {
|
||||||
|
tearDown := setUpMPLSNetlinkTest(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
mplsDst := 100
|
||||||
|
route := Route{
|
||||||
|
LinkIndex: link.Attrs().Index,
|
||||||
|
MPLSDst: &mplsDst,
|
||||||
|
NewDst: &MPLSDestination{
|
||||||
|
Labels: []int{200, 300},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := RouteAdd(&route); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
routes, err := RouteList(link, FAMILY_MPLS)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(routes) != 1 {
|
||||||
|
t.Fatal("Route not added properly")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := RouteDel(&route); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
routes, err = RouteList(link, FAMILY_MPLS)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(routes) != 0 {
|
||||||
|
t.Fatal("Route not removed properly")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue