Created
December 23, 2025 09:52
-
-
Save ldmsys/f286868037891fc8fc5c6e4a62d6e9c5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/srsepc/hdr/mme/nas.h b/srsepc/hdr/mme/nas.h | |
| index 88364cdcd..c8409b8f8 100644 | |
| --- a/srsepc/hdr/mme/nas.h | |
| +++ b/srsepc/hdr/mme/nas.h | |
| @@ -247,6 +247,9 @@ public: | |
| bool pack_tracking_area_update_reject(srsran::byte_buffer_t* nas_buffer, uint8_t emm_cause); | |
| bool pack_attach_accept(srsran::byte_buffer_t* nas_buffer); | |
| + // Generic SMS Packing Function | |
| + bool pack_sms_notification(srsran::byte_buffer_t* nas_buffer, const std::string& msg_text); | |
| + | |
| /* Security functions */ | |
| bool integrity_check(srsran::byte_buffer_t* pdu, bool warn_failure = true); | |
| bool short_integrity_check(srsran::byte_buffer_t* pdu); | |
| @@ -289,6 +292,9 @@ private: | |
| // Timer functions | |
| bool start_t3413(); | |
| bool expire_t3413(); | |
| + | |
| + // SMS RP-Message Reference Counter | |
| + uint8_t m_next_rp_msg_ref = 0; | |
| }; | |
| } // namespace srsepc | |
| diff --git a/srsepc/src/mme/nas.cc b/srsepc/src/mme/nas.cc | |
| index 7e9834d06..e81401f1c 100644 | |
| --- a/srsepc/src/mme/nas.cc | |
| +++ b/srsepc/src/mme/nas.cc | |
| @@ -1157,6 +1157,191 @@ bool nas::handle_attach_complete(srsran::byte_buffer_t* nas_rx) | |
| m_logger.info("Sending EMM Information"); | |
| } | |
| m_emm_ctx.state = EMM_STATE_REGISTERED; | |
| + | |
| + // Send SMS Notification | |
| + srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); | |
| + if (nas_tx) { | |
| + // Enter desired text (ASCII characters recommended) | |
| + std::string welcome_msg = "Welcome to KAIST EE515 by Prof. Yongdae Kim!"; | |
| + | |
| + if (pack_sms_notification(nas_tx.get(), welcome_msg)) { | |
| + m_s1ap->send_downlink_nas_transport( | |
| + m_ecm_ctx.enb_ue_s1ap_id, | |
| + m_ecm_ctx.mme_ue_s1ap_id, | |
| + nas_tx.get(), | |
| + m_ecm_ctx.enb_sri | |
| + ); | |
| + srsran::console("Sent Welcome SMS to UE: %s\n", welcome_msg.c_str()); | |
| + } | |
| + } | |
| + | |
| + return true; | |
| +} | |
| + | |
| +// 3GPP TS 23.038: Packs 8-bit ASCII characters into 7-bit septets | |
| +void convert_string_to_gsm7(const std::string& input, std::vector<uint8_t>& output, uint8_t& septet_count) { | |
| + uint32_t bit_buffer = 0; | |
| + int bit_count = 0; | |
| + septet_count = 0; | |
| + | |
| + for (char c : input) { | |
| + // Note: Commercial implementations require a full GSM mapping table (e.g., '@' -> 0x00). | |
| + // Applying 0x7F masking here for standard alphanumeric ASCII compatibility. | |
| + uint8_t val = c & 0x7F; | |
| + | |
| + bit_buffer |= (val << bit_count); | |
| + bit_count += 7; | |
| + septet_count++; | |
| + | |
| + if (bit_count >= 8) { | |
| + output.push_back(static_cast<uint8_t>(bit_buffer & 0xFF)); | |
| + bit_buffer >>= 8; | |
| + bit_count -= 8; | |
| + } | |
| + } | |
| + | |
| + if (bit_count > 0) { | |
| + output.push_back(static_cast<uint8_t>(bit_buffer & 0xFF)); | |
| + } | |
| +} | |
| + | |
| +// TP-SCTS Format: YY MM DD HH MM SS TZ (7 bytes) | |
| +// Each byte is swapped BCD: e.g., 23 -> 0x32 | |
| +uint8_t to_swapped_bcd (int num) { | |
| + int tens = (num / 10) % 10; | |
| + int units = num % 10; | |
| + return (units << 4) | tens; | |
| +} | |
| + | |
| +void get_current_scts(uint8_t* buf) { | |
| + time_t now = time(nullptr); | |
| + struct tm* t = localtime(&now); | |
| + | |
| + buf[0] = to_swapped_bcd((t->tm_year % 100)); // Year | |
| + buf[1] = to_swapped_bcd(t->tm_mon + 1); // Month | |
| + buf[2] = to_swapped_bcd(t->tm_mday); // Day | |
| + buf[3] = to_swapped_bcd(t->tm_hour); // Hour | |
| + buf[4] = to_swapped_bcd(t->tm_min); // Minute | |
| + buf[5] = to_swapped_bcd(t->tm_sec); // Second | |
| + buf[6] = 0x00; // TimeZone (Local/Unknown) | |
| +} | |
| + | |
| +bool nas::pack_sms_notification(srsran::byte_buffer_t* nas_buffer, const std::string& msg_text) | |
| +{ | |
| + m_logger.info("Packing SMS Notification: '%s'", msg_text.c_str()); | |
| + | |
| + // Layer 1: TP-User-Data Packing (GSM 7-bit) | |
| + std::vector<uint8_t> tp_ud_packed; | |
| + uint8_t septet_count = 0; | |
| + convert_string_to_gsm7(msg_text, tp_ud_packed, septet_count); | |
| + | |
| + // Layer 1~3: SMS Payload Construction (CP-DATA) | |
| + srsran::unique_byte_buffer_t cp_data_buf = srsran::make_byte_buffer(); | |
| + if (!cp_data_buf) { | |
| + m_logger.error("Failed to allocate buffer for SMS payload"); | |
| + return false; | |
| + } | |
| + | |
| + uint8_t* ptr = cp_data_buf->msg; | |
| + int len = 0; | |
| + | |
| + // [Layer 3] CP-DATA Header (TS 24.011) | |
| + ptr[len++] = 0x09; // Protocol Discriminator (SMS) | |
| + ptr[len++] = 0x01; // Message Type (CP-DATA) | |
| + int cp_len_idx = len++; | |
| + | |
| + // [Layer 2] RP-DATA Header (TS 24.011) | |
| + int rp_start_idx = len; | |
| + ptr[len++] = 0x01; // RP-Message Type (Network->MS) | |
| + ptr[len++] = m_next_rp_msg_ref++; // RP-Message Reference | |
| + | |
| + // RP-Originator Address (SMSC Addr: 0000) | |
| + ptr[len++] = 0x04; ptr[len++] = 0x80; ptr[len++] = 0x00; ptr[len++] = 0x00; ptr[len++] = 0x00; | |
| + ptr[len++] = 0x00; // RP-Destination Address Length (0 for DL) | |
| + int rp_ud_len_idx = len++;; | |
| + | |
| + // [Layer 1] TP-PDU (SMS-DELIVER) (TS 23.040) | |
| + int tp_start_idx = len; | |
| + ptr[len++] = 0x04; // TP-MTI (SMS-DELIVER, No More Msg) | |
| + ptr[len++] = 0x04; ptr[len++] = 0x91; ptr[len++] = 0x21; ptr[len++] = 0x43; // TP-OA (Sender: +1234) | |
| + ptr[len++] = 0x00; // TP-PID | |
| + ptr[len++] = 0x00; // TP-DCS (7bit default) | |
| + | |
| + // Dynamic Timestamp (TP-SCTS) | |
| + get_current_scts(&ptr[len]); | |
| + len += 7; | |
| + | |
| + // TP-UDL: Uncompressed length (number of septets) | |
| + ptr[len++] = septet_count; | |
| + | |
| + memcpy(&ptr[len], tp_ud_packed.data(), tp_ud_packed.size()); | |
| + len += tp_ud_packed.size(); | |
| + | |
| + // Length Fixup | |
| + ptr[rp_ud_len_idx] = len - tp_start_idx; | |
| + ptr[cp_len_idx] = len - rp_start_idx; | |
| + cp_data_buf->N_bytes = len; | |
| + | |
| + // Layer 4: NAS Transport Packing & Security | |
| + LIBLTE_MME_DOWNLINK_NAS_TRANSPORT_MSG_STRUCT dl_nas_transport; | |
| + | |
| + // Copy Payload | |
| + dl_nas_transport.nas_msg.N_bytes = cp_data_buf->N_bytes; | |
| + if(cp_data_buf->N_bytes <= LIBLTE_MAX_MSG_SIZE_BYTES) { | |
| + memcpy(dl_nas_transport.nas_msg.msg, cp_data_buf->msg, cp_data_buf->N_bytes); | |
| + } else { | |
| + m_logger.error("SMS Payload too large"); | |
| + return false; | |
| + } | |
| + | |
| + // Set Security Header Type (Integrity & Ciphered) | |
| + uint8_t sec_hdr_type = LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED; | |
| + | |
| + // Increment Downlink NAS Count | |
| + m_sec_ctx.dl_nas_count++; | |
| + | |
| + // Pack Downlink NAS Transport using LibLTE | |
| + LIBLTE_ERROR_ENUM err = liblte_mme_pack_downlink_nas_transport_msg( | |
| + &dl_nas_transport, | |
| + sec_hdr_type, | |
| + m_sec_ctx.dl_nas_count, | |
| + (LIBLTE_BYTE_MSG_STRUCT*)nas_buffer | |
| + ); | |
| + | |
| + if (err!= LIBLTE_SUCCESS) { | |
| + m_logger.error("Error packing Downlink NAS Transport"); | |
| + return false; | |
| + } | |
| + | |
| + // Encrypt NAS message | |
| + cipher_encrypt(nas_buffer); | |
| + | |
| + // Integrity protect NAS message | |
| + uint8_t mac[4]; | |
| + integrity_generate(nas_buffer, mac); | |
| + memcpy(&nas_buffer->msg[1], mac, 4); // MAC offset is 1 (after header byte) | |
| + | |
| + m_logger.info("Packed SMS Notification with Security (Seq: %d)", m_sec_ctx.dl_nas_count); | |
| return true; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment