2018-04-05 12:04:06 +00:00
|
|
|
/* packet-pdcp-nr.c
|
|
|
|
* Routines for nr PDCP
|
|
|
|
*
|
|
|
|
* Martin Mathieson
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <epan/packet.h>
|
|
|
|
#include <epan/prefs.h>
|
|
|
|
#include <epan/expert.h>
|
|
|
|
#include <epan/uat.h>
|
|
|
|
#include <epan/proto_data.h>
|
|
|
|
|
|
|
|
#include <wsutil/wsgcrypt.h>
|
2021-01-01 15:51:00 +00:00
|
|
|
#include <wsutil/report_message.h>
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2021-01-16 22:16:19 +00:00
|
|
|
/* Define these symbols if you have working implementations of SNOW3G/ZUC f8() and f9() available.
|
|
|
|
Note that the use of these algorithms is restricted, so a version of Wireshark with these
|
|
|
|
ciphering algorithms enabled would not be distributable. */
|
2020-12-29 13:55:35 +00:00
|
|
|
/* #define HAVE_SNOW3G */
|
2021-01-16 22:16:19 +00:00
|
|
|
/* #define HAVE_ZUC */
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
#include "packet-rlc-nr.h"
|
|
|
|
#include "packet-pdcp-nr.h"
|
|
|
|
|
|
|
|
void proto_register_pdcp_nr(void);
|
|
|
|
void proto_reg_handoff_pdcp_nr(void);
|
|
|
|
|
|
|
|
/* Described in:
|
|
|
|
* 3GPP TS 38.323 Technical Specification Group Radio Access Netowrk; NR;
|
|
|
|
* Packet Data Convergence Protocol (PDCP) specification (Release 15.1.0)
|
2020-12-29 13:55:35 +00:00
|
|
|
* 3GPP TS 37.324 Technical Specification Group Radio Access Network; E-UTRA and NR;
|
|
|
|
* Service Data Adaptation Protocol (SDAP) specification (Release 15)
|
2018-04-05 12:04:06 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO:
|
2020-12-29 13:55:35 +00:00
|
|
|
- look into refactoring/sharing parts of deciphering/integrity with LTE implementation
|
|
|
|
*/
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Initialize the protocol and registered fields. */
|
|
|
|
int proto_pdcp_nr = -1;
|
|
|
|
|
|
|
|
extern int proto_rlc_nr;
|
|
|
|
|
|
|
|
/* Configuration (info known outside of PDU) */
|
|
|
|
static int hf_pdcp_nr_configuration = -1;
|
|
|
|
static int hf_pdcp_nr_direction = -1;
|
|
|
|
static int hf_pdcp_nr_ueid = -1;
|
|
|
|
static int hf_pdcp_nr_bearer_type = -1;
|
|
|
|
static int hf_pdcp_nr_bearer_id = -1;
|
|
|
|
static int hf_pdcp_nr_plane = -1;
|
|
|
|
static int hf_pdcp_nr_seqnum_length = -1;
|
2020-12-29 13:55:35 +00:00
|
|
|
static int hf_pdcp_nr_maci_present = -1;
|
2020-09-25 12:47:08 +00:00
|
|
|
static int hf_pdcp_nr_sdap = -1;
|
2021-02-28 18:09:54 +00:00
|
|
|
static int hf_pdcp_nr_ciphering_disabled = -1;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
static int hf_pdcp_nr_rohc_compression = -1;
|
|
|
|
static int hf_pdcp_nr_rohc_mode = -1;
|
|
|
|
static int hf_pdcp_nr_rohc_rnd = -1;
|
|
|
|
static int hf_pdcp_nr_rohc_udp_checksum_present = -1;
|
|
|
|
static int hf_pdcp_nr_rohc_profile = -1;
|
|
|
|
static int hf_pdcp_nr_cid_inclusion_info = -1;
|
|
|
|
static int hf_pdcp_nr_large_cid_present = -1;
|
|
|
|
|
|
|
|
/* PDCP header fields */
|
|
|
|
static int hf_pdcp_nr_control_plane_reserved = -1;
|
|
|
|
static int hf_pdcp_nr_reserved3 = -1;
|
|
|
|
static int hf_pdcp_nr_seq_num_12 = -1;
|
|
|
|
static int hf_pdcp_nr_reserved5 = -1;
|
|
|
|
static int hf_pdcp_nr_seq_num_18 = -1;
|
|
|
|
static int hf_pdcp_nr_signalling_data = -1;
|
|
|
|
static int hf_pdcp_nr_mac = -1;
|
|
|
|
static int hf_pdcp_nr_data_control = -1;
|
|
|
|
static int hf_pdcp_nr_user_plane_data = -1;
|
|
|
|
static int hf_pdcp_nr_control_pdu_type = -1;
|
|
|
|
static int hf_pdcp_nr_fmc = -1;
|
|
|
|
static int hf_pdcp_nr_reserved4 = -1;
|
|
|
|
static int hf_pdcp_nr_bitmap = -1;
|
|
|
|
static int hf_pdcp_nr_bitmap_byte = -1;
|
|
|
|
|
|
|
|
/* Sequence Analysis */
|
|
|
|
static int hf_pdcp_nr_sequence_analysis = -1;
|
|
|
|
static int hf_pdcp_nr_sequence_analysis_ok = -1;
|
|
|
|
static int hf_pdcp_nr_sequence_analysis_previous_frame = -1;
|
|
|
|
static int hf_pdcp_nr_sequence_analysis_next_frame = -1;
|
|
|
|
static int hf_pdcp_nr_sequence_analysis_expected_sn = -1;
|
|
|
|
static int hf_pdcp_nr_sequence_analysis_repeated = -1;
|
|
|
|
static int hf_pdcp_nr_sequence_analysis_skipped = -1;
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Security Settings */
|
|
|
|
static int hf_pdcp_nr_security = -1;
|
|
|
|
static int hf_pdcp_nr_security_setup_frame = -1;
|
|
|
|
static int hf_pdcp_nr_security_integrity_algorithm = -1;
|
|
|
|
static int hf_pdcp_nr_security_ciphering_algorithm = -1;
|
|
|
|
|
|
|
|
static int hf_pdcp_nr_security_bearer = -1;
|
|
|
|
static int hf_pdcp_nr_security_direction = -1;
|
|
|
|
static int hf_pdcp_nr_security_count = -1;
|
|
|
|
static int hf_pdcp_nr_security_cipher_key = -1;
|
|
|
|
static int hf_pdcp_nr_security_integrity_key = -1;
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Protocol subtree. */
|
|
|
|
static int ett_pdcp = -1;
|
|
|
|
static int ett_pdcp_configuration = -1;
|
|
|
|
static int ett_pdcp_packet = -1;
|
|
|
|
static int ett_pdcp_nr_sequence_analysis = -1;
|
|
|
|
static int ett_pdcp_report_bitmap = -1;
|
2020-12-29 13:55:35 +00:00
|
|
|
static int ett_pdcp_security = -1;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
static expert_field ei_pdcp_nr_sequence_analysis_wrong_sequence_number = EI_INIT;
|
|
|
|
static expert_field ei_pdcp_nr_reserved_bits_not_zero = EI_INIT;
|
|
|
|
static expert_field ei_pdcp_nr_sequence_analysis_sn_repeated = EI_INIT;
|
|
|
|
static expert_field ei_pdcp_nr_sequence_analysis_sn_missing = EI_INIT;
|
2020-12-29 13:55:35 +00:00
|
|
|
static expert_field ei_pdcp_nr_digest_wrong = EI_INIT;
|
2018-04-12 13:48:12 +00:00
|
|
|
static expert_field ei_pdcp_nr_unknown_udp_framing_tag = EI_INIT;
|
|
|
|
static expert_field ei_pdcp_nr_missing_udp_framing_tag = EI_INIT;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/*-------------------------------------
|
|
|
|
* UAT for UE Keys
|
|
|
|
*-------------------------------------
|
|
|
|
*/
|
|
|
|
/* UAT entry structure. */
|
|
|
|
typedef struct {
|
|
|
|
guint32 ueid;
|
|
|
|
gchar *rrcCipherKeyString;
|
|
|
|
gchar *upCipherKeyString;
|
|
|
|
gchar *rrcIntegrityKeyString;
|
|
|
|
gchar *upIntegrityKeyString;
|
|
|
|
|
|
|
|
guint8 rrcCipherBinaryKey[16];
|
|
|
|
gboolean rrcCipherKeyOK;
|
|
|
|
guint8 upCipherBinaryKey[16];
|
|
|
|
gboolean upCipherKeyOK;
|
|
|
|
guint8 rrcIntegrityBinaryKey[16];
|
|
|
|
gboolean rrcIntegrityKeyOK;
|
|
|
|
guint8 upIntegrityBinaryKey[16];
|
|
|
|
gboolean upIntegrityKeyOK;
|
|
|
|
|
|
|
|
} uat_ue_keys_record_t;
|
|
|
|
|
|
|
|
/* N.B. this is an array/table of the struct above, where ueid is the key */
|
|
|
|
static uat_ue_keys_record_t *uat_ue_keys_records = NULL;
|
|
|
|
|
|
|
|
/* Entries added by UAT */
|
|
|
|
static uat_t * ue_keys_uat = NULL;
|
|
|
|
static guint num_ue_keys_uat = 0;
|
|
|
|
|
|
|
|
/* Convert an ascii hex character into a digit. Should only be given valid
|
|
|
|
hex ascii characters */
|
|
|
|
static guchar hex_ascii_to_binary(gchar c)
|
|
|
|
{
|
|
|
|
if ((c >= '0') && (c <= '9')) {
|
|
|
|
return c - '0';
|
|
|
|
}
|
|
|
|
else if ((c >= 'a') && (c <= 'f')) {
|
|
|
|
return 10 + c - 'a';
|
|
|
|
}
|
|
|
|
else if ((c >= 'A') && (c <= 'F')) {
|
|
|
|
return 10 + c - 'A';
|
|
|
|
}
|
2021-01-01 15:51:00 +00:00
|
|
|
else {
|
2020-12-29 13:55:35 +00:00
|
|
|
return 0;
|
2021-01-01 15:51:00 +00:00
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void* uat_ue_keys_record_copy_cb(void* n, const void* o, size_t siz _U_) {
|
|
|
|
uat_ue_keys_record_t* new_rec = (uat_ue_keys_record_t *)n;
|
|
|
|
const uat_ue_keys_record_t* old_rec = (const uat_ue_keys_record_t *)o;
|
|
|
|
|
|
|
|
new_rec->ueid = old_rec->ueid;
|
|
|
|
new_rec->rrcCipherKeyString = g_strdup(old_rec->rrcCipherKeyString);
|
|
|
|
new_rec->upCipherKeyString = g_strdup(old_rec->upCipherKeyString);
|
|
|
|
new_rec->rrcIntegrityKeyString = g_strdup(old_rec->rrcIntegrityKeyString);
|
|
|
|
new_rec->upIntegrityKeyString = g_strdup(old_rec->upIntegrityKeyString);
|
|
|
|
|
|
|
|
return new_rec;
|
|
|
|
}
|
|
|
|
|
2021-01-01 15:51:00 +00:00
|
|
|
/* If raw_string is a valid key, set check_string & return TRUE. Can be spaced out with ' ' or '-' */
|
|
|
|
static gboolean check_valid_key_string(const char* raw_string, char* checked_string, char **error)
|
2020-12-29 13:55:35 +00:00
|
|
|
{
|
|
|
|
guint n;
|
|
|
|
guint written = 0;
|
|
|
|
guint length = (gint)strlen(raw_string);
|
|
|
|
|
|
|
|
/* Can't be valid if not long enough. */
|
|
|
|
if (length < 32) {
|
2021-01-01 15:51:00 +00:00
|
|
|
if (length > 0) {
|
2021-02-14 15:00:19 +00:00
|
|
|
*error = g_strdup_printf("PDCP NR: Invalid key string (%s) - should include 32 ASCII hex characters (16 bytes) but only %u chars given",
|
|
|
|
raw_string, length);
|
2021-01-01 15:51:00 +00:00
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (n=0; (n < length) && (written < 32); n++) {
|
|
|
|
char c = raw_string[n];
|
|
|
|
|
|
|
|
/* Skipping past allowed 'padding' characters */
|
|
|
|
if ((c == ' ') || (c == '-')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Other characters must be hex digits, otherwise string is invalid */
|
|
|
|
if (((c >= '0') && (c <= '9')) ||
|
|
|
|
((c >= 'a') && (c <= 'f')) ||
|
|
|
|
((c >= 'A') && (c <= 'F'))) {
|
|
|
|
checked_string[written++] = c;
|
|
|
|
}
|
|
|
|
else {
|
2021-02-14 15:00:19 +00:00
|
|
|
*error = g_strdup_printf("PDCP-NR: Invalid char '%c' given in key", c);
|
2020-12-29 13:55:35 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Must have found exactly 32 hex ascii chars for 16-byte key */
|
2021-01-01 15:51:00 +00:00
|
|
|
if (n<length) {
|
2021-02-14 15:00:19 +00:00
|
|
|
*error = g_strdup_printf("PDCP-NR: Key (%s) should contain 32 hex characters (16 bytes) but more detected", raw_string);
|
2021-01-01 15:51:00 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (written != 32) {
|
2021-02-14 15:00:19 +00:00
|
|
|
*error = g_strdup_printf("PDCP-NR: Key (%s) should contain 32 hex characters (16 bytes) but %u detected", raw_string, written);
|
2021-01-01 15:51:00 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return TRUE;
|
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write binary key by converting each nibble from the string version */
|
2021-01-01 15:51:00 +00:00
|
|
|
static void update_key_from_string(const char *stringKey, guint8 *binaryKey, gboolean *pKeyOK, char **error)
|
2020-12-29 13:55:35 +00:00
|
|
|
{
|
|
|
|
int n;
|
|
|
|
char cleanString[32];
|
|
|
|
|
2021-01-01 15:51:00 +00:00
|
|
|
if (!check_valid_key_string(stringKey, cleanString, error)) {
|
2020-12-29 13:55:35 +00:00
|
|
|
*pKeyOK = FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (n=0; n < 32; n += 2) {
|
|
|
|
binaryKey[n/2] = (hex_ascii_to_binary(cleanString[n]) << 4) +
|
|
|
|
hex_ascii_to_binary(cleanString[n+1]);
|
|
|
|
}
|
|
|
|
*pKeyOK = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update by checking whether the 3 key strings are valid or not, and storing result */
|
2021-01-01 15:51:00 +00:00
|
|
|
static gboolean uat_ue_keys_record_update_cb(void* record, char** error) {
|
2020-12-29 13:55:35 +00:00
|
|
|
uat_ue_keys_record_t* rec = (uat_ue_keys_record_t *)record;
|
|
|
|
|
|
|
|
/* Check and convert RRC cipher key */
|
2021-01-01 15:51:00 +00:00
|
|
|
update_key_from_string(rec->rrcCipherKeyString, rec->rrcCipherBinaryKey, &rec->rrcCipherKeyOK, error);
|
2020-12-29 13:55:35 +00:00
|
|
|
|
|
|
|
/* Check and convert User-plane cipher key */
|
2021-01-01 15:51:00 +00:00
|
|
|
update_key_from_string(rec->upCipherKeyString, rec->upCipherBinaryKey, &rec->upCipherKeyOK, error);
|
2020-12-29 13:55:35 +00:00
|
|
|
|
|
|
|
/* Check and convert RRC Integrity key */
|
2021-01-01 15:51:00 +00:00
|
|
|
update_key_from_string(rec->rrcIntegrityKeyString, rec->rrcIntegrityBinaryKey, &rec->rrcIntegrityKeyOK, error);
|
2020-12-29 13:55:35 +00:00
|
|
|
|
|
|
|
/* Check and convert User-plane Integrity key */
|
2021-01-01 15:51:00 +00:00
|
|
|
update_key_from_string(rec->upIntegrityKeyString, rec->upIntegrityBinaryKey, &rec->upIntegrityKeyOK, error);
|
2020-12-29 13:55:35 +00:00
|
|
|
|
2021-01-01 15:51:00 +00:00
|
|
|
/* Return TRUE only if *error has not been set by checking code. */
|
|
|
|
return *error == NULL;
|
2020-12-29 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Free heap parts of record */
|
|
|
|
static void uat_ue_keys_record_free_cb(void*r) {
|
|
|
|
uat_ue_keys_record_t* rec = (uat_ue_keys_record_t*)r;
|
|
|
|
|
|
|
|
g_free(rec->rrcCipherKeyString);
|
|
|
|
g_free(rec->upCipherKeyString);
|
|
|
|
g_free(rec->rrcIntegrityKeyString);
|
|
|
|
g_free(rec->upIntegrityKeyString);
|
|
|
|
}
|
|
|
|
|
|
|
|
UAT_DEC_CB_DEF(uat_ue_keys_records, ueid, uat_ue_keys_record_t)
|
|
|
|
UAT_CSTRING_CB_DEF(uat_ue_keys_records, rrcCipherKeyString, uat_ue_keys_record_t)
|
|
|
|
UAT_CSTRING_CB_DEF(uat_ue_keys_records, upCipherKeyString, uat_ue_keys_record_t)
|
|
|
|
UAT_CSTRING_CB_DEF(uat_ue_keys_records, rrcIntegrityKeyString, uat_ue_keys_record_t)
|
|
|
|
UAT_CSTRING_CB_DEF(uat_ue_keys_records, upIntegrityKeyString, uat_ue_keys_record_t)
|
|
|
|
|
|
|
|
/* Also supporting a hash table with entries from these functions */
|
|
|
|
|
|
|
|
/* Table from ueid -> uat_ue_keys_record_t* */
|
|
|
|
static wmem_map_t *pdcp_security_key_hash = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
void set_pdcp_nr_rrc_ciphering_key(guint16 ueid, const char *key)
|
|
|
|
{
|
2021-01-09 00:30:00 +00:00
|
|
|
char *err = NULL;
|
2021-01-01 15:51:00 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Get or create struct for this UE */
|
|
|
|
uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash,
|
|
|
|
GUINT_TO_POINTER((guint)ueid));
|
|
|
|
if (key_record == NULL) {
|
|
|
|
/* Create and add to table */
|
|
|
|
key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t);
|
|
|
|
key_record->ueid = ueid;
|
|
|
|
wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record);
|
|
|
|
}
|
|
|
|
|
2021-01-12 22:21:49 +00:00
|
|
|
/* Check and convert RRC ciphering key */
|
2020-12-29 13:55:35 +00:00
|
|
|
key_record->rrcCipherKeyString = g_strdup(key);
|
2021-01-09 00:30:00 +00:00
|
|
|
update_key_from_string(key_record->rrcCipherKeyString, key_record->rrcCipherBinaryKey, &key_record->rrcCipherKeyOK, &err);
|
2021-01-01 15:51:00 +00:00
|
|
|
if (err) {
|
2021-01-09 00:30:00 +00:00
|
|
|
report_failure("%s: (RRC Ciphering Key)", err);
|
|
|
|
g_free(err);
|
2021-01-01 15:51:00 +00:00
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void set_pdcp_nr_rrc_integrity_key(guint16 ueid, const char *key)
|
|
|
|
{
|
2021-01-09 00:30:00 +00:00
|
|
|
char *err = NULL;
|
2021-01-01 15:51:00 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Get or create struct for this UE */
|
|
|
|
uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash,
|
|
|
|
GUINT_TO_POINTER((guint)ueid));
|
|
|
|
if (key_record == NULL) {
|
|
|
|
/* Create and add to table */
|
|
|
|
key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t);
|
|
|
|
key_record->ueid = ueid;
|
|
|
|
wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check and convert RRC integrity key */
|
|
|
|
key_record->rrcIntegrityKeyString = g_strdup(key);
|
2021-01-09 00:30:00 +00:00
|
|
|
update_key_from_string(key_record->rrcIntegrityKeyString, key_record->rrcIntegrityBinaryKey, &key_record->rrcIntegrityKeyOK, &err);
|
2021-01-01 15:51:00 +00:00
|
|
|
if (err) {
|
2021-01-09 00:30:00 +00:00
|
|
|
report_failure("%s: (RRC Integrity Key)", err);
|
|
|
|
g_free(err);
|
2021-01-01 15:51:00 +00:00
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void set_pdcp_nr_up_ciphering_key(guint16 ueid, const char *key)
|
|
|
|
{
|
2021-01-09 00:30:00 +00:00
|
|
|
char *err = NULL;
|
2021-01-01 15:51:00 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Get or create struct for this UE */
|
|
|
|
uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash,
|
|
|
|
GUINT_TO_POINTER((guint)ueid));
|
|
|
|
if (key_record == NULL) {
|
|
|
|
/* Create and add to table */
|
|
|
|
key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t);
|
|
|
|
key_record->ueid = ueid;
|
|
|
|
wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record);
|
|
|
|
}
|
|
|
|
|
2021-01-12 22:21:49 +00:00
|
|
|
/* Check and convert ciphering UP key */
|
2020-12-29 13:55:35 +00:00
|
|
|
key_record->upCipherKeyString = g_strdup(key);
|
2021-01-09 00:30:00 +00:00
|
|
|
update_key_from_string(key_record->upCipherKeyString, key_record->upCipherBinaryKey, &key_record->upCipherKeyOK, &err);
|
2021-01-01 15:51:00 +00:00
|
|
|
if (err) {
|
2021-01-09 00:30:00 +00:00
|
|
|
report_failure("%s: (UserPlane Ciphering Key)", err);
|
|
|
|
g_free(err);
|
2021-01-01 15:51:00 +00:00
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void set_pdcp_nr_up_integrity_key(guint16 ueid, const char *key)
|
|
|
|
{
|
2021-01-09 00:30:00 +00:00
|
|
|
char *err = NULL;
|
2021-01-01 15:51:00 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Get or create struct for this UE */
|
|
|
|
uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash,
|
|
|
|
GUINT_TO_POINTER((guint)ueid));
|
|
|
|
if (key_record == NULL) {
|
|
|
|
/* Create and add to table */
|
|
|
|
key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t);
|
|
|
|
key_record->ueid = ueid;
|
|
|
|
wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record);
|
|
|
|
}
|
|
|
|
|
2021-01-01 22:19:28 +00:00
|
|
|
/* Check and convert UP integrity key */
|
2020-12-29 13:55:35 +00:00
|
|
|
key_record->upIntegrityKeyString = g_strdup(key);
|
2021-01-09 00:30:00 +00:00
|
|
|
update_key_from_string(key_record->upIntegrityKeyString, key_record->upIntegrityBinaryKey, &key_record->upIntegrityKeyOK, &err);
|
2021-01-01 15:51:00 +00:00
|
|
|
if (err) {
|
2021-01-09 00:30:00 +00:00
|
|
|
report_failure("%s: (UserPlane Integrity Key)", err);
|
|
|
|
g_free(err);
|
2021-01-01 15:51:00 +00:00
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
static const value_string direction_vals[] =
|
|
|
|
{
|
2018-06-04 20:17:52 +00:00
|
|
|
{ PDCP_NR_DIRECTION_UPLINK, "Uplink"},
|
|
|
|
{ PDCP_NR_DIRECTION_DOWNLINK, "Downlink"},
|
2018-04-05 12:04:06 +00:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const value_string pdcp_plane_vals[] = {
|
|
|
|
{ NR_SIGNALING_PLANE, "Signalling" },
|
|
|
|
{ NR_USER_PLANE, "User" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string bearer_type_vals[] = {
|
|
|
|
{ Bearer_DCCH, "DCCH"},
|
|
|
|
{ Bearer_BCCH_BCH, "BCCH_BCH"},
|
|
|
|
{ Bearer_BCCH_DL_SCH, "BCCH_DL_SCH"},
|
|
|
|
{ Bearer_CCCH, "CCCH"},
|
|
|
|
{ Bearer_PCCH, "PCCH"},
|
|
|
|
{ 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string rohc_mode_vals[] = {
|
|
|
|
{ UNIDIRECTIONAL, "Unidirectional" },
|
|
|
|
{ OPTIMISTIC_BIDIRECTIONAL, "Optimistic Bidirectional" },
|
|
|
|
{ RELIABLE_BIDIRECTIONAL, "Reliable Bidirectional" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Entries taken from Table 5.7.1-1.
|
|
|
|
Descriptions from http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.txt */
|
|
|
|
static const value_string rohc_profile_vals[] = {
|
|
|
|
{ 0x0000, "ROHC uncompressed" }, /* [RFC5795] */
|
|
|
|
{ 0x0001, "ROHC RTP" }, /* [RFC3095] */
|
|
|
|
{ 0x0002, "ROHC UDP" }, /* [RFC3095] */
|
|
|
|
{ 0x0003, "ROHC ESP" }, /* [RFC3095] */
|
|
|
|
{ 0x0004, "ROHC IP" }, /* [RFC3843] */
|
|
|
|
{ 0x0006, "ROHC TCP" }, /* [RFC4996] */
|
|
|
|
|
|
|
|
{ 0x0101, "ROHCv2 RTP" }, /* [RFC5225] */
|
|
|
|
{ 0x0102, "ROHCv2 UDP" }, /* [RFC5225] */
|
|
|
|
{ 0x0103, "ROHCv2 ESP" }, /* [RFC5225] */
|
|
|
|
{ 0x0104, "ROHCv2 IP" }, /* [RFC5225] */
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const true_false_string pdu_type_bit = {
|
|
|
|
"Data PDU",
|
|
|
|
"Control PDU"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const value_string control_pdu_type_vals[] = {
|
|
|
|
{ 0, "PDCP status report" },
|
|
|
|
{ 1, "Interspersed ROHC feedback packet" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string integrity_algorithm_vals[] = {
|
2021-01-01 22:19:28 +00:00
|
|
|
{ nia0, "NIA0 (NULL)" },
|
|
|
|
{ nia1, "NIA1 (SNOW3G)" },
|
|
|
|
{ nia2, "NIA2 (AES)" },
|
|
|
|
{ nia3, "NIA3 (ZUC)" },
|
2018-04-05 12:04:06 +00:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string ciphering_algorithm_vals[] = {
|
2021-01-01 22:19:28 +00:00
|
|
|
{ nea0, "NEA0 (NULL)" },
|
|
|
|
{ nea1, "NEA1 (SNOW3G)" },
|
|
|
|
{ nea2, "NEA2 (AES)" },
|
|
|
|
{ nea3, "NEA3 (ZUC)" },
|
|
|
|
{ nea_disabled, "Ciphering disabled" },
|
2018-04-05 12:04:06 +00:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-06-04 20:17:52 +00:00
|
|
|
/* SDAP header fields and tree */
|
|
|
|
static int proto_sdap = -1;
|
|
|
|
static int hf_sdap_rdi = -1;
|
|
|
|
static int hf_sdap_rqi = -1;
|
|
|
|
static int hf_sdap_qfi = -1;
|
2018-06-20 12:30:51 +00:00
|
|
|
static int hf_sdap_data_control = -1;
|
2018-06-04 20:17:52 +00:00
|
|
|
static int hf_sdap_reserved = -1;
|
|
|
|
static gint ett_sdap = -1;
|
|
|
|
|
|
|
|
static const true_false_string sdap_rdi = {
|
|
|
|
"To store QoS flow to DRB mapping rule",
|
|
|
|
"No action"
|
|
|
|
};
|
|
|
|
|
|
|
|
static const true_false_string sdap_rqi = {
|
|
|
|
"To inform NAS that RQI bit is set to 1",
|
|
|
|
"No action"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
static dissector_handle_t ip_handle;
|
|
|
|
static dissector_handle_t ipv6_handle;
|
|
|
|
static dissector_handle_t rohc_handle;
|
2020-02-01 09:36:41 +00:00
|
|
|
static dissector_handle_t nr_rrc_ul_ccch;
|
|
|
|
static dissector_handle_t nr_rrc_ul_ccch1;
|
|
|
|
static dissector_handle_t nr_rrc_dl_ccch;
|
|
|
|
static dissector_handle_t nr_rrc_pcch;
|
|
|
|
static dissector_handle_t nr_rrc_bcch_bch;
|
|
|
|
static dissector_handle_t nr_rrc_bcch_dl_sch;
|
|
|
|
static dissector_handle_t nr_rrc_ul_dcch;
|
|
|
|
static dissector_handle_t nr_rrc_dl_dcch;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
#define SEQUENCE_ANALYSIS_RLC_ONLY 1
|
|
|
|
#define SEQUENCE_ANALYSIS_PDCP_ONLY 2
|
|
|
|
|
|
|
|
/* Preference variables */
|
|
|
|
static gboolean global_pdcp_dissect_user_plane_as_ip = TRUE;
|
|
|
|
static gboolean global_pdcp_dissect_signalling_plane_as_rrc = TRUE;
|
|
|
|
static gint global_pdcp_check_sequence_numbers = TRUE;
|
|
|
|
static gboolean global_pdcp_dissect_rohc = FALSE;
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Preference settings for deciphering and integrity checking. */
|
|
|
|
static gboolean global_pdcp_decipher_signalling = TRUE;
|
|
|
|
static gboolean global_pdcp_decipher_userplane = FALSE; /* Can be slow, so default to FALSE */
|
|
|
|
static gboolean global_pdcp_check_integrity = TRUE;
|
|
|
|
static gboolean global_pdcp_ignore_sec = FALSE; /* Ignore Set Security Algo calls */
|
|
|
|
|
|
|
|
/* Use these values where we know the keys but may have missed the algorithm,
|
|
|
|
e.g. when handing over and RRCReconfigurationRequest goes to target cell only */
|
|
|
|
static enum nr_security_ciphering_algorithm_e global_default_ciphering_algorithm = nea0;
|
|
|
|
static enum nr_security_integrity_algorithm_e global_default_integrity_algorithm = nia0;
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Which layer info to show in the info column */
|
|
|
|
enum layer_to_show {
|
|
|
|
ShowRLCLayer, ShowPDCPLayer, ShowTrafficLayer
|
|
|
|
};
|
|
|
|
static gint global_pdcp_nr_layer_to_show = (gint)ShowRLCLayer;
|
|
|
|
|
|
|
|
|
2018-11-10 21:53:16 +00:00
|
|
|
/* Function to be called from outside this module (e.g. in a plugin) to get per-packet data */
|
|
|
|
pdcp_nr_info *get_pdcp_nr_proto_data(packet_info *pinfo)
|
|
|
|
{
|
|
|
|
return (pdcp_nr_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_pdcp_nr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to be called from outside this module (e.g. in a plugin) to set per-packet data */
|
|
|
|
void set_pdcp_nr_proto_data(packet_info *pinfo, pdcp_nr_info *p_pdcp_nr_info)
|
|
|
|
{
|
|
|
|
p_add_proto_data(wmem_file_scope(), pinfo, proto_pdcp_nr, 0, p_pdcp_nr_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/**************************************************/
|
|
|
|
/* Sequence number analysis */
|
|
|
|
|
|
|
|
/* Bearer key */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
/* Using bit fields to fit into 32 bits, so avoiding the need to allocate
|
|
|
|
heap memory for these structs */
|
|
|
|
guint ueId : 16;
|
|
|
|
guint plane : 2;
|
|
|
|
guint bearerId : 6;
|
|
|
|
guint direction : 1;
|
|
|
|
guint notUsed : 7;
|
|
|
|
} pdcp_bearer_hash_key;
|
|
|
|
|
|
|
|
/* Bearer state */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
guint32 previousSequenceNumber;
|
|
|
|
guint32 previousFrameNum;
|
|
|
|
guint32 hfn;
|
|
|
|
} pdcp_bearer_status;
|
|
|
|
|
|
|
|
/* The sequence analysis bearer hash table.
|
|
|
|
Maps key -> status */
|
|
|
|
static wmem_map_t *pdcp_sequence_analysis_bearer_hash = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Hash table types & functions for frame reports */
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
guint32 frameNumber;
|
|
|
|
guint32 SN : 18;
|
|
|
|
guint32 plane : 2;
|
|
|
|
guint32 bearerId: 5;
|
|
|
|
guint32 direction: 1;
|
|
|
|
guint32 notUsed : 6;
|
|
|
|
} pdcp_result_hash_key;
|
|
|
|
|
|
|
|
static gint pdcp_result_hash_equal(gconstpointer v, gconstpointer v2)
|
|
|
|
{
|
|
|
|
const pdcp_result_hash_key* val1 = (const pdcp_result_hash_key *)v;
|
|
|
|
const pdcp_result_hash_key* val2 = (const pdcp_result_hash_key *)v2;
|
|
|
|
|
|
|
|
/* All fields must match */
|
|
|
|
return (memcmp(val1, val2, sizeof(pdcp_result_hash_key)) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute a hash value for a given key. */
|
|
|
|
static guint pdcp_result_hash_func(gconstpointer v)
|
|
|
|
{
|
|
|
|
const pdcp_result_hash_key* val1 = (const pdcp_result_hash_key *)v;
|
|
|
|
|
|
|
|
/* TODO: This is a bit random. */
|
|
|
|
return val1->frameNumber + (val1->bearerId<<7) +
|
|
|
|
(val1->plane<<12) +
|
|
|
|
(val1->SN<<14) +
|
|
|
|
(val1->direction<<6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pdcp_bearer_hash_key fits into the pointer, so just copy the value into
|
|
|
|
a guint, cast to a pointer and return that as the key */
|
|
|
|
static gpointer get_bearer_hash_key(pdcp_bearer_hash_key *key)
|
|
|
|
{
|
|
|
|
guint asInt = 0;
|
|
|
|
/* TODO: assert that sizeof(pdcp_bearer_hash_key) <= sizeof(guint) ? */
|
|
|
|
memcpy(&asInt, key, sizeof(pdcp_bearer_hash_key));
|
|
|
|
return GUINT_TO_POINTER(asInt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convenience function to get a pointer for the hash_func to work with */
|
|
|
|
static gpointer get_report_hash_key(guint32 SN, guint32 frameNumber,
|
|
|
|
pdcp_nr_info *p_pdcp_nr_info,
|
|
|
|
gboolean do_persist)
|
|
|
|
{
|
|
|
|
static pdcp_result_hash_key key;
|
|
|
|
pdcp_result_hash_key *p_key;
|
|
|
|
|
|
|
|
/* Only allocate a struct when will be adding entry */
|
|
|
|
if (do_persist) {
|
|
|
|
p_key = wmem_new(wmem_file_scope(), pdcp_result_hash_key);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
memset(&key, 0, sizeof(pdcp_result_hash_key));
|
|
|
|
p_key = &key;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill in details, and return pointer */
|
|
|
|
p_key->frameNumber = frameNumber;
|
|
|
|
p_key->SN = SN;
|
|
|
|
p_key->plane = (guint8)p_pdcp_nr_info->plane;
|
|
|
|
p_key->bearerId = p_pdcp_nr_info->bearerId;
|
|
|
|
p_key->direction = p_pdcp_nr_info->direction;
|
|
|
|
p_key->notUsed = 0;
|
|
|
|
|
|
|
|
return p_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Info to attach to frame when first read, recording what to show about sequence */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
SN_OK, SN_Repeated, SN_MAC_Retx, SN_Retx, SN_Missing
|
|
|
|
} sequence_state;
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gboolean sequenceExpectedCorrect;
|
|
|
|
guint32 sequenceExpected;
|
|
|
|
guint32 previousFrameNum;
|
|
|
|
guint32 nextFrameNum;
|
|
|
|
|
|
|
|
guint32 firstSN;
|
|
|
|
guint32 lastSN;
|
|
|
|
guint32 hfn;
|
|
|
|
|
|
|
|
sequence_state state;
|
|
|
|
} pdcp_sequence_report_in_frame;
|
|
|
|
|
|
|
|
/* The sequence analysis frame report hash table.
|
|
|
|
Maps pdcp_result_hash_key* -> pdcp_sequence_report_in_frame* */
|
|
|
|
static wmem_map_t *pdcp_nr_sequence_analysis_report_hash = NULL;
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Gather together security settings in order to be able to do deciphering */
|
|
|
|
typedef struct pdu_security_settings_t
|
|
|
|
{
|
|
|
|
enum nr_security_ciphering_algorithm_e ciphering;
|
|
|
|
enum nr_security_integrity_algorithm_e integrity;
|
|
|
|
guint8* cipherKey;
|
|
|
|
guint8* integrityKey;
|
|
|
|
gboolean cipherKeyValid;
|
|
|
|
gboolean integrityKeyValid;
|
|
|
|
guint32 count;
|
|
|
|
guint8 bearer;
|
|
|
|
guint8 direction;
|
|
|
|
} pdu_security_settings_t;
|
|
|
|
|
|
|
|
static uat_ue_keys_record_t* look_up_keys_record(guint16 ueid)
|
|
|
|
{
|
|
|
|
unsigned int record_id;
|
|
|
|
|
|
|
|
/* Try hash table first (among entries added by set_pdcp_nr_xxx_key() functions) */
|
|
|
|
uat_ue_keys_record_t* key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash,
|
|
|
|
GUINT_TO_POINTER((guint)ueid));
|
|
|
|
if (key_record != NULL) {
|
|
|
|
return key_record;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Else look up UAT entries. N.B. linear search... */
|
|
|
|
for (record_id=0; record_id < num_ue_keys_uat; record_id++) {
|
|
|
|
if (uat_ue_keys_records[record_id].ueid == ueid) {
|
|
|
|
return &uat_ue_keys_records[record_id];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No match at all - return NULL */
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Add to the tree values associated with sequence analysis for this frame */
|
|
|
|
static void addBearerSequenceInfo(pdcp_sequence_report_in_frame *p,
|
2020-12-29 13:55:35 +00:00
|
|
|
pdcp_nr_info *p_pdcp_nr_info,
|
|
|
|
guint32 sequenceNumber,
|
|
|
|
packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb,
|
|
|
|
proto_tree *security_tree,
|
|
|
|
pdu_security_settings_t *pdu_security)
|
2018-04-05 12:04:06 +00:00
|
|
|
{
|
|
|
|
proto_tree *seqnum_tree;
|
|
|
|
proto_item *seqnum_ti;
|
|
|
|
proto_item *ti_expected_sn;
|
|
|
|
proto_item *ti;
|
2020-12-29 13:55:35 +00:00
|
|
|
uat_ue_keys_record_t *keys_record;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Create subtree */
|
|
|
|
seqnum_ti = proto_tree_add_string_format(tree,
|
|
|
|
hf_pdcp_nr_sequence_analysis,
|
|
|
|
tvb, 0, 0,
|
|
|
|
"", "Sequence Analysis");
|
|
|
|
seqnum_tree = proto_item_add_subtree(seqnum_ti,
|
|
|
|
ett_pdcp_nr_sequence_analysis);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(seqnum_ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Previous bearer frame */
|
|
|
|
if (p->previousFrameNum != 0) {
|
|
|
|
proto_tree_add_uint(seqnum_tree, hf_pdcp_nr_sequence_analysis_previous_frame,
|
|
|
|
tvb, 0, 0, p->previousFrameNum);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Expected sequence number */
|
|
|
|
ti_expected_sn = proto_tree_add_uint(seqnum_tree, hf_pdcp_nr_sequence_analysis_expected_sn,
|
|
|
|
tvb, 0, 0, p->sequenceExpected);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti_expected_sn);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Make sure we have recognised SN length */
|
|
|
|
switch (p_pdcp_nr_info->seqnum_length) {
|
|
|
|
case PDCP_NR_SN_LENGTH_12_BITS:
|
|
|
|
case PDCP_NR_SN_LENGTH_18_BITS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DISSECTOR_ASSERT_NOT_REACHED();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (p->state) {
|
|
|
|
case SN_OK:
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_hidden(ti_expected_sn);
|
2018-04-05 12:04:06 +00:00
|
|
|
ti = proto_tree_add_boolean(seqnum_tree, hf_pdcp_nr_sequence_analysis_ok,
|
|
|
|
tvb, 0, 0, TRUE);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
proto_item_append_text(seqnum_ti, " - OK");
|
|
|
|
|
|
|
|
/* Link to next SN in bearer (if known) */
|
|
|
|
if (p->nextFrameNum != 0) {
|
|
|
|
proto_tree_add_uint(seqnum_tree, hf_pdcp_nr_sequence_analysis_next_frame,
|
|
|
|
tvb, 0, 0, p->nextFrameNum);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SN_Missing:
|
|
|
|
ti = proto_tree_add_boolean(seqnum_tree, hf_pdcp_nr_sequence_analysis_ok,
|
|
|
|
tvb, 0, 0, FALSE);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
ti = proto_tree_add_boolean(seqnum_tree, hf_pdcp_nr_sequence_analysis_skipped,
|
|
|
|
tvb, 0, 0, TRUE);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
if (p->lastSN != p->firstSN) {
|
|
|
|
expert_add_info_format(pinfo, ti, &ei_pdcp_nr_sequence_analysis_sn_missing,
|
|
|
|
"PDCP SNs (%u to %u) missing for %s on UE %u (%s-%u)",
|
|
|
|
p->firstSN, p->lastSN,
|
|
|
|
val_to_str_const(p_pdcp_nr_info->direction, direction_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->ueid,
|
|
|
|
val_to_str_const(p_pdcp_nr_info->bearerType, bearer_type_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->bearerId);
|
|
|
|
proto_item_append_text(seqnum_ti, " - SNs missing (%u to %u)",
|
|
|
|
p->firstSN, p->lastSN);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
expert_add_info_format(pinfo, ti, &ei_pdcp_nr_sequence_analysis_sn_missing,
|
|
|
|
"PDCP SN (%u) missing for %s on UE %u (%s-%u)",
|
|
|
|
p->firstSN,
|
|
|
|
val_to_str_const(p_pdcp_nr_info->direction, direction_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->ueid,
|
|
|
|
val_to_str_const(p_pdcp_nr_info->bearerType, bearer_type_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->bearerId);
|
|
|
|
proto_item_append_text(seqnum_ti, " - SN missing (%u)",
|
|
|
|
p->firstSN);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SN_Repeated:
|
|
|
|
ti = proto_tree_add_boolean(seqnum_tree, hf_pdcp_nr_sequence_analysis_ok,
|
|
|
|
tvb, 0, 0, FALSE);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
ti = proto_tree_add_boolean(seqnum_tree, hf_pdcp_nr_sequence_analysis_repeated,
|
|
|
|
tvb, 0, 0, TRUE);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
expert_add_info_format(pinfo, ti, &ei_pdcp_nr_sequence_analysis_sn_repeated,
|
|
|
|
"PDCP SN (%u) repeated for %s for UE %u (%s-%u)",
|
|
|
|
p->firstSN,
|
|
|
|
val_to_str_const(p_pdcp_nr_info->direction, direction_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->ueid,
|
|
|
|
val_to_str_const(p_pdcp_nr_info->bearerType, bearer_type_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->bearerId);
|
|
|
|
proto_item_append_text(seqnum_ti, "- SN %u Repeated",
|
|
|
|
p->firstSN);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Incorrect sequence number */
|
|
|
|
expert_add_info_format(pinfo, ti_expected_sn, &ei_pdcp_nr_sequence_analysis_wrong_sequence_number,
|
|
|
|
"Wrong Sequence Number for %s on UE %u (%s-%u) - got %u, expected %u",
|
|
|
|
val_to_str_const(p_pdcp_nr_info->direction, direction_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->ueid,
|
|
|
|
val_to_str_const(p_pdcp_nr_info->bearerType, bearer_type_vals, "Unknown"),
|
|
|
|
p_pdcp_nr_info->bearerId,
|
|
|
|
sequenceNumber, p->sequenceExpected);
|
|
|
|
break;
|
|
|
|
}
|
2021-02-28 18:09:54 +00:00
|
|
|
|
|
|
|
/* May also be able to add key inputs to security tree here */
|
|
|
|
if ((pdu_security->ciphering != nea0) ||
|
|
|
|
(pdu_security->integrity != nia0)) {
|
|
|
|
guint32 hfn_multiplier;
|
|
|
|
guint32 count;
|
|
|
|
gchar *cipher_key = NULL;
|
|
|
|
gchar *integrity_key = NULL;
|
|
|
|
|
|
|
|
/* BEARER */
|
|
|
|
ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_bearer,
|
|
|
|
tvb, 0, 0, p_pdcp_nr_info->bearerId-1);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
pdu_security->bearer = p_pdcp_nr_info->bearerId-1;
|
|
|
|
|
|
|
|
/* DIRECTION */
|
|
|
|
ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_direction,
|
|
|
|
tvb, 0, 0, p_pdcp_nr_info->direction);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
|
|
|
|
/* COUNT (HFN * snLength^2 + SN) */
|
|
|
|
switch (p_pdcp_nr_info->seqnum_length) {
|
|
|
|
case PDCP_NR_SN_LENGTH_12_BITS:
|
|
|
|
hfn_multiplier = 4096;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_SN_LENGTH_18_BITS:
|
|
|
|
hfn_multiplier = 262144;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DISSECTOR_ASSERT_NOT_REACHED();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
count = (p->hfn * hfn_multiplier) + sequenceNumber;
|
|
|
|
ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_count,
|
|
|
|
tvb, 0, 0, count);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
pdu_security->count = count;
|
|
|
|
|
|
|
|
/* KEY. Look this UE up among UEs that have keys configured */
|
|
|
|
keys_record = look_up_keys_record(p_pdcp_nr_info->ueid);
|
|
|
|
if (keys_record != NULL) {
|
|
|
|
if (p_pdcp_nr_info->plane == NR_SIGNALING_PLANE) {
|
|
|
|
/* Get RRC ciphering key */
|
|
|
|
if (keys_record->rrcCipherKeyOK) {
|
|
|
|
cipher_key = keys_record->rrcCipherKeyString;
|
|
|
|
pdu_security->cipherKey = &(keys_record->rrcCipherBinaryKey[0]);
|
|
|
|
pdu_security->cipherKeyValid = TRUE;
|
|
|
|
}
|
|
|
|
/* Get RRC integrity key */
|
|
|
|
if (keys_record->rrcIntegrityKeyOK) {
|
|
|
|
integrity_key = keys_record->rrcIntegrityKeyString;
|
|
|
|
pdu_security->integrityKey = &(keys_record->rrcIntegrityBinaryKey[0]);
|
|
|
|
pdu_security->integrityKeyValid = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Get userplane ciphering key */
|
|
|
|
if (keys_record->upCipherKeyOK) {
|
|
|
|
cipher_key = keys_record->upCipherKeyString;
|
|
|
|
pdu_security->cipherKey = &(keys_record->upCipherBinaryKey[0]);
|
|
|
|
pdu_security->cipherKeyValid = TRUE;
|
|
|
|
}
|
|
|
|
/* Get userplane integrity key */
|
|
|
|
if (keys_record->upIntegrityKeyOK) {
|
|
|
|
integrity_key = keys_record->upIntegrityKeyString;
|
|
|
|
pdu_security->integrityKey = &(keys_record->upIntegrityBinaryKey[0]);
|
|
|
|
pdu_security->integrityKeyValid = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Show keys where known and valid */
|
|
|
|
if (cipher_key != NULL) {
|
|
|
|
ti = proto_tree_add_string(security_tree, hf_pdcp_nr_security_cipher_key,
|
|
|
|
tvb, 0, 0, cipher_key);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
}
|
|
|
|
if (integrity_key != NULL) {
|
|
|
|
ti = proto_tree_add_string(security_tree, hf_pdcp_nr_security_integrity_key,
|
|
|
|
tvb, 0, 0, integrity_key);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
}
|
|
|
|
|
|
|
|
pdu_security->direction = p_pdcp_nr_info->direction;
|
|
|
|
}
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Update the bearer status and set report for this frame */
|
|
|
|
static void checkBearerSequenceInfo(packet_info *pinfo, tvbuff_t *tvb,
|
2020-12-29 13:55:35 +00:00
|
|
|
pdcp_nr_info *p_pdcp_nr_info,
|
|
|
|
guint32 sequenceNumber,
|
|
|
|
proto_tree *tree,
|
|
|
|
proto_tree *security_tree,
|
|
|
|
pdu_security_settings_t *pdu_security)
|
2018-04-05 12:04:06 +00:00
|
|
|
{
|
|
|
|
pdcp_bearer_hash_key bearer_key;
|
|
|
|
pdcp_bearer_status *p_bearer_status;
|
|
|
|
pdcp_sequence_report_in_frame *p_report_in_frame = NULL;
|
|
|
|
gboolean createdBearer = FALSE;
|
|
|
|
guint32 expectedSequenceNumber = 0;
|
|
|
|
guint32 snLimit = 0;
|
|
|
|
|
|
|
|
/* If find stat_report_in_frame already, use that and get out */
|
2020-12-29 13:55:35 +00:00
|
|
|
if (PINFO_FD_VISITED(pinfo)) {
|
2018-04-05 12:04:06 +00:00
|
|
|
p_report_in_frame =
|
|
|
|
(pdcp_sequence_report_in_frame*)wmem_map_lookup(pdcp_nr_sequence_analysis_report_hash,
|
|
|
|
get_report_hash_key(sequenceNumber,
|
|
|
|
pinfo->num,
|
|
|
|
p_pdcp_nr_info, FALSE));
|
|
|
|
if (p_report_in_frame != NULL) {
|
|
|
|
addBearerSequenceInfo(p_report_in_frame, p_pdcp_nr_info,
|
|
|
|
sequenceNumber,
|
2020-12-29 13:55:35 +00:00
|
|
|
pinfo, tree, tvb, security_tree, pdu_security);
|
2018-04-05 12:04:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Give up - we must have tried already... */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************/
|
|
|
|
/* Create or find an entry for this bearer state */
|
|
|
|
bearer_key.ueId = p_pdcp_nr_info->ueid;
|
|
|
|
bearer_key.plane = p_pdcp_nr_info->plane;
|
|
|
|
bearer_key.bearerId = p_pdcp_nr_info->bearerId;
|
|
|
|
bearer_key.direction = p_pdcp_nr_info->direction;
|
|
|
|
bearer_key.notUsed = 0;
|
|
|
|
|
|
|
|
/* Do the table lookup */
|
|
|
|
p_bearer_status = (pdcp_bearer_status*)wmem_map_lookup(pdcp_sequence_analysis_bearer_hash,
|
|
|
|
get_bearer_hash_key(&bearer_key));
|
|
|
|
|
|
|
|
/* Create table entry if necessary */
|
|
|
|
if (p_bearer_status == NULL) {
|
|
|
|
createdBearer = TRUE;
|
|
|
|
|
|
|
|
/* Allocate a new value and duplicate key contents */
|
|
|
|
p_bearer_status = wmem_new0(wmem_file_scope(), pdcp_bearer_status);
|
|
|
|
|
|
|
|
/* Add entry */
|
|
|
|
wmem_map_insert(pdcp_sequence_analysis_bearer_hash,
|
|
|
|
get_bearer_hash_key(&bearer_key), p_bearer_status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create space for frame state_report */
|
|
|
|
p_report_in_frame = wmem_new(wmem_file_scope(), pdcp_sequence_report_in_frame);
|
|
|
|
p_report_in_frame->nextFrameNum = 0;
|
|
|
|
|
|
|
|
switch (p_pdcp_nr_info->seqnum_length) {
|
|
|
|
case PDCP_NR_SN_LENGTH_12_BITS:
|
|
|
|
snLimit = 4096;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_SN_LENGTH_18_BITS:
|
|
|
|
snLimit = 262144;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DISSECTOR_ASSERT_NOT_REACHED();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Work out expected sequence number */
|
|
|
|
if (!createdBearer) {
|
|
|
|
expectedSequenceNumber = (p_bearer_status->previousSequenceNumber + 1) % snLimit;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
expectedSequenceNumber = sequenceNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set report for this frame */
|
|
|
|
/* For PDCP, sequence number is always expectedSequence number */
|
|
|
|
p_report_in_frame->sequenceExpectedCorrect = (sequenceNumber == expectedSequenceNumber);
|
|
|
|
p_report_in_frame->hfn = p_bearer_status->hfn;
|
|
|
|
|
2021-02-28 18:09:54 +00:00
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* For wrong sequence number... */
|
|
|
|
if (!p_report_in_frame->sequenceExpectedCorrect) {
|
|
|
|
|
|
|
|
/* Frames are not missing if we get an earlier sequence number again */
|
|
|
|
if (((snLimit + expectedSequenceNumber - sequenceNumber) % snLimit) > 15) {
|
|
|
|
p_report_in_frame->state = SN_Missing;
|
|
|
|
p_report_in_frame->firstSN = expectedSequenceNumber;
|
|
|
|
p_report_in_frame->lastSN = (snLimit + sequenceNumber - 1) % snLimit;
|
|
|
|
|
|
|
|
p_report_in_frame->sequenceExpected = expectedSequenceNumber;
|
|
|
|
p_report_in_frame->previousFrameNum = p_bearer_status->previousFrameNum;
|
|
|
|
|
|
|
|
/* Update Bearer status to remember *this* frame */
|
|
|
|
p_bearer_status->previousFrameNum = pinfo->num;
|
|
|
|
p_bearer_status->previousSequenceNumber = sequenceNumber;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* An SN has been repeated */
|
|
|
|
p_report_in_frame->state = SN_Repeated;
|
|
|
|
p_report_in_frame->firstSN = sequenceNumber;
|
|
|
|
|
|
|
|
p_report_in_frame->sequenceExpected = expectedSequenceNumber;
|
|
|
|
p_report_in_frame->previousFrameNum = p_bearer_status->previousFrameNum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* SN was OK */
|
|
|
|
p_report_in_frame->state = SN_OK;
|
|
|
|
p_report_in_frame->sequenceExpected = expectedSequenceNumber;
|
|
|
|
p_report_in_frame->previousFrameNum = p_bearer_status->previousFrameNum;
|
|
|
|
/* SN has rolled around, inc hfn! */
|
|
|
|
if (!createdBearer && (sequenceNumber == 0)) {
|
2021-02-28 18:09:54 +00:00
|
|
|
/* Should handover before HFN needs to wrap, so don't worry about it */
|
2018-04-05 12:04:06 +00:00
|
|
|
p_bearer_status->hfn++;
|
|
|
|
p_report_in_frame->hfn = p_bearer_status->hfn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update Bearer status to remember *this* frame */
|
|
|
|
p_bearer_status->previousFrameNum = pinfo->num;
|
|
|
|
p_bearer_status->previousSequenceNumber = sequenceNumber;
|
|
|
|
|
|
|
|
if (p_report_in_frame->previousFrameNum != 0) {
|
|
|
|
/* Get report for previous frame */
|
|
|
|
pdcp_sequence_report_in_frame *p_previous_report;
|
|
|
|
p_previous_report = (pdcp_sequence_report_in_frame*)wmem_map_lookup(pdcp_nr_sequence_analysis_report_hash,
|
|
|
|
get_report_hash_key((sequenceNumber+262144) % 262144,
|
|
|
|
p_report_in_frame->previousFrameNum,
|
|
|
|
p_pdcp_nr_info,
|
|
|
|
FALSE));
|
|
|
|
/* It really shouldn't be NULL... */
|
|
|
|
if (p_previous_report != NULL) {
|
|
|
|
/* Point it forward to this one */
|
|
|
|
p_previous_report->nextFrameNum = pinfo->num;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Associate with this frame number */
|
|
|
|
wmem_map_insert(pdcp_nr_sequence_analysis_report_hash,
|
|
|
|
get_report_hash_key(sequenceNumber, pinfo->num,
|
|
|
|
p_pdcp_nr_info, TRUE),
|
|
|
|
p_report_in_frame);
|
|
|
|
|
|
|
|
/* Add state report for this frame into tree */
|
|
|
|
addBearerSequenceInfo(p_report_in_frame, p_pdcp_nr_info, sequenceNumber,
|
2020-12-29 13:55:35 +00:00
|
|
|
pinfo, tree, tvb, security_tree, pdu_security);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Hash table for security state for a UE
|
|
|
|
Maps UEId -> pdcp_security_info_t* */
|
|
|
|
static wmem_map_t *pdcp_security_hash = NULL;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Result is (ueid, framenum) -> pdcp_security_info_t* */
|
|
|
|
typedef struct ueid_frame_t {
|
|
|
|
guint32 framenum;
|
|
|
|
guint16 ueid;
|
|
|
|
} ueid_frame_t;
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Convenience function to get a pointer for the hash_func to work with */
|
|
|
|
static gpointer get_ueid_frame_hash_key(guint16 ueid, guint32 frameNumber,
|
|
|
|
gboolean do_persist)
|
|
|
|
{
|
|
|
|
static ueid_frame_t key;
|
|
|
|
ueid_frame_t *p_key;
|
|
|
|
|
|
|
|
/* Only allocate a struct when will be adding entry */
|
|
|
|
if (do_persist) {
|
|
|
|
p_key = wmem_new(wmem_file_scope(), ueid_frame_t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Only looking up, so just use static */
|
|
|
|
memset(&key, 0, sizeof(ueid_frame_t));
|
|
|
|
p_key = &key;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill in details, and return pointer */
|
|
|
|
p_key->framenum = frameNumber;
|
|
|
|
p_key->ueid = ueid;
|
|
|
|
|
|
|
|
return p_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint pdcp_nr_ueid_frame_hash_equal(gconstpointer v, gconstpointer v2)
|
|
|
|
{
|
|
|
|
const ueid_frame_t *ueid_frame_1 = (const ueid_frame_t *)v;
|
|
|
|
const ueid_frame_t *ueid_frame_2 = (const ueid_frame_t *)v2;
|
|
|
|
return ((ueid_frame_1->framenum == ueid_frame_2->framenum) &&
|
|
|
|
(ueid_frame_1->ueid == ueid_frame_2->ueid));
|
|
|
|
}
|
|
|
|
static guint pdcp_nr_ueid_frame_hash_func(gconstpointer v)
|
|
|
|
{
|
|
|
|
const ueid_frame_t *ueid_frame = (const ueid_frame_t *)v;
|
|
|
|
return ueid_frame->framenum + 100*ueid_frame->ueid;
|
|
|
|
}
|
|
|
|
static wmem_map_t *pdcp_security_result_hash = NULL;
|
|
|
|
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Write the given formatted text to:
|
|
|
|
- the info column
|
2021-01-12 22:21:49 +00:00
|
|
|
- the top-level PDCP PDU item */
|
2018-04-05 12:04:06 +00:00
|
|
|
static void write_pdu_label_and_info(proto_item *pdu_ti,
|
|
|
|
packet_info *pinfo, const char *format, ...)
|
|
|
|
{
|
|
|
|
#define MAX_INFO_BUFFER 256
|
|
|
|
static char info_buffer[MAX_INFO_BUFFER];
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
g_vsnprintf(info_buffer, MAX_INFO_BUFFER, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
/* Add to indicated places */
|
|
|
|
col_append_str(pinfo->cinfo, COL_INFO, info_buffer);
|
|
|
|
/* TODO: gets called a lot, so a shame there isn't a proto_item_append_string() */
|
|
|
|
proto_item_append_text(pdu_ti, "%s", info_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Show in the tree the config info attached to this frame, as generated fields */
|
|
|
|
static void show_pdcp_config(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree,
|
|
|
|
pdcp_nr_info *p_pdcp_info)
|
|
|
|
{
|
|
|
|
proto_item *ti;
|
|
|
|
proto_tree *configuration_tree;
|
|
|
|
proto_item *configuration_ti = proto_tree_add_item(tree,
|
|
|
|
hf_pdcp_nr_configuration,
|
|
|
|
tvb, 0, 0, ENC_ASCII|ENC_NA);
|
|
|
|
configuration_tree = proto_item_add_subtree(configuration_ti, ett_pdcp_configuration);
|
|
|
|
|
|
|
|
/* Direction */
|
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_direction, tvb, 0, 0,
|
|
|
|
p_pdcp_info->direction);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Plane */
|
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_plane, tvb, 0, 0,
|
|
|
|
p_pdcp_info->plane);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* UEId */
|
|
|
|
if (p_pdcp_info->ueid != 0) {
|
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_ueid, tvb, 0, 0,
|
|
|
|
p_pdcp_info->ueid);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-11-10 21:53:16 +00:00
|
|
|
write_pdu_label_and_info(configuration_ti, pinfo, "UEId=%3u", p_pdcp_info->ueid);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Bearer type */
|
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_bearer_type, tvb, 0, 0,
|
|
|
|
p_pdcp_info->bearerType);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
if (p_pdcp_info->bearerId != 0) {
|
2021-01-12 22:21:49 +00:00
|
|
|
/* Bearer id */
|
2018-04-05 12:04:06 +00:00
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_bearer_id, tvb, 0, 0,
|
|
|
|
p_pdcp_info->bearerId);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
2021-01-12 22:21:49 +00:00
|
|
|
/* Show bearer type in root/Info */
|
2018-11-10 21:53:16 +00:00
|
|
|
if (p_pdcp_info->bearerType == Bearer_DCCH) {
|
|
|
|
write_pdu_label_and_info(configuration_ti, pinfo, " %s-%u ",
|
|
|
|
(p_pdcp_info->plane == NR_SIGNALING_PLANE) ? "SRB" : "DRB",
|
|
|
|
p_pdcp_info->bearerId);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
write_pdu_label_and_info(configuration_ti, pinfo, " %s",
|
|
|
|
val_to_str_const(p_pdcp_info->bearerType, bearer_type_vals, "Unknown"));
|
|
|
|
}
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Seqnum length */
|
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_seqnum_length, tvb, 0, 0,
|
|
|
|
p_pdcp_info->seqnum_length);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
|
|
|
|
/* MAC-I Present */
|
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_maci_present, tvb, 0, 0,
|
|
|
|
p_pdcp_info->maci_present);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
|
2021-02-28 18:09:54 +00:00
|
|
|
/* Ciphering disabled */
|
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_ciphering_disabled, tvb, 0, 0,
|
|
|
|
p_pdcp_info->ciphering_disabled);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
/* Hide unless set */
|
|
|
|
if (!p_pdcp_info->ciphering_disabled) {
|
|
|
|
proto_item_set_hidden(ti);
|
|
|
|
}
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
if (p_pdcp_info->plane == NR_USER_PLANE) {
|
2018-04-11 20:02:46 +00:00
|
|
|
|
2020-09-25 12:47:08 +00:00
|
|
|
/* SDAP */
|
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_sdap, tvb, 0, 0,
|
|
|
|
(p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK) ?
|
|
|
|
p_pdcp_info->sdap_header & PDCP_NR_UL_SDAP_HEADER_PRESENT :
|
|
|
|
p_pdcp_info->sdap_header & PDCP_NR_DL_SDAP_HEADER_PRESENT);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* ROHC compression */
|
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_rohc_compression, tvb, 0, 0,
|
|
|
|
p_pdcp_info->rohc.rohc_compression);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* ROHC-specific settings */
|
|
|
|
if (p_pdcp_info->rohc.rohc_compression) {
|
|
|
|
|
|
|
|
/* Show ROHC mode */
|
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_rohc_mode, tvb, 0, 0,
|
|
|
|
p_pdcp_info->rohc.mode);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Show RND */
|
2018-04-23 14:38:12 +00:00
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_rohc_rnd, tvb, 0, 0,
|
|
|
|
p_pdcp_info->rohc.rnd);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* UDP Checksum */
|
2018-04-23 14:38:12 +00:00
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_rohc_udp_checksum_present, tvb, 0, 0,
|
|
|
|
p_pdcp_info->rohc.udp_checksum_present);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* ROHC profile */
|
|
|
|
ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_rohc_profile, tvb, 0, 0,
|
|
|
|
p_pdcp_info->rohc.profile);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* CID Inclusion Info */
|
2018-04-23 14:38:12 +00:00
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_cid_inclusion_info, tvb, 0, 0,
|
|
|
|
p_pdcp_info->rohc.cid_inclusion_info);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Large CID */
|
2018-04-23 14:38:12 +00:00
|
|
|
ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_large_cid_present, tvb, 0, 0,
|
|
|
|
p_pdcp_info->rohc.large_cid_present);
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-28 18:09:54 +00:00
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Append summary to configuration root */
|
|
|
|
proto_item_append_text(configuration_ti, "(direction=%s, plane=%s",
|
|
|
|
val_to_str_const(p_pdcp_info->direction, direction_vals, "Unknown"),
|
|
|
|
val_to_str_const(p_pdcp_info->plane, pdcp_plane_vals, "Unknown"));
|
|
|
|
|
|
|
|
if (p_pdcp_info->rohc.rohc_compression) {
|
|
|
|
const char *mode = val_to_str_const(p_pdcp_info->rohc.mode, rohc_mode_vals, "Error");
|
|
|
|
proto_item_append_text(configuration_ti, ", mode=%c, profile=%s",
|
|
|
|
mode[0],
|
|
|
|
val_to_str_const(p_pdcp_info->rohc.profile, rohc_profile_vals, "Unknown"));
|
|
|
|
}
|
|
|
|
proto_item_append_text(configuration_ti, ")");
|
2019-04-03 21:32:30 +00:00
|
|
|
proto_item_set_generated(configuration_ti);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Show plane in info column */
|
|
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, " %s: ",
|
|
|
|
val_to_str_const(p_pdcp_info->plane, pdcp_plane_vals, "Unknown"));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Look for an RRC dissector for signalling data (using Bearer type and direction) */
|
2020-02-01 09:36:41 +00:00
|
|
|
static dissector_handle_t lookup_rrc_dissector_handle(struct pdcp_nr_info *p_pdcp_info, guint32 data_length)
|
2018-04-05 12:04:06 +00:00
|
|
|
{
|
2020-02-01 09:36:41 +00:00
|
|
|
dissector_handle_t rrc_handle = NULL;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
switch (p_pdcp_info->bearerType)
|
|
|
|
{
|
|
|
|
case Bearer_CCCH:
|
2020-02-01 09:36:41 +00:00
|
|
|
if (p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK) {
|
|
|
|
rrc_handle = (data_length == 8) ? nr_rrc_ul_ccch1 : nr_rrc_ul_ccch;
|
|
|
|
} else {
|
|
|
|
rrc_handle = nr_rrc_dl_ccch;
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
break;
|
|
|
|
case Bearer_PCCH:
|
2020-02-01 09:36:41 +00:00
|
|
|
rrc_handle = nr_rrc_pcch;
|
2018-04-05 12:04:06 +00:00
|
|
|
break;
|
|
|
|
case Bearer_BCCH_BCH:
|
2020-02-01 09:36:41 +00:00
|
|
|
rrc_handle = nr_rrc_bcch_bch;
|
2018-04-05 12:04:06 +00:00
|
|
|
break;
|
|
|
|
case Bearer_BCCH_DL_SCH:
|
2020-02-01 09:36:41 +00:00
|
|
|
rrc_handle = nr_rrc_bcch_dl_sch;
|
2018-04-05 12:04:06 +00:00
|
|
|
break;
|
|
|
|
case Bearer_DCCH:
|
2020-02-01 09:36:41 +00:00
|
|
|
if (p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK) {
|
|
|
|
rrc_handle = nr_rrc_ul_dcch;
|
|
|
|
} else {
|
|
|
|
rrc_handle = nr_rrc_dl_dcch;
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rrc_handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Called from control protocol to configure security algorithms for the given UE */
|
|
|
|
void set_pdcp_nr_security_algorithms(guint16 ueid, pdcp_nr_security_info_t *security_info)
|
|
|
|
{
|
|
|
|
/* Use for this frame so can check integrity on SecurityCommandRequest frame */
|
|
|
|
/* N.B. won't work for internal, non-RRC signalling methods... */
|
|
|
|
pdcp_nr_security_info_t *p_frame_security;
|
|
|
|
|
|
|
|
/* Disable this entire sub-routine with the Preference */
|
|
|
|
/* Used when the capture is already deciphered */
|
|
|
|
if (global_pdcp_ignore_sec) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create or update current settings, by UEID */
|
|
|
|
pdcp_nr_security_info_t* ue_security =
|
|
|
|
(pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_hash,
|
|
|
|
GUINT_TO_POINTER((guint)ueid));
|
|
|
|
if (ue_security == NULL) {
|
|
|
|
/* Copy whole security struct */
|
|
|
|
ue_security = wmem_new(wmem_file_scope(), pdcp_nr_security_info_t);
|
|
|
|
*ue_security = *security_info;
|
|
|
|
|
|
|
|
/* And add into security table */
|
|
|
|
wmem_map_insert(pdcp_security_hash, GUINT_TO_POINTER((guint)ueid), ue_security);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Just update existing entry already in table */
|
|
|
|
ue_security->previous_configuration_frame = ue_security->configuration_frame;
|
|
|
|
ue_security->previous_integrity = ue_security->integrity;
|
|
|
|
ue_security->previous_ciphering = ue_security->ciphering;
|
|
|
|
|
|
|
|
ue_security->configuration_frame = security_info->configuration_frame;
|
|
|
|
ue_security->integrity = security_info->integrity;
|
|
|
|
ue_security->ciphering = security_info->ciphering;
|
|
|
|
ue_security->seen_next_ul_pdu = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Also add an entry for this PDU already to use these settings, as otherwise it won't be present
|
|
|
|
when we query it on the first pass. */
|
|
|
|
p_frame_security = wmem_new(wmem_file_scope(), pdcp_nr_security_info_t);
|
2021-01-12 22:21:49 +00:00
|
|
|
/* Deep copy*/
|
2020-12-29 13:55:35 +00:00
|
|
|
*p_frame_security = *ue_security;
|
|
|
|
wmem_map_insert(pdcp_security_result_hash,
|
|
|
|
get_ueid_frame_hash_key(ueid, ue_security->configuration_frame, TRUE),
|
|
|
|
p_frame_security);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* UE failed to process SecurityModeCommand so go back to previous security settings */
|
|
|
|
void set_pdcp_nr_security_algorithms_failed(guint16 ueid)
|
|
|
|
{
|
|
|
|
/* Look up current state by UEID */
|
|
|
|
pdcp_nr_security_info_t* ue_security =
|
|
|
|
(pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_hash,
|
|
|
|
GUINT_TO_POINTER((guint)ueid));
|
|
|
|
if (ue_security != NULL) {
|
|
|
|
/* TODO: could remove from table if previous_configuration_frame is 0 */
|
|
|
|
/* Go back to previous state */
|
|
|
|
ue_security->configuration_frame = ue_security->previous_configuration_frame;
|
|
|
|
ue_security->integrity = ue_security->previous_integrity;
|
|
|
|
ue_security->ciphering = ue_security->previous_ciphering;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decipher payload if algorithm is supported and plausible inputs are available */
|
|
|
|
static tvbuff_t *decipher_payload(tvbuff_t *tvb, packet_info *pinfo, int *offset,
|
|
|
|
pdu_security_settings_t *pdu_security_settings,
|
|
|
|
struct pdcp_nr_info *p_pdcp_info, guint sdap_length,
|
|
|
|
gboolean will_be_deciphered, gboolean *deciphered)
|
|
|
|
{
|
|
|
|
guint8* decrypted_data = NULL;
|
|
|
|
gint payload_length = 0;
|
|
|
|
tvbuff_t *decrypted_tvb;
|
|
|
|
|
|
|
|
/* Nothing to do if NULL ciphering */
|
2021-01-01 22:19:28 +00:00
|
|
|
if (pdu_security_settings->ciphering == nea0 || pdu_security_settings->ciphering == nea_disabled) {
|
2020-12-29 13:55:35 +00:00
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Nothing to do if don't have valid cipher key */
|
|
|
|
if (!pdu_security_settings->cipherKeyValid) {
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check whether algorithm supported (only drop through and process if we do) */
|
|
|
|
if (pdu_security_settings->ciphering == nea1) {
|
|
|
|
#ifndef HAVE_SNOW3G
|
|
|
|
return tvb;
|
2021-01-16 22:16:19 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else if (pdu_security_settings->ciphering == nea3) {
|
|
|
|
#ifndef HAVE_ZUC
|
|
|
|
return tvb;
|
2020-12-29 13:55:35 +00:00
|
|
|
#endif
|
|
|
|
}
|
2021-02-28 18:09:54 +00:00
|
|
|
else if (pdu_security_settings->ciphering != nea2) {
|
2020-12-29 13:55:35 +00:00
|
|
|
/* An algorithm we don't support at all! */
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
2021-02-28 18:09:54 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Don't decipher if turned off in preferences */
|
|
|
|
if (((p_pdcp_info->plane == NR_SIGNALING_PLANE) && !global_pdcp_decipher_signalling) ||
|
|
|
|
((p_pdcp_info->plane == NR_USER_PLANE) && !global_pdcp_decipher_userplane)) {
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't decipher user-plane control messages */
|
|
|
|
if ((p_pdcp_info->plane == NR_USER_PLANE) && ((tvb_get_guint8(tvb, 0) & 0x80) == 0x00)) {
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't decipher common control messages */
|
|
|
|
if ((p_pdcp_info->plane == NR_SIGNALING_PLANE) && (p_pdcp_info->bearerType != Bearer_DCCH)) {
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't decipher if not yet past SecurityModeResponse */
|
|
|
|
if (!will_be_deciphered) {
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* AES */
|
|
|
|
if (pdu_security_settings->ciphering == nea2) {
|
|
|
|
unsigned char ctr_block[16];
|
|
|
|
gcry_cipher_hd_t cypher_hd;
|
|
|
|
int gcrypt_err;
|
|
|
|
/* TS 33.501 D.4.4 defers to TS 33.401 B.1.3 */
|
|
|
|
|
|
|
|
/* Set CTR */
|
|
|
|
memset(ctr_block, 0, 16);
|
|
|
|
/* Only first 5 bytes set */
|
|
|
|
ctr_block[0] = (pdu_security_settings->count & 0xff000000) >> 24;
|
|
|
|
ctr_block[1] = (pdu_security_settings->count & 0x00ff0000) >> 16;
|
|
|
|
ctr_block[2] = (pdu_security_settings->count & 0x0000ff00) >> 8;
|
|
|
|
ctr_block[3] = (pdu_security_settings->count & 0x000000ff);
|
|
|
|
ctr_block[4] = (pdu_security_settings->bearer << 3) + (pdu_security_settings->direction << 2);
|
|
|
|
|
|
|
|
/* Open gcrypt handle */
|
|
|
|
gcrypt_err = gcry_cipher_open(&cypher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the key */
|
|
|
|
gcrypt_err = gcry_cipher_setkey(cypher_hd, pdu_security_settings->cipherKey, 16);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
gcry_cipher_close(cypher_hd);
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the CTR */
|
|
|
|
gcrypt_err = gcry_cipher_setctr(cypher_hd, ctr_block, 16);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
gcry_cipher_close(cypher_hd);
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extract the encrypted data into a buffer */
|
|
|
|
payload_length = tvb_captured_length_remaining(tvb, *offset+sdap_length);
|
|
|
|
decrypted_data = (guint8 *)tvb_memdup(pinfo->pool, tvb, *offset+sdap_length, payload_length);
|
|
|
|
|
|
|
|
/* Decrypt the actual data */
|
|
|
|
gcrypt_err = gcry_cipher_decrypt(cypher_hd,
|
|
|
|
decrypted_data, payload_length,
|
|
|
|
NULL, 0);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
gcry_cipher_close(cypher_hd);
|
|
|
|
return tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Close gcrypt handle */
|
|
|
|
gcry_cipher_close(cypher_hd);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_SNOW3G
|
|
|
|
/* SNOW-3G */
|
|
|
|
if (pdu_security_settings->ciphering == nea1) {
|
|
|
|
/* TS 33.501 D.4.3 defers to RS 33.401 */
|
|
|
|
|
|
|
|
/* Extract the encrypted data into a buffer */
|
|
|
|
payload_length = tvb_captured_length_remaining(tvb, *offset+sdap_length);
|
|
|
|
decrypted_data = (guint8 *)tvb_memdup(pinfo->pool, tvb, *offset+sdap_length, payload_length);
|
|
|
|
|
|
|
|
/* Do the algorithm */
|
|
|
|
snow3g_f8(pdu_security_settings->cipherKey,
|
|
|
|
pdu_security_settings->count,
|
|
|
|
pdu_security_settings->bearer,
|
|
|
|
pdu_security_settings->direction,
|
|
|
|
decrypted_data, payload_length*8);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-01-16 22:16:19 +00:00
|
|
|
#ifdef HAVE_ZUC
|
|
|
|
/* ZUC */
|
|
|
|
if (pdu_security_settings->ciphering == nea3) {
|
|
|
|
/* Extract the encrypted data into a buffer */
|
|
|
|
payload_length = tvb_captured_length_remaining(tvb, *offset+sdap_length);
|
|
|
|
decrypted_data = (guint8 *)tvb_memdup(pinfo->pool, tvb, *offset+sdap_length, payload_length);
|
|
|
|
|
|
|
|
/* Do the algorithm. Assuming implementation works in-place */
|
|
|
|
zuc_f8(pdu_security_settings->cipherKey,
|
|
|
|
pdu_security_settings->count,
|
|
|
|
pdu_security_settings->bearer,
|
|
|
|
pdu_security_settings->direction,
|
|
|
|
payload_length*8, /* Length is in bits */
|
|
|
|
(guint32*)decrypted_data, (guint32*)decrypted_data);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Create tvb for resulting deciphered sdu */
|
|
|
|
decrypted_tvb = tvb_new_child_real_data(tvb, decrypted_data, payload_length, payload_length);
|
|
|
|
add_new_data_source(pinfo, decrypted_tvb, "Deciphered Payload");
|
|
|
|
|
|
|
|
/* Return deciphered data, i.e. beginning of new tvb */
|
|
|
|
*offset = 0;
|
|
|
|
*deciphered = TRUE;
|
|
|
|
return decrypted_tvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to calculate digest to compare with that found in frame. */
|
|
|
|
static guint32 calculate_digest(pdu_security_settings_t *pdu_security_settings, tvbuff_t *header_tvb _U_,
|
|
|
|
tvbuff_t *tvb _U_, gint offset _U_, guint sdap_length _U_, gboolean *calculated)
|
|
|
|
{
|
|
|
|
*calculated = FALSE;
|
|
|
|
|
|
|
|
if (pdu_security_settings->integrity == nia0) {
|
|
|
|
/* Should be zero in this case */
|
|
|
|
*calculated = TRUE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Can't calculate if don't have valid integrity key */
|
|
|
|
if (!pdu_security_settings->integrityKeyValid) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Can only do if indicated in preferences */
|
|
|
|
if (!global_pdcp_check_integrity) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (pdu_security_settings->integrity) {
|
|
|
|
|
|
|
|
#ifdef HAVE_SNOW3G
|
|
|
|
case nia1:
|
|
|
|
{
|
|
|
|
/* SNOW3G */
|
|
|
|
guint8 *mac;
|
2021-02-09 08:16:35 +00:00
|
|
|
guint header_length = tvb_reported_length(header_tvb);
|
2020-12-29 13:55:35 +00:00
|
|
|
gint message_length = tvb_captured_length_remaining(tvb, offset) - 4;
|
|
|
|
guint8 *message_data = (guint8 *)wmem_alloc0(wmem_packet_scope(), header_length+message_length-sdap_length+4);
|
|
|
|
|
|
|
|
/* TS 33.401 B.2.2 */
|
|
|
|
|
|
|
|
/* Data is header bytes */
|
|
|
|
tvb_memcpy(header_tvb, message_data, 0, header_length);
|
|
|
|
/* Followed by the decrypted message (but not the digest bytes) */
|
|
|
|
tvb_memcpy(tvb, message_data+header_length, offset+sdap_length, message_length-sdap_length);
|
|
|
|
|
|
|
|
mac = (u8*)snow3g_f9(pdu_security_settings->integrityKey,
|
|
|
|
pdu_security_settings->count,
|
|
|
|
/* 'Fresh' is the bearer bits then zeros */
|
|
|
|
pdu_security_settings->bearer << 27,
|
|
|
|
pdu_security_settings->direction,
|
|
|
|
message_data,
|
|
|
|
(message_length+1)*8);
|
|
|
|
|
|
|
|
*calculated = TRUE;
|
|
|
|
return ((mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if GCRYPT_VERSION_NUMBER >= 0x010600 /* 1.6.0 */
|
|
|
|
case nia2:
|
|
|
|
{
|
|
|
|
/* AES */
|
|
|
|
gcry_mac_hd_t mac_hd;
|
|
|
|
int gcrypt_err;
|
2021-02-09 08:16:35 +00:00
|
|
|
guint header_length;
|
2020-12-29 13:55:35 +00:00
|
|
|
gint message_length;
|
|
|
|
guint8 *message_data;
|
|
|
|
guint8 mac[4];
|
|
|
|
size_t read_digest_length = 4;
|
|
|
|
|
|
|
|
/* Open gcrypt handle */
|
|
|
|
gcrypt_err = gcry_mac_open(&mac_hd, GCRY_MAC_CMAC_AES, 0, NULL);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the key */
|
|
|
|
gcrypt_err = gcry_mac_setkey(mac_hd, pdu_security_settings->integrityKey, 16);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
gcry_mac_close(mac_hd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TS 33.501 D.4.3 defers to TS 33.401 B.2.3 */
|
|
|
|
|
|
|
|
/* Extract the encrypted data into a buffer */
|
2021-02-09 08:16:35 +00:00
|
|
|
header_length = tvb_reported_length(header_tvb);
|
2020-12-29 13:55:35 +00:00
|
|
|
message_length = tvb_captured_length_remaining(tvb, offset) - 4;
|
|
|
|
message_data = (guint8 *)wmem_alloc0(wmem_packet_scope(), 8+header_length+message_length-sdap_length);
|
|
|
|
message_data[0] = (pdu_security_settings->count & 0xff000000) >> 24;
|
|
|
|
message_data[1] = (pdu_security_settings->count & 0x00ff0000) >> 16;
|
|
|
|
message_data[2] = (pdu_security_settings->count & 0x0000ff00) >> 8;
|
|
|
|
message_data[3] = (pdu_security_settings->count & 0x000000ff);
|
|
|
|
message_data[4] = (pdu_security_settings->bearer << 3) + (pdu_security_settings->direction << 2);
|
|
|
|
/* rest of first 8 bytes are left as zeroes... */
|
|
|
|
|
|
|
|
/* Now the header bytes */
|
|
|
|
tvb_memcpy(header_tvb, message_data+8, 0, header_length);
|
|
|
|
/* Followed by the decrypted message (but not the digest bytes or any SDAP bytes) */
|
|
|
|
tvb_memcpy(tvb, message_data+8+header_length, offset+sdap_length, message_length-sdap_length);
|
|
|
|
|
|
|
|
/* Pass in the message */
|
|
|
|
gcrypt_err = gcry_mac_write(mac_hd, message_data, 8+header_length+message_length-sdap_length);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
gcry_mac_close(mac_hd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read out the digest */
|
|
|
|
gcrypt_err = gcry_mac_read(mac_hd, mac, &read_digest_length);
|
|
|
|
if (gcrypt_err != 0) {
|
|
|
|
gcry_mac_close(mac_hd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now close the mac handle */
|
|
|
|
gcry_mac_close(mac_hd);
|
|
|
|
|
|
|
|
*calculated = TRUE;
|
|
|
|
return ((mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]);
|
|
|
|
}
|
|
|
|
#endif
|
2021-01-16 22:16:19 +00:00
|
|
|
#ifdef HAVE_ZUC
|
|
|
|
case nia3:
|
|
|
|
{
|
|
|
|
/* ZUC */
|
|
|
|
guint32 mac;
|
2021-02-09 08:16:35 +00:00
|
|
|
guint header_length = tvb_reported_length(header_tvb);
|
2021-01-16 22:16:19 +00:00
|
|
|
gint message_length = tvb_captured_length_remaining(tvb, offset) - 4;
|
|
|
|
guint8 *message_data = (guint8 *)wmem_alloc0(wmem_packet_scope(), header_length+message_length-sdap_length+4);
|
|
|
|
|
|
|
|
/* Data is header bytes */
|
|
|
|
tvb_memcpy(header_tvb, message_data, 0, header_length);
|
|
|
|
/* Followed by the decrypted message (but not the digest bytes) */
|
|
|
|
tvb_memcpy(tvb, message_data+header_length, offset+sdap_length, message_length-sdap_length);
|
|
|
|
|
|
|
|
zuc_f9(pdu_security_settings->integrityKey,
|
|
|
|
pdu_security_settings->count,
|
|
|
|
pdu_security_settings->direction,
|
|
|
|
pdu_security_settings->bearer,
|
|
|
|
(message_length+header_length)*8,
|
|
|
|
(guint32*)message_data,
|
|
|
|
&mac);
|
|
|
|
|
|
|
|
*calculated = TRUE;
|
|
|
|
return mac;
|
|
|
|
}
|
|
|
|
#endif
|
2020-12-29 13:55:35 +00:00
|
|
|
|
|
|
|
default:
|
2021-01-16 22:16:19 +00:00
|
|
|
/* Can't calculate */
|
2020-12-29 13:55:35 +00:00
|
|
|
*calculated = FALSE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Forward declarations */
|
2018-04-05 12:04:06 +00:00
|
|
|
static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data);
|
|
|
|
|
2018-04-12 13:48:12 +00:00
|
|
|
static void report_heur_error(proto_tree *tree, packet_info *pinfo, expert_field *eiindex,
|
|
|
|
tvbuff_t *tvb, gint start, gint length)
|
|
|
|
{
|
|
|
|
proto_item *ti;
|
|
|
|
proto_tree *subtree;
|
|
|
|
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "PDCP-NR");
|
|
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
ti = proto_tree_add_item(tree, proto_pdcp_nr, tvb, 0, -1, ENC_NA);
|
|
|
|
subtree = proto_item_add_subtree(ti, ett_pdcp);
|
|
|
|
proto_tree_add_expert(subtree, pinfo, eiindex, tvb, start, length);
|
|
|
|
}
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Heuristic dissector looks for supported framing protocol (see wiki page) */
|
|
|
|
static gboolean dissect_pdcp_nr_heur(tvbuff_t *tvb, packet_info *pinfo,
|
|
|
|
proto_tree *tree, void *data _U_)
|
|
|
|
{
|
|
|
|
gint offset = 0;
|
|
|
|
struct pdcp_nr_info *p_pdcp_nr_info;
|
|
|
|
tvbuff_t *pdcp_tvb;
|
|
|
|
guint8 tag = 0;
|
|
|
|
gboolean seqnumLengthTagPresent = FALSE;
|
|
|
|
|
|
|
|
/* Needs to be at least as long as:
|
|
|
|
- the signature string
|
2018-04-11 19:59:14 +00:00
|
|
|
- fixed header byte(s)
|
|
|
|
- tag for data
|
|
|
|
- at least one byte of PDCP PDU payload.
|
|
|
|
However, let attempted dissection show if there are any tags at all. */
|
2018-04-05 12:04:06 +00:00
|
|
|
gint min_length = (gint)(strlen(PDCP_NR_START_STRING) + 3); /* signature */
|
|
|
|
|
|
|
|
if (tvb_captured_length_remaining(tvb, offset) < min_length) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, compare with signature string */
|
|
|
|
if (tvb_strneql(tvb, offset, PDCP_NR_START_STRING, strlen(PDCP_NR_START_STRING)) != 0) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
offset += (gint)strlen(PDCP_NR_START_STRING);
|
|
|
|
|
|
|
|
|
|
|
|
/* If redissecting, use previous info struct (if available) */
|
|
|
|
p_pdcp_nr_info = (pdcp_nr_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_pdcp_nr, 0);
|
|
|
|
if (p_pdcp_nr_info == NULL) {
|
|
|
|
/* Allocate new info struct for this frame */
|
|
|
|
p_pdcp_nr_info = wmem_new0(wmem_file_scope(), pdcp_nr_info);
|
|
|
|
|
2018-04-13 18:35:59 +00:00
|
|
|
/* Read fixed fields */
|
|
|
|
p_pdcp_nr_info->plane = (enum pdcp_nr_plane)tvb_get_guint8(tvb, offset++);
|
|
|
|
if (p_pdcp_nr_info->plane == NR_SIGNALING_PLANE) {
|
|
|
|
/* Signalling plane always has 12 SN bits */
|
|
|
|
p_pdcp_nr_info->seqnum_length = PDCP_NR_SN_LENGTH_12_BITS;
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2018-04-13 18:35:59 +00:00
|
|
|
/* Read tagged fields */
|
|
|
|
while (tag != PDCP_NR_PAYLOAD_TAG) {
|
|
|
|
/* Process next tag */
|
|
|
|
tag = tvb_get_guint8(tvb, offset++);
|
|
|
|
switch (tag) {
|
|
|
|
case PDCP_NR_SEQNUM_LENGTH_TAG:
|
|
|
|
p_pdcp_nr_info->seqnum_length = tvb_get_guint8(tvb, offset);
|
|
|
|
offset++;
|
|
|
|
seqnumLengthTagPresent = TRUE;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_DIRECTION_TAG:
|
|
|
|
p_pdcp_nr_info->direction = tvb_get_guint8(tvb, offset);
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_BEARER_TYPE_TAG:
|
|
|
|
p_pdcp_nr_info->bearerType = (NRBearerType)tvb_get_guint8(tvb, offset);
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_BEARER_ID_TAG:
|
|
|
|
p_pdcp_nr_info->bearerId = tvb_get_guint8(tvb, offset);
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_UEID_TAG:
|
|
|
|
p_pdcp_nr_info->ueid = tvb_get_ntohs(tvb, offset);
|
|
|
|
offset += 2;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_COMPRESSION_TAG:
|
|
|
|
p_pdcp_nr_info->rohc.rohc_compression = TRUE;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_IP_VERSION_TAG:
|
|
|
|
p_pdcp_nr_info->rohc.rohc_ip_version = tvb_get_guint8(tvb, offset);
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_CID_INC_INFO_TAG:
|
2018-04-23 14:38:12 +00:00
|
|
|
p_pdcp_nr_info->rohc.cid_inclusion_info = TRUE;
|
2018-04-13 18:35:59 +00:00
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_LARGE_CID_PRES_TAG:
|
2018-04-23 14:38:12 +00:00
|
|
|
p_pdcp_nr_info->rohc.large_cid_present = TRUE;
|
2018-04-13 18:35:59 +00:00
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_MODE_TAG:
|
|
|
|
p_pdcp_nr_info->rohc.mode = (enum rohc_mode)tvb_get_guint8(tvb, offset);
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_RND_TAG:
|
2018-04-23 14:38:12 +00:00
|
|
|
p_pdcp_nr_info->rohc.rnd = TRUE;
|
2018-04-13 18:35:59 +00:00
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_UDP_CHECKSUM_PRES_TAG:
|
2018-04-23 14:38:12 +00:00
|
|
|
p_pdcp_nr_info->rohc.udp_checksum_present = TRUE;
|
2018-04-13 18:35:59 +00:00
|
|
|
break;
|
|
|
|
case PDCP_NR_ROHC_PROFILE_TAG:
|
|
|
|
p_pdcp_nr_info->rohc.profile = tvb_get_ntohs(tvb, offset);
|
|
|
|
offset += 2;
|
|
|
|
break;
|
2018-05-24 15:07:18 +00:00
|
|
|
case PDCP_NR_MACI_PRES_TAG:
|
|
|
|
p_pdcp_nr_info->maci_present = TRUE;
|
|
|
|
break;
|
2018-06-04 20:17:52 +00:00
|
|
|
case PDCP_NR_SDAP_HEADER_TAG:
|
|
|
|
p_pdcp_nr_info->sdap_header = tvb_get_guint8(tvb, offset) & 0x03;
|
|
|
|
offset++;
|
|
|
|
break;
|
2021-01-01 22:19:28 +00:00
|
|
|
case PDCP_NR_CIPHER_DISABLED_TAG:
|
|
|
|
p_pdcp_nr_info->ciphering_disabled = TRUE;
|
|
|
|
break;
|
|
|
|
|
2018-04-13 18:35:59 +00:00
|
|
|
|
|
|
|
case PDCP_NR_PAYLOAD_TAG:
|
|
|
|
/* Have reached data, so get out of loop */
|
|
|
|
p_pdcp_nr_info->pdu_length = tvb_reported_length_remaining(tvb, offset);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* It must be a recognised tag */
|
|
|
|
report_heur_error(tree, pinfo, &ei_pdcp_nr_unknown_udp_framing_tag, tvb, offset-1, 1);
|
|
|
|
wmem_free(wmem_file_scope(), p_pdcp_nr_info);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
2018-04-13 18:35:59 +00:00
|
|
|
if ((p_pdcp_nr_info->plane == NR_USER_PLANE) && (seqnumLengthTagPresent == FALSE)) {
|
|
|
|
/* Conditional field is not present */
|
|
|
|
report_heur_error(tree, pinfo, &ei_pdcp_nr_missing_udp_framing_tag, tvb, 0, offset);
|
|
|
|
wmem_free(wmem_file_scope(), p_pdcp_nr_info);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Store info in packet */
|
|
|
|
p_add_proto_data(wmem_file_scope(), pinfo, proto_pdcp_nr, 0, p_pdcp_nr_info);
|
|
|
|
}
|
2018-04-13 18:35:59 +00:00
|
|
|
else {
|
|
|
|
offset = tvb_reported_length(tvb) - p_pdcp_nr_info->pdu_length;
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/**************************************/
|
|
|
|
/* OK, now dissect as PDCP nr */
|
|
|
|
|
|
|
|
/* Create tvb that starts at actual PDCP PDU */
|
|
|
|
pdcp_tvb = tvb_new_subset_remaining(tvb, offset);
|
|
|
|
dissect_pdcp_nr(pdcp_tvb, pinfo, tree, data);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************/
|
|
|
|
/* Main dissection function. */
|
2018-08-09 13:08:43 +00:00
|
|
|
static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
|
2018-04-05 12:04:06 +00:00
|
|
|
{
|
|
|
|
const char *mode;
|
2018-05-24 15:07:18 +00:00
|
|
|
proto_tree *pdcp_tree = NULL;
|
|
|
|
proto_item *root_ti = NULL;
|
2018-04-05 12:04:06 +00:00
|
|
|
proto_item *ti;
|
|
|
|
gint offset = 0;
|
|
|
|
struct pdcp_nr_info *p_pdcp_info;
|
2018-05-24 15:07:18 +00:00
|
|
|
tvbuff_t *rohc_tvb = NULL;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
pdcp_nr_security_info_t *current_security = NULL; /* current security for this UE */
|
|
|
|
pdcp_nr_security_info_t *pdu_security; /* security in place for this PDU */
|
|
|
|
proto_tree *security_tree = NULL;
|
|
|
|
proto_item *security_ti;
|
2018-04-05 12:04:06 +00:00
|
|
|
tvbuff_t *payload_tvb;
|
2020-12-29 13:55:35 +00:00
|
|
|
pdu_security_settings_t pdu_security_settings;
|
|
|
|
gboolean payload_deciphered = FALSE;
|
|
|
|
|
|
|
|
/* Initialise security settings */
|
|
|
|
memset(&pdu_security_settings, 0, sizeof(pdu_security_settings));
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Set protocol name. */
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "PDCP-NR");
|
|
|
|
|
|
|
|
/* Look for attached packet info! */
|
|
|
|
p_pdcp_info = (struct pdcp_nr_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_pdcp_nr, 0);
|
|
|
|
/* Can't dissect anything without it... */
|
|
|
|
if (p_pdcp_info == NULL) {
|
2018-08-09 13:08:43 +00:00
|
|
|
if (!data) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
p_pdcp_info = (struct pdcp_nr_info *)data;
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 11:03:22 +00:00
|
|
|
/* If no RLC layer in this frame, query RLC table for configured drb settings */
|
|
|
|
if (!p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_nr, 0)) {
|
2021-01-16 22:16:19 +00:00
|
|
|
/* Signalling plane is always 12 bits SN */
|
|
|
|
if (p_pdcp_info->plane == NR_SIGNALING_PLANE) {
|
|
|
|
p_pdcp_info->seqnum_length = PDCP_NR_SN_LENGTH_12_BITS;
|
|
|
|
}
|
|
|
|
/* If DRB channel, query rlc mappings (from RRC) */
|
|
|
|
else if (p_pdcp_info->plane == NR_USER_PLANE) {
|
2021-01-04 11:03:22 +00:00
|
|
|
pdcp_bearer_parameters *params = get_rlc_nr_drb_pdcp_mapping(p_pdcp_info->ueid, p_pdcp_info->bearerId);
|
|
|
|
if (params) {
|
|
|
|
if (p_pdcp_info->direction == DIRECTION_UPLINK) {
|
|
|
|
p_pdcp_info->seqnum_length = params->pdcp_sn_bits_ul;
|
|
|
|
if (params->pdcp_sdap_ul) {
|
|
|
|
p_pdcp_info->sdap_header |= PDCP_NR_UL_SDAP_HEADER_PRESENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_pdcp_info->seqnum_length = params->pdcp_sn_bits_dl;
|
|
|
|
if (params->pdcp_sdap_dl) {
|
|
|
|
p_pdcp_info->sdap_header |= PDCP_NR_DL_SDAP_HEADER_PRESENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p_pdcp_info->maci_present = params->pdcp_integrity;
|
|
|
|
p_pdcp_info->ciphering_disabled = params->pdcp_ciphering_disabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Don't want to overwrite the RLC Info column if configured not to */
|
|
|
|
if ((global_pdcp_nr_layer_to_show == ShowRLCLayer) &&
|
|
|
|
(p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_nr, 0) != NULL)) {
|
|
|
|
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, FALSE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* TODO: won't help with multiple PDCP-or-traffic PDUs / frame... */
|
|
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, TRUE);
|
|
|
|
}
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* MACI always present for SRBs */
|
|
|
|
if (p_pdcp_info->plane == NR_SIGNALING_PLANE) {
|
|
|
|
p_pdcp_info->maci_present = TRUE;
|
|
|
|
}
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Create pdcp tree. */
|
|
|
|
if (tree) {
|
|
|
|
root_ti = proto_tree_add_item(tree, proto_pdcp_nr, tvb, offset, -1, ENC_NA);
|
|
|
|
pdcp_tree = proto_item_add_subtree(root_ti, ett_pdcp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set mode string */
|
|
|
|
mode = val_to_str_const(p_pdcp_info->rohc.mode, rohc_mode_vals, "Error");
|
|
|
|
|
|
|
|
/*****************************************************/
|
|
|
|
/* Show configuration (attached packet) info in tree */
|
|
|
|
if (pdcp_tree) {
|
|
|
|
show_pdcp_config(pinfo, tvb, pdcp_tree, p_pdcp_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Show ROHC mode */
|
|
|
|
if (p_pdcp_info->rohc.rohc_compression) {
|
|
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, " (mode=%c)", mode[0]);
|
|
|
|
}
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/***************************************/
|
|
|
|
/* UE security algorithms */
|
|
|
|
if (!PINFO_FD_VISITED(pinfo)) {
|
|
|
|
/* Look up current state by UEID */
|
|
|
|
current_security = (pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_hash,
|
|
|
|
GUINT_TO_POINTER((guint)p_pdcp_info->ueid));
|
|
|
|
if (current_security != NULL) {
|
|
|
|
/* Store any result for this frame in the result table */
|
|
|
|
pdcp_nr_security_info_t *security_to_store = wmem_new(wmem_file_scope(), pdcp_nr_security_info_t);
|
|
|
|
/* Take a deep copy of the settings */
|
|
|
|
*security_to_store = *current_security;
|
2021-01-01 22:19:28 +00:00
|
|
|
|
|
|
|
/* But ciphering may be turned off for this channel */
|
|
|
|
if (p_pdcp_info->ciphering_disabled) {
|
|
|
|
security_to_store->ciphering = nea_disabled;
|
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
wmem_map_insert(pdcp_security_result_hash,
|
|
|
|
get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, TRUE),
|
|
|
|
security_to_store);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* No entry added from RRC, but still use configured defaults */
|
|
|
|
if ((global_default_ciphering_algorithm != nea0) ||
|
|
|
|
(global_default_integrity_algorithm != nia0)) {
|
|
|
|
/* Copy algorithms from preference defaults */
|
|
|
|
pdcp_nr_security_info_t *security_to_store = wmem_new0(wmem_file_scope(), pdcp_nr_security_info_t);
|
|
|
|
security_to_store->ciphering = global_default_ciphering_algorithm;
|
|
|
|
security_to_store->integrity = global_default_integrity_algorithm;
|
|
|
|
security_to_store->seen_next_ul_pdu = TRUE;
|
|
|
|
wmem_map_insert(pdcp_security_result_hash,
|
|
|
|
get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, TRUE),
|
|
|
|
security_to_store);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Show security settings for this PDU */
|
|
|
|
pdu_security = (pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_result_hash,
|
|
|
|
get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, FALSE));
|
|
|
|
if (pdu_security != NULL) {
|
|
|
|
/* Create subtree */
|
|
|
|
security_ti = proto_tree_add_string_format(pdcp_tree,
|
|
|
|
hf_pdcp_nr_security,
|
|
|
|
tvb, 0, 0,
|
|
|
|
"", "UE Security");
|
|
|
|
security_tree = proto_item_add_subtree(security_ti, ett_pdcp_security);
|
|
|
|
proto_item_set_generated(security_ti);
|
|
|
|
|
|
|
|
/* Setup frame */
|
|
|
|
if (pinfo->num > pdu_security->configuration_frame) {
|
|
|
|
ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_setup_frame,
|
|
|
|
tvb, 0, 0, pdu_security->configuration_frame);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ciphering */
|
|
|
|
ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_ciphering_algorithm,
|
|
|
|
tvb, 0, 0, pdu_security->ciphering);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
|
|
|
|
/* Integrity */
|
|
|
|
ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_integrity_algorithm,
|
|
|
|
tvb, 0, 0, pdu_security->integrity);
|
|
|
|
proto_item_set_generated(ti);
|
|
|
|
|
2021-01-01 15:51:00 +00:00
|
|
|
/* Show algorithms in security root */
|
2020-12-29 13:55:35 +00:00
|
|
|
proto_item_append_text(security_ti, " (ciphering=%s, integrity=%s)",
|
|
|
|
val_to_str_const(pdu_security->ciphering, ciphering_algorithm_vals, "Unknown"),
|
|
|
|
val_to_str_const(pdu_security->integrity, integrity_algorithm_vals, "Unknown"));
|
|
|
|
|
|
|
|
pdu_security_settings.ciphering = pdu_security->ciphering;
|
|
|
|
pdu_security_settings.integrity = pdu_security->integrity;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/***********************************/
|
|
|
|
/* Handle PDCP header */
|
|
|
|
|
|
|
|
guint32 seqnum = 0;
|
|
|
|
gboolean seqnum_set = FALSE;
|
|
|
|
|
|
|
|
guint8 first_byte = tvb_get_guint8(tvb, offset);
|
|
|
|
|
|
|
|
/*****************************/
|
|
|
|
/* Signalling plane messages */
|
|
|
|
if (p_pdcp_info->plane == NR_SIGNALING_PLANE) {
|
|
|
|
/* Always 12 bits SN */
|
|
|
|
/* Verify 4 reserved bits are 0 */
|
|
|
|
guint8 reserved = (first_byte & 0xf0) >> 4;
|
|
|
|
ti = proto_tree_add_item(pdcp_tree, hf_pdcp_nr_control_plane_reserved,
|
|
|
|
tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
if (reserved != 0) {
|
|
|
|
expert_add_info_format(pinfo, ti, &ei_pdcp_nr_reserved_bits_not_zero,
|
|
|
|
"PDCP signalling header reserved bits not zero");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 12-bit sequence number */
|
|
|
|
proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_seq_num_12, tvb, offset, 2, ENC_BIG_ENDIAN, &seqnum);
|
|
|
|
seqnum_set = TRUE;
|
|
|
|
write_pdu_label_and_info(root_ti, pinfo, " (SN=%-4u)", seqnum);
|
|
|
|
offset += 2;
|
|
|
|
|
|
|
|
if (tvb_captured_length_remaining(tvb, offset) == 0) {
|
|
|
|
/* Only PDCP header was captured, stop dissection here */
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (p_pdcp_info->plane == NR_USER_PLANE) {
|
|
|
|
|
|
|
|
/**********************************/
|
|
|
|
/* User-plane messages */
|
|
|
|
gboolean is_user_plane;
|
|
|
|
|
|
|
|
/* Data/Control flag */
|
|
|
|
proto_tree_add_item_ret_boolean(pdcp_tree, hf_pdcp_nr_data_control, tvb, offset, 1, ENC_BIG_ENDIAN, &is_user_plane);
|
|
|
|
|
|
|
|
if (is_user_plane) {
|
|
|
|
/*****************************/
|
|
|
|
/* User-plane Data */
|
|
|
|
guint32 reserved_value;
|
|
|
|
|
|
|
|
/* Number of sequence number bits depends upon config */
|
|
|
|
switch (p_pdcp_info->seqnum_length) {
|
|
|
|
case PDCP_NR_SN_LENGTH_12_BITS:
|
|
|
|
/* 3 reserved bits */
|
|
|
|
ti = proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_reserved3, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved_value);
|
|
|
|
|
|
|
|
/* Complain if not 0 */
|
|
|
|
if (reserved_value != 0) {
|
|
|
|
expert_add_info_format(pinfo, ti, &ei_pdcp_nr_reserved_bits_not_zero,
|
|
|
|
"Reserved bits have value 0x%x - should be 0x0",
|
|
|
|
reserved_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 12-bit sequence number */
|
|
|
|
proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_seq_num_12, tvb, offset, 2, ENC_BIG_ENDIAN, &seqnum);
|
|
|
|
seqnum_set = TRUE;
|
|
|
|
offset += 2;
|
|
|
|
break;
|
|
|
|
case PDCP_NR_SN_LENGTH_18_BITS:
|
|
|
|
/* 5 reserved bits */
|
|
|
|
ti = proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_reserved5, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved_value);
|
|
|
|
|
|
|
|
/* Complain if not 0 */
|
|
|
|
if (reserved_value != 0) {
|
|
|
|
expert_add_info_format(pinfo, ti, &ei_pdcp_nr_reserved_bits_not_zero,
|
|
|
|
"Reserved bits have value 0x%x - should be 0x0",
|
|
|
|
reserved_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 18-bit sequence number */
|
|
|
|
proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_seq_num_18, tvb, offset, 3, ENC_BIG_ENDIAN, &seqnum);
|
|
|
|
seqnum_set = TRUE;
|
|
|
|
offset += 3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Not a recognised data format!!!!! */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
write_pdu_label_and_info(root_ti, pinfo, " (SN=%-6u)", seqnum);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*******************************/
|
|
|
|
/* User-plane Control messages */
|
|
|
|
guint32 control_pdu_type;
|
|
|
|
proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_control_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN, &control_pdu_type);
|
|
|
|
|
|
|
|
switch (control_pdu_type) {
|
|
|
|
case 0: /* PDCP status report */
|
|
|
|
{
|
|
|
|
guint32 fmc;
|
|
|
|
guint not_received = 0;
|
|
|
|
guint i, j, l;
|
|
|
|
guint32 len, bit_offset;
|
|
|
|
proto_tree *bitmap_tree;
|
|
|
|
proto_item *bitmap_ti = NULL;
|
|
|
|
gchar *buff = NULL;
|
2018-05-21 12:11:55 +00:00
|
|
|
#define BUFF_SIZE 89
|
2018-04-05 12:04:06 +00:00
|
|
|
guint32 reserved_value;
|
|
|
|
|
|
|
|
/* 4 bits reserved */
|
|
|
|
ti = proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_reserved4, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved_value);
|
|
|
|
|
|
|
|
/* Complain if not 0 */
|
|
|
|
if (reserved_value != 0) {
|
|
|
|
expert_add_info_format(pinfo, ti, &ei_pdcp_nr_reserved_bits_not_zero,
|
|
|
|
"Reserved bits have value 0x%x - should be 0x0",
|
|
|
|
reserved_value);
|
|
|
|
}
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
/* First-Missing-Count */
|
|
|
|
proto_tree_add_item_ret_uint(pdcp_tree, hf_pdcp_nr_fmc, tvb, offset, 4, ENC_BIG_ENDIAN, &fmc);
|
|
|
|
offset += 4;
|
|
|
|
|
|
|
|
|
|
|
|
/* Bitmap tree */
|
|
|
|
if (tvb_reported_length_remaining(tvb, offset) > 0) {
|
|
|
|
bitmap_ti = proto_tree_add_item(pdcp_tree, hf_pdcp_nr_bitmap, tvb,
|
|
|
|
offset, -1, ENC_NA);
|
|
|
|
bitmap_tree = proto_item_add_subtree(bitmap_ti, ett_pdcp_report_bitmap);
|
|
|
|
|
|
|
|
buff = (gchar *)wmem_alloc(wmem_packet_scope(), BUFF_SIZE);
|
|
|
|
len = tvb_reported_length_remaining(tvb, offset);
|
|
|
|
bit_offset = offset<<3;
|
|
|
|
|
|
|
|
/* For each byte... */
|
|
|
|
for (i=0; i<len; i++) {
|
|
|
|
guint8 bits = tvb_get_bits8(tvb, bit_offset, 8);
|
|
|
|
for (l=0, j=0; l<8; l++) {
|
|
|
|
if ((bits << l) & 0x80) {
|
|
|
|
if (bitmap_tree) {
|
2021-01-16 22:16:19 +00:00
|
|
|
/* TODO: better to do mod and show as SN instead? */
|
2018-05-21 12:11:55 +00:00
|
|
|
j += g_snprintf(&buff[j], BUFF_SIZE-j, "%10u,", (unsigned)(fmc+(8*i)+l+1));
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bitmap_tree) {
|
2018-05-21 12:11:55 +00:00
|
|
|
j += (guint)g_strlcpy(&buff[j], " ,", BUFF_SIZE-j);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
not_received++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bitmap_tree) {
|
|
|
|
proto_tree_add_uint_format(bitmap_tree, hf_pdcp_nr_bitmap_byte, tvb, bit_offset/8, 1, bits, "%s", buff);
|
|
|
|
}
|
|
|
|
bit_offset += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bitmap_ti != NULL) {
|
|
|
|
proto_item_append_text(bitmap_ti, " (%u SNs not received)", not_received);
|
|
|
|
}
|
|
|
|
write_pdu_label_and_info(root_ti, pinfo, " Status Report (fmc=%u) not-received=%u",
|
|
|
|
fmc, not_received);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case 1: /* ROHC Feedback */
|
|
|
|
offset++;
|
|
|
|
break; /* Drop-through to dissect feedback */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Invalid plane setting...! */
|
|
|
|
write_pdu_label_and_info(root_ti, pinfo, " - INVALID PLANE (%u)",
|
|
|
|
p_pdcp_info->plane);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Have reached the end of the header (for data frames) */
|
|
|
|
gint header_length = offset;
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Do sequence analysis if configured to. */
|
|
|
|
if (seqnum_set) {
|
|
|
|
gboolean do_analysis = FALSE;
|
|
|
|
|
|
|
|
switch (global_pdcp_check_sequence_numbers) {
|
|
|
|
case FALSE:
|
|
|
|
break;
|
|
|
|
case SEQUENCE_ANALYSIS_RLC_ONLY:
|
|
|
|
if ((p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_nr, 0) != NULL) &&
|
|
|
|
!p_pdcp_info->is_retx) {
|
|
|
|
do_analysis = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SEQUENCE_ANALYSIS_PDCP_ONLY:
|
|
|
|
if (p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_nr, 0) == NULL) {
|
|
|
|
do_analysis = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_analysis) {
|
|
|
|
checkBearerSequenceInfo(pinfo, tvb, p_pdcp_info,
|
2020-12-29 13:55:35 +00:00
|
|
|
seqnum, pdcp_tree, security_tree,
|
|
|
|
&pdu_security_settings);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************/
|
|
|
|
/* Now deal with the payload */
|
|
|
|
/*******************************************************/
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Any SDAP bytes (between header and payload) are ignored for integrity/encryption */
|
|
|
|
guint sdap_length = 0;
|
|
|
|
if (p_pdcp_info->plane == NR_USER_PLANE) {
|
|
|
|
if ((p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK && (p_pdcp_info->sdap_header & PDCP_NR_UL_SDAP_HEADER_PRESENT)) ||
|
|
|
|
(p_pdcp_info->direction == PDCP_NR_DIRECTION_DOWNLINK && (p_pdcp_info->sdap_header & PDCP_NR_DL_SDAP_HEADER_PRESENT))) {
|
|
|
|
/* Currently, all SDAP message bytes are 1 byte long */
|
|
|
|
sdap_length = 1;
|
|
|
|
}
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Decipher payload if necessary */
|
2021-02-28 18:09:54 +00:00
|
|
|
gboolean should_decipher = FALSE;
|
|
|
|
if (pdu_security && !p_pdcp_info->ciphering_disabled) {
|
|
|
|
if (p_pdcp_info->plane == NR_USER_PLANE) {
|
|
|
|
/* Should decipher DRBs if have key */
|
|
|
|
should_decipher = TRUE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Past SecurityModeComplete */
|
|
|
|
should_decipher= pdu_security->seen_next_ul_pdu;
|
|
|
|
}
|
|
|
|
}
|
2020-12-29 13:55:35 +00:00
|
|
|
payload_tvb = decipher_payload(tvb, pinfo, &offset, &pdu_security_settings, p_pdcp_info, sdap_length,
|
2021-02-28 18:09:54 +00:00
|
|
|
should_decipher,
|
|
|
|
&payload_deciphered);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
proto_item *mac_ti = NULL;
|
|
|
|
guint32 calculated_digest = 0;
|
|
|
|
gboolean digest_was_calculated = FALSE;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
/* Try to calculate digest so we can check it */
|
|
|
|
if (global_pdcp_check_integrity && p_pdcp_info->maci_present) {
|
|
|
|
calculated_digest = calculate_digest(&pdu_security_settings,
|
|
|
|
tvb_new_subset_length(tvb, 0, header_length),
|
|
|
|
payload_tvb,
|
|
|
|
offset, sdap_length, &digest_was_calculated);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_pdcp_info->plane == NR_SIGNALING_PLANE) {
|
|
|
|
/* Compute payload length (no MAC on common control Bearers) */
|
|
|
|
guint32 data_length = tvb_reported_length_remaining(payload_tvb, offset)-4;
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* RRC data is all but last 4 bytes.
|
|
|
|
Call nr-rrc dissector (according to direction and Bearer type) if we have valid data */
|
2020-12-29 13:55:35 +00:00
|
|
|
if ((global_pdcp_dissect_signalling_plane_as_rrc) &&
|
2021-02-28 18:09:54 +00:00
|
|
|
((pdu_security == NULL) || (pdu_security->ciphering == nea0) || payload_deciphered ||
|
|
|
|
p_pdcp_info->ciphering_disabled || !pdu_security->seen_next_ul_pdu)) {
|
2020-12-29 13:55:35 +00:00
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Get appropriate dissector handle */
|
2020-02-01 09:36:41 +00:00
|
|
|
dissector_handle_t rrc_handle = lookup_rrc_dissector_handle(p_pdcp_info, data_length);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2020-02-01 09:36:41 +00:00
|
|
|
if (rrc_handle != NULL) {
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Call RRC dissector if have one */
|
|
|
|
tvbuff_t *rrc_payload_tvb = tvb_new_subset_length(payload_tvb, offset, data_length);
|
|
|
|
gboolean was_writable = col_get_writable(pinfo->cinfo, COL_INFO);
|
|
|
|
|
|
|
|
/* We always want to see this in the info column */
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, TRUE);
|
|
|
|
|
2021-02-28 18:09:54 +00:00
|
|
|
/* N.B. Have seen some cases where RRC dissector throws an exception and doesn't return here, or show as malformed... */
|
|
|
|
/* Have attempted to TRY CATCH etc, but with no joy */
|
2018-04-05 12:04:06 +00:00
|
|
|
call_dissector_only(rrc_handle, rrc_payload_tvb, pinfo, pdcp_tree, NULL);
|
|
|
|
|
|
|
|
/* Restore to whatever it was */
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, was_writable);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Just show data */
|
2020-12-29 13:55:35 +00:00
|
|
|
proto_tree_add_item(pdcp_tree, hf_pdcp_nr_signalling_data, payload_tvb, offset,
|
|
|
|
data_length, ENC_NA);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!PINFO_FD_VISITED(pinfo) &&
|
|
|
|
(current_security != NULL) && !current_security->seen_next_ul_pdu &&
|
|
|
|
p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK)
|
|
|
|
{
|
|
|
|
/* i.e. we have already seen SecurityModeResponse! */
|
|
|
|
current_security->seen_next_ul_pdu = TRUE;
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Just show as unparsed data */
|
|
|
|
proto_tree_add_item(pdcp_tree, hf_pdcp_nr_signalling_data, payload_tvb, offset,
|
|
|
|
data_length, ENC_NA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tvb_captured_length_remaining(payload_tvb, offset)) {
|
2018-05-24 15:07:18 +00:00
|
|
|
/* User-plane payload here. */
|
|
|
|
gint payload_length = tvb_reported_length_remaining(payload_tvb, offset) - ((p_pdcp_info->maci_present) ? 4 : 0);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
if (sdap_length) {
|
2020-09-25 12:47:08 +00:00
|
|
|
|
|
|
|
/* SDAP */
|
2018-06-04 20:17:52 +00:00
|
|
|
proto_item *sdap_ti;
|
|
|
|
proto_tree *sdap_tree;
|
2020-09-25 12:47:08 +00:00
|
|
|
guint32 qfi;
|
2018-06-04 20:17:52 +00:00
|
|
|
|
2020-09-25 12:47:08 +00:00
|
|
|
/* Protocol subtree */
|
2018-06-04 20:17:52 +00:00
|
|
|
sdap_ti = proto_tree_add_item(pdcp_tree, proto_sdap, payload_tvb, offset, 1, ENC_NA);
|
|
|
|
sdap_tree = proto_item_add_subtree(sdap_ti, ett_sdap);
|
|
|
|
if (p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK) {
|
2020-09-25 12:47:08 +00:00
|
|
|
gboolean data_control;
|
|
|
|
proto_tree_add_item_ret_boolean(sdap_tree, hf_sdap_data_control, payload_tvb, offset, 1, ENC_NA, &data_control);
|
2018-06-04 20:17:52 +00:00
|
|
|
proto_tree_add_item(sdap_tree, hf_sdap_reserved, payload_tvb, offset, 1, ENC_NA);
|
2020-09-25 12:47:08 +00:00
|
|
|
proto_item_append_text(sdap_ti, " (%s", tfs_get_string(data_control, &pdu_type_bit));
|
2018-06-04 20:17:52 +00:00
|
|
|
} else {
|
2020-09-25 12:47:08 +00:00
|
|
|
gboolean rdi, rqi;
|
|
|
|
proto_tree_add_item_ret_boolean(sdap_tree, hf_sdap_rdi, payload_tvb, offset, 1, ENC_NA, &rdi);
|
|
|
|
proto_tree_add_item_ret_boolean(sdap_tree, hf_sdap_rqi, payload_tvb, offset, 1, ENC_NA, &rqi);
|
|
|
|
proto_item_append_text(sdap_ti, " (RDI=%s, RQI=%s",
|
|
|
|
tfs_get_string(rdi, &sdap_rdi), tfs_get_string(rqi, &sdap_rqi));
|
2018-06-04 20:17:52 +00:00
|
|
|
}
|
2020-09-25 12:47:08 +00:00
|
|
|
proto_tree_add_item_ret_uint(sdap_tree, hf_sdap_qfi, payload_tvb, offset, 1, ENC_NA, &qfi);
|
2018-06-04 20:17:52 +00:00
|
|
|
offset++;
|
2020-09-25 12:47:08 +00:00
|
|
|
proto_item_append_text(sdap_ti, " QFI=%u)", qfi);
|
2018-06-04 20:17:52 +00:00
|
|
|
payload_length--;
|
|
|
|
}
|
|
|
|
|
2018-06-20 12:30:51 +00:00
|
|
|
if (payload_length > 0) {
|
|
|
|
/* If not compressed with ROHC, show as user-plane data */
|
|
|
|
if (!p_pdcp_info->rohc.rohc_compression) {
|
2021-01-09 00:30:00 +00:00
|
|
|
/* Not attempting to decode payload if payload ciphered and we did decipher */
|
|
|
|
if (global_pdcp_dissect_user_plane_as_ip &&
|
|
|
|
((pdu_security == NULL) || (pdu_security->ciphering == nea0) || payload_deciphered)) {
|
|
|
|
|
2018-06-18 21:38:17 +00:00
|
|
|
tvbuff_t *ip_payload_tvb = tvb_new_subset_length(payload_tvb, offset, payload_length);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Don't update info column for ROHC unless configured to */
|
|
|
|
if (global_pdcp_nr_layer_to_show != ShowTrafficLayer) {
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (tvb_get_guint8(ip_payload_tvb, 0) & 0xf0) {
|
|
|
|
case 0x40:
|
|
|
|
call_dissector_only(ip_handle, ip_payload_tvb, pinfo, pdcp_tree, NULL);
|
|
|
|
break;
|
|
|
|
case 0x60:
|
|
|
|
call_dissector_only(ipv6_handle, ip_payload_tvb, pinfo, pdcp_tree, NULL);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
call_data_dissector(ip_payload_tvb, pinfo, pdcp_tree);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Freeze the columns again because we don't want other layers writing to info */
|
|
|
|
if (global_pdcp_nr_layer_to_show == ShowTrafficLayer) {
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
2018-05-24 15:07:18 +00:00
|
|
|
proto_tree_add_item(pdcp_tree, hf_pdcp_nr_user_plane_data, payload_tvb, offset, payload_length, ENC_NA);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-24 15:07:18 +00:00
|
|
|
else {
|
2018-06-20 12:30:51 +00:00
|
|
|
/***************************/
|
|
|
|
/* ROHC packets */
|
|
|
|
/***************************/
|
|
|
|
|
|
|
|
/* Only attempt ROHC if configured to */
|
|
|
|
if (!global_pdcp_dissect_rohc) {
|
|
|
|
col_append_fstr(pinfo->cinfo, COL_PROTOCOL, "|ROHC(%s)",
|
|
|
|
val_to_str_const(p_pdcp_info->rohc.profile, rohc_profile_vals, "Unknown"));
|
|
|
|
proto_tree_add_item(pdcp_tree, hf_pdcp_nr_user_plane_data, payload_tvb, offset, payload_length, ENC_NA);
|
2018-05-24 15:07:18 +00:00
|
|
|
}
|
|
|
|
else {
|
2018-06-20 12:30:51 +00:00
|
|
|
rohc_tvb = tvb_new_subset_length(payload_tvb, offset, payload_length);
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2018-06-20 12:30:51 +00:00
|
|
|
/* Only enable writing to column if configured to show ROHC */
|
|
|
|
if (global_pdcp_nr_layer_to_show != ShowTrafficLayer) {
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, FALSE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
2018-06-20 12:30:51 +00:00
|
|
|
/* Call the ROHC dissector */
|
|
|
|
call_dissector_with_data(rohc_handle, rohc_tvb, pinfo, tree, &p_pdcp_info->rohc);
|
|
|
|
}
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* MAC */
|
2018-05-24 15:07:18 +00:00
|
|
|
if (p_pdcp_info->maci_present) {
|
2018-04-05 12:04:06 +00:00
|
|
|
/* Last 4 bytes are MAC */
|
2020-12-29 13:55:35 +00:00
|
|
|
gint mac_offset = tvb_reported_length(payload_tvb)-4;
|
2018-04-05 12:04:06 +00:00
|
|
|
guint32 mac = tvb_get_ntohl(payload_tvb, mac_offset);
|
2020-12-29 13:55:35 +00:00
|
|
|
mac_ti = proto_tree_add_item(pdcp_tree, hf_pdcp_nr_mac, payload_tvb, mac_offset, 4, ENC_BIG_ENDIAN);
|
|
|
|
offset += 4;
|
|
|
|
|
|
|
|
if (digest_was_calculated) {
|
|
|
|
/* Compare what was found with calculated value! */
|
|
|
|
if (mac != calculated_digest) {
|
|
|
|
expert_add_info_format(pinfo, mac_ti, &ei_pdcp_nr_digest_wrong,
|
|
|
|
"MAC-I Digest wrong - calculated %08x but found %08x",
|
|
|
|
calculated_digest, mac);
|
|
|
|
proto_item_append_text(mac_ti, " (but calculated %08x !)", calculated_digest);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
proto_item_append_text(mac_ti, " [Matches calculated result]");
|
|
|
|
}
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, " MAC=0x%08x", mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Let RLC write to columns again */
|
|
|
|
col_set_writable(pinfo->cinfo, COL_INFO, global_pdcp_nr_layer_to_show == ShowRLCLayer);
|
|
|
|
|
|
|
|
return tvb_captured_length(tvb);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void proto_register_pdcp_nr(void)
|
|
|
|
{
|
2018-06-04 20:17:52 +00:00
|
|
|
static hf_register_info hf_pdcp[] =
|
2018-04-05 12:04:06 +00:00
|
|
|
{
|
|
|
|
{ &hf_pdcp_nr_configuration,
|
|
|
|
{ "Configuration",
|
|
|
|
"pdcp-nr.configuration", FT_STRING, BASE_NONE, NULL, 0x0,
|
|
|
|
"Configuration info passed into dissector", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_direction,
|
|
|
|
{ "Direction",
|
|
|
|
"pdcp-nr.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0,
|
|
|
|
"Direction of message", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_ueid,
|
|
|
|
{ "UE",
|
|
|
|
"pdcp-nr.ueid", FT_UINT16, BASE_DEC, 0, 0x0,
|
|
|
|
"UE Identifier", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_bearer_type,
|
|
|
|
{ "Bearer type",
|
|
|
|
"pdcp-nr.Bearer-type", FT_UINT8, BASE_DEC, VALS(bearer_type_vals), 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_bearer_id,
|
|
|
|
{ "Bearer Id",
|
|
|
|
"pdcp-nr.bearer-id", FT_UINT8, BASE_DEC, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_plane,
|
|
|
|
{ "Plane",
|
|
|
|
"pdcp-nr.plane", FT_UINT8, BASE_DEC, VALS(pdcp_plane_vals), 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_seqnum_length,
|
|
|
|
{ "Seqnum length",
|
|
|
|
"pdcp-nr.seqnum_length", FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
"Sequence Number Length", HFILL
|
|
|
|
}
|
|
|
|
},
|
2020-12-29 13:55:35 +00:00
|
|
|
{ &hf_pdcp_nr_maci_present,
|
|
|
|
{ "MAC-I Present",
|
|
|
|
"pdcp-nr.maci_present", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
|
|
|
"Indicates whether MAC-I digest bytes are expected", HFILL
|
|
|
|
}
|
|
|
|
},
|
2020-09-25 12:47:08 +00:00
|
|
|
{ &hf_pdcp_nr_sdap,
|
|
|
|
{ "SDAP header",
|
|
|
|
"pdcp-nr.sdap", FT_BOOLEAN, 8, TFS(&tfs_present_not_present), 0x0,
|
|
|
|
"Indicates whether SDAP appears after PDCP headers", HFILL
|
|
|
|
}
|
|
|
|
},
|
2021-02-28 18:09:54 +00:00
|
|
|
{ &hf_pdcp_nr_ciphering_disabled,
|
|
|
|
{ "Ciphering disabled",
|
|
|
|
"pdcp-nr.ciphering-disabled", FT_BOOLEAN, 8, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
{ &hf_pdcp_nr_rohc_compression,
|
|
|
|
{ "ROHC Compression",
|
|
|
|
"pdcp-nr.rohc.compression", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_rohc_mode,
|
|
|
|
{ "ROHC Mode",
|
|
|
|
"pdcp-nr.rohc.mode", FT_UINT8, BASE_DEC, VALS(rohc_mode_vals), 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_rohc_rnd,
|
|
|
|
{ "RND",
|
2018-04-23 14:38:12 +00:00
|
|
|
"pdcp-nr.rohc.rnd", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
2018-04-05 12:04:06 +00:00
|
|
|
"RND of outer ip header", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_rohc_udp_checksum_present,
|
|
|
|
{ "UDP Checksum",
|
2018-04-23 14:38:12 +00:00
|
|
|
"pdcp-nr.rohc.checksum-present", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
2018-04-05 12:04:06 +00:00
|
|
|
"UDP Checksum present", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_rohc_profile,
|
|
|
|
{ "ROHC profile",
|
|
|
|
"pdcp-nr.rohc.profile", FT_UINT8, BASE_DEC, VALS(rohc_profile_vals), 0x0,
|
|
|
|
"ROHC Mode", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_cid_inclusion_info,
|
|
|
|
{ "CID Inclusion Info",
|
2018-04-23 14:38:12 +00:00
|
|
|
"pdcp-nr.cid-inclusion-info", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
2018-04-05 12:04:06 +00:00
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_large_cid_present,
|
|
|
|
{ "Large CID Present",
|
2018-04-23 14:38:12 +00:00
|
|
|
"pdcp-nr.large-cid-present", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
2018-04-05 12:04:06 +00:00
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
{ &hf_pdcp_nr_control_plane_reserved,
|
|
|
|
{ "Reserved",
|
|
|
|
"pdcp-nr.reserved", FT_UINT8, BASE_DEC, NULL, 0xf0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_reserved3,
|
|
|
|
{ "Reserved",
|
|
|
|
"pdcp-nr.reserved3", FT_UINT8, BASE_HEX, NULL, 0x70,
|
|
|
|
"3 reserved bits", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_seq_num_12,
|
|
|
|
{ "Seq Num",
|
|
|
|
"pdcp-nr.seq-num", FT_UINT16, BASE_DEC, NULL, 0x0fff,
|
|
|
|
"PDCP Seq num", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_reserved5,
|
|
|
|
{ "Reserved",
|
|
|
|
"pdcp-nr.reserved5", FT_UINT8, BASE_HEX, NULL, 0x7c,
|
|
|
|
"5 reserved bits", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_seq_num_18,
|
|
|
|
{ "Seq Num",
|
2021-07-29 09:40:15 +00:00
|
|
|
"pdcp-nr.seq-num", FT_UINT24, BASE_DEC, NULL, 0x03ffff,
|
2018-04-05 12:04:06 +00:00
|
|
|
"PDCP Seq num", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_signalling_data,
|
|
|
|
{ "Signalling Data",
|
|
|
|
"pdcp-nr.signalling-data", FT_BYTES, BASE_NONE, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_mac,
|
|
|
|
{ "MAC",
|
|
|
|
"pdcp-nr.mac", FT_UINT32, BASE_HEX, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_data_control,
|
|
|
|
{ "PDU Type",
|
|
|
|
"pdcp-nr.pdu-type", FT_BOOLEAN, 8, TFS(&pdu_type_bit), 0x80,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_user_plane_data,
|
|
|
|
{ "User-Plane Data",
|
|
|
|
"pdcp-nr.user-data", FT_BYTES, BASE_NONE, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_control_pdu_type,
|
|
|
|
{ "Control PDU Type",
|
|
|
|
"pdcp-nr.control-pdu-type", FT_UINT8, BASE_HEX, VALS(control_pdu_type_vals), 0x70,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_fmc,
|
|
|
|
{ "First Missing Count",
|
|
|
|
"pdcp-nr.fmc", FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_reserved4,
|
|
|
|
{ "Reserved",
|
|
|
|
"pdcp-nr.reserved4", FT_UINT8, BASE_HEX, NULL, 0x0f,
|
|
|
|
"4 reserved bits", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_bitmap,
|
|
|
|
{ "Bitmap",
|
|
|
|
"pdcp-nr.bitmap", FT_NONE, BASE_NONE, NULL, 0x0,
|
|
|
|
"Status report bitmap (0=error, 1=OK)", HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_bitmap_byte,
|
|
|
|
{ "Bitmap byte",
|
|
|
|
"pdcp-nr.bitmap.byte", FT_UINT8, BASE_HEX, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
{ &hf_pdcp_nr_sequence_analysis,
|
|
|
|
{ "Sequence Analysis",
|
|
|
|
"pdcp-nr.sequence-analysis", FT_STRING, BASE_NONE, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_sequence_analysis_ok,
|
|
|
|
{ "OK",
|
|
|
|
"pdcp-nr.sequence-analysis.ok", FT_BOOLEAN, BASE_NONE, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_sequence_analysis_previous_frame,
|
|
|
|
{ "Previous frame for Bearer",
|
|
|
|
"pdcp-nr.sequence-analysis.previous-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_sequence_analysis_next_frame,
|
|
|
|
{ "Next frame for Bearer",
|
|
|
|
"pdcp-nr.sequence-analysis.next-frame", FT_FRAMENUM, BASE_NONE, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_sequence_analysis_expected_sn,
|
|
|
|
{ "Expected SN",
|
|
|
|
"pdcp-nr.sequence-analysis.expected-sn", FT_UINT32, BASE_DEC, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_sequence_analysis_skipped,
|
|
|
|
{ "Skipped frames",
|
|
|
|
"pdcp-nr.sequence-analysis.skipped-frames", FT_BOOLEAN, BASE_NONE, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_sequence_analysis_repeated,
|
|
|
|
{ "Repeated frame",
|
|
|
|
"pdcp-nr.sequence-analysis.repeated-frame", FT_BOOLEAN, BASE_NONE, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
2020-12-29 13:55:35 +00:00
|
|
|
|
|
|
|
/* Security fields */
|
|
|
|
{ &hf_pdcp_nr_security,
|
|
|
|
{ "Security Config",
|
|
|
|
"pdcp-nr.security-config", FT_STRING, BASE_NONE, 0, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_setup_frame,
|
|
|
|
{ "Configuration frame",
|
|
|
|
"pdcp-nr.security-config.setup-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_integrity_algorithm,
|
|
|
|
{ "Integrity Algorithm",
|
|
|
|
"pdcp-nr.security-config.integrity", FT_UINT16, BASE_DEC, VALS(integrity_algorithm_vals), 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_ciphering_algorithm,
|
|
|
|
{ "Ciphering Algorithm",
|
|
|
|
"pdcp-nr.security-config.ciphering", FT_UINT16, BASE_DEC, VALS(ciphering_algorithm_vals), 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_bearer,
|
|
|
|
{ "BEARER",
|
|
|
|
"pdcp-nr.security-config.bearer", FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_direction,
|
|
|
|
{ "DIRECTION",
|
|
|
|
"pdcp-nr.security-config.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_count,
|
|
|
|
{ "COUNT",
|
|
|
|
"pdcp-nr.security-config.count", FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_cipher_key,
|
|
|
|
{ "CIPHER KEY",
|
|
|
|
"pdcp-nr.security-config.cipher-key", FT_STRING, BASE_NONE, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_pdcp_nr_security_integrity_key,
|
|
|
|
{ "INTEGRITY KEY",
|
|
|
|
"pdcp-nr.security-config.integrity-key", FT_STRING, BASE_NONE, NULL, 0x0,
|
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
}
|
2018-04-05 12:04:06 +00:00
|
|
|
};
|
|
|
|
|
2018-06-04 20:17:52 +00:00
|
|
|
static hf_register_info hf_sdap[] =
|
|
|
|
{
|
|
|
|
{ &hf_sdap_rdi,
|
|
|
|
{ "RDI",
|
|
|
|
"sdap.rdi", FT_BOOLEAN, 8, TFS(&sdap_rdi), 0x80,
|
2020-09-24 15:01:29 +00:00
|
|
|
"Reflective QoS flow to DRB mapping Indication", HFILL
|
2018-06-04 20:17:52 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_sdap_rqi,
|
|
|
|
{ "RQI",
|
|
|
|
"sdap.rqi", FT_BOOLEAN, 8, TFS(&sdap_rqi), 0x40,
|
2020-09-24 15:01:29 +00:00
|
|
|
"Reflective QoS Indication", HFILL
|
2018-06-04 20:17:52 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{ &hf_sdap_qfi,
|
|
|
|
{ "QFI",
|
|
|
|
"sdap.qfi", FT_UINT8, BASE_DEC, NULL, 0x3f,
|
2020-09-24 15:01:29 +00:00
|
|
|
"QoS Flow ID", HFILL
|
2018-06-04 20:17:52 +00:00
|
|
|
}
|
|
|
|
},
|
2018-06-20 12:30:51 +00:00
|
|
|
{ &hf_sdap_data_control,
|
|
|
|
{ "PDU Type",
|
2020-09-24 15:01:29 +00:00
|
|
|
"sdap.pdu-type", FT_BOOLEAN, 8, TFS(&pdu_type_bit), 0x80,
|
2018-06-20 12:30:51 +00:00
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
},
|
2018-06-04 20:17:52 +00:00
|
|
|
{ &hf_sdap_reserved,
|
|
|
|
{ "Reserved",
|
2018-06-20 12:30:51 +00:00
|
|
|
"sdap.reserved", FT_UINT8, BASE_HEX, NULL, 0x40,
|
2018-06-04 20:17:52 +00:00
|
|
|
NULL, HFILL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
static gint *ett[] =
|
|
|
|
{
|
|
|
|
&ett_pdcp,
|
|
|
|
&ett_pdcp_configuration,
|
|
|
|
&ett_pdcp_packet,
|
|
|
|
&ett_pdcp_nr_sequence_analysis,
|
2018-06-04 20:17:52 +00:00
|
|
|
&ett_pdcp_report_bitmap,
|
2020-12-29 13:55:35 +00:00
|
|
|
&ett_sdap,
|
|
|
|
&ett_pdcp_security
|
2018-04-05 12:04:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static ei_register_info ei[] = {
|
|
|
|
{ &ei_pdcp_nr_sequence_analysis_sn_missing, { "pdcp-nr.sequence-analysis.sn-missing", PI_SEQUENCE, PI_WARN, "PDCP SN missing", EXPFILL }},
|
|
|
|
{ &ei_pdcp_nr_sequence_analysis_sn_repeated, { "pdcp-nr.sequence-analysis.sn-repeated", PI_SEQUENCE, PI_WARN, "PDCP SN repeated", EXPFILL }},
|
|
|
|
{ &ei_pdcp_nr_sequence_analysis_wrong_sequence_number, { "pdcp-nr.sequence-analysis.wrong-sequence-number", PI_SEQUENCE, PI_WARN, "Wrong Sequence Number", EXPFILL }},
|
|
|
|
{ &ei_pdcp_nr_reserved_bits_not_zero, { "pdcp-nr.reserved-bits-not-zero", PI_MALFORMED, PI_ERROR, "Reserved bits not zero", EXPFILL }},
|
2020-12-29 13:55:35 +00:00
|
|
|
{ &ei_pdcp_nr_digest_wrong, { "pdcp-nr.maci-wrong", PI_SEQUENCE, PI_ERROR, "MAC-I doesn't match expected value", EXPFILL }},
|
2018-04-12 13:48:12 +00:00
|
|
|
{ &ei_pdcp_nr_unknown_udp_framing_tag, { "pdcp-nr.unknown-udp-framing-tag", PI_UNDECODED, PI_WARN, "Unknown UDP framing tag, aborting dissection", EXPFILL }},
|
|
|
|
{ &ei_pdcp_nr_missing_udp_framing_tag, { "pdcp-nr.missing-udp-framing-tag", PI_UNDECODED, PI_WARN, "Missing UDP framing conditional tag, aborting dissection", EXPFILL }}
|
2018-04-05 12:04:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const enum_val_t sequence_analysis_vals[] = {
|
|
|
|
{"no-analysis", "No-Analysis", FALSE},
|
|
|
|
{"rlc-only", "Only-RLC-frames", SEQUENCE_ANALYSIS_RLC_ONLY},
|
|
|
|
{"pdcp-only", "Only-PDCP-frames", SEQUENCE_ANALYSIS_PDCP_ONLY},
|
|
|
|
{NULL, NULL, -1}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const enum_val_t show_info_col_vals[] = {
|
|
|
|
{"show-rlc", "RLC Info", ShowRLCLayer},
|
|
|
|
{"show-pdcp", "PDCP Info", ShowPDCPLayer},
|
|
|
|
{"show-traffic", "Traffic Info", ShowTrafficLayer},
|
|
|
|
{NULL, NULL, -1}
|
|
|
|
};
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
static const enum_val_t default_ciphering_algorithm_vals[] = {
|
|
|
|
{"nea0", "NEA0 (NULL)", nea0},
|
|
|
|
{"nea1", "NEA1 (SNOW3G)", nea1},
|
|
|
|
{"nea2", "NEA2 (AES)", nea2},
|
|
|
|
{"nea3", "NEA3 (ZUC)", nea3},
|
|
|
|
{NULL, NULL, -1}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const enum_val_t default_integrity_algorithm_vals[] = {
|
|
|
|
{"nia0", "NIA0 (NULL)", nia0},
|
|
|
|
{"nia1", "NIA1 (SNOW3G)", nia1},
|
|
|
|
{"nia2", "NIA2 (AES)", nia2},
|
|
|
|
{"nia3", "NIA3 (ZUC)", nia3},
|
|
|
|
{NULL, NULL, -1}
|
|
|
|
};
|
|
|
|
|
|
|
|
static uat_field_t ue_keys_uat_flds[] = {
|
|
|
|
UAT_FLD_DEC(uat_ue_keys_records, ueid, "UEId", "UE Identifier of UE associated with keys"),
|
2021-01-01 22:19:28 +00:00
|
|
|
UAT_FLD_CSTRING(uat_ue_keys_records, rrcCipherKeyString, "RRC Cipher Key", "Key for deciphering signalling messages"),
|
|
|
|
UAT_FLD_CSTRING(uat_ue_keys_records, upCipherKeyString, "User-Plane Cipher Key", "Key for deciphering user-plane messages"),
|
|
|
|
UAT_FLD_CSTRING(uat_ue_keys_records, rrcIntegrityKeyString, "RRC Integrity Key", "Key for calculating signalling integrity MAC"),
|
|
|
|
UAT_FLD_CSTRING(uat_ue_keys_records, upIntegrityKeyString, "User-Plane Integrity Key", "Key for calculating user-plane integrity MAC"),
|
2020-12-29 13:55:35 +00:00
|
|
|
UAT_END_FIELDS
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
module_t *pdcp_nr_module;
|
|
|
|
expert_module_t* expert_pdcp_nr;
|
|
|
|
|
|
|
|
/* Register protocol. */
|
|
|
|
proto_pdcp_nr = proto_register_protocol("PDCP-NR", "PDCP-NR", "pdcp-nr");
|
2018-06-04 20:17:52 +00:00
|
|
|
proto_register_field_array(proto_pdcp_nr, hf_pdcp, array_length(hf_pdcp));
|
2018-04-05 12:04:06 +00:00
|
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
|
|
expert_pdcp_nr = expert_register_protocol(proto_pdcp_nr);
|
|
|
|
expert_register_field_array(expert_pdcp_nr, ei, array_length(ei));
|
2018-06-04 20:17:52 +00:00
|
|
|
proto_sdap = proto_register_protocol("SDAP", "SDAP", "sdap");
|
|
|
|
proto_register_field_array(proto_sdap, hf_sdap, array_length(hf_sdap));
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
/* Allow other dissectors to find this one by name. */
|
|
|
|
register_dissector("pdcp-nr", dissect_pdcp_nr, proto_pdcp_nr);
|
|
|
|
|
|
|
|
pdcp_nr_module = prefs_register_protocol(proto_pdcp_nr, NULL);
|
|
|
|
|
|
|
|
/* Dissect uncompressed user-plane data as IP */
|
|
|
|
prefs_register_bool_preference(pdcp_nr_module, "show_user_plane_as_ip",
|
|
|
|
"Show uncompressed User-Plane data as IP",
|
|
|
|
"Show uncompressed User-Plane data as IP",
|
|
|
|
&global_pdcp_dissect_user_plane_as_ip);
|
|
|
|
|
|
|
|
/* Dissect unciphered signalling data as RRC */
|
|
|
|
prefs_register_bool_preference(pdcp_nr_module, "show_signalling_plane_as_rrc",
|
|
|
|
"Show unciphered Signalling-Plane data as RRC",
|
|
|
|
"Show unciphered Signalling-Plane data as RRC",
|
|
|
|
&global_pdcp_dissect_signalling_plane_as_rrc);
|
|
|
|
|
|
|
|
/* Check for missing sequence numbers */
|
|
|
|
prefs_register_enum_preference(pdcp_nr_module, "check_sequence_numbers",
|
|
|
|
"Do sequence number analysis",
|
|
|
|
"Do sequence number analysis",
|
|
|
|
&global_pdcp_check_sequence_numbers, sequence_analysis_vals, FALSE);
|
|
|
|
|
|
|
|
/* Attempt to dissect ROHC messages */
|
|
|
|
prefs_register_bool_preference(pdcp_nr_module, "dissect_rohc",
|
|
|
|
"Attempt to decode ROHC data",
|
|
|
|
"Attempt to decode ROHC data",
|
|
|
|
&global_pdcp_dissect_rohc);
|
|
|
|
|
|
|
|
prefs_register_obsolete_preference(pdcp_nr_module, "heuristic_pdcp_nr_over_udp");
|
|
|
|
|
|
|
|
prefs_register_enum_preference(pdcp_nr_module, "layer_to_show",
|
|
|
|
"Which layer info to show in Info column",
|
|
|
|
"Can show RLC, PDCP or Traffic layer info in Info column",
|
|
|
|
&global_pdcp_nr_layer_to_show, show_info_col_vals, FALSE);
|
|
|
|
|
2020-12-29 13:55:35 +00:00
|
|
|
ue_keys_uat = uat_new("PDCP UE security keys",
|
|
|
|
sizeof(uat_ue_keys_record_t), /* record size */
|
|
|
|
"pdcp_nr_ue_keys", /* filename */
|
|
|
|
TRUE, /* from_profile */
|
|
|
|
&uat_ue_keys_records, /* data_ptr */
|
|
|
|
&num_ue_keys_uat, /* numitems_ptr */
|
|
|
|
UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */
|
|
|
|
NULL, /* help */
|
|
|
|
uat_ue_keys_record_copy_cb, /* copy callback */
|
|
|
|
uat_ue_keys_record_update_cb, /* update callback */
|
|
|
|
uat_ue_keys_record_free_cb, /* free callback */
|
|
|
|
NULL, /* post update callback */
|
|
|
|
NULL, /* reset callback */
|
|
|
|
ue_keys_uat_flds); /* UAT field definitions */
|
|
|
|
|
|
|
|
prefs_register_uat_preference(pdcp_nr_module,
|
|
|
|
"ue_keys_table",
|
|
|
|
"PDCP UE Keys",
|
|
|
|
"Preconfigured PDCP keys",
|
|
|
|
ue_keys_uat);
|
|
|
|
|
|
|
|
prefs_register_enum_preference(pdcp_nr_module, "default_ciphering_algorithm",
|
|
|
|
"Ciphering algorithm to use if not signalled",
|
|
|
|
"If RRC Security Info not seen, e.g. in Handover",
|
|
|
|
(gint*)&global_default_ciphering_algorithm, default_ciphering_algorithm_vals, FALSE);
|
|
|
|
|
|
|
|
prefs_register_enum_preference(pdcp_nr_module, "default_integrity_algorithm",
|
|
|
|
"Integrity algorithm to use if not signalled",
|
|
|
|
"If RRC Security Info not seen, e.g. in Handover",
|
|
|
|
(gint*)&global_default_integrity_algorithm, default_integrity_algorithm_vals, FALSE);
|
|
|
|
|
|
|
|
/* Attempt to decipher RRC messages */
|
|
|
|
prefs_register_bool_preference(pdcp_nr_module, "decipher_signalling",
|
|
|
|
"Attempt to decipher Signalling (RRC) SDUs",
|
|
|
|
"N.B. only possible if build with algorithm support, and have key available and configured",
|
|
|
|
&global_pdcp_decipher_signalling);
|
|
|
|
|
|
|
|
/* Attempt to decipher user-plane messages */
|
|
|
|
prefs_register_bool_preference(pdcp_nr_module, "decipher_userplane",
|
|
|
|
"Attempt to decipher User-plane (IP) SDUs",
|
|
|
|
"N.B. only possible if build with algorithm support, and have key available and configured",
|
|
|
|
&global_pdcp_decipher_userplane);
|
|
|
|
|
|
|
|
/* Attempt to verify RRC integrity/authentication digest */
|
|
|
|
prefs_register_bool_preference(pdcp_nr_module, "verify_integrity",
|
|
|
|
"Attempt to check integrity calculation",
|
|
|
|
"N.B. only possible if build with algorithm support, and have key available and configured",
|
|
|
|
&global_pdcp_check_integrity);
|
|
|
|
|
|
|
|
|
|
|
|
prefs_register_bool_preference(pdcp_nr_module, "ignore_rrc_sec_params",
|
|
|
|
"Ignore RRC security parameters",
|
|
|
|
"Ignore the NR RRC security algorithm configuration, to be used when PDCP is already deciphered in the capture",
|
|
|
|
&global_pdcp_ignore_sec);
|
|
|
|
|
2018-04-05 12:04:06 +00:00
|
|
|
|
|
|
|
pdcp_sequence_analysis_bearer_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal);
|
|
|
|
pdcp_nr_sequence_analysis_report_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_result_hash_func, pdcp_result_hash_equal);
|
2020-12-29 13:55:35 +00:00
|
|
|
pdcp_security_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal);
|
|
|
|
pdcp_security_result_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_nr_ueid_frame_hash_func, pdcp_nr_ueid_frame_hash_equal);
|
|
|
|
pdcp_security_key_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void proto_reg_handoff_pdcp_nr(void)
|
|
|
|
{
|
|
|
|
/* Add as a heuristic UDP dissector */
|
|
|
|
heur_dissector_add("udp", dissect_pdcp_nr_heur, "PDCP-NR over UDP", "pdcp_nr_udp", proto_pdcp_nr, HEURISTIC_DISABLE);
|
|
|
|
|
|
|
|
ip_handle = find_dissector_add_dependency("ip", proto_pdcp_nr);
|
|
|
|
ipv6_handle = find_dissector_add_dependency("ipv6", proto_pdcp_nr);
|
|
|
|
rohc_handle = find_dissector_add_dependency("rohc", proto_pdcp_nr);
|
2020-02-01 09:36:41 +00:00
|
|
|
nr_rrc_ul_ccch = find_dissector_add_dependency("nr-rrc.ul.ccch", proto_pdcp_nr);
|
|
|
|
nr_rrc_ul_ccch1 = find_dissector_add_dependency("nr-rrc.ul.ccch1", proto_pdcp_nr);
|
|
|
|
nr_rrc_dl_ccch = find_dissector_add_dependency("nr-rrc.dl.ccch", proto_pdcp_nr);
|
|
|
|
nr_rrc_pcch = find_dissector_add_dependency("nr-rrc.pcch", proto_pdcp_nr);
|
|
|
|
nr_rrc_bcch_bch = find_dissector_add_dependency("nr-rrc.bcch.bch", proto_pdcp_nr);
|
|
|
|
nr_rrc_bcch_dl_sch = find_dissector_add_dependency("nr-rrc.bcch.dl.sch", proto_pdcp_nr);
|
|
|
|
nr_rrc_ul_dcch = find_dissector_add_dependency("nr-rrc.ul.dcch", proto_pdcp_nr);
|
|
|
|
nr_rrc_dl_dcch = find_dissector_add_dependency("nr-rrc.dl.dcch", proto_pdcp_nr);
|
2018-04-05 12:04:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Editor modelines
|
|
|
|
*
|
|
|
|
* Local Variables:
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* ex: set shiftwidth=4 tabstop=8 expandtab:
|
|
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
|
|
*/
|