blob: f3f2ca9adb19b8d06391584c2fc52633b263a039 [file] [log] [blame]
// Copyright 2017 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
// OSPFType denotes what kind of OSPF type it is
type OSPFType uint8
// Potential values for OSPF.Type.
const (
OSPFHello OSPFType = 1
OSPFDatabaseDescription OSPFType = 2
OSPFLinkStateRequest OSPFType = 3
OSPFLinkStateUpdate OSPFType = 4
OSPFLinkStateAcknowledgment OSPFType = 5
)
// LSA Function Codes for LSAheader.LSType
const (
RouterLSAtypeV2 = 0x1
RouterLSAtype = 0x2001
NetworkLSAtypeV2 = 0x2
NetworkLSAtype = 0x2002
SummaryLSANetworktypeV2 = 0x3
InterAreaPrefixLSAtype = 0x2003
SummaryLSAASBRtypeV2 = 0x4
InterAreaRouterLSAtype = 0x2004
ASExternalLSAtypeV2 = 0x5
ASExternalLSAtype = 0x4005
NSSALSAtype = 0x2007
LinkLSAtype = 0x0008
IntraAreaPrefixLSAtype = 0x2009
)
// String conversions for OSPFType
func (i OSPFType) String() string {
switch i {
case OSPFHello:
return "Hello"
case OSPFDatabaseDescription:
return "Database Description"
case OSPFLinkStateRequest:
return "Link State Request"
case OSPFLinkStateUpdate:
return "Link State Update"
case OSPFLinkStateAcknowledgment:
return "Link State Acknowledgment"
default:
return ""
}
}
// Prefix extends IntraAreaPrefixLSA
type Prefix struct {
PrefixLength uint8
PrefixOptions uint8
Metric uint16
AddressPrefix []byte
}
// IntraAreaPrefixLSA is the struct from RFC 5340 A.4.10.
type IntraAreaPrefixLSA struct {
NumOfPrefixes uint16
RefLSType uint16
RefLinkStateID uint32
RefAdvRouter uint32
Prefixes []Prefix
}
// LinkLSA is the struct from RFC 5340 A.4.9.
type LinkLSA struct {
RtrPriority uint8
Options uint32
LinkLocalAddress []byte
NumOfPrefixes uint32
Prefixes []Prefix
}
// ASExternalLSAV2 is the struct from RFC 2328 A.4.5.
type ASExternalLSAV2 struct {
NetworkMask uint32
ExternalBit uint8
Metric uint32
ForwardingAddress uint32
ExternalRouteTag uint32
}
// ASExternalLSA is the struct from RFC 5340 A.4.7.
type ASExternalLSA struct {
Flags uint8
Metric uint32
PrefixLength uint8
PrefixOptions uint8
RefLSType uint16
AddressPrefix []byte
ForwardingAddress []byte
ExternalRouteTag uint32
RefLinkStateID uint32
}
// InterAreaRouterLSA is the struct from RFC 5340 A.4.6.
type InterAreaRouterLSA struct {
Options uint32
Metric uint32
DestinationRouterID uint32
}
// InterAreaPrefixLSA is the struct from RFC 5340 A.4.5.
type InterAreaPrefixLSA struct {
Metric uint32
PrefixLength uint8
PrefixOptions uint8
AddressPrefix []byte
}
// NetworkLSA is the struct from RFC 5340 A.4.4.
type NetworkLSA struct {
Options uint32
AttachedRouter []uint32
}
// RouterV2 extends RouterLSAV2
type RouterV2 struct {
Type uint8
LinkID uint32
LinkData uint32
Metric uint16
}
// RouterLSAV2 is the struct from RFC 2328 A.4.2.
type RouterLSAV2 struct {
Flags uint8
Links uint16
Routers []RouterV2
}
// Router extends RouterLSA
type Router struct {
Type uint8
Metric uint16
InterfaceID uint32
NeighborInterfaceID uint32
NeighborRouterID uint32
}
// RouterLSA is the struct from RFC 5340 A.4.3.
type RouterLSA struct {
Flags uint8
Options uint32
Routers []Router
}
// LSAheader is the struct from RFC 5340 A.4.2 and RFC 2328 A.4.1.
type LSAheader struct {
LSAge uint16
LSType uint16
LinkStateID uint32
AdvRouter uint32
LSSeqNumber uint32
LSChecksum uint16
Length uint16
LSOptions uint8
}
// LSA links LSAheader with the structs from RFC 5340 A.4.
type LSA struct {
LSAheader
Content interface{}
}
// LSUpdate is the struct from RFC 5340 A.3.5.
type LSUpdate struct {
NumOfLSAs uint32
LSAs []LSA
}
// LSReq is the struct from RFC 5340 A.3.4.
type LSReq struct {
LSType uint16
LSID uint32
AdvRouter uint32
}
// DbDescPkg is the struct from RFC 5340 A.3.3.
type DbDescPkg struct {
Options uint32
InterfaceMTU uint16
Flags uint16
DDSeqNumber uint32
LSAinfo []LSAheader
}
// HelloPkg is the struct from RFC 5340 A.3.2.
type HelloPkg struct {
InterfaceID uint32
RtrPriority uint8
Options uint32
HelloInterval uint16
RouterDeadInterval uint32
DesignatedRouterID uint32
BackupDesignatedRouterID uint32
NeighborID []uint32
}
// HelloPkgV2 extends the HelloPkg struct with OSPFv2 information
type HelloPkgV2 struct {
HelloPkg
NetworkMask uint32
}
// OSPF is a basic OSPF packet header with common fields of Version 2 and Version 3.
type OSPF struct {
Version uint8
Type OSPFType
PacketLength uint16
RouterID uint32
AreaID uint32
Checksum uint16
Content interface{}
}
//OSPFv2 extend the OSPF head with version 2 specific fields
type OSPFv2 struct {
BaseLayer
OSPF
AuType uint16
Authentication uint64
}
// OSPFv3 extend the OSPF head with version 3 specific fields
type OSPFv3 struct {
BaseLayer
OSPF
Instance uint8
Reserved uint8
}
// getLSAsv2 parses the LSA information from the packet for OSPFv2
func getLSAsv2(num uint32, data []byte) ([]LSA, error) {
var lsas []LSA
var i uint32 = 0
var offset uint32 = 0
for ; i < num; i++ {
lstype := uint16(data[offset+3])
lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20])
content, err := extractLSAInformation(lstype, lsalength, data[offset:])
if err != nil {
return nil, fmt.Errorf("Could not extract Link State type.")
}
lsa := LSA{
LSAheader: LSAheader{
LSAge: binary.BigEndian.Uint16(data[offset : offset+2]),
LSOptions: data[offset+2],
LSType: lstype,
LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]),
AdvRouter: binary.BigEndian.Uint32(data[offset+8 : offset+12]),
LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]),
LSChecksum: binary.BigEndian.Uint16(data[offset+16 : offset+18]),
Length: lsalength,
},
Content: content,
}
lsas = append(lsas, lsa)
offset += uint32(lsalength)
}
return lsas, nil
}
// extractLSAInformation extracts all the LSA information
func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{}, error) {
if lsalength < 20 {
return nil, fmt.Errorf("Link State header length %v too short, %v required", lsalength, 20)
}
if len(data) < int(lsalength) {
return nil, fmt.Errorf("Link State header length %v too short, %v required", len(data), lsalength)
}
var content interface{}
switch lstype {
case RouterLSAtypeV2:
var routers []RouterV2
links := binary.BigEndian.Uint16(data[22:24])
content = RouterLSAV2{
Flags: data[20],
Links: links,
Routers: routers,
}
case ASExternalLSAtypeV2:
content = ASExternalLSAV2{
NetworkMask: binary.BigEndian.Uint32(data[20:24]),
ExternalBit: data[24] & 0x80,
Metric: binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF,
ForwardingAddress: binary.BigEndian.Uint32(data[28:32]),
ExternalRouteTag: binary.BigEndian.Uint32(data[32:36]),
}
case RouterLSAtype:
var routers []Router
var j uint32
for j = 24; j < uint32(lsalength); j += 16 {
router := Router{
Type: uint8(data[j]),
Metric: binary.BigEndian.Uint16(data[j+2 : j+4]),
InterfaceID: binary.BigEndian.Uint32(data[j+4 : j+8]),
NeighborInterfaceID: binary.BigEndian.Uint32(data[j+8 : j+12]),
NeighborRouterID: binary.BigEndian.Uint32(data[j+12 : j+16]),
}
routers = append(routers, router)
}
content = RouterLSA{
Flags: uint8(data[20]),
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
Routers: routers,
}
case NetworkLSAtype:
var routers []uint32
var j uint32
for j = 24; j < uint32(lsalength); j += 4 {
routers = append(routers, binary.BigEndian.Uint32(data[j:j+4]))
}
content = NetworkLSA{
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
AttachedRouter: routers,
}
case InterAreaPrefixLSAtype:
content = InterAreaPrefixLSA{
Metric: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
PrefixLength: uint8(data[24]),
PrefixOptions: uint8(data[25]),
AddressPrefix: data[28:uint32(lsalength)],
}
case InterAreaRouterLSAtype:
content = InterAreaRouterLSA{
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
Metric: binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF,
DestinationRouterID: binary.BigEndian.Uint32(data[28:32]),
}
case ASExternalLSAtype:
fallthrough
case NSSALSAtype:
flags := uint8(data[20])
prefixLen := uint8(data[24]) / 8
var forwardingAddress []byte
if (flags & 0x02) == 0x02 {
forwardingAddress = data[28+uint32(prefixLen) : 28+uint32(prefixLen)+16]
}
content = ASExternalLSA{
Flags: flags,
Metric: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
PrefixLength: prefixLen,
PrefixOptions: uint8(data[25]),
RefLSType: binary.BigEndian.Uint16(data[26:28]),
AddressPrefix: data[28 : 28+uint32(prefixLen)],
ForwardingAddress: forwardingAddress,
}
case LinkLSAtype:
var prefixes []Prefix
var prefixOffset uint32 = 44
var j uint32
numOfPrefixes := binary.BigEndian.Uint32(data[40:44])
for j = 0; j < numOfPrefixes; j++ {
prefixLen := uint8(data[prefixOffset])
prefix := Prefix{
PrefixLength: prefixLen,
PrefixOptions: uint8(data[prefixOffset+1]),
AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8],
}
prefixes = append(prefixes, prefix)
prefixOffset = prefixOffset + 4 + uint32(prefixLen)/8
}
content = LinkLSA{
RtrPriority: uint8(data[20]),
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
LinkLocalAddress: data[24:40],
NumOfPrefixes: numOfPrefixes,
Prefixes: prefixes,
}
case IntraAreaPrefixLSAtype:
var prefixes []Prefix
var prefixOffset uint32 = 32
var j uint16
numOfPrefixes := binary.BigEndian.Uint16(data[20:22])
for j = 0; j < numOfPrefixes; j++ {
prefixLen := uint8(data[prefixOffset])
prefix := Prefix{
PrefixLength: prefixLen,
PrefixOptions: uint8(data[prefixOffset+1]),
Metric: binary.BigEndian.Uint16(data[prefixOffset+2 : prefixOffset+4]),
AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8],
}
prefixes = append(prefixes, prefix)
prefixOffset = prefixOffset + 4 + uint32(prefixLen)
}
content = IntraAreaPrefixLSA{
NumOfPrefixes: numOfPrefixes,
RefLSType: binary.BigEndian.Uint16(data[22:24]),
RefLinkStateID: binary.BigEndian.Uint32(data[24:28]),
RefAdvRouter: binary.BigEndian.Uint32(data[28:32]),
Prefixes: prefixes,
}
default:
return nil, fmt.Errorf("Unknown Link State type.")
}
return content, nil
}
// getLSAs parses the LSA information from the packet for OSPFv3
func getLSAs(num uint32, data []byte) ([]LSA, error) {
var lsas []LSA
var i uint32 = 0
var offset uint32 = 0
for ; i < num; i++ {
var content interface{}
lstype := binary.BigEndian.Uint16(data[offset+2 : offset+4])
lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20])
content, err := extractLSAInformation(lstype, lsalength, data[offset:])
if err != nil {
return nil, fmt.Errorf("Could not extract Link State type.")
}
lsa := LSA{
LSAheader: LSAheader{
LSAge: binary.BigEndian.Uint16(data[offset : offset+2]),
LSType: lstype,
LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]),
AdvRouter: binary.BigEndian.Uint32(data[offset+8 : offset+12]),
LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]),
LSChecksum: binary.BigEndian.Uint16(data[offset+16 : offset+18]),
Length: lsalength,
},
Content: content,
}
lsas = append(lsas, lsa)
offset += uint32(lsalength)
}
return lsas, nil
}
// DecodeFromBytes decodes the given bytes into the OSPF layer.
func (ospf *OSPFv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 24 {
return fmt.Errorf("Packet too smal for OSPF Version 2")
}
ospf.Version = uint8(data[0])
ospf.Type = OSPFType(data[1])
ospf.PacketLength = binary.BigEndian.Uint16(data[2:4])
ospf.RouterID = binary.BigEndian.Uint32(data[4:8])
ospf.AreaID = binary.BigEndian.Uint32(data[8:12])
ospf.Checksum = binary.BigEndian.Uint16(data[12:14])
ospf.AuType = binary.BigEndian.Uint16(data[14:16])
ospf.Authentication = binary.BigEndian.Uint64(data[16:24])
switch ospf.Type {
case OSPFHello:
var neighbors []uint32
for i := 44; uint16(i+4) <= ospf.PacketLength; i += 4 {
neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4]))
}
ospf.Content = HelloPkgV2{
NetworkMask: binary.BigEndian.Uint32(data[24:28]),
HelloPkg: HelloPkg{
HelloInterval: binary.BigEndian.Uint16(data[28:30]),
Options: uint32(data[30]),
RtrPriority: uint8(data[31]),
RouterDeadInterval: binary.BigEndian.Uint32(data[32:36]),
DesignatedRouterID: binary.BigEndian.Uint32(data[36:40]),
BackupDesignatedRouterID: binary.BigEndian.Uint32(data[40:44]),
NeighborID: neighbors,
},
}
case OSPFDatabaseDescription:
var lsas []LSAheader
for i := 32; uint16(i+20) <= ospf.PacketLength; i += 20 {
lsa := LSAheader{
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
}
lsas = append(lsas, lsa)
}
ospf.Content = DbDescPkg{
InterfaceMTU: binary.BigEndian.Uint16(data[24:26]),
Options: uint32(data[26]),
Flags: uint16(data[27]),
DDSeqNumber: binary.BigEndian.Uint32(data[28:32]),
LSAinfo: lsas,
}
case OSPFLinkStateRequest:
var lsrs []LSReq
for i := 24; uint16(i+12) <= ospf.PacketLength; i += 12 {
lsr := LSReq{
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
LSID: binary.BigEndian.Uint32(data[i+4 : i+8]),
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
}
lsrs = append(lsrs, lsr)
}
ospf.Content = lsrs
case OSPFLinkStateUpdate:
num := binary.BigEndian.Uint32(data[24:28])
lsas, err := getLSAsv2(num, data[28:])
if err != nil {
return fmt.Errorf("Cannot parse Link State Update packet: %v", err)
}
ospf.Content = LSUpdate{
NumOfLSAs: num,
LSAs: lsas,
}
case OSPFLinkStateAcknowledgment:
var lsas []LSAheader
for i := 24; uint16(i+20) <= ospf.PacketLength; i += 20 {
lsa := LSAheader{
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
LSOptions: data[i+2],
LSType: uint16(data[i+3]),
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
}
lsas = append(lsas, lsa)
}
ospf.Content = lsas
}
return nil
}
// DecodeFromBytes decodes the given bytes into the OSPF layer.
func (ospf *OSPFv3) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 16 {
return fmt.Errorf("Packet too smal for OSPF Version 3")
}
ospf.Version = uint8(data[0])
ospf.Type = OSPFType(data[1])
ospf.PacketLength = binary.BigEndian.Uint16(data[2:4])
ospf.RouterID = binary.BigEndian.Uint32(data[4:8])
ospf.AreaID = binary.BigEndian.Uint32(data[8:12])
ospf.Checksum = binary.BigEndian.Uint16(data[12:14])
ospf.Instance = uint8(data[14])
ospf.Reserved = uint8(data[15])
switch ospf.Type {
case OSPFHello:
var neighbors []uint32
for i := 36; uint16(i+4) <= ospf.PacketLength; i += 4 {
neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4]))
}
ospf.Content = HelloPkg{
InterfaceID: binary.BigEndian.Uint32(data[16:20]),
RtrPriority: uint8(data[20]),
Options: binary.BigEndian.Uint32(data[21:25]) >> 8,
HelloInterval: binary.BigEndian.Uint16(data[24:26]),
RouterDeadInterval: uint32(binary.BigEndian.Uint16(data[26:28])),
DesignatedRouterID: binary.BigEndian.Uint32(data[28:32]),
BackupDesignatedRouterID: binary.BigEndian.Uint32(data[32:36]),
NeighborID: neighbors,
}
case OSPFDatabaseDescription:
var lsas []LSAheader
for i := 28; uint16(i+20) <= ospf.PacketLength; i += 20 {
lsa := LSAheader{
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
}
lsas = append(lsas, lsa)
}
ospf.Content = DbDescPkg{
Options: binary.BigEndian.Uint32(data[16:20]) & 0x00FFFFFF,
InterfaceMTU: binary.BigEndian.Uint16(data[20:22]),
Flags: binary.BigEndian.Uint16(data[22:24]),
DDSeqNumber: binary.BigEndian.Uint32(data[24:28]),
LSAinfo: lsas,
}
case OSPFLinkStateRequest:
var lsrs []LSReq
for i := 16; uint16(i+12) <= ospf.PacketLength; i += 12 {
lsr := LSReq{
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
LSID: binary.BigEndian.Uint32(data[i+4 : i+8]),
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
}
lsrs = append(lsrs, lsr)
}
ospf.Content = lsrs
case OSPFLinkStateUpdate:
num := binary.BigEndian.Uint32(data[16:20])
lsas, err := getLSAs(num, data[20:])
if err != nil {
return fmt.Errorf("Cannot parse Link State Update packet: %v", err)
}
ospf.Content = LSUpdate{
NumOfLSAs: num,
LSAs: lsas,
}
case OSPFLinkStateAcknowledgment:
var lsas []LSAheader
for i := 16; uint16(i+20) <= ospf.PacketLength; i += 20 {
lsa := LSAheader{
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
}
lsas = append(lsas, lsa)
}
ospf.Content = lsas
default:
}
return nil
}
// LayerType returns LayerTypeOSPF
func (ospf *OSPFv2) LayerType() gopacket.LayerType {
return LayerTypeOSPF
}
func (ospf *OSPFv3) LayerType() gopacket.LayerType {
return LayerTypeOSPF
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (ospf *OSPFv2) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
func (ospf *OSPFv3) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (ospf *OSPFv2) CanDecode() gopacket.LayerClass {
return LayerTypeOSPF
}
func (ospf *OSPFv3) CanDecode() gopacket.LayerClass {
return LayerTypeOSPF
}
func decodeOSPF(data []byte, p gopacket.PacketBuilder) error {
if len(data) < 14 {
return fmt.Errorf("Packet too smal for OSPF")
}
switch uint8(data[0]) {
case 2:
ospf := &OSPFv2{}
return decodingLayerDecoder(ospf, data, p)
case 3:
ospf := &OSPFv3{}
return decodingLayerDecoder(ospf, data, p)
default:
}
return fmt.Errorf("Unable to determine OSPF type.")
}