Skip to content

Commit

Permalink
Add MRT-specific MP_REACH_NLRI encoding
Browse files Browse the repository at this point in the history
This form, as defined in RFC 6396 4.3.4, is found in many public MRT
dumps.
  • Loading branch information
hfreeb authored and fujita committed Jul 1, 2023
1 parent 78e9380 commit c801e18
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 41 deletions.
90 changes: 69 additions & 21 deletions pkg/packet/bgp/bgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,21 @@ import (
)

type MarshallingOption struct {
AddPath map[RouteFamily]BGPAddPathMode
Attributes map[BGPAttrType]bool
AddPath map[RouteFamily]BGPAddPathMode
Attributes map[BGPAttrType]bool
ImplicitPrefix AddrPrefixInterface
}

// GetImplicitPrefix gets the implicit prefix associated with decoding/serialisation. This is used for
// the MRT representation of MP_REACH_NLRI (see RFC 6396 4.3.4).
func GetImplicitPrefix(options []*MarshallingOption) AddrPrefixInterface {
for _, opt := range options {
if opt != nil && opt.ImplicitPrefix != nil {
return opt.ImplicitPrefix
}
}

return nil
}

func IsAddPathEnabled(decode bool, f RouteFamily, options []*MarshallingOption) bool {
Expand Down Expand Up @@ -10952,19 +10965,35 @@ func (p *PathAttributeMpReachNLRI) DecodeFromBytes(data []byte, options ...*Mars
if p.Length < 3 {
return NewMessageError(eCode, eSubCode, value, "mpreach header length is short")
}
afi := binary.BigEndian.Uint16(value[0:2])
safi := value[2]

var afi uint16
var safi uint8

// In MRT dumps, AFI+SAFI+NLRI is implicit based on RIB Entry Header, see RFC 6396 4.3.4
implicitPrefix := GetImplicitPrefix(options)
if implicitPrefix == nil {
afi = binary.BigEndian.Uint16(value[0:2])
safi = value[2]

value = value[3:]
} else {
afi = implicitPrefix.AFI()
safi = implicitPrefix.SAFI()

p.Value = []AddrPrefixInterface{implicitPrefix}
}

p.AFI = afi
p.SAFI = safi
_, err = NewPrefixFromRouteFamily(afi, safi)
if err != nil {
return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, eData, err.Error())
}
nexthoplen := int(value[3])
if len(value) < 4+nexthoplen {
nexthoplen := int(value[0])
if len(value) < 1+nexthoplen {
return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is short")
}
nexthopbin := value[4 : 4+nexthoplen]
nexthopbin := value[1 : 1+nexthoplen]
if nexthoplen > 0 {
v4addrlen := 4
v6addrlen := 16
Expand All @@ -10984,7 +11013,13 @@ func (p *PathAttributeMpReachNLRI) DecodeFromBytes(data []byte, options ...*Mars
return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is incorrect")
}
}
value = value[4+nexthoplen:]

// NLRI implicit for MRT dumps
if implicitPrefix != nil {
return nil
}

value = value[1+nexthoplen:]
// skip reserved
if len(value) == 0 {
return NewMessageError(eCode, eSubCode, value, "no skip byte")
Expand Down Expand Up @@ -11030,27 +11065,40 @@ func (p *PathAttributeMpReachNLRI) Serialize(options ...*MarshallingOption) ([]b
if p.LinkLocalNexthop != nil && p.LinkLocalNexthop.IsLinkLocalUnicast() {
nexthoplen = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
}
buf := make([]byte, 4+nexthoplen)
binary.BigEndian.PutUint16(buf[0:], afi)
buf[2] = safi
buf[3] = uint8(nexthoplen)
var buf []byte
includeNLRI := GetImplicitPrefix(options) == nil
if includeNLRI {
family := make([]byte, 3)
binary.BigEndian.PutUint16(family[0:], afi)
family[2] = safi

buf = append(buf, family...)
}
buf = append(buf, uint8(nexthoplen))
if nexthoplen != 0 {
nexthop := make([]byte, nexthoplen)

if p.Nexthop.To4() == nil {
copy(buf[4+offset:], p.Nexthop.To16())
copy(nexthop[offset:], p.Nexthop.To16())

if nexthoplen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL {
copy(buf[4+offset+16:], p.LinkLocalNexthop.To16())
copy(nexthop[offset+16:], p.LinkLocalNexthop.To16())
}
} else {
copy(buf[4+offset:], p.Nexthop)
copy(nexthop[offset:], p.Nexthop)
}

buf = append(buf, nexthop...)
}
buf = append(buf, 0)
for _, prefix := range p.Value {
pbuf, err := prefix.Serialize(options...)
if err != nil {
return nil, err
if includeNLRI {
buf = append(buf, 0)
for _, prefix := range p.Value {
pbuf, err := prefix.Serialize(options...)
if err != nil {
return nil, err
}
buf = append(buf, pbuf...)
}
buf = append(buf, pbuf...)
}
return p.PathAttribute.Serialize(buf, options...)
}
Expand Down
32 changes: 32 additions & 0 deletions pkg/packet/bgp/bgp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,38 @@ func Test_MpReachNLRIWithIPv4PrefixWithIPv6Nexthop(t *testing.T) {
assert.Equal(bufin, bufout)
}

func Test_MpReachNLRIWithImplicitPrefix(t *testing.T) {
assert := assert.New(t)
bufin := []byte{
0x80, 0x0e, 0x11, // flags(1), type(1), length(1)
0x10, // nexthoplen(1)
0x20, 0x01, 0x0d, 0xb8, // nexthop(32)
0x00, 0x01, 0x00, 0x00, // = "2001:db8:1::1"
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
}
prefix := NewIPAddrPrefix(24, "192.168.10.0")
// Test DecodeFromBytes()
p := &PathAttributeMpReachNLRI{}
option := &MarshallingOption{ImplicitPrefix: prefix}
err := p.DecodeFromBytes(bufin, option)
assert.Nil(err)
// Test decoded values
assert.Equal(BGPAttrFlag(0x80), p.Flags)
assert.Equal(BGPAttrType(0xe), p.Type)
assert.Equal(uint16(0x11), p.Length)
assert.Equal(prefix.AFI(), p.AFI)
assert.Equal(prefix.SAFI(), p.SAFI)
assert.Equal(net.ParseIP("2001:db8:1::1"), p.Nexthop)
value := []AddrPrefixInterface{prefix}
assert.Equal(value, p.Value)
// Test Serialize()
bufout, err := p.Serialize(option)
assert.Nil(err)
// Test serialised value
assert.Equal(bufin, bufout)
}

func Test_ParseRouteDistinguisher(t *testing.T) {
assert := assert.New(t)

Expand Down
22 changes: 6 additions & 16 deletions pkg/packet/mrt/mrt.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ type RibEntry struct {

var errNotAllRibEntryBytesAvailable = errors.New("not all RibEntry bytes are available")

func (e *RibEntry) DecodeFromBytes(data []byte) ([]byte, error) {
func (e *RibEntry) DecodeFromBytes(data []byte, prefix bgp.AddrPrefixInterface) ([]byte, error) {
if len(data) < 8 {
return nil, errNotAllRibEntryBytesAvailable
}
Expand All @@ -384,7 +384,7 @@ func (e *RibEntry) DecodeFromBytes(data []byte) ([]byte, error) {
if err != nil {
return nil, err
}
err = p.DecodeFromBytes(data)
err = p.DecodeFromBytes(data, &bgp.MarshallingOption{ImplicitPrefix: prefix})
if err != nil {
return nil, err
}
Expand All @@ -398,21 +398,11 @@ func (e *RibEntry) DecodeFromBytes(data []byte) ([]byte, error) {
return data, nil
}

func (e *RibEntry) Serialize() ([]byte, error) {
func (e *RibEntry) Serialize(prefix bgp.AddrPrefixInterface) ([]byte, error) {
pbuf := make([]byte, 0)
totalLen := 0
for _, pattr := range e.PathAttributes {
// TODO special modification is needed for MP_REACH_NLRI
// but also Quagga doesn't implement this.
//
// RFC 6396 4.3.4
// There is one exception to the encoding of BGP attributes for the BGP
// MP_REACH_NLRI attribute (BGP Type Code 14).
// Since the AFI, SAFI, and NLRI information is already encoded
// in the RIB Entry Header or RIB_GENERIC Entry Header,
// only the Next Hop Address Length and Next Hop Address fields are included.

pb, err := pattr.Serialize()
pb, err := pattr.Serialize(&bgp.MarshallingOption{ImplicitPrefix: prefix})
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -492,7 +482,7 @@ func (u *Rib) DecodeFromBytes(data []byte) error {
e := &RibEntry{
isAddPath: u.isAddPath,
}
data, err = e.DecodeFromBytes(data)
data, err = e.DecodeFromBytes(data, prefix)
if err != nil {
return err
}
Expand Down Expand Up @@ -524,7 +514,7 @@ func (u *Rib) Serialize() ([]byte, error) {
}
buf = append(buf, bbuf...)
for _, entry := range u.Entries {
bbuf, err = entry.Serialize()
bbuf, err = entry.Serialize(u.Prefix)
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/packet/mrt/mrt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ func TestMrtRibEntry(t *testing.T) {
}

e1 := NewRibEntry(1, uint32(time.Now().Unix()), 0, p, false)
b1, err := e1.Serialize()
b1, err := e1.Serialize(nil)
if err != nil {
t.Fatal(err)
}

e2 := &RibEntry{}
rest, err := e2.DecodeFromBytes(b1)
rest, err := e2.DecodeFromBytes(b1, nil)
if err != nil {
t.Fatal(err)
}
Expand All @@ -145,13 +145,13 @@ func TestMrtRibEntryWithAddPath(t *testing.T) {
bgp.NewPathAttributeLocalPref(1 << 22),
}
e1 := NewRibEntry(1, uint32(time.Now().Unix()), 200, p, true)
b1, err := e1.Serialize()
b1, err := e1.Serialize(nil)
if err != nil {
t.Fatal(err)
}

e2 := &RibEntry{isAddPath: true}
rest, err := e2.DecodeFromBytes(b1)
rest, err := e2.DecodeFromBytes(b1, nil)
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit c801e18

Please sign in to comment.