diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go index e83482e5865dcf0b0084a0aa056c838ae075ed78..b714dc19b03333930e6b9b60d53093e15f957314 100644 --- a/protocols/bgp/packet/mp_reach_nlri.go +++ b/protocols/bgp/packet/mp_reach_nlri.go @@ -28,11 +28,7 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptions tempBuf.WriteByte(0) // RESERVED for cur := n.NLRI; cur != nil; cur = cur.Next { - if opt.UseAddPath { - n.NLRI.serializeAddPath(tempBuf) - } else { - n.NLRI.serialize(tempBuf) - } + cur.serialize(tempBuf, opt.UseAddPath) } buf.Write(tempBuf.Bytes()) diff --git a/protocols/bgp/packet/mp_unreach_nlri.go b/protocols/bgp/packet/mp_unreach_nlri.go index f6c365bb220fb4d76165bcbb8fc207a9b77ae3de..ed2c863d1af21b201695dc11b9eebef08c91ed9f 100644 --- a/protocols/bgp/packet/mp_unreach_nlri.go +++ b/protocols/bgp/packet/mp_unreach_nlri.go @@ -4,28 +4,24 @@ import ( "bytes" "fmt" - bnet "github.com/bio-routing/bio-rd/net" "github.com/bio-routing/bio-rd/util/decode" "github.com/taktv6/tflow2/convert" ) // MultiProtocolUnreachNLRI represents network layer withdraw information for one prefix of an IP address family (rfc4760) type MultiProtocolUnreachNLRI struct { - AFI uint16 - SAFI uint8 - Prefixes []bnet.Prefix - PathID uint32 + AFI uint16 + SAFI uint8 + NLRI *NLRI } func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptions) uint16 { tempBuf := bytes.NewBuffer(nil) tempBuf.Write(convert.Uint16Byte(n.AFI)) tempBuf.WriteByte(n.SAFI) - for _, pfx := range n.Prefixes { - if opt.UseAddPath { - tempBuf.Write(convert.Uint32Byte(n.PathID)) - } - tempBuf.Write(serializePrefix(pfx)) + + for cur := n.NLRI; cur != nil; cur = cur.Next { + cur.serialize(tempBuf, opt.UseAddPath) } buf.Write(tempBuf.Bytes()) @@ -33,7 +29,7 @@ func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptio return uint16(tempBuf.Len()) } -func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, error) { +func deserializeMultiProtocolUnreachNLRI(b []byte, addPath bool) (MultiProtocolUnreachNLRI, error) { n := MultiProtocolUnreachNLRI{} prefixesLength := len(b) - 3 // 3 <- AFI + SAFI @@ -41,42 +37,27 @@ func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, er return n, fmt.Errorf("Invalid length of MP_UNREACH_NLRI: expected more than 3 bytes but got %d", len(b)) } - prefixes := make([]byte, prefixesLength) + nlris := make([]byte, prefixesLength) fields := []interface{}{ &n.AFI, &n.SAFI, - &prefixes, + &nlris, } err := decode.Decode(bytes.NewBuffer(b), fields) if err != nil { return MultiProtocolUnreachNLRI{}, err } - if len(prefixes) == 0 { + if len(nlris) == 0 { return n, nil } - idx := uint16(0) - for idx < uint16(len(prefixes)) { - pfxLen := prefixes[idx] - numBytes := uint16(BytesInAddr(pfxLen)) - idx++ - - r := uint16(len(prefixes)) - idx - if r < numBytes { - return MultiProtocolUnreachNLRI{}, fmt.Errorf("expected %d bytes for NLRI, only %d remaining", numBytes, r) - } - - start := idx - end := idx + numBytes - pfx, err := deserializePrefix(prefixes[start:end], pfxLen, n.AFI) - if err != nil { - return MultiProtocolUnreachNLRI{}, err - } - n.Prefixes = append(n.Prefixes, pfx) - - idx = idx + numBytes + buf := bytes.NewBuffer(nlris) + nlri, err := decodeNLRIs(buf, uint16(buf.Len()), n.AFI, addPath) + if err != nil { + return MultiProtocolUnreachNLRI{}, err } + n.NLRI = nlri return n, nil } diff --git a/protocols/bgp/packet/mp_unreach_nlri_test.go b/protocols/bgp/packet/mp_unreach_nlri_test.go index 467b19e030dd208f5e1103d81465107f531591ec..a550f3b3a02f5314c5430e000263a061af680385 100644 --- a/protocols/bgp/packet/mp_unreach_nlri_test.go +++ b/protocols/bgp/packet/mp_unreach_nlri_test.go @@ -20,8 +20,8 @@ func TestSerializeMultiProtocolUnreachNLRI(t *testing.T) { nlri: MultiProtocolUnreachNLRI{ AFI: IPv6AFI, SAFI: UnicastSAFI, - Prefixes: []bnet.Prefix{ - bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + NLRI: &NLRI{ + Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), }, }, expected: []byte{ @@ -35,10 +35,10 @@ func TestSerializeMultiProtocolUnreachNLRI(t *testing.T) { nlri: MultiProtocolUnreachNLRI{ AFI: IPv6AFI, SAFI: UnicastSAFI, - Prefixes: []bnet.Prefix{ - bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + NLRI: &NLRI{ + PathIdentifier: 100, + Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), }, - PathID: 100, }, expected: []byte{ 0x00, 0x02, // AFI diff --git a/protocols/bgp/packet/nlri.go b/protocols/bgp/packet/nlri.go index b5878b56fbe94354811ef0b685d153af9b4b6c0c..1f4f7f6e4293771349b60d1e581e697f5fa085d4 100644 --- a/protocols/bgp/packet/nlri.go +++ b/protocols/bgp/packet/nlri.go @@ -89,20 +89,22 @@ func decodeNLRI(buf *bytes.Buffer, afi uint16, addPath bool) (*NLRI, uint8, erro return nlri, consumed, nil } -func (n *NLRI) serialize(buf *bytes.Buffer) uint8 { - buf.WriteByte(n.Prefix.Pfxlen()) - b := n.Prefix.Addr().Bytes() +func (n *NLRI) serialize(buf *bytes.Buffer, addPath bool) uint8 { + numBytes := uint8(0) - nBytes := BytesInAddr(n.Prefix.Pfxlen()) - buf.Write(b[:nBytes]) + if addPath { + buf.Write(convert.Uint32Byte(n.PathIdentifier)) + numBytes += 4 + } - return nBytes + 1 -} + buf.WriteByte(n.Prefix.Pfxlen()) + numBytes++ -func (n *NLRI) serializeAddPath(buf *bytes.Buffer) uint8 { - buf.Write(convert.Uint32Byte(n.PathIdentifier)) + pfxNumBytes := BytesInAddr(n.Prefix.Pfxlen()) + buf.Write(n.Prefix.Addr().Bytes()[:pfxNumBytes]) + numBytes += pfxNumBytes - return uint8(n.serialize(buf) + 4) + return numBytes } // BytesInAddr gets the amount of bytes needed to encode an NLRI of prefix length pfxlen diff --git a/protocols/bgp/packet/nlri_test.go b/protocols/bgp/packet/nlri_test.go index ddbc8d85ae8a61d1a0c10c6243f98b22cea98443..1efb81eaf44e758685923c2e553d3890a92c5592 100644 --- a/protocols/bgp/packet/nlri_test.go +++ b/protocols/bgp/packet/nlri_test.go @@ -220,6 +220,7 @@ func TestNLRISerialize(t *testing.T) { tests := []struct { name string nlri *NLRI + addPath bool expected []byte }{ { @@ -243,51 +244,38 @@ func TestNLRISerialize(t *testing.T) { }, expected: []byte{17, 100, 200, 128}, }, - } - - for _, test := range tests { - buf := bytes.NewBuffer(nil) - test.nlri.serialize(buf) - res := buf.Bytes() - assert.Equal(t, test.expected, res) - } -} - -func TestNLRIAddPathSerialize(t *testing.T) { - tests := []struct { - name string - nlri *NLRI - expected []byte - }{ { - name: "Test #1", + name: "with add-path #1", nlri: &NLRI{ PathIdentifier: 100, Prefix: bnet.NewPfx(bnet.IPv4FromOctets(1, 2, 3, 0), 25), }, + addPath: true, expected: []byte{0, 0, 0, 100, 25, 1, 2, 3, 0}, }, { - name: "Test #2", + name: "with add-path #2", nlri: &NLRI{ PathIdentifier: 100, Prefix: bnet.NewPfx(bnet.IPv4FromOctets(1, 2, 3, 0), 24), }, + addPath: true, expected: []byte{0, 0, 0, 100, 24, 1, 2, 3}, }, { - name: "Test #3", + name: "with add-path #3", nlri: &NLRI{ PathIdentifier: 100, Prefix: bnet.NewPfx(bnet.IPv4FromOctets(100, 200, 128, 0), 17), }, + addPath: true, expected: []byte{0, 0, 0, 100, 17, 100, 200, 128}, }, } for _, test := range tests { buf := bytes.NewBuffer(nil) - test.nlri.serializeAddPath(buf) + test.nlri.serialize(buf, test.addPath) res := buf.Bytes() assert.Equal(t, test.expected, res) } diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index f162e277801410660406c187d9216400fe294fa3..a09a3e2c81a5e130c4f59fe769f6ed4432db87f1 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -114,7 +114,7 @@ func decodePathAttr(buf *bytes.Buffer, opt *DecodeOptions) (pa *PathAttribute, c return nil, consumed, fmt.Errorf("Failed to multi protocol reachable NLRI: %v", err) } case MultiProtocolUnreachNLRICode: - if err := pa.decodeMultiProtocolUnreachNLRI(buf); err != nil { + if err := pa.decodeMultiProtocolUnreachNLRI(buf, opt.AddPath); err != nil { return nil, consumed, fmt.Errorf("Failed to multi protocol unreachable NLRI: %v", err) } case AS4AggregatorAttr: @@ -153,7 +153,7 @@ func (pa *PathAttribute) decodeMultiProtocolReachNLRI(buf *bytes.Buffer, addPath return nil } -func (pa *PathAttribute) decodeMultiProtocolUnreachNLRI(buf *bytes.Buffer) error { +func (pa *PathAttribute) decodeMultiProtocolUnreachNLRI(buf *bytes.Buffer, addPath bool) error { b := make([]byte, pa.Length) n, err := buf.Read(b) if err != nil { @@ -163,7 +163,7 @@ func (pa *PathAttribute) decodeMultiProtocolUnreachNLRI(buf *bytes.Buffer) error return fmt.Errorf("Unable to read %d bytes from buffer, only got %d bytes", pa.Length, n) } - nlri, err := deserializeMultiProtocolUnreachNLRI(b) + nlri, err := deserializeMultiProtocolUnreachNLRI(b, addPath) if err != nil { return fmt.Errorf("Unable to decode MP_UNREACH_NLRI: %v", err) } diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index 1513fecf0352ad76f1bbafa6e29c75385d1bbf80..07fff30276f065d4f132b84a84f72f1c8593b43f 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -1037,8 +1037,8 @@ func TestDecodeMultiProtocolUnreachNLRI(t *testing.T) { Value: MultiProtocolUnreachNLRI{ AFI: IPv6AFI, SAFI: UnicastSAFI, - Prefixes: []bnet.Prefix{ - bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + NLRI: &NLRI{ + Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), }, }, }, @@ -1073,7 +1073,7 @@ func TestDecodeMultiProtocolUnreachNLRI(t *testing.T) { pa := &PathAttribute{ Length: l, } - err := pa.decodeMultiProtocolUnreachNLRI(bytes.NewBuffer(test.input)) + err := pa.decodeMultiProtocolUnreachNLRI(bytes.NewBuffer(test.input), false) if test.wantFail { if err != nil { diff --git a/protocols/bgp/packet/update.go b/protocols/bgp/packet/update.go index 167439ab06b77d669c5de258a252b5c2105794a6..8d142fdfc08a43480ed7a3789678f99da4a14f4d 100644 --- a/protocols/bgp/packet/update.go +++ b/protocols/bgp/packet/update.go @@ -18,18 +18,11 @@ type BGPUpdate struct { // SerializeUpdate serializes an BGPUpdate to wire format func (b *BGPUpdate) SerializeUpdate(opt *EncodeOptions) ([]byte, error) { budget := MaxLen - MinLen - nlriLen := 0 buf := bytes.NewBuffer(nil) withdrawBuf := bytes.NewBuffer(nil) for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { - if opt.UseAddPath { - nlriLen = int(withdraw.serializeAddPath(withdrawBuf)) - } else { - nlriLen = int(withdraw.serialize(withdrawBuf)) - } - - budget -= nlriLen + budget -= int(withdraw.serialize(withdrawBuf, opt.UseAddPath)) if budget < 0 { return nil, fmt.Errorf("update too long") } @@ -46,13 +39,7 @@ func (b *BGPUpdate) SerializeUpdate(opt *EncodeOptions) ([]byte, error) { nlriBuf := bytes.NewBuffer(nil) for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { - if opt.UseAddPath { - nlriLen = int(nlri.serializeAddPath(nlriBuf)) - } else { - nlriLen = int(nlri.serialize(nlriBuf)) - } - - budget -= nlriLen + budget -= int(nlri.serialize(nlriBuf, opt.UseAddPath)) if budget < 0 { return nil, fmt.Errorf("update too long") } @@ -92,8 +79,7 @@ func (b *BGPUpdate) SerializeUpdateAddPath(opt *EncodeOptions) ([]byte, error) { withdrawBuf := bytes.NewBuffer(nil) for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { - nlriLen := int(withdraw.serialize(withdrawBuf)) - budget -= nlriLen + budget -= int(withdraw.serialize(withdrawBuf, opt.UseAddPath)) if budget < 0 { return nil, fmt.Errorf("update too long") } @@ -110,8 +96,7 @@ func (b *BGPUpdate) SerializeUpdateAddPath(opt *EncodeOptions) ([]byte, error) { nlriBuf := bytes.NewBuffer(nil) for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { - nlriLen := int(nlri.serialize(nlriBuf)) - budget -= nlriLen + budget -= int(nlri.serialize(nlriBuf, opt.UseAddPath)) if budget < 0 { return nil, fmt.Errorf("update too long") } diff --git a/protocols/bgp/server/fsm_address_family.go b/protocols/bgp/server/fsm_address_family.go index 04aa26ab9ed70662bd854cbbbea6a3425d9929d7..772a2dbf436cf1256ce965f16f0cb7ac5a2e1647 100644 --- a/protocols/bgp/server/fsm_address_family.go +++ b/protocols/bgp/server/fsm_address_family.go @@ -161,8 +161,8 @@ func (f *fsmAddressFamily) multiProtocolWithdraw(path *route.Path, nlri packet.M return } - for _, pfx := range nlri.Prefixes { - f.adjRIBIn.RemovePath(pfx, path) + for cur := nlri.NLRI; cur != nil; cur = cur.Next { + f.adjRIBIn.RemovePath(cur.Prefix, path) } } diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 87e999caca45a87a124ee1f2269db7169d0eae80..17b021a55504af3e6cba17285da7a33ba9d0a360 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -311,10 +311,12 @@ func (u *UpdateSender) withdrawPrefixMultiProtocol(out io.Writer, pfx bnet.Prefi PathAttributes: &packet.PathAttribute{ TypeCode: packet.MultiProtocolUnreachNLRICode, Value: packet.MultiProtocolUnreachNLRI{ - AFI: u.addressFamily.afi, - SAFI: u.addressFamily.safi, - Prefixes: []bnet.Prefix{pfx}, - PathID: pathID, + AFI: u.addressFamily.afi, + SAFI: u.addressFamily.safi, + NLRI: &packet.NLRI{ + PathIdentifier: pathID, + Prefix: pfx, + }, }, }, }