libe3 0.2.5
C++ library for E3AP RAN vendor integration
Loading...
Searching...
No Matches
asn1_encoder.cpp
Go to the documentation of this file.
1/**
2 * @file asn1_encoder.cpp
3 * @brief ASN.1 APER Encoder implementation
4 *
5 * Ported from the original C implementation's e3ap_handler.c ASN.1 functions.
6 *
7 * SPDX-FileCopyrightText: Copyright (c) 2026 Northeastern University
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11#include "asn1_encoder.hpp"
12#include "libe3/logger.hpp"
13
14// ASN.1 generated headers
15extern "C" {
16#include "E3-PDU.h"
17#include "E3-SetupRequest.h"
18#include "E3-SetupResponse.h"
19#include "E3-RanFunctionDefinition.h"
20#include "E3-SubscriptionRequest.h"
21#include "E3-SubscriptionDelete.h"
22#include "E3-SubscriptionResponse.h"
23#include "E3-IndicationMessage.h"
24#include "E3-DAppControlAction.h"
25#include "E3-DAppReport.h"
26#include "E3-XAppControlAction.h"
27#include "E3-ReleaseMessage.h"
28#include "E3-MessageAck.h"
29#include "E3-Timestamp.h"
30}
31
32#include <cstring>
33
34namespace libe3 {
35
36namespace {
37constexpr const char* LOG_TAG = "Asn1Enc";
38
39// E3-Timestamp is an unconstrained INTEGER, which asn1c renders as a native
40// signed integer rather than the arbitrary-precision INTEGER_t. That keeps the
41// codec allocation-free on the indication path, at the cost of being tied to
42// the platform's long. Fail the build rather than silently truncating
43// Pdu::timestamp anywhere that type is narrower than the field it carries.
44//
45// The signedness costs nothing in practice: the conversions below are
46// two's-complement round trips, so the full uint64_t range survives intact.
47// Only past 2262-04-11, where a nanosecond stamp crosses 2^63, does the value
48// travel as a negative INTEGER, and it still decodes back bit-for-bit.
49static_assert(sizeof(E3_Timestamp_t) >= sizeof(uint64_t),
50 "E3_Timestamp_t is narrower than Pdu::timestamp; "
51 "give E3-Timestamp an explicit upper bound to force INTEGER_t "
52 "on this platform");
53} // anonymous namespace
54
55// ============================================================================
56// Encoding
57// ============================================================================
58
60 E3_PDU* asn1_pdu = pdu_to_asn1(pdu);
61 if (!asn1_pdu) {
62 E3_LOG_ERROR(LOG_TAG) << "Failed to convert PDU to ASN.1";
63 return tl::unexpected(ErrorCode::ENCODE_FAILED);
64 }
65
66 // Allocate buffer for encoding
67 std::vector<uint8_t> buffer(BUFFER_SIZE);
68
69 // Encode using ASN.1 APER
70 asn_enc_rval_t enc_rval = aper_encode_to_buffer(
71 &asn_DEF_E3_PDU,
72 nullptr,
73 asn1_pdu,
74 buffer.data(),
75 buffer.size()
76 );
77
78 if (enc_rval.encoded == -1) {
79 E3_LOG_ERROR(LOG_TAG) << "APER encoding failed for type: "
80 << (enc_rval.failed_type ? enc_rval.failed_type->name : "Unknown");
81 ASN_STRUCT_FREE(asn_DEF_E3_PDU, asn1_pdu);
82 return tl::unexpected(ErrorCode::ENCODE_FAILED);
83 }
84
85 // Resize buffer to actual encoded size
86 const size_t encoded_bytes = (static_cast<size_t>(enc_rval.encoded) + 7) / 8;
87 buffer.resize(encoded_bytes);
88
89 ASN_STRUCT_FREE(asn_DEF_E3_PDU, asn1_pdu);
90
92 msg.buffer = std::move(buffer);
94
95 E3_LOG_DEBUG(LOG_TAG) << "Encoded PDU: " << enc_rval.encoded << " bytes";
96 return msg;
97}
98
99// ============================================================================
100// Decoding
101// ============================================================================
102
104 return decode(encoded.buffer.data(), encoded.buffer.size());
105}
106
107EncodeResult<Pdu> Asn1E3Encoder::decode(const uint8_t* data, size_t size) {
108 if (!data || size == 0) {
109 return tl::unexpected(ErrorCode::INVALID_PARAM);
110 }
111
112 // Decode ASN.1 buffer
113 E3_PDU* asn1_pdu = nullptr;
114 asn_dec_rval_t dec_rval = aper_decode(
115 nullptr,
116 &asn_DEF_E3_PDU,
117 reinterpret_cast<void**>(&asn1_pdu),
118 data,
119 size,
120 0, 0
121 );
122
123 if (dec_rval.code != RC_OK) {
124 E3_LOG_ERROR(LOG_TAG) << "APER decoding failed with code " << dec_rval.code;
125 if (asn1_pdu) {
126 ASN_STRUCT_FREE(asn_DEF_E3_PDU, asn1_pdu);
127 }
128 return tl::unexpected(ErrorCode::ENCODE_FAILED);
129 }
130
131 // Convert to generic PDU
132 Pdu pdu = asn1_to_pdu(asn1_pdu);
133
134 ASN_STRUCT_FREE(asn_DEF_E3_PDU, asn1_pdu);
135
136 return pdu;
137}
138
139// ============================================================================
140// PDU Conversion: Generic -> ASN.1
141// ============================================================================
142
143E3_PDU* Asn1E3Encoder::pdu_to_asn1(const Pdu& pdu) const {
144 E3_PDU* asn1_pdu = static_cast<E3_PDU*>(calloc(1, sizeof(E3_PDU)));
145 if (!asn1_pdu) {
146 return nullptr;
147 }
148
149 // Set top-level message ID (E3-PDU.id)
150 asn1_pdu->id = pdu.message_id;
151
152 // Set the optional envelope timestamp (E3-PDU.timestamp). Zero is the
153 // "no producer reference" sentinel and is sent as an absent field, so a
154 // producer that does not stamp its PDUs pays one preamble bit, not nine
155 // octets. Failing to allocate is not fatal: the PDU still carries its
156 // payload, it just loses the timestamp, which is exactly what an
157 // unstamped PDU looks like.
158 if (pdu.timestamp != 0) {
159 asn1_pdu->timestamp =
160 static_cast<E3_Timestamp_t*>(calloc(1, sizeof(E3_Timestamp_t)));
161 if (asn1_pdu->timestamp) {
162 *asn1_pdu->timestamp = static_cast<E3_Timestamp_t>(pdu.timestamp);
163 }
164 }
165
166
167 switch (pdu.type) {
169 const auto* req = std::get_if<SetupRequest>(&pdu.choice);
170 if (!req) { free(asn1_pdu); return nullptr; }
171
172 asn1_pdu->msg.present = E3_PDU__msg_PR_setupRequest;
173 asn1_pdu->msg.choice.setupRequest = static_cast<E3_SetupRequest_t*>(
174 calloc(1, sizeof(E3_SetupRequest_t)));
175 if (!asn1_pdu->msg.choice.setupRequest) { free(asn1_pdu); return nullptr; }
176
177 // Set e3apProtocolVersion string
178 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.setupRequest->e3apProtocolVersion,
179 req->e3ap_protocol_version.c_str(),
180 static_cast<int>(req->e3ap_protocol_version.size()));
181
182 // Set dAppName string
183 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.setupRequest->dAppName,
184 req->dapp_name.c_str(),
185 static_cast<int>(req->dapp_name.size()));
186
187 // Set dAppVersion string
188 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.setupRequest->dAppVersion,
189 req->dapp_version.c_str(),
190 static_cast<int>(req->dapp_version.size()));
191
192 // Set vendor string
193 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.setupRequest->vendor,
194 req->vendor.c_str(),
195 static_cast<int>(req->vendor.size()));
196 break;
197 }
198
200 const auto* resp = std::get_if<SetupResponse>(&pdu.choice);
201 if (!resp) { free(asn1_pdu); return nullptr; }
202
203 asn1_pdu->msg.present = E3_PDU__msg_PR_setupResponse;
204 asn1_pdu->msg.choice.setupResponse = static_cast<E3_SetupResponse_t*>(
205 calloc(1, sizeof(E3_SetupResponse_t)));
206 if (!asn1_pdu->msg.choice.setupResponse) { free(asn1_pdu); return nullptr; }
207
208 asn1_pdu->msg.choice.setupResponse->requestId = resp->request_id;
209 asn1_pdu->msg.choice.setupResponse->responseCode =
210 (resp->response_code == ResponseCode::POSITIVE) ? 0 : 1;
211
212 // Set optional e3apProtocolVersion
213 if (resp->e3ap_protocol_version.has_value()) {
214 asn1_pdu->msg.choice.setupResponse->e3apProtocolVersion =
215 static_cast<OCTET_STRING_t*>(calloc(1, sizeof(OCTET_STRING_t)));
216 OCTET_STRING_fromBuf(asn1_pdu->msg.choice.setupResponse->e3apProtocolVersion,
217 resp->e3ap_protocol_version.value().c_str(),
218 static_cast<int>(resp->e3ap_protocol_version.value().size()));
219 }
220
221 // Set optional dAppIdentifier
222 if (resp->dapp_identifier.has_value()) {
223 asn1_pdu->msg.choice.setupResponse->dAppIdentifier =
224 static_cast<long*>(malloc(sizeof(long)));
225 *asn1_pdu->msg.choice.setupResponse->dAppIdentifier = resp->dapp_identifier.value();
226 }
227
228 // Set mandatory ranIdentifier
229 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.setupResponse->ranIdentifier,
230 resp->ran_identifier.c_str(),
231 static_cast<int>(resp->ran_identifier.size()));
232
233 // Set optional ranFunctionList
234 if (!resp->ran_function_list.empty()) {
235 for (const auto& func : resp->ran_function_list) {
236 E3_RanFunctionDefinition_t* ran_func =
237 static_cast<E3_RanFunctionDefinition_t*>(calloc(1, sizeof(E3_RanFunctionDefinition_t)));
238 ran_func->ranFunctionIdentifier = func.ran_function_identifier;
239
240 // Encode telemetryIdentifierList
241 for (uint32_t tel_id : func.telemetry_identifier_list) {
242 long* id = static_cast<long*>(malloc(sizeof(long)));
243 *id = tel_id;
244 ASN_SEQUENCE_ADD(&ran_func->telemetryIdentifierList, id);
245 }
246
247 // Encode controlIdentifierList
248 for (uint32_t ctrl_id : func.control_identifier_list) {
249 long* id = static_cast<long*>(malloc(sizeof(long)));
250 *id = ctrl_id;
251 ASN_SEQUENCE_ADD(&ran_func->controlIdentifierList, id);
252 }
253
254 OCTET_STRING_fromBuf(&ran_func->ranFunctionData,
255 reinterpret_cast<const char*>(func.ran_function_data.data()),
256 static_cast<int>(func.ran_function_data.size()));
257
258 // Ensure ranFunctionList container is allocated (optional field)
259 if (!asn1_pdu->msg.choice.setupResponse->ranFunctionList) {
260 asn1_pdu->msg.choice.setupResponse->ranFunctionList =
261 static_cast<decltype(asn1_pdu->msg.choice.setupResponse->ranFunctionList)>(
262 calloc(1, sizeof(*asn1_pdu->msg.choice.setupResponse->ranFunctionList)));
263 }
264
265 // Append the ran function into the sequence's internal list
266 ASN_SEQUENCE_ADD(&asn1_pdu->msg.choice.setupResponse->ranFunctionList->list, ran_func);
267 }
268 }
269 break;
270 }
271
273 const auto* req = std::get_if<SubscriptionRequest>(&pdu.choice);
274 if (!req) { free(asn1_pdu); return nullptr; }
275
276 asn1_pdu->msg.present = E3_PDU__msg_PR_subscriptionRequest;
277 asn1_pdu->msg.choice.subscriptionRequest = static_cast<E3_SubscriptionRequest_t*>(
278 calloc(1, sizeof(E3_SubscriptionRequest_t)));
279 if (!asn1_pdu->msg.choice.subscriptionRequest) { free(asn1_pdu); return nullptr; }
280
281 asn1_pdu->msg.choice.subscriptionRequest->dAppIdentifier = req->dapp_identifier;
282 asn1_pdu->msg.choice.subscriptionRequest->ranFunctionIdentifier = req->ran_function_identifier;
283
284 // Encode telemetryIdentifierList
285 for (uint32_t tel_id : req->telemetry_identifier_list) {
286 long* id = static_cast<long*>(malloc(sizeof(long)));
287 *id = tel_id;
288 ASN_SEQUENCE_ADD(&asn1_pdu->msg.choice.subscriptionRequest->telemetryIdentifierList, id);
289 }
290
291 // Encode controlIdentifierList
292 for (uint32_t ctrl_id : req->control_identifier_list) {
293 long* id = static_cast<long*>(malloc(sizeof(long)));
294 *id = ctrl_id;
295 ASN_SEQUENCE_ADD(&asn1_pdu->msg.choice.subscriptionRequest->controlIdentifierList, id);
296 }
297
298 // Set optional subscriptionTime
299 if (req->subscription_time.has_value()) {
300 asn1_pdu->msg.choice.subscriptionRequest->subscriptionTime =
301 static_cast<long*>(malloc(sizeof(long)));
302 *asn1_pdu->msg.choice.subscriptionRequest->subscriptionTime = req->subscription_time.value();
303 }
304 // Set optional periodicity
305 if (req->periodicity.has_value()) {
306 asn1_pdu->msg.choice.subscriptionRequest->periodicity =
307 static_cast<long*>(malloc(sizeof(long)));
308 *asn1_pdu->msg.choice.subscriptionRequest->periodicity = req->periodicity.value();
309 }
310 break;
311 }
312
314 const auto* del = std::get_if<SubscriptionDelete>(&pdu.choice);
315 if (!del) { free(asn1_pdu); return nullptr; }
316
317 asn1_pdu->msg.present = E3_PDU__msg_PR_subscriptionDelete;
318 asn1_pdu->msg.choice.subscriptionDelete = static_cast<E3_SubscriptionDelete_t*>(
319 calloc(1, sizeof(E3_SubscriptionDelete_t)));
320 if (!asn1_pdu->msg.choice.subscriptionDelete) { free(asn1_pdu); return nullptr; }
321
322 asn1_pdu->msg.choice.subscriptionDelete->dAppIdentifier = del->dapp_identifier;
323 asn1_pdu->msg.choice.subscriptionDelete->subscriptionId = del->subscription_id;
324 break;
325 }
326
328 const auto* resp = std::get_if<SubscriptionResponse>(&pdu.choice);
329 if (!resp) { free(asn1_pdu); return nullptr; }
330
331 asn1_pdu->msg.present = E3_PDU__msg_PR_subscriptionResponse;
332 asn1_pdu->msg.choice.subscriptionResponse = static_cast<E3_SubscriptionResponse_t*>(
333 calloc(1, sizeof(E3_SubscriptionResponse_t)));
334 if (!asn1_pdu->msg.choice.subscriptionResponse) { free(asn1_pdu); return nullptr; }
335
336 asn1_pdu->msg.choice.subscriptionResponse->requestId = resp->request_id;
337 asn1_pdu->msg.choice.subscriptionResponse->dAppIdentifier = resp->dapp_identifier;
338 asn1_pdu->msg.choice.subscriptionResponse->responseCode =
339 (resp->response_code == ResponseCode::POSITIVE) ? 0 : 1;
340
341 // Set optional subscriptionId
342 if (resp->subscription_id.has_value()) {
343 asn1_pdu->msg.choice.subscriptionResponse->subscriptionId =
344 static_cast<long*>(malloc(sizeof(long)));
345 *asn1_pdu->msg.choice.subscriptionResponse->subscriptionId = resp->subscription_id.value();
346 }
347 break;
348 }
349
351 const auto* msg = std::get_if<IndicationMessage>(&pdu.choice);
352 if (!msg) { free(asn1_pdu); return nullptr; }
353
354 asn1_pdu->msg.present = E3_PDU__msg_PR_indicationMessage;
355 asn1_pdu->msg.choice.indicationMessage = static_cast<E3_IndicationMessage_t*>(
356 calloc(1, sizeof(E3_IndicationMessage_t)));
357 if (!asn1_pdu->msg.choice.indicationMessage) { free(asn1_pdu); return nullptr; }
358
359 asn1_pdu->msg.choice.indicationMessage->dAppIdentifier = msg->dapp_identifier;
360 asn1_pdu->msg.choice.indicationMessage->ranFunctionIdentifier = msg->ran_function_identifier;
361
362 // Copy protocol data
363 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.indicationMessage->protocolData,
364 reinterpret_cast<const char*>(msg->protocol_data.data()),
365 static_cast<int>(msg->protocol_data.size()));
366 break;
367 }
368
370 const auto* action = std::get_if<DAppControlAction>(&pdu.choice);
371 if (!action) { free(asn1_pdu); return nullptr; }
372
373 asn1_pdu->msg.present = E3_PDU__msg_PR_dAppControlAction;
374 asn1_pdu->msg.choice.dAppControlAction = static_cast<E3_DAppControlAction_t*>(
375 calloc(1, sizeof(E3_DAppControlAction_t)));
376 if (!asn1_pdu->msg.choice.dAppControlAction) { free(asn1_pdu); return nullptr; }
377
378 asn1_pdu->msg.choice.dAppControlAction->dAppIdentifier = action->dapp_identifier;
379 asn1_pdu->msg.choice.dAppControlAction->ranFunctionIdentifier = action->ran_function_identifier;
380 asn1_pdu->msg.choice.dAppControlAction->controlIdentifier = action->control_identifier;
381
382 // sequenceId is OPTIONAL here: present only when this control
383 // re-issues an xApp one. Zero is the absent sentinel, same
384 // convention as the envelope timestamp above.
385 if (action->sequence_id != 0) {
386 asn1_pdu->msg.choice.dAppControlAction->sequenceId =
387 static_cast<E3_SequenceID_t*>(calloc(1, sizeof(E3_SequenceID_t)));
388 if (asn1_pdu->msg.choice.dAppControlAction->sequenceId) {
389 *asn1_pdu->msg.choice.dAppControlAction->sequenceId =
390 static_cast<E3_SequenceID_t>(action->sequence_id);
391 }
392 }
393
394 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.dAppControlAction->actionData,
395 reinterpret_cast<const char*>(action->action_data.data()),
396 static_cast<int>(action->action_data.size()));
397 break;
398 }
399
401 const auto* report = std::get_if<DAppReport>(&pdu.choice);
402 if (!report) { free(asn1_pdu); return nullptr; }
403
404 asn1_pdu->msg.present = E3_PDU__msg_PR_dAppReport;
405 asn1_pdu->msg.choice.dAppReport = static_cast<E3_DAppReport_t*>(
406 calloc(1, sizeof(E3_DAppReport_t)));
407 if (!asn1_pdu->msg.choice.dAppReport) { free(asn1_pdu); return nullptr; }
408
409 asn1_pdu->msg.choice.dAppReport->dAppIdentifier = report->dapp_identifier;
410 asn1_pdu->msg.choice.dAppReport->ranFunctionIdentifier = report->ran_function_identifier;
411 asn1_pdu->msg.choice.dAppReport->sequenceId =
412 static_cast<E3_SequenceID_t>(report->sequence_id);
413
414 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.dAppReport->reportData,
415 reinterpret_cast<const char*>(report->report_data.data()),
416 static_cast<int>(report->report_data.size()));
417 break;
418 }
419
421 const auto* action = std::get_if<XAppControlAction>(&pdu.choice);
422 if (!action) { free(asn1_pdu); return nullptr; }
423
424 asn1_pdu->msg.present = E3_PDU__msg_PR_xAppControlAction;
425 asn1_pdu->msg.choice.xAppControlAction = static_cast<E3_XAppControlAction_t*>(
426 calloc(1, sizeof(E3_XAppControlAction_t)));
427 if (!asn1_pdu->msg.choice.xAppControlAction) { free(asn1_pdu); return nullptr; }
428
429 asn1_pdu->msg.choice.xAppControlAction->dAppIdentifier = action->dapp_identifier;
430 asn1_pdu->msg.choice.xAppControlAction->ranFunctionIdentifier = action->ran_function_identifier;
431 asn1_pdu->msg.choice.xAppControlAction->sequenceId =
432 static_cast<E3_SequenceID_t>(action->sequence_id);
433
434 OCTET_STRING_fromBuf(&asn1_pdu->msg.choice.xAppControlAction->xAppControlData,
435 reinterpret_cast<const char*>(action->xapp_control_data.data()),
436 static_cast<int>(action->xapp_control_data.size()));
437 break;
438 }
439
441 const auto* ack = std::get_if<MessageAck>(&pdu.choice);
442 if (!ack) { free(asn1_pdu); return nullptr; }
443
444 asn1_pdu->msg.present = E3_PDU__msg_PR_messageAck;
445 asn1_pdu->msg.choice.messageAck = static_cast<E3_MessageAck_t*>(
446 calloc(1, sizeof(E3_MessageAck_t)));
447 if (!asn1_pdu->msg.choice.messageAck) { free(asn1_pdu); return nullptr; }
448
449 asn1_pdu->msg.choice.messageAck->requestId = ack->request_id;
450 asn1_pdu->msg.choice.messageAck->responseCode =
451 (ack->response_code == ResponseCode::POSITIVE) ? 0 : 1;
452 break;
453 }
454
456 const auto* msg = std::get_if<ReleaseMessage>(&pdu.choice);
457 if (!msg) { free(asn1_pdu); return nullptr; }
458
459 asn1_pdu->msg.present = E3_PDU__msg_PR_releaseMessage;
460 asn1_pdu->msg.choice.releaseMessage = static_cast<E3_ReleaseMessage_t*>(
461 calloc(1, sizeof(E3_ReleaseMessage_t)));
462 if (!asn1_pdu->msg.choice.releaseMessage) { free(asn1_pdu); return nullptr; }
463
464 asn1_pdu->msg.choice.releaseMessage->dAppIdentifier = msg->dapp_identifier;
465 break;
466 }
467
468 default:
469 free(asn1_pdu);
470 return nullptr;
471 }
472
473 return asn1_pdu;
474}
475
476// ============================================================================
477// PDU Conversion: ASN.1 -> Generic
478// ============================================================================
479
480Pdu Asn1E3Encoder::asn1_to_pdu(const E3_PDU* asn1_pdu) const {
481 Pdu pdu;
482
483 // Read top-level message ID (E3-PDU.id)
484 pdu.message_id = static_cast<uint32_t>(asn1_pdu->id);
485
486 // Read the optional envelope timestamp (E3-PDU.timestamp). The assignment
487 // is unconditional on purpose: Pdu's default constructor stamps the
488 // current clock, so without the else branch an absent field would decode
489 // to the *receiver's* wall clock at decode time rather than to the
490 // "unset" sentinel, which is far harder to notice than a zero.
491 pdu.timestamp = asn1_pdu->timestamp
492 ? static_cast<uint64_t>(*asn1_pdu->timestamp)
493 : 0;
494
495
496 switch (asn1_pdu->msg.present) {
497 case E3_PDU__msg_PR_setupRequest: {
498 pdu.type = PduType::SETUP_REQUEST;
499
500 SetupRequest req;
501
502 // Extract e3apProtocolVersion string
503 const OCTET_STRING_t* proto_ver = &asn1_pdu->msg.choice.setupRequest->e3apProtocolVersion;
504 req.e3ap_protocol_version.assign(reinterpret_cast<const char*>(proto_ver->buf), proto_ver->size);
505
506 // Extract dAppName string
507 const OCTET_STRING_t* dapp_name = &asn1_pdu->msg.choice.setupRequest->dAppName;
508 req.dapp_name.assign(reinterpret_cast<const char*>(dapp_name->buf), dapp_name->size);
509
510 // Extract dAppVersion string
511 const OCTET_STRING_t* dapp_ver = &asn1_pdu->msg.choice.setupRequest->dAppVersion;
512 req.dapp_version.assign(reinterpret_cast<const char*>(dapp_ver->buf), dapp_ver->size);
513
514 // Extract vendor string
515 const OCTET_STRING_t* vendor = &asn1_pdu->msg.choice.setupRequest->vendor;
516 req.vendor.assign(reinterpret_cast<const char*>(vendor->buf), vendor->size);
517
518 pdu.choice = req;
519 break;
520 }
521
522 case E3_PDU__msg_PR_setupResponse: {
523 pdu.type = PduType::SETUP_RESPONSE;
524
525 SetupResponse resp;
526 resp.request_id = static_cast<uint32_t>(asn1_pdu->msg.choice.setupResponse->requestId);
527 resp.response_code = (asn1_pdu->msg.choice.setupResponse->responseCode == 0)
529
530 // Extract optional e3apProtocolVersion
531 if (asn1_pdu->msg.choice.setupResponse->e3apProtocolVersion) {
532 const OCTET_STRING_t* proto_ver = asn1_pdu->msg.choice.setupResponse->e3apProtocolVersion;
533 resp.e3ap_protocol_version = std::string(
534 reinterpret_cast<const char*>(proto_ver->buf), proto_ver->size);
535 }
536
537 // Extract optional dAppIdentifier
538 if (asn1_pdu->msg.choice.setupResponse->dAppIdentifier) {
539 resp.dapp_identifier = *asn1_pdu->msg.choice.setupResponse->dAppIdentifier;
540 }
541
542 // Extract mandatory ranIdentifier
543 const OCTET_STRING_t* ran_id = &asn1_pdu->msg.choice.setupResponse->ranIdentifier;
544 resp.ran_identifier.assign(reinterpret_cast<const char*>(ran_id->buf), ran_id->size);
545
546 // Extract ranFunctionList
547 if (asn1_pdu->msg.choice.setupResponse->ranFunctionList) {
548 int count = asn1_pdu->msg.choice.setupResponse->ranFunctionList->list.count;
549 for (int i = 0; i < count; i++) {
550 E3_RanFunctionDefinition_t* asn_func =
551 asn1_pdu->msg.choice.setupResponse->ranFunctionList->list.array[i];
552 RanFunctionDef func;
553 func.ran_function_identifier = static_cast<uint32_t>(asn_func->ranFunctionIdentifier);
554
555 // Decode telemetryIdentifierList
556 int tel_count = asn_func->telemetryIdentifierList.list.count;
557 for (int j = 0; j < tel_count; j++) {
558 func.telemetry_identifier_list.push_back(
559 static_cast<uint32_t>(*asn_func->telemetryIdentifierList.list.array[j]));
560 }
561
562 // Decode controlIdentifierList
563 int ctrl_count = asn_func->controlIdentifierList.list.count;
564 for (int j = 0; j < ctrl_count; j++) {
565 func.control_identifier_list.push_back(
566 static_cast<uint32_t>(*asn_func->controlIdentifierList.list.array[j]));
567 }
568
569 func.ran_function_data.assign(
570 asn_func->ranFunctionData.buf,
571 asn_func->ranFunctionData.buf + asn_func->ranFunctionData.size);
572 resp.ran_function_list.push_back(func);
573 }
574 }
575
576 pdu.choice = resp;
577 break;
578 }
579
580 case E3_PDU__msg_PR_subscriptionRequest: {
582
583 SubscriptionRequest req;
584 req.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.subscriptionRequest->dAppIdentifier);
585 req.ran_function_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.subscriptionRequest->ranFunctionIdentifier);
586
587 // Decode telemetryIdentifierList
588 int tel_count = asn1_pdu->msg.choice.subscriptionRequest->telemetryIdentifierList.list.count;
589 for (int i = 0; i < tel_count; i++) {
590 req.telemetry_identifier_list.push_back(
591 static_cast<uint32_t>(*asn1_pdu->msg.choice.subscriptionRequest->telemetryIdentifierList.list.array[i]));
592 }
593
594 // Decode controlIdentifierList
595 int ctrl_count = asn1_pdu->msg.choice.subscriptionRequest->controlIdentifierList.list.count;
596 for (int i = 0; i < ctrl_count; i++) {
597 req.control_identifier_list.push_back(
598 static_cast<uint32_t>(*asn1_pdu->msg.choice.subscriptionRequest->controlIdentifierList.list.array[i]));
599 }
600
601 // Decode optional subscriptionTime
602 if (asn1_pdu->msg.choice.subscriptionRequest->subscriptionTime) {
603 req.subscription_time = *asn1_pdu->msg.choice.subscriptionRequest->subscriptionTime;
604 }
605 // Decode optional periodicity
606 if (asn1_pdu->msg.choice.subscriptionRequest->periodicity) {
607 req.periodicity = static_cast<uint32_t>(*asn1_pdu->msg.choice.subscriptionRequest->periodicity);
608 }
609
610 pdu.choice = req;
611 break;
612 }
613
614 case E3_PDU__msg_PR_subscriptionDelete: {
616
617 SubscriptionDelete del;
618 del.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.subscriptionDelete->dAppIdentifier);
619 del.subscription_id = static_cast<uint32_t>(asn1_pdu->msg.choice.subscriptionDelete->subscriptionId);
620
621 pdu.choice = del;
622 break;
623 }
624
625 case E3_PDU__msg_PR_subscriptionResponse: {
627
628 SubscriptionResponse resp;
629 resp.request_id = static_cast<uint32_t>(asn1_pdu->msg.choice.subscriptionResponse->requestId);
630 resp.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.subscriptionResponse->dAppIdentifier);
631 resp.response_code = (asn1_pdu->msg.choice.subscriptionResponse->responseCode == 0)
633
634 // Decode optional subscriptionId
635 if (asn1_pdu->msg.choice.subscriptionResponse->subscriptionId) {
636 resp.subscription_id = *asn1_pdu->msg.choice.subscriptionResponse->subscriptionId;
637 }
638
639 pdu.choice = resp;
640 break;
641 }
642
643 case E3_PDU__msg_PR_indicationMessage: {
645
646 IndicationMessage msg;
647 msg.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.indicationMessage->dAppIdentifier);
648 msg.ran_function_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.indicationMessage->ranFunctionIdentifier);
649
650 // Copy protocol data
651 const OCTET_STRING_t* data = &asn1_pdu->msg.choice.indicationMessage->protocolData;
652 msg.protocol_data.assign(data->buf, data->buf + data->size);
653
654 pdu.choice = msg;
655 break;
656 }
657
658 case E3_PDU__msg_PR_dAppControlAction: {
660
661 DAppControlAction action;
662 action.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.dAppControlAction->dAppIdentifier);
663 action.ran_function_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.dAppControlAction->ranFunctionIdentifier);
664 action.control_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.dAppControlAction->controlIdentifier);
665 action.sequence_id = asn1_pdu->msg.choice.dAppControlAction->sequenceId
666 ? static_cast<uint32_t>(*asn1_pdu->msg.choice.dAppControlAction->sequenceId)
667 : 0;
668
669 const OCTET_STRING_t* data = &asn1_pdu->msg.choice.dAppControlAction->actionData;
670 action.action_data.assign(data->buf, data->buf + data->size);
671
672 pdu.choice = action;
673 break;
674 }
675
676 case E3_PDU__msg_PR_dAppReport: {
677 pdu.type = PduType::DAPP_REPORT;
678
679 DAppReport report;
680 report.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.dAppReport->dAppIdentifier);
681 report.ran_function_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.dAppReport->ranFunctionIdentifier);
682 report.sequence_id = static_cast<uint32_t>(asn1_pdu->msg.choice.dAppReport->sequenceId);
683
684 const OCTET_STRING_t* data = &asn1_pdu->msg.choice.dAppReport->reportData;
685 report.report_data.assign(data->buf, data->buf + data->size);
686
687 pdu.choice = report;
688 break;
689 }
690
691 case E3_PDU__msg_PR_xAppControlAction: {
693
694 XAppControlAction action;
695 action.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.xAppControlAction->dAppIdentifier);
696 action.ran_function_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.xAppControlAction->ranFunctionIdentifier);
697 action.sequence_id = static_cast<uint32_t>(asn1_pdu->msg.choice.xAppControlAction->sequenceId);
698
699 const OCTET_STRING_t* data = &asn1_pdu->msg.choice.xAppControlAction->xAppControlData;
700 action.xapp_control_data.assign(data->buf, data->buf + data->size);
701
702 pdu.choice = action;
703 break;
704 }
705
706 case E3_PDU__msg_PR_messageAck: {
707 pdu.type = PduType::MESSAGE_ACK;
708
709 MessageAck ack;
710 ack.request_id = static_cast<uint32_t>(asn1_pdu->msg.choice.messageAck->requestId);
711 ack.response_code = (asn1_pdu->msg.choice.messageAck->responseCode == 0)
713
714 pdu.choice = ack;
715 break;
716 }
717
718 case E3_PDU__msg_PR_releaseMessage: {
719 pdu.type = PduType::RELEASE_MESSAGE;
720
721 ReleaseMessage msg;
722 msg.dapp_identifier = static_cast<uint32_t>(asn1_pdu->msg.choice.releaseMessage->dAppIdentifier);
723
724 pdu.choice = msg;
725 break;
726 }
727
728 default:
729 E3_LOG_ERROR(LOG_TAG) << "Unknown ASN.1 PDU type: " << asn1_pdu->msg.present;
730 break;
731 }
732
733 return pdu;
734}
735
736} // namespace libe3
ASN.1 APER Encoder for E3AP PDUs.
EncodeResult< Pdu > decode(const EncodedMessage &encoded) override
Decode bytes to a PDU.
EncodeResult< EncodedMessage > encode(const Pdu &pdu) override
Encode a PDU to bytes.
Logging facility for libe3.
#define E3_LOG_DEBUG(component)
Definition logger.hpp:228
#define E3_LOG_ERROR(component)
Definition logger.hpp:225
ResponseCode
E3AP response codes.
Definition types.hpp:101
@ NEGATIVE
Request rejected.
Definition types.hpp:103
@ POSITIVE
Request accepted.
Definition types.hpp:102
@ SUBSCRIPTION_DELETE
Definition types.hpp:113
@ SUBSCRIPTION_RESPONSE
Definition types.hpp:114
@ SUBSCRIPTION_REQUEST
Definition types.hpp:112
@ XAPP_CONTROL_ACTION
Definition types.hpp:118
@ DAPP_CONTROL_ACTION
Definition types.hpp:116
@ ASN1
ASN.1 PER encoding (standard O-RAN format).
Definition types.hpp:52
tl::expected< T, ErrorCode > EncodeResult
Result type for encoding/decoding operations.
Encoded message wrapper.
Definition types.hpp:175
std::vector< uint8_t > buffer
Encoded data buffer.
Definition types.hpp:176
EncodingFormat format
Format used for encoding.
Definition types.hpp:177
Generic E3AP PDU structure.
Definition types.hpp:345
PduType type
Definition types.hpp:346
PduChoice choice
Definition types.hpp:347
uint64_t timestamp
Producer timestamp, in nanoseconds since the Unix epoch.
Definition types.hpp:363
uint32_t message_id
Unique message identifier.
Definition types.hpp:348