From 13245c56f64ddee03fda5bca7723dffa1360c4a1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 29 Jun 2019 07:49:28 +0200 Subject: [PATCH] C-Netz: Add authentication support (process only) No real authentication, since the algorithm and the keys inside the cards are not known. A challenge can be specified and optionally the response can be verified. --- src/cnetz/cnetz.c | 142 +++++++++++++++++++++++++++++++--------- src/cnetz/cnetz.h | 11 +++- src/cnetz/database.c | 27 ++++++-- src/cnetz/database.h | 4 +- src/cnetz/main.c | 47 ++++++++++--- src/cnetz/sysinfo.c | 4 +- src/cnetz/sysinfo.h | 3 +- src/cnetz/telegramm.c | 2 +- src/cnetz/telegramm.h | 2 +- src/cnetz/transaction.c | 24 +++++-- src/cnetz/transaction.h | 30 +++++---- 11 files changed, 220 insertions(+), 76 deletions(-) diff --git a/src/cnetz/cnetz.c b/src/cnetz/cnetz.c index 8109566..44dd635 100644 --- a/src/cnetz/cnetz.c +++ b/src/cnetz/cnetz.c @@ -246,7 +246,7 @@ int cnetz_init(void) } /* Create transceiver instance and link to a list. */ -int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int auth, int warteschlange, int metering, double dbm0_deviation, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback) +int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int challenge_valid, uint64_t challenge, int response_valid, uint64_t response, int warteschlange, int metering, double dbm0_deviation, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback) { sender_t *sender; cnetz_t *cnetz; @@ -327,7 +327,10 @@ int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev } cnetz->chan_type = chan_type; - cnetz->auth = auth; + cnetz->challenge_valid = challenge_valid; + cnetz->challenge = challenge; + cnetz->response_valid = response_valid; + cnetz->response = response; cnetz->warteschlange = warteschlange; cnetz->metering = metering; cnetz->ms_power = ms_power; @@ -353,6 +356,7 @@ int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev break; default: /* send two cells and select by the first message from mobile */ + cnetz->cell_nr = 0; /* use cell 0 until selected */ cnetz->cell_auto = 1; } @@ -368,13 +372,13 @@ int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev cnetz_go_idle(cnetz); #ifdef DEBUG_SPK - transaction_t *trans = create_transaction(cnetz, TRANS_DS, 2, 2, 22002, -1); + transaction_t *trans = create_transaction(cnetz, TRANS_DS, 2, 2, 22002, -1, -1); trans->mo_call = 1; cnetz_set_sched_dsp_mode(cnetz, DSP_MODE_SPK_K, 2); #else /* create transaction for speech channel loopback */ if (loopback && chan_type == CHAN_TYPE_SPK) { - transaction_t *trans = create_transaction(cnetz, TRANS_VHQ, 2, 2, 22002, -1); + transaction_t *trans = create_transaction(cnetz, TRANS_VHQ_K, 2, 2, 22002, -1, -1); trans->mo_call = 1; cnetz_set_dsp_mode(cnetz, DSP_MODE_SPK_K); cnetz_set_sched_dsp_mode(cnetz, DSP_MODE_SPK_K, 0); @@ -384,16 +388,16 @@ int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev #if 0 /* debug flushing transactions */ transaction_t *trans1, *trans2; - trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1); + trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1); destroy_transaction(trans1); - trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1); + trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1); destroy_transaction(trans1); - trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1); - trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1); + trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1); + trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1, -1); unlink_transaction(trans1); link_transaction(trans1, cnetz); cnetz_flush_other_transactions(cnetz, trans1); - trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1); + trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1, -1); cnetz_flush_other_transactions(cnetz, trans2); #endif @@ -546,6 +550,7 @@ int call_down_setup(int callref, const char __attribute__((unused)) *caller_id, { sender_t *sender; cnetz_t *cnetz, *spk; + int rc; int extended; transaction_t *trans; uint8_t futln_nat; @@ -575,8 +580,8 @@ inval: futln_rest = atoi(dialing + 2); /* 2. check if the subscriber is attached */ - extended = find_db(futln_nat, futln_fuvst, futln_rest); - if (extended < 0) { + rc = find_db(futln_nat, futln_fuvst, futln_rest, NULL, &extended); + if (rc < 0) { PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call to not attached subscriber, rejecting!\n"); return -CAUSE_OUTOFORDER; } @@ -614,7 +619,7 @@ inval: PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Call to mobile station, paging station id '%s'\n", dialing); /* 6. trying to page mobile station */ - trans = create_transaction(cnetz, (spk) ? TRANS_VAK : TRANS_WSK, dialing[0] - '0', dialing[1] - '0', atoi(dialing + 2), -1); + trans = create_transaction(cnetz, (spk) ? TRANS_VAK : TRANS_WSK, dialing[0] - '0', dialing[1] - '0', atoi(dialing + 2), -1, -1); if (!trans) { PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n"); return -CAUSE_TEMPFAIL; @@ -730,7 +735,7 @@ int cnetz_meldeaufruf(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_res PDEBUG(DCNETZ, DEBUG_NOTICE, "'Meldeaufruf', but OgK is currently busy!\n"); return -CAUSE_NOCHANNEL; } - trans = create_transaction(cnetz, TRANS_MA, futln_nat, futln_fuvst, futln_rest, -1); + trans = create_transaction(cnetz, TRANS_MA, futln_nat, futln_fuvst, futln_rest, -1, -1); if (!trans) { PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n"); return -CAUSE_TEMPFAIL; @@ -833,7 +838,24 @@ void transaction_timeout(struct timer *timer) trans_new_state(trans, TRANS_AF); trans->repeat = 0; break; - case TRANS_VHQ: + case TRANS_ZFZ: + PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response after sending random number 'Zufallszahl'\n"); + if (trans->callref) { + call_up_release(trans->callref, CAUSE_TEMPFAIL); + trans->callref = 0; + } + cnetz_release(trans, CNETZ_CAUSE_FUNKTECHNISCH); + break; + case TRANS_AP: + PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response after waiting for challenge response 'Autorisierungsparameter'\n"); + if (trans->callref) { + call_up_release(trans->callref, CAUSE_TEMPFAIL); + trans->callref = 0; + } + cnetz_release(trans, CNETZ_CAUSE_FUNKTECHNISCH); + break; + case TRANS_VHQ_K: + case TRANS_VHQ_V: if (cnetz->dsp_mode != DSP_MODE_SPK_V) PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response while holding call 'Quittung Verbindung halten'\n"); else @@ -929,7 +951,7 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz) telegramm.bedingte_genauigkeit_der_fufst = si[cnetz->cell_nr].genauigkeit; telegramm.zeitschlitz_nr = cnetz->sched_ts; telegramm.grenzwert_fuer_einbuchen_und_umbuchen = si[cnetz->cell_nr].grenz_einbuchen; - telegramm.authentifikationsbit = cnetz->auth; + telegramm.authentifikationsbit = si[cnetz->cell_nr].authentifikationsbit; telegramm.vermittlungstechnische_sperren = si[cnetz->cell_nr].vermittlungstechnische_sperren; telegramm.ws_kennung = si[cnetz->cell_nr].ws_kennung; telegramm.reduzierungsfaktor = si[cnetz->cell_nr].reduzierung; @@ -1114,7 +1136,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo if (!match_fuz(cnetz, telegramm, cnetz->cell_nr)) break; rufnummer = telegramm2rufnummer(telegramm); - if (cnetz->auth && telegramm->chipkarten_futelg_bit) + if (si[cnetz->cell_nr].authentifikationsbit && telegramm->chipkarten_futelg_bit) PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received Attachment 'Einbuchen' message from Subscriber '%s' with chip card's ID %d (vendor id %d, hardware version %d, software version %d)\n", rufnummer, telegramm->kartenkennung, telegramm->herstellerkennung, telegramm->hardware_des_futelg, telegramm->software_des_futelg); else PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received Attachment 'Einbuchen' message from Subscriber '%s' with %s card's security code %d\n", rufnummer, (telegramm->chipkarten_futelg_bit) ? "chip":"magnet", telegramm->sicherungs_code); @@ -1124,7 +1146,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Attachment from subscriber '%s', because we are busy becoming SpK.\n", rufnummer); break; } - trans = create_transaction(cnetz, TRANS_EM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->erweitertes_frequenzbandbit); + trans = create_transaction(cnetz, TRANS_EM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->chipkarten_futelg_bit, telegramm->erweitertes_frequenzbandbit); if (!trans) { PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n"); break; @@ -1135,7 +1157,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo if (!match_fuz(cnetz, telegramm, cnetz->cell_nr)) break; rufnummer = telegramm2rufnummer(telegramm); - if (cnetz->auth && telegramm->chipkarten_futelg_bit) + if (si[cnetz->cell_nr].authentifikationsbit && telegramm->chipkarten_futelg_bit) PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received Roaming 'Umbuchen' message from Subscriber '%s' with chip card's ID %d (vendor id %d, hardware version %d, software version %d)\n", rufnummer, telegramm->kartenkennung, telegramm->herstellerkennung, telegramm->hardware_des_futelg, telegramm->software_des_futelg); else PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received Roaming 'Umbuchen' message from Subscriber '%s' with %s card's security code %d\n", rufnummer, (telegramm->chipkarten_futelg_bit) ? "chip":"magnet", telegramm->sicherungs_code); @@ -1145,7 +1167,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Roaming from subscriber '%s', because we are busy becoming SpK.\n", rufnummer); break; } - trans = create_transaction(cnetz, TRANS_UM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->erweitertes_frequenzbandbit); + trans = create_transaction(cnetz, TRANS_UM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->chipkarten_futelg_bit, telegramm->erweitertes_frequenzbandbit); if (!trans) { PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n"); break; @@ -1169,7 +1191,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Call from subscriber '%s', because we are busy becoming SpK.\n", rufnummer); break; } - trans = create_transaction(cnetz, TRANS_VWG, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, -1); + trans = create_transaction(cnetz, TRANS_VWG, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, -1, telegramm->erweitertes_frequenzbandbit); if (!trans) { PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n"); break; @@ -1199,7 +1221,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo trans = search_transaction_number(cnetz, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr); if (!trans) { /* create transaction, in case the phone repeats the release after we have acked it */ - trans = create_transaction(cnetz, TRANS_ATQ, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->erweitertes_frequenzbandbit); + trans = create_transaction(cnetz, TRANS_ATQ, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, -1, -1); if (!trans) { PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n"); break; @@ -1262,21 +1284,46 @@ const telegramm_t *cnetz_transmit_telegramm_spk_k(cnetz_t *cnetz) telegramm.futln_rest_nr = trans->futln_rest; telegramm.frequenz_nr = cnetz->sender.kanal; telegramm.bedingte_genauigkeit_der_fufst = si[cnetz->cell_nr].genauigkeit; + telegramm.zufallszahl = cnetz->challenge; switch (trans->state) { case TRANS_BQ: PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Belegungsquittung' on traffic channel\n"); telegramm.opcode = OPCODE_BQ_K; if (++trans->repeat >= 8 && !timer_running(&trans->timer)) { - trans_new_state(trans, TRANS_VHQ); + if (cnetz->challenge_valid) { + if (si[cnetz->cell_nr].authentifikationsbit == 0) { + PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Cannot authenticate, because base station does not support it. (Authentication disabled in sysinfo.)\n"); + goto no_auth; + } + if (trans->futelg_bit == 0) { + PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Cannot authenticate, because mobile station does not support it. (Mobile station has magnetic card.)\n"); + goto no_auth; + } + PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Perform authentication with subscriber's card.\n"); + trans_new_state(trans, TRANS_ZFZ); + timer_start(&trans->timer, 0.0375 * F_ZFZ); /* F_ZFZ frames */ + } else { +no_auth: + trans_new_state(trans, TRANS_VHQ_K); + timer_start(&trans->timer, 0.0375 * F_VHQK); /* F_VHQK frames */ + } trans->repeat = 0; - timer_start(&trans->timer, 0.0375 * F_VHQK); /* F_VHQK frames */ } break; - case TRANS_VHQ: + case TRANS_ZFZ: + PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Zufallszahl' on traffic channel (0x%016" PRIx64 ").\n", telegramm.zufallszahl); + telegramm.opcode = OPCODE_ZFZ_K; + break; + case TRANS_AP: + PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Quittung Verbindung halten' on traffic channel\n"); + telegramm.opcode = OPCODE_VHQ_K; + break; + case TRANS_VHQ_K: if (!cnetz->sender.loopback) PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Quittung Verbindung halten' on traffic channel\n"); telegramm.opcode = OPCODE_VHQ_K; + /* continue until next sub frame, so we send DS from first block of next sub frame. */ if (!cnetz->sender.loopback && (cnetz->sched_ts & 7) == 7 && cnetz->sched_r_m && !timer_running(&trans->timer)) { /* next sub frame */ if (trans->mo_call) { @@ -1304,9 +1351,10 @@ const telegramm_t *cnetz_transmit_telegramm_spk_k(cnetz_t *cnetz) case TRANS_DS: PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Durchschalten' on traffic channel\n"); telegramm.opcode = OPCODE_DSB_K; + /* send exactly a sub frame (8 time slots) */ if ((cnetz->sched_ts & 7) == 7 && cnetz->sched_r_m && !timer_running(&trans->timer)) { /* next sub frame */ - trans_new_state(trans, TRANS_VHQ); + trans_new_state(trans, TRANS_VHQ_V); trans->repeat = 0; cnetz_set_sched_dsp_mode(cnetz, DSP_MODE_SPK_V, 1); #ifndef DEBUG_SPK @@ -1323,7 +1371,7 @@ const telegramm_t *cnetz_transmit_telegramm_spk_k(cnetz_t *cnetz) telegramm.opcode = OPCODE_AHQ_K; if ((cnetz->sched_ts & 7) == 7 && cnetz->sched_r_m) { /* next sub frame */ - trans_new_state(trans, TRANS_VHQ); + trans_new_state(trans, TRANS_VHQ_V); trans->repeat = 0; cnetz_set_sched_dsp_mode(cnetz, DSP_MODE_SPK_V, 1); timer_start(&trans->timer, 0.075 + 0.6 * F_VHQ); /* one slot + F_VHQ frames */ @@ -1431,6 +1479,38 @@ void cnetz_receive_telegramm_spk_k(cnetz_t *cnetz, telegramm_t *telegramm) cnetz->scrambler_switch = 0; timer_stop(&trans->timer); break; + case OPCODE_ZFZQ_K: + PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received random number acknowledge 'Zufallszahlquittung' message.\n"); + valid_frame = 1; + if (trans->state != TRANS_ZFZ) + break; + if (cnetz->challenge != telegramm->zufallszahl) { + PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Received random number acknowledge (0x%016" PRIx64 ") does not match the transmitted one (0x%016" PRIx64 "), ignoring!\n", telegramm->zufallszahl, cnetz->challenge); + break; + } + timer_stop(&trans->timer); + trans_new_state(trans, TRANS_AP); + timer_start(&trans->timer, T_AP); /* 750 milliseconds */ + break; + case OPCODE_AP_K: + PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received challenge response 'Autorisierungsparameter' message (0x%016" PRIx64 ").\n", telegramm->authorisierungsparameter); + valid_frame = 1; + if (trans->state != TRANS_AP) + break; + /* if authentication response from card does not match */ + if (cnetz->response_valid && telegramm->authorisierungsparameter != cnetz->response) { + PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Received challenge response (0x%016" PRIx64 ") does not match the expected one (0x%016" PRIx64 "), releasing!\n", telegramm->authorisierungsparameter, cnetz->response); + if (trans->callref) { + call_up_release(trans->callref, CAUSE_TEMPFAIL); /* jolly guesses that */ + trans->callref = 0; + } + cnetz_release(trans, CNETZ_CAUSE_GASSENBESETZT); /* when authentication is not valid */ + break; + } + timer_stop(&trans->timer); + trans_new_state(trans, TRANS_VHQ_K); + timer_start(&trans->timer, 0.0375 * F_VHQK); /* F_VHQK frames */ + break; case OPCODE_VH_K: if (!match_fuz(cnetz, telegramm, cnetz->cell_nr)) { break; @@ -1440,7 +1520,7 @@ void cnetz_receive_telegramm_spk_k(cnetz_t *cnetz, telegramm_t *telegramm) } PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received connection hold 'Verbindung halten' message.\n"); valid_frame = 1; - if (trans->state != TRANS_VHQ) + if (trans->state != TRANS_VHQ_K) break; timer_stop(&trans->timer); break; @@ -1467,7 +1547,7 @@ void cnetz_receive_telegramm_spk_k(cnetz_t *cnetz, telegramm_t *telegramm) PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received answer frame 'Abheben' message.\n"); valid_frame = 1; /* if already received this frame, or if we are already on VHQ or if we are releasing */ - if (trans->state == TRANS_AHQ || trans->state == TRANS_VHQ || trans->state == TRANS_AF) + if (trans->state == TRANS_AHQ || trans->state == TRANS_VHQ_K || trans->state == TRANS_AF) break; cnetz->scrambler = telegramm->betriebs_art; cnetz->scrambler_switch = 0; @@ -1539,7 +1619,7 @@ const telegramm_t *cnetz_transmit_telegramm_spk_v(cnetz_t *cnetz) telegramm.ausloesegrund = trans->release_cause; switch (trans->state) { - case TRANS_VHQ: + case TRANS_VHQ_V: if ((cnetz->sched_ts & 8) == 0) { /* sub frame 1 and 3 */ PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Quittung Verbindung halten 1' on traffic channel\n"); telegramm.opcode = OPCODE_VHQ1_V; @@ -1557,7 +1637,7 @@ const telegramm_t *cnetz_transmit_telegramm_spk_v(cnetz_t *cnetz) } break; case TRANS_AT: - PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Auslosen durch FuFst' on traffic channel\n"); + PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending acknowledge to 'Ausloesen durch FuTln' on traffic channel\n"); telegramm.opcode = OPCODE_AF_V; if (++trans->repeat == 1) { destroy_transaction(trans); @@ -1589,7 +1669,7 @@ void cnetz_receive_telegramm_spk_v(cnetz_t *cnetz, telegramm_t *telegramm) if (!match_futln(telegramm, trans->futln_nat, trans->futln_fuvst, trans->futln_rest)) { break; } - if (trans->state != TRANS_VHQ) + if (trans->state != TRANS_VHQ_V) break; timer_start(&trans->timer, 0.6 * F_VHQ); /* F_VHQ frames */ switch (opcode) { diff --git a/src/cnetz/cnetz.h b/src/cnetz/cnetz.h index feaec7b..d27b8fd 100644 --- a/src/cnetz/cnetz.h +++ b/src/cnetz/cnetz.h @@ -33,6 +33,7 @@ enum cnetz_state { #define F_BQ 8 /* number of not received frames at BQ state */ #define F_VHQK 16 /* number of not received frames at VHQ state during concentrated signaling */ #define F_VHQ 16 /* number of not received frames at VHQ state during distributed signaling */ +#define F_ZFZ 16 /* number of not received frames at ZFZ state (guessed, no documentation avail) */ #define F_DS 16 /* number of not received frames at DS state */ #define F_RTA 16 /* number of not received frames at RTA state */ #define N_AFKT 6 /* number of release frames to send during concentrated signaling */ @@ -40,6 +41,7 @@ enum cnetz_state { #define N 3 /* now many times we repeat a message on OgK */ #define T_VAG2 180 /* time on outgoing queue */ #define T_VAK 60 /* time on incoming queue */ +#define T_AP 750 /* Time to wait for SIM card's authentication reply */ /* clear causes */ #define CNETZ_CAUSE_GASSENBESETZT 0 /* network congested */ @@ -71,9 +73,12 @@ typedef struct cnetz { int de_emphasis; /* use de_emphasis by this instance */ emphasis_t estate; - /* cell config */ + /* call config */ int ms_power; /* power level of MS, use 0..3 */ - int auth; /* authentication support of the cell */ + int challenge_valid; /* send authorizaton value */ + uint64_t challenge; /* authorization value */ + int response_valid; /* expect authorizaton response */ + uint64_t response; /* authorization response */ int warteschlange; /* use queue */ int metering; /* use metering pulses in seconds 0 = off */ @@ -133,7 +138,7 @@ int cnetz_channel_by_short_name(const char *short_name); const char *chan_type_short_name(enum cnetz_chan_type chan_type); const char *chan_type_long_name(enum cnetz_chan_type chan_type); int cnetz_init(void); -int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int auth, int warteschlange, int metering, double dbm0_deviation, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback); +int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int challenge_valid, uint64_t challenge, int response_valid, uint64_t response, int warteschlange, int metering, double dbm0_deviation, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback); void cnetz_destroy(sender_t *sender); void cnetz_go_idle(cnetz_t *cnetz); void cnetz_sync_frame(cnetz_t *cnetz, double sync, int ts); diff --git a/src/cnetz/database.c b/src/cnetz/database.c index a8b2887..c6d3765 100644 --- a/src/cnetz/database.c +++ b/src/cnetz/database.c @@ -40,6 +40,7 @@ typedef struct cnetz_database { uint8_t futln_nat; /* who ... */ uint8_t futln_fuvst; uint16_t futln_rest; + int futelg_bit; /* chip card inside */ int extended; /* mobile supports extended frequencies */ struct timer timer; /* timer for next availability check */ int retry; /* counts number of retries */ @@ -89,7 +90,7 @@ static void db_timeout(struct timer *timer) } /* create/update db entry */ -int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended, int busy, int failed) +int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended, int busy, int failed) { cnetz_db_t *db, **dbp; @@ -123,8 +124,11 @@ int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t PDEBUG(DDB, DEBUG_INFO, "Adding subscriber '%d,%d,%d' to database.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); } - if (extended >= 0) - db->extended = extended; + if (futelg_bit && *futelg_bit >= 0) + db->futelg_bit = *futelg_bit; + + if (extended && *extended >= 0) + db->extended = *extended; if (busy) { PDEBUG(DDB, DEBUG_INFO, "Subscriber '%d,%d,%d' busy now.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); @@ -143,18 +147,27 @@ int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t timer_start(&db->timer, MELDE_WIEDERHOLUNG); /* when to do retry */ } - return db->extended; + if (futelg_bit) + *futelg_bit = db->futelg_bit; + if (extended) + *extended = db->extended; + return 0; } -int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest) +int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended) { cnetz_db_t *db = cnetz_db_head; while (db) { if (db->futln_nat == futln_nat && db->futln_fuvst == futln_fuvst - && db->futln_rest == futln_rest) - return db->extended; + && db->futln_rest == futln_rest) { + if (futelg_bit) + *futelg_bit = db->futelg_bit; + if (extended) + *extended = db->extended; + return 0; + } db = db->next; } return -1; diff --git a/src/cnetz/database.h b/src/cnetz/database.h index de0226c..a1339a0 100644 --- a/src/cnetz/database.h +++ b/src/cnetz/database.h @@ -1,6 +1,6 @@ -int update_db(cnetz_t *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended, int busy, int failed); -int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest); +int update_db(cnetz_t *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended, int busy, int failed); +int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended); void flush_db(void); void dump_db(void); diff --git a/src/cnetz/main.c b/src/cnetz/main.c index c373fa6..1001a9c 100644 --- a/src/cnetz/main.c +++ b/src/cnetz/main.c @@ -46,12 +46,16 @@ double clock_speed[2] = { 0.0, 0.0 }; int set_clock_speed = 0; const char *flip_polarity = "auto"; int ms_power = 0; /* 0..3 */ -int auth = 0; int warteschlange = 1; +int challenge_valid; +uint64_t challenge; +int response_valid; +uint64_t response; uint8_t fuz_nat = 1; uint8_t fuz_fuvst = 1; uint8_t fuz_rest = 38; uint8_t kennung_fufst = 1; /* normal prio */ +uint8_t authentifikationsbit = 0; uint8_t ws_kennung = 0; /* no queue */ uint8_t fuvst_sperren = 0; /* no blocking registration/calls */ uint8_t grenz_einbuchen = 1; /* > 15 SNR */ @@ -94,10 +98,16 @@ void print_help(const char *arg0) printf(" -P --ms-power \n"); printf(" Give power level of the mobile station 0..3. (default = '%d')\n", ms_power); printf(" 0 = 50-125 mW; 1 = 0.5-1 W; 2 = 4-8 W; 3 = 10-20 W\n"); - printf(" -A --authentication\n"); - printf(" Enable authentication on the base station. Since we cannot\n"); - printf(" authenticate, because we don't know the secret key and the algorithm,\n"); - printf(" we just accept any card. With this we get the vendor IDs of the phone.\n"); + printf(" -A --authentication \n"); + printf(" Enable authorization flag on the base station and use given challenge\n"); + printf(" as autorization random. Depending on the key inside the card you will\n"); + printf(" get a response. Any response is accepted. Phone must have smart card!\n"); + printf(" The challenge can be any 64 bit (hex) number like: 0x0123456789abcdef\n"); + printf(" Note: Authentication is automatically enabled for the base station\n"); + printf(" -A --authentication ,\n"); + printf(" Same as above, but the given response must match the response from\n"); + printf(" smart card. The response can be any 64 bit (hex) number.\n"); + printf(" Note: Authentication is automatically enabled for the base station\n"); printf(" -Q --queue | --warteschlange 1 | 0\n"); printf(" Enable queue support. If no channel is available, calls will be kept\n"); printf(" in a queue for maximum of 60 seconds. (default = %d)\n", warteschlange); @@ -122,6 +132,13 @@ void print_help(const char *arg0) printf(" 2 = Higher priority base station.\n"); printf(" 3 = Highest priority base station.\n"); printf(" Note: Priority has no effect, because there is only one base station.\n"); + printf(" -A --sysinfo authentifikationsbit=auth>\n"); + printf(" Enable authentication flag on the base station. Since we cannot\n"); + printf(" authenticate, because we don't know the secret key and the algorithm,\n"); + printf(" we just accept any card. Useful get the vendor IDs of the phone.\n"); + printf(" 0 = Disable. Even chip card phones behave like magnetic card phones.\n"); + printf(" 1 = Enable. Chip card phones send their card ID.\n"); + printf(" (default = %d)\n", authentifikationsbit); printf(" -S --sysinfo ws-kennung=\n"); printf(" Queue setting of base station. (default = %d)\n", ws_kennung); printf(" 0 = No queue, calls will be handled directly.\n"); @@ -231,7 +248,7 @@ static void add_options(void) option_add('C', "clock-speed", 1); option_add('F', "flip-polarity", 1); option_add('P', "ms-power", 1); - option_add('A', "authentication", 0); + option_add('A', "authentifikationsbit", 1); option_add('Q', "queue", 1); option_add(OPT_WARTESCHLANGE, "warteschlange", 1); option_add('G', "gebuehren", 1); @@ -287,11 +304,18 @@ static int handle_options(int short_option, int argi, char **argv) ms_power = atoi_limit(argv[argi], 0, 3); break; case 'A': - auth = 1; + authentifikationsbit = 1; + challenge_valid = 1; + challenge = strtoul(argv[argi], NULL, 0); + p = strchr(argv[argi], ','); + if (p) { + response_valid = 1; + response = strtoul(p + 1, NULL, 0); + } break; case 'Q': case OPT_WARTESCHLANGE: - warteschlange = atoi_limit(argv[argi], 0, 1);; + warteschlange = atoi_limit(argv[argi], 0, 1); break; case 'G': metering = atoi(argv[argi]); @@ -318,6 +342,9 @@ static int handle_options(int short_option, int argi, char **argv) if (!strncasecmp(argv[argi], "kennung-fufst=", p - argv[argi])) { kennung_fufst = atoi_limit(p, 0, 3); } else + if (!strncasecmp(argv[argi], "auth=", p - argv[argi])) { + authentifikationsbit = atoi_limit(p, 0, 1); + } else if (!strncasecmp(argv[argi], "ws-kennung=", p - argv[argi])) { ws_kennung = atoi_limit(p, 0, 3); } else @@ -481,7 +508,7 @@ int main(int argc, char *argv[]) } if (anzahl_gesperrter_teilnehmergruppen) printf("Blocked subscriber with number's last 4 bits from 0x%x to 0x%x\n", teilnehmergruppensperre, (teilnehmergruppensperre + anzahl_gesperrter_teilnehmergruppen - 1) & 0xf); - init_sysinfo(fuz_nat, fuz_fuvst, fuz_rest, kennung_fufst, ws_kennung, fuvst_sperren, grenz_einbuchen, grenz_umschalten, grenz_ausloesen, mittel_umschalten, mittel_ausloesen, genauigkeit, bewertung, entfernung, reduzierung, nachbar_prio, teilnehmergruppensperre, anzahl_gesperrter_teilnehmergruppen); + init_sysinfo(fuz_nat, fuz_fuvst, fuz_rest, kennung_fufst, authentifikationsbit, ws_kennung, fuvst_sperren, grenz_einbuchen, grenz_umschalten, grenz_ausloesen, mittel_umschalten, mittel_ausloesen, genauigkeit, bewertung, entfernung, reduzierung, nachbar_prio, teilnehmergruppensperre, anzahl_gesperrter_teilnehmergruppen); dsp_init(); rc = init_telegramm(); if (rc < 0) { @@ -551,7 +578,7 @@ int main(int argc, char *argv[]) /* create transceiver instance */ for (i = 0; i < num_kanal; i++) { - rc = cnetz_create(kanal[i], chan_type[i], audiodev[i], use_sdr, demod, samplerate, rx_gain, auth, warteschlange, metering, dbm0_deviation, ms_power, (i == 0) ? measure_speed : 0, clock_speed, polarity, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback); + rc = cnetz_create(kanal[i], chan_type[i], audiodev[i], use_sdr, demod, samplerate, rx_gain, challenge_valid, challenge, response_valid, response, warteschlange, metering, dbm0_deviation, ms_power, (i == 0) ? measure_speed : 0, clock_speed, polarity, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback); if (rc < 0) { fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n"); goto fail; diff --git a/src/cnetz/sysinfo.c b/src/cnetz/sysinfo.c index 6b1fcad..172c0a7 100644 --- a/src/cnetz/sysinfo.c +++ b/src/cnetz/sysinfo.c @@ -4,7 +4,7 @@ cnetz_si si[2]; -void init_sysinfo(uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t ws_kennung, uint8_t vermittlungstechnische_sperren, uint8_t grenz_einbuchen, uint8_t grenz_umschalten, uint8_t grenz_ausloesen, uint8_t mittel_umschalten, uint8_t mittel_ausloesen, uint8_t genauigkeit, uint8_t bewertung, uint8_t entfernung, uint8_t reduzierung, uint8_t nachbar_prio, int8_t teilnehmergruppensperre, uint8_t anzahl_gesperrter_teilnehmergruppen) +void init_sysinfo(uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t authentifikationsbit, uint8_t ws_kennung, uint8_t vermittlungstechnische_sperren, uint8_t grenz_einbuchen, uint8_t grenz_umschalten, uint8_t grenz_ausloesen, uint8_t mittel_umschalten, uint8_t mittel_ausloesen, uint8_t genauigkeit, uint8_t bewertung, uint8_t entfernung, uint8_t reduzierung, uint8_t nachbar_prio, int8_t teilnehmergruppensperre, uint8_t anzahl_gesperrter_teilnehmergruppen) { memset(&si[0], 0, sizeof(cnetz_si)); @@ -39,6 +39,8 @@ void init_sysinfo(uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t si[0].kennung_fufst = kennung_fufst; + si[0].authentifikationsbit = authentifikationsbit; + si[0].ws_kennung = ws_kennung; si[0].nachbar_prio = nachbar_prio; diff --git a/src/cnetz/sysinfo.h b/src/cnetz/sysinfo.h index ba8dfb8..141dc21 100644 --- a/src/cnetz/sysinfo.h +++ b/src/cnetz/sysinfo.h @@ -13,6 +13,7 @@ typedef struct system_information { uint8_t entfernung; uint8_t grenz_einbuchen; uint8_t kennung_fufst; /* prio of base station */ + uint8_t authentifikationsbit; /* base station suppoerts authentication */ uint8_t ws_kennung; /* queue setting sof base station */ uint8_t nachbar_prio; uint8_t bewertung; @@ -23,5 +24,5 @@ typedef struct system_information { extern cnetz_si si[]; -void init_sysinfo(uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t ws_kennung, uint8_t vermittlungstechnische_sperren, uint8_t grenz_einbuchen, uint8_t grenz_umschalten, uint8_t grenz_ausloesen, uint8_t mittel_umschalten, uint8_t mittel_ausloesen, uint8_t genauigkeit, uint8_t bewertung, uint8_t entfernung, uint8_t reduzierung, uint8_t nachbar_prio, int8_t teilnehmergruppensperre, uint8_t anzahl_gesperrter_teilnehmergruppen); +void init_sysinfo(uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t authentifikationsbit, uint8_t ws_kennung, uint8_t vermittlungstechnische_sperren, uint8_t grenz_einbuchen, uint8_t grenz_umschalten, uint8_t grenz_ausloesen, uint8_t mittel_umschalten, uint8_t mittel_ausloesen, uint8_t genauigkeit, uint8_t bewertung, uint8_t entfernung, uint8_t reduzierung, uint8_t nachbar_prio, int8_t teilnehmergruppensperre, uint8_t anzahl_gesperrter_teilnehmergruppen); diff --git a/src/cnetz/telegramm.c b/src/cnetz/telegramm.c index d252008..10d9295 100644 --- a/src/cnetz/telegramm.c +++ b/src/cnetz/telegramm.c @@ -1486,7 +1486,7 @@ void cnetz_decode_telegramm(cnetz_t *cnetz, const char *bits, double level, doub return; } - disassemble_telegramm(&telegramm, bits, cnetz->auth); + disassemble_telegramm(&telegramm, bits, si[cnetz->cell_nr].authentifikationsbit); opcode = telegramm.opcode; telegramm.level = level; telegramm.sync_time = sync_time; diff --git a/src/cnetz/telegramm.h b/src/cnetz/telegramm.h index b6bb351..1c2ede3 100644 --- a/src/cnetz/telegramm.h +++ b/src/cnetz/telegramm.h @@ -101,7 +101,7 @@ typedef struct telegramm { uint8_t authentifikationsbit; uint8_t mittelungsfaktor_fuer_ausloesen; uint8_t mittelungsfaktor_fuer_umschalten; - uint16_t zufallszahl; + uint64_t zufallszahl; uint8_t bewertung_nach_pegel_und_entfernung; uint64_t authorisierungsparameter; uint8_t entfernungsangabe_der_fufst; diff --git a/src/cnetz/transaction.c b/src/cnetz/transaction.c index 33821d0..acfe416 100644 --- a/src/cnetz/transaction.c +++ b/src/cnetz/transaction.c @@ -38,7 +38,7 @@ const char *transaction2rufnummer(transaction_t *trans) } /* create transaction */ -transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended) +transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int futelg_bit, int extended) { sender_t *sender; transaction_t *trans = NULL; @@ -88,7 +88,9 @@ transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_ link_transaction(trans, cnetz); /* update database: now busy */ - trans->extended = update_db(cnetz, futln_nat, futln_fuvst, futln_rest, extended, 1, 0); + update_db(cnetz, futln_nat, futln_fuvst, futln_rest, &futelg_bit, &extended, 1, 0); + trans->futelg_bit = futelg_bit; + trans->extended = extended; return trans; } @@ -97,7 +99,7 @@ transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_ void destroy_transaction(transaction_t *trans) { /* update database: now idle */ - update_db(trans->cnetz, trans->futln_nat, trans->futln_fuvst, trans->futln_rest, -1, 0, trans->page_failed); + update_db(trans->cnetz, trans->futln_nat, trans->futln_fuvst, trans->futln_rest, NULL, NULL, 0, trans->page_failed); unlink_transaction(trans); @@ -228,8 +230,14 @@ static const char *trans_state_name(uint64_t state) return "VAK"; case TRANS_BQ: return "BQ"; - case TRANS_VHQ: - return "VHQ"; + case TRANS_ZFZ: + return "ZFZ"; + case TRANS_VHQ_K: + return "VHQ_K"; + case TRANS_VHQ_V: + return "VHQ_V"; + case TRANS_AP: + return "AP"; case TRANS_RTA: return "RTA"; case TRANS_DS: @@ -273,11 +281,15 @@ const char *trans_short_state_name(uint64_t state) case TRANS_WBP: case TRANS_WBN: return "DIALING"; + case TRANS_ZFZ: + case TRANS_AP: + return "AUTHENTICATE"; case TRANS_VAG: case TRANS_WSK: case TRANS_VAK: case TRANS_BQ: - case TRANS_VHQ: + case TRANS_VHQ_K: + case TRANS_VHQ_V: return "ASSIGN"; case TRANS_RTA: return "ALERT"; diff --git a/src/cnetz/transaction.h b/src/cnetz/transaction.h index dcbd6fe..edb1832 100644 --- a/src/cnetz/transaction.h +++ b/src/cnetz/transaction.h @@ -17,20 +17,23 @@ #define TRANS_VAK (1 << 10) /* establishment of call sent, switching channel */ /* traffic channel */ #define TRANS_BQ (1 << 11) /* accnowledge channel */ -#define TRANS_VHQ (1 << 12) /* hold call */ -#define TRANS_RTA (1 << 13) /* hold call and make the phone ring */ -#define TRANS_DS (1 << 14) /* establish speech connection */ -#define TRANS_AHQ (1 << 15) /* establish speech connection after answer */ +#define TRANS_ZFZ (1 << 12) /* hold call and send and receive challenge request */ +#define TRANS_AP (1 << 13) /* hold call and send and receive challenge request */ +#define TRANS_VHQ_K (1 << 14) /* hold call until speech channel or challenge response is available */ +#define TRANS_VHQ_V (1 << 15) /* hold call while in conversation on distributed signalling */ +#define TRANS_RTA (1 << 16) /* hold call and make the phone ring */ +#define TRANS_DS (1 << 17) /* establish speech connection */ +#define TRANS_AHQ (1 << 18) /* establish speech connection after answer */ /* release */ -#define TRANS_VA (1 << 16) /* release call in queue by base station (OgK) */ -#define TRANS_AF (1 << 17) /* release connection by base station (SpK) */ -#define TRANS_AT (1 << 18) /* release connection by mobile station */ -#define TRANS_ATQ (1 << 19) /* acknowledge release of MO call in queue */ +#define TRANS_VA (1 << 19) /* release call in queue by base station (OgK) */ +#define TRANS_AF (1 << 20) /* release connection by base station (SpK) */ +#define TRANS_AT (1 << 21) /* release connection by mobile station */ +#define TRANS_ATQ (1 << 22) /* acknowledge release of MO call in queue */ /* queue */ -#define TRANS_MO_QUEUE (1 << 20) /* MO queue */ -#define TRANS_MT_QUEUE (1 << 21) /* MT queue */ -#define TRANS_MO_DELAY (1 << 22) /* delay to be sure the channel is free again */ -#define TRANS_MT_DELAY (1 << 23) +#define TRANS_MO_QUEUE (1 << 23) /* MO queue */ +#define TRANS_MT_QUEUE (1 << 24) /* MT queue */ +#define TRANS_MO_DELAY (1 << 25) /* delay to be sure the channel is free again */ +#define TRANS_MT_DELAY (1 << 26) typedef struct transaction { struct transaction *next; /* pointer to next node in list */ @@ -39,6 +42,7 @@ typedef struct transaction { uint8_t futln_nat; /* current station ID (3 values) */ uint8_t futln_fuvst; uint16_t futln_rest; + int futelg_bit; /* chip card inside phone */ int extended; /* extended frequency capability */ char dialing[18]; /* number dialed by the phone */ int64_t state; /* state of transaction */ @@ -53,7 +57,7 @@ typedef struct transaction { } transaction_t; const char *transaction2rufnummer(transaction_t *trans); -transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended); +transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int futelg_bit, int extended); void destroy_transaction(transaction_t *trans); void link_transaction(transaction_t *trans, cnetz_t *cnetz); void unlink_transaction(transaction_t *trans);