3GPP ==== .. _the-threegpp-l3msg-module: The :mod:`threegpp.l3msg` module --------------------------------- .. module:: threegpp.l3msg :synopsis: 3GPP Layer 3 Message Processing Library 3GPP Layer 3 Message Processing Library This module provides classes and utilities for processing 3GPP Layer 3 messages, particularly for 5G Session Management (5GSM) and 5G Mobility Management (5GMM) protocols. It builds upon the encoding module to provide high-level message parsing and generation. .. class:: FGMMMessageType 5G Mobility Management (5GMM) message type identifiers. - RegistrationRequest = 0x41 - RegistrationAccept = 0x42 - RegistrationComplete = 0x43 - RegistrationReject = 0x44 - DeregistrationRequestUEOriginating = 0x45 - DeregistrationAcceptUEOriginating = 0x46 - DeregistrationRequestNetworkOriginating = 0x47 - DeregistrationAcceptNetworkOriginating = 0x48 - ServiceRequest = 0x4C - ServiceReject = 0x4D - ServiceAccept = 0x4E - ControlPlaneServiceRequest = 0x4F - NetworkSliceSpecificAuthenticationCommand = 0x50 - NetworkSliceSpecificAuthenticationComplete = 0x51 - NetworkSliceSpecificAuthenticationResult = 0x52 - ConfigurationUpdateCommand = 0x54 - ConfigurationUpdateComplete = 0x55 - AuthenticationRequest = 0x56 - AuthenticationResponse = 0x57 - AuthenticationReject = 0x58 - AuthenticationFailure = 0x59 - AuthenticationResult = 0x5A - IdentityRequest = 0x5B - IdentityResponse = 0x5C - SecurityModeCommand = 0x5D - SecurityModeComplete = 0x5E - SecurityModeReject = 0x5F - FiveGMMStatus = 0x64 - Notification = 0x65 - NotificationResponse = 0x66 - UplinkNasTransport = 0x67 - DownlinkNasTransport = 0x68 - RelayKeyRequest = 0x69 - RelayKeyAccept = 0x6A - RelayKeyReject = 0x6B - RelayAuthenticationRequest = 0x6C - RelayAuthenticationResponse = 0x6D .. class:: FGSMMessageType 5G Session Management (5GSM) message type identifiers. - PDUSessionEstablishmentRequest = 0xC1 - PDUSessionEstablishmentAccept = 0xC2 - PDUSessionEstablishmentReject = 0xC3 - PDUSessionAuthenticationCommand = 0xC5 - PDUSessionAuthenticationComplete = 0xC6 - PDUSessionAuthenticationResult = 0xC7 - PDUSessionModificationRequest = 0xC9 - PDUSessionModificationReject = 0xCA - PDUSessionModificationCommand = 0xCB - PDUSessionModificationComplete = 0xCC - PDUSessionModificationCompleteReject = 0xCD - PDUSessionReleaseRequest = 0xD1 - PDUSessionReleaseReject = 0xD2 - PDUSessionReleaseCommand = 0xD3 - PDUSessionReleaseComplete = 0xD4 - FiveGSMStatus = 0xD6 - ServiceLevelAuthenticationCommand = 0xD8 - ServiceLevelAuthenticationComplete = 0xD9 - RemoteUEReport = 0xDA - RemoteUEReportResponse = 0xDB .. class:: IE(desc, value) Information Element within a message. Represents a single Information Element (IE) in a 3GPP message. It contains a `value` property which can be either an integer for single-octet IEs or a bytes object for multi-octet IEs. Upon setting this property the value is validated against the format defined in the IE descriptor. Objects of this class know how to encode themselves into binary format using their descriptor attribute `desc`. :param IEDesc desc: IE descriptor defining the structure. :param any value: Value of the IE. .. method:: encode(buffer) Encode the IE into a buffer. :param bytearray buffer: Buffer to encode into. .. property:: value Get the IE value. :returns: Current value of the IE. :rtype: any .. class:: IEDesc(iei, name, fmt) Information Element Descriptor. Describes a single Information Element (IE) within a 3GPP message, including its identifier, name, and encoding format. :param iei: Information Element Identifier (IEI). None for mandatory IEs. :type iei: int or None :param str name: Descriptive name of the IE. :param fmt: Encoding format specification for this IE. :type fmt: encoding format object .. property:: mandatory Check if this IE is mandatory. :returns: True if the IE is mandatory (iei is None). :rtype: bool .. class:: Message(desc, ies) Represents a 3GPP Layer 3 message. A `Message` consists of an ordered list of IEs. This list is accessible via the `ies` parameter and can be modified by inserting, deleting, or reordering the elements. However, this is not the preferred method of interacting with a `Message`. Instead, Nokia advises to use the provided methods and operators to get, set, or delete Information Elements. This ensures that the message remains valid according to its descriptor: - mandatory IEs cannot be deleted, - optional IEs are inserted at the correct position, - values are validated according to their format, but without further semantic checks. By default these methods act on the assumption that only a single IE of each type will be present. This is the most common case. However, if multiple IEs of the same type are expected, the `all` parameter can be set to True to modify the behavior accordingly; that is, the values being passed and returned are lists instead of single values. :param MessageDescriptor desc: Message descriptor defining the structure. :param ies: List of Information Elements in this message. :type ies: list of IE .. staticmethod:: decode(data) Decode a Layer 3 message from binary data. Automatically determines the protocol type from the protocol discriminator and uses the appropriate decoder. :param data: Binary message data to decode. :type data: bytes or bytearray :returns: Decoded Message object. :rtype: Message :raises ValueError: If data is too short. :raises RuntimeError: If protocol is not supported. .. method:: delIE(key, all=False) Delete Information Element(s) by key. :param key: IEI (as int) or name (as str) of the IE. :type key: int or str :param bool all: If True, delete all occurrences; if False, delete first only. :raises RuntimeError: If attempting to delete mandatory IEs. .. method:: encode() Encode the message to binary format. :returns: Binary representation of the message. :rtype: bytes .. method:: getIE(key, all=False) Get the value(s) of an Information Element by key. :param key: IEI (as int) or name (as str) of the IE. :type key: int or str :param bool all: If True, return all occurrences; if False, return first only. :returns: IE value(s) - single value if all=False, list if all=True. :rtype: any or list .. property:: protocol Get the protocol discriminator value. :returns: Extended or regular protocol discriminator value. :rtype: int or None .. method:: setIE(key, value, all=False) Set the value(s) of an Information Element by key. :param key: IEI (as int) or name (as str) of the IE. :type key: int or str :param value: Value to set. Must be list/tuple if all=True. :type value: any or list or tuple :param bool all: If True, set multiple occurrences; if False, set first only. :raises TypeError: :raises ValueError: If value format is incorrect or IE is unknown. .. class:: MessageDescriptor(ies) Message Descriptor for 3GPP Layer 3 messages. Defines the structure of a message including its mandatory and optional Information Elements, and provides methods for decoding messages. :param ies: List of Information Element descriptors for this message. :type ies: list of IEDesc .. method:: decode(data, unknown) Decode a message from binary data. :param data: Binary data to decode. :type data: bytes or bytearray :param callable unknown: Callback function to handle unknown IE types. :returns: Decoded Message object. :rtype: Message :raises ValueError: If an IE cannot be decoded or set. .. class:: ProtocolDiscriminator Protocol Discriminator values for 3GPP Layer 3 messages. - GroupCallControl = 0x00 - BroadcastCallControl = 0x01 - EPSSessionManagement = 0x02 - CallControl = 0x03 - GTTP = 0x04 - MobilityManagement = 0x05 - RadioResourceManagement = 0x06 - EPSMobilityManagement = 0x07 - GPRSMobilityManagement = 0x08 - SMSMessages = 0x09 - GRPSSessionManagement = 0x0A - NonCallRelatedSS = 0x0B - LocationServices = 0x0C - FiveGSessionManagement = 0x2E - FiveGMobilityManagement = 0x7E .. data:: SPEC_VERSION = 'V17.9.0' 3GPP specification version used for message definitions. This constant indicates the version of 3GPP TS 24.501 specification that was used to define the message structures and IEs in this module. .. data:: fgmm_ie_descriptors 5G Mobility Management (5GMM) Information Element descriptors. .. data:: fgmm_security_protected_encrypted_msg 5GMM security protected and encrypted message descriptor. MessageDescriptor for 5GMM messages with both integrity protection and encryption. Used when the security header type indicates both integrity and ciphering (security header types 2 and 4). The payload contains encrypted NAS message data. .. data:: fgmm_security_protected_msg 5GMM security protected message descriptor. MessageDescriptor for 5GMM messages with integrity protection (no encryption). Used when the security header type indicates integrity protection only (security header types 1 and 3). .. data:: fgsm_ie_descriptors 5G Session Management (5GSM) Information Element descriptors. .. _the-threegpp-encoding-module: The :mod:`threegpp.encoding` module ------------------------------------- .. module:: threegpp.encoding :synopsis: 3GPP Message Encoding Library 3GPP Message Encoding Library This module provides encoding and decoding functionality for 3GPP Layer 3 message Information Elements (IEs) according to the format specifications in TS 24.007. The module implements all standard IE encoding formats: - Type 1: V format and TV format - Type 2: T format - Type 3: V format and TV format - Type 4: LV format and TLV format - Type 6: LVE format and TLVE format Each format class adheres to the interface defined by the abstract base class `Formatter`. .. class:: Formatter Abstract base class for 3GPP message format encoders/decoders. .. note:: This class is abstract and documents the interface implemented by objects returned by factory functions such as :func:`LV`. It should not be used directly. Defines the interface that all format classes must implement for encoding and decoding Information Elements in 3GPP messages. .. method:: check_value(value) Validate the value to be encoded. :param any value: Value to validate. :raises ValueError: If value is invalid for this format. .. method:: decode(data) Decode an Information Element from binary data. :param data: Binary data to decode. :type data: bytes or bytearray :returns: Tuple of (iei, value, remaining_data). :raises ValueError: If data cannot be decoded. .. method:: encode(iei, value, buffer) Encode an Information Element into a buffer. :param iei: Information Element Identifier. :type iei: int or None :param any value: Value to encode. :param bytearray buffer: Buffer to append encoded data to. :raises ValueError: If IEI or value is invalid. .. function:: LV(min_len=None, max_len=None) Create a formatter for Type 4 Format LV. :param min_len: Minimum total length in bytes (including length field). :type min_len: int or None :param max_len: Maximum total length in bytes (including length field). :type max_len: int or None :rtype: Formatter instance .. function:: LVE(min_len=None, max_len=None) Create a formatter for Type 6 Format LVE. :param min_len: Minimum total length in bytes (including length field). :type min_len: int or None :param max_len: Maximum total length in bytes (including length field). :type max_len: int or None :rtype: Formatter instance .. data:: T Formatter for Type 2 Format T IE. .. function:: TLV(min_len=None, max_len=None) Create a formatter for Type 4 Format TLV. :param min_len: Minimum total length in bytes (including IEI and length field). :type min_len: int or None :param max_len: Maximum total length in bytes (including IEI and length field). :type max_len: int or None :rtype: Formatter instance .. function:: TLVE(min_len=None, max_len=None) Create a formatter for Type 6 Format TLVE. :param min_len: Minimum total length in bytes (including IEI and length field). :type min_len: int or None :param max_len: Maximum total length in bytes (including IEI and length field). :type max_len: int or None :rtype: Formatter instance .. function:: TV(length) Create a Formatter for Type 3 Format TV. :param int length: Total length including IEI (minimum 1 byte). :rtype: Formatter instance .. data:: TV1 Formatter for Type 1 Format TV IE. .. function:: V(length, remainder=None) Create a Formatter for Type 3 Format V. :param int length: Fixed length of the value field in bytes. :param remainder: If ``'n'``, allows variable length from length to max. If None, fixed length. :type remainder: 'n' or None :rtype: Formatter instance .. data:: Vlower Formatter for Type 1 Format V lower half-octet IE. .. data:: Vupper Formatter for Type 1 Format V upper half-octet IE. Examples -------- 1. Basic modification ^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python :linenos: from binascii import unhexlify from threegpp.l3msg import Message data = unhexlify(b"2e0101c1ffff91a12801017b001380000100001000000c00000e00000300000d00") # Decode a message from a byte string msg = Message.decode(data) # Access the protocol discriminator value protocol = msg.protocol # Returns 0x2E for 5GSM # Get the value of an IE capability = msg["5GSMCapability"] # Check if an IE is present in the message if "SuggestedInterfaceIdentifier" in msg: # Delete the IE from the message del msg["SuggestedInterfaceIdentifier"] # Set a new value for an existing IE msg["5GSMCapability"] = 2 # Setting a value for a non-existing IE adds it to the message msg["MaximumNumberOfSupportedPacketFilters"] = b"\x01\x02" # The IE-indexing functions also work with IEIs msg[0x55] = b"\x03\x04" # Encode the modified message back to a byte string modified_data = msg.encode() 2. Messages with multi-valued IEs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python :linenos: from binascii import unhexlify from threegpp.l3msg import Message # Example message with multiple optional IEs data = unhexlify(b"2e0101c1ffff91a12801017b001380000100001000000c00000e00000300000d00") # Decode a message from a byte string msg = Message.decode(data) # getIE() - Get a single IE value (first occurrence by default) capability = msg.getIE("5GSMCapability") # getIE(all=True) - Get all occurrences of an IE all_capabilities = msg.getIE("5GSMCapability", all=True) # setIE(all=True) - Replace all occurrences with new values # This replaces all occurrences with the values in the list msg.setIE("5GSMCapability", [0xAA, 0xBB, 0xCC], all=True) # delIE() - Delete first occurrence of an IE msg.delIE("5GSMCapability") # delIE(all=True) - Delete all remaining occurrences of an IE msg.delIE("5GSMCapability", all=True) # Check if IE exists after deletion exists = "5GSMCapability" in msg # Using IEI (0x28) instead of name msg.setIE(0x28, [0x12, 0x34], all=True) capabilities_by_iei = msg.getIE(0x28, all=True) # Difference between [] and getIE() first_only = msg["5GSMCapability"] # Returns first value only first_getie = msg.getIE("5GSMCapability") # Also returns first value only all_getie = msg.getIE("5GSMCapability", all=True) # Returns list of all values 3. Working with custom IEs and the IE list ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python :linenos: from binascii import unhexlify from threegpp.l3msg import Message, IE, IEDesc from threegpp.encoding import TLV # Example message data = unhexlify(b"2e0101c1ffff91a12801017b001380000100001000000c00000e00000300000d00") msg = Message.decode(data) # Direct access to the IEs list # Message.ies is a list of IE objects ies_list = msg.ies # Iterate over all IEs in the message for ie in msg.ies: # Each IE has a descriptor (desc) and a value ie_name = ie.desc.name ie_value = ie.value ie_iei = ie.desc.iei # Find a specific IE by examining the list capability_ie = next((ie for ie in msg.ies if ie.desc.name == "5GSMCapability"), None) # Modify an IE's value directly if capability_ie: capability_ie.value = 0xAB # Add a new IE by creating an IE object and appending to the list # Note: No ordering checks are performed when manipulating the list directly new_ie_desc = IEDesc(0x99, "CustomIE", TLV(3, 255)) new_ie = IE(new_ie_desc, b"\x01\x02") msg.ies.append(new_ie) # Insert an IE at a specific position another_ie_desc = IEDesc(0xAA, "AnotherCustomIE", TV(2)) another_ie = IE(another_ie_desc, 1) msg.ies.insert(5, another_ie) # Insert at position 5 # Remove an IE from the list by index del msg.ies[5] # Remove an IE by finding it first ie_to_remove = next((ie for ie in msg.ies if ie.desc.name == "5GSMCapability"), None) if ie_to_remove: msg.ies.remove(ie_to_remove) # Add multiple IEs of the same type for value in [0x11, 0x22, 0x33]: ie_desc = msg.desc["5GSMCapability"] msg.ies.append(IE(ie_desc, value)) # Encode the modified message modified_data = msg.encode() 4. Custom messages ^^^^^^^^^^^^^^^^^^^ .. code-block:: python :linenos: from threegpp.l3msg import Message, MessageDescriptor, IEDesc, fgsm_ie_descriptors from threegpp.encoding import V, TV, TLV, LV # Define a custom message type code (use a reserved/unassigned value) CUSTOM_MESSAGE_TYPE = 0xFF # Create a MessageDescriptor for the custom message # This defines the structure of IEs for this new message type custom_message_descriptor = MessageDescriptor( [ # Mandatory IEs IEDesc(None, "ExtendedProtocolDiscriminator", V(1)), IEDesc(None, "PDUSessionID", V(1)), IEDesc(None, "PTI", V(1)), IEDesc(None, "MessageType", V(1)), IEDesc(None, "CustomMandatoryField", V(2)), # Optional IEs (with IEI values) IEDesc(0x10, "CustomOptionalField1", TV(2)), IEDesc(0x20, "CustomOptionalField2", TLV(3, 255)), IEDesc(0x30, "CustomOptionalField3", TLV(3, 255)), ] ) # Add the custom message descriptor to the 5GSM descriptor table fgsm_ie_descriptors[CUSTOM_MESSAGE_TYPE] = custom_message_descriptor # Now we can decode a message of this custom type # Construct a raw message with the custom message type custom_message_data = bytes([ 0x2E, # Extended Protocol Discriminator (5GSM) 0x05, # PDU Session ID 0x01, # PTI 0xFF, # Message Type (our custom type) 0x12, 0x34, # CustomMandatoryField (2 bytes) 0x10, 0xAB, # CustomOptionalField1 (TV format, IEI=0x10, value=0xAB) 0x20, 0x03, 0x01, 0x02, 0x03, # CustomOptionalField2 (TLV format) ]) # Decode the custom message msg = Message.decode(custom_message_data) # Access the custom IEs protocol_disc = msg["ExtendedProtocolDiscriminator"] session_id = msg["PDUSessionID"] mandatory_field = msg["CustomMandatoryField"] optional_field1 = msg["CustomOptionalField1"] optional_field2 = msg["CustomOptionalField2"] # Modify custom IE values # For V format fields, value must match the exact length msg["CustomMandatoryField"] = b"\x56\x78" msg["CustomOptionalField1"] = 0xCD # Add another optional IE msg["CustomOptionalField3"] = b"\x01\x02\x03\x04" # Encode the modified message back encoded_data = msg.encode() 5. Error handling ^^^^^^^^^^^^^^^^^^ .. code-block:: python :linenos: from threegpp.l3msg import Message # Example 1: Handling unknown message types # Attempting to decode a message with an unknown message type try: unknown_msg_data = bytes([ 0x2E, # Extended Protocol Discriminator (5GSM) 0x05, # PDU Session ID 0x01, # PTI 0xEE, # Unknown Message Type ]) msg = Message.decode(unknown_msg_data) except RuntimeError as e: # Will raise: "Unknown 5GSM message type: 0xee" print(e) # Example 2: Data too short for message header try: short_data = bytes([0x2E, 0x01]) # Only 2 bytes, needs at least 4 for 5GSM msg = Message.decode(short_data) except ValueError as e: # Will raise: "Data too short to contain 5GSM header" print(e) # Example 3: Invalid protocol discriminator try: invalid_protocol = bytes([0xFF, 0x01, 0x02, 0x03]) msg = Message.decode(invalid_protocol) except RuntimeError as e: # Protocol discriminator not in decoder table print(e) # Example 4: Attempting to delete a mandatory IE # Create a RegistrationComplete message (5GMM message type 0x43) msg = Message.decode(bytes([ 0x7E, # Extended Protocol Discriminator (5GMM) 0x00, # Security Header Type (0) | Spare Half Octet (0) 0x43, # Message Type (RegistrationComplete) ])) try: del msg["MessageType"] # MessageType is mandatory except RuntimeError as e: # Will raise: "Cannot delete mandatory IEs" print(e) # Example 5: Setting invalid value for IE try: # SORTransparentContainer expects TLVE format (min 20 bytes) # The format validates the value length msg["SORTransparentContainer"] = b"\x01" # Too short except ValueError as e: # Format validation error print(e) # Example 6: Attempting to set unknown IE when not allowed # This is caught when trying to insert an IE that doesn't exist in the descriptor try: msg["NonExistentIE"] = b"\x01\x02\x03" except RuntimeError as e: # Will raise: "Cannot insert unknown IE" print(e) # Example 7: Value validation - out of range for nibble fields try: # SecurityHeaderType is a nibble field (upper 4 bits), values 0x0-0xF only msg["SecurityHeaderType"] = 0x20 # Too large for a nibble except ValueError as e: # Will raise: "Value out of range" print(e) # Example 8: Encoding with invalid data try: # Create a message with invalid mandatory field value msg["ExtendedProtocolDiscriminator"] = 0x1234 # Too large for 1 byte except ValueError as e: # Value validation fails print(e)