From 8808902f3d5e768a02f3936c4a1a8732da682895 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Testillano Date: Sun, 22 Mar 2015 20:41:00 +0100 Subject: [PATCH] Fixed Failed-AVP bug (feature). Now wrong avps inside grouped are correctly tracked --- .../ProtocolErrors/BadAARtoServer/case_3.tc | 4 +- include/anna/diameter/codec/Avp.hpp | 17 ++-- include/anna/diameter/codec/Message.hpp | 63 +++++++------- include/anna/diameter/codec/functions.hpp | 22 +++++ source/diameter/codec/Avp.cpp | 84 ++++++++++-------- source/diameter/codec/Message.cpp | 87 +++++++------------ source/diameter/codec/functions.cpp | 41 +++++++++ 7 files changed, 182 insertions(+), 136 deletions(-) diff --git a/example/diameter/launcher/resources/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_3.tc b/example/diameter/launcher/resources/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_3.tc index f1fdf40..06b5efc 100644 --- a/example/diameter/launcher/resources/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_3.tc +++ b/example/diameter/launcher/resources/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_3.tc @@ -8,6 +8,4 @@ WAIT4MESSAGE # Check Failed-AVP & Subscription-Id within: CHECKPATTERN -CHECKPATTERN ( *)( *)( *)( *) diff --git a/include/anna/diameter/codec/Avp.hpp b/include/anna/diameter/codec/Avp.hpp index bdf2227..c991703 100644 --- a/include/anna/diameter/codec/Avp.hpp +++ b/include/anna/diameter/codec/Avp.hpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -205,7 +206,7 @@ class Avp { static Avp * addAvp(avp_container &avps, int &insertionPositionForChilds, AvpId id, Engine *engine) throw(); static bool removeAvp(avp_container &avps, find_container &finds, AvpId id, int ocurrence, Engine *engine) throw(); static void fix(avp_container &avps, find_container &finds, int &insertionPositionForChilds, anna::diameter::stack::const_avprule_iterator ruleBegin, anna::diameter::stack::const_avprule_iterator ruleEnd) throw(); - static bool validLevel(const avp_container &avps, anna::diameter::stack::const_avprule_iterator ruleBegin, anna::diameter::stack::const_avprule_iterator ruleEnd, Engine * engine, const std::string & parentDescription, Message *answer) throw(anna::RuntimeException); // validates mandatory/fixed and cardinality + static bool validLevel(const avp_container &avps, anna::diameter::stack::const_avprule_iterator ruleBegin, anna::diameter::stack::const_avprule_iterator ruleEnd, Engine * engine, const anna::diameter::codec::parent_t & parent, Message *answer) throw(anna::RuntimeException); // validates mandatory/fixed and cardinality static const Avp* getAvp(const avp_container &avps, find_container &finds, AvpId id, int ocurrence, Engine *engine, anna::Exception::Mode::_v emode) throw(anna::RuntimeException); static int countAvp(const avp_container &avps, AvpId id) throw(); static const Avp* firstAvp(const avp_container &avps, AvpId id) throw(); @@ -247,12 +248,12 @@ class Avp { /** Validates an Avp regarding dictionary rules like enumerated range, flags coherence, mandatory and fixed types, cardinality qualifiers, etc. - @param parentDescription Parent description. Internally used for alarms and tracing + @param parent Parent description. Internally used for alarms, tracing and Failed-AVP construction @param answer Answer could be modified with any validation problem during requests validation @return Boolean indicating validation result */ - bool valid(const std::string & parentDescription, Message *answer) const throw(anna::RuntimeException); + bool valid(const anna::diameter::codec::parent_t & parent, Message *answer) const throw(anna::RuntimeException); /** Decodes buffer provided over class content. If an error ocurred, decoding will stop launching exception (fatal error) or a warning trace (perhaps the achieved @@ -260,9 +261,10 @@ class Avp { depending on validation depth (codec::Engine::ValidationDepth). @param db Buffer data block processed + @param parent Parent description. Internally used for alarms, tracing and Failed-AVP construction @param answer Answer built for request decoding/validation */ - void decode(const anna::DataBlock &db, Message *answer) throw(anna::RuntimeException); + void decode(const anna::DataBlock &db, const anna::diameter::codec::parent_t & parent, Message *answer) throw(anna::RuntimeException); ///////////////////////////////////////////// @@ -405,9 +407,10 @@ protected: @param buffer Avp data part start pointer @param size Avp data part size + @param parent Parent description. Internally used for alarms, tracing and Failed-AVP construction @param answer Answer built for request decoding/validation */ - void decodeDataPart(const char * buffer, int size, Message *answer) throw(anna::RuntimeException); + void decodeDataPart(const char * buffer, int size, const anna::diameter::codec::parent_t & parent, Message *answer) throw(anna::RuntimeException); public: @@ -729,10 +732,8 @@ public: Default implementation launch alarm and counter indicating the anomaly but don't launch exception (traces at warning level). Relay and Redirect agents could reimplement this method to avoid oam management (another way is avoid alarm/counter registration on these applications). Result-Code DIAMETER_AVP_UNSUPPORTED will be stored for possible answer message. - - @param answer Answer built for request decoding/validation */ - virtual void unknownAvpWithMandatoryBit(Message *answer) const throw(anna::RuntimeException); + virtual void unknownAvpWithMandatoryBit() const throw(anna::RuntimeException); friend class Message; diff --git a/include/anna/diameter/codec/Message.hpp b/include/anna/diameter/codec/Message.hpp index fb519b5..3f17e09 100644 --- a/include/anna/diameter/codec/Message.hpp +++ b/include/anna/diameter/codec/Message.hpp @@ -146,9 +146,31 @@ class Message { bool flagsOK(int &rc) const throw(); // flags coherence regarding dictionary. Only must be called when Message is identified at the dictionary. int addChild(Avp *avp) throw() { return Avp::addChild(a_avps, a_insertionPositionForChilds, avp); } const anna::diameter::stack::Command *getStackCommand(CommandId id) const throw(anna::RuntimeException); - Avp * addTheFailedAVP() throw(); // returns the Failed-AVP if exists, creates it when missing. The method could be named 'addFailedAVP' - // but we consider this better because only one instance (as RFC 6733 says in section 7.5) will be - // added by internal procedures (although, the application could obviously add more). + + void setFailedAvp(const parent_t &parent, AvpId wrong) throw(anna::RuntimeException); + // During message decoding and validation, the first wrong avp is stored and all the tracking is managed to find out its + // nested path for the case of grouped avps with wrong avps inside. Remember the RFC 6733, section 7.5: + // + // In the case where the offending AVP is embedded within a Grouped AVP, + // the Failed-AVP MAY contain the grouped AVP, which in turn contains + // the single offending AVP. The same method MAY be employed if the + // grouped AVP itself is embedded in yet another grouped AVP and so on. + // In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up + // to the single offending AVP. This enables the recipient to detect + // the location of the offending AVP when embedded in a group. + // + // The first wrong avp found will set the final result code, as the RFC recommends: + // + // The value of the Result-Code AVP will provide information on the reason + // for the Failed-AVP AVP. A Diameter answer message SHOULD contain an + // instance of the Failed-AVP AVP that corresponds to the error + // indicated by the Result-Code AVP. For practical purposes, this + // Failed-AVP would typically refer to the first AVP processing error + // that a Diameter node encounters. + // + // The message keeps the list (reverse order) of avps hierarchy (in case of grouping) for the final Failed-AVP construction, + // which is done at the end of decoding or validation, and only the first wrong avp is stored with its corresponding path. + protected: @@ -342,7 +364,12 @@ public: Result-Code and/or Failed-AVP AVPs if proceed, but be aware of DIAMETER_COMMAND_UNSUPPORTED Result-Code, because becomes impossible to fix (Session-Id SHOULD appear immediately following the Diameter header, and #fix do this manually even if no information about the command structure is known, but perhaps another fixed AVPs could not comply... use #getResultCode to find out this situation before - using #setStandardToAnswer). + using #setStandardToAnswer). Anyway, application could add another Failed-AVP content no detected internally, for example: + DIAMETER_CONTRADICTING_AVPS or DIAMETER_INVALID_AVP_BIT_COMBO). Also, application could add more Failed-AVP avps with other + wrong avps, or accumulate wrong avps inside the one and only Failed-AVP managed by the stack. The standard is open to add multiple + avps inside Failed-AVP or multiple Failed-AVP avps with single or multiple avps inside. This depends on application criteria regarding + other nodes. However, internally the Anna::diameter stack only provides one Failed-AVP with the first wrong avp found, as RFC 6733 says + in section 7.5. If application decoding and/or validation operations are ok, user may search for other problems and put the appropiate Result-Code. For example, DIAMETER_TOO_BUSY (3004) depends on congestion issues at business layer and cannot be decided with the only message @@ -407,34 +434,6 @@ public: int getResultCode() const throw(); - /** - Adds the wrong AVP within the Failed-AVP over an answer message (for requests, do nothing). - If Failed-AVP AVP doesn't exists, is added and then filled (added within) with the value provided (empty AVP id representantion). - If Failed-AVP AVP already exists, is probably filled by a previous found error, but anyway this is verified and if empty then is - filled (added within) with the value provided (empty AVP id representantion). - - This method is internally used during #decode and/or #valid procedures in order to build automatic answers, but application - could call this for set another Failed-AVP content no detected by these methods, for example: DIAMETER_CONTRADICTING_AVPS or - DIAMETER_INVALID_AVP_BIT_COMBO). Also, application could add more Failed-AVP avps with other wrong avps, or accumulate wrong - avps inside the one and only Failed-AVP managed by the stack (see section 7.5 of RFC 6733). - - @param id Avp identifier as pair (code,vendor-id). - - @return Pointer to the new AVP added within Failed-AVP, to make easy data-part access if needed. - */ - Avp * setFailedAvp(AvpId id) throw(anna::RuntimeException); - - /** - Same as #setFailedAvp(AvpId id) but providing an avp pointer with the needed information - - @param avp Pointer to the added wrong avp - - @return Pointer to the new AVP added within Failed-AVP, to make easy data-part access if needed. - */ - Avp * setFailedAvp(Avp *avp) throw(anna::RuntimeException); - - - /** Adds an avp child providing its identifier and reserve internal memory it. diff --git a/include/anna/diameter/codec/functions.hpp b/include/anna/diameter/codec/functions.hpp index 3b12419..9108b19 100644 --- a/include/anna/diameter/codec/functions.hpp +++ b/include/anna/diameter/codec/functions.hpp @@ -45,6 +45,8 @@ // STL #include +#include + //------------------------------------------------------------------------------ //---------------------------------------------------------------------- #define @@ -67,6 +69,25 @@ namespace diameter { namespace codec { +// Used for alarms, tracing and Failed-AVP construction: +typedef struct parent { + + // Used on decoding: + anna::diameter::CommandId MessageId; + std::string MessageName; + + std::vector AvpsId; + std::vector AvpsName; + + void setMessage(const anna::diameter::CommandId & mid, const char *mname = NULL /* well known in validation */) throw(); + void addAvp(const anna::diameter::AvpId & aid, const char *aname = NULL /* well known in validation */) throw(); + std::string asString() const throw(); + +} parent_t; + + + + struct functions { // getters & helpers @@ -81,6 +102,7 @@ struct functions { static bool isAnswer(const CommandId & cid) throw() { return (!isRequest(cid)); } static bool isAnswer(const anna::DataBlock & db) throw(anna::RuntimeException) { return (!isRequest(db)); } + /** * Decodes a Command Header. This helper cannot check boundaries. start pointer must be a valid command context. * diff --git a/source/diameter/codec/Avp.cpp b/source/diameter/codec/Avp.cpp index 7f33851..aa52c5f 100644 --- a/source/diameter/codec/Avp.cpp +++ b/source/diameter/codec/Avp.cpp @@ -632,7 +632,7 @@ U24 Avp::getLength() const throw() { //------------------------------------------------------------------------------ //-------------------------------------------- Avp::unknownAvpWithMandatoryBit() //------------------------------------------------------------------------------ -void Avp::unknownAvpWithMandatoryBit(Message *answer) const throw(anna::RuntimeException) { +void Avp::unknownAvpWithMandatoryBit() const throw(anna::RuntimeException) { OamModule &oamModule = OamModule::instantiate(); const char *c_aid = STRING_WITH_QUOTATION_MARKS__C_STR(anna::diameter::functions::avpIdAsPairString(a_id)); oamModule.activateAlarm(OamModule::Alarm::AvpDecode__UnknownAvp__s__WithMandatoryBit, c_aid); @@ -641,17 +641,12 @@ void Avp::unknownAvpWithMandatoryBit(Message *answer) const throw(anna::RuntimeE std::string msg = anna::functions::asString("Detected unknown Avp %s with mandatory bit activated", c_aid); anna::Logger::warning(msg, ANNA_FILE_LOCATION); ); - - if(answer) { - answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_AVP_UNSUPPORTED); - answer->setFailedAvp((Avp*)this); - } } //------------------------------------------------------------------------------ //-------------------------------------------------------- Avp::decodeDataPart() //------------------------------------------------------------------------------ -void Avp::decodeDataPart(const char * buffer, int size, Message *answer) throw(anna::RuntimeException) { +void Avp::decodeDataPart(const char * buffer, int size, const parent_t & parent, Message *answer) throw(anna::RuntimeException) { // OAM OamModule &oamModule = OamModule::instantiate(); // Dictionary stack avp and format: @@ -682,7 +677,7 @@ void Avp::decodeDataPart(const char * buffer, int size, Message *answer) throw(a if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_LENGTH); - //answer->setFailedAvp(a_id); + answer->setFailedAvp(parent, a_id); } throw anna::RuntimeException("Avp format error, the avp length is incorrect (must be multiple of 4 on grouped type)", ANNA_FILE_LOCATION); @@ -692,11 +687,15 @@ void Avp::decodeDataPart(const char * buffer, int size, Message *answer) throw(a Avp* avp; anna::DataBlock db; + // Me as parent: + parent_t me = parent; + me.addAvp(a_id); + while(avpPos < size) { try { avp = getEngine()->allocateAvp(); db.assign(buffer + avpPos, size - avpPos /* is valid to pass total size (indeed i don't know the real avp size) because it will be limited and this has deep copy disabled (no memory is reserved) */); - avp -> decode(db, answer); + avp -> decode(db, me, answer); } catch(anna::RuntimeException &ex) { getEngine()->releaseAvp(avp); throw; @@ -723,7 +722,7 @@ void Avp::decodeDataPart(const char * buffer, int size, Message *answer) throw(a //------------------------------------------------------------------------------ //---------------------------------------------------------------- Avp::decode() //------------------------------------------------------------------------------ -void Avp::decode(const anna::DataBlock &db, Message *answer) throw(anna::RuntimeException) { +void Avp::decode(const anna::DataBlock &db, const parent_t & parent, Message *answer) throw(anna::RuntimeException) { // OAM OamModule &oamModule = OamModule::instantiate(); @@ -785,7 +784,15 @@ void Avp::decode(const anna::DataBlock &db, Message *answer) throw(anna::Runtime // The 'M' Bit, known as the Mandatory bit, indicates whether support of the AVP is required. If an AVP with the 'M' bit set is received by // a Diameter client, server, proxy, or translation agent and either the AVP or its value is unrecognized, the message MUST be rejected. // Diameter Relay and redirect agents MUST NOT reject messages with unrecognized AVPs. - if(!getStackAvp() && mandatoryBit()) unknownAvpWithMandatoryBit(answer); + if(!getStackAvp() && mandatoryBit()) { + + if(answer) { + answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_AVP_UNSUPPORTED); + answer->setFailedAvp(parent, a_id); + } + + unknownAvpWithMandatoryBit(); + } // Avp Length U24 length = DECODE3BYTES_INDX_VALUETYPE(buffer, 5, U24); @@ -800,21 +807,21 @@ void Avp::decode(const anna::DataBlock &db, Message *answer) throw(anna::Runtime if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_LENGTH); - //answer->setFailedAvp(a_id); + answer->setFailedAvp(parent, a_id); } throw anna::RuntimeException(anna::functions::asString("Avp format error, the avp length is incorrect (avp code = %u)", code), ANNA_FILE_LOCATION); } try { - decodeDataPart(buffer + startDataPos, dataBytes, answer); + decodeDataPart(buffer + startDataPos, dataBytes, parent, answer); } catch(anna::RuntimeException &ex) { oamModule.activateAlarm(OamModule::Alarm::AvpDecode__DataPartInconsistence); oamModule.count(OamModule::Counter::AvpDecode__DataPartInconsistence); if(answer) { - answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_VALUE); // unspecified error ... - //answer->setFailedAvp((Avp*)this); + answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_VALUE); + answer->setFailedAvp(parent, a_id); } throw anna::RuntimeException(anna::functions::asString("Internal Avp decoding error (avp code = %u): %s", code, ex.getText().c_str()), ANNA_FILE_LOCATION); @@ -902,7 +909,7 @@ void Avp::fix() throw() { //------------------------------------------------------------------------------ //------------------------------------------------------------ Avp::validLevel() //------------------------------------------------------------------------------ -bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avprule_iterator ruleBegin, anna::diameter::stack::const_avprule_iterator ruleEnd, Engine * engine, const std::string & parentDescription, Message *answer) throw(anna::RuntimeException) { +bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avprule_iterator ruleBegin, anna::diameter::stack::const_avprule_iterator ruleEnd, Engine * engine, const parent_t & parent, Message *answer) throw(anna::RuntimeException) { bool result = true; // OAM OamModule &oamModule = OamModule::instantiate(); @@ -940,15 +947,15 @@ bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avp if(!okFixed) { result = false; // OAM & Depth management - oamModule.activateAlarm(OamModule::Alarm::LevelValidation__MissingFixedRule__s__Inside__s__, STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/))); + oamModule.activateAlarm(OamModule::Alarm::LevelValidation__MissingFixedRule__s__Inside__s__, STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/)), STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString())); oamModule.count(OamModule::Counter::LevelValidation__MissingFixedRule); if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_MISSING_AVP); - answer->setFailedAvp((*rule_it).second.getId()); + answer->setFailedAvp(parent, (*rule_it).second.getId()); } - engine->validationAnomaly(anna::functions::asString("Missing fixed rule %s inside %s", STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/)), STRING_WITH_QUOTATION_MARKS__C_STR(parentDescription))); + engine->validationAnomaly(anna::functions::asString("Missing fixed rule %s inside %s", STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/)), STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString()))); } } else break; // finish fixed } @@ -971,7 +978,7 @@ bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avp // Failed rule %s for cardinality (found %d items) result = false; // OAM & Depth management - oamModule.activateAlarm(OamModule::Alarm::LevelValidation__FailedRule__s__ForCardinality_Found__d__ItemsInside__s__, STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parentDescription)); + oamModule.activateAlarm(OamModule::Alarm::LevelValidation__FailedRule__s__ForCardinality_Found__d__ItemsInside__s__, STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString())); oamModule.count(OamModule::Counter::LevelValidation__FailedRuleForCardinality); if(amount < min) { @@ -979,18 +986,18 @@ bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avp if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_MISSING_AVP); - answer->setFailedAvp(id); + answer->setFailedAvp(parent, id); } } else { oamModule.count(OamModule::Counter::LevelValidation__FailedRuleForCardinalityMoreThanNeeded); if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_AVP_OCCURS_TOO_MANY_TIMES); - answer->setFailedAvp((Avp*)firstAvp(avps, id) /* first instance */); + answer->setFailedAvp(parent, id); } } - engine->validationAnomaly(anna::functions::asString("Failed rule %s for cardinality (found %d items inside %s)", STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parentDescription))); + engine->validationAnomaly(anna::functions::asString("Failed rule %s for cardinality (found %d items inside %s)", STRING_WITH_QUOTATION_MARKS__C_STR((*rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString()))); } } @@ -1031,15 +1038,15 @@ bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avp // Failed Generic AVP rule %s for cardinality (found %d disregarded items inside %s) result = false; // OAM & Depth management - oamModule.activateAlarm(OamModule::Alarm::LevelValidation__FailedGenericAvpRule__s__ForCardinality_Found__d__DisregardedItemsInside__s__, STRING_WITH_QUOTATION_MARKS__C_STR((*generic_rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parentDescription)); + oamModule.activateAlarm(OamModule::Alarm::LevelValidation__FailedGenericAvpRule__s__ForCardinality_Found__d__DisregardedItemsInside__s__, STRING_WITH_QUOTATION_MARKS__C_STR((*generic_rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString())); oamModule.count(OamModule::Counter::LevelValidation__FailedGenericAvpRuleForCardinalityFoundDisregardedItem); if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_AVP_NOT_ALLOWED); - //answer->setFailedAvp((Avp*)firstAvp(avps, id) /* first instance */); // NO SENSE... what to put ? + answer->setFailedAvp(parent, id); // NO SENSE... what to put ? } - engine->validationAnomaly(anna::functions::asString("Failed Generic AVP rule %s for cardinality (found %d disregarded items inside %s)", STRING_WITH_QUOTATION_MARKS__C_STR((*generic_rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parentDescription))); + engine->validationAnomaly(anna::functions::asString("Failed Generic AVP rule %s for cardinality (found %d disregarded items inside %s)", STRING_WITH_QUOTATION_MARKS__C_STR((*generic_rule_it).second.asString(false /*ommit dots & pair*/)), amount, STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString()))); } } else if(disregardeds) { // When Generic AVP missing, no disregarded Avps are allowed // Found %d disregarded items inside %s and Generic AVP was not specified @@ -1057,15 +1064,15 @@ bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avp // We wouldn't know where are these disregarded, but... if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_AVP_NOT_ALLOWED); - answer->setFailedAvp((Avp*)firstAvp(avps, id) /* first instance */); + answer->setFailedAvp(parent, id); } } s_disregardeds.erase(s_disregardeds.size() - 2, 2); // remove last ', ' // OAM & Depth management - oamModule.activateAlarm(OamModule::Alarm::LevelValidation__FoundDisregardedItemsInside__s__AndGenericAVPWasNotSpecified__s__, STRING_WITH_QUOTATION_MARKS__C_STR(parentDescription), STRING_WITH_QUOTATION_MARKS__C_STR(s_disregardeds)); + oamModule.activateAlarm(OamModule::Alarm::LevelValidation__FoundDisregardedItemsInside__s__AndGenericAVPWasNotSpecified__s__, STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString()), STRING_WITH_QUOTATION_MARKS__C_STR(s_disregardeds)); oamModule.count(OamModule::Counter::LevelValidation__FoundDisregardedItemsAndGenericAVPWasNotSpecified); - engine->validationAnomaly(anna::functions::asString("Found disregarded items inside %s and Generic AVP was not specified: %s", STRING_WITH_QUOTATION_MARKS__C_STR(parentDescription), STRING_WITH_QUOTATION_MARKS__C_STR(s_disregardeds))); + engine->validationAnomaly(anna::functions::asString("Found disregarded items inside %s and Generic AVP was not specified: %s", STRING_WITH_QUOTATION_MARKS__C_STR(parent.asString()), STRING_WITH_QUOTATION_MARKS__C_STR(s_disregardeds))); } return result; @@ -1075,20 +1082,21 @@ bool Avp::validLevel(const avp_container &avps, anna::diameter::stack::const_avp //------------------------------------------------------------------------------ //----------------------------------------------------------------- Avp::valid() //------------------------------------------------------------------------------ -bool Avp::valid(const std::string & parentDescription, Message *answer) const throw(anna::RuntimeException) { +bool Avp::valid(const parent_t & parent, Message *answer) const throw(anna::RuntimeException) { // OAM OamModule &oamModule = OamModule::instantiate(); // Dictionary stack avp: const stack::Avp *stackAvp = getStackAvp(); const stack::Format *stackFormat = stackAvp ? (stackAvp->getFormat()) : NULL /*Unknown*/; - std::string me; if(!stackAvp) { // No dictionary avp reference found. Cannot validate return true; // perhaps a unknown Avp } - me = parentDescription + "->" + stackAvp->getName(); + // Me as parent: + parent_t me = parent; + me.addAvp(a_id, stackAvp->getName().c_str()); if(!stackFormat) { // No format avp reference found. Cannot validate @@ -1102,15 +1110,15 @@ bool Avp::valid(const std::string & parentDescription, Message *answer) const th if(!result) { // OAM & Depth management - oamModule.activateAlarm(OamModule::Alarm::AvpValidation__Avp__s__Flags__d__DoesNotFulfillTheDefinedFlagRules__s__, STRING_WITH_QUOTATION_MARKS__C_STR(me), (int)a_flags, STRING_WITH_QUOTATION_MARKS__C_STR(stackAvp->getFlagRulesDescription())); + oamModule.activateAlarm(OamModule::Alarm::AvpValidation__Avp__s__Flags__d__DoesNotFulfillTheDefinedFlagRules__s__, STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags, STRING_WITH_QUOTATION_MARKS__C_STR(stackAvp->getFlagRulesDescription())); oamModule.count(OamModule::Counter::AvpValidation__AvpFlagsDoesNotFulfillTheDefinedFlagRules); if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_BITS); - answer->setFailedAvp((Avp*)this); // RFC 6733 says nothing about Failed-AVP in this case... + answer->setFailedAvp(parent, a_id); // RFC 6733 says nothing about Failed-AVP in this case... } - getEngine()->validationAnomaly(anna::functions::asString("The AVP %s flags (%d) does not fulfill the defined flag rules: %s", STRING_WITH_QUOTATION_MARKS__C_STR(me), (int)a_flags, STRING_WITH_QUOTATION_MARKS__C_STR(stackAvp->getFlagRulesDescription()))); + getEngine()->validationAnomaly(anna::functions::asString("The AVP %s flags (%d) does not fulfill the defined flag rules: %s", STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags, STRING_WITH_QUOTATION_MARKS__C_STR(stackAvp->getFlagRulesDescription()))); } ////////////////////// @@ -1120,15 +1128,15 @@ bool Avp::valid(const std::string & parentDescription, Message *answer) const th if(!stackAvp->allowEnum(a_Enumerated->getValue())) { result = false; // OAM & Depth management - oamModule.activateAlarm(OamModule::Alarm::AvpValidation__EnumeratedAvp__s__WithValue__d__DoesNotComplyRestriction__s__, STRING_WITH_QUOTATION_MARKS__C_STR(me), a_Enumerated->getValue(), stackAvp->getEnums()); + oamModule.activateAlarm(OamModule::Alarm::AvpValidation__EnumeratedAvp__s__WithValue__d__DoesNotComplyRestriction__s__, STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), a_Enumerated->getValue(), stackAvp->getEnums()); oamModule.count(OamModule::Counter::AvpValidation__EnumeratedAvpWithValueDoesNotComplyRestriction); if(answer) { answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_VALUE); - answer->setFailedAvp((Avp*)this); + answer->setFailedAvp(parent, a_id); } - getEngine()->validationAnomaly(anna::functions::asString("Enumerated AVP %s with value %d does not comply to restriction: %s", STRING_WITH_QUOTATION_MARKS__C_STR(me), a_Enumerated->getValue(), stackAvp->getEnums())); + getEngine()->validationAnomaly(anna::functions::asString("Enumerated AVP %s with value %d does not comply to restriction: %s", STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), a_Enumerated->getValue(), stackAvp->getEnums())); } } diff --git a/source/diameter/codec/Message.cpp b/source/diameter/codec/Message.cpp index 7fc92e8..3a989cf 100644 --- a/source/diameter/codec/Message.cpp +++ b/source/diameter/codec/Message.cpp @@ -59,6 +59,7 @@ // STL #include +#include using namespace anna; @@ -369,16 +370,17 @@ void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna:: Avp* avp; anna::DataBlock db_aux; + // Parent information: + parent_t parent; + parent.setMessage(a_id); + + while(avpPos < dataBytes) { try { avp = getEngine()->allocateAvp(); db_aux.assign(startData + avpPos, dataBytes - avpPos /* is valid to pass total length (indeed i don't know the real avp length) because it will be limited and this has deep copy disabled (no memory is reserved) */); - avp -> decode(db_aux, answer); + avp -> decode(db_aux, parent, answer); } catch(anna::RuntimeException &ex) { - if(answer) { - //answer->setResultCode(helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_AVP_VALUE); // unspecified error ... - answer->setFailedAvp(avp->getId()); - } getEngine()->releaseAvp(avp); LOGWARNING( anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION); @@ -455,25 +457,12 @@ int Message::getResultCode() const throw() { } -//------------------------------------------------------------------------------ -//--------------------------------------------------- Message::addTheFailedAVP() -//------------------------------------------------------------------------------ -Avp * Message::addTheFailedAVP() throw() { - Avp *result = getAvp(helpers::base::AVPID__Failed_AVP, 1, anna::Exception::Mode::Ignore); - - // Section 7.5 RFC 6733: A Diameter message SHOULD contain one Failed-AVP AVP - if(!result) result = addAvp(helpers::base::AVPID__Failed_AVP); - - return result; -} - - //------------------------------------------------------------------------------ //------------------------------------------------------ Message::setFailedAvp() //------------------------------------------------------------------------------ -Avp * Message::setFailedAvp(AvpId id) throw(anna::RuntimeException) { +void Message::setFailedAvp(const parent_t &parent, AvpId wrong) throw(anna::RuntimeException) { - if(isRequest()) return NULL; + if(isRequest()) return; // RFC 6733: // @@ -494,44 +483,30 @@ Avp * Message::setFailedAvp(AvpId id) throw(anna::RuntimeException) { // Probably the RFC wants to give freedom to the application layer, but it is recommended to // have only one child (wrong avp) inside a unique message Failed-AVP to ease the Result-Code // correspondence. Anyway, this behaviour could be easily opened commenting condition block (*). - Avp *theFailedAvp = addTheFailedAVP(); - LOGDEBUG( - std::string msg = "Adding wrong avp "; - msg += anna::diameter::functions::avpIdAsPairString(id); - msg += " within the message Failed-AVP ..."; - anna::Logger::debug(msg, ANNA_FILE_LOCATION); - ); - - if (theFailedAvp->countChilds()) { // (*) - LOGDEBUG(anna::Logger::debug("Discarding wrong avp. A previous wrong avp was already added into the Failed-AVP. RFC 6733 Section 7.5 recommends to store only the first.", ANNA_FILE_LOCATION)); - return NULL; + Avp *theFailedAvp = getAvp(helpers::base::AVPID__Failed_AVP, 1, anna::Exception::Mode::Ignore); + if (theFailedAvp) { + LOGDEBUG(anna::Logger::debug("Failed-AVP has already been added. RFC 6733 Section 7.5 recommends to store only the first error found", ANNA_FILE_LOCATION)); + return; } - return (theFailedAvp->addAvp(id)); -} - - -//------------------------------------------------------------------------------ -//------------------------------------------------------ Message::setFailedAvp() -//------------------------------------------------------------------------------ -Avp * Message::setFailedAvp(Avp *avp) throw(anna::RuntimeException) { - - if(!avp || isRequest()) return NULL; + // Section 7.5 RFC 6733: A Diameter message SHOULD contain one Failed-AVP AVP + theFailedAvp = addAvp(helpers::base::AVPID__Failed_AVP); + Avp *leaf = theFailedAvp; - Avp *theFailedAvp = addTheFailedAVP(); LOGDEBUG( - std::string msg = "Adding wrong avp "; - msg += anna::diameter::functions::avpIdAsPairString(avp->getId()); - msg += " within the message Failed-AVP ..."; + std::string msg = "Adding to Failed-AVP, the wrong avp "; + msg += anna::diameter::functions::avpIdAsPairString(wrong); + msg += " found inside "; + msg += parent.asString(); + anna::Logger::debug(msg, ANNA_FILE_LOCATION); ); - if (theFailedAvp->countChilds()) { // (*) - LOGDEBUG(anna::Logger::debug("Discarding wrong avp. A previous wrong avp was already added into the Failed-AVP. RFC 6733 Section 7.5 recommends to store only the first.", ANNA_FILE_LOCATION)); - return NULL; - } + std::vector::const_iterator it; + for(it = parent.AvpsId.begin(); it != parent.AvpsId.end(); it++) + leaf = leaf->addAvp(*it); - return (theFailedAvp->addAvp(avp)); + leaf->addAvp(wrong); } @@ -611,7 +586,6 @@ bool Message::valid(Message *ptrAnswer) const throw(anna::RuntimeException) { OamModule &oamModule = OamModule::instantiate(); // Dictionary stack command: const stack::Command *stackCommand = getStackCommand(); - std::string me; // Only build answer for a request: Message *answer = isRequest() ? ptrAnswer : NULL; @@ -619,7 +593,7 @@ bool Message::valid(Message *ptrAnswer) const throw(anna::RuntimeException) { if(!stackCommand) { // OAM - me = anna::diameter::functions::commandIdAsPairString(a_id); + std::string me = anna::diameter::functions::commandIdAsPairString(a_id); oamModule.activateAlarm(OamModule::Alarm::MessageValidation__UnknownOperation__s__UnableToValidate, STRING_WITH_QUOTATION_MARKS__C_STR(me)); oamModule.count(OamModule::Counter::MessageValidation__UnknownOperationUnableToValidate); @@ -629,7 +603,10 @@ bool Message::valid(Message *ptrAnswer) const throw(anna::RuntimeException) { return false; } - me = stackCommand->getName(); + // Parent information: + parent_t me; + me.setMessage(a_id, stackCommand->getName().c_str()); + ////////////////////////////// // Flags coherence checking // ////////////////////////////// @@ -638,12 +615,12 @@ bool Message::valid(Message *ptrAnswer) const throw(anna::RuntimeException) { if(!result) { // OAM & Depth management - oamModule.activateAlarm(OamModule::Alarm::MessageValidation__Operation__s__HaveIncoherentFlags__d__, STRING_WITH_QUOTATION_MARKS__C_STR(me), (int)a_flags); + oamModule.activateAlarm(OamModule::Alarm::MessageValidation__Operation__s__HaveIncoherentFlags__d__, STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags); oamModule.count(OamModule::Counter::MessageValidation__OperationHaveIncoherentFlags); if(answer) answer->setResultCode(rc); - getEngine()->validationAnomaly(anna::functions::asString("Operation %s have incoherent flags (%d)", STRING_WITH_QUOTATION_MARKS__C_STR(me), (int)a_flags)); + getEngine()->validationAnomaly(anna::functions::asString("Operation %s have incoherent flags (%d)", STRING_WITH_QUOTATION_MARKS__C_STR(me.asString()), (int)a_flags)); } //////////////////// diff --git a/source/diameter/codec/functions.cpp b/source/diameter/codec/functions.cpp index 88749a0..7c5516c 100644 --- a/source/diameter/codec/functions.cpp +++ b/source/diameter/codec/functions.cpp @@ -53,6 +53,47 @@ using namespace anna::diameter::codec; + + +// Parent struct helper ///////////////////////////////////////////////////////////////////////////// +void parent::setMessage(const anna::diameter::CommandId & mid, const char *mname) throw() { + MessageId = mid; + if (mname) { + MessageName = mname; + } + else { + MessageName = "Message"; + MessageName += anna::diameter::functions::commandIdAsPairString(mid); + } +} + +void parent::addAvp(const anna::diameter::AvpId & aid, const char *aname) throw() { + AvpsId.push_back(aid); + std::string name; + if (aname) { + name = aname; + } + else { + name = "Avp"; + name += anna::diameter::functions::avpIdAsPairString(aid); + } + AvpsName.push_back(name); +} + +std::string parent::asString() const throw() { // "->->...->" + std::string result = MessageName; + for (std::vector::const_iterator it = AvpsName.begin(); it != AvpsName.end(); it++) { + result += "->"; + result += (*it); + } + + return result; +} +///////////////////////////////////////////////////////////////////////////////////////////////////// + + + + // getters anna::diameter::CommandId functions::getCommandId(const anna::DataBlock & db) throw(anna::RuntimeException) { if(db.getSize() < Message::HeaderLength) -- 2.20.1