From 3d6486b86ad54bdbaa6b3e4518d5d02a8f58a18e 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 | 110 +++++++++++- 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_proto_test.cc | 307 +++++++++++++++++++++++++++++++-- 12 files changed, 473 insertions(+), 38 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 6360d4891ae..09d6fcb4aba 100644 --- a/src/bgp/bgp_aspath.cc +++ b/src/bgp/bgp_aspath.cc @@ -56,6 +56,15 @@ std::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 a29c6372e02..184fe9d6270 100644 --- a/src/bgp/bgp_aspath.h +++ b/src/bgp/bgp_aspath.h @@ -63,6 +63,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 72245db4acb..8f0871273d3 100644 --- a/src/bgp/bgp_attr.cc +++ b/src/bgp/bgp_attr.cc @@ -5,6 +5,7 @@ #include "bgp/bgp_attr.h" #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); @@ -149,6 +150,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) { @@ -161,9 +181,11 @@ 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); + const PmsiTunnelSpec &rhs = static_cast(rhs_attr); + KEY_COMPARE(tunnel_flags, rhs.tunnel_flags); + KEY_COMPARE(tunnel_type, rhs.tunnel_type); + KEY_COMPARE(label, rhs.label); + KEY_COMPARE(identifier, rhs.identifier); return 0; } @@ -255,13 +277,47 @@ void EdgeDiscoverySpec::Edge::SetLabels( labels.push_back(last_label); } +template +int STLSortedCompare(InputIterator first1, InputIterator last1, + InputIterator first2, InputIterator last2, + CompareOp op) { + InputIterator iter1 = first1; + InputIterator iter2 = first2; + while (iter1 != last1 && iter2 != last2) { + int result = op(*iter1, *iter2); + if (result != 0) { + return result; + } + ++iter1; + ++iter2; + } + if (iter1 != last1) { + return 1; + } + if (iter2 != last2) { + return -1; + } + return 0; +} + +struct EdgeDiscoverySpecEdgeCompare { + int operator()(const EdgeDiscoverySpec::Edge *lhs, + const EdgeDiscoverySpec::Edge *rhs) const { + KEY_COMPARE(lhs->address, rhs->address); + KEY_COMPARE(lhs->labels, rhs->labels); + return 0; + } +}; + 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; + static_cast(rhs_attr); + ret = STLSortedCompare(edge_list.begin(), edge_list.end(), + rhs.edge_list.begin(), rhs.edge_list.end(), + EdgeDiscoverySpecEdgeCompare()); + return ret; } void EdgeDiscoverySpec::ToCanonical(BgpAttr *attr) { @@ -285,6 +341,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; @@ -349,13 +416,26 @@ void EdgeForwardingSpec::Edge::SetOutboundIp4Address(Ip4Address addr) { outbound_address.begin()); } +struct EdgeForwardingSpecEdgeCompare { + int operator()(const EdgeForwardingSpec::Edge *lhs, + const EdgeForwardingSpec::Edge *rhs) const { + KEY_COMPARE(lhs->inbound_address, rhs->inbound_address); + KEY_COMPARE(lhs->outbound_address, rhs->outbound_address); + KEY_COMPARE(lhs->inbound_label, rhs->inbound_label); + KEY_COMPARE(lhs->outbound_label, rhs->outbound_label); + return 0; + } +}; + 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; + static_cast(rhs_attr); + ret = STLSortedCompare(edge_list.begin(), edge_list.end(), + rhs.edge_list.begin(), rhs.edge_list.end(), + EdgeForwardingSpecEdgeCompare()); + return ret; } void EdgeForwardingSpec::ToCanonical(BgpAttr *attr) { @@ -380,6 +460,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 717b9aeaad7..f3cb92531c5 100644 --- a/src/bgp/bgp_attr.h +++ b/src/bgp/bgp_attr.h @@ -150,6 +150,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; @@ -237,6 +238,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; @@ -298,6 +300,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 2d95b1bb9d1..e8fcdd1b1f0 100644 --- a/src/bgp/bgp_attr_base.cc +++ b/src/bgp/bgp_attr_base.cc @@ -32,10 +32,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 47a9dcc0294..41ab8298aa2 100644 --- a/src/bgp/bgp_attr_base.h +++ b/src/bgp/bgp_attr_base.h @@ -16,7 +16,8 @@ class BgpAttr; -struct BgpAttribute : public ParseObject { +class BgpAttribute : public ParseObject { +public: enum Flag { Optional = 1 << 7, Transitive = 1 << 6, @@ -59,6 +60,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 5cd65cab2de..6d894481ec1 100644 --- a/src/bgp/bgp_proto.cc +++ b/src/bgp/bgp_proto.cc @@ -575,7 +575,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 { @@ -807,7 +807,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 : @@ -1379,6 +1387,7 @@ class BgpPathAttribute : public ProtoChoice { mpl::pair, BgpAttrTemplate >, + mpl::pair, BgpPathAttributeAsPath>, mpl::pair, BgpAttrTemplate >, @@ -1392,12 +1401,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 ffcb9ba50f3..fb00a519d50 100644 --- a/src/bgp/bgp_session.cc +++ b/src/bgp/bgp_session.cc @@ -77,9 +77,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; @@ -92,5 +91,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 a880c65bc71..0f4fc10c276 100644 --- a/src/bgp/community.cc +++ b/src/bgp/community.cc @@ -44,6 +44,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); } @@ -66,6 +70,10 @@ std::string ExtCommunitySpec::ToString() const { return std::string(repr); } +size_t ExtCommunitySpec::EncodeLength() const { + return communities.size() * sizeof(uint64_t); +} + void ExtCommunitySpec::ToCanonical(BgpAttr *attr) { attr->set_ext_community(this); } diff --git a/src/bgp/community.h b/src/bgp/community.h index 7c67dc128ee..2bd8eb2677d 100644 --- a/src/bgp/community.h +++ b/src/bgp/community.h @@ -35,6 +35,7 @@ struct CommunitySpec : public BgpAttribute { } virtual void ToCanonical(BgpAttr *attr); virtual std::string ToString() const; + virtual size_t EncodeLength() const; }; class Community { @@ -108,11 +109,13 @@ class CommunityDB : public BgpPathAttributeDB communities; virtual int CompareTo(const BgpAttribute &rhs_attr) const { int ret = BgpAttribute::CompareTo(rhs_attr); 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) {