Merge branch 'next' of github.com:softwareradiosystems/srsLTE into next

master
Pedro Alvarez 7 years ago
commit badbb1de77

@ -330,8 +330,30 @@ struct gtpc_pdn_address_allocation_ie
struct in6_addr ipv6;
};
/****************************************************************************
*
* GTP-C Bearer Quality of Service IE
* Ref: 3GPP TS 29.274 v10.14.0 Figure 8.15-1
*
***************************************************************************/
struct gtpc_bearer_qos_ie
{
struct {
uint8_t pvi : 1;
uint8_t spare : 1;
uint8_t pl : 4;
uint8_t pci : 1;
uint8_t spare2 : 1;
} arp;
uint8_t qci;
uint8_t mbr_ul;
uint8_t mbr_dl;
uint8_t gbr_ul;
uint8_t gbr_dl;
};
//TODO
//TODO IEs between 8.15 and 8.17 missing
//TODO IEs between 8.16 and 8.17 missing
//TODO
/****************************************************************************

@ -202,6 +202,7 @@ struct gtpc_create_session_request
struct gtpc_f_teid_ie s12_rnc_f_teid;
bool s2b_u_epdg_f_teid_present;
struct gtpc_f_teid_ie s2b_u_epdg_f_teid;
struct gtpc_bearer_qos_ie bearer_qos; // M
} eps_bearer_context_created; // M
//bool bearer_context_deleted_present;
//struct bearer_context_ bearer_context_deleted; // C
@ -268,8 +269,8 @@ struct gtpc_create_session_response
struct gtpc_f_teid_ie s12_sgw_f_teid;
bool s2b_u_pgw_f_teid_present;
struct gtpc_f_teid_ie s2b_u_pgw_f_teid;
//bearer_level_qos_present
//bearer_level_qos
bool bearer_level_qos_present;
struct gtpc_bearer_qos_ie bearer_level_qos;
//charging_id_present
//charging_id
//bearer_flags_present

@ -11,6 +11,7 @@ class hss_interface_s1ap
{
public:
virtual bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) = 0;
virtual bool gen_update_loc_answer(uint64_t imsi, uint8_t *qci) = 0;
virtual bool resync_sqn(uint64_t imsi, uint8_t *auts) = 0;
};

@ -60,6 +60,7 @@ typedef struct{
uint8_t opc[16];
uint8_t amf[2];
uint8_t sqn[6];
uint16_t qci;
uint8_t last_rand[16];
}hss_ue_ctx_t;
@ -77,6 +78,8 @@ public:
void stop(void);
bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres);
bool gen_update_loc_answer(uint64_t imsi, uint8_t* qci);
bool resync_sqn(uint64_t imsi, uint8_t *auts);
private:

@ -111,7 +111,7 @@ typedef struct{
typedef struct{
uint8_t eksi;
uint8_t k_asme[32];
uint8_t k_asme[32];
uint8_t xres[16]; //minimum 6, maximum 16
uint32_t dl_nas_count;
uint32_t ul_nas_count;
@ -128,6 +128,7 @@ typedef struct{
typedef struct{
enum erab_state state;
uint8_t erab_id;
uint8_t qci;
srslte::gtpc_f_teid_ie enb_fteid;
srslte::gtpc_f_teid_ie sgw_s1u_fteid;
srslte::gtpc_pdn_address_allocation_ie pdn_addr_alloc;
@ -160,7 +161,6 @@ typedef struct{
ue_emm_ctx_t emm_ctx;
eps_sec_ctx_t sec_ctx;
ue_ecm_ctx_t ecm_ctx;
erab_ctx_t erabs_ctx[MAX_ERABS_PER_UE];
} ue_ctx_t;
}//namespace

@ -153,7 +153,7 @@ hss::read_db_file(std::string db_filename)
{
if(line[0] != '#')
{
uint column_size = 7;
uint column_size = 8;
std::vector<std::string> split = split_string(line,',');
if(split.size() != column_size)
{
@ -192,7 +192,8 @@ hss::read_db_file(std::string db_filename)
m_hss_log->debug_hex(ue_ctx->opc, 16, "User OPc : ");
m_hss_log->debug_hex(ue_ctx->amf, 2, "AMF : ");
m_hss_log->debug_hex(ue_ctx->sqn, 6, "SQN : ");
ue_ctx->qci = atoi(split[7].c_str());
m_hss_log->debug("Default Bearer QCI: %d\n",ue_ctx->qci);
m_imsi_to_ue_ctx.insert(std::pair<uint64_t,hss_ue_ctx_t*>(ue_ctx->imsi,ue_ctx));
}
}
@ -225,7 +226,7 @@ bool hss::write_db_file(std::string db_filename)
//Write comment info
m_db_file << "# " << std::endl
<< "# .csv to store UE's information in HSS " << std::endl
<< "# Kept in the following format: \"Name,IMSI,Key,OP_Type,OP,AMF,SQN\" " << std::endl
<< "# Kept in the following format: \"Name,IMSI,Key,OP_Type,OP,AMF,SQN,QCI\" " << std::endl
<< "# " << std::endl
<< "# Name: Human readable name to help distinguish UE's. Ignored by the HSS " << std::endl
<< "# IMSI: UE's IMSI value " << std::endl
@ -234,6 +235,7 @@ bool hss::write_db_file(std::string db_filename)
<< "# OP/OPc: Operator Code/Cyphered Operator Code, stored in hexadecimal " << std::endl
<< "# AMF: Authentication management field, stored in hexadecimal " << std::endl
<< "# SQN: UE's Sequence number for freshness of the authentication " << std::endl
<< "# QCI: QoS Class Identifier for the UE's default bearer. " << std::endl
<< "# " << std::endl
<< "# Note: Lines starting by '#' are ignored and will be overwritten " << std::endl;
@ -258,10 +260,11 @@ bool hss::write_db_file(std::string db_filename)
m_db_file << hex_string(it->second->amf, 2);
m_db_file << ",";
m_db_file << hex_string(it->second->sqn, 6);
m_db_file << ",";
m_db_file << it->second->qci;
m_db_file << std::endl;
it++;
}
if(m_db_file.is_open())
{
m_db_file.close();
@ -287,89 +290,7 @@ hss::gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t
}
bool
hss::resync_sqn(uint64_t imsi, uint8_t *auts)
{
bool ret = false;
switch (m_auth_algo)
{
case HSS_ALGO_XOR:
ret = resync_sqn_xor(imsi, auts);
break;
case HSS_ALGO_MILENAGE:
ret = resync_sqn_milenage(imsi, auts);
break;
}
increment_ue_sqn(imsi);
return ret;
}
bool
hss::resync_sqn_xor(uint64_t imsi, uint8_t *auts)
{
m_hss_log->error("XOR SQN synchronization not supported yet\n");
m_hss_log->console("XOR SQNs synchronization not supported yet\n");
return false;
}
bool
hss::resync_sqn_milenage(uint64_t imsi, uint8_t *auts)
{
uint8_t last_rand[16];
uint8_t ak[6];
uint8_t mac_s[8];
uint8_t sqn_ms_xor_ak[6];
uint8_t k[16];
uint8_t amf[2];
uint8_t opc[16];
uint8_t sqn[6];
if(!get_k_amf_opc_sqn(imsi, k, amf, opc, sqn))
{
return false;
}
get_last_rand(imsi, last_rand);
for(int i=0; i<6; i++){
sqn_ms_xor_ak[i] = auts[i];
}
for(int i=0; i<8; i++){
mac_s[i] = auts[i+6];
}
m_hss_log->debug_hex(k, 16, "User Key : ");
m_hss_log->debug_hex(opc, 16, "User OPc : ");
m_hss_log->debug_hex(last_rand, 16, "User Last Rand : ");
m_hss_log->debug_hex(auts, 16, "AUTS : ");
m_hss_log->debug_hex(sqn_ms_xor_ak, 6, "SQN xor AK : ");
m_hss_log->debug_hex(mac_s, 8, "MAC : ");
security_milenage_f5_star(k, opc, last_rand, ak);
m_hss_log->debug_hex(ak, 6, "Resynch AK : ");
uint8_t sqn_ms[6];
for(int i=0; i<6; i++){
sqn_ms[i] = sqn_ms_xor_ak[i] ^ ak[i];
}
m_hss_log->debug_hex(sqn_ms, 6, "SQN MS : ");
m_hss_log->debug_hex(sqn , 6, "SQN HE : ");
m_hss_log->debug_hex(amf, 2, "AMF : ");
uint8_t mac_s_tmp[8];
security_milenage_f1_star(k, opc, last_rand, sqn_ms, amf, mac_s_tmp);
m_hss_log->debug_hex(mac_s_tmp, 8, "MAC calc : ");
set_sqn(imsi, sqn_ms);
return true;
}
bool
hss::gen_auth_info_answer_milenage(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres)
@ -534,7 +455,7 @@ hss::gen_auth_info_answer_xor(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uin
mcc,
mnc,
k_asme);
m_hss_log->debug("User MCC : %x MNC : %x \n", mcc, mnc);
m_hss_log->debug_hex(k_asme, 32, "User k_asme : ");
@ -559,6 +480,23 @@ hss::gen_auth_info_answer_xor(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uin
return true;
}
bool
hss::gen_update_loc_answer(uint64_t imsi, uint8_t* qci)
{
std::map<uint64_t,hss_ue_ctx_t*>::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi);
if(ue_ctx_it == m_imsi_to_ue_ctx.end())
{
m_hss_log->info("User not found. IMSI: %015lu\n",imsi);
m_hss_log->console("User not found. IMSI: %015lu\n",imsi);
return false;
}
hss_ue_ctx_t *ue_ctx = ue_ctx_it->second;
m_hss_log->info("Found User %015lu\n",imsi);
*qci = ue_ctx->qci;
return true;
}
bool
hss::get_k_amf_opc_sqn(uint64_t imsi, uint8_t *k, uint8_t *amf, uint8_t *opc, uint8_t *sqn)
@ -581,6 +519,90 @@ hss::get_k_amf_opc_sqn(uint64_t imsi, uint8_t *k, uint8_t *amf, uint8_t *opc, ui
return true;
}
bool
hss::resync_sqn(uint64_t imsi, uint8_t *auts)
{
bool ret = false;
switch (m_auth_algo)
{
case HSS_ALGO_XOR:
ret = resync_sqn_xor(imsi, auts);
break;
case HSS_ALGO_MILENAGE:
ret = resync_sqn_milenage(imsi, auts);
break;
}
increment_ue_sqn(imsi);
return ret;
}
bool
hss::resync_sqn_xor(uint64_t imsi, uint8_t *auts)
{
m_hss_log->error("XOR SQN synchronization not supported yet\n");
m_hss_log->console("XOR SQNs synchronization not supported yet\n");
return false;
}
bool
hss::resync_sqn_milenage(uint64_t imsi, uint8_t *auts)
{
uint8_t last_rand[16];
uint8_t ak[6];
uint8_t mac_s[8];
uint8_t sqn_ms_xor_ak[6];
uint8_t k[16];
uint8_t amf[2];
uint8_t opc[16];
uint8_t sqn[6];
if(!get_k_amf_opc_sqn(imsi, k, amf, opc, sqn))
{
return false;
}
get_last_rand(imsi, last_rand);
for(int i=0; i<6; i++){
sqn_ms_xor_ak[i] = auts[i];
}
for(int i=0; i<8; i++){
mac_s[i] = auts[i+6];
}
m_hss_log->debug_hex(k, 16, "User Key : ");
m_hss_log->debug_hex(opc, 16, "User OPc : ");
m_hss_log->debug_hex(last_rand, 16, "User Last Rand : ");
m_hss_log->debug_hex(auts, 16, "AUTS : ");
m_hss_log->debug_hex(sqn_ms_xor_ak, 6, "SQN xor AK : ");
m_hss_log->debug_hex(mac_s, 8, "MAC : ");
security_milenage_f5_star(k, opc, last_rand, ak);
m_hss_log->debug_hex(ak, 6, "Resynch AK : ");
uint8_t sqn_ms[6];
for(int i=0; i<6; i++){
sqn_ms[i] = sqn_ms_xor_ak[i] ^ ak[i];
}
m_hss_log->debug_hex(sqn_ms, 6, "SQN MS : ");
m_hss_log->debug_hex(sqn , 6, "SQN HE : ");
m_hss_log->debug_hex(amf, 2, "AMF : ");
uint8_t mac_s_tmp[8];
security_milenage_f1_star(k, opc, last_rand, sqn_ms, amf, mac_s_tmp);
m_hss_log->debug_hex(mac_s_tmp, 8, "MAC calc : ");
set_sqn(imsi, sqn_ms);
return true;
}
void
hss::increment_ue_sqn(uint64_t imsi)
{
@ -613,6 +635,7 @@ hss::increment_sqn(uint8_t *sqn, uint8_t *next_sqn)
}
return;
}
void
hss::set_sqn(uint64_t imsi, uint8_t *sqn)
{
@ -668,7 +691,7 @@ bool hss::get_ue_ctx(uint64_t imsi, hss_ue_ctx_t **ue_ctx)
m_hss_log->info("User not found. IMSI: %015lu\n",imsi);
return false;
}
*ue_ctx = ue_ctx_it->second;
return true;
}
@ -713,15 +736,4 @@ hss::hex_string(uint8_t *hex, int size)
}
return ss.str();
}
/*
uint64_t
string_to_imsi()
{
uint64_t imsi = 0;
for(int i=0;i<=14;i++){
imsi += attach_req.eps_mobile_id.imsi[i]*std::pow(10,14-i);
}
return imsi;
}
*/
} //namespace srsepc

