Skip to content

Commit 2feb95f

Browse files
committed
chore(agent): encode CommandTermination in control report shape
1 parent b7f5721 commit 2feb95f

1 file changed

Lines changed: 84 additions & 11 deletions

File tree

‎.github/scripts/agent_p3_control_lifetime_repair.py‎

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,94 @@ def once(old: str, new: str, label: str) -> None:
9696
''',
9797
'enhanced select evidence')
9898

99-
# MmsInformationReportSpanCodec validates data_set_reference even when the
100-
# DataSetName optional field is not selected. Supply a valid internal reference;
101-
# it is not emitted on the wire because OptFlds keeps DataSetName disabled.
99+
# CommandTermination is an ordinary MMS InformationReport with an explicit
100+
# variable-access reference to CO$...$Oper. RCB report framing is valid MMS but
101+
# does not populate MmsInformationReport::variable_references, so the command
102+
# decoder cannot correlate it to a pending enhanced-security Oper.
102103
once(
103-
''' report.report_id = "CommandTermination";
104-
report.optional_fields = opt;
105-
report.conf_revision = 1U;
104+
'''#include "ariec61850/mms/data_codec.hpp"
105+
#include "ariec61850/mms/services.hpp"
106+
''',
107+
'''#include "ariec61850/mms/data_codec.hpp"
108+
#include "ariec61850/mms/reporting.hpp"
109+
#include "ariec61850/mms/services.hpp"
106110
''',
107-
''' report.report_id = "CommandTermination";
111+
'control report codec include')
112+
113+
once(
114+
'''[[nodiscard]] bool encode_host_control_termination(
115+
const mms::MmsStaticConnectionRuntime& connection,
116+
HostControl& host,
117+
ConnectionBuffers& buffers,
118+
std::size_t& bytes_written) noexcept {
119+
bytes_written = 0U;
120+
if (connection.state() != mms::MmsStaticConnectionState::established ||
121+
connection.mms_presentation_context_id() == 0U || host.last_oper.empty()) return false;
122+
const std::array<std::uint8_t, 2U> opt{{0x04U, 0x00U}}; // dataRef
123+
const std::array<mms::MmsInformationReportReferenceInput, 1U> refs{{
124+
{host.manifest->domain, host.oper_item}}};
125+
const std::array<mms::MmsReadAccessResultInput, 1U> results{{
126+
{true, host.last_oper, 0U}}};
127+
mms::MmsInformationReportSnapshotInput report;
128+
report.report_id = "CommandTermination";
108129
report.optional_fields = opt;
109-
report.data_set_reference = mms::MmsInformationReportReferenceInput{
110-
host.manifest->domain, host.oper_item};
111130
report.conf_revision = 1U;
131+
report.member_references = refs;
132+
report.member_results = results;
133+
report.reason_for_inclusion = 0x40U;
134+
const auto raw = mms::MmsInformationReportSpanCodec::encode_snapshot_into(
135+
report, buffers.report_response);
136+
if (!raw.success()) return false;
137+
const auto p_data = ar::iec61850::osi::PresentationSpanCodec::encode_p_data_into(
138+
std::span<const std::uint8_t>{buffers.report_response.data(), raw.bytes_written},
139+
buffers.report_workspace,
140+
connection.mms_presentation_context_id(),
141+
true);
142+
''',
143+
'''[[nodiscard]] bool encode_host_control_termination(
144+
const mms::MmsStaticConnectionRuntime& connection,
145+
HostControl& host,
146+
ConnectionBuffers& buffers,
147+
std::size_t& bytes_written) noexcept {
148+
bytes_written = 0U;
149+
if (connection.state() != mms::MmsStaticConnectionState::established ||
150+
connection.mms_presentation_context_id() == 0U || host.last_oper.empty()) return false;
151+
try {
152+
mms::MmsInformationReport report;
153+
report.variable_references.push_back(
154+
mms::MmsObjectName::domain_specific(host.manifest->domain, host.oper_item));
155+
report.items.push_back({
156+
0U,
157+
mms::MmsDataCodec::decode(host.last_oper),
158+
std::nullopt});
159+
const auto raw = mms::MmsInformationReportCodec::encode_pdu(report);
160+
if (raw.empty() || raw.size() > buffers.report_response.size()) return false;
161+
std::copy(raw.begin(), raw.end(), buffers.report_response.begin());
162+
const auto p_data = ar::iec61850::osi::PresentationSpanCodec::encode_p_data_into(
163+
std::span<const std::uint8_t>{buffers.report_response.data(), raw.size()},
164+
buffers.report_workspace,
165+
connection.mms_presentation_context_id(),
166+
true);
167+
''',
168+
'command termination report shape')
169+
170+
once(
171+
''' bytes_written = tpkt.bytes_written;
172+
return true;
173+
}
174+
175+
[[nodiscard]] bool poll_host_control_terminations(
176+
''',
177+
''' bytes_written = tpkt.bytes_written;
178+
return true;
179+
} catch (...) {
180+
return false;
181+
}
182+
}
183+
184+
[[nodiscard]] bool poll_host_control_terminations(
112185
''',
113-
'command termination internal reference')
186+
'command termination exception boundary')
114187

115188
p.write_text(s, encoding='utf-8')
116-
print('P3 lifetime/exact-type/termination repair applied')
189+
print('P3 lifetime/exact-type/control-termination repair applied')

0 commit comments

Comments
 (0)