Skip to content

Commit

Permalink
Support LWTUNNEL_ENCAP_SEG6
Browse files Browse the repository at this point in the history
  • Loading branch information
ebiken committed Nov 3, 2017
1 parent 71fa81e commit 5977a4b
Show file tree
Hide file tree
Showing 5 changed files with 332 additions and 5 deletions.
38 changes: 38 additions & 0 deletions netlink_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package netlink

import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"runtime"
"strings"
"testing"
Expand Down Expand Up @@ -61,6 +63,42 @@ func setUpMPLSNetlinkTest(t *testing.T) tearDownNetlinkTest {
return f
}

func setUpSEG6NetlinkTest(t *testing.T) tearDownNetlinkTest {
// check if SEG6 options are enabled in Kernel Config
cmd := exec.Command("uname", "-r")
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
t.Fatal("Failed to run: uname -r")
}
s := []string{"/boot/config-", strings.TrimRight(out.String(), "\n")}
filename := strings.Join(s, "")

grepKey := func(key, fname string) (string, error) {
cmd := exec.Command("grep", key, filename)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run() // "err != nil" if no line matched with grep
return strings.TrimRight(out.String(), "\n"), err
}
key := string("CONFIG_IPV6_SEG6_LWTUNNEL=y")
if _, err := grepKey(key, filename); err != nil {
msg := "Skipped test because it requires SEG6_LWTUNNEL support."
log.Printf(msg)
t.Skip(msg)
}
key = string("CONFIG_IPV6_SEG6_INLINE=y")
if _, err := grepKey(key, filename); err != nil {
msg := "Skipped test because it requires SEG6_INLINE support."
log.Printf(msg)
t.Skip(msg)
}
// Add CONFIG_IPV6_SEG6_HMAC to support seg6_hamc
// key := string("CONFIG_IPV6_SEG6_HMAC=y")

return setUpNetlinkTest(t)
}

func setUpNetlinkTestWithKModule(t *testing.T, name string) tearDownNetlinkTest {
file, err := ioutil.ReadFile("/proc/modules")
if err != nil {
Expand Down
111 changes: 111 additions & 0 deletions nl/seg6_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package nl

import (
"errors"
"fmt"
"net"
)

type IPv6SrHdr struct {
nextHdr uint8
hdrLen uint8
routingType uint8
segmentsLeft uint8
firstSegment uint8
flags uint8
reserved uint16

Segments []net.IP
}

func (s1 *IPv6SrHdr) Equal(s2 IPv6SrHdr) bool {
if len(s1.Segments) != len(s2.Segments) {
return false
}
for i := range s1.Segments {
if s1.Segments[i].Equal(s2.Segments[i]) != true {
return false
}
}
return s1.nextHdr == s2.nextHdr &&
s1.hdrLen == s2.hdrLen &&
s1.routingType == s2.routingType &&
s1.segmentsLeft == s2.segmentsLeft &&
s1.firstSegment == s2.firstSegment &&
s1.flags == s2.flags
// reserved doesn't need to be identical.
}

// seg6 encap mode
const (
SEG6_IPTUN_MODE_INLINE = iota
SEG6_IPTUN_MODE_ENCAP
)

// number of nested RTATTR
// from include/uapi/linux/seg6_iptunnel.h
const (
SEG6_IPTUNNEL_UNSPEC = iota
SEG6_IPTUNNEL_SRH
__SEG6_IPTUNNEL_MAX
)
const (
SEG6_IPTUNNEL_MAX = __SEG6_IPTUNNEL_MAX - 1
)

func EncodeSEG6Encap(mode int, segments []net.IP) ([]byte, error) {
nsegs := len(segments) // nsegs: number of segments
if nsegs == 0 {
return nil, errors.New("EncodeSEG6Encap: No Segment in srh")
}
b := make([]byte, 12, 12+len(segments)*16)
native := NativeEndian()
native.PutUint32(b, uint32(mode))
b[4] = 0 // srh.nextHdr (0 when calling netlink)
b[5] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit)
b[6] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA)
b[7] = uint8(nsegs - 1) // srh.segmentsLeft
b[8] = uint8(nsegs - 1) // srh.firstSegment
b[9] = 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[10:], 0) // srh.reserved
for _, netIP := range segments {
b = append(b, netIP...) // srh.Segments
}
return b, nil
}

func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) {
native := NativeEndian()
mode := int(native.Uint32(buf))
srh := IPv6SrHdr{
nextHdr: buf[4],
hdrLen: buf[5],
routingType: buf[6],
segmentsLeft: buf[7],
firstSegment: buf[8],
flags: buf[9],
reserved: native.Uint16(buf[10:12]),
}
buf = buf[12:]
if len(buf)%16 != 0 {
err := fmt.Errorf("DecodeSEG6Encap: error parsing Segment List (buf len: %d)\n", len(buf))
return mode, nil, err
}
for len(buf) > 0 {
srh.Segments = append(srh.Segments, net.IP(buf[:16]))
buf = buf[16:]
}
return mode, srh.Segments, nil
}

