Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPv6 SRH decoding #889

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 115 additions & 16 deletions layers/ip6.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,46 +547,145 @@ func (o *IPv6HopByHopOption) SetJumboLength(len uint32) {
o.OptionAlignment = [2]uint8{4, 2}
}

// IPv6Routing is the IPv6 routing extension.
type IPv6Routing struct {
// Base layer for IPv6 routing headers.
type IPv6RoutingBase struct {
ipv6ExtensionBase
RoutingType uint8
SegmentsLeft uint8
// This segment is supposed to be zero according to RFC2460, the second set of
// 4 bytes in the extension.
Reserved []byte

// SourceRoutingIPs is the set of IPv6 addresses requested for source routing,
// set only if RoutingType == 0.
// To be interpreted according to actual routing headers e.g. source routing
// IPs (type 0) or SegmentList (type 4).
SourceRoutingIPs []net.IP

// That varies see extended type (e.g. 4 for type 0 RFC2460, 2 for type 4 RFC8754)
Reserved []byte
}

const IPv6SourceRoutingType = 0
const IPv6SegmentRoutingHeaderType = 4

// IPv6Routing is the IPv6 routing extension.
type IPv6Routing struct {
IPv6RoutingBase
}

// LayerType returns LayerTypeIPv6Routing.
func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing }

// IPv6SegmentRoutingHeader is the SRv6 SRH.
type IPv6SegmentRoutingHeader struct {
IPv6RoutingBase

LastEntry uint8
Flags uint8
Tag uint16

// Undecoded TLVs.
TLVs []byte
}

// LayerType returns LayerTypeIPv6Routing.
func (i *IPv6SegmentRoutingHeader) LayerType() gopacket.LayerType { return LayerTypeIPv6SegmentRouting }

func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error {
base, err := decodeIPv6ExtensionBase(data, p)
if err != nil {
return err
}
i := &IPv6Routing{
routingBase := IPv6RoutingBase{
ipv6ExtensionBase: base,
RoutingType: data[2],
SegmentsLeft: data[3],
Reserved: data[4:8],
}
switch i.RoutingType {
case 0: // Source routing
if (i.ActualLength-8)%16 != 0 {
return fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength)

var layer gopacket.Layer

switch routingBase.RoutingType {
case IPv6SourceRoutingType:
if layer, err = decodeIPv6RoutingType0(&routingBase, data); err != nil {
return err
}
for d := i.Contents[8:]; len(d) >= 16; d = d[16:] {
i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16]))
case IPv6SegmentRoutingHeaderType:
if layer, err = decodeIPv6RoutingType4(&routingBase, data); err != nil {
return err
}
default:
return fmt.Errorf("Unknown IPv6 routing header type %d", i.RoutingType)
return fmt.Errorf("Unknown IPv6 routing header type %d", routingBase.RoutingType)
}
p.AddLayer(i)
return p.NextDecoder(i.NextHeader)
p.AddLayer(layer)
return p.NextDecoder(routingBase.NextHeader)
}

func decodeIPv6RoutingType0(base *IPv6RoutingBase, data []byte) (gopacket.Layer, error) {
i := &IPv6Routing{
IPv6RoutingBase: *base,
}
i.Reserved = data[4:8]

if (i.ActualLength-8)%16 != 0 {
return nil, fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength)
}
for d := i.Contents[8:]; len(d) >= 16; d = d[16:] {
i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16]))
}
return i, nil
}

func decodeIPv6RoutingType4(base *IPv6RoutingBase, data []byte) (gopacket.Layer, error) {
srh := &IPv6SegmentRoutingHeader{
IPv6RoutingBase: *base,
LastEntry: data[4],
Flags: data[5],
}
srh.Reserved = data[6:8]

// Compute in bytes
hdrLength := int(srh.HeaderLength * 8)
sidLength := int(srh.LastEntry+1) * 16

if sidLength > hdrLength {
return nil, fmt.Errorf("Invalid IPv6 SRH, last entry out-of-bounds")
}

// Check for TLVs
if hdrLength > sidLength {
srh.TLVs = data[8+sidLength : 8+hdrLength]
} else {
if hdrLength%16 != 0 {
return nil, fmt.Errorf("Invalid IPv6 SHR, length of type 4 packet %d", srh.ActualLength)
}
}
for d := srh.Contents[8:]; len(d) >= 16; d = d[16:] {
srh.SourceRoutingIPs = append(srh.SourceRoutingIPs, net.IP(d[:16]))
}
return srh, nil
}

func (srh *IPv6SegmentRoutingHeader) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
totalLen := int(8) + 16*(int(srh.LastEntry)+1) + len(srh.TLVs)
bytes, err := b.PrependBytes(totalLen)

if err != nil {
return fmt.Errorf("Failed to serialize SRH: %v", err)
}

bytes[0] = byte(srh.NextHeader)
bytes[1] = byte((totalLen - 8) / 8)
bytes[2] = 4
bytes[3] = srh.SegmentsLeft
bytes[4] = srh.LastEntry
bytes[5] = srh.Flags
binary.BigEndian.PutUint16(bytes[6:8], srh.Tag)

for i, ip := range srh.SourceRoutingIPs {
copy(bytes[8+(i*16):], ip)
}

copy(bytes[8+(srh.LastEntry+1)*16:], srh.TLVs)

return nil
}

// IPv6Fragment is the IPv6 fragment header, used for packet
Expand Down
92 changes: 91 additions & 1 deletion layers/ip6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ package layers

import (
"bytes"
"github.com/google/gopacket"
"net"
"reflect"
"testing"

"github.com/google/gopacket"
)

func TestSerializeIPv6HeaderTLVOptions(t *testing.T) {
Expand Down Expand Up @@ -428,3 +429,92 @@ func TestIPv6JumbogramDecode(t *testing.T) {
t.Error("No Payload layer type found in packet")
}
}

var testPacketSegmentRoutingHeader = []byte{
0x60, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x0b, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82,
0x1c, 0x03, 0xca, 0xfe, 0x16, 0x10, 0x30, 0x09, 0x29, 0x02, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00,
0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x60, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x10, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x20, 0x01, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80,
0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x7e, 0xc8, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x6c, 0x62, 0x00, 0x00, 0x71, 0x6a, 0x68, 0x71,
0x68, 0x74, 0x5b, 0x71, 0x09, 0x31, 0x75, 0x75, 0x39, 0x75, 0x69, 0x71, 0x6a, 0x68, 0x67, 0x66,
0x65, 0x67, 0x68, 0x6a, 0x6e, 0x62, 0x76, 0x61, 0x66, 0x61, 0x6a, 0x67, 0x6b, 0x27, 0x61, 0x6d,
0x6a, 0x67, 0x6c, 0x6b, 0x61, 0x6d, 0x67, 0x66,
}

func TestPacketIPv6SegmentRoutingHeaderDecode(t *testing.T) {
packet := gopacket.NewPacket(testPacketSegmentRoutingHeader, LayerTypeIPv6, gopacket.Default)

if packet.ErrorLayer() != nil {
t.Fatalf("IPv6 packet SRH decoding failed: %v", packet.ErrorLayer())
}
if len(packet.Layers()) < 2 {
t.Fatalf("IPv6 packet SRH decoding failed, expecting at least 2 decoded layers")
}
if packet.Layers()[0].LayerType() != LayerTypeIPv6 {
t.Fatalf("Expecting IPv6 header as first layer")
}
if packet.Layers()[1].LayerType() != LayerTypeIPv6SegmentRouting {
t.Fatalf("Expecting IPv6 SRH header as second layer")
}
srh := packet.Layers()[1].(*IPv6SegmentRoutingHeader)
if srh.SegmentsLeft != 1 {
t.Fatalf("Expecting SRH SL=1")
}
if len(srh.SourceRoutingIPs) != 1 {
t.Fatalf("Expecting 1 SID")
}
if srh.SourceRoutingIPs[0].String() != "fc80::282:3c00:0:0:4" {
t.Errorf("Wrong SID")
}
}

