/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr

  Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.

 *******************************************************************************/
/*****************************************************************************
Source		usim_data.c

Version		0.1

Date		2012/10/31

Product		USIM data generator

Subsystem	USIM data generator main process

Author		Frederic Maurel

Description	Implements the utility used to generate data stored in the
		USIM application

*****************************************************************************/

#include "network.h"

#include "usim_api.h"
#include "memory.h"

#include <stdio.h>	// perror, printf
#include <stdlib.h>	// exit
#include <string.h>	// memset, memcpy, strncpy

/****************************************************************************/
/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
/****************************************************************************/

#define KSI			USIM_KSI_NOT_AVAILABLE
#define KSI_ASME		USIM_KSI_NOT_AVAILABLE
#define INT_ALGO		USIM_INT_EIA1
#define ENC_ALGO		USIM_ENC_EEA1
#define SECURITY_ALGORITHMS	(ENC_ALGO | INT_ALGO)

#define MIN_TAC			0x0000
#define MAX_TAC			0xFFFE

#define DEFAULT_TMSI		0x0000000D
#define DEFAULT_P_TMSI		0x0000000D
#define DEFAULT_M_TMSI		0x0000000D
#define DEFAULT_LAC		0xFFFE
#define DEFAULT_RAC		0x01
#define DEFAULT_TAC		0x0001

#define DEFAULT_MME_ID		0x0102
#define DEFAULT_MME_CODE	0x0F

#define PRINT_PLMN_DIGIT(d)	if ((d) != 0xf) printf("%u", (d))

#define PRINT_PLMN(plmn)		\
    PRINT_PLMN_DIGIT((plmn).MCCdigit1);	\
    PRINT_PLMN_DIGIT((plmn).MCCdigit2);	\
    PRINT_PLMN_DIGIT((plmn).MCCdigit3);	\
    PRINT_PLMN_DIGIT((plmn).MNCdigit1);	\
    PRINT_PLMN_DIGIT((plmn).MNCdigit2);	\
    PRINT_PLMN_DIGIT((plmn).MNCdigit3)

/****************************************************************************/
/*******************  L O C A L    D E F I N I T I O N S  *******************/
/****************************************************************************/

static void _display_usage(const char* command);

static void _display_usim_data(const usim_data_t* data);

/****************************************************************************/
/******************  E X P O R T E D    F U N C T I O N S  ******************/
/****************************************************************************/