@ -124,9 +124,13 @@ mme_gtpc::send_create_session_request(uint64_t imsi)
// APN
strncpy(cs_req->apn, m_s1ap->m_s1ap_args.mme_apn.c_str(), sizeof(cs_req->apn)-1);
cs_req->apn[sizeof(cs_req->apn)-1] = 0;
// RAT Type
//cs_req->rat_type = srslte::GTPC_RAT_TYPE::EUTRAN;
//Bearer QoS
cs_req->eps_bearer_context_created.ebi = 5;
//Check whether this UE is already registed
std::map<uint64_t, struct gtpc_ctx>::iterator it = m_imsi_to_gtpc_ctx.find(imsi);
if(it != m_imsi_to_gtpc_ctx.end())
@ -243,10 +247,9 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu)
//Set EPS bearer context
//FIXME default EPS bearer is hard-coded
int default_bearer=5;
erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[default_bearer];
erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[default_bearer];
erab_ctx->pdn_addr_alloc= cs_resp->paa;
erab_ctx->sgw_s1u_fteid = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid;
m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(emm_ctx, ecm_ctx, erab_ctx);
return;
}

@ -114,13 +114,13 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx,
in_ctxt_req->E_RABToBeSetupListCtxtSUReq.len = 1;
erab_ctx_req->e_RAB_ID.E_RAB_ID = erab_ctx->erab_id;
//Setup E-RAB QoS parameters
erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI = 9;
erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI = erab_ctx->qci;
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel.PriorityLevel = 15 ;//Lowest
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability = LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION;
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability = LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE;
erab_ctx_req->e_RABlevelQoSParameters.gbrQosInformation_present=false;
//Set E-RAB S-GW F-TEID
//if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){
// m_s1ap_log->error("Did not receive S1-U TEID in create session response\n");
@ -188,8 +188,6 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx,
//Change E-RAB state to Context Setup Requested and save S-GW control F-TEID
ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].state = ERAB_CTX_REQUESTED;
//ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.teid = sgw_ctrl_fteid.teid;
//ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.ipv4 = sgw_ctrl_fteid.ipv4;
struct in_addr addr;
addr.s_addr = htonl(sgw_s1u_ip);
@ -199,6 +197,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx,
m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID);
m_s1ap_log->console("Initial Context Setup Request -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr));
m_s1ap_log->console("Initial Context Setup Request -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr));
m_s1ap_log->console("Initial Context Setup Request -- QCI %d \n", erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI);
m_pool->deallocate(reply_buffer);
m_pool->deallocate(nas_buffer);

@ -139,7 +139,7 @@ s1ap_nas_transport::handle_initial_ue_message(LIBLTE_S1AP_MESSAGE_INITIALUEMESSA
m_s1ap_log->info("Detach Request -- S-TMSI 0x%x\n", ntohl(*m_tmsi));
m_s1ap_log->console("Detach Request -- S-TMSI 0x%x\n", ntohl(*m_tmsi) );
m_s1ap_log->info("Detach Request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id);
m_s1ap_log->console("Detach Request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id);
m_s1ap_log->console("Detach Request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id);
handle_nas_detach_request(ntohl(*m_tmsi), enb_ue_s1ap_id, nas_msg, reply_buffer,reply_flag, enb_sri);
return true;
@ -227,9 +227,8 @@ s1ap_nas_transport::handle_uplink_nas_transport(LIBLTE_S1AP_MESSAGE_UPLINKNASTRA
(msg_type == LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE && sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY))
{
//Only identity response and authentication response are valid as plain NAS.
//Sometimes authentication response and identity are sent as integrity protected,
//Sometimes authentication response/failure and identity response are sent as integrity protected,
//but these messages are sent when the securty context is not setup yet, so we cannot integrity check it.
//FIXME Double-check
switch(msg_type)
{
case LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE:
@ -710,6 +709,11 @@ s1ap_nas_transport::handle_nas_guti_attach_request( uint32_t enb_ue_s1ap_id,
}
else
{
//Get subscriber info from HSS
uint8_t default_bearer=5;
m_hss->gen_update_loc_answer(emm_ctx->imsi,&ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
m_s1ap_log->debug("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
m_s1ap_log->console("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
m_mme_gtpc->send_create_session_request(emm_ctx->imsi);
*reply_flag = false; //No reply needed
}
@ -1082,6 +1086,11 @@ s1ap_nas_transport::handle_nas_security_mode_complete(srslte::byte_buffer_t *nas
}
else
{
//Get subscriber info from HSS
uint8_t default_bearer=5;
m_hss->gen_update_loc_answer(emm_ctx->imsi,&ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
m_s1ap_log->debug("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
m_s1ap_log->console("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
//FIXME The packging of GTP-C messages is not ready.
//This means that GTP-U tunnels are created with function calls, as opposed to GTP-C.
m_mme_gtpc->send_create_session_request(emm_ctx->imsi);
@ -1163,6 +1172,12 @@ s1ap_nas_transport::handle_esm_information_response(srslte::byte_buffer_t *nas_m
m_s1ap_log->console("ESM Info: %d Protocol Configuration Options\n",esm_info_resp.protocol_cnfg_opts.N_opts);
}
//Get subscriber info from HSS
uint8_t default_bearer=5;
m_hss->gen_update_loc_answer(ue_ctx->emm_ctx.imsi,&ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
m_s1ap_log->debug("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
m_s1ap_log->console("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci);
//FIXME The packging of GTP-C messages is not ready.
//This means that GTP-U tunnels are created with function calls, as opposed to GTP-C.
m_mme_gtpc->send_create_session_request(ue_ctx->emm_ctx.imsi);

@ -536,6 +536,7 @@ spgw::handle_create_session_request(struct srslte::gtpc_create_session_request *
cs_resp->eps_bearer_context_created.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present=true;
cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid = tunnel_ctx->up_user_fteid;
//Fill in the PAA
cs_resp->paa_present = true;
cs_resp->paa.pdn_type = srslte::GTPC_PDN_TYPE_IPV4;

@ -1,6 +1,6 @@
#
# .csv to store UE's information in HSS
# Kept in the following format: "Name,IMSI,Key,OP_Type,OP,AMF,SQN"
# Kept in the following format: "Name,IMSI,Key,OP_Type,OP,AMF,SQN,QCI"
#
# Name: Human readable name to help distinguish UE's. Ignored by the HSS
# IMSI: UE's IMSI value
@ -9,7 +9,8 @@
# OP/OPc: Operator Code/Cyphered Operator Code, stored in hexadecimal
# AMF: Authentication management field, stored in hexadecimal
# SQN: UE's Sequence number for freshness of the authentication
# QCI: QoS Class Identifier for the UE's default bearer.
#
# Note: Lines starting by '#' are ignored and will be overwritten
ue1,001010123456789,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,9001,000000001234
ue2,001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234
ue1,001010123456789,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,9001,000000001234,9
ue2,001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234,7

Loading…
Cancel
Save