diff --git a/nl/ip6tnl_linux.go b/nl/ip6tnl_linux.go new file mode 100644 index 000000000..d5dd69e0c --- /dev/null +++ b/nl/ip6tnl_linux.go @@ -0,0 +1,21 @@ +package nl + +// id's of route attribute from https://elixir.bootlin.com/linux/v5.17.3/source/include/uapi/linux/lwtunnel.h#L38 +// the value's size are specified in https://elixir.bootlin.com/linux/v5.17.3/source/net/ipv4/ip_tunnel_core.c#L928 + +const ( + LWTUNNEL_IP6_UNSPEC = iota + LWTUNNEL_IP6_ID + LWTUNNEL_IP6_DST + LWTUNNEL_IP6_SRC + LWTUNNEL_IP6_HOPLIMIT + LWTUNNEL_IP6_TC + LWTUNNEL_IP6_FLAGS + LWTUNNEL_IP6_PAD // not implemented + LWTUNNEL_IP6_OPTS // not implemented + __LWTUNNEL_IP6_MAX +) + + + + diff --git a/route_linux.go b/route_linux.go index 8da886657..10c35392e 100644 --- a/route_linux.go +++ b/route_linux.go @@ -590,6 +590,109 @@ func (e *BpfEncap) Equal(x Encap) bool { return true } +// IP6tnlEncap definition +type IP6tnlEncap struct { + + ID uint64 + Dst net.IP + Src net.IP + Hoplimit uint8 + TC uint8 + Flags uint16 + +} + +func (e *IP6tnlEncap) Type() int { + return nl.LWTUNNEL_ENCAP_IP6 +} + +func (e *IP6tnlEncap) Decode(buf []byte) error { + attrs, err := nl.ParseRouteAttr(buf) + if err != nil { + return err + } + for _, attr := range attrs { + switch attr.Attr.Type { + case nl.LWTUNNEL_IP6_ID: + e.ID = uint64(native.Uint64(attr.Value[0:4])) + case nl.LWTUNNEL_IP6_DST: + e.Dst = net.IP(attr.Value[:]) + case nl.LWTUNNEL_IP6_SRC: + e.Src = net.IP(attr.Value[:]) + case nl.LWTUNNEL_IP6_HOPLIMIT: + e.Hoplimit = attr.Value[0] + case nl.LWTUNNEL_IP6_TC: + e.TC = attr.Value[0] + case nl.LWTUNNEL_IP6_FLAGS: + e.Flags = uint16(native.Uint16(attr.Value[0:2])) + case nl.LWTUNNEL_IP6_PAD: + err = fmt.Errorf("decoding PAD in IP6tnlEncap is not supported",) + case nl.LWTUNNEL_IP6_OPTS: + err = fmt.Errorf("decoding OPTS in IP6tnlEncap is not supported") + } + } + return err +} + +func (e *IP6tnlEncap) Encode() ([]byte, error) { + + final := []byte{} + + resID := make([]byte, 12) + native.PutUint16(resID, 12) // 2+2+8 + native.PutUint16(resID[2:], nl.LWTUNNEL_IP6_ID) + native.PutUint64(resID[4:], 0) + final = append(final,resID...) + + resDst := make([]byte, 4) + native.PutUint16(resDst, 20) // 2+2+16 + native.PutUint16(resDst[2:], nl.LWTUNNEL_IP6_DST) + resDst = append(resDst, e.Dst...) + final = append(final,resDst...) + + resSrc := make([]byte, 4) + native.PutUint16(resSrc, 20) + native.PutUint16(resSrc[2:], nl.LWTUNNEL_IP6_SRC) + resSrc = append(resSrc, e.Src...) + final = append(final,resSrc...) + + resTc := make([]byte, 5) + native.PutUint16(resTc, 5) + native.PutUint16(resTc[2:], nl.LWTUNNEL_IP6_TC) + resTc[4] = e.TC + final = append(final,resTc...) + + resHops := make([]byte, 5) + native.PutUint16(resHops, 5) + native.PutUint16(resHops[2:], nl.LWTUNNEL_IP6_HOPLIMIT) + resHops[4] = e.Hoplimit + final = append(final,resHops...) + + resFlags := make([]byte, 6) + native.PutUint16(resFlags, 6) + native.PutUint16(resFlags[2:], nl.LWTUNNEL_IP6_FLAGS) + native.PutUint16(resFlags[4:], e.Flags) + final = append(final,resFlags...) + + return final, nil +} + +func (e *IP6tnlEncap) String() string { + return fmt.Sprintf("id %d src %s dst %s hoplimit %d tc %d flags 0x%.4x", e.ID, e.Src, e.Dst, e.Hoplimit, e.TC, e.Flags) +} + +func (e *IP6tnlEncap) Equal(x Encap) bool { + o, ok := x.(*IP6tnlEncap) + if !ok { + return false + } + + if e.ID != o.ID || e.Flags != o.Flags || e.Hoplimit != o.Hoplimit || e.Src.Equal(o.Src) || e.Dst.Equal(o.Dst) || e.TC != o.TC { + return false + } + return true +} + type Via struct { AddrFamily int Addr net.IP diff --git a/route_test.go b/route_test.go index a8f3e501e..22f0a7856 100644 --- a/route_test.go +++ b/route_test.go @@ -968,6 +968,66 @@ func TestMPLSRouteAddDel(t *testing.T) { } +func TestIP6tnlRouteAddDel(t *testing.T) { + _, err := RouteList(nil, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + + 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) + } + + _, dst, err := net.ParseCIDR("192.168.99.0/24") + if err != nil { + t.Fatalf("cannot parse destination prefix: %v", err) + } + + encap := IP6tnlEncap{ + Dst: net.ParseIP("2001:db8::"), + Src: net.ParseIP("::"), + } + + route := &Route{ + LinkIndex: link.Attrs().Index, + Dst: dst, + Encap: &encap, + } + + if err := RouteAdd(route); err != nil { + t.Fatalf("Cannot add route: %v", err) + } + routes, err := RouteList(link, FAMILY_V4) + 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_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 0 { + t.Fatal("Route not removed properly") + } + +} + func TestRouteEqual(t *testing.T) { mplsDst := 100 seg6encap := &SEG6Encap{Mode: nl.SEG6_IPTUN_MODE_ENCAP}