Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions include/ariec61850/mms/connection_runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "ariec61850/mms/static_dispatcher.hpp"

#include <array>
#include <cstddef>
#include <cstdint>
#include <span>
Expand Down Expand Up @@ -31,9 +32,26 @@ enum class MmsStaticConnectionStatus : std::uint8_t {
};

struct MmsStaticConnectionPolicy final {
static constexpr std::size_t maximum_owner_bytes = 16U;

std::uint16_t cotp_source_reference{0x1001U};
std::uint8_t maximum_tpdu_size_code{0x0AU};
bool require_end_of_transmission{true};

// Optional portable association identity used by contextual MMS writes.
// The transport/server adapter assigns association_id and stable Owner.
// Leaving these zero/empty preserves legacy non-contextual behavior.
std::uint64_t association_id{};
std::array<std::uint8_t, maximum_owner_bytes> owner{};
std::size_t owner_size{};

[[nodiscard]] constexpr MmsStaticRequestAccessContext access_context() const noexcept {
return {
association_id,
owner_size > 0U && owner_size <= owner.size()
? std::span<const std::uint8_t>{owner.data(), owner_size}
: std::span<const std::uint8_t>{}};
}
};

struct MmsStaticConnectionResult final {
Expand Down Expand Up @@ -81,6 +99,10 @@ class MmsStaticConnectionRuntime final {
return mms_presentation_context_id_;
}

[[nodiscard]] constexpr MmsStaticRequestAccessContext access_context() const noexcept {
return policy_.access_context();
}

private:
const MmsStaticApplicationDispatcher& dispatcher_;
MmsStaticConnectionPolicy policy_{};
Expand Down
6 changes: 4 additions & 2 deletions include/ariec61850/mms/static_dispatcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ class MmsStaticApplicationDispatcher final {
[[nodiscard]] MmsStaticDispatchResult dispatch(
std::span<const std::uint8_t> mms_request,
std::span<std::uint8_t> response,
std::span<std::uint8_t> workspace) const noexcept;
std::span<std::uint8_t> workspace,
const MmsStaticRequestAccessContext& access = {}) const noexcept;

[[nodiscard]] MmsStaticDispatchResult dispatch(
const MmsConfirmedPduView& request,
std::span<std::uint8_t> response,
std::span<std::uint8_t> workspace) const noexcept;
std::span<std::uint8_t> workspace,
const MmsStaticRequestAccessContext& access = {}) const noexcept;

[[nodiscard]] constexpr const MmsStaticDispatchPolicy& policy() const noexcept {
return policy_;
Expand Down
23 changes: 22 additions & 1 deletion include/ariec61850/mms/static_object_table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ using MmsStaticWriteCallback = MmsStaticWriteResult (*)(
void* context,
std::span<const std::uint8_t> encoded_data) noexcept;

// Per-request identity propagated by a connection/runtime adapter. The core
// deliberately treats Owner as opaque binary identity and does not bind it to
// an IP address, socket, TLS implementation, or operating-system handle.
struct MmsStaticRequestAccessContext final {
std::uint64_t association_id{};
std::span<const std::uint8_t> owner{};

[[nodiscard]] constexpr bool valid() const noexcept {
return association_id != 0U && !owner.empty();
}
};

using MmsStaticContextualWriteCallback = MmsStaticWriteResult (*)(
void* context,
std::span<const std::uint8_t> encoded_data,
const MmsStaticRequestAccessContext& access) noexcept;

struct MmsStaticObjectEntry final {
std::string_view domain;
std::string_view item;
Expand All @@ -34,8 +51,12 @@ struct MmsStaticObjectEntry final {
MmsStaticWriteCallback write{};
void* write_context{};

// Appended for source compatibility with existing aggregate initializers.
// A contextual callback takes precedence over the legacy write callback.
MmsStaticContextualWriteCallback contextual_write{};

[[nodiscard]] constexpr bool writable() const noexcept {
return write != nullptr;
return write != nullptr || contextual_write != nullptr;
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/mms/connection_runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ MmsStaticConnectionResult MmsStaticConnectionRuntime::process_tcp_window(
}

const auto application = dispatcher_.dispatch(
pdv.single_asn1_type, response, workspace);
pdv.single_asn1_type, response, workspace, policy_.access_context());
if (!application.success()) {
if (application.status == MmsStaticDispatchStatus::response_buffer_too_small) {
const auto fully_encoded = osi::PresentationSpanCodec::fully_encoded_data_size(
Expand Down
17 changes: 11 additions & 6 deletions src/mms/static_dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,8 @@ namespace {
const MmsStaticObjectTable& objects,
const MmsStaticDispatchPolicy& policy,
const MmsConfirmedPduView& confirmed,
const std::span<std::uint8_t> response) noexcept {
const std::span<std::uint8_t> response,
const MmsStaticRequestAccessContext& access) noexcept {
MmsWriteRequestView request;
if (!MmsServiceSpanCodec::try_decode_write_request(confirmed, request)) {
return make_status(MmsStaticDispatchStatus::malformed_request, confirmed);
Expand Down Expand Up @@ -352,7 +353,9 @@ namespace {
false, policy.access_denied_failure_code};
continue;
}
const auto applied = object->write(object->write_context, values[index]);
const auto applied = object->contextual_write != nullptr
? object->contextual_write(object->write_context, values[index], access)
: object->write(object->write_context, values[index]);
results[index] = MmsWriteAccessResultInput{
applied.success,
applied.success ? 0U : applied.failure_code};
Expand All @@ -371,7 +374,8 @@ namespace {
MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch(
const std::span<const std::uint8_t> mms_request,
const std::span<std::uint8_t> response,
const std::span<std::uint8_t> workspace) const noexcept {
const std::span<std::uint8_t> workspace,
const MmsStaticRequestAccessContext& access) const noexcept {
MmsConfirmedPduView request;
if (!MmsPduSpanCodec::try_decode_confirmed_request_view(mms_request, request)) {
return MmsStaticDispatchResult{
Expand All @@ -381,13 +385,14 @@ MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch(
0U,
0U};
}
return dispatch(request, response, workspace);
return dispatch(request, response, workspace, access);
}

MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch(
const MmsConfirmedPduView& request,
const std::span<std::uint8_t> response,
const std::span<std::uint8_t> workspace) const noexcept {
const std::span<std::uint8_t> workspace,
const MmsStaticRequestAccessContext& access) const noexcept {
if (!objects_.valid() || !data_sets_.valid_against(objects_) || !policy_valid(policy_)) {
return make_status(MmsStaticDispatchStatus::invalid_object_table, request);
}
Expand All @@ -405,7 +410,7 @@ MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch(
case MmsWireConfirmedService::read:
return dispatch_read(objects_, policy_, request, response, workspace);
case MmsWireConfirmedService::write:
return dispatch_write(objects_, policy_, request, response);
return dispatch_write(objects_, policy_, request, response, access);
default:
return make_status(MmsStaticDispatchStatus::unsupported_service, request);
}
Expand Down
Loading