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) {