int main (int argc, const char* argv[])
{
    int rc;
    usim_data_t usim_data;

    unsigned char gen_data;

    /*
     * Read command line parameters
     */
    if (argc != 2) {
	fprintf(stderr, "Invalid parameter\n");
	_display_usage(argv[0]);
	exit(EXIT_FAILURE);
    }
    else if ( (strcmp(argv[1], "--gen") == 0) ||
	      (strcmp(argv[1], "-g") == 0) ) {
	/* Generate USIM data files */
	gen_data = TRUE;
    }
    else if ( (strcmp(argv[1], "--print") == 0) ||
	      (strcmp(argv[1], "-p") == 0) ) {
	/* Display content of USIM data files */
	gen_data = FALSE;
    }
    else {
	/* Display usage */
	_display_usage(argv[0]);
	exit(EXIT_SUCCESS);
    }

    if (gen_data)
    {
	/*
	 * Initialize USIM data
	 */
	memset(&usim_data, 0, sizeof(usim_data_t));
	/*
	 * International Mobile Subscriber Identity
	 * IMSI = MCC + MNC + MSIN = 208 (France) + 10 (SFR) + 00001234
	 */
	usim_data.imsi.length = 8;
	usim_data.imsi.u.num.parity = EVEN_PARITY;      // Parity: even
	usim_data.imsi.u.num.digit1 = 2;                // MCC digit 1
	usim_data.imsi.u.num.digit2 = 0;                // MCC digit 2
	usim_data.imsi.u.num.digit3 = 8;                // MCC digit 3
	usim_data.imsi.u.num.digit4 = 1;                // MNC digit 1
	usim_data.imsi.u.num.digit5 = 0;                // MNC digit 2
	// LG usim_data.imsi.u.num.digit6 = 0b1111;     // MNC digit 3
    usim_data.imsi.u.num.digit6 = 0;                // MNC digit 3
	usim_data.imsi.u.num.digit7 = 0;
	usim_data.imsi.u.num.digit8 = 0;
	usim_data.imsi.u.num.digit9 = 0;
	usim_data.imsi.u.num.digit10 = 0;
	usim_data.imsi.u.num.digit11 = 1;
	usim_data.imsi.u.num.digit12 = 2;
	usim_data.imsi.u.num.digit13 = 3;
	usim_data.imsi.u.num.digit14 = 4;
	usim_data.imsi.u.num.digit15 = 0b1111;
	/*
	 * Ciphering and Integrity Keys
	 */
	usim_data.keys.ksi = KSI;
	memset(&usim_data.keys.ck, 0, USIM_CK_SIZE);
	memset(&usim_data.keys.ik, 0, USIM_IK_SIZE);

	/*
	 * Higher Priority PLMN search period
	 */
	usim_data.hpplmn = 0x00;	/* Disable timer */
	/*
	 * List of Forbidden PLMNs
	 */
	for (int i = 0; i < USIM_FPLMN_MAX; i++) {
	    memset(&usim_data.fplmn[i], 0xff, sizeof(plmn_t));
	}
	/*
	 * Location Information
	 */
	usim_data.loci.tmsi = DEFAULT_TMSI;
	usim_data.loci.lai.plmn = network_records[SFR1].plmn;
	usim_data.loci.lai.lac = DEFAULT_LAC;
	usim_data.loci.status = USIM_LOCI_NOT_UPDATED;
	/*
	 * Packet Switched Location Information
	 */
	usim_data.psloci.p_tmsi = DEFAULT_P_TMSI;
	usim_data.psloci.signature[0] = 0x01;
	usim_data.psloci.signature[1] = 0x02;
	usim_data.psloci.signature[2] = 0x03;
	usim_data.psloci.rai.plmn = network_records[SFR1].plmn;
	usim_data.psloci.rai.lac = DEFAULT_LAC;
	usim_data.psloci.rai.rac = DEFAULT_RAC;
	usim_data.psloci.status = USIM_PSLOCI_NOT_UPDATED;
	/*
	 * Administrative Data
	 */
	usim_data.ad.UE_Operation_Mode = USIM_NORMAL_MODE;
	usim_data.ad.Additional_Info = 0xffff;
	usim_data.ad.MNC_Length = 2;
	/*
	 * EPS NAS security context
	 */
	usim_data.securityctx.length = 52;
	usim_data.securityctx.KSIasme.type = USIM_KSI_ASME_TAG;
	usim_data.securityctx.KSIasme.length = 1;
	usim_data.securityctx.KSIasme.value[0] = KSI_ASME;
	usim_data.securityctx.Kasme.type = USIM_K_ASME_TAG;
	usim_data.securityctx.Kasme.length = USIM_K_ASME_SIZE;
	memset(usim_data.securityctx.Kasme.value, 0,
	       usim_data.securityctx.Kasme.length);
	usim_data.securityctx.ulNAScount.type = USIM_UL_NAS_COUNT_TAG;
	usim_data.securityctx.ulNAScount.length = USIM_UL_NAS_COUNT_SIZE;
	memset(usim_data.securityctx.ulNAScount.value, 0,
	       usim_data.securityctx.ulNAScount.length);
	usim_data.securityctx.dlNAScount.type = USIM_DL_NAS_COUNT_TAG;
	usim_data.securityctx.dlNAScount.length = USIM_DL_NAS_COUNT_SIZE;
	memset(usim_data.securityctx.dlNAScount.value, 0,
	       usim_data.securityctx.dlNAScount.length);
	usim_data.securityctx.algorithmID.type = USIM_INT_ENC_ALGORITHMS_TAG;
	usim_data.securityctx.algorithmID.length = 1;
	usim_data.securityctx.algorithmID.value[0] = SECURITY_ALGORITHMS;
	/*
	 * Subcriber's Number
	 */
	usim_data.msisdn.length = 7;
	usim_data.msisdn.number.ext = 1;
	usim_data.msisdn.number.ton = MSISDN_TON_UNKNOWKN;
	usim_data.msisdn.number.npi = MSISDN_NPI_ISDN_TELEPHONY;
	usim_data.msisdn.number.digit[0].msb = 3;
	usim_data.msisdn.number.digit[0].lsb = 3;
	usim_data.msisdn.number.digit[1].msb = 6;
	usim_data.msisdn.number.digit[1].lsb = 1;
	usim_data.msisdn.number.digit[2].msb = 1;
	usim_data.msisdn.number.digit[2].lsb = 1;
	usim_data.msisdn.number.digit[3].msb = 2;
	usim_data.msisdn.number.digit[3].lsb = 3;
	usim_data.msisdn.number.digit[4].msb = 4;
	usim_data.msisdn.number.digit[4].lsb = 5;
	usim_data.msisdn.number.digit[5].msb = 6;
	usim_data.msisdn.number.digit[5].lsb = 0xf;
	usim_data.msisdn.number.digit[6].msb = 0xf;
	usim_data.msisdn.number.digit[6].lsb = 0xf;
	usim_data.msisdn.number.digit[7].msb = 0xf;
	usim_data.msisdn.number.digit[7].lsb = 0xf;
	usim_data.msisdn.number.digit[8].msb = 0xf;
	usim_data.msisdn.number.digit[8].lsb = 0xf;
	usim_data.msisdn.number.digit[9].msb = 0xf;
	usim_data.msisdn.number.digit[9].lsb = 0xf;
	usim_data.msisdn.conf1_record_id = 0xff;	/* Not used */
	usim_data.msisdn.ext1_record_id = 0xff;		/* Not used */
	/*
	 * PLMN Network Name and Operator PLMN List
	 */
	for (int i = SFR1; i < VDF2; i++) {
	    network_record_t record = network_records[i];
	    usim_data.pnn[i].fullname.type = USIM_PNN_FULLNAME_TAG;
	    usim_data.pnn[i].fullname.length = strlen(record.fullname);
	    strncpy((char*)usim_data.pnn[i].fullname.value, record.fullname,
		    usim_data.pnn[i].fullname.length);
	    usim_data.pnn[i].shortname.type = USIM_PNN_SHORTNAME_TAG;
	    usim_data.pnn[i].shortname.length = strlen(record.shortname);
	    strncpy((char*)usim_data.pnn[i].shortname.value, record.shortname,
		    usim_data.pnn[i].shortname.length);
	    usim_data.opl[i].plmn = record.plmn;
	    usim_data.opl[i].start = record.tac_start;
	    usim_data.opl[i].end = record.tac_end;
	    usim_data.opl[i].record_id = i;
	}
	for (int i = VDF2; i < USIM_OPL_MAX; i++) {
	    memset(&usim_data.opl[i].plmn, 0xff, sizeof(plmn_t));
	}

	/*
	 * List of Equivalent HPLMNs
	 */
	usim_data.ehplmn[0] = network_records[SFR2].plmn;
	usim_data.ehplmn[1] = network_records[SFR3].plmn;
	/*
	 * Home PLMN Selector with Access Technology
	 */
	usim_data.hplmn.plmn = network_records[SFR1].plmn;
	usim_data.hplmn.AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
	/*
	 * List of user controlled PLMN selector with Access Technology
	 */
	for (int i = 0; i < USIM_PLMN_MAX; i++) {
	    memset(&usim_data.plmn[i], 0xff, sizeof(plmn_t));
	}
	/*
	 * List of operator controlled PLMN selector with Access Technology
	 */
	usim_data.oplmn[0].plmn = network_records[VDF1].plmn;
	usim_data.oplmn[0].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
	usim_data.oplmn[1].plmn = network_records[VDF2].plmn;
	usim_data.oplmn[1].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
	usim_data.oplmn[2].plmn = network_records[VDF3].plmn;
	usim_data.oplmn[2].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
	usim_data.oplmn[3].plmn = network_records[VDF4].plmn;
	usim_data.oplmn[3].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
	usim_data.oplmn[4].plmn = network_records[VDF5].plmn;
	usim_data.oplmn[4].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
	for (int i = 5; i < USIM_OPLMN_MAX; i++) {
	    memset(&usim_data.oplmn[i], 0xff, sizeof(plmn_t));
	}
	/*
	 * EPS Location Information
	 */
	usim_data.epsloci.guti.gummei.plmn = network_records[SFR1].plmn;
	usim_data.epsloci.guti.gummei.MMEgid = DEFAULT_MME_ID;
	usim_data.epsloci.guti.gummei.MMEcode = DEFAULT_MME_CODE;
	usim_data.epsloci.guti.m_tmsi = DEFAULT_M_TMSI;
	usim_data.epsloci.tai.plmn = usim_data.epsloci.guti.gummei.plmn;
	usim_data.epsloci.tai.tac = DEFAULT_TAC;
	usim_data.epsloci.status = USIM_EPSLOCI_UPDATED;
	/*
	 * Non-Access Stratum configuration
	 */
	usim_data.nasconfig.NAS_SignallingPriority.type = USIM_NAS_SIGNALLING_PRIORITY_TAG;
	usim_data.nasconfig.NAS_SignallingPriority.length = 1;
	usim_data.nasconfig.NAS_SignallingPriority.value[0] = 0x00;
	usim_data.nasconfig.NMO_I_Behaviour.type = USIM_NMO_I_BEHAVIOUR_TAG;
	usim_data.nasconfig.NMO_I_Behaviour.length = 1;
	usim_data.nasconfig.NMO_I_Behaviour.value[0] = 0x00;
	usim_data.nasconfig.AttachWithImsi.type = USIM_ATTACH_WITH_IMSI_TAG;
	usim_data.nasconfig.AttachWithImsi.length = 1;
#if defined(START_WITH_GUTI)
	usim_data.nasconfig.AttachWithImsi.value[0] = 0x00;
#else
    usim_data.nasconfig.AttachWithImsi.value[0] = 0x01;
#endif
	usim_data.nasconfig.MinimumPeriodicSearchTimer.type = USIM_MINIMUM_PERIODIC_SEARCH_TIMER_TAG;
	usim_data.nasconfig.MinimumPeriodicSearchTimer.length = 1;
	usim_data.nasconfig.MinimumPeriodicSearchTimer.value[0] = 0x00;
	usim_data.nasconfig.ExtendedAccessBarring.type = USIM_EXTENDED_ACCESS_BARRING_TAG;
	usim_data.nasconfig.ExtendedAccessBarring.length = 1;
	usim_data.nasconfig.ExtendedAccessBarring.value[0] = 0x00;
	usim_data.nasconfig.Timer_T3245_Behaviour.type = USIM_TIMER_T3245_BEHAVIOUR_TAG;
	usim_data.nasconfig.Timer_T3245_Behaviour.length = 1;
	usim_data.nasconfig.Timer_T3245_Behaviour.value[0] = 0x00;

	/*
	 * Write USIM application data
	 */
	rc = usim_api_write(&usim_data);
	if (rc != RETURNok) {
	    perror("ERROR\t: usim_api_write() failed");
	    exit(EXIT_FAILURE);
	}
    }

    /*
     * Read USIM application data
     */
    memset(&usim_data, 0, sizeof(usim_data_t));
    rc = usim_api_read(&usim_data);
    if (rc != RETURNok) {
	perror("ERROR\t: usim_api_read() failed");
	exit(EXIT_FAILURE);
    }

    /*
     * Display USIM application data
     */
    printf("\nUSIM data:\n\n");
    _display_usim_data(&usim_data);

    /*
     * Display USIM file location
     */
    char* path = memory_get_path("USIM_DIR", ".usim.nvram");
    printf("\nUSIM data file: %s\n", path);
    free(path);

    exit(EXIT_SUCCESS);
}

