diff --git a/openbsc/.gitignore b/openbsc/.gitignore index e977f8494..c57ae4b9c 100644 --- a/openbsc/.gitignore +++ b/openbsc/.gitignore @@ -66,6 +66,7 @@ tests/abis/abis_test tests/si/si_test tests/smpp/smpp_test tests/bsc/bsc_test +tests/trau/trau_test tests/atconfig tests/atlocal diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 081e416d7..fe9956976 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -169,6 +169,7 @@ AC_OUTPUT( tests/si/Makefile tests/abis/Makefile tests/smpp/Makefile + tests/trau/Makefile doc/Makefile doc/examples/Makefile Makefile) diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h index 4a99bb11b..ffc247b7c 100644 --- a/openbsc/include/openbsc/mncc.h +++ b/openbsc/include/openbsc/mncc.h @@ -95,6 +95,7 @@ struct gsm_call { #define GSM_TCHF_FRAME 0x0300 #define GSM_TCHF_FRAME_EFR 0x0301 +#define GSM_TCHF_BAD_FRAME 0x03ff #define MNCC_SOCKET_HELLO 0x0400 diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h index 2c01b06db..3de50f772 100644 --- a/openbsc/include/openbsc/trau_mux.h +++ b/openbsc/include/openbsc/trau_mux.h @@ -53,3 +53,13 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); /* callback invoked if we receive TRAU frames */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); + +/* TRAU frame transcoding */ +struct msgb *trau_decode_fr(uint32_t callref, + const struct decoded_trau_frame *tf); +struct msgb *trau_decode_efr(uint32_t callref, + const struct decoded_trau_frame *tf); +void trau_encode_fr(struct decoded_trau_frame *tf, + const unsigned char *data); +void trau_encode_efr(struct decoded_trau_frame *tf, + const unsigned char *data); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 3cfc455df..63603934b 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -2876,6 +2876,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) case MNCC_FRAME_RECV: return tch_recv_mncc(net, data->callref, 1); case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: /* Find callref */ trans = trans_find_by_callref(net, data->callref); if (!trans) { diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c index 9272ac04f..c9d77cf78 100644 --- a/openbsc/src/libtrau/trau_mux.c +++ b/openbsc/src/libtrau/trau_mux.c @@ -30,6 +30,7 @@ #include #include #include +#include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ @@ -46,6 +47,53 @@ static const uint8_t gsm_fr_map[] = { 3, 3, 3, 3 }; + +/* + * EFR TRAU parity + * + * g(x) = x^3 + x^1 + 1 + */ +static const struct osmo_crc8gen_code gsm0860_efr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +/* EFR parity bits */ +static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 0, 22); + memcpy(check_bits + 22 , d_bits + 24, 3); + check_bits[25] = d_bits[28]; +} + +static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 42, 10); + memcpy(check_bits + 10 , d_bits + 90, 2); +} + +static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 98, 5); + check_bits[5] = d_bits[104]; + memcpy(check_bits + 6 , d_bits + 143, 2); +} + +static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 151, 10); + memcpy(check_bits + 10 , d_bits + 199, 2); +} + +static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 207, 5); + check_bits[5] = d_bits[213]; + memcpy(check_bits + 6 , d_bits + 252, 2); +} + struct map_entry { struct llist_head list; struct gsm_e1_subslot src, dst; @@ -158,7 +206,117 @@ lookup_trau_upqueue(const struct gsm_e1_subslot *src) return NULL; } -static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 }; + +struct msgb *trau_decode_fr(uint32_t callref, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, k, l, o; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, + "GSM-DATA"); + if (!msg) + return NULL; + + frame = (struct gsm_data_frame *)msg->data; + memset(frame, 0, sizeof(struct gsm_data_frame)); + data = frame->data; + data[0] = 0xd << 4; + /* reassemble d-bits */ + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + k = gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset input bits */ + while (i < 260) { + data[j/8] |= (tf->d_bits[k+o] << (7-(j%8))); + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } + frame->msg_type = GSM_TCHF_FRAME; + frame->callref = callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 33); + + return msg; +} + +struct msgb *trau_decode_efr(uint32_t callref, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, rc; + ubit_t check_bits[26]; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 31, + "GSM-DATA"); + if (!msg) + return NULL; + + frame = (struct gsm_data_frame *)msg->data; + memset(frame, 0, sizeof(struct gsm_data_frame)); + frame->msg_type = GSM_TCHF_FRAME_EFR; + frame->callref = callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 31); + + if (tf->c_bits[11]) /* BFI */ + goto bad_frame; + + data = frame->data; + data[0] = 0xc << 4; + /* reassemble d-bits */ + for (i = 1, j = 4; i < 39; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_1(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + if (rc) + goto bad_frame; + for (i = 42, j = 42; i < 95; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_2(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + if (rc) + goto bad_frame; + for (i = 98, j = 95; i < 148; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_3(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + if (rc) + goto bad_frame; + for (i = 151, j = 145; i < 204; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_4(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + if (rc) + goto bad_frame; + for (i = 207, j = 198; i < 257; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_5(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); + if (rc) + goto bad_frame; + + return msg; + +bad_frame: + frame->msg_type = GSM_TCHF_BAD_FRAME; + + return msg; +} /* we get called by subchan_demux */ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, @@ -177,45 +335,25 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, return rc; if (!dst_e1_ss) { - struct msgb *msg; - struct gsm_data_frame *frame; - unsigned char *data; - int i, j, k, l, o; + struct msgb *msg = NULL; /* frame shall be sent to upqueue */ if (!(ue = lookup_trau_upqueue(src_e1_ss))) return -EINVAL; if (!ue->callref) return -EINVAL; - if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check))) + if (!memcmp(tf.c_bits, c_bits_check_fr, 5)) + msg = trau_decode_fr(ue->callref, &tf); + else if (!memcmp(tf.c_bits, c_bits_check_efr, 5)) + msg = trau_decode_efr(ue->callref, &tf); + else { DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", - osmo_hexdump(tf.c_bits, sizeof(c_bits_check))); - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, - "GSM-DATA"); + osmo_hexdump(tf.c_bits, 5)); + DEBUGPC(DLMUX, "test trau (C1-C5) %s\n", + osmo_hexdump(c_bits_check_efr, 5)); + return -EINVAL; + } if (!msg) return -ENOMEM; - - frame = (struct gsm_data_frame *)msg->data; - memset(frame, 0, sizeof(struct gsm_data_frame)); - data = frame->data; - data[0] = 0xd << 4; - /* reassemble d-bits */ - i = 0; /* counts bits */ - j = 4; /* counts output bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset input bits */ - while (i < 260) { - data[j/8] |= (tf.d_bits[k+o] << (7-(j%8))); - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - frame->msg_type = GSM_TCHF_FRAME; - frame->callref = ue->callref; - msgb_put(msg, sizeof(struct gsm_data_frame) + 33); trau_tx_to_mncc(ue->net, msg); return 0; @@ -275,13 +413,86 @@ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref) return 0; } +void trau_encode_fr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j, k, l, o; + + /* set c-bits and t-bits */ + tf->c_bits[0] = 1; + tf->c_bits[1] = 1; + tf->c_bits[2] = 1; + tf->c_bits[3] = 0; + tf->c_bits[4] = 0; + memset(&tf->c_bits[5], 0, 6); + memset(&tf->c_bits[11], 1, 10); + memset(&tf->t_bits[0], 1, 4); + /* reassemble d-bits */ + i = 0; /* counts bits */ + j = 4; /* counts input bits */ + k = gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset output bits */ + while (i < 260) { + tf->d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1; + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } +} + +void trau_encode_efr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j; + ubit_t check_bits[26]; + + /* set c-bits and t-bits */ + tf->c_bits[0] = 1; + tf->c_bits[1] = 1; + tf->c_bits[2] = 0; + tf->c_bits[3] = 1; + tf->c_bits[4] = 0; + memset(&tf->c_bits[5], 0, 6); + memset(&tf->c_bits[11], 1, 10); + memset(&tf->t_bits[0], 1, 4); + /* reassemble d-bits */ + tf->d_bits[0] = 1; + for (i = 1, j = 4; i < 39; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_1(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + for (i = 42, j = 42; i < 95; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_2(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + for (i = 98, j = 95; i < 148; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_3(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + for (i = 151, j = 145; i < 204; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_4(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + for (i = 207, j = 198; i < 257; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_5(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); +} + int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) { uint8_t trau_bits_out[TRAU_FRAME_BITS]; struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link; struct subch_mux *mx; - int i, j, k, l, o; - unsigned char *data = frame->data; struct decoded_trau_frame tf; mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); @@ -290,30 +501,10 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) switch (frame->msg_type) { case GSM_TCHF_FRAME: - /* set c-bits and t-bits */ - tf.c_bits[0] = 1; - tf.c_bits[1] = 1; - tf.c_bits[2] = 1; - tf.c_bits[3] = 0; - tf.c_bits[4] = 0; - memset(&tf.c_bits[5], 0, 6); - memset(&tf.c_bits[11], 1, 10); - memset(&tf.t_bits[0], 1, 4); - /* reassemble d-bits */ - i = 0; /* counts bits */ - j = 4; /* counts input bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset output bits */ - while (i < 260) { - tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1; - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } + trau_encode_fr(&tf, frame->data); + break; + case GSM_TCHF_FRAME_EFR: + trau_encode_efr(&tf, frame->data); break; default: DEBUGPC(DLMUX, "unsupported message type %d\n", diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 7f51bfa13..d4bb954a6 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = gsm0408 db channel mgcp gprs si abis gbproxy +SUBDIRS = gsm0408 db channel mgcp gprs si abis gbproxy trau if BUILD_NAT SUBDIRS += bsc-nat bsc-nat-trie diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 76110f5d9..652cfe96c 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -81,3 +81,9 @@ AT_KEYWORDS([gbproxy]) cat $abs_srcdir/gbproxy/gbproxy_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gbproxy/gbproxy_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([trau]) +AT_KEYWORDS([trau]) +cat $abs_srcdir/trau/trau_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/trau/trau_test], [], [expout], [ignore]) +AT_CLEANUP diff --git a/openbsc/tests/trau/Makefile.am b/openbsc/tests/trau/Makefile.am new file mode 100644 index 000000000..d4aa1c327 --- /dev/null +++ b/openbsc/tests/trau/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = trau_test.ok + +noinst_PROGRAMS = trau_test + +trau_test_SOURCES = trau_test.c +trau_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) -ldl -ldbi + diff --git a/openbsc/tests/trau/trau_test.c b/openbsc/tests/trau/trau_test.c new file mode 100644 index 000000000..f8a48dbd8 --- /dev/null +++ b/openbsc/tests/trau/trau_test.c @@ -0,0 +1,82 @@ +/* (C) 2013 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 Affero 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include + +void test_trau_fr_efr(unsigned char *data) +{ + struct decoded_trau_frame tf; + struct msgb *msg; + struct gsm_data_frame *frame; + + printf("Testing TRAU FR transcoding.\n"); + data[0] = 0xd0; + trau_encode_fr(&tf, data); + tf.c_bits[11] = 0; /* clear BFI */ + msg = trau_decode_fr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME); + OSMO_ASSERT(!memcmp(frame->data, data, 33)); + msgb_free(msg); + + printf("Testing TRAU EFR transcoding.\n"); + data[0] = 0xc0; + trau_encode_efr(&tf, data); + OSMO_ASSERT(tf.d_bits[0] == 1); /* spare bit must be 1 */ + tf.c_bits[11] = 0; /* clear BFI */ + msg = trau_decode_efr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME_EFR); + OSMO_ASSERT(!memcmp(frame->data, data, 31)); + + printf("Testing TRAU EFR decoding with CRC error.\n"); + tf.d_bits[0] = 0; /* spare bit must be included */ + msg = trau_decode_efr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_BAD_FRAME); + msgb_free(msg); +} + +int main() +{ + unsigned char data[33]; + int i; + + memset(data, 0x00, sizeof(data)); + test_trau_fr_efr(data); + memset(data, 0xff, sizeof(data)); + test_trau_fr_efr(data); + srandom(42); + for (i = 0; i < sizeof(data); i++) + data[i] = random(); + test_trau_fr_efr(data); + printf("Done\n"); + return 0; +} + +/* stubs */ +void vty_out() {} diff --git a/openbsc/tests/trau/trau_test.ok b/openbsc/tests/trau/trau_test.ok new file mode 100644 index 000000000..ef7123073 --- /dev/null +++ b/openbsc/tests/trau/trau_test.ok @@ -0,0 +1,10 @@ +Testing TRAU FR transcoding. +Testing TRAU EFR transcoding. +Testing TRAU EFR decoding with CRC error. +Testing TRAU FR transcoding. +Testing TRAU EFR transcoding. +Testing TRAU EFR decoding with CRC error. +Testing TRAU FR transcoding. +Testing TRAU EFR transcoding. +Testing TRAU EFR decoding with CRC error. +Done