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