/****************************************************************************/
/*********************  L O C A L    F U N C T I O N S  *********************/
/****************************************************************************/

/*
 * Displays command line usage
 */
static void _display_usage(const char* command)
{
    fprintf(stderr, "usage: %s [OPTION]\n", command);
    fprintf(stderr, "\t[--gen|-g]\tGenerate the USIM data file\n");
    fprintf(stderr, "\t[--print|-p]\tDisplay the content of the USIM data file\n");
    fprintf(stderr, "\t[--help|-h]\tDisplay this usage\n");
    const char* path = getenv("USIM_DIR");
    if (path != NULL) {
	fprintf(stderr, "USIM_DIR = %s\n", path);
    }
    else {
	fprintf(stderr, "USIM_DIR environment variable is not defined\n");
    }
}

/*
 * Displays USIM application data
 */
static void _display_usim_data(const usim_data_t* data)
{
    int digits;

    printf("Administrative Data:\n");
    printf("\tUE_Operation_Mode\t= 0x%.2x\n", data->ad.UE_Operation_Mode);
    printf("\tAdditional_Info\t\t= 0x%.4x\n", data->ad.Additional_Info);
    printf("\tMNC_Length\t\t= %d\n\n", data->ad.MNC_Length);

    printf("IMSI:\n");
    printf("\tlength\t= %d\n", data->imsi.length);
    printf("\tparity\t= %s\n", data->imsi.u.num.parity == EVEN_PARITY ? "Even" : "Odd");
    digits = (data->imsi.length * 2) - 1 - (data->imsi.u.num.parity == EVEN_PARITY ? 1 : 0);
    printf("\tdigits\t= %d\n", digits);
    printf("\tdigits\t= %u%u%u%u%u%x%u%u%u%u",
           data->imsi.u.num.digit1, // MCC digit 1
           data->imsi.u.num.digit2, // MCC digit 2
           data->imsi.u.num.digit3, // MCC digit 3
           data->imsi.u.num.digit4, // MNC digit 1
           data->imsi.u.num.digit5, // MNC digit 2
           data->imsi.u.num.digit6, // MNC digit 3
           data->imsi.u.num.digit7,
           data->imsi.u.num.digit8,
           data->imsi.u.num.digit9,
           data->imsi.u.num.digit10);
    if (digits >= 11)
        printf("%x", data->imsi.u.num.digit11);
    if (digits >= 12)
        printf("%x", data->imsi.u.num.digit12);
    if (digits >= 13)
        printf("%x", data->imsi.u.num.digit13);
    if (digits >= 14)
        printf("%x", data->imsi.u.num.digit14);
    if (digits >= 15)
        printf("%x", data->imsi.u.num.digit15);
    printf("\n\n");

    printf("Ciphering and Integrity Keys:\n");
    printf("\tKSI\t: 0x%.2x\n", data->keys.ksi);
    char key[USIM_CK_SIZE+1];
    key[USIM_CK_SIZE] = '\0';
    memcpy(key, data->keys.ck, USIM_CK_SIZE);
    printf("\tCK\t: \"%s\"\n", key);
    memcpy(key, data->keys.ik, USIM_IK_SIZE);
    printf("\tIK\t: \"%s\"\n", key);

    printf("EPS NAS security context:\n");
    printf("\tKSIasme\t: 0x%.2x\n", data->securityctx.KSIasme.value[0]);
    char kasme[USIM_K_ASME_SIZE+1];
    kasme[USIM_K_ASME_SIZE] = '\0';
    memcpy(kasme, data->securityctx.Kasme.value, USIM_K_ASME_SIZE);
    printf("\tKasme\t: \"%s\"\n", kasme);
    printf("\tulNAScount\t: 0x%.8x\n",
	   *(UInt32_t*)data->securityctx.ulNAScount.value);
    printf("\tdlNAScount\t: 0x%.8x\n",
	   *(UInt32_t*)data->securityctx.dlNAScount.value);
    printf("\talgorithmID\t: 0x%.2x\n\n",
	   data->securityctx.algorithmID.value[0]);

    printf("MSISDN\t= %u%u%u %u%u%u%u %u%u%u%u\n\n",
	   data->msisdn.number.digit[0].msb,
	   data->msisdn.number.digit[0].lsb,
	   data->msisdn.number.digit[1].msb,
	   data->msisdn.number.digit[1].lsb,
	   data->msisdn.number.digit[2].msb,
	   data->msisdn.number.digit[2].lsb,
	   data->msisdn.number.digit[3].msb,
	   data->msisdn.number.digit[3].lsb,
	   data->msisdn.number.digit[4].msb,
	   data->msisdn.number.digit[4].lsb,
	   data->msisdn.number.digit[5].msb);

    for (int i = 0; i < USIM_PNN_MAX; i++) {
	printf("PNN[%d]\t= {%s, %s}\n", i,
	       data->pnn[i].fullname.value, data->pnn[i].shortname.value);
    }
    printf("\n");

    for (int i = 0; i < USIM_OPL_MAX; i++) {
	printf("OPL[%d]\t= ", i); PRINT_PLMN(data->opl[i].plmn);
	printf(", TAC = [%.4x - %.4x], record_id = %d\n",
	   data->opl[i].start, data->opl[i].end, data->opl[i].record_id);
    }
    printf("\n");

    printf("HPLMN\t\t= "); PRINT_PLMN(data->hplmn.plmn);
    printf(", AcT = 0x%x\n\n", data->hplmn.AcT);

    for (int i = 0; i < USIM_FPLMN_MAX; i++) {
	printf("FPLMN[%d]\t= ", i); PRINT_PLMN(data->fplmn[i]);
	printf("\n");
    }
    printf("\n");

    for (int i = 0; i < USIM_EHPLMN_MAX; i++) {
	printf("EHPLMN[%d]\t= ", i); PRINT_PLMN(data->ehplmn[i]);
	printf("\n");
    }
    printf("\n");

    for (int i = 0; i < USIM_PLMN_MAX; i++) {
	printf("PLMN[%d]\t\t= ", i); PRINT_PLMN(data->plmn[i].plmn);
	printf(", AcTPLMN = 0x%x", data->plmn[i].AcT);
	printf("\n");
    }
    printf("\n");

    for (int i = 0; i < USIM_OPLMN_MAX; i++) {
	printf("OPLMN[%d]\t= ", i); PRINT_PLMN(data->oplmn[i].plmn);
	printf(", AcTPLMN = 0x%x", data->oplmn[i].AcT);
	printf("\n");
    }
    printf("\n");

    printf("HPPLMN\t\t= 0x%.2x (%d minutes)\n\n", data->hpplmn, data->hpplmn);

    printf("LOCI:\n");
    printf("\tTMSI = 0x%.4x\n", data->loci.tmsi);
    printf("\tLAI\t: PLMN = "); PRINT_PLMN(data->loci.lai.plmn);
    printf(", LAC = 0x%.2x\n", data->loci.lai.lac);
    printf("\tstatus\t= %d\n\n", data->loci.status);

    printf("PSLOCI:\n");
    printf("\tP-TMSI = 0x%.4x\n", data->psloci.p_tmsi);
    printf("\tsignature = 0x%x 0x%x 0x%x\n",
	   data->psloci.signature[0],
	   data->psloci.signature[1],
	   data->psloci.signature[2]);
    printf("\tRAI\t: PLMN = "); PRINT_PLMN(data->psloci.rai.plmn);
    printf(", LAC = 0x%.2x, RAC = 0x%.1x\n",
	   data->psloci.rai.lac, data->psloci.rai.rac);
    printf("\tstatus\t= %d\n\n", data->psloci.status);

    printf("EPSLOCI:\n");
    printf("\tGUTI\t: GUMMEI\t: (PLMN = ");
    PRINT_PLMN(data->epsloci.guti.gummei.plmn);
    printf(", MMEgid = 0x%.2x, MMEcode = 0x%.1x)",
	   data->epsloci.guti.gummei.MMEgid,
	   data->epsloci.guti.gummei.MMEcode);
    printf(", M-TMSI = 0x%.4x\n", data->epsloci.guti.m_tmsi);
    printf("\tTAI\t: PLMN = ");
    PRINT_PLMN(data->epsloci.tai.plmn);
    printf(", TAC = 0x%.2x\n",
	   data->epsloci.tai.tac);
    printf("\tstatus\t= %d\n\n", data->epsloci.status);

    printf("NASCONFIG:\n");
    printf("\tNAS_SignallingPriority\t\t: 0x%.2x\n",
	   data->nasconfig.NAS_SignallingPriority.value[0]);
    printf("\tNMO_I_Behaviour\t\t\t: 0x%.2x\n",
	   data->nasconfig.NMO_I_Behaviour.value[0]);
    printf("\tAttachWithImsi\t\t\t: 0x%.2x\n",
	   data->nasconfig.AttachWithImsi.value[0]);
    printf("\tMinimumPeriodicSearchTimer\t: 0x%.2x\n",
	   data->nasconfig.MinimumPeriodicSearchTimer.value[0]);
    printf("\tExtendedAccessBarring\t\t: 0x%.2x\n",
	   data->nasconfig.ExtendedAccessBarring.value[0]);
    printf("\tTimer_T3245_Behaviour\t\t: 0x%.2x\n",
	   data->nasconfig.Timer_T3245_Behaviour.value[0]);
}