// Helper functions
func SEG6EncapModeString(mode int) string {
switch mode {
case SEG6_IPTUN_MODE_INLINE:
return "inline"
case SEG6_IPTUN_MODE_ENCAP:
return "encap"
}
return "unknown"
}
10 changes: 10 additions & 0 deletions nl/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,14 @@ const (
LWTUNNEL_ENCAP_IP
LWTUNNEL_ENCAP_ILA
LWTUNNEL_ENCAP_IP6
LWTUNNEL_ENCAP_SEG6
LWTUNNEL_ENCAP_BPF
)

// routing header types
const (
IPV6_SRCRT_STRICT = 0x01 // Deprecated; will be removed
IPV6_SRCRT_TYPE_0 = 0 // Deprecated; will be removed
IPV6_SRCRT_TYPE_2 = 2 // IPv6 type 2 Routing Header
IPV6_SRCRT_TYPE_4 = 4 // Segment Routing with IPv6
)
88 changes: 83 additions & 5 deletions route_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,17 @@ func (e *MPLSEncap) Type() int {

func (e *MPLSEncap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("Lack of bytes")
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
l := native.Uint16(buf)
if len(buf) < int(l) {
return fmt.Errorf("Lack of bytes")
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)
return fmt.Errorf("unknown MPLS Encap Type: %d", typ)
}
e.Labels = nl.DecodeMPLSStack(buf[4:])
return nil
Expand Down Expand Up @@ -186,6 +186,79 @@ func (e *MPLSEncap) Equal(x Encap) bool {
return true
}

// SEG6 definitions
type SEG6Encap struct {
Mode int
Segments []net.IP
}

func (e *SEG6Encap) Type() int {
return nl.LWTUNNEL_ENCAP_SEG6
}
func (e *SEG6Encap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
// Get Length(l) & Type(typ) : 2 + 2 bytes
l := native.Uint16(buf)
if len(buf) < int(l) {
return fmt.Errorf("lack of bytes")
}
buf = buf[:l] // make sure buf size upper limit is Length
typ := native.Uint16(buf[2:])
if typ != nl.SEG6_IPTUNNEL_SRH {
return fmt.Errorf("unknown SEG6 Type: %d", typ)
}

var err error
e.Mode, e.Segments, err = nl.DecodeSEG6Encap(buf[4:])

return err
}
func (e *SEG6Encap) Encode() ([]byte, error) {
s, err := nl.EncodeSEG6Encap(e.Mode, e.Segments)
native := nl.NativeEndian()
hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.SEG6_IPTUNNEL_SRH)
return append(hdr, s...), err
}
func (e *SEG6Encap) String() string {
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]))
}
str := fmt.Sprintf("mode %s segs %d [ %s ]", nl.SEG6EncapModeString(e.Mode),
len(e.Segments), strings.Join(segs, " "))
return str
}
func (e *SEG6Encap) Equal(x Encap) bool {
o, ok := x.(*SEG6Encap)
if !ok {
return false
}
if e == o {
return true
}
if e == nil || o == nil {
return false
}
if e.Mode != o.Mode {
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
}
}
return true
}

// RouteAdd will add a route to the system.
// Equivalent to: `ip route add $route`
func RouteAdd(route *Route) error {
Expand Down Expand Up @@ -537,11 +610,11 @@ func deserializeRoute(m []byte) (Route, error) {
case unix.RTA_MULTIPATH:
parseRtNexthop := func(value []byte) (*NexthopInfo, []byte, error) {
if len(value) < unix.SizeofRtNexthop {
return nil, nil, fmt.Errorf("Lack of bytes")
return nil, nil, fmt.Errorf("lack of bytes")
}
nh := nl.DeserializeRtNexthop(value)
if len(value) < int(nh.RtNexthop.Len) {
return nil, nil, fmt.Errorf("Lack of bytes")
return nil, nil, fmt.Errorf("lack of bytes")
}
info := &NexthopInfo{
LinkIndex: int(nh.RtNexthop.Ifindex),
Expand Down Expand Up @@ -624,6 +697,11 @@ func deserializeRoute(m []byte) (Route, error) {
if err := e.Decode(encap.Value); err != nil {
return route, err
}
case nl.LWTUNNEL_ENCAP_SEG6:
e = &SEG6Encap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
}
route.Encap = e
}
Expand Down
Loading

0 comments on commit 5977a4b

Please sign in to comment.