From 57ba648ac4bad5d3f50249ecb0c24d0321282cb1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 8 May 2016 15:34:14 +0200 Subject: C-Netz: Add tiny database to track if phones are online Reject calls, if not online. Use an interval to check if the phones are still online. --- src/cnetz/Makefile.am | 1 + src/cnetz/cnetz.c | 85 +++++++++++++++++++++++++-- src/cnetz/cnetz.h | 31 +++++----- src/cnetz/database.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cnetz/database.h | 5 ++ src/cnetz/main.c | 3 + src/common/debug.c | 1 + src/common/debug.h | 1 + 8 files changed, 265 insertions(+), 18 deletions(-) create mode 100644 src/cnetz/database.c create mode 100644 src/cnetz/database.h diff --git a/src/cnetz/Makefile.am b/src/cnetz/Makefile.am index 9662317..236c1d0 100644 --- a/src/cnetz/Makefile.am +++ b/src/cnetz/Makefile.am @@ -5,6 +5,7 @@ bin_PROGRAMS = \ cnetz_SOURCES = \ cnetz.c \ + database.c \ sysinfo.c \ telegramm.c \ dsp.c \ diff --git a/src/cnetz/cnetz.c b/src/cnetz/cnetz.c index 5cba320..33fede5 100644 --- a/src/cnetz/cnetz.c +++ b/src/cnetz/cnetz.c @@ -28,6 +28,7 @@ #include "../common/call.h" #include "../common/cause.h" #include "cnetz.h" +#include "database.h" #include "sysinfo.h" #include "telegramm.h" #include "dsp.h" @@ -367,7 +368,13 @@ inval: futln_fuvst = dialing[1] - '0'; futln_rest = atoi(dialing + 2); - /* 2. check if given number is already in a call, return BUSY */ + /* 2. check if the subscriber is attached */ + if (!find_db(futln_nat, futln_fuvst, futln_rest)) { + PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call to not attached subscriber, rejecting!\n"); + return -CAUSE_OUTOFORDER; + } + + /* 3. check if given number is already in a call, return BUSY */ for (sender = sender_head; sender; sender = sender->next) { cnetz = (cnetz_t *) sender; /* search transaction for this number */ @@ -386,12 +393,13 @@ inval: return -CAUSE_BUSY; } - /* 3. check if all senders are busy, return NOCHANNEL */ + /* 4. check if all senders are busy, return NOCHANNEL */ if (!search_free_spk()) { PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but no free channel, rejecting!\n"); return -CAUSE_NOCHANNEL; } + /* 5. check if we have no OgK, return NOCHANNEL */ cnetz = search_ogk(); if (!cnetz) { PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but OgK is currently busy, rejecting!\n"); @@ -400,7 +408,7 @@ inval: PDEBUG(DCNETZ, DEBUG_INFO, "Call to mobile station, paging station id '%s'\n", dialing); - /* 4. trying to page mobile station */ + /* 6. trying to page mobile station */ cnetz->sender.callref = callref; trans = create_transaction(cnetz, TRANS_VAK, dialing[0] - '0', dialing[1] - '0', atoi(dialing + 2)); @@ -514,6 +522,25 @@ void call_out_release(int callref, int cause) } } +int cnetz_meldeaufruf(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest) +{ + cnetz_t *cnetz; + transaction_t *trans; + + cnetz = search_ogk(); + if (!cnetz) { + PDEBUG(DCNETZ, DEBUG_NOTICE, "'Meldeaufruf', but OgK is currently busy, rejecting!\n"); + return -CAUSE_NOCHANNEL; + } + trans = create_transaction(cnetz, TRANS_MA, futln_nat, futln_fuvst, futln_rest); + if (!trans) { + PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n"); + return -CAUSE_TEMPFAIL; + } + + return 0; +} + /* * Transaction handling */ @@ -575,6 +602,9 @@ static transaction_t *create_transaction(cnetz_t *cnetz, uint32_t state, uint8_t link_transaction(trans, cnetz); + /* update database: now busy */ + update_db(cnetz, futln_nat, futln_fuvst, futln_rest, 1, 0); + return trans; } @@ -598,6 +628,9 @@ static void unlink_transaction(transaction_t *trans) /* destroy transaction */ static void destroy_transaction(transaction_t *trans) { + /* update database: now idle */ + update_db(trans->cnetz, trans->futln_nat, trans->futln_fuvst, trans->futln_rest, 0, trans->ma_failed); + unlink_transaction(trans); const char *rufnummer = transaction2rufnummer(trans); @@ -626,6 +659,24 @@ static transaction_t *search_transaction(cnetz_t *cnetz, uint32_t state_mask) return NULL; } +static transaction_t *search_transaction_number(cnetz_t *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest) +{ + transaction_t *trans = cnetz->trans_list; + + while (trans) { + if (trans->futln_nat == futln_nat + && trans->futln_fuvst == futln_fuvst + && trans->futln_rest == futln_rest) { + const char *rufnummer = transaction2rufnummer(trans); + PDEBUG(DCNETZ, DEBUG_DEBUG, "Found transaction for subscriber '%s'\n", rufnummer); + return trans; + } + trans = trans->next; + } + + return NULL; +} + static const char *trans_state_name(int state) { switch (state) { @@ -637,6 +688,8 @@ static const char *trans_state_name(int state) return "UM"; case TRANS_MA: return "MA"; + case TRANS_MFT: + return "MFT"; case TRANS_VWG: return "VWG"; case TRANS_WAF: @@ -743,6 +796,8 @@ static void transaction_timeout(struct timer *timer) case TRANS_WAF: PDEBUG(DCNETZ, DEBUG_NOTICE, "No response after dialing request 'Wahlaufforderung'\n"); if (++trans->count == 3) { + /* no response to dialing is like MA failed */ + trans->ma_failed = 1; trans_new_state(trans, TRANS_WBN); break; } @@ -785,6 +840,10 @@ static void transaction_timeout(struct timer *timer) cnetz->sender.callref = 0; cnetz_release(trans, CNETZ_CAUSE_FUNKTECHNISCH); break; + case TRANS_MFT: + trans->ma_failed = 1; + destroy_transaction(trans); + break; default: PDEBUG(DCNETZ, DEBUG_ERROR, "Timeout unhandled in state %d\n", trans->state); } @@ -963,7 +1022,7 @@ const telegramm_t *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz) telegramm.ogk_vorschlag = CNETZ_OGK_KANAL; telegramm.fuz_rest_nr = si.fuz_rest; - trans = search_transaction(cnetz, TRANS_VWG); + trans = search_transaction(cnetz, TRANS_VWG | TRANS_MA); if (trans) { switch (trans->state) { case TRANS_VWG: @@ -975,6 +1034,15 @@ const telegramm_t *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz) trans_new_state(trans, TRANS_WAF); timer_start(&trans->timer, 4.0); /* Wait two slot cycles until resending */ break; + case TRANS_MA: + PDEBUG(DCNETZ, DEBUG_INFO, "Sending keepalive request 'Meldeaufruf'\n"); + telegramm.opcode = OPCODE_MA_M; + telegramm.futln_nationalitaet = trans->futln_nat; + telegramm.futln_heimat_fuvst_nr = trans->futln_fuvst; + telegramm.futln_rest_nr = trans->futln_rest; + trans_new_state(trans, TRANS_MFT); + timer_start(&trans->timer, 4.0); /* Wait two slot cycles until timeout */ + break; default: ; /* MLR */ } @@ -1066,6 +1134,15 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo trans_new_state(trans, TRANS_WBP); valid_frame = 1; break; + case OPCODE_MFT_M: + trans = search_transaction_number(cnetz, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr); + if (!trans) { + PDEBUG(DCNETZ, DEBUG_NOTICE, "Received acknowledge 'Meldun Funktelefonteilnemer' message without transaction, ignoring!\n"); + break; + } + destroy_transaction(trans); + valid_frame = 1; + break; default: PDEBUG(DCNETZ, DEBUG_NOTICE, "Received unexpected Telegramm (opcode %d = %s)\n", opcode, telegramm_name(opcode)); } diff --git a/src/cnetz/cnetz.h b/src/cnetz/cnetz.h index 66ca53e..531abb8 100644 --- a/src/cnetz/cnetz.h +++ b/src/cnetz/cnetz.h @@ -32,24 +32,25 @@ enum cnetz_state { /* roaming to different base station/network */ #define TRANS_UM (1 << 1) /* roaming request received, sending reply */ /* check if phone is still on */ -#define TRANS_MA (1 << 2) /* periodic online check sent, waiting for reply */ +#define TRANS_MA (1 << 2) /* periodic online check, waiting for time slot to send order */ +#define TRANS_MFT (1 << 3) /* periodic online check sent, waiting for reply */ /* mobile originated call */ -#define TRANS_VWG (1 << 3) /* received dialing request, waiting for time slot to send dial order */ -#define TRANS_WAF (1 << 4) /* dial order sent, waiting for dialing */ -#define TRANS_WBP (1 << 5) /* dialing received, waiting for time slot to acknowledge call */ -#define TRANS_WBN (1 << 6) /* dialing received, waiting for time slot to reject call */ -#define TRANS_VAG (1 << 7) /* establishment of call sent, switching channel */ +#define TRANS_VWG (1 << 4) /* received dialing request, waiting for time slot to send dial order */ +#define TRANS_WAF (1 << 5) /* dial order sent, waiting for dialing */ +#define TRANS_WBP (1 << 6) /* dialing received, waiting for time slot to acknowledge call */ +#define TRANS_WBN (1 << 7) /* dialing received, waiting for time slot to reject call */ +#define TRANS_VAG (1 << 8) /* establishment of call sent, switching channel */ /* mobile terminated call */ -#define TRANS_VAK (1 << 8) /* establishment of call sent, switching channel */ +#define TRANS_VAK (1 << 9) /* establishment of call sent, switching channel */ /* traffic channel */ -#define TRANS_BQ (1 << 9) /* accnowledge channel */ -#define TRANS_VHQ (1 << 10) /* hold call */ -#define TRANS_RTA (1 << 11) /* hold call and make the phone ring */ -#define TRANS_DS (1 << 12) /* establish speech connection */ -#define TRANS_AHQ (1 << 13) /* establish speech connection after answer */ +#define TRANS_BQ (1 << 10) /* accnowledge channel */ +#define TRANS_VHQ (1 << 11) /* hold call */ +#define TRANS_RTA (1 << 12) /* hold call and make the phone ring */ +#define TRANS_DS (1 << 13) /* establish speech connection */ +#define TRANS_AHQ (1 << 14) /* establish speech connection after answer */ /* release */ -#define TRANS_AF (1 << 14) /* release connection by base station */ -#define TRANS_AT (1 << 15) /* release connection by mobile station */ +#define TRANS_AF (1 << 15) /* release connection by base station */ +#define TRANS_AT (1 << 16) /* release connection by mobile station */ /* timers */ #define F_BQ 8 /* number of not received frames at BQ state */ @@ -81,6 +82,7 @@ typedef struct transaction { struct timer timer; /* for varous timeouts */ int mo_call; /* flags a moile originating call */ int mt_call; /* flags a moile terminating call */ + int ma_failed; /* failed to get a response from MS */ } transaction_t; struct clock_speed { @@ -157,6 +159,7 @@ int cnetz_init(void); int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, double rx_gain, int auth, int ms_power, int measure_speed, double clock_speed[2], double deviation, double noise, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback); void cnetz_destroy(sender_t *sender); void cnetz_sync_frame(cnetz_t *cnetz, double sync, int ts); +int cnetz_meldeaufruf(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest); const struct telegramm *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz); const struct telegramm *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz); void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, struct telegramm *telegramm, int block); diff --git a/src/cnetz/database.c b/src/cnetz/database.c new file mode 100644 index 0000000..59ff9e0 --- /dev/null +++ b/src/cnetz/database.c @@ -0,0 +1,156 @@ +/* C-Netz database + * + * (C) 2016 by Andreas Eversberg + * All Rights Reserved + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include +#include +#include "../common/debug.h" +#include "../common/timer.h" +#include "cnetz.h" +#include "database.h" + +#define MELDE_INTERVAL 60.0 +#define MELDE_WIEDERHOLUNG 10.0 +#define MELDE_MAXIMAL 3 + +typedef struct cnetz_database { + struct cnetz_database *next; + uint8_t futln_nat; /* who ... */ + uint8_t futln_fuvst; + uint16_t futln_rest; + struct timer timer; /* timer for next availability check */ + int retry; /* counts number of retries */ +} cnetz_db_t; + +cnetz_db_t *cnetz_db_head; + +/* destroy transaction */ +static void remove_db(cnetz_db_t *db) +{ + cnetz_db_t **dbp; + + /* uinlink */ + dbp = &cnetz_db_head; + while (*dbp && *dbp != db) + dbp = &((*dbp)->next); + if (!(*dbp)) { + PDEBUG(DDB, DEBUG_ERROR, "Transaction not in list, please fix!!\n"); + abort(); + } + *dbp = db->next; + + PDEBUG(DDB, DEBUG_INFO, "Removing subscriber '%d,%d,%d' from database.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); + + timer_exit(&db->timer); + + free(db); +} + +/* Timeout handling */ +static void db_timeout(struct timer *timer) +{ + cnetz_db_t *db = (cnetz_db_t *)timer->priv; + int rc; + + PDEBUG(DDB, DEBUG_INFO, "Check, if subscriber '%d,%d,%d' is still available.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); + + rc = cnetz_meldeaufruf(db->futln_nat, db->futln_fuvst, db->futln_rest); + if (rc < 0) { + /* OgK is used for speech, but this never happens in a real + * network. We just assume that the phone has responded and + * assume we had a response. */ + PDEBUG(DDB, DEBUG_INFO, "OgK busy, so we assume a positive response.\n"); + timer_start(&db->timer, MELDE_INTERVAL); /* when to check avaiability again */ + db->retry = 0; + } +} + +/* create/update db entry */ +void update_db(cnetz_t *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int busy, int failed) +{ + cnetz_db_t *db, **dbp; + + /* search transaction for this subsriber */ + db = cnetz_db_head; + while (db) { + if (db->futln_nat == futln_nat + && db->futln_fuvst == futln_fuvst + && db->futln_rest == futln_rest) + break; + db = db->next; + } + if (!db) { + db = calloc(1, sizeof(*db)); + if (!db) { + PDEBUG(DDB, DEBUG_ERROR, "No memory!\n"); + return; + } + timer_init(&db->timer, db_timeout, db); + + db->futln_nat = futln_nat; + db->futln_fuvst = futln_fuvst; + db->futln_rest = futln_rest; + + /* attach to end of list */ + dbp = &cnetz_db_head; + while (*dbp) + dbp = &((*dbp)->next); + *dbp = db; + + PDEBUG(DDB, DEBUG_INFO, "Adding subscriber '%d,%d,%d' to database.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); + } + + if (busy) { + PDEBUG(DDB, DEBUG_INFO, "Subscriber '%d,%d,%d' busy now.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); + timer_stop(&db->timer); + } else if (!failed) { + PDEBUG(DDB, DEBUG_INFO, "Subscriber '%d,%d,%d' idle now.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); + timer_start(&db->timer, MELDE_INTERVAL); /* when to check avaiability (again) */ + db->retry = 0; + } else { + if (++db->retry == MELDE_MAXIMAL) { + PDEBUG(DDB, DEBUG_NOTICE, "Paging subscriber '%d,%d,%d' failed.\n", db->futln_nat, db->futln_fuvst, db->futln_rest); + remove_db(db); + return; + } + timer_start(&db->timer, MELDE_WIEDERHOLUNG); /* when to do retry */ + } +} + +int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest) +{ + 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 1; + db = db->next; + } + return 0; +} + +void flush_db(void) +{ + while (cnetz_db_head) + remove_db(cnetz_db_head); +} + diff --git a/src/cnetz/database.h b/src/cnetz/database.h new file mode 100644 index 0000000..59fb0b8 --- /dev/null +++ b/src/cnetz/database.h @@ -0,0 +1,5 @@ + +void update_db(cnetz_t *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int busy, int failed); +int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest); +void flush_db(void); + diff --git a/src/cnetz/main.c b/src/cnetz/main.c index d9f45bd..2e65756 100644 --- a/src/cnetz/main.c +++ b/src/cnetz/main.c @@ -32,6 +32,7 @@ #include "../common/freiton.h" #include "../common/besetztton.h" #include "cnetz.h" +#include "database.h" #include "sysinfo.h" #include "dsp.h" #include "telegramm.h" @@ -292,6 +293,8 @@ fail: if (use_mncc_sock) mncc_exit(); + flush_db(); + /* destroy transceiver instance */ while (sender_head) cnetz_destroy(sender_head); diff --git a/src/common/debug.c b/src/common/debug.c index 146662d..2749241 100644 --- a/src/common/debug.c +++ b/src/common/debug.c @@ -43,6 +43,7 @@ struct debug_cat { { "frame", "\033[0;36m" }, { "call", "\033[1;37m" }, { "mncc", "\033[1;32m" }, + { "database", "\033[0;33m" }, }; int debuglevel = DEBUG_INFO; diff --git a/src/common/debug.h b/src/common/debug.h index 6351827..f0b0232 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -14,6 +14,7 @@ #define DFRAME 7 #define DCALL 8 #define DMNCC 9 +#define DDB 10 #define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, fmt, ## arg) void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *fmt, ...); -- cgit v1.2.3