From ffe1a389e9570875433d1c4b87f8f0f1b45904af Mon Sep 17 00:00:00 2001 From: Pedro Marques Date: Wed, 13 May 2015 01:19:22 +0000 Subject: [PATCH] Add BGP ExtendedLength attribute encoding. Add support for Extended Length encoding for variable length BGP attributes. Revamp the exploratory tests so that larger messages get generated. Fix the encoding of Unknown Attributes. When generating NOTIFICATIONs use a max message size buffer and do not assert if encoding fails. Closes-bug: #1454431 Change-Id: I545e28f61b4d7a840b73fb890da88a6322b2ecbf --- src/base/proto.h | 13 +- src/bgp/bgp_aspath.cc | 9 + src/bgp/bgp_aspath.h | 1 + src/bgp/bgp_attr.cc | 52 +++++- src/bgp/bgp_attr.h | 3 + src/bgp/bgp_attr_base.cc | 14 +- src/bgp/bgp_attr_base.h | 13 +- src/bgp/bgp_proto.cc | 18 +- src/bgp/bgp_session.cc | 10 +- src/bgp/community.cc | 8 + src/bgp/community.h | 5 +- src/bgp/test/bgp_attr_test.cc | 9 - src/bgp/test/bgp_proto_test.cc | 307 +++++++++++++++++++++++++++++++-- 13 files changed, 415 insertions(+), 47 deletions(-) diff --git a/src/base/proto.h b/src/base/proto.h index 8f0dce827e6..91d1e42a59a 100644 --- a/src/base/proto.h +++ b/src/base/proto.h @@ -439,7 +439,7 @@ class ProtoChoice : public ChoiceBase { ChoiceEncoder(EncodeContext *context, const T *msg, uint8_t *data, int size, int *resultp) : context(context), msg(msg), data(data), size(size), - resultp(resultp) { + resultp(resultp), found(false) { } template struct EncoderTrue { @@ -465,8 +465,9 @@ class ProtoChoice : public ChoiceBase { struct EncoderSetter { int operator()(int opt, EncodeContext *context, const CtxType *msg, uint8_t *data, int size) { - if (Derived::Setter::get(msg) == opt) { - return EncoderTrue()(opt, context, msg, data, size); + int value = Derived::Setter::get(msg); + if (value == opt || opt == -1) { + return EncoderTrue()(value, context, msg, data, size); } return 0; } @@ -508,7 +509,7 @@ class ProtoChoice : public ChoiceBase { }; template void operator()(U x) { - if (*resultp < 0) { + if (*resultp < 0 || found) { return; } // The choice element can be determined by: @@ -534,7 +535,8 @@ class ProtoChoice : public ChoiceBase { int result = choice(U::first::value, context, msg, data, size); if (result < 0) { *resultp = result; - } else { + } else if (result > 0) { + found = true; *resultp += result; } } @@ -544,6 +546,7 @@ class ProtoChoice : public ChoiceBase { uint8_t *data; int size; int *resultp; + bool found; }; }; diff --git a/src/bgp/bgp_aspath.cc b/src/bgp/bgp_aspath.cc index cb3611734df..67b94bb656b 100644 --- a/src/bgp/bgp_aspath.cc +++ b/src/bgp/bgp_aspath.cc @@ -58,6 +58,15 @@ string AsPathSpec::ToString() const { return oss.str(); } +size_t AsPathSpec::EncodeLength() const { + size_t sz = 0; + for (size_t i = 0; i < path_segments.size(); i++) { + sz += 2; + sz += path_segments[i]->path_segment.size() * 2; + } + return sz; +} + bool AsPathSpec::AsPathLoop(as_t as) const { for (size_t i = 0; i < path_segments.size(); i++) for (size_t j = 0; j < path_segments[i]->path_segment.size(); j++) diff --git a/src/bgp/bgp_aspath.h b/src/bgp/bgp_aspath.h index 9f3d9f4370d..35c67861f99 100644 --- a/src/bgp/bgp_aspath.h +++ b/src/bgp/bgp_aspath.h @@ -66,6 +66,7 @@ struct AsPathSpec : public BgpAttribute { virtual int CompareTo(const BgpAttribute &rhs_attr) const; virtual void ToCanonical(BgpAttr *attr); + virtual size_t EncodeLength() const; virtual std::string ToString() const; AsPathSpec *Add(as_t asn) const; std::vector path_segments; diff --git a/src/bgp/bgp_attr.cc b/src/bgp/bgp_attr.cc index 2e5936689de..35a9554ee15 100644 --- a/src/bgp/bgp_attr.cc +++ b/src/bgp/bgp_attr.cc @@ -9,6 +9,7 @@ #include "base/util.h" #include "bgp/bgp_proto.h" +#include "net/bgp_af.h" int BgpAttrOrigin::CompareTo(const BgpAttribute &rhs_attr) const { int ret = BgpAttribute::CompareTo(rhs_attr); @@ -153,6 +154,25 @@ int BgpMpNlri::CompareTo(const BgpAttribute &rhs_attr) const { void BgpMpNlri::ToCanonical(BgpAttr *attr) { } +size_t BgpMpNlri::EncodeLength() const { + size_t sz = 2 /* safi */ + 1 /* afi */ + + 1 /* NlriNextHopLength */ + + 1 /* Reserved */; + sz += nexthop.size(); + for (std::vector::const_iterator iter = nlri.begin(); + iter != nlri.end(); ++iter) { + size_t bytes = 0; + if (afi == BgpAf::L2Vpn && + (safi == BgpAf::EVpn || safi == BgpAf::ErmVpn)) { + bytes = (*iter)->prefixlen; + } else { + bytes = ((*iter)->prefixlen + 7) / 8; + } + sz += 1 + bytes; + } + return sz; +} + PmsiTunnelSpec::PmsiTunnelSpec() : BgpAttribute(PmsiTunnel, kFlags), tunnel_flags(0), tunnel_type(0), label(0) { @@ -165,9 +185,6 @@ PmsiTunnelSpec::PmsiTunnelSpec(const BgpAttribute &rhs) int PmsiTunnelSpec::CompareTo(const BgpAttribute &rhs_attr) const { int ret = BgpAttribute::CompareTo(rhs_attr); if (ret != 0) return ret; - const PmsiTunnelSpec &rhs = - static_cast(rhs_attr); - KEY_COMPARE(this, &rhs); return 0; } @@ -262,9 +279,6 @@ void EdgeDiscoverySpec::Edge::SetLabels( int EdgeDiscoverySpec::CompareTo(const BgpAttribute &rhs_attr) const { int ret = BgpAttribute::CompareTo(rhs_attr); if (ret != 0) return ret; - const EdgeDiscoverySpec &rhs = - static_cast(rhs_attr); - KEY_COMPARE(this, &rhs); return 0; } @@ -289,6 +303,17 @@ std::string EdgeDiscoverySpec::ToString() const { return oss.str(); } +size_t EdgeDiscoverySpec::EncodeLength() const { + size_t sz = 0; + for (EdgeList::const_iterator iter = edge_list.begin(); + iter != edge_list.end(); ++iter) { + sz += 2; /* AddressLen + LabelLen */ + sz += (*iter)->address.size(); + sz += (*iter)->labels.size() * sizeof(uint32_t); + } + return sz; +} + EdgeDiscovery::Edge::Edge(const EdgeDiscoverySpec::Edge *spec_edge) { address = spec_edge->GetIp4Address(); uint32_t first_label, last_label; @@ -356,9 +381,6 @@ void EdgeForwardingSpec::Edge::SetOutboundIp4Address(Ip4Address addr) { int EdgeForwardingSpec::CompareTo(const BgpAttribute &rhs_attr) const { int ret = BgpAttribute::CompareTo(rhs_attr); if (ret != 0) return ret; - const EdgeForwardingSpec &rhs = - static_cast(rhs_attr); - KEY_COMPARE(this, &rhs); return 0; } @@ -384,6 +406,18 @@ std::string EdgeForwardingSpec::ToString() const { return oss.str(); } +size_t EdgeForwardingSpec::EncodeLength() const { + size_t sz = 0; + for (EdgeList::const_iterator iter = edge_list.begin(); + iter != edge_list.end(); ++iter) { + sz += 1 /* address len */ + 8 /* 2 labels */; + sz += (*iter)->inbound_address.size(); + sz += (*iter)->outbound_address.size(); + } + + return sz; +} + EdgeForwarding::Edge::Edge(const EdgeForwardingSpec::Edge *spec_edge) { inbound_address = spec_edge->GetInboundIp4Address(); outbound_address = spec_edge->GetOutboundIp4Address(); diff --git a/src/bgp/bgp_attr.h b/src/bgp/bgp_attr.h index 4956c86bbdf..15b615481d2 100644 --- a/src/bgp/bgp_attr.h +++ b/src/bgp/bgp_attr.h @@ -165,6 +165,7 @@ struct BgpMpNlri : public BgpAttribute { } virtual int CompareTo(const BgpAttribute &rhs_attr) const; virtual void ToCanonical(BgpAttr *attr); + virtual size_t EncodeLength() const; uint16_t afi; uint8_t safi; @@ -252,6 +253,7 @@ struct EdgeDiscoverySpec : public BgpAttribute { virtual int CompareTo(const BgpAttribute &rhs_attr) const; virtual void ToCanonical(BgpAttr *attr); virtual std::string ToString() const; + virtual size_t EncodeLength() const; struct Edge : public ParseObject { Ip4Address GetIp4Address() const; @@ -313,6 +315,7 @@ struct EdgeForwardingSpec : public BgpAttribute { virtual int CompareTo(const BgpAttribute &rhs_attr) const; virtual void ToCanonical(BgpAttr *attr); virtual std::string ToString() const; + virtual size_t EncodeLength() const; struct Edge : public ParseObject { Ip4Address GetInboundIp4Address() const; diff --git a/src/bgp/bgp_attr_base.cc b/src/bgp/bgp_attr_base.cc index 634ceee27bd..b341eeb626b 100644 --- a/src/bgp/bgp_attr_base.cc +++ b/src/bgp/bgp_attr_base.cc @@ -33,10 +33,22 @@ void BgpProtoPrefix::WriteLabel(size_t label_offset, uint32_t label) { int BgpAttribute::CompareTo(const BgpAttribute &rhs) const { KEY_COMPARE(code, rhs.code); KEY_COMPARE(subcode, rhs.subcode); - KEY_COMPARE(flags, rhs.flags); + KEY_COMPARE(flags & ~ExtendedLength, rhs.flags & ~ExtendedLength); return 0; } +size_t BgpAttribute::EncodeLength() const { + return 0; +} + +uint8_t BgpAttribute::GetEncodeFlags() const { + uint8_t value = flags; + if (EncodeLength() >= sizeof(uint8_t) << 8) { + value |= BgpAttribute::ExtendedLength; + } + return value; +} + std::string BgpAttribute::ToString() const { char repr[80]; snprintf(repr, sizeof(repr), "", code, flags); diff --git a/src/bgp/bgp_attr_base.h b/src/bgp/bgp_attr_base.h index a6a4f7555b1..7e28494134d 100644 --- a/src/bgp/bgp_attr_base.h +++ b/src/bgp/bgp_attr_base.h @@ -19,7 +19,8 @@ class BgpAttr; -struct BgpAttribute : public ParseObject { +class BgpAttribute : public ParseObject { +public: enum Flag { Optional = 1 << 7, Transitive = 1 << 6, @@ -62,6 +63,16 @@ struct BgpAttribute : public ParseObject { uint8_t code; uint8_t subcode; uint8_t flags; + /* + * Variable length attributes should return the size of the attribute + * for encoding purposes in order to set the ExtendedLength flag. + */ + virtual size_t EncodeLength() const; + /* + * Helper method to compute flags used when encoding attributes. + */ + uint8_t GetEncodeFlags() const; + virtual std::string ToString() const; virtual int CompareTo(const BgpAttribute &rhs) const; virtual void ToCanonical(BgpAttr *attr) { } diff --git a/src/bgp/bgp_proto.cc b/src/bgp/bgp_proto.cc index 86bb953bd59..10793c8aea5 100644 --- a/src/bgp/bgp_proto.cc +++ b/src/bgp/bgp_proto.cc @@ -586,7 +586,7 @@ class BgpPathAttrLength : public ProtoElement { typedef AttrLen SequenceLength; struct AttrSizeSet { static int get(const BgpAttribute *obj) { - if (obj->flags & BgpAttribute::ExtendedLength) { + if (obj->GetEncodeFlags() & BgpAttribute::ExtendedLength) { // Extended Length: use 2 bytes to read return 2; } else { @@ -820,7 +820,15 @@ class BgpPathAttributeAsPath : public ProtoSequence { class BgpPathAttributeFlags : public ProtoElement { public: static const int kSize = 1; - typedef Accessor Setter; + struct FlagsAccessor { + static void set(BgpAttribute *obj, uint8_t value) { + obj->flags = value; + } + static uint8_t get(const BgpAttribute *obj) { + return obj->GetEncodeFlags(); + } + }; + typedef FlagsAccessor Setter; }; class BgpPathAttributeCommunityList : @@ -1395,6 +1403,7 @@ class BgpPathAttribute : public ProtoChoice { mpl::pair, BgpAttrTemplate >, + mpl::pair, BgpPathAttributeAsPath>, mpl::pair, BgpAttrTemplate >, @@ -1408,12 +1417,11 @@ class BgpPathAttribute : public ProtoChoice { BgpPathAttributeAtomicAggregate>, mpl::pair, BgpPathAttributeAggregator>, + mpl::pair, + BgpPathAttributeCommunities>, mpl::pair, BgpAttrTemplate >, - mpl::pair, BgpPathAttributeAsPath>, - mpl::pair, - BgpPathAttributeCommunities>, mpl::pair, BgpPathAttributeMpReachNlriSequence>, mpl::pair, diff --git a/src/bgp/bgp_session.cc b/src/bgp/bgp_session.cc index 8297999c0df..7f4b3248dce 100644 --- a/src/bgp/bgp_session.cc +++ b/src/bgp/bgp_session.cc @@ -79,9 +79,8 @@ void BgpSession::SendNotification(int code, int subcode, msg.error = code; msg.subcode = subcode; msg.data = data; - uint8_t buf[256]; - int result = BgpProto::Encode(&msg, buf, sizeof(buf)); - assert(result > BgpProto::kMinMessageSize); + uint8_t buf[BgpProto::kMaxMessageSize]; + int msglen = BgpProto::Encode(&msg, buf, sizeof(buf)); // Use SYS_DEBUG for connection collision, SYS_NOTICE for the rest. SandeshLevel::type log_level; @@ -94,5 +93,8 @@ void BgpSession::SendNotification(int code, int subcode, BGP_LOG(BgpPeerNotification, log_level, BGP_LOG_FLAG_ALL, peer_ ? peer_->ToUVEKey() : ToString(), BGP_PEER_DIR_OUT, code, subcode, msg.ToString()); - Send(buf, result, NULL); + + if (msglen > BgpProto::kMinMessageSize) { + Send(buf, msglen, NULL); + } } diff --git a/src/bgp/community.cc b/src/bgp/community.cc index 1f09495e038..50403e7392e 100644 --- a/src/bgp/community.cc +++ b/src/bgp/community.cc @@ -51,6 +51,10 @@ int Community::CompareTo(const Community &rhs) const { return 0; } +size_t CommunitySpec::EncodeLength() const { + return communities.size() * sizeof(uint32_t); +} + void Community::Remove() { comm_db_->Delete(this); } @@ -73,6 +77,10 @@ string ExtCommunitySpec::ToString() const { return string(repr); } +size_t ExtCommunitySpec::EncodeLength() const { + return communities.size() * sizeof(uint64_t); +} + int ExtCommunitySpec::CompareTo(const BgpAttribute &rhs_attr) const { int ret = BgpAttribute::CompareTo(rhs_attr); if (ret != 0) return ret; diff --git a/src/bgp/community.h b/src/bgp/community.h index b638db9deb9..1722361d941 100644 --- a/src/bgp/community.h +++ b/src/bgp/community.h @@ -37,6 +37,7 @@ struct CommunitySpec : public BgpAttribute { } virtual void ToCanonical(BgpAttr *attr); virtual std::string ToString() const; + virtual size_t EncodeLength() const; }; class Community { @@ -113,11 +114,13 @@ class CommunityDB : public BgpPathAttributeDB communities; virtual int CompareTo(const BgpAttribute &rhs_attr) const; virtual void ToCanonical(BgpAttr *attr); diff --git a/src/bgp/test/bgp_attr_test.cc b/src/bgp/test/bgp_attr_test.cc index 7530e4be6d8..f734c70b9e4 100644 --- a/src/bgp/test/bgp_attr_test.cc +++ b/src/bgp/test/bgp_attr_test.cc @@ -681,10 +681,7 @@ TEST_F(BgpAttrTest, PmsiTunnel6) { TEST_F(BgpAttrTest, PmsiTunnelSpecCompareTo) { PmsiTunnelSpec pmsi_spec1; - PmsiTunnelSpec pmsi_spec2; EXPECT_EQ(0, pmsi_spec1.CompareTo(pmsi_spec1)); - EXPECT_NE(0, pmsi_spec1.CompareTo(pmsi_spec2)); - EXPECT_NE(0, pmsi_spec2.CompareTo(pmsi_spec1)); } TEST_F(BgpAttrTest, PmsiTunnelSpecToString1) { @@ -846,10 +843,7 @@ TEST_F(BgpAttrTest, EdgeDiscovery6) { TEST_F(BgpAttrTest, EdgeDiscoveryCompareTo) { EdgeDiscoverySpec edspec1; - EdgeDiscoverySpec edspec2; EXPECT_EQ(0, edspec1.CompareTo(edspec1)); - EXPECT_NE(0, edspec1.CompareTo(edspec2)); - EXPECT_NE(0, edspec2.CompareTo(edspec1)); } TEST_F(BgpAttrTest, EdgeDiscoveryToString1) { @@ -1023,10 +1017,7 @@ TEST_F(BgpAttrTest, EdgeForwarding6) { TEST_F(BgpAttrTest, EdgeForwardingCompareTo) { EdgeForwardingSpec efspec1; - EdgeForwardingSpec efspec2; EXPECT_EQ(0, efspec1.CompareTo(efspec1)); - EXPECT_NE(0, efspec1.CompareTo(efspec2)); - EXPECT_NE(0, efspec2.CompareTo(efspec1)); } TEST_F(BgpAttrTest, EdgeForwardingToString1) { diff --git a/src/bgp/test/bgp_proto_test.cc b/src/bgp/test/bgp_proto_test.cc index 104a9791fd5..b1baa0ebb5b 100644 --- a/src/bgp/test/bgp_proto_test.cc +++ b/src/bgp/test/bgp_proto_test.cc @@ -130,7 +130,7 @@ class BuildUpdateMessage { static void AddCommunity(BgpProto::Update *msg) { CommunitySpec *community = new CommunitySpec; - for (int len = rand() % 10; len > 0; len--) { + for (int len = rand() % 32; len > 0; len--) { community->communities.push_back(rand()); } msg->path_attributes.push_back(community); @@ -139,7 +139,7 @@ class BuildUpdateMessage { static void AddMpNlri(BgpProto::Update *msg) { static const int kMaxRoutes = 500; BgpMpNlri *mp_nlri = new BgpMpNlri; - mp_nlri->flags = BgpAttribute::Optional|BgpAttribute::ExtendedLength; + mp_nlri->flags = BgpAttribute::Optional; mp_nlri->code = BgpAttribute::MPReachNlri; mp_nlri->afi = 1; mp_nlri->safi = 128; @@ -160,7 +160,7 @@ class BuildUpdateMessage { static void AddExtCommunity(BgpProto::Update *msg) { ExtCommunitySpec *ext_community = new ExtCommunitySpec; - for (int i = rand() % 10; i > 0; i--) { + for (int i = rand() % 64; i > 0; i--) { ext_community->communities.push_back(rand()); } msg->path_attributes.push_back(ext_community); @@ -178,7 +178,7 @@ class BuildUpdateMessage { static void AddEdgeDiscovery(BgpProto::Update *msg) { EdgeDiscoverySpec *edspec = new EdgeDiscoverySpec; - for (int i = rand() % 4; i > 0; i--) { + for (int i = rand() % 64; i > 0; i--) { boost::system::error_code ec; EdgeDiscoverySpec::Edge *edge = new EdgeDiscoverySpec::Edge; edge->SetIp4Address(Ip4Address::from_string("10.1.1.1", ec)); @@ -190,7 +190,7 @@ class BuildUpdateMessage { static void AddEdgeForwarding(BgpProto::Update *msg) { EdgeForwardingSpec *efspec = new EdgeForwardingSpec; - for (int i = rand() % 4; i > 0; i--) { + for (int i = rand() % 64; i > 0; i--) { boost::system::error_code ec; EdgeForwardingSpec::Edge *edge = new EdgeForwardingSpec::Edge; edge->SetInboundIp4Address(Ip4Address::from_string("10.1.1.1", ec)); @@ -205,7 +205,8 @@ class BuildUpdateMessage { static void AddUnknown(BgpProto::Update *msg) { BgpAttrUnknown *unk = new BgpAttrUnknown; unk->flags = BgpAttribute::Optional; - unk->code = (rand() % 236) + 20; + // Code must be different than attributes define in BgpAttribute::Code + unk->code = (rand() % 64) + 64; for (int len = rand() % 8; len > 0; len--) { unk->value.push_back(rand()); } @@ -392,7 +393,51 @@ TEST_F(BgpProtoTest, L3VPNUpdate) { } } +TEST_F(BgpProtoTest, 32PlusExtCommunities) { + BgpProto::Update update; + BgpMessageTest::GenerateUpdateMessage(&update, BgpAf::IPv4, BgpAf::Vpn); + uint8_t data[BgpProto::kMaxMessageSize]; + + ExtCommunitySpec *ext_community = NULL; + for (size_t i = 0; i < update.path_attributes.size(); ++i) { + BgpAttribute *attr = update.path_attributes[i]; + if (attr->code == BgpAttribute::ExtendedCommunities) { + ext_community = static_cast(attr); + break; + } + } + ASSERT_TRUE(ext_community != NULL); + for (uint i = 0; i < 32; ++i) { + uint64_t value = 0x0002fc00007a1200ULL; + ext_community->communities.push_back(value + i); + } + int res = BgpProto::Encode(&update, data, BgpProto::kMaxMessageSize); + EXPECT_NE(-1, res); + if (detail::debug_) { + for (int i = 0; i < res; i++) { + printf("%02x ", data[i]); + } + printf("\n"); + } + + const BgpProto::Update *result; + result = static_cast(BgpProto::Decode(data, res)); + EXPECT_TRUE(result != NULL); + if (result) { + EXPECT_EQ(0, result->CompareTo(update)); + delete result; + } else { + for (int i = 0; i < res; i++) { + if (i % 16 == 0) { + printf("\n"); + } + printf("%02x ", data[i]); + } + printf("\n"); + } + +} TEST_F(BgpProtoTest, EvpnUpdate) { BgpProto::Update update; @@ -608,6 +653,17 @@ TEST_F(BgpProtoTest, UpdateAttrFlagsError) { } } +TEST_F(BgpProtoTest, LargeNotification) { + BgpProto::Notification msg; + msg.error = BgpProto::Notification::UpdateMsgErr; + msg.subcode = BgpProto::Notification::AttribLengthError; + msg.data = string(300, 'x'); + uint8_t buf[BgpProto::kMaxMessageSize]; + int result = BgpProto::Encode(&msg, buf, sizeof(buf)); + const int minMessageSize = BgpProto::kMinMessageSize; + EXPECT_LT(minMessageSize, result); +} + TEST_F(BgpProtoTest, UpdateScale) { BgpProto::Update update; static const int kMaxRoutes = 500; @@ -638,7 +694,7 @@ TEST_F(BgpProtoTest, UpdateScale) { update.path_attributes.push_back(community); BgpMpNlri *mp_nlri = new BgpMpNlri(BgpAttribute::MPReachNlri); - mp_nlri->flags = BgpAttribute::Optional|BgpAttribute::ExtendedLength; + mp_nlri->flags = BgpAttribute::Optional; mp_nlri->afi = 1; mp_nlri->safi = 128; uint8_t nh[4] = {192,168,1,1}; @@ -678,6 +734,77 @@ TEST_F(BgpProtoTest, UpdateScale) { } } +TEST_F(BgpProtoTest, KeepaliveError) { + uint8_t data[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x13, 0x04 }; + + // marker error + data[15] = 0xf1; + ParseAndVerifyError(data, sizeof(data), BgpProto::Notification::MsgHdrErr, + BgpProto::Notification::ConnNotSync, "BgpMarker", 0, 16); + + // msg length error + data[15] = 0xff; + data[17] = 0; + ParseAndVerifyError(data, sizeof(data), BgpProto::Notification::MsgHdrErr, + BgpProto::Notification::BadMsgLength, "BgpMsgLength", 16, 2); +} + +TEST_F(BgpProtoTest, RandomUpdate) { + uint8_t data[BgpProto::kMaxMessageSize]; + int count = 10000; + + // + // When running under memory profiling mode, this test can take a really + // long time. Hence reduce the number of iterations + // + if (getenv("HEAPCHECK")) count = 100; + for (int i = 0; i < count; i++) { + BgpProto::Update update; + BuildUpdateMessage::Generate(&update); + int msglen = BgpProto::Encode(&update, data, sizeof(data)); + /* + * Some message combinations will generate an update that is too + * big. Ignore this for now since the test generates useful + * permutations of attributes. + */ + if (msglen == -1) { + continue; + } + + ParseErrorContext err; + std::auto_ptr result( + static_cast( + BgpProto::Decode(data, msglen, &err))); + EXPECT_TRUE(result.get() != NULL); + if (result.get() != NULL) { + EXPECT_EQ(0, result->CompareTo(update)); + } else { + string repr(BgpProto::Notification::toString( + (BgpProto::Notification::Code) err.error_code, + err.error_subcode)); + printf("Decode: %s\n", repr.c_str()); + for (int x = 0; x < err.data_size; ++x) { + if (x % 16 == 0) { + printf("\n"); + } + printf("%02x ", err.data[x]); + } + + printf("UPDATE: \n"); + for (int x = 0; x < msglen; ++x) { + if (x % 16 == 0) { + printf("\n"); + } + printf("%02x ", data[x]); + } + printf("\n"); + + } + } +} + TEST_F(BgpProtoTest, RandomError) { uint8_t data[4096]; int count = 10000; @@ -690,18 +817,174 @@ TEST_F(BgpProtoTest, RandomError) { for (int i = 0; i < count; i++) { BgpProto::Update update; BuildUpdateMessage::Generate(&update); - int res = BgpProto::Encode(&update, data, sizeof(data)); - EXPECT_NE(-1, res); + int msglen = BgpProto::Encode(&update, data, sizeof(data)); + if (msglen == -1) { + continue; + } if (detail::debug_) { - cout << res << " Bytes encoded" << endl; - for (int i = 0; i < res; i++) { + cout << msglen << " Bytes encoded" << endl; + for (int i = 0; i < msglen; i++) { printf("%02x ", data[i]); } printf("\n"); } - GenerateByteError(data, res); + GenerateByteError(data, msglen); } } + +class EncodeLengthTest : public testing::Test { + protected: + + int EncodeAndReadAttributeLength(BgpAttribute *spec) { + BgpProto::Update update; + EncodeOffsets offsets; + update.path_attributes.push_back(spec); + int msglen = BgpProto::Encode(&update, data_, sizeof(data_), &offsets); + EXPECT_LT(0, msglen); + + int offset = offsets.FindOffset("BgpPathAttribute"); + EXPECT_LT(0, offset); + if (offset <= 0) { + return -1; + } + uint8_t flags = data_[offset + 2]; + uint attrlen = 0; + if (flags & BgpAttribute::ExtendedLength) { + attrlen = get_value(&data_[offset + 4], 2); + } else { + attrlen = get_value(&data_[offset + 4], 1); + } + return attrlen; + } + + uint8_t data_[BgpProto::kMaxMessageSize]; +}; + +static void BuildASPath(AsPathSpec::PathSegment *segment) { + for (int i = rand() % 128; i > 0; i--) { + segment->path_segment.push_back(i); + } +} + +TEST_F(EncodeLengthTest, AsPath) { + const int count = 32; + for (int i = 0; i < count; ++i) { + AsPathSpec *spec = new AsPathSpec; + for (int segments = rand() % 16; segments > 0; segments--) { + AsPathSpec::PathSegment *segment = new AsPathSpec::PathSegment; + BuildASPath(segment); + spec->path_segments.push_back(segment); + } + uint encodeLen = spec->EncodeLength(); + int attrlen = EncodeAndReadAttributeLength(spec); + EXPECT_EQ(attrlen, encodeLen); + } +} + +TEST_F(EncodeLengthTest, BgpMpNlri) { + const int count = 32; + for (int i = 0; i < count; ++i) { + BgpMpNlri *mp_nlri = new BgpMpNlri; + mp_nlri->flags = BgpAttribute::Optional; + mp_nlri->code = BgpAttribute::MPReachNlri; + mp_nlri->afi = BgpAf::IPv4; + mp_nlri->safi = BgpAf::Vpn; + if (rand() & 1) { + boost::system::error_code err; + Ip4Address addr = Ip4Address::from_string("127.0.0.1", err); + const Ip4Address::bytes_type &bytes = addr.to_bytes(); + mp_nlri->nexthop.insert( + mp_nlri->nexthop.begin(), bytes.begin(), bytes.end()); + } else { + boost::system::error_code err; + Ip6Address addr = Ip6Address::from_string( + "2001:db8:85a3::8a2e:370:7334", err); + const Ip6Address::bytes_type &bytes = addr.to_bytes(); + mp_nlri->nexthop.insert( + mp_nlri->nexthop.begin(), bytes.begin(), bytes.end()); + } + int len = rand() % 64; + for (int i = 0; i < len; i++) { + BgpProtoPrefix *prefix = new BgpProtoPrefix; + int len = rand() % 16; + prefix->prefixlen = len*8; + for (int j = 0; j < len; j++) { + prefix->prefix.push_back(rand() % 256); + } + mp_nlri->nlri.push_back(prefix); + } + + uint encodeLen = mp_nlri->EncodeLength(); + int attrlen = EncodeAndReadAttributeLength(mp_nlri); + EXPECT_EQ(attrlen, encodeLen); + } +} + +TEST_F(EncodeLengthTest, ExtendedCommunity) { + /* Boundary condition at 32 Extended communities */ + { + ExtCommunitySpec *spec = new ExtCommunitySpec; + for (int j = 32; j > 0; j--) { + spec->communities.push_back(8000000 + j); + } + uint encodeLen = spec->EncodeLength(); + int attrlen = EncodeAndReadAttributeLength(spec); + EXPECT_EQ(attrlen, encodeLen); + } + + const int count = 32; + for (int i = 0; i < count; ++i) { + ExtCommunitySpec *spec = new ExtCommunitySpec; + for (int j = rand() % 128; j > 0; j--) { + spec->communities.push_back(j); + } + uint encodeLen = spec->EncodeLength(); + int attrlen = EncodeAndReadAttributeLength(spec); + EXPECT_EQ(attrlen, encodeLen); + } +} + +TEST_F(EncodeLengthTest, EdgeDiscovery) { + const int count = 32; + for (int i = 0; i < count; ++i) { + EdgeDiscoverySpec *spec = new EdgeDiscoverySpec; + for (int j = rand() % 64; j > 0; j--) { + EdgeDiscoverySpec::Edge *edge = new EdgeDiscoverySpec::Edge; + boost::system::error_code err; + Ip4Address addr = Ip4Address::from_string("192.168.0.1", err); + edge->SetIp4Address(addr); + edge->SetLabels(10000, 20000); + spec->edge_list.push_back(edge); + } + uint encodeLen = spec->EncodeLength(); + int attrlen = EncodeAndReadAttributeLength(spec); + EXPECT_EQ(attrlen, encodeLen); + } +} + +static void BuildEdge(EdgeForwardingSpec::Edge *edge) { + boost::system::error_code err; + edge->SetInboundIp4Address(Ip4Address::from_string("10.1.1.1", err)); + edge->inbound_label = 10000; + edge->SetOutboundIp4Address(Ip4Address::from_string("10.1.1.2", err)); + edge->outbound_label = 10001; +} + +TEST_F(EncodeLengthTest, EdgeForwarding) { + const int count = 32; + for (int i = 0; i < count; ++i) { + EdgeForwardingSpec *spec = new EdgeForwardingSpec; + for (int j = rand() % 64; j > 0; j--) { + EdgeForwardingSpec::Edge *edge = new EdgeForwardingSpec::Edge; + BuildEdge(edge); + spec->edge_list.push_back(edge); + } + uint encodeLen = spec->EncodeLength(); + int attrlen = EncodeAndReadAttributeLength(spec); + EXPECT_EQ(attrlen, encodeLen); + } +} + } // namespace int main(int argc, char **argv) {