bool LinkLayerReceiver::ValidateFunctionCode() { //Now make sure that the function code is known and that the FCV is appropriate if(mHeader.IsPriToSec()) { bool fcv_set = false; switch(mHeader.GetFuncEnum()) { case(FC_PRI_CONFIRMED_USER_DATA): case(FC_PRI_TEST_LINK_STATES): fcv_set = true; break; case(FC_PRI_REQUEST_LINK_STATUS): case(FC_PRI_RESET_LINK_STATES): case(FC_PRI_UNCONFIRMED_USER_DATA): break; default: { ERROR_BLOCK(LEV_WARNING, "Unknown PriToSec FUNCTION: " << mHeader.GetFuncEnum(), DLERR_UNKNOWN_FUNC); return false; } } //now check the fcv if(fcv_set != mHeader.IsFcvDfcSet()) { ERROR_BLOCK(LEV_WARNING, "Bad FCV for FUNCTION: " << mHeader.GetFuncEnum(), DLERR_UNEXPECTED_FCV); return false; } //if fcv isn't expected to be set, fcb can be either 1 or 0, doesn't matter } else // SecToPri - just validate the function codes and that FCB is 0 { switch(mHeader.GetFuncEnum()) { case(FC_SEC_ACK): case(FC_SEC_NACK): case(FC_SEC_LINK_STATUS): case(FC_SEC_NOT_SUPPORTED): break; default: { ERROR_BLOCK(LEV_ERROR, "Unknown SecToPri FUNCTION: " << mHeader.GetFuncEnum(), DLERR_UNKNOWN_FUNC); return false; } } //now check the fcb, it should always be zero if(mHeader.IsFcbSet()) { ERROR_BLOCK(LEV_ERROR, "FCB set for SecToPri FUNCTION: " << mHeader.GetFuncEnum(), DLERR_UNEXPECTED_FCB); return false; } } return true; //valid! }
void Slave::HandleWriteIIN(HeaderReadIterator& arHdr) { for (ObjectReadIterator obj = arHdr.BeginRead(); !obj.IsEnd(); ++obj) { switch (obj->Index()) { case IINI_DEVICE_RESTART: { bool value = Group80Var1::Inst()->Read(*obj, obj->Start(), obj->Index()); if (!value) { mIIN.SetDeviceRestart(false); } else { mRspIIN.SetParameterError(true); ERROR_BLOCK(LEV_WARNING, "", SERR_INVALID_IIN_WRITE); } break; } case IINI_NEED_TIME: mpTimeTimer->Cancel(); mpTimeTimer = NULL; mIIN.SetNeedTime(false); LOG_BLOCK(LEV_INFO, "Master forced clear time needed flag"); break; default: mRspIIN.SetParameterError(true); ERROR_BLOCK(LEV_WARNING, "", SERR_INVALID_IIN_WRITE); break; } } }
bool TransportRx::ValidateHeader(bool aFir, bool aFin, int aSeq, size_t aPayloadSize) { //get the transport byte and parse it if(aFir) { mSeq = aSeq; //always accept the sequence on FIR if(mNumBytesRead > 0) { /* 2004-03-29_DNP3_Doc_Library.pdf: 2-2 Page 64. When a secondary station receives a frame with the FIR bit set, all previously received unterminated frame sequences are discarded. */ ERROR_BLOCK(LEV_WARNING, "FIR received mid-fragment, discarding: " << mNumBytesRead << "bytes", TLERR_NEW_FIR); mNumBytesRead = 0; } } else if(mNumBytesRead == 0) { //non-first packet with 0 prior bytes ERROR_BLOCK(LEV_WARNING, "non-FIR packet with 0 prior bytes", TLERR_MESSAGE_WITHOUT_FIR); return false; } if(!aFin && aPayloadSize != TL_MAX_TPDU_PAYLOAD) { //if it's not a FIN packet it should have a length of ERROR_BLOCK(LEV_WARNING, "Partial non-FIN frame, payload= " << aPayloadSize, TLERR_BAD_LENGTH); return false; } if(aSeq != mSeq) { ERROR_BLOCK(LEV_WARNING, "Ignoring bad sequence, got: " << aSeq << " expected: " << mSeq, TLERR_BAD_SEQUENCE); return false; } return true; }
bool LinkLayerReceiver::ValidateBody() { size_t len = mHeader.GetLength() - LS_MIN_LENGTH; if(LinkFrame::ValidateBodyCRC(mBuffer.ReadBuff() + LS_HEADER_SIZE, len)) return true; else { ERROR_BLOCK(LEV_ERROR, "CRC failure in body", DLERR_CRC); return false; } }
void TransportRx::HandleReceive(const boost::uint8_t* apData, size_t aNumBytes) { switch(aNumBytes) { case(1): ERROR_BLOCK(LEV_WARNING, "Received tpdu with no payload", TLERR_NO_PAYLOAD); return; case(0): throw ArgumentException(LOCATION, "Zero length invalid"); default: if(aNumBytes > TL_MAX_TPDU_LENGTH) { ostringstream oss; oss << "Illegal arg: " << aNumBytes << " exceeds max tpdu size of " << TL_MAX_TPDU_LENGTH; throw ArgumentException(LOCATION, oss.str()); } } boost::uint8_t hdr = apData[0]; LOG_BLOCK(LEV_INTERPRET, "<- " << TransportLayer::ToString(hdr)); bool first = (hdr & TL_HDR_FIR) != 0; bool last = (hdr & TL_HDR_FIN) != 0; int seq = hdr & TL_HDR_SEQ; size_t payload_len = aNumBytes - 1; if(this->ValidateHeader(first, last, seq, payload_len)) { if(BufferRemaining() < payload_len) { ERROR_BLOCK(LEV_WARNING, "Exceeded the buffer size before a complete fragment was read", TLERR_BUFFER_FULL); mNumBytesRead = 0; } else { //passed all validation memcpy(mBuffer + mNumBytesRead, apData + 1, payload_len); mNumBytesRead += payload_len; mSeq = (mSeq + 1) % 64; if(last) { size_t tmp = mNumBytesRead; mNumBytesRead = 0; mpContext->ReceiveAPDU(mBuffer, tmp); } } } }
bool LinkLayerReceiver::ValidateHeader() { //first thing to do is check the CRC if(!DNPCrc::IsCorrectCRC(mBuffer.ReadBuff(), LI_CRC)) { mCrcFailures.Increment(); ERROR_BLOCK(LEV_ERROR, "CRC failure in header", DLERR_CRC); return false; } if(!mHeader.ValidLength()) { ERROR_BLOCK(LEV_ERROR, "LENGTH out of range [5,255]: " << static_cast<int>(mHeader.GetLength()), DLERR_INVALID_LENGTH); return false; } LOG_BLOCK(LEV_INTERPRET, "<~ " << mHeader.ToString()); // some combinations of these header parameters are invalid // check for them here //Now make sure that the function code is known and that the FCV is appropriate if(!this->ValidateFunctionCode()) return false; boost::uint8_t user_data_length = mHeader.GetLength() - LS_MIN_LENGTH; mFrameSize = LinkFrame::CalcFrameSize(user_data_length); FuncCodes func = mHeader.GetFuncEnum(); // make sure that the presence/absence of user data // matches the function code if(func == FC_PRI_CONFIRMED_USER_DATA || func == FC_PRI_UNCONFIRMED_USER_DATA) { if(user_data_length > 0) { //mFrameSize = LinkFrame::CalcFrameSize(user_data_length); } else { ERROR_BLOCK(LEV_ERROR, "User data packet received with zero payload. FUNCTION: " << func, DLERR_NO_DATA); return false; } } else { if(user_data_length > 0) { ERROR_BLOCK(LEV_ERROR, "Unexpected LENGTH in frame: " << static_cast<int>(user_data_length) << " with FUNCTION: " << func, DLERR_UNEXPECTED_DATA); return false; } } if(user_data_length > 0) { if(func == FC_PRI_CONFIRMED_USER_DATA || func == FC_PRI_UNCONFIRMED_USER_DATA) { } else { ERROR_BLOCK(LEV_ERROR, "Unexpected LENGTH in frame: " << static_cast<int>(user_data_length) << " with FUNCTION: " << func, DLERR_UNEXPECTED_DATA); return false; } } else { if(func == FC_PRI_CONFIRMED_USER_DATA || func == FC_PRI_UNCONFIRMED_USER_DATA) { ERROR_BLOCK(LEV_ERROR, "User data packet received with zero payload. FUNCTION: " << func, DLERR_NO_DATA); return false; } } return true; }
void Slave::HandleVtoTransfer(const APDU& arRequest) { for(HeaderReadIterator hdr = arRequest.BeginRead(); !hdr.IsEnd(); ++hdr) { switch(hdr->GetGroup()) { case 112: this->HandleWriteVto(hdr); break; default: mRspIIN.SetFuncNotSupported(true); ERROR_BLOCK(LEV_WARNING, "Object/Function mismatch", SERR_OBJ_FUNC_MISMATCH); break; } } }
bool LinkLayer::Validate(bool aIsMaster, boost::uint16_t aSrc, boost::uint16_t aDest) { if(!mIsOnline) throw InvalidStateException(LOCATION, "LowerLayerDown"); if(aIsMaster == mCONFIG.IsMaster) { ERROR_BLOCK(LEV_WARNING, (aIsMaster ? "Master frame received for master" : "Slave frame received for slave"), DLERR_MASTER_BIT_MATCH); return false; } if(aDest != mCONFIG.LocalAddr) { ERROR_BLOCK(LEV_WARNING, "Frame for unknown destintation", DLERR_UNKNOWN_DESTINATION); return false; } if(aSrc != mCONFIG.RemoteAddr) { ERROR_BLOCK(LEV_WARNING, "Frame from unknwon source", DLERR_UNKNOWN_SOURCE); return false; } return true; }
void Slave::HandleOperate(const APDU& arRequest, SequenceInfo aSeqInfo) { if (aSeqInfo == SI_PREV && mLastRequest == arRequest) { return; } mResponse.Set(FC_RESPONSE); for (HeaderReadIterator hdr = arRequest.BeginRead(); !hdr.IsEnd(); ++hdr) { ObjectReadIterator i = hdr.BeginRead(); switch (MACRO_DNP_RADIX(hdr->GetGroup(), hdr->GetVariation())) { case (MACRO_DNP_RADIX(12, 1)): this->RespondToCommands<BinaryOutput>(Group12Var1::Inst(), i, boost::bind(&Slave::Operate<BinaryOutput>, this, _1, _2, false, hdr.info(), aSeqInfo, arRequest.GetControl().SEQ)); break; case (MACRO_DNP_RADIX(41, 1)): this->RespondToCommands<Setpoint>(Group41Var1::Inst(), i, boost::bind(&Slave::Operate<Setpoint>, this, _1, _2, false, hdr.info(), aSeqInfo, arRequest.GetControl().SEQ)); break; case (MACRO_DNP_RADIX(41, 2)): this->RespondToCommands<Setpoint>(Group41Var2::Inst(), i, boost::bind(&Slave::Operate<Setpoint>, this, _1, _2, false, hdr.info(), aSeqInfo, arRequest.GetControl().SEQ)); break; case (MACRO_DNP_RADIX(41, 3)): this->RespondToCommands<Setpoint>(Group41Var3::Inst(), i, boost::bind(&Slave::Operate<Setpoint>, this, _1, _2, false, hdr.info(), aSeqInfo, arRequest.GetControl().SEQ)); break; case (MACRO_DNP_RADIX(41, 4)): this->RespondToCommands<Setpoint>(Group41Var4::Inst(), i, boost::bind(&Slave::Operate<Setpoint>, this, _1, _2, false, hdr.info(), aSeqInfo, arRequest.GetControl().SEQ)); break; default: mRspIIN.SetFuncNotSupported(true); ERROR_BLOCK(LEV_WARNING, "Object/Function mismatch", SERR_OBJ_FUNC_MISMATCH); break; } } }
void ResponseLoader::ProcessSizeByVariation(HeaderReadIterator& arIter, int aGrp, int aVar) { /* * These objects only require matching on the aGrp field. */ switch (aGrp) { /* Virtual Terminal Objects */ case (112): this->ReadVto(arIter, Group112Var0::Inst()); break; case (113): this->ReadVto(arIter, Group113Var0::Inst()); break; default: /* * If we reach this point, then we don't yet support this object type. */ ERROR_BLOCK(LEV_WARNING, "Group: " << aGrp << " " "Var: " << aVar << " " "does not map to a data type", MERR_UNSUPPORTED_OBJECT_TYPE); break; } }
void Slave::HandleWrite(const APDU& arRequest) { for (HeaderReadIterator hdr = arRequest.BeginRead(); !hdr.IsEnd(); ++hdr) { switch (hdr->GetGroup()) { case 112: this->HandleWriteVto(hdr); continue; } switch (MACRO_DNP_RADIX(hdr->GetGroup(), hdr->GetVariation())) { case (MACRO_DNP_RADIX(80, 1)): this->HandleWriteIIN(hdr); break; case (MACRO_DNP_RADIX(50, 1)): this->HandleWriteTimeDate(hdr); break; default: mRspIIN.SetFuncNotSupported(true); ERROR_BLOCK(LEV_WARNING, "Object/Function mismatch", SERR_OBJ_FUNC_MISMATCH); break; } } }