mirror of https://github.com/vishvananda/netlink
Support LWTUNNEL_ENCAP_SEG6_LOCAL (including tests)
This commit is contained in:
parent
b7f066956c
commit
16769db002
|
@ -99,6 +99,49 @@ func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) {
|
|||
return mode, srh.Segments, nil
|
||||
}
|
||||
|
||||
func DecodeSEG6Srh(buf []byte) ([]net.IP, error) {
|
||||
native := NativeEndian()
|
||||
srh := IPv6SrHdr{
|
||||
nextHdr: buf[0],
|
||||
hdrLen: buf[1],
|
||||
routingType: buf[2],
|
||||
segmentsLeft: buf[3],
|
||||
firstSegment: buf[4],
|
||||
flags: buf[5],
|
||||
reserved: native.Uint16(buf[6:8]),
|
||||
}
|
||||
buf = buf[8:]
|
||||
if len(buf)%16 != 0 {
|
||||
err := fmt.Errorf("DecodeSEG6Srh: error parsing Segment List (buf len: %d)\n", len(buf))
|
||||
return nil, err
|
||||
}
|
||||
for len(buf) > 0 {
|
||||
srh.Segments = append(srh.Segments, net.IP(buf[:16]))
|
||||
buf = buf[16:]
|
||||
}
|
||||
return srh.Segments, nil
|
||||
}
|
||||
func EncodeSEG6Srh(segments []net.IP) ([]byte, error) {
|
||||
nsegs := len(segments) // nsegs: number of segments
|
||||
if nsegs == 0 {
|
||||
return nil, errors.New("EncodeSEG6Srh: No Segments\n")
|
||||
}
|
||||
b := make([]byte, 8, 8+len(segments)*16)
|
||||
native := NativeEndian()
|
||||
b[0] = 0 // srh.nextHdr (0 when calling netlink)
|
||||
b[1] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit)
|
||||
b[2] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA)
|
||||
b[3] = uint8(nsegs - 1) // srh.segmentsLeft
|
||||
b[4] = uint8(nsegs - 1) // srh.firstSegment
|
||||
b[5] = 0 // srh.flags (SR6_FLAG1_HMAC for srh_hmac)
|
||||
// srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07
|
||||
native.PutUint16(b[6:], 0) // srh.reserved
|
||||
for _, netIP := range segments {
|
||||
b = append(b, netIP...) // srh.Segments
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func SEG6EncapModeString(mode int) string {
|
||||
switch mode {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package nl
|
||||
|
||||
import ()
|
||||
|
||||
// seg6local parameters
|
||||
const (
|
||||
SEG6_LOCAL_UNSPEC = iota
|
||||
SEG6_LOCAL_ACTION
|
||||
SEG6_LOCAL_SRH
|
||||
SEG6_LOCAL_TABLE
|
||||
SEG6_LOCAL_NH4
|
||||
SEG6_LOCAL_NH6
|
||||
SEG6_LOCAL_IIF
|
||||
SEG6_LOCAL_OIF
|
||||
__SEG6_LOCAL_MAX
|
||||
)
|
||||
const (
|
||||
SEG6_LOCAL_MAX = __SEG6_LOCAL_MAX
|
||||
)
|
||||
|
||||
// seg6local actions
|
||||
const (
|
||||
SEG6_LOCAL_ACTION_END = iota + 1 // 1
|
||||
SEG6_LOCAL_ACTION_END_X // 2
|
||||
SEG6_LOCAL_ACTION_END_T // 3
|
||||
SEG6_LOCAL_ACTION_END_DX2 // 4
|
||||
SEG6_LOCAL_ACTION_END_DX6 // 5
|
||||
SEG6_LOCAL_ACTION_END_DX4 // 6
|
||||
SEG6_LOCAL_ACTION_END_DT6 // 7
|
||||
SEG6_LOCAL_ACTION_END_DT4 // 8
|
||||
SEG6_LOCAL_ACTION_END_B6 // 9
|
||||
SEG6_LOCAL_ACTION_END_B6_ENCAPS // 10
|
||||
SEG6_LOCAL_ACTION_END_BM // 11
|
||||
SEG6_LOCAL_ACTION_END_S // 12
|
||||
SEG6_LOCAL_ACTION_END_AS // 13
|
||||
SEG6_LOCAL_ACTION_END_AM // 14
|
||||
__SEG6_LOCAL_ACTION_MAX
|
||||
)
|
||||
const (
|
||||
SEG6_LOCAL_ACTION_MAX = __SEG6_LOCAL_ACTION_MAX - 1
|
||||
)
|
||||
|
||||
// Helper functions
|
||||
func SEG6LocalActionString(action int) string {
|
||||
switch action {
|
||||
case SEG6_LOCAL_ACTION_END:
|
||||
return "End"
|
||||
case SEG6_LOCAL_ACTION_END_X:
|
||||
return "End.X"
|
||||
case SEG6_LOCAL_ACTION_END_T:
|
||||
return "End.T"
|
||||
case SEG6_LOCAL_ACTION_END_DX2:
|
||||
return "End.DX2"
|
||||
case SEG6_LOCAL_ACTION_END_DX6:
|
||||
return "End.DX6"
|
||||
case SEG6_LOCAL_ACTION_END_DX4:
|
||||
return "End.DX4"
|
||||
case SEG6_LOCAL_ACTION_END_DT6:
|
||||
return "End.DT6"
|
||||
case SEG6_LOCAL_ACTION_END_DT4:
|
||||
return "End.DT4"
|
||||
case SEG6_LOCAL_ACTION_END_B6:
|
||||
return "End.B6"
|
||||
case SEG6_LOCAL_ACTION_END_B6_ENCAPS:
|
||||
return "End.B6.Encaps"
|
||||
case SEG6_LOCAL_ACTION_END_BM:
|
||||
return "End.BM"
|
||||
case SEG6_LOCAL_ACTION_END_S:
|
||||
return "End.S"
|
||||
case SEG6_LOCAL_ACTION_END_AS:
|
||||
return "End.AS"
|
||||
case SEG6_LOCAL_ACTION_END_AM:
|
||||
return "End.AM"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
|
@ -67,6 +67,7 @@ const (
|
|||
LWTUNNEL_ENCAP_IP6
|
||||
LWTUNNEL_ENCAP_SEG6
|
||||
LWTUNNEL_ENCAP_BPF
|
||||
LWTUNNEL_ENCAP_SEG6_LOCAL
|
||||
)
|
||||
|
||||
// routing header types
|
||||
|
|
188
route_linux.go
188
route_linux.go
|
@ -207,6 +207,7 @@ func (e *SEG6Encap) Decode(buf []byte) error {
|
|||
}
|
||||
buf = buf[:l] // make sure buf size upper limit is Length
|
||||
typ := native.Uint16(buf[2:])
|
||||
// LWTUNNEL_ENCAP_SEG6 has only one attr type SEG6_IPTUNNEL_SRH
|
||||
if typ != nl.SEG6_IPTUNNEL_SRH {
|
||||
return fmt.Errorf("unknown SEG6 Type: %d", typ)
|
||||
}
|
||||
|
@ -259,6 +260,188 @@ func (e *SEG6Encap) Equal(x Encap) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// SEG6Local definitions
|
||||
type SEG6LocalEncap struct {
|
||||
Flags [nl.SEG6_LOCAL_MAX]bool
|
||||
Action int
|
||||
Segments []net.IP // from SRH in seg6_local_lwt
|
||||
Table int // table id for End.T and End.DT6
|
||||
InAddr net.IP
|
||||
In6Addr net.IP
|
||||
Iif int
|
||||
Oif int
|
||||
}
|
||||
|
||||
func (e *SEG6LocalEncap) Type() int {
|
||||
return nl.LWTUNNEL_ENCAP_SEG6_LOCAL
|
||||
}
|
||||
func (e *SEG6LocalEncap) Decode(buf []byte) error {
|
||||
attrs, err := nl.ParseRouteAttr(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
native := nl.NativeEndian()
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case nl.SEG6_LOCAL_ACTION:
|
||||
e.Action = int(native.Uint32(attr.Value[0:4]))
|
||||
e.Flags[nl.SEG6_LOCAL_ACTION] = true
|
||||
case nl.SEG6_LOCAL_SRH:
|
||||
e.Segments, err = nl.DecodeSEG6Srh(attr.Value[:])
|
||||
e.Flags[nl.SEG6_LOCAL_SRH] = true
|
||||
case nl.SEG6_LOCAL_TABLE:
|
||||
e.Table = int(native.Uint32(attr.Value[0:4]))
|
||||
e.Flags[nl.SEG6_LOCAL_TABLE] = true
|
||||
case nl.SEG6_LOCAL_NH4:
|
||||
e.InAddr = net.IP(attr.Value[0:4])
|
||||
e.Flags[nl.SEG6_LOCAL_NH4] = true
|
||||
case nl.SEG6_LOCAL_NH6:
|
||||
e.In6Addr = net.IP(attr.Value[0:16])
|
||||
e.Flags[nl.SEG6_LOCAL_NH6] = true
|
||||
case nl.SEG6_LOCAL_IIF:
|
||||
e.Iif = int(native.Uint32(attr.Value[0:4]))
|
||||
e.Flags[nl.SEG6_LOCAL_IIF] = true
|
||||
case nl.SEG6_LOCAL_OIF:
|
||||
e.Oif = int(native.Uint32(attr.Value[0:4]))
|
||||
e.Flags[nl.SEG6_LOCAL_OIF] = true
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
func (e *SEG6LocalEncap) Encode() ([]byte, error) {
|
||||
var err error
|
||||
native := nl.NativeEndian()
|
||||
res := make([]byte, 8)
|
||||
native.PutUint16(res, 8) // length
|
||||
native.PutUint16(res[2:], nl.SEG6_LOCAL_ACTION)
|
||||
native.PutUint32(res[4:], uint32(e.Action))
|
||||
if e.Flags[nl.SEG6_LOCAL_SRH] {
|
||||
srh, err := nl.EncodeSEG6Srh(e.Segments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attr := make([]byte, 4)
|
||||
native.PutUint16(attr, uint16(len(srh)+4))
|
||||
native.PutUint16(attr[2:], nl.SEG6_LOCAL_SRH)
|
||||
attr = append(attr, srh...)
|
||||
res = append(res, attr...)
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_TABLE] {
|
||||
attr := make([]byte, 8)
|
||||
native.PutUint16(attr, 8)
|
||||
native.PutUint16(attr[2:], nl.SEG6_LOCAL_TABLE)
|
||||
native.PutUint32(attr[4:], uint32(e.Table))
|
||||
res = append(res, attr...)
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_NH4] {
|
||||
attr := make([]byte, 4)
|
||||
native.PutUint16(attr, 8)
|
||||
native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH4)
|
||||
ipv4 := e.InAddr.To4()
|
||||
if ipv4 == nil {
|
||||
err = fmt.Errorf("SEG6_LOCAL_NH4 has invalid IPv4 address")
|
||||
return nil, err
|
||||
}
|
||||
attr = append(attr, ipv4...)
|
||||
res = append(res, attr...)
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_NH6] {
|
||||
attr := make([]byte, 4)
|
||||
native.PutUint16(attr, 20)
|
||||
native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH6)
|
||||
attr = append(attr, e.In6Addr...)
|
||||
res = append(res, attr...)
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_IIF] {
|
||||
attr := make([]byte, 8)
|
||||
native.PutUint16(attr, 8)
|
||||
native.PutUint16(attr[2:], nl.SEG6_LOCAL_IIF)
|
||||
native.PutUint32(attr[4:], uint32(e.Iif))
|
||||
res = append(res, attr...)
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_OIF] {
|
||||
attr := make([]byte, 8)
|
||||
native.PutUint16(attr, 8)
|
||||
native.PutUint16(attr[2:], nl.SEG6_LOCAL_OIF)
|
||||
native.PutUint32(attr[4:], uint32(e.Oif))
|
||||
res = append(res, attr...)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
func (e *SEG6LocalEncap) String() string {
|
||||
strs := make([]string, 0, nl.SEG6_LOCAL_MAX)
|
||||
strs = append(strs, fmt.Sprintf("action %s", nl.SEG6LocalActionString(e.Action)))
|
||||
|
||||
if e.Flags[nl.SEG6_LOCAL_TABLE] {
|
||||
strs = append(strs, fmt.Sprintf("table %d", e.Table))
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_NH4] {
|
||||
strs = append(strs, fmt.Sprintf("nh4 %s", e.InAddr))
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_NH6] {
|
||||
strs = append(strs, fmt.Sprintf("nh6 %s", e.In6Addr))
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_IIF] {
|
||||
link, err := LinkByIndex(e.Iif)
|
||||
if err != nil {
|
||||
strs = append(strs, fmt.Sprintf("iif %d", e.Iif))
|
||||
} else {
|
||||
strs = append(strs, fmt.Sprintf("iif %s", link.Attrs().Name))
|
||||
}
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_OIF] {
|
||||
link, err := LinkByIndex(e.Oif)
|
||||
if err != nil {
|
||||
strs = append(strs, fmt.Sprintf("oif %d", e.Oif))
|
||||
} else {
|
||||
strs = append(strs, fmt.Sprintf("oif %s", link.Attrs().Name))
|
||||
}
|
||||
}
|
||||
if e.Flags[nl.SEG6_LOCAL_SRH] {
|
||||
segs := make([]string, 0, len(e.Segments))
|
||||
//append segment backwards (from n to 0) since seg#0 is the last segment.
|
||||
for i := len(e.Segments); i > 0; i-- {
|
||||
segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1]))
|
||||
}
|
||||
strs = append(strs, fmt.Sprintf("segs %d [ %s ]", len(e.Segments), strings.Join(segs, " ")))
|
||||
}
|
||||
return strings.Join(strs, " ")
|
||||
}
|
||||
func (e *SEG6LocalEncap) Equal(x Encap) bool {
|
||||
o, ok := x.(*SEG6LocalEncap)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if e == o {
|
||||
return true
|
||||
}
|
||||
if e == nil || o == nil {
|
||||
return false
|
||||
}
|
||||
// compare all arrays first
|
||||
for i := range e.Flags {
|
||||
if e.Flags[i] != o.Flags[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(e.Segments) != len(o.Segments) {
|
||||
return false
|
||||
}
|
||||
for i := range e.Segments {
|
||||
if !e.Segments[i].Equal(o.Segments[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// compare values
|
||||
if !e.InAddr.Equal(o.InAddr) || !e.In6Addr.Equal(o.In6Addr) {
|
||||
return false
|
||||
}
|
||||
if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RouteAdd will add a route to the system.
|
||||
// Equivalent to: `ip route add $route`
|
||||
func RouteAdd(route *Route) error {
|
||||
|
@ -734,6 +917,11 @@ func deserializeRoute(m []byte) (Route, error) {
|
|||
if err := e.Decode(encap.Value); err != nil {
|
||||
return route, err
|
||||
}
|
||||
case nl.LWTUNNEL_ENCAP_SEG6_LOCAL:
|
||||
e = &SEG6LocalEncap{}
|
||||
if err := e.Decode(encap.Value); err != nil {
|
||||
return route, err
|
||||
}
|
||||
}
|
||||
route.Encap = e
|
||||
}
|
||||
|
|
178
route_test.go
178
route_test.go
|
@ -992,6 +992,105 @@ func TestIPNetEqual(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSEG6LocalEqual(t *testing.T) {
|
||||
// Different attributes exists in different Actions. For example, Action
|
||||
// SEG6_LOCAL_ACTION_END_X has In6Addr, SEG6_LOCAL_ACTION_END_T has Table etc.
|
||||
segs := []net.IP{net.ParseIP("fc00:a000::11")}
|
||||
// set flags for each actions.
|
||||
var flags_end [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end[nl.SEG6_LOCAL_ACTION] = true
|
||||
var flags_end_x [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_x[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_x[nl.SEG6_LOCAL_NH6] = true
|
||||
var flags_end_t [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_t[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_t[nl.SEG6_LOCAL_TABLE] = true
|
||||
var flags_end_dx2 [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_dx2[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_dx2[nl.SEG6_LOCAL_OIF] = true
|
||||
var flags_end_dx6 [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_dx6[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_dx6[nl.SEG6_LOCAL_NH6] = true
|
||||
var flags_end_dx4 [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_dx4[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_dx4[nl.SEG6_LOCAL_NH4] = true
|
||||
var flags_end_dt6 [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_dt6[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_dt6[nl.SEG6_LOCAL_TABLE] = true
|
||||
var flags_end_dt4 [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_dt4[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_dt4[nl.SEG6_LOCAL_TABLE] = true
|
||||
var flags_end_b6 [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_b6[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_b6[nl.SEG6_LOCAL_SRH] = true
|
||||
var flags_end_b6_encaps [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_b6_encaps[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_b6_encaps[nl.SEG6_LOCAL_SRH] = true
|
||||
|
||||
cases := []SEG6LocalEncap{
|
||||
{
|
||||
Flags: flags_end,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END,
|
||||
},
|
||||
{
|
||||
Flags: flags_end_x,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_X,
|
||||
In6Addr: net.ParseIP("2001:db8::1"),
|
||||
},
|
||||
{
|
||||
Flags: flags_end_t,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_T,
|
||||
Table: 10,
|
||||
},
|
||||
{
|
||||
Flags: flags_end_dx2,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_DX2,
|
||||
Oif: 20,
|
||||
},
|
||||
{
|
||||
Flags: flags_end_dx6,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_DX6,
|
||||
In6Addr: net.ParseIP("2001:db8::1"),
|
||||
},
|
||||
{
|
||||
Flags: flags_end_dx4,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_DX4,
|
||||
InAddr: net.IPv4(192, 168, 10, 10),
|
||||
},
|
||||
{
|
||||
Flags: flags_end_dt6,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_DT6,
|
||||
Table: 30,
|
||||
},
|
||||
{
|
||||
Flags: flags_end_dt4,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_DT4,
|
||||
Table: 40,
|
||||
},
|
||||
{
|
||||
Flags: flags_end_b6,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_B6,
|
||||
Segments: segs,
|
||||
},
|
||||
{
|
||||
Flags: flags_end_b6_encaps,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_B6_ENCAPS,
|
||||
Segments: segs,
|
||||
},
|
||||
}
|
||||
for i1 := range cases {
|
||||
for i2 := range cases {
|
||||
got := cases[i1].Equal(&cases[i2])
|
||||
expected := i1 == i2
|
||||
if got != expected {
|
||||
t.Errorf("Equal(%v,%v) == %s but expected %s",
|
||||
cases[i1], cases[i2],
|
||||
strconv.FormatBool(got),
|
||||
strconv.FormatBool(expected))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestSEG6RouteAddDel(t *testing.T) {
|
||||
// add/del routes with LWTUNNEL_SEG6 to/from loopback interface.
|
||||
// Test both seg6 modes: encap (IPv4) & inline (IPv6).
|
||||
|
@ -1078,6 +1177,85 @@ func TestSEG6RouteAddDel(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// add/del routes with LWTUNNEL_ENCAP_SEG6_LOCAL to/from dummy interface.
|
||||
func TestSEG6LocalRoute6AddDel(t *testing.T) {
|
||||
minKernelRequired(t, 4, 14)
|
||||
tearDown := setUpSEG6NetlinkTest(t)
|
||||
defer tearDown()
|
||||
|
||||
// create dummy interface
|
||||
// IPv6 route added to loopback interface will be unreachable
|
||||
la := NewLinkAttrs()
|
||||
la.Name = "dummy_route6"
|
||||
la.TxQLen = 1500
|
||||
dummy := &Dummy{LinkAttrs: la}
|
||||
if err := LinkAdd(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// get dummy interface and bring it up
|
||||
link, err := LinkByName("dummy_route6")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := LinkSetUp(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dst1 := &net.IPNet{
|
||||
IP: net.ParseIP("2001:db8::1"),
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
}
|
||||
|
||||
// Create Route including Action SEG6_LOCAL_ACTION_END_B6.
|
||||
// Could be any Action but thought better to have seg list.
|
||||
var s1 []net.IP
|
||||
s1 = append(s1, net.ParseIP("fc00:a000::12"))
|
||||
s1 = append(s1, net.ParseIP("fc00:a000::11"))
|
||||
var flags_end_b6_encaps [nl.SEG6_LOCAL_MAX]bool
|
||||
flags_end_b6_encaps[nl.SEG6_LOCAL_ACTION] = true
|
||||
flags_end_b6_encaps[nl.SEG6_LOCAL_SRH] = true
|
||||
e1 := &SEG6LocalEncap{
|
||||
Flags: flags_end_b6_encaps,
|
||||
Action: nl.SEG6_LOCAL_ACTION_END_B6,
|
||||
Segments: s1,
|
||||
}
|
||||
route1 := Route{LinkIndex: link.Attrs().Index, Dst: dst1, Encap: e1}
|
||||
|
||||
// Add SEG6Local routes
|
||||
if err := RouteAdd(&route1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// typically one route (fe80::/64) will be created when dummy_route6 is created.
|
||||
// Thus you cannot use RouteList() to find the route entry just added.
|
||||
// Lookup route and confirm it's SEG6Local route just added.
|
||||
routesFound, err := RouteGet(dst1.IP)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(routesFound) != 1 { // should only find 1 route entry
|
||||
t.Fatal("SEG6Local route not added correctly")
|
||||
}
|
||||
if !e1.Equal(routesFound[0].Encap) {
|
||||
t.Fatal("Encap does not match the original SEG6LocalEncap")
|
||||
}
|
||||
|
||||
// Del SEG6Local routes
|
||||
if err := RouteDel(&route1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Confirm route is deleted.
|
||||
routesFound, err = RouteGet(dst1.IP)
|
||||
if err == nil {
|
||||
t.Fatal("SEG6Local route still exists.")
|
||||
}
|
||||
|
||||
// cleanup dummy interface created for the test
|
||||
if err := LinkDel(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMTURouteAddDel(t *testing.T) {
|
||||
_, err := RouteList(nil, FAMILY_V4)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue