diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go index 1aa9e92a1cb8d8f97088145a666ac3553d10e8ac..305ae994d1cdb383ac13a4c1455510d3cce0509d 100644 --- a/protocols/bgp/packet/mp_reach_nlri.go +++ b/protocols/bgp/packet/mp_reach_nlri.go @@ -7,6 +7,7 @@ import ( "github.com/taktv6/tflow2/convert" bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/types" ) // MultiProtocolReachNLRI represents network layer reachability information for one prefix of an IP address family (rfc4760) @@ -15,9 +16,10 @@ type MultiProtocolReachNLRI struct { SAFI uint8 NextHop bnet.IP Prefixes []bnet.Prefix + PathID uint32 } -func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint16 { +func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer, opt *types.Options) uint16 { nextHop := n.NextHop.Bytes() tempBuf := bytes.NewBuffer(nil) @@ -27,6 +29,9 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint16 { tempBuf.Write(nextHop) tempBuf.WriteByte(0) // RESERVED for _, pfx := range n.Prefixes { + if opt.AddPathRX { + tempBuf.Write(convert.Uint32Byte(n.PathID)) + } tempBuf.Write(serializePrefix(pfx)) } diff --git a/protocols/bgp/packet/mp_reach_nlri_test.go b/protocols/bgp/packet/mp_reach_nlri_test.go index f16ac3c4a181eb3bd0534f0e831bbb00eb09c8cf..73a52bba8c517f8924e65181cafcf376be3dddb0 100644 --- a/protocols/bgp/packet/mp_reach_nlri_test.go +++ b/protocols/bgp/packet/mp_reach_nlri_test.go @@ -5,6 +5,7 @@ import ( "testing" bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/stretchr/testify/assert" ) @@ -13,6 +14,7 @@ func TestSerializeMultiProtocolReachNLRI(t *testing.T) { name string nlri MultiProtocolReachNLRI expected []byte + addPath bool }{ { name: "Simple IPv6 prefix", @@ -32,12 +34,35 @@ func TestSerializeMultiProtocolReachNLRI(t *testing.T) { 0x30, 0x26, 0x00, 0x00, 0x06, 0xff, 0x05, // Prefix }, }, + { + name: "IPv6 prefix with ADD-PATH", + nlri: MultiProtocolReachNLRI{ + AFI: IPv6AFI, + SAFI: UnicastSAFI, + NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2), + Prefixes: []bnet.Prefix{ + bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48), + }, + PathID: 100, + }, + expected: []byte{ + 0x00, 0x02, // AFI + 0x01, // SAFI + 0x10, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // NextHop + 0x00, // RESERVED + 0x00, 0x00, 0x00, 100, // PathID + 0x30, 0x26, 0x00, 0x00, 0x06, 0xff, 0x05, // Prefix + }, + addPath: true, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { buf := &bytes.Buffer{} - test.nlri.serialize(buf) + test.nlri.serialize(buf, &types.Options{ + AddPathRX: test.addPath, + }) assert.Equal(t, test.expected, buf.Bytes()) }) } diff --git a/protocols/bgp/packet/mp_unreach_nlri.go b/protocols/bgp/packet/mp_unreach_nlri.go index 3a78b00f7701a76a663005c404e4757eb934404c..2e3475425ddb19292a6c4d5f355c8456aeac7b03 100644 --- a/protocols/bgp/packet/mp_unreach_nlri.go +++ b/protocols/bgp/packet/mp_unreach_nlri.go @@ -5,6 +5,7 @@ import ( "fmt" bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/taktv6/tflow2/convert" ) @@ -13,13 +14,17 @@ type MultiProtocolUnreachNLRI struct { AFI uint16 SAFI uint8 Prefixes []bnet.Prefix + PathID uint32 } -func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint16 { +func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer, opt *types.Options) uint16 { tempBuf := bytes.NewBuffer(nil) tempBuf.Write(convert.Uint16Byte(n.AFI)) tempBuf.WriteByte(n.SAFI) for _, pfx := range n.Prefixes { + if opt.AddPathRX { + tempBuf.Write(convert.Uint32Byte(n.PathID)) + } tempBuf.Write(serializePrefix(pfx)) } diff --git a/protocols/bgp/packet/mp_unreach_nlri_test.go b/protocols/bgp/packet/mp_unreach_nlri_test.go index 02e1b00fff6d161b37509e1748d20bd04dcb3d35..c4484f97f71d182a8886854063368b9127154a8f 100644 --- a/protocols/bgp/packet/mp_unreach_nlri_test.go +++ b/protocols/bgp/packet/mp_unreach_nlri_test.go @@ -5,6 +5,7 @@ import ( "testing" bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/stretchr/testify/assert" ) @@ -13,6 +14,7 @@ func TestSerializeMultiProtocolUnreachNLRI(t *testing.T) { name string nlri MultiProtocolUnreachNLRI expected []byte + addPath bool }{ { name: "Simple IPv6 prefix", @@ -29,12 +31,32 @@ func TestSerializeMultiProtocolUnreachNLRI(t *testing.T) { 0x2c, 0x26, 0x20, 0x01, 0x10, 0x90, 0x00, // Prefix }, }, + { + name: "IPv6 prefix with ADD-PATH", + nlri: MultiProtocolUnreachNLRI{ + AFI: IPv6AFI, + SAFI: UnicastSAFI, + Prefixes: []bnet.Prefix{ + bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + }, + PathID: 100, + }, + expected: []byte{ + 0x00, 0x02, // AFI + 0x01, // SAFI + 0x00, 0x00, 0x00, 100, // PathID + 0x2c, 0x26, 0x20, 0x01, 0x10, 0x90, 0x00, // Prefix + }, + addPath: true, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { buf := &bytes.Buffer{} - test.nlri.serialize(buf) + test.nlri.serialize(buf, &types.Options{ + AddPathRX: test.addPath, + }) assert.Equal(t, test.expected, buf.Bytes()) }) } diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index 22ea8a8c3e1c0ba253e98c87296a7bdde0b92f02..5b5af4ecd816650ed5575dd16ede7f529fc349aa 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -468,9 +468,9 @@ func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *types.Options) uint16 case LargeCommunitiesAttr: pathAttrLen = uint16(pa.serializeLargeCommunities(buf)) case MultiProtocolReachNLRICode: - pathAttrLen = pa.serializeMultiProtocolReachNLRI(buf) + pathAttrLen = pa.serializeMultiProtocolReachNLRI(buf, opt) case MultiProtocolUnreachNLRICode: - pathAttrLen = pa.serializeMultiProtocolUnreachNLRI(buf) + pathAttrLen = pa.serializeMultiProtocolUnreachNLRI(buf, opt) case OriginatorIDAttr: pathAttrLen = uint16(pa.serializeOriginatorID(buf)) case ClusterListAttr: @@ -695,22 +695,22 @@ func (pa *PathAttribute) serializeUnknownAttribute(buf *bytes.Buffer) uint16 { return uint16(len(b) + 2) } -func (pa *PathAttribute) serializeMultiProtocolReachNLRI(buf *bytes.Buffer) uint16 { +func (pa *PathAttribute) serializeMultiProtocolReachNLRI(buf *bytes.Buffer, opt *types.Options) uint16 { v := pa.Value.(MultiProtocolReachNLRI) pa.Optional = true tempBuf := bytes.NewBuffer(nil) - v.serialize(tempBuf) + v.serialize(tempBuf, opt) return pa.serializeGeneric(tempBuf.Bytes(), buf) } -func (pa *PathAttribute) serializeMultiProtocolUnreachNLRI(buf *bytes.Buffer) uint16 { +func (pa *PathAttribute) serializeMultiProtocolUnreachNLRI(buf *bytes.Buffer, opt *types.Options) uint16 { v := pa.Value.(MultiProtocolUnreachNLRI) pa.Optional = true tempBuf := bytes.NewBuffer(nil) - v.serialize(tempBuf) + v.serialize(tempBuf, opt) return pa.serializeGeneric(tempBuf.Bytes(), buf) } diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index 3ece04475f645340a60e814e3452110ce56ab5ff..5ac4e09aa72251641c3e6268badabf90ddcf7792 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -179,10 +179,7 @@ func (s *openSentState) processMultiProtocolCapability(cap packet.MultiProtocolC } func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapability) { - if addPathCap.AFI != 1 { - return - } - if addPathCap.SAFI != 1 { + if addPathCap.SAFI != packet.UnicastSAFI { return } diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index 02cf053bf000c5d1dd232c2243b7b617062a8fcf..6cb5da02fb20ce1d5e8772dd8fdeab9e210e5886 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -136,10 +136,7 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error caps := make(packet.Capabilities, 0) - addPathEnabled, addPathCap := handleAddPathCapability(c) - if addPathEnabled { - caps = append(caps, addPathCap) - } + caps = append(caps, addPathCapabilities(c)...) caps = append(caps, asn4Capability(c)) @@ -174,7 +171,9 @@ func multiProtocolCapability(afi uint16) packet.Capability { } } -func handleAddPathCapability(c config.Peer) (bool, packet.Capability) { +func addPathCapabilities(c config.Peer) []packet.Capability { + caps := make([]packet.Capability, 0) + addPath := uint8(0) if c.AddPathRecv { addPath += packet.AddPathReceive @@ -184,17 +183,30 @@ func handleAddPathCapability(c config.Peer) (bool, packet.Capability) { } if addPath == 0 { - return false, packet.Capability{} + return caps } - return true, packet.Capability{ + caps = append(caps, packet.Capability{ Code: packet.AddPathCapabilityCode, Value: packet.AddPathCapability{ AFI: packet.IPv4AFI, SAFI: packet.UnicastSAFI, SendReceive: addPath, }, + }) + + if c.IPv6 { + caps = append(caps, packet.Capability{ + Code: packet.AddPathCapabilityCode, + Value: packet.AddPathCapability{ + AFI: packet.IPv6AFI, + SAFI: packet.UnicastSAFI, + SendReceive: addPath, + }, + }) } + + return caps } func filterOrDefault(f *filter.Filter) *filter.Filter { diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 478e9a08b137bf306ddbab0453b42ad869636411..f154cab7bcdac3ef38a054d23bb2d6b1e03419cd 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -102,6 +102,10 @@ func (u *UpdateSender) sender(aggrTime time.Duration) { for _, pfx := range pathNLRIs.pfxs { budget -= int(packet.BytesInAddr(pfx.Pfxlen())) + 1 + if u.fsm.options.AddPathRX { + budget -= 4 + } + if budget < 0 { updatesPrefixes = append(updatesPrefixes, prefixes) prefixes = make([]bnet.Prefix, 0, 1) @@ -183,6 +187,7 @@ func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.Pat SAFI: packet.UnicastSAFI, NextHop: nextHop, Prefixes: pfxs, + PathID: pathID, }, } attrs.Next = pa @@ -224,7 +229,7 @@ func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool { func (u *UpdateSender) withdrawPrefix(pfx bnet.Prefix, p *route.Path) error { if u.fsm.options.SupportsMultiProtocol { - return withDrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx) + return withDrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx, p) } return withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 013f6ce33c5b247d6021292c3b611f3c325f24f5..c3793bf7c86016413651f7747ff303cc3532d912 100644 --- a/protocols/bgp/server/withdraw.go +++ b/protocols/bgp/server/withdraw.go @@ -59,7 +59,12 @@ func withDrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix, return serializeAndSendUpdate(out, update, opt) } -func withDrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Prefix) error { +func withDrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Prefix, p *route.Path) error { + pathID := uint32(0) + if p.BGPPath != nil { + pathID = p.BGPPath.PathIdentifier + } + update := &packet.BGPUpdate{ PathAttributes: &packet.PathAttribute{ TypeCode: packet.MultiProtocolUnreachNLRICode, @@ -67,8 +72,10 @@ func withDrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Pr AFI: packet.IPv6AFI, SAFI: packet.UnicastSAFI, Prefixes: []net.Prefix{pfx}, + PathID: pathID, }, }, } + return serializeAndSendUpdate(out, update, opt) } diff --git a/protocols/bgp/server/withdraw_test.go b/protocols/bgp/server/withdraw_test.go index 393be032dcd178b2cef772f576b24e1c5b2d29e4..59fc61780bde64a90bfe3c1f46d43b9b0774ad28 100644 --- a/protocols/bgp/server/withdraw_test.go +++ b/protocols/bgp/server/withdraw_test.go @@ -91,7 +91,7 @@ func TestWithDrawPrefixesMultiProtocol(t *testing.T) { opt := &types.Options{ AddPathRX: false, } - err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix) + err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix, &route.Path{}) if err != nil { t.Fatalf("unexpected error: %v", err) }