Skip to content
This repository has been archived by the owner on Sep 1, 2022. It is now read-only.

Commit

Permalink
Merge pull request #312 from dnp3/feature-broadcast
Browse files Browse the repository at this point in the history
Add broadcast support
  • Loading branch information
jadamcrain committed Jun 17, 2019
2 parents 4d16387 + 1d98939 commit 5605aeb
Show file tree
Hide file tree
Showing 27 changed files with 681 additions and 115 deletions.
14 changes: 7 additions & 7 deletions cpp/examples/master-gprs/ExampleListenCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ void ExampleListenCallbacks::OnFirstFrame(uint64_t sessionid,

// do we already have a session with this outstation?
const auto iter = std::find_if(this->sessions.begin(), this->sessions.end(),
[&](const auto& item) { return item.address == header.src; });
[&](const auto& item) { return item.address == header.addresses.source; });
if (iter != this->sessions.end())
{

std::cout << "Already connected to outstation w/ address " << header.src << ". Closing first connection."
std::cout << "Already connected to outstation w/ address " << header.addresses.source << ". Closing first connection."
<< std::endl;

// if so, shutdown the existing session
Expand All @@ -165,22 +165,22 @@ void ExampleListenCallbacks::OnFirstFrame(uint64_t sessionid,
MasterStackConfig config;

// use the master and outstation addresses that the outstation is using
config.link.LocalAddr = header.dest;
config.link.RemoteAddr = header.src;
config.link.LocalAddr = header.addresses.destination;
config.link.RemoteAddr = header.addresses.source;

// don't disable unsolicited reporting when the master comes online
config.master.disableUnsolOnStartup = false;
// don't perform an integrity scan when the application layer comes online
config.master.startupIntegrityClassMask = ClassField::None();

const auto session
= acceptor.AcceptSession(GetSessionName(header.src, sessionid), std::make_shared<ExampleSOEHandler>(header.src),
= acceptor.AcceptSession(GetSessionName(header.addresses.source, sessionid), std::make_shared<ExampleSOEHandler>(header.addresses.source),
std::make_shared<DefaultMasterApplication>(), config);

// add to the list
this->sessions.emplace_back(SessionInfo{sessionid, header.src, session});
this->sessions.emplace_back(SessionInfo{sessionid, header.addresses.source, session});

std::cout << "Outstation session start: " << header.src << std::endl;
std::cout << "Outstation session start: " << header.addresses.source << std::endl;
}

void ExampleListenCallbacks::OnConnectionClose(uint64_t sessionid,
Expand Down
1 change: 1 addition & 0 deletions cpp/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ set(opendnp3_src
./src/gen/objects/Group121.cpp
./src/gen/objects/Group122.cpp

./src/link/Addresses.cpp
./src/link/CRC.cpp
./src/link/LinkContext.cpp
./src/link/LinkFrame.cpp
Expand Down
2 changes: 2 additions & 0 deletions cpp/lib/include/opendnp3/link/Addresses.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ struct Addresses
return !((*this) == other);
}

bool IsBroadcast() const;

uint16_t source = 0;
uint16_t destination = 0;
};
Expand Down
10 changes: 2 additions & 8 deletions cpp/lib/include/opendnp3/link/LinkHeaderFields.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,13 @@ struct LinkHeaderFields
{
LinkHeaderFields();

LinkHeaderFields(LinkFunction func, bool isMaster, bool fcb, bool fcvdfc, uint16_t dest, uint16_t src_);

Addresses ToAddresses() const
{
return Addresses(src, dest);
}
LinkHeaderFields(LinkFunction func, bool isMaster, bool fcb, bool fcvdfc, Addresses addresses);

LinkFunction func;
bool isFromMaster;
bool fcb;
bool fcvdfc;
uint16_t dest;
uint16_t src;
Addresses addresses;
};

} // namespace opendnp3
Expand Down
4 changes: 2 additions & 2 deletions cpp/lib/src/channel/IOHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,12 @@ void IOHandler::OnNewChannel(const std::shared_ptr<IAsyncChannel>& channel)

bool IOHandler::OnFrame(const LinkHeaderFields& header, const ser4cpp::rseq_t& userdata)
{
if (this->SendToSession(Addresses(header.src, header.dest), header, userdata))
if (this->SendToSession(header.addresses, header, userdata))
{
return true;
}

FORMAT_LOG_BLOCK(this->logger, flags::WARN, "Frame w/ unknown route, source: %i, dest %i", header.src, header.dest);
FORMAT_LOG_BLOCK(this->logger, flags::WARN, "Frame w/ unknown route, source: %i, dest %i", header.addresses.source, header.addresses.destination);
return false;
}

Expand Down
34 changes: 34 additions & 0 deletions cpp/lib/src/link/Addresses.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2013-2019 Automatak, LLC
*
* Licensed to Green Energy Corp (www.greenenergycorp.com) and Automatak
* LLC (www.automatak.com) under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. Green Energy Corp and Automatak LLC license
* this file to you under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may obtain
* a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "opendnp3/link/Addresses.h"

#include "link/LinkLayerConstants.h"

namespace opendnp3
{

bool Addresses::IsBroadcast() const
{
return this->destination == LinkBroadcastAddress::DontConfirm ||
this->destination == LinkBroadcastAddress::ShallConfirm ||
this->destination == LinkBroadcastAddress::OptionalConfirm;
}

} // namespace opendnp3
39 changes: 30 additions & 9 deletions cpp/lib/src/link/LinkContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,20 +334,41 @@ bool LinkContext::OnFrame(const LinkHeaderFields& header, const ser4cpp::rseq_t&
return false;
}

if (header.dest != config.LocalAddr)
if (header.addresses.destination != config.LocalAddr && !header.addresses.IsBroadcast())
{
++statistics.numUnknownDestination;
this->listener->OnUnknownDestinationAddress(header.dest);
this->listener->OnUnknownDestinationAddress(header.addresses.destination);
return false;
}

if (header.src != config.RemoteAddr && !config.respondToAnySource)
if (header.addresses.source != config.RemoteAddr && !config.respondToAnySource)
{
++statistics.numUnknownSource;
this->listener->OnUnknownSourceAddress(header.src);
this->listener->OnUnknownSourceAddress(header.addresses.source);
return false;
}

if(header.addresses.IsBroadcast())
{
// Broadcast addresses can only be used for sending data.
// If confirmed data is used, no response is sent back.
if(header.func == LinkFunction::PRI_UNCONFIRMED_USER_DATA)
{
this->PushDataUp(Message(header.addresses, userdata));
return true;
}
else if(header.func == LinkFunction::PRI_CONFIRMED_USER_DATA)
{
pSecState = &pSecState->OnConfirmedUserData(*this, header.addresses.source, header.fcb, true, Message(header.addresses, userdata));
}
else
{
FORMAT_LOG_BLOCK(logger, flags::WARN, "Received invalid function (%s) with broadcast destination address", LinkFunctionToString(header.func));
++statistics.numUnexpectedFrame;
return false;
}
}

// reset the keep-alive timestamp
this->lastMessageTimestamp = Timestamp(this->executor->get_time());

Expand All @@ -366,20 +387,20 @@ bool LinkContext::OnFrame(const LinkHeaderFields& header, const ser4cpp::rseq_t&
pPriState = &pPriState->OnNotSupported(*this, header.fcvdfc);
break;
case (LinkFunction::PRI_TEST_LINK_STATES):
pSecState = &pSecState->OnTestLinkStatus(*this, header.src, header.fcb);
pSecState = &pSecState->OnTestLinkStatus(*this, header.addresses.source, header.fcb);
break;
case (LinkFunction::PRI_RESET_LINK_STATES):
pSecState = &pSecState->OnResetLinkStates(*this, header.src);
pSecState = &pSecState->OnResetLinkStates(*this, header.addresses.source);
break;
case (LinkFunction::PRI_REQUEST_LINK_STATUS):
pSecState = &pSecState->OnRequestLinkStatus(*this, header.src);
pSecState = &pSecState->OnRequestLinkStatus(*this, header.addresses.source);
break;
case (LinkFunction::PRI_CONFIRMED_USER_DATA):
pSecState
= &pSecState->OnConfirmedUserData(*this, header.src, header.fcb, Message(header.ToAddresses(), userdata));
= &pSecState->OnConfirmedUserData(*this, header.addresses.source, header.fcb, false, Message(header.addresses, userdata));
break;
case (LinkFunction::PRI_UNCONFIRMED_USER_DATA):
this->PushDataUp(Message(header.ToAddresses(), userdata));
this->PushDataUp(Message(header.addresses, userdata));
break;
default:
break;
Expand Down
7 changes: 4 additions & 3 deletions cpp/lib/src/link/LinkHeaderFields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@
namespace opendnp3
{
LinkHeaderFields::LinkHeaderFields()
: func(LinkFunction::INVALID), isFromMaster(false), fcb(false), fcvdfc(false), dest(0), src(0)
: func(LinkFunction::INVALID), isFromMaster(false), fcb(false), fcvdfc(false), addresses(Addresses())
{
}

LinkHeaderFields::LinkHeaderFields(
LinkFunction func_, bool isMaster_, bool fcb_, bool fcvdfc_, uint16_t dest_, uint16_t src_)
: func(func_), isFromMaster(isMaster_), fcb(fcb_), fcvdfc(fcvdfc_), dest(dest_), src(src_)
LinkFunction func_, bool isMaster_, bool fcb_, bool fcvdfc_, Addresses addresses_)
: func(func_), isFromMaster(isMaster_), fcb(fcb_), fcvdfc(fcvdfc_), addresses(addresses_)
{
}

} // namespace opendnp3
8 changes: 8 additions & 0 deletions cpp/lib/src/link/LinkLayerConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ const uint8_t LPDU_DATA_PLUS_CRC_SIZE = 18;
const uint8_t LPDU_MAX_USER_DATA_SIZE = 250;
const uint16_t LPDU_MAX_FRAME_SIZE = 292; // 10(header) + 250 (user data) + 32 (block CRC's) = 292 frame bytes

// Broadcast addresses
enum LinkBroadcastAddress : uint16_t
{
DontConfirm = 0xFFFD,
ShallConfirm = 0xFFFE,
OptionalConfirm = 0xFFFF,
};

/// Indices for use with buffers containing link headers
enum LinkHeaderIndex : uint8_t
{
Expand Down
2 changes: 1 addition & 1 deletion cpp/lib/src/link/LinkLayerParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ LinkLayerParser::State LinkLayerParser::ParseBody()
void LinkLayerParser::PushFrame(IFrameSink& sink)
{
LinkHeaderFields fields(header.GetFuncEnum(), header.IsFromMaster(), header.IsFcbSet(), header.IsFcvDfcSet(),
header.GetDest(), header.GetSrc());
Addresses(header.GetSrc(), header.GetDest()));

sink.OnFrame(fields, userData);

Expand Down
8 changes: 6 additions & 2 deletions cpp/lib/src/link/SecLinkLayerStates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ SecStateBase& SLLS_NotReset::OnTestLinkStatus(LinkContext& ctx, uint16_t /*sourc
SecStateBase& SLLS_NotReset::OnConfirmedUserData(LinkContext& ctx,
uint16_t /*source*/,
bool /*fcb*/,
bool /*isBroadcast*/,
const Message& /*message*/)
{
++ctx.statistics.numUnexpectedFrame;
Expand Down Expand Up @@ -94,9 +95,12 @@ SecStateBase& SLLS_Reset::OnTestLinkStatus(LinkContext& ctx, uint16_t source, bo
return *this;
}

SecStateBase& SLLS_Reset::OnConfirmedUserData(LinkContext& ctx, uint16_t source, bool fcb, const Message& message)
SecStateBase& SLLS_Reset::OnConfirmedUserData(LinkContext& ctx, uint16_t source, bool fcb, bool isBroadcast, const Message& message)
{
ctx.QueueAck(source);
if(!isBroadcast)
{
ctx.QueueAck(source);
}

if (ctx.nextReadFCB == fcb)
{
Expand Down
8 changes: 5 additions & 3 deletions cpp/lib/src/link/SecLinkLayerStates.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class SecStateBase
virtual SecStateBase& OnRequestLinkStatus(LinkContext&, uint16_t source) = 0;

virtual SecStateBase& OnTestLinkStatus(LinkContext&, uint16_t source, bool fcb) = 0;
virtual SecStateBase& OnConfirmedUserData(LinkContext&, uint16_t source, bool fcb, const Message& message) = 0;
virtual SecStateBase& OnConfirmedUserData(LinkContext&, uint16_t source, bool fcb, bool isBroadcast, const Message& message) = 0;

virtual SecStateBase& OnTxReady(LinkContext& ctx);

Expand All @@ -65,6 +65,7 @@ template<class NextState> class SLLS_TransmitWaitBase : public SecStateBase
virtual SecStateBase& OnConfirmedUserData(LinkContext&,
uint16_t source,
bool fcb,
bool isBroadcast,
const Message& message) override final;
};

Expand Down Expand Up @@ -98,6 +99,7 @@ template<class NextState>
SecStateBase& SLLS_TransmitWaitBase<NextState>::OnConfirmedUserData(LinkContext& ctx,
uint16_t source,
bool fcb,
bool isBroadcast,
const Message& message)
{
SIMPLE_LOG_BLOCK(ctx.logger, flags::WARN, "Ignoring link frame, remote is flooding");
Expand All @@ -112,7 +114,7 @@ class SLLS_NotReset final : public SecStateBase
public:
MACRO_STATE_SINGLETON_INSTANCE(SLLS_NotReset);

virtual SecStateBase& OnConfirmedUserData(LinkContext&, uint16_t source, bool fcb, const Message& message) override;
virtual SecStateBase& OnConfirmedUserData(LinkContext&, uint16_t source, bool fcb, bool isBroadcast, const Message& message) override;
virtual SecStateBase& OnResetLinkStates(LinkContext&, uint16_t source) override;
virtual SecStateBase& OnRequestLinkStatus(LinkContext&, uint16_t source) override;
virtual SecStateBase& OnTestLinkStatus(LinkContext&, uint16_t source, bool fcb) override;
Expand All @@ -125,7 +127,7 @@ class SLLS_Reset final : public SecStateBase
{
MACRO_STATE_SINGLETON_INSTANCE(SLLS_Reset);

virtual SecStateBase& OnConfirmedUserData(LinkContext&, uint16_t source, bool fcb, const Message& message) override;
virtual SecStateBase& OnConfirmedUserData(LinkContext&, uint16_t source, bool fcb, bool isBroadcast, const Message& message) override;
virtual SecStateBase& OnResetLinkStates(LinkContext&, uint16_t source) override;
virtual SecStateBase& OnRequestLinkStatus(LinkContext&, uint16_t source) override;
virtual SecStateBase& OnTestLinkStatus(LinkContext&, uint16_t source, bool fcb) override;
Expand Down
4 changes: 2 additions & 2 deletions cpp/lib/src/master/DefaultListenCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ void DefaultListenCallbacks::OnFirstFrame(uint64_t sessionid,

// full implementations will look up config information for the SRC address

config.link.LocalAddr = header.dest;
config.link.RemoteAddr = header.src;
config.link.LocalAddr = header.addresses.destination;
config.link.RemoteAddr = header.addresses.source;

auto soe = std::make_shared<PrintingSOEHandler>();
auto app = std::make_shared<DefaultMasterApplication>();
Expand Down

0 comments on commit 5605aeb

Please sign in to comment.