summaryrefslogtreecommitdiffstats
path: root/src/gsm
diff options
context:
space:
mode:
Diffstat (limited to 'src/gsm')
-rw-r--r--src/gsm/Makefile.am13
-rw-r--r--src/gsm/comp128.c230
-rw-r--r--src/gsm/gprs_cipher_core.c99
-rw-r--r--src/gsm/gsm0480.c461
-rw-r--r--src/gsm/gsm0808.c369
-rw-r--r--src/gsm/gsm48.c415
-rw-r--r--src/gsm/gsm48_ie.c1095
-rw-r--r--src/gsm/gsm_utils.c465
-rw-r--r--src/gsm/rsl.c371
-rw-r--r--src/gsm/rxlev_stat.c94
-rw-r--r--src/gsm/tlv_parser.c179
11 files changed, 3791 insertions, 0 deletions
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 <marc@scard.org>, Ian Goldberg <iang@cs.berkeley.edu>,
+ * and David Wagner <daw@cs.berkeley.edu>
+ *
+ * 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 <tnt@246tNt.com>
+ *
+ * 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 <string.h>
+#include <stdint.h>
+
+/* 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<<n); i++)
+ for (j=0; j<(1<<m); j++) {
+ a = j + i * (2<<m);
+ b = a + (1<<m);
+ y = (x[a] + (x[b]<<1)) & ((32<<m)-1);
+ z = ((x[a]<<1) + x[b]) & ((32<<m)-1);
+ x[a] = tbl[y];
+ x[b] = tbl[z];
+ }
+}
+
+static inline void
+_comp128_compression(uint8_t *x)
+{
+ int n;
+ for (n=0; n<5; n++)
+ _comp128_compression_round(x, n, _comp128_table[n]);
+}
+
+static inline void
+_comp128_bitsfrombytes(uint8_t *x, uint8_t *bits)
+{
+ int i;
+ memset(bits, 0x00, 128);
+ for (i=0; i<128; i++)
+ if (x[i>>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 <laforge@gnumonks.org>
+ *
+ * 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 <errno.h>
+#include <stdint.h>
+
+#include <osmocore/utils.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/plugin.h>
+
+#include <osmocom/crypt/gprs_cipher.h>
+
+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 <zecke@selfish.org>
+ * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
+ *
+ * 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 <osmocore/gsm0480.h>
+#include <osmocore/gsm_utils.h>
+
+#include <osmocore/logging.h>
+
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/protocol/gsm_04_80.h>
+
+#include <string.h>
+
+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 <zecke@selfish.org>
+ * (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 <osmocore/gsm0808.h>
+#include <osmocore/protocol/gsm_08_08.h>
+#include <osmocore/gsm48.h>
+
+#include <arpa/inet.h>
+
+#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 */