From fba495e5f6084800c076e0ecae990ed9e6483530 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 23 Mar 2011 18:08:08 +0100 Subject: This patch moves the GSM-specific functions to the new library libosmogsm which is provided by libosmocore. I have also moved generate_backtrace() to backtrace.c instead of gsm_utils.c, otherwise the timer and msgfile tests depend on libosmogsm. Signed-off-by: Pablo Neira Ayuso --- src/Makefile.am | 14 +- src/backtrace.c | 50 ++ src/comp128.c | 230 ---------- src/gprs_cipher_core.c | 99 ---- src/gsm/Makefile.am | 13 + src/gsm/comp128.c | 230 ++++++++++ src/gsm/gprs_cipher_core.c | 99 ++++ src/gsm/gsm0480.c | 461 +++++++++++++++++++ src/gsm/gsm0808.c | 369 +++++++++++++++ src/gsm/gsm48.c | 415 +++++++++++++++++ src/gsm/gsm48_ie.c | 1095 ++++++++++++++++++++++++++++++++++++++++++++ src/gsm/gsm_utils.c | 465 +++++++++++++++++++ src/gsm/rsl.c | 371 +++++++++++++++ src/gsm/rxlev_stat.c | 94 ++++ src/gsm/tlv_parser.c | 179 ++++++++ src/gsm0480.c | 461 ------------------- src/gsm0808.c | 369 --------------- src/gsm48.c | 415 ----------------- src/gsm48_ie.c | 1095 -------------------------------------------- src/gsm_utils.c | 488 -------------------- src/panic.c | 1 + src/rsl.c | 371 --------------- src/rxlev_stat.c | 94 ---- src/tlv_parser.c | 179 -------- 24 files changed, 3849 insertions(+), 3808 deletions(-) create mode 100644 src/backtrace.c delete mode 100644 src/comp128.c delete mode 100644 src/gprs_cipher_core.c create mode 100644 src/gsm/Makefile.am create mode 100644 src/gsm/comp128.c create mode 100644 src/gsm/gprs_cipher_core.c create mode 100644 src/gsm/gsm0480.c create mode 100644 src/gsm/gsm0808.c create mode 100644 src/gsm/gsm48.c create mode 100644 src/gsm/gsm48_ie.c create mode 100644 src/gsm/gsm_utils.c create mode 100644 src/gsm/rsl.c create mode 100644 src/gsm/rxlev_stat.c create mode 100644 src/gsm/tlv_parser.c delete mode 100644 src/gsm0480.c delete mode 100644 src/gsm0808.c delete mode 100644 src/gsm48.c delete mode 100644 src/gsm48_ie.c delete mode 100644 src/gsm_utils.c delete mode 100644 src/rsl.c delete mode 100644 src/rxlev_stat.c delete mode 100644 src/tlv_parser.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 94492c53..c5c8a21d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=. vty codec +SUBDIRS=. vty codec gsm # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification @@ -9,12 +9,12 @@ AM_CFLAGS = -fPIC -Wall lib_LTLIBRARIES = libosmocore.la -libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c bits.c \ - tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \ - write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \ - logging.c logging_syslog.c gsm0808.c rate_ctr.c \ - gsmtap_util.c gprs_cipher_core.c crc16.c panic.c \ - process.c gsm0480.c +libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \ + bitvec.c statistics.c \ + write_queue.c utils.c \ + logging.c logging_syslog.c rate_ctr.c \ + gsmtap_util.c crc16.c panic.c backtrace.c \ + process.c if ENABLE_PLUGIN libosmocore_la_SOURCES += plugin.c diff --git a/src/backtrace.c b/src/backtrace.c new file mode 100644 index 00000000..42394453 --- /dev/null +++ b/src/backtrace.c @@ -0,0 +1,50 @@ +/* + * (C) 2008 by Daniel Willmann + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2009-2010 by Harald Welte + * (C) 2010 by Nico Golde + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include "config.h" + +#ifdef HAVE_EXECINFO_H +#include +void generate_backtrace() +{ + int i, nptrs; + void *buffer[100]; + char **strings; + + nptrs = backtrace(buffer, ARRAY_SIZE(buffer)); + printf("backtrace() returned %d addresses\n", nptrs); + + strings = backtrace_symbols(buffer, nptrs); + if (!strings) + return; + + for (i = 1; i < nptrs; i++) + printf("%s\n", strings[i]); + + free(strings); +} +#endif diff --git a/src/comp128.c b/src/comp128.c deleted file mode 100644 index 5d5680c7..00000000 --- a/src/comp128.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * COMP128 implementation - * - * - * This code is inspired by original code from : - * Marc Briceno , Ian Goldberg , - * and David Wagner - * - * But it has been fully rewritten from various PDFs found online describing - * the algorithm because the licence of the code referenced above was unclear. - * A comment snippet from the original code is included below, it describes - * where the doc came from and how the algorithm was reverse engineered. - * - * - * (C) 2009 by Sylvain Munaut - * - * 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 2 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -/* - * --- SNIP --- - * - * This code derived from a leaked document from the GSM standards. - * Some missing pieces were filled in by reverse-engineering a working SIM. - * We have verified that this is the correct COMP128 algorithm. - * - * The first page of the document identifies it as - * _Technical Information: GSM System Security Study_. - * 10-1617-01, 10th June 1988. - * The bottom of the title page is marked - * Racal Research Ltd. - * Worton Drive, Worton Grange Industrial Estate, - * Reading, Berks. RG2 0SB, England. - * Telephone: Reading (0734) 868601 Telex: 847152 - * The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy! - * - * Note: There are three typos in the spec (discovered by - * reverse-engineering). - * First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read - * "z = (2 * x[m] + x[n]) mod 2^(9-j)". - * Second, the "k" loop in the "Form bits from bytes" section is severely - * botched: the k index should run only from 0 to 3, and clearly the range - * on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8, - * to be consistent with the subsequent section). - * Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as - * claimed in the document. (And the document doesn't specify how Kc is - * derived, but that was also easily discovered with reverse engineering.) - * All of these typos have been corrected in the following code. - * - * --- /SNIP --- - */ - -#include -#include - -/* The compression tables (just copied ...) */ -static const uint8_t table_0[512] = { - 102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188, - 109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161, - 252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70, - 67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116, - 247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225, - 182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48, - 149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176, - 250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121, - 61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196, - 56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231, - 174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255, - 239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82, - 104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5, - 57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226, - 184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23, - 80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119, - 177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246, - 213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108, - 37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59, - 26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207, - 194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215, - 243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245, - 90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137, - 27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32, - 103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172, - 197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210, - 165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125, - 54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192, - 170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198, - 254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147, - 218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154, - 159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253, -}, table_1[256] = { - 19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43, - 27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5, - 35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6, - 53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20, - 90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78, - 76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51, - 57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67, - 12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75, - 95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29, - 82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114, - 82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74, - 113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73, - 118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83, - 104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126, - 31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52, - 101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35, -}, table_2[128] = { - 52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43, - 37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18, - 55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59, - 62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56, - 48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61, - 29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0, - 20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27, - 31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7, -}, table_3[64] = { - 1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31, - 28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9, - 20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10, - 17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19, -}, table_4[32] = { - 15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8, - 10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12, -}; - -static const uint8_t *_comp128_table[5] = { table_0, table_1, table_2, table_3, table_4 }; - - -static inline void -_comp128_compression_round(uint8_t *x, int n, const uint8_t *tbl) -{ - int i, j, m, a, b, y, z; - m = 4 - n; - for (i=0; i<(1<>2] & (1<<(3-(i&3)))) - bits[i] = 1; -} - -static inline void -_comp128_permutation(uint8_t *x, uint8_t *bits) -{ - int i; - memset(&x[16], 0x00, 16); - for (i=0; i<128; i++) - x[(i>>3)+16] |= bits[(i*17) & 127] << (7-(i&7)); -} - -void -comp128(uint8_t *ki, uint8_t *rand, uint8_t *sres, uint8_t *kc) -{ - int i; - uint8_t x[32], bits[128]; - - /* x[16-31] = RAND */ - memcpy(&x[16], rand, 16); - - /* Round 1-7 */ - for (i=0; i<7; i++) { - /* x[0-15] = Ki */ - memcpy(x, ki, 16); - - /* Compression */ - _comp128_compression(x); - - /* FormBitFromBytes */ - _comp128_bitsfrombytes(x, bits); - - /* Permutation */ - _comp128_permutation(x, bits); - } - - /* Round 8 (final) */ - /* x[0-15] = Ki */ - memcpy(x, ki, 16); - - /* Compression */ - _comp128_compression(x); - - /* Output stage */ - for (i=0; i<8; i+=2) - sres[i>>1] = x[i]<<4 | x[i+1]; - - for (i=0; i<12; i+=2) - kc[i>>1] = (x[i + 18] << 6) | - (x[i + 19] << 2) | - (x[i + 20] >> 2); - - kc[6] = (x[30]<<6) | (x[31]<<2); - kc[7] = 0; -} - diff --git a/src/gprs_cipher_core.c b/src/gprs_cipher_core.c deleted file mode 100644 index 6174bd72..00000000 --- a/src/gprs_cipher_core.c +++ /dev/null @@ -1,99 +0,0 @@ -/* GPRS LLC cipher core infrastructure */ - -/* (C) 2010 by Harald Welte - * - * 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 2 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include - -#include -#include -#include - -#include - -static LLIST_HEAD(gprs_ciphers); - -static struct gprs_cipher_impl *selected_ciphers[_GPRS_ALGO_NUM]; - -/* register a cipher with the core */ -int gprs_cipher_register(struct gprs_cipher_impl *ciph) -{ - if (ciph->algo > ARRAY_SIZE(selected_ciphers)) - return -ERANGE; - - llist_add_tail(&ciph->list, &gprs_ciphers); - - /* check if we want to select this implementation over others */ - if (!selected_ciphers[ciph->algo] || - (selected_ciphers[ciph->algo]->priority > ciph->priority)) - selected_ciphers[ciph->algo] = ciph; - - return 0; -} - -/* load all available GPRS cipher plugins */ -int gprs_cipher_load(const char *path) -{ - /* load all plugins available from path */ - return plugin_load_all(path); -} - -/* function to be called by core code */ -int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo, - uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir) -{ - if (algo > ARRAY_SIZE(selected_ciphers)) - return -ERANGE; - - if (!selected_ciphers[algo]) - return -EINVAL; - - if (len > GSM0464_CIPH_MAX_BLOCK) - return -ERANGE; - - /* run the actual cipher from the plugin */ - return selected_ciphers[algo]->run(out, len, kc, iv, dir); -} - -int gprs_cipher_supported(enum gprs_ciph_algo algo) -{ - if (algo > ARRAY_SIZE(selected_ciphers)) - return -ERANGE; - - if (selected_ciphers[algo]) - return 1; - - return 0; -} - -/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ -uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc) -{ - uint32_t sx = ((1<<27) * sapi) + (1<<31); - - return (iov_ui ^ sx) + lfn + oc; -} - -/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ -uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc) -{ - return iov_i + lfn + oc; -} diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am new file mode 100644 index 00000000..a8c2e569 --- /dev/null +++ b/src/gsm/Makefile.am @@ -0,0 +1,13 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification +LIBVERSION=0:0:0 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -fPIC -Wall + +lib_LTLIBRARIES = libosmogsm.la + +libosmogsm_la_SOURCES = rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \ + rsl.c gsm48.c gsm48_ie.c gsm0808.c \ + gprs_cipher_core.c gsm0480.c +libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/gsm/comp128.c b/src/gsm/comp128.c new file mode 100644 index 00000000..5d5680c7 --- /dev/null +++ b/src/gsm/comp128.c @@ -0,0 +1,230 @@ +/* + * COMP128 implementation + * + * + * This code is inspired by original code from : + * Marc Briceno , Ian Goldberg , + * and David Wagner + * + * But it has been fully rewritten from various PDFs found online describing + * the algorithm because the licence of the code referenced above was unclear. + * A comment snippet from the original code is included below, it describes + * where the doc came from and how the algorithm was reverse engineered. + * + * + * (C) 2009 by Sylvain Munaut + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* + * --- SNIP --- + * + * This code derived from a leaked document from the GSM standards. + * Some missing pieces were filled in by reverse-engineering a working SIM. + * We have verified that this is the correct COMP128 algorithm. + * + * The first page of the document identifies it as + * _Technical Information: GSM System Security Study_. + * 10-1617-01, 10th June 1988. + * The bottom of the title page is marked + * Racal Research Ltd. + * Worton Drive, Worton Grange Industrial Estate, + * Reading, Berks. RG2 0SB, England. + * Telephone: Reading (0734) 868601 Telex: 847152 + * The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy! + * + * Note: There are three typos in the spec (discovered by + * reverse-engineering). + * First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read + * "z = (2 * x[m] + x[n]) mod 2^(9-j)". + * Second, the "k" loop in the "Form bits from bytes" section is severely + * botched: the k index should run only from 0 to 3, and clearly the range + * on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8, + * to be consistent with the subsequent section). + * Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as + * claimed in the document. (And the document doesn't specify how Kc is + * derived, but that was also easily discovered with reverse engineering.) + * All of these typos have been corrected in the following code. + * + * --- /SNIP --- + */ + +#include +#include + +/* The compression tables (just copied ...) */ +static const uint8_t table_0[512] = { + 102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188, + 109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161, + 252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70, + 67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116, + 247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225, + 182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48, + 149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176, + 250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121, + 61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196, + 56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231, + 174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255, + 239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82, + 104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5, + 57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226, + 184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23, + 80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119, + 177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246, + 213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108, + 37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59, + 26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207, + 194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215, + 243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245, + 90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137, + 27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32, + 103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172, + 197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210, + 165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125, + 54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192, + 170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198, + 254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147, + 218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154, + 159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253, +}, table_1[256] = { + 19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43, + 27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5, + 35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6, + 53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20, + 90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78, + 76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51, + 57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67, + 12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75, + 95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29, + 82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114, + 82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74, + 113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73, + 118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83, + 104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126, + 31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52, + 101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35, +}, table_2[128] = { + 52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43, + 37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18, + 55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59, + 62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56, + 48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61, + 29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0, + 20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27, + 31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7, +}, table_3[64] = { + 1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31, + 28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9, + 20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10, + 17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19, +}, table_4[32] = { + 15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8, + 10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12, +}; + +static const uint8_t *_comp128_table[5] = { table_0, table_1, table_2, table_3, table_4 }; + + +static inline void +_comp128_compression_round(uint8_t *x, int n, const uint8_t *tbl) +{ + int i, j, m, a, b, y, z; + m = 4 - n; + for (i=0; i<(1<>2] & (1<<(3-(i&3)))) + bits[i] = 1; +} + +static inline void +_comp128_permutation(uint8_t *x, uint8_t *bits) +{ + int i; + memset(&x[16], 0x00, 16); + for (i=0; i<128; i++) + x[(i>>3)+16] |= bits[(i*17) & 127] << (7-(i&7)); +} + +void +comp128(uint8_t *ki, uint8_t *rand, uint8_t *sres, uint8_t *kc) +{ + int i; + uint8_t x[32], bits[128]; + + /* x[16-31] = RAND */ + memcpy(&x[16], rand, 16); + + /* Round 1-7 */ + for (i=0; i<7; i++) { + /* x[0-15] = Ki */ + memcpy(x, ki, 16); + + /* Compression */ + _comp128_compression(x); + + /* FormBitFromBytes */ + _comp128_bitsfrombytes(x, bits); + + /* Permutation */ + _comp128_permutation(x, bits); + } + + /* Round 8 (final) */ + /* x[0-15] = Ki */ + memcpy(x, ki, 16); + + /* Compression */ + _comp128_compression(x); + + /* Output stage */ + for (i=0; i<8; i+=2) + sres[i>>1] = x[i]<<4 | x[i+1]; + + for (i=0; i<12; i+=2) + kc[i>>1] = (x[i + 18] << 6) | + (x[i + 19] << 2) | + (x[i + 20] >> 2); + + kc[6] = (x[30]<<6) | (x[31]<<2); + kc[7] = 0; +} + diff --git a/src/gsm/gprs_cipher_core.c b/src/gsm/gprs_cipher_core.c new file mode 100644 index 00000000..6174bd72 --- /dev/null +++ b/src/gsm/gprs_cipher_core.c @@ -0,0 +1,99 @@ +/* GPRS LLC cipher core infrastructure */ + +/* (C) 2010 by Harald Welte + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include +#include +#include + +#include + +static LLIST_HEAD(gprs_ciphers); + +static struct gprs_cipher_impl *selected_ciphers[_GPRS_ALGO_NUM]; + +/* register a cipher with the core */ +int gprs_cipher_register(struct gprs_cipher_impl *ciph) +{ + if (ciph->algo > ARRAY_SIZE(selected_ciphers)) + return -ERANGE; + + llist_add_tail(&ciph->list, &gprs_ciphers); + + /* check if we want to select this implementation over others */ + if (!selected_ciphers[ciph->algo] || + (selected_ciphers[ciph->algo]->priority > ciph->priority)) + selected_ciphers[ciph->algo] = ciph; + + return 0; +} + +/* load all available GPRS cipher plugins */ +int gprs_cipher_load(const char *path) +{ + /* load all plugins available from path */ + return plugin_load_all(path); +} + +/* function to be called by core code */ +int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo, + uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir) +{ + if (algo > ARRAY_SIZE(selected_ciphers)) + return -ERANGE; + + if (!selected_ciphers[algo]) + return -EINVAL; + + if (len > GSM0464_CIPH_MAX_BLOCK) + return -ERANGE; + + /* run the actual cipher from the plugin */ + return selected_ciphers[algo]->run(out, len, kc, iv, dir); +} + +int gprs_cipher_supported(enum gprs_ciph_algo algo) +{ + if (algo > ARRAY_SIZE(selected_ciphers)) + return -ERANGE; + + if (selected_ciphers[algo]) + return 1; + + return 0; +} + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc) +{ + uint32_t sx = ((1<<27) * sapi) + (1<<31); + + return (iov_ui ^ sx) + lfn + oc; +} + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc) +{ + return iov_i + lfn + oc; +} diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c new file mode 100644 index 00000000..b6b345cb --- /dev/null +++ b/src/gsm/gsm0480.c @@ -0,0 +1,461 @@ +/* Format functions for GSM 04.80 */ + +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2009 by Mike Haben + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include + +#include +#include + +#include + +static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) +{ + uint8_t *data = msgb_push(msgb, 2); + + data[0] = tag; + data[1] = msgb->len - 2; + return data; +} + +static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, + uint8_t value) +{ + uint8_t *data = msgb_push(msgb, 3); + + data[0] = tag; + data[1] = 1; + data[2] = value; + return data; +} + +/* wrap an invoke around it... the other way around + * + * 1.) Invoke Component tag + * 2.) Invoke ID Tag + * 3.) Operation + * 4.) Data + */ +int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id) +{ + /* 3. operation */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, op); + + /* 2. invoke id tag */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id); + + /* 1. component tag */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_INVOKE); + + return 0; +} + +/* wrap the GSM 04.08 Facility IE around it */ +int gsm0480_wrap_facility(struct msgb *msg) +{ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + return 0; +} + +struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text) +{ + struct msgb *msg; + uint8_t *seq_len_ptr, *ussd_len_ptr, *data; + int len; + + msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + if (!msg) + return NULL; + + /* SEQUENCE { */ + msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG); + seq_len_ptr = msgb_put(msg, 1); + + /* DCS { */ + msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, 0x0F); + /* } DCS */ + + /* USSD-String { */ + msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); + ussd_len_ptr = msgb_put(msg, 1); + data = msgb_put(msg, 0); + len = gsm_7bit_encode(data, text); + msgb_put(msg, len); + ussd_len_ptr[0] = len; + /* USSD-String } */ + + /* alertingPattern { */ + msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, alertPattern); + /* } alertingPattern */ + + seq_len_ptr[0] = 3 + 2 + ussd_len_ptr[0] + 3; + /* } SEQUENCE */ + + return msg; +} + +struct msgb *gsm0480_create_notifySS(const char *text) +{ + struct msgb *msg; + uint8_t *data, *tmp_len; + uint8_t *seq_len_ptr, *cal_len_ptr, *opt_len_ptr, *nam_len_ptr; + int len; + + len = strlen(text); + if (len < 1 || len > 160) + return NULL; + + msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + if (!msg) + return NULL; + + msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG); + seq_len_ptr = msgb_put(msg, 1); + + /* ss_code for CNAP { */ + msgb_put_u8(msg, 0x81); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, 0x19); + /* } ss_code */ + + + /* nameIndicator { */ + msgb_put_u8(msg, 0xB4); + nam_len_ptr = msgb_put(msg, 1); + + /* callingName { */ + msgb_put_u8(msg, 0xA0); + opt_len_ptr = msgb_put(msg, 1); + msgb_put_u8(msg, 0xA0); + cal_len_ptr = msgb_put(msg, 1); + + /* namePresentationAllowed { */ + /* add the DCS value */ + msgb_put_u8(msg, 0x80); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, 0x0F); + + /* add the lengthInCharacters */ + msgb_put_u8(msg, 0x81); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, strlen(text)); + + /* add the actual string */ + msgb_put_u8(msg, 0x82); + tmp_len = msgb_put(msg, 1); + data = msgb_put(msg, 0); + len = gsm_7bit_encode(data, text); + tmp_len[0] = len; + msgb_put(msg, len); + + /* }; namePresentationAllowed */ + + cal_len_ptr[0] = 3 + 3 + 2 + len; + opt_len_ptr[0] = cal_len_ptr[0] + 2; + /* }; callingName */ + + nam_len_ptr[0] = opt_len_ptr[0] + 2; + /* ); nameIndicator */ + + /* write the lengths... */ + seq_len_ptr[0] = 3 + nam_len_ptr[0] + 2; + + return msg; +} + +/* Forward declarations */ +static int parse_ussd(const struct gsm48_hdr *hdr, + uint16_t len, struct ussd_request *req); +static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len, + struct ussd_request *req); +static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, + struct ussd_request *req); +static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, + struct ussd_request *req); +static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, + struct ussd_request *req); + +/* Decode a mobile-originated USSD-request message */ +int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, + struct ussd_request *req) +{ + int rc = 0; + + if (len < sizeof(*hdr) + 2) { + LOGP(0, LOGL_DEBUG, "USSD Request is too short.\n"); + return 0; + } + + if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) { + req->transaction_id = hdr->proto_discr & 0x70; + rc = parse_ussd(hdr, len, req); + } + + if (!rc) + LOGP(0, LOGL_DEBUG, "Error occurred while parsing received USSD!\n"); + + return rc; +} + +static int parse_ussd(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *req) +{ + int rc = 1; + uint8_t msg_type = hdr->msg_type & 0xBF; /* message-type - section 3.4 */ + + switch (msg_type) { + case GSM0480_MTYPE_RELEASE_COMPLETE: + LOGP(0, LOGL_DEBUG, "USS Release Complete\n"); + /* could also parse out the optional Cause/Facility data */ + req->text[0] = 0xFF; + break; + case GSM0480_MTYPE_REGISTER: + case GSM0480_MTYPE_FACILITY: + rc &= parse_ussd_info_elements(&hdr->data[0], len - sizeof(*hdr), req); + break; + default: + LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n", + hdr->msg_type); + rc = 0; + break; + } + + return rc; +} + +static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len, + struct ussd_request *req) +{ + int rc = -1; + /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */ + uint8_t iei; + uint8_t iei_length; + + iei = ussd_ie[0]; + iei_length = ussd_ie[1]; + + /* If the data does not fit, report an error */ + if (len - 2 < iei_length) + return 0; + + switch (iei) { + case GSM48_IE_CAUSE: + break; + case GSM0480_IE_FACILITY: + rc = parse_facility_ie(ussd_ie+2, iei_length, req); + break; + case GSM0480_IE_SS_VERSION: + break; + default: + LOGP(0, LOGL_DEBUG, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n", + iei); + rc = 0; + break; + } + + return rc; +} + +static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, + struct ussd_request *req) +{ + int rc = 1; + uint8_t offset = 0; + + while (offset + 2 <= length) { + /* Component Type tag - table 3.7 */ + uint8_t component_type = facility_ie[offset]; + uint8_t component_length = facility_ie[offset+1]; + + /* size check */ + if (offset + 2 + component_length > length) { + LOGP(0, LOGL_ERROR, "Component does not fit.\n"); + return 0; + } + + switch (component_type) { + case GSM0480_CTYPE_INVOKE: + rc &= parse_ss_invoke(facility_ie+2, + component_length, + req); + break; + case GSM0480_CTYPE_RETURN_RESULT: + break; + case GSM0480_CTYPE_RETURN_ERROR: + break; + case GSM0480_CTYPE_REJECT: + break; + default: + LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 Facility " + "Component Type 0x%02x\n", component_type); + rc = 0; + break; + } + offset += (component_length+2); + }; + + return rc; +} + +/* Parse an Invoke component - see table 3.3 */ +static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, + struct ussd_request *req) +{ + int rc = 1; + uint8_t offset; + + if (length < 3) + return 0; + + /* mandatory part */ + if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) { + LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag " + "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]); + } + + offset = invoke_data[1] + 2; + req->invoke_id = invoke_data[2]; + + /* look ahead once */ + if (offset + 1 > length) + return 0; + + /* optional part */ + if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID) + offset += invoke_data[offset+1] + 2; /* skip over it */ + + /* mandatory part */ + if (invoke_data[offset] == GSM0480_OPERATION_CODE) { + if (offset + 2 > length) + return 0; + uint8_t operation_code = invoke_data[offset+2]; + switch (operation_code) { + case GSM0480_OP_CODE_PROCESS_USS_REQ: + rc = parse_process_uss_req(invoke_data + offset + 3, + length - offset - 3, + req); + break; + default: + LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x " + "is not yet handled\n", operation_code); + rc = 0; + break; + } + } else { + LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag 0x%02x " + "(expecting Operation Code tag)\n", + invoke_data[0]); + rc = 0; + } + + return rc; +} + +/* Parse the parameters of a Process UnstructuredSS Request */ +static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, + struct ussd_request *req) +{ + int rc = 0; + int num_chars; + uint8_t dcs; + + + /* we need at least that much */ + if (length < 8) + return 0; + + + if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) { + if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) { + dcs = uss_req_data[4]; + if ((dcs == 0x0F) && + (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) { + num_chars = (uss_req_data[6] * 8) / 7; + /* Prevent a mobile-originated buffer-overrun! */ + if (num_chars > MAX_LEN_USSD_STRING) + num_chars = MAX_LEN_USSD_STRING; + gsm_7bit_decode(req->text, + &(uss_req_data[7]), num_chars); + rc = 1; + } + } + } + return rc; +} + +struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text) +{ + struct msgb *msg; + struct gsm48_hdr *gh; + uint8_t *ptr8; + int response_len; + + msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + if (!msg) + return NULL; + + /* First put the payload text into the message */ + ptr8 = msgb_put(msg, 0); + response_len = gsm_7bit_encode(ptr8, text); + msgb_put(msg, response_len); + + /* Then wrap it as an Octet String */ + msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); + + /* Pre-pend the DCS octet string */ + msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); + + /* Then wrap these as a Sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the operation code */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, + GSM0480_OP_CODE_PROCESS_USS_REQ); + + /* Wrap the operation code and IA5 string as a sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); + + /* Wrap this up as a Return Result component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); + + /* Wrap the component in a Facility message */ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS | trans_id + | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return msg; +} diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c new file mode 100644 index 00000000..dc450cc4 --- /dev/null +++ b/src/gsm/gsm0808.c @@ -0,0 +1,369 @@ +/* (C) 2009,2010 by Holger Hans Peter Freyther + * (C) 2009,2010 by On-Waves + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include + +#define BSSMAP_MSG_SIZE 512 +#define BSSMAP_MSG_HEADROOM 128 + +static void put_data_16(uint8_t *data, const uint16_t val) +{ + memcpy(data, &val, sizeof(val)); +} + +struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, uint16_t _ci) +{ + uint8_t *data; + uint8_t *ci; + struct msgb* msg; + struct gsm48_loc_area_id *lai; + + msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap cmpl l3"); + if (!msg) + return NULL; + + /* create the bssmap header */ + msg->l3h = msgb_put(msg, 2); + msg->l3h[0] = 0x0; + + /* create layer 3 header */ + data = msgb_put(msg, 1); + data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3; + + /* create the cell header */ + data = msgb_put(msg, 3); + data[0] = GSM0808_IE_CELL_IDENTIFIER; + data[1] = 1 + sizeof(*lai) + 2; + data[2] = CELL_IDENT_WHOLE_GLOBAL; + + lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); + gsm48_generate_lai(lai, cc, nc, lac); + + ci = msgb_put(msg, 2); + put_data_16(ci, htons(_ci)); + + /* copy the layer3 data */ + data = msgb_put(msg, msgb_l3len(msg_l3) + 2); + data[0] = GSM0808_IE_LAYER_3_INFORMATION; + data[1] = msgb_l3len(msg_l3); + memcpy(&data[2], msg_l3->l3h, data[1]); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + + return msg; +} + +struct msgb *gsm0808_create_reset(void) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: reset"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 6); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0x04; + msg->l3h[2] = 0x30; + msg->l3h[3] = 0x04; + msg->l3h[4] = 0x01; + msg->l3h[5] = 0x20; + return msg; +} + +struct msgb *gsm0808_create_clear_complete(void) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 1; + msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE; + + return msg; +} + +struct msgb *gsm0808_create_clear_command(uint8_t reason) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear command"); + if (!msg) + return NULL; + + msg->l3h = msgb_tv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 4); + msgb_v_put(msg, BSS_MAP_MSG_CLEAR_CMD); + msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &reason); + return msg; +} + +struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "cipher-complete"); + if (!msg) + return NULL; + + /* send response with BSS override for A5/1... cheating */ + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE; + + /* include layer3 in case we have at least two octets */ + if (layer3 && msgb_l3len(layer3) > 2) { + msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2); + msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS; + msg->l4h[1] = msgb_l3len(layer3); + memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3)); + } + + /* and the optional BSS message */ + msg->l4h = msgb_put(msg, 2); + msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + msg->l4h[1] = alg_id; + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_cipher_reject(uint8_t cause) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 2; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT; + msg->l3h[3] = cause; + + return msg; +} + +struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark_data, uint8_t length) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "classmark-update"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE; + + msg->l4h = msgb_put(msg, length); + memcpy(msg->l4h, classmark_data, length); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_sapi_reject(uint8_t link_id) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: sapi 'n' reject"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 5); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 3; + msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT; + msg->l3h[3] = link_id; + msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED; + + return msg; +} + +struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause, + uint8_t chosen_channel, uint8_t encr_alg_id, + uint8_t speech_mode) +{ + uint8_t *data; + + struct msgb *msg = msgb_alloc(35, "bssmap: ass compl"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE; + + /* write 3.2.2.22 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = rr_cause; + + /* write cirtcuit identity code 3.2.2.2 */ + /* write cell identifier 3.2.2.17 */ + /* write chosen channel 3.2.2.33 when BTS picked it */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_CHANNEL; + data[1] = chosen_channel; + + /* write chosen encryption algorithm 3.2.2.44 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + data[1] = encr_alg_id; + + /* write circuit pool 3.2.2.45 */ + /* write speech version chosen: 3.2.2.51 when BTS picked it */ + if (speech_mode != 0) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_SPEECH_VERSION; + data[1] = speech_mode; + } + + /* write LSA identifier 3.2.2.15 */ + + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause) +{ + uint8_t *data; + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: ass fail"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 6); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE; + msg->l3h[3] = GSM0808_IE_CAUSE; + msg->l3h[4] = 1; + msg->l3h[5] = cause; + + /* RR cause 3.2.2.22 */ + if (rr_cause) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = *rr_cause; + } + + /* Circuit pool 3.22.45 */ + /* Circuit pool list 3.2.2.46 */ + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_clear_rqst(uint8_t cause) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear rqst"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 2 + 4); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 4; + + msg->l3h[2] = BSS_MAP_MSG_CLEAR_RQST; + msg->l3h[3] = GSM0808_IE_CAUSE; + msg->l3h[4] = 1; + msg->l3h[5] = cause; + return msg; +} + +void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id) +{ + uint8_t *hh = msgb_push(msg, 3); + hh[0] = BSSAP_MSG_DTAP; + hh[1] = link_id; + hh[2] = msg->len - 3; +} + +struct msgb *gsm0808_create_dtap(struct msgb *msg_l3, uint8_t link_id) +{ + struct dtap_header *header; + uint8_t *data; + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "dtap"); + if (!msg) + return NULL; + + /* DTAP header */ + msg->l3h = msgb_put(msg, sizeof(*header)); + header = (struct dtap_header *) &msg->l3h[0]; + header->type = BSSAP_MSG_DTAP; + header->link_id = link_id; + header->length = msgb_l3len(msg_l3); + + /* Payload */ + data = msgb_put(msg, header->length); + memcpy(data, msg_l3->l3h, header->length); + + return msg; +} + +static const struct tlv_definition bss_att_tlvdef = { + .def = { + [GSM0808_IE_IMSI] = { TLV_TYPE_TLV }, + [GSM0808_IE_TMSI] = { TLV_TYPE_TLV }, + [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV }, + [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV }, + [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV }, + [GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV }, + [GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_FIXED, 2 }, + [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV }, + [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV }, + [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV }, + [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV }, + [GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T }, + [GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV }, + [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV }, + [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TLV }, + [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV }, + [GSM0808_IE_CELL_IDENTIFIER] = { TLV_TYPE_TLV }, + [GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV }, + [GSM0808_IE_LAYER_3_INFORMATION] = { TLV_TYPE_TLV }, + [GSM0808_IE_SPEECH_VERSION] = { TLV_TYPE_TV }, + [GSM0808_IE_CHOSEN_ENCR_ALG] = { TLV_TYPE_TV }, + }, +}; + +const struct tlv_definition *gsm0808_att_tlvdef() +{ + return &bss_att_tlvdef; +} diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c new file mode 100644 index 00000000..daec4f39 --- /dev/null +++ b/src/gsm/gsm48.c @@ -0,0 +1,415 @@ +/* GSM Mobile Radio Interface Layer 3 messages + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2008, 2009 by Holger Hans Peter Freyther + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +const struct tlv_definition gsm48_att_tlvdef = { + .def = { + [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV }, + [GSM48_IE_UTC] = { TLV_TYPE_TV }, + [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 }, + [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV }, + + [GSM48_IE_BEARER_CAP] = { TLV_TYPE_TLV }, + [GSM48_IE_CAUSE] = { TLV_TYPE_TLV }, + [GSM48_IE_CC_CAP] = { TLV_TYPE_TLV }, + [GSM48_IE_ALERT] = { TLV_TYPE_TLV }, + [GSM48_IE_FACILITY] = { TLV_TYPE_TLV }, + [GSM48_IE_PROGR_IND] = { TLV_TYPE_TLV }, + [GSM48_IE_AUX_STATUS] = { TLV_TYPE_TLV }, + [GSM48_IE_NOTIFY] = { TLV_TYPE_TV }, + [GSM48_IE_KPD_FACILITY] = { TLV_TYPE_TV }, + [GSM48_IE_SIGNAL] = { TLV_TYPE_TV }, + [GSM48_IE_CONN_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CONN_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLING_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLING_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLED_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLED_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_REDIR_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_REDIR_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_LOWL_COMPAT] = { TLV_TYPE_TLV }, + [GSM48_IE_HIGHL_COMPAT] = { TLV_TYPE_TLV }, + [GSM48_IE_USER_USER] = { TLV_TYPE_TLV }, + [GSM48_IE_SS_VERS] = { TLV_TYPE_TLV }, + [GSM48_IE_MORE_DATA] = { TLV_TYPE_T }, + [GSM48_IE_CLIR_SUPP] = { TLV_TYPE_T }, + [GSM48_IE_CLIR_INVOC] = { TLV_TYPE_T }, + [GSM48_IE_REV_C_SETUP] = { TLV_TYPE_T }, + [GSM48_IE_REPEAT_CIR] = { TLV_TYPE_T }, + [GSM48_IE_REPEAT_SEQ] = { TLV_TYPE_T }, + /* FIXME: more elements */ + }, +}; + +/* RR elements */ +const struct tlv_definition gsm48_rr_att_tlvdef = { + .def = { + /* NOTE: Don't add IE 17 = MOBILE_ID here, it already used. */ + [GSM48_IE_VGCS_TARGET] = { TLV_TYPE_TLV }, + [GSM48_IE_FRQSHORT_AFTER] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_MUL_RATE_CFG] = { TLV_TYPE_TLV }, + [GSM48_IE_FREQ_L_AFTER] = { TLV_TYPE_TLV }, + [GSM48_IE_MSLOT_DESC] = { TLV_TYPE_TLV }, + [GSM48_IE_CHANMODE_2] = { TLV_TYPE_TV }, + [GSM48_IE_FRQSHORT_BEFORE] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_CHANMODE_3] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_4] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_5] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_6] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_7] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_8] = { TLV_TYPE_TV }, + [GSM48_IE_FREQ_L_BEFORE] = { TLV_TYPE_TLV }, + [GSM48_IE_CH_DESC_1_BEFORE] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_CH_DESC_2_BEFORE] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_F_CH_SEQ_BEFORE] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_CLASSMARK3] = { TLV_TYPE_TLV }, + [GSM48_IE_MA_BEFORE] = { TLV_TYPE_TLV }, + [GSM48_IE_RR_PACKET_UL] = { TLV_TYPE_TLV }, + [GSM48_IE_RR_PACKET_DL] = { TLV_TYPE_TLV }, + [GSM48_IE_CELL_CH_DESC] = { TLV_TYPE_FIXED, 16 }, + [GSM48_IE_CHANMODE_1] = { TLV_TYPE_TV }, + [GSM48_IE_CHDES_2_AFTER] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_MODE_SEC_CH] = { TLV_TYPE_TV }, + [GSM48_IE_F_CH_SEQ_AFTER] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_MA_AFTER] = { TLV_TYPE_TLV }, + [GSM48_IE_BA_RANGE] = { TLV_TYPE_TLV }, + [GSM48_IE_GROUP_CHDES] = { TLV_TYPE_TLV }, + [GSM48_IE_BA_LIST_PREF] = { TLV_TYPE_TLV }, + [GSM48_IE_MOB_OVSERV_DIF] = { TLV_TYPE_TLV }, + [GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV }, + [GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 }, + [GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV }, + [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV }, + }, +}; + +/* MM elements */ +const struct tlv_definition gsm48_mm_att_tlvdef = { + .def = { + [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV }, + [GSM48_IE_UTC] = { TLV_TYPE_TV }, + [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 }, + [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV }, + + [GSM48_IE_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 }, + [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T }, + [GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T }, + }, +}; + +static const struct value_string rr_cause_names[] = { + { GSM48_RR_CAUSE_NORMAL, "Normal event" }, + { GSM48_RR_CAUSE_ABNORMAL_UNSPEC, "Abnormal release, unspecified" }, + { GSM48_RR_CAUSE_ABNORMAL_UNACCT, "Abnormal release, channel unacceptable" }, + { GSM48_RR_CAUSE_ABNORMAL_TIMER, "Abnormal release, timer expired" }, + { GSM48_RR_CAUSE_ABNORMAL_NOACT, "Abnormal release, no activity on radio path" }, + { GSM48_RR_CAUSE_PREMPTIVE_REL, "Preemptive release" }, + { GSM48_RR_CAUSE_HNDOVER_IMP, "Handover impossible, timing advance out of range" }, + { GSM48_RR_CAUSE_CHAN_MODE_UNACCT, "Channel mode unacceptable" }, + { GSM48_RR_CAUSE_FREQ_NOT_IMPL, "Frequency not implemented" }, + { GSM48_RR_CAUSE_CALL_CLEARED, "Call already cleared" }, + { GSM48_RR_CAUSE_SEMANT_INCORR, "Semantically incorrect message" }, + { GSM48_RR_CAUSE_INVALID_MAND_INF, "Invalid mandatory information" }, + { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existant or not implemented" }, + { GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT, "Message type not compatible with protocol state" }, + { GSM48_RR_CAUSE_COND_IE_ERROR, "Conditional IE error" }, + { GSM48_RR_CAUSE_NO_CELL_ALLOC_A, "No cell allocation available" }, + { GSM48_RR_CAUSE_PROT_ERROR_UNSPC, "Protocol error unspecified" }, + { 0, NULL }, +}; + +/* FIXME: convert to value_string */ +static const char *cc_state_names[32] = { + "NULL", + "INITIATED", + "MM_CONNECTION_PEND", + "MO_CALL_PROC", + "CALL_DELIVERED", + "illegal state 5", + "CALL_PRESENT", + "CALL_RECEIVED", + "CONNECT_REQUEST", + "MO_TERM_CALL_CONF", + "ACTIVE", + "DISCONNECT_REQ", + "DISCONNECT_IND", + "illegal state 13", + "illegal state 14", + "illegal state 15", + "illegal state 16", + "illegal state 17", + "illegal state 18", + "RELEASE_REQ", + "illegal state 20", + "illegal state 21", + "illegal state 22", + "illegal state 23", + "illegal state 24", + "illegal state 25", + "MO_ORIG_MODIFY", + "MO_TERM_MODIFY", + "CONNECT_IND", + "illegal state 29", + "illegal state 30", + "illegal state 31", +}; + +const char *gsm48_cc_state_name(uint8_t state) +{ + if (state < ARRAY_SIZE(cc_state_names)) + return cc_state_names[state]; + + return "invalid"; +} + +static const struct value_string cc_msg_names[] = { + { GSM48_MT_CC_ALERTING, "ALERTING" }, + { GSM48_MT_CC_CALL_PROC, "CALL_PROC" }, + { GSM48_MT_CC_PROGRESS, "PROGRESS" }, + { GSM48_MT_CC_ESTAB, "ESTAB" }, + { GSM48_MT_CC_SETUP, "SETUP" }, + { GSM48_MT_CC_ESTAB_CONF, "ESTAB_CONF" }, + { GSM48_MT_CC_CONNECT, "CONNECT" }, + { GSM48_MT_CC_CALL_CONF, "CALL_CONF" }, + { GSM48_MT_CC_START_CC, "START_CC" }, + { GSM48_MT_CC_RECALL, "RECALL" }, + { GSM48_MT_CC_EMERG_SETUP, "EMERG_SETUP" }, + { GSM48_MT_CC_CONNECT_ACK, "CONNECT_ACK" }, + { GSM48_MT_CC_USER_INFO, "USER_INFO" }, + { GSM48_MT_CC_MODIFY_REJECT, "MODIFY_REJECT" }, + { GSM48_MT_CC_MODIFY, "MODIFY" }, + { GSM48_MT_CC_HOLD, "HOLD" }, + { GSM48_MT_CC_HOLD_ACK, "HOLD_ACK" }, + { GSM48_MT_CC_HOLD_REJ, "HOLD_REJ" }, + { GSM48_MT_CC_RETR, "RETR" }, + { GSM48_MT_CC_RETR_ACK, "RETR_ACK" }, + { GSM48_MT_CC_RETR_REJ, "RETR_REJ" }, + { GSM48_MT_CC_MODIFY_COMPL, "MODIFY_COMPL" }, + { GSM48_MT_CC_DISCONNECT, "DISCONNECT" }, + { GSM48_MT_CC_RELEASE_COMPL, "RELEASE_COMPL" }, + { GSM48_MT_CC_RELEASE, "RELEASE" }, + { GSM48_MT_CC_STOP_DTMF, "STOP_DTMF" }, + { GSM48_MT_CC_STOP_DTMF_ACK, "STOP_DTMF_ACK" }, + { GSM48_MT_CC_STATUS_ENQ, "STATUS_ENQ" }, + { GSM48_MT_CC_START_DTMF, "START_DTMF" }, + { GSM48_MT_CC_START_DTMF_ACK, "START_DTMF_ACK" }, + { GSM48_MT_CC_START_DTMF_REJ, "START_DTMF_REJ" }, + { GSM48_MT_CC_CONG_CTRL, "CONG_CTRL" }, + { GSM48_MT_CC_FACILITY, "FACILITY" }, + { GSM48_MT_CC_STATUS, "STATUS" }, + { GSM48_MT_CC_NOTIFY, "NOTFIY" }, + { 0, NULL } +}; + +const char *gsm48_cc_msg_name(uint8_t msgtype) +{ + return get_value_string(cc_msg_names, msgtype); +} + +const char *rr_cause_name(uint8_t cause) +{ + return get_value_string(rr_cause_names, cause); +} + +static void to_bcd(uint8_t *bcd, uint16_t val) +{ + bcd[2] = val % 10; + val = val / 10; + bcd[1] = val % 10; + val = val / 10; + bcd[0] = val % 10; + val = val / 10; +} + +void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc, + uint16_t mnc, uint16_t lac) +{ + uint8_t bcd[3]; + + to_bcd(bcd, mcc); + lai48->digits[0] = bcd[0] | (bcd[1] << 4); + lai48->digits[1] = bcd[2]; + + to_bcd(bcd, mnc); + /* FIXME: do we need three-digit MNC? See Table 10.5.3 */ + if (mnc > 99) { + lai48->digits[1] |= bcd[2] << 4; + lai48->digits[2] = bcd[0] | (bcd[1] << 4); + } else { + lai48->digits[1] |= 0xf << 4; + lai48->digits[2] = bcd[1] | (bcd[2] << 4); + } + + lai48->lac = htons(lac); +} + +int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi) +{ + uint32_t *tptr = (uint32_t *) &buf[3]; + + buf[0] = GSM48_IE_MOBILE_ID; + buf[1] = GSM48_TMSI_LEN; + buf[2] = 0xf0 | GSM_MI_TYPE_TMSI; + *tptr = htonl(tmsi); + + return 7; +} + +int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi) +{ + unsigned int length = strlen(imsi), i, off = 0; + uint8_t odd = (length & 0x1) == 1; + + buf[0] = GSM48_IE_MOBILE_ID; + buf[2] = char2