var testPacketSegmentRoutingHeaderWithTlv = []byte{
0x60, 0x00, 0x00, 0x00, 0x00, 0x88, 0x2b, 0x0b, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82,
0x1c, 0x03, 0xca, 0xfe, 0x16, 0x10, 0x30, 0x09, 0x29, 0x03, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00,
0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x10,
0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x20, 0x01, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80,
0x7e, 0xc8, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
0x6c, 0x62, 0x00, 0x00, 0x71, 0x6a, 0x68, 0x71, 0x68, 0x74, 0x5b, 0x71, 0x09, 0x31, 0x75, 0x75,
0x39, 0x75, 0x69, 0x71, 0x6a, 0x68, 0x67, 0x66, 0x65, 0x67, 0x68, 0x6a, 0x6e, 0x62, 0x76, 0x61,
0x66, 0x61, 0x6a, 0x67, 0x6b, 0x27, 0x61, 0x6d, 0x6a, 0x67, 0x6c, 0x6b, 0x61, 0x6d, 0x67, 0x66,
}

func TestPacketIPv6SegmentRoutingHeaderDecodeWithTlv(t *testing.T) {
packet := gopacket.NewPacket(testPacketSegmentRoutingHeaderWithTlv, LayerTypeIPv6, gopacket.Default)

if packet.ErrorLayer() != nil {
t.Fatalf("IPv6 packet SRH decoding failed: %v", packet.ErrorLayer())
}
if len(packet.Layers()) < 2 {
t.Fatalf("IPv6 packet SRH decoding failed, expecting at least 2 decoded layers")
}
if packet.Layers()[0].LayerType() != LayerTypeIPv6 {
t.Fatalf("Expecting IPv6 header as first layer")
}
if packet.Layers()[1].LayerType() != LayerTypeIPv6SegmentRouting {
t.Fatalf("Expecting IPv6 SRH header as second layer")
}
// It's the same packet but with some bogus tlv
srh := packet.Layers()[1].(*IPv6SegmentRoutingHeader)
if srh.SegmentsLeft != 1 {
t.Fatalf("Expecting SRH SL=1")
}
if len(srh.SourceRoutingIPs) != 1 {
t.Fatalf("Expecting 1 SID")
}
if srh.SourceRoutingIPs[0].String() != "fc80::282:3c00:0:0:4" {
t.Errorf("Wrong SID")
}
if len(srh.TLVs) != 8 {
t.Errorf("Expecting 8 bytes TLV, got %d", len(srh.TLVs))
}
if string(srh.TLVs) != string([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}) {
t.Errorf("Wrong TLV value decoded")
}
}
1 change: 1 addition & 0 deletions layers/layertypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ var (
LayerTypeASFPresencePong = gopacket.RegisterLayerType(144, gopacket.LayerTypeMetadata{Name: "ASFPresencePong", Decoder: gopacket.DecodeFunc(decodeASFPresencePong)})
LayerTypeERSPANII = gopacket.RegisterLayerType(145, gopacket.LayerTypeMetadata{Name: "ERSPAN Type II", Decoder: gopacket.DecodeFunc(decodeERSPANII)})
LayerTypeRADIUS = gopacket.RegisterLayerType(146, gopacket.LayerTypeMetadata{Name: "RADIUS", Decoder: gopacket.DecodeFunc(decodeRADIUS)})
LayerTypeIPv6SegmentRouting = gopacket.RegisterLayerType(147, gopacket.LayerTypeMetadata{Name: "IPv6SegmentRouting", Decoder: gopacket.DecodeFunc(decodeIPv6Routing)})
)

var (
Expand Down