diff options
234 files changed, 57491 insertions, 0 deletions
diff --git a/hlrsync/hlrsync.py b/hlrsync/hlrsync.py new file mode 100755 index 000000000..e4a495555 --- /dev/null +++ b/hlrsync/hlrsync.py @@ -0,0 +1,125 @@ +#!/usr/bin/python2.5 + +from __future__ import with_statement + +from pysqlite2 import dbapi2 as sqlite3 +import sys + +hlr = sqlite3.connect(sys.argv[1]) +web = sqlite3.connect(sys.argv[2]) + +# switch to autocommit +hlr.isolation_level = None +web.isolation_level = None + +hlr.row_factory = sqlite3.Row +web.row_factory = sqlite3.Row + +with hlr: + hlr_subscrs = hlr.execute(""" + SELECT * FROM Subscriber + """).fetchall() + hlr_tokens = hlr.execute(""" + SELECT * FROM AuthToken + """).fetchall() + +with web: + web_tokens = web.execute(""" + SELECT * FROM reg_tokens + """).fetchall() + web_sms = web.execute(""" + SELECT * FROM sms_queue + """).fetchall() + +# index by subscr id +hlr_subscrs_by_id = {} +hlr_subscrs_by_ext = {} +hlr_tokens_by_subscr_id = {} +for x in hlr_subscrs: + hlr_subscrs_by_id[x['id']] = x + hlr_subscrs_by_ext[x['extension']] = x +del hlr_subscrs +for x in hlr_tokens: + hlr_tokens_by_subscr_id[x['subscriber_id']] = x +del hlr_tokens + +web_tokens_by_subscr_id = {} +for x in web_tokens: + web_tokens_by_subscr_id[x['subscriber_id']] = x +del web_tokens + +# remove leftover web_tokens and correct inconsistent fields +with web: + for x in web_tokens_by_subscr_id.values(): + subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None) + if subscr is None: + web.execute(""" + DELETE FROM reg_tokens WHERE subscriber_id = ? + """, (x['subscriber_id'],)) + del web_tokens_by_subscr_id[x['subscriber_id']] + continue + if str(x['imsi']) != str(subscr['imsi']) or \ + x['extension'] != subscr['extension'] or \ + x['tmsi'] != subscr['tmsi'] or \ + x['lac'] != subscr['lac']: + web.execute(""" + UPDATE reg_tokens + SET imsi = ?, extension = ?, tmsi = ?, lac = ? + WHERE subscriber_id = ? + """, (str(subscr['imsi']), subscr['extension'], + subscr['tmsi'], subscr['lac'], x['subscriber_id'])) + +# add missing web_tokens +with web: + for x in hlr_tokens_by_subscr_id.values(): + subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None) + if subscr is None: + hlr.execute(""" + DELETE FROM AuthToken WHERE subscriber_id = ? + """, (x['subscriber_id'],)) + del hlr_tokens_by_subscr_id[x['subscriber_id']] + continue + webtoken = web_tokens_by_subscr_id.get(x['subscriber_id'], None) + if webtoken is None: + web.execute(""" + INSERT INTO reg_tokens + (subscriber_id, extension, reg_completed, name, email, lac, imsi, token, tmsi) + VALUES + (?, ?, 0, ?, '', ?, ?, ?, ?) + """, (x['subscriber_id'], subscr['extension'], subscr['name'], + subscr['lac'], str(subscr['imsi']), x['token'], subscr['tmsi'])) + +# authorize subscribers +with hlr: + for x in web_tokens_by_subscr_id.values(): + subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None) + if x['reg_completed'] and not subscr['authorized']: + hlr.execute(""" + UPDATE Subscriber + SET authorized = 1 + WHERE id = ? + """, (x['subscriber_id'],)) + +# Sync SMS from web to hlr +with hlr: + for sms in web_sms: + subscr = hlr_subscrs_by_ext.get(sms['receiver_ext']) + if subscr is None: + print '%s not found' % sms['receiver_ext'] + continue + hlr.execute(""" + INSERT INTO SMS + (created, sender_id, receiver_id, reply_path_req, status_rep_req, protocol_id, data_coding_scheme, ud_hdr_ind, text) + VALUES + (?, 1, ?, 0, 0, 0, 0, 0, ?) + """, (sms['created'], subscr['id'], sms['text'])) +with web: + for sms in web_sms: + web.execute(""" + DELETE FROM sms_queue WHERE id = ? + """, (sms['id'],)) + + +hlr.close() +web.close() + diff --git a/.gitignore b/libosmocore/.gitignore index c292a6110..c292a6110 100644 --- a/.gitignore +++ b/libosmocore/.gitignore diff --git a/COPYING b/libosmocore/COPYING index d511905c1..d511905c1 100644 --- a/COPYING +++ b/libosmocore/COPYING diff --git a/Makefile.am b/libosmocore/Makefile.am index 8007b7458..8007b7458 100644 --- a/Makefile.am +++ b/libosmocore/Makefile.am diff --git a/configure.in b/libosmocore/configure.in index abf7bf477..abf7bf477 100644 --- a/configure.in +++ b/libosmocore/configure.in diff --git a/include/Makefile.am b/libosmocore/include/Makefile.am index f0015d5f4..f0015d5f4 100644 --- a/include/Makefile.am +++ b/libosmocore/include/Makefile.am diff --git a/include/osmocore/Makefile.am b/libosmocore/include/osmocore/Makefile.am index fb4f089b7..fb4f089b7 100644 --- a/include/osmocore/Makefile.am +++ b/libosmocore/include/osmocore/Makefile.am diff --git a/include/osmocore/bitvec.h b/libosmocore/include/osmocore/bitvec.h index 7a26bce45..7a26bce45 100644 --- a/include/osmocore/bitvec.h +++ b/libosmocore/include/osmocore/bitvec.h diff --git a/include/osmocore/comp128.h b/libosmocore/include/osmocore/comp128.h index c37808f0f..c37808f0f 100644 --- a/include/osmocore/comp128.h +++ b/libosmocore/include/osmocore/comp128.h diff --git a/include/osmocore/gsm48.h b/libosmocore/include/osmocore/gsm48.h index 787cdd0d9..787cdd0d9 100644 --- a/include/osmocore/gsm48.h +++ b/libosmocore/include/osmocore/gsm48.h diff --git a/include/osmocore/gsm48_ie.h b/libosmocore/include/osmocore/gsm48_ie.h index 200619a76..200619a76 100644 --- a/include/osmocore/gsm48_ie.h +++ b/libosmocore/include/osmocore/gsm48_ie.h diff --git a/include/osmocore/gsm_utils.h b/libosmocore/include/osmocore/gsm_utils.h index c87e967bd..c87e967bd 100644 --- a/include/osmocore/gsm_utils.h +++ b/libosmocore/include/osmocore/gsm_utils.h diff --git a/include/osmocore/gsmtap.h b/libosmocore/include/osmocore/gsmtap.h index dcd64bdf5..dcd64bdf5 100644 --- a/include/osmocore/gsmtap.h +++ b/libosmocore/include/osmocore/gsmtap.h diff --git a/include/osmocore/linuxlist.h b/libosmocore/include/osmocore/linuxlist.h index fb99c5ec8..fb99c5ec8 100644 --- a/include/osmocore/linuxlist.h +++ b/libosmocore/include/osmocore/linuxlist.h diff --git a/include/osmocore/mncc.h b/libosmocore/include/osmocore/mncc.h index a094bb9b6..a094bb9b6 100644 --- a/include/osmocore/mncc.h +++ b/libosmocore/include/osmocore/mncc.h diff --git a/include/osmocore/msgb.h b/libosmocore/include/osmocore/msgb.h index 31db71942..31db71942 100644 --- a/include/osmocore/msgb.h +++ b/libosmocore/include/osmocore/msgb.h diff --git a/include/osmocore/protocol/Makefile.am b/libosmocore/include/osmocore/protocol/Makefile.am index 6d8883e61..6d8883e61 100644 --- a/include/osmocore/protocol/Makefile.am +++ b/libosmocore/include/osmocore/protocol/Makefile.am diff --git a/include/osmocore/protocol/gsm_04_08.h b/libosmocore/include/osmocore/protocol/gsm_04_08.h index 801b9b54c..801b9b54c 100644 --- a/include/osmocore/protocol/gsm_04_08.h +++ b/libosmocore/include/osmocore/protocol/gsm_04_08.h diff --git a/include/osmocore/protocol/gsm_04_11.h b/libosmocore/include/osmocore/protocol/gsm_04_11.h index c6a2b1930..c6a2b1930 100644 --- a/include/osmocore/protocol/gsm_04_11.h +++ b/libosmocore/include/osmocore/protocol/gsm_04_11.h diff --git a/include/osmocore/protocol/gsm_04_80.h b/libosmocore/include/osmocore/protocol/gsm_04_80.h index fa5c94514..fa5c94514 100644 --- a/include/osmocore/protocol/gsm_04_80.h +++ b/libosmocore/include/osmocore/protocol/gsm_04_80.h diff --git a/include/osmocore/protocol/gsm_08_58.h b/libosmocore/include/osmocore/protocol/gsm_08_58.h index ca9398f8f..ca9398f8f 100644 --- a/include/osmocore/protocol/gsm_08_58.h +++ b/libosmocore/include/osmocore/protocol/gsm_08_58.h diff --git a/include/osmocore/protocol/gsm_12_21.h b/libosmocore/include/osmocore/protocol/gsm_12_21.h index 9cae45da7..9cae45da7 100644 --- a/include/osmocore/protocol/gsm_12_21.h +++ b/libosmocore/include/osmocore/protocol/gsm_12_21.h diff --git a/include/osmocore/rsl.h b/libosmocore/include/osmocore/rsl.h index c1080812e..c1080812e 100644 --- a/include/osmocore/rsl.h +++ b/libosmocore/include/osmocore/rsl.h diff --git a/include/osmocore/rxlev_stat.h b/libosmocore/include/osmocore/rxlev_stat.h index 415509dc2..415509dc2 100644 --- a/include/osmocore/rxlev_stat.h +++ b/libosmocore/include/osmocore/rxlev_stat.h diff --git a/include/osmocore/select.h b/libosmocore/include/osmocore/select.h index 2d8b3ec07..2d8b3ec07 100644 --- a/include/osmocore/select.h +++ b/libosmocore/include/osmocore/select.h diff --git a/include/osmocore/signal.h b/libosmocore/include/osmocore/signal.h index 02d83d2e9..02d83d2e9 100644 --- a/include/osmocore/signal.h +++ b/libosmocore/include/osmocore/signal.h diff --git a/include/osmocore/statistics.h b/libosmocore/include/osmocore/statistics.h index 1d56054ab..1d56054ab 100644 --- a/include/osmocore/statistics.h +++ b/libosmocore/include/osmocore/statistics.h diff --git a/include/osmocore/talloc.h b/libosmocore/include/osmocore/talloc.h index f7f7643b8..f7f7643b8 100644 --- a/include/osmocore/talloc.h +++ b/libosmocore/include/osmocore/talloc.h diff --git a/include/osmocore/timer.h b/libosmocore/include/osmocore/timer.h index fee888bfd..fee888bfd 100644 --- a/include/osmocore/timer.h +++ b/libosmocore/include/osmocore/timer.h diff --git a/include/osmocore/tlv.h b/libosmocore/include/osmocore/tlv.h index c733dbc9a..c733dbc9a 100644 --- a/include/osmocore/tlv.h +++ b/libosmocore/include/osmocore/tlv.h diff --git a/include/osmocore/utils.h b/libosmocore/include/osmocore/utils.h index 51c6f0357..51c6f0357 100644 --- a/include/osmocore/utils.h +++ b/libosmocore/include/osmocore/utils.h diff --git a/include/osmocore/write_queue.h b/libosmocore/include/osmocore/write_queue.h index c84000c1e..c84000c1e 100644 --- a/include/osmocore/write_queue.h +++ b/libosmocore/include/osmocore/write_queue.h diff --git a/libosmocore.pc.in b/libosmocore/libosmocore.pc.in index 7c298693a..7c298693a 100644 --- a/libosmocore.pc.in +++ b/libosmocore/libosmocore.pc.in diff --git a/src/Makefile.am b/libosmocore/src/Makefile.am index f0effa2ea..f0effa2ea 100644 --- a/src/Makefile.am +++ b/libosmocore/src/Makefile.am diff --git a/src/bitvec.c b/libosmocore/src/bitvec.c index eb83ac667..eb83ac667 100644 --- a/src/bitvec.c +++ b/libosmocore/src/bitvec.c diff --git a/src/comp128.c b/libosmocore/src/comp128.c index 5d5680c72..5d5680c72 100644 --- a/src/comp128.c +++ b/libosmocore/src/comp128.c diff --git a/src/gsm48.c b/libosmocore/src/gsm48.c index ff989eaf6..ff989eaf6 100644 --- a/src/gsm48.c +++ b/libosmocore/src/gsm48.c diff --git a/src/gsm48_ie.c b/libosmocore/src/gsm48_ie.c index 4ca5fb800..4ca5fb800 100644 --- a/src/gsm48_ie.c +++ b/libosmocore/src/gsm48_ie.c diff --git a/src/gsm_utils.c b/libosmocore/src/gsm_utils.c index 593dd5c94..593dd5c94 100644 --- a/src/gsm_utils.c +++ b/libosmocore/src/gsm_utils.c diff --git a/src/msgb.c b/libosmocore/src/msgb.c index 60af373eb..60af373eb 100644 --- a/src/msgb.c +++ b/libosmocore/src/msgb.c diff --git a/src/rsl.c b/libosmocore/src/rsl.c index c864b12f7..c864b12f7 100644 --- a/src/rsl.c +++ b/libosmocore/src/rsl.c diff --git a/src/rxlev_stat.c b/libosmocore/src/rxlev_stat.c index 1bfd6795a..1bfd6795a 100644 --- a/src/rxlev_stat.c +++ b/libosmocore/src/rxlev_stat.c diff --git a/src/select.c b/libosmocore/src/select.c index 9517778ce..9517778ce 100644 --- a/src/select.c +++ b/libosmocore/src/select.c diff --git a/src/signal.c b/libosmocore/src/signal.c index c7ca86c48..c7ca86c48 100644 --- a/src/signal.c +++ b/libosmocore/src/signal.c diff --git a/src/statistics.c b/libosmocore/src/statistics.c index 34e6a408e..34e6a408e 100644 --- a/src/statistics.c +++ b/libosmocore/src/statistics.c diff --git a/src/talloc.c b/libosmocore/src/talloc.c index 98c2ee097..98c2ee097 100644 --- a/src/talloc.c +++ b/libosmocore/src/talloc.c diff --git a/src/timer.c b/libosmocore/src/timer.c index 37d7d166b..37d7d166b 100644 --- a/src/timer.c +++ b/libosmocore/src/timer.c diff --git a/src/tlv_parser.c b/libosmocore/src/tlv_parser.c index 407e57aa2..407e57aa2 100644 --- a/src/tlv_parser.c +++ b/libosmocore/src/tlv_parser.c diff --git a/src/utils.c b/libosmocore/src/utils.c index 2a73d397e..2a73d397e 100644 --- a/src/utils.c +++ b/libosmocore/src/utils.c diff --git a/src/write_queue.c b/libosmocore/src/write_queue.c index 7d908b4ca..7d908b4ca 100644 --- a/src/write_queue.c +++ b/libosmocore/src/write_queue.c diff --git a/tests/Makefile.am b/libosmocore/tests/Makefile.am index 0119a02cf..0119a02cf 100644 --- a/tests/Makefile.am +++ b/libosmocore/tests/Makefile.am diff --git a/tests/sms/Makefile.am b/libosmocore/tests/sms/Makefile.am index a8f1ff6a2..a8f1ff6a2 100644 --- a/tests/sms/Makefile.am +++ b/libosmocore/tests/sms/Makefile.am diff --git a/tests/sms/sms_test.c b/libosmocore/tests/sms/sms_test.c index f5183d546..f5183d546 100644 --- a/tests/sms/sms_test.c +++ b/libosmocore/tests/sms/sms_test.c diff --git a/tests/timer/Makefile.am b/libosmocore/tests/timer/Makefile.am index d3decf556..d3decf556 100644 --- a/tests/timer/Makefile.am +++ b/libosmocore/tests/timer/Makefile.am diff --git a/tests/timer/timer_test.c b/libosmocore/tests/timer/timer_test.c index 1b458d81d..1b458d81d 100644 --- a/tests/timer/timer_test.c +++ b/libosmocore/tests/timer/timer_test.c diff --git a/linux-kernel/linux-2.6.27.4-misdn-abis.diff b/linux-kernel/linux-2.6.27.4-misdn-abis.diff new file mode 100644 index 000000000..3691eda8a --- /dev/null +++ b/linux-kernel/linux-2.6.27.4-misdn-abis.diff @@ -0,0 +1,144 @@ +diff -Nru --exclude-from /sunbeam/home/laforge/scripts/dontdiff linux-2.6.27.4-clean/drivers/isdn/mISDN/layer2.c linux-2.6.27.4/drivers/isdn/mISDN/layer2.c +--- linux-2.6.27.4-clean/drivers/isdn/mISDN/layer2.c 2008-10-26 00:05:07.000000000 +0200 ++++ linux-2.6.27.4/drivers/isdn/mISDN/layer2.c 2008-12-23 16:16:29.000000000 +0100 +@@ -94,8 +94,10 @@ + struct layer2 *l2 = fi->userdata; + va_list va; + ++#if 0 + if (!(*debug & DEBUG_L2_FSM)) + return; ++#endif + va_start(va, fmt); + printk(KERN_DEBUG "l2 (tei %d): ", l2->tei); + vprintk(fmt, va); +@@ -882,6 +884,8 @@ + l2->va = 0; + l2->vr = 0; + l2->sow = 0; ++ l2->sapi = skb->data[0] >> 2; ++ set_channel_address(&l2->ch, l2->sapi, l2->tei); + clear_exception(l2); + send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); + mISDN_FsmChangeState(fi, ST_L2_7); +@@ -898,6 +902,7 @@ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + ++ printk(KERN_DEBUG "l2_send_UA()\n"); + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + } + +@@ -931,6 +936,8 @@ + l2->va = 0; + l2->vr = 0; + l2->sow = 0; ++ l2->sapi = skb->data[0] >> 2; ++ set_channel_address(&l2->ch, l2->sapi, l2->tei); + mISDN_FsmChangeState(fi, ST_L2_7); + stop_t200(l2, 3); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); +@@ -982,6 +989,8 @@ + } else if (l2->vs != l2->va) { + skb_queue_purge(&l2->i_queue); + pr = DL_ESTABLISH_IND; ++ //l2->sapi = skb->data[0] >> 2; ++ //set_channel_address(&l2->ch, l2->sapi, l2->tei); + } + stop_t200(l2, 5); + l2->vr = 0; +@@ -1841,11 +1850,14 @@ + u_int l; + int c = 0; + ++ printk(KERN_DEBUG "ph_data_indication 0x%x 0x%x 0x%x\n", datap[0], datap[1], datap[2]); ++ + l = l2addrsize(l2); + if (skb->len <= l) { + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); + return ret; + } ++#if 0 + if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */ + psapi = *datap++; + ptei = *datap++; +@@ -1875,6 +1887,7 @@ + return 0; + } + } else ++#endif + datap += l; + if (!(*datap & 1)) { /* I-Frame */ + c = iframe_error(l2, skb); +@@ -1890,6 +1903,7 @@ + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); + } else if (IsSABME(datap, l2)) { + c = unnum_error(l2, skb, CMD); ++ printk(KERN_DEBUG "IsSABME() returned true, unnum_error=%d\n", c); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); + } else if (IsUA(datap)) { +@@ -2087,7 +2101,7 @@ + test_and_set_bit(FLG_LAPD, &l2->flag); + test_and_set_bit(FLG_LAPD_NET, &l2->flag); + test_and_set_bit(FLG_MOD128, &l2->flag); +- l2->sapi = 0; ++ l2->sapi = 62; + l2->maxlen = MAX_DFRAME_LEN; + if (test_bit(OPTION_L2_PMX, &options)) + l2->window = 7; +diff -Nru --exclude-from /sunbeam/home/laforge/scripts/dontdiff linux-2.6.27.4-clean/drivers/isdn/mISDN/tei.c linux-2.6.27.4/drivers/isdn/mISDN/tei.c +--- linux-2.6.27.4-clean/drivers/isdn/mISDN/tei.c 2008-10-26 00:05:07.000000000 +0200 ++++ linux-2.6.27.4/drivers/isdn/mISDN/tei.c 2008-12-23 16:32:59.000000000 +0100 +@@ -830,18 +830,29 @@ + int tei, ri; + struct layer2 *l2; + ++ printk(KERN_DEBUG "new tei request: tei=%d\n", dp[3] >> 1); ++ + ri = dp[0] << 8; + ri += dp[1]; +- if (!mgr->up) +- goto denied; +- tei = get_free_tei(mgr); +- if (tei < 0) { +- printk(KERN_WARNING "%s:No free tei\n", __func__); ++ if (!mgr->up) { ++ printk(KERN_DEBUG "mgr->up == NULL\n"); + goto denied; + } ++ if (dp[3] != 0xff) { ++ /* This is a TEI request according to 3GPP TS 08.56 6.1.11.2 */ ++ tei = dp[3] >> 1; ++ } else { ++ tei = get_free_tei(mgr); ++ if (tei < 0) { ++ printk(KERN_WARNING "%s:No free tei\n", __func__); ++ goto denied; ++ } ++ } + l2 = create_new_tei(mgr, tei); +- if (!l2) ++ if (!l2) { ++ printk(KERN_DEBUG "create_new_tei == NULL\n"); + goto denied; ++ } + else + mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp); + return; +@@ -1159,12 +1170,14 @@ + return -ENOTCONN; + if (skb->len != 3) + return -ENOTCONN; ++#if 0 + if (skb->data[0] != 0) + /* only SAPI 0 command */ + return -ENOTCONN; ++#endif + if (!(skb->data[1] & 1)) /* invalid EA1 */ + return -EINVAL; +- tei = skb->data[1] >> 0; ++ tei = skb->data[1] >> 1; + if (tei > 63) /* not a fixed tei */ + return -ENOTCONN; + if ((skb->data[2] & ~0x10) != SABME) diff --git a/linux-kernel/linux-2.6.30-hfcmulti-multibts.patch b/linux-kernel/linux-2.6.30-hfcmulti-multibts.patch new file mode 100644 index 000000000..bb94d342f --- /dev/null +++ b/linux-kernel/linux-2.6.30-hfcmulti-multibts.patch @@ -0,0 +1,486 @@ +This experimental patch splits one E1 card into three virtual cards, + +TS 1,2,3,4,5 is card 0 +TS 6,7,8,9,10 is card 1 +TS 11,12,13,14 is card 2 + +This allows you to run one L2 TEI handler on each of the virtual cards, +which is required if you want to run multiple BTS on a single E1 link. + +Thanks to Andreas Eversberg for this patch. + +diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h +index 0c77386..02dd4a1 100644 +--- a/drivers/isdn/hardware/mISDN/hfc_multi.h ++++ b/drivers/isdn/hardware/mISDN/hfc_multi.h +@@ -209,14 +209,17 @@ struct hfc_multi { + u_long ledstate; /* save last state of leds */ + int opticalsupport; /* has the e1 board */ + /* an optical Interface */ +- int dslot; /* channel # of d-channel (E1) default 16 */ ++ ++ u_int bmask[32]; /* bitmask of bchannels for port */ ++ u_char dnum[32]; /* array of used dchannel numbers for port */ ++ u_char created[32]; /* what port is created */ ++ u_int activity[32]; /* if there is any action on this */ ++ /* port (will be cleared after */ ++ /* showing led-states) */ + + u_long wdcount; /* every 500 ms we need to */ + /* send the watchdog a signal */ + u_char wdbyte; /* watchdog toggle byte */ +- u_int activity[8]; /* if there is any action on this */ +- /* port (will be cleared after */ +- /* showing led-states) */ + int e1_state; /* keep track of last state */ + int e1_getclock; /* if sync is retrieved from interface */ + int syncronized; /* keep track of existing sync interface */ +@@ -233,7 +236,6 @@ struct hfc_multi { + * the bch->channel is equvalent to the hfc-channel + */ + struct hfc_chan chan[32]; +- u_char created[8]; /* what port is created */ + signed char slot_owner[256]; /* owner channel of slot */ + }; + +diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c +index e1dab30..4fe2d27 100644 +--- a/drivers/isdn/hardware/mISDN/hfcmulti.c ++++ b/drivers/isdn/hardware/mISDN/hfcmulti.c +@@ -1619,8 +1619,8 @@ hfcmulti_leds(struct hfc_multi *hc) + * left red: frame sync, but no L1 + * right green: L2 active + */ +- if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */ +- if (hc->chan[hc->dslot].dch->dev.D.protocol ++ if (hc->chan[hc->dnum[0]].sync != 2) { /* no frame sync */ ++ if (hc->chan[hc->dnum[0]].dch->dev.D.protocol + != ISDN_P_NT_E1) { + led[0] = 1; + led[1] = 1; +@@ -2428,55 +2428,56 @@ handle_timer_irq(struct hfc_multi *hc) + } + } + if (hc->ctype == HFC_TYPE_E1 && hc->created[0]) { +- dch = hc->chan[hc->dslot].dch; +- if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { ++#warning todo: put interface parameters to hc ++ dch = hc->chan[hc->dnum[0]].dch; ++ if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { + /* LOS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; +- if (!temp && hc->chan[hc->dslot].los) ++ if (!temp && hc->chan[hc->dnum[0]].los) + signal_state_up(dch, L1_SIGNAL_LOS_ON, + "LOS detected"); +- if (temp && !hc->chan[hc->dslot].los) ++ if (temp && !hc->chan[hc->dnum[0]].los) + signal_state_up(dch, L1_SIGNAL_LOS_OFF, + "LOS gone"); +- hc->chan[hc->dslot].los = temp; ++ hc->chan[hc->dnum[0]].los = temp; + } +- if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) { ++ if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dnum[0]].cfg)) { + /* AIS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; +- if (!temp && hc->chan[hc->dslot].ais) ++ if (!temp && hc->chan[hc->dnum[0]].ais) + signal_state_up(dch, L1_SIGNAL_AIS_ON, + "AIS detected"); +- if (temp && !hc->chan[hc->dslot].ais) ++ if (temp && !hc->chan[hc->dnum[0]].ais) + signal_state_up(dch, L1_SIGNAL_AIS_OFF, + "AIS gone"); +- hc->chan[hc->dslot].ais = temp; ++ hc->chan[hc->dnum[0]].ais = temp; + } +- if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) { ++ if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dnum[0]].cfg)) { + /* SLIP */ + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; +- if (!temp && hc->chan[hc->dslot].slip_rx) ++ if (!temp && hc->chan[hc->dnum[0]].slip_rx) + signal_state_up(dch, L1_SIGNAL_SLIP_RX, + " bit SLIP detected RX"); +- hc->chan[hc->dslot].slip_rx = temp; ++ hc->chan[hc->dnum[0]].slip_rx = temp; + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; +- if (!temp && hc->chan[hc->dslot].slip_tx) ++ if (!temp && hc->chan[hc->dnum[0]].slip_tx) + signal_state_up(dch, L1_SIGNAL_SLIP_TX, + " bit SLIP detected TX"); +- hc->chan[hc->dslot].slip_tx = temp; ++ hc->chan[hc->dnum[0]].slip_tx = temp; + } +- if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) { ++ if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dnum[0]].cfg)) { + /* RDI */ + temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; +- if (!temp && hc->chan[hc->dslot].rdi) ++ if (!temp && hc->chan[hc->dnum[0]].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_ON, + "RDI detected"); +- if (temp && !hc->chan[hc->dslot].rdi) ++ if (temp && !hc->chan[hc->dnum[0]].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_OFF, + "RDI gone"); +- hc->chan[hc->dslot].rdi = temp; ++ hc->chan[hc->dnum[0]].rdi = temp; + } + temp = HFC_inb_nodebug(hc, R_JATT_DIR); +- switch (hc->chan[hc->dslot].sync) { ++ switch (hc->chan[hc->dnum[0]].sync) { + case 0: + if ((temp & 0x60) == 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) +@@ -2485,10 +2486,10 @@ handle_timer_irq(struct hfc_multi *hc) + "in clock sync\n", + __func__, hc->id); + HFC_outb(hc, R_RX_OFF, +- hc->chan[hc->dslot].jitter | V_RX_INIT); ++ hc->chan[hc->dnum[0]].jitter | V_RX_INIT); + HFC_outb(hc, R_TX_OFF, +- hc->chan[hc->dslot].jitter | V_RX_INIT); +- hc->chan[hc->dslot].sync = 1; ++ hc->chan[hc->dnum[0]].jitter | V_RX_INIT); ++ hc->chan[hc->dnum[0]].sync = 1; + goto check_framesync; + } + break; +@@ -2499,7 +2500,7 @@ handle_timer_irq(struct hfc_multi *hc) + "%s: (id=%d) E1 " + "lost clock sync\n", + __func__, hc->id); +- hc->chan[hc->dslot].sync = 0; ++ hc->chan[hc->dnum[0]].sync = 0; + break; + } + check_framesync: +@@ -2510,7 +2511,7 @@ check_framesync: + "%s: (id=%d) E1 " + "now in frame sync\n", + __func__, hc->id); +- hc->chan[hc->dslot].sync = 2; ++ hc->chan[hc->dnum[0]].sync = 2; + } + break; + case 2: +@@ -2520,7 +2521,7 @@ check_framesync: + "%s: (id=%d) E1 lost " + "clock & frame sync\n", + __func__, hc->id); +- hc->chan[hc->dslot].sync = 0; ++ hc->chan[hc->dnum[0]].sync = 0; + break; + } + temp = HFC_inb_nodebug(hc, R_SYNC_STA); +@@ -2530,7 +2531,7 @@ check_framesync: + "%s: (id=%d) E1 " + "lost frame sync\n", + __func__, hc->id); +- hc->chan[hc->dslot].sync = 1; ++ hc->chan[hc->dnum[0]].sync = 1; + } + break; + } +@@ -2746,7 +2747,8 @@ hfcmulti_interrupt(int intno, void *dev_id) + if (r_irq_misc & V_STA_IRQ) { + if (hc->ctype == HFC_TYPE_E1) { + /* state machine */ +- dch = hc->chan[hc->dslot].dch; ++#warning todo ++ dch = hc->chan[hc->dnum[0]].dch; + e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && hc->e1_getclock) { +@@ -2768,7 +2770,15 @@ hfcmulti_interrupt(int intno, void *dev_id) + } + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA) + & 0x7; ++#warning todo hack!!! broadcast state change!!! ++ dch = hc->chan[hc->dnum[0]].dch; + schedule_event(dch, FLG_PHCHANGE); ++ dch = hc->chan[hc->dnum[1]].dch; ++ dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA) ++ & 0x7; ++ schedule_event(dch, FLG_PHCHANGE); ++ ++ + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 (id=%d) newstate %x\n", +@@ -3851,31 +3861,35 @@ hfcmulti_initmode(struct dchannel *dch) + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + ++ i = dch->slot; ++ pt = hc->chan[i].port; + if (hc->ctype == HFC_TYPE_E1) { +- hc->chan[hc->dslot].slot_tx = -1; +- hc->chan[hc->dslot].slot_rx = -1; +- hc->chan[hc->dslot].conf = -1; +- if (hc->dslot) { +- mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol, ++ /* E1 */ ++#warning todo: don''t do it if dnum == 0 ++ hc->chan[hc->dnum[pt]].slot_tx = -1; ++ hc->chan[hc->dnum[pt]].slot_rx = -1; ++ hc->chan[hc->dnum[pt]].conf = -1; ++ if (hc->dnum[pt]) { ++ mode_hfcmulti(hc, dch->slot, dch->dev.D.protocol, + -1, 0, -1, 0); + dch->timer.function = (void *) hfcmulti_dbusy_timer; + dch->timer.data = (long) dch; + init_timer(&dch->timer); + } + for (i = 1; i <= 31; i++) { +- if (i == hc->dslot) ++ if (!((1 << i) & hc->bmask[pt])) /* skip unused channel */ + continue; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); + } +- /* E1 */ +- if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { ++#warning todo (global) ++ if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[pt]].cfg)) { + HFC_outb(hc, R_LOS0, 255); /* 2 ms */ + HFC_outb(hc, R_LOS1, 255); /* 512 ms */ + } +- if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) { ++ if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dnum[pt]].cfg)) { + HFC_outb(hc, R_RX0, 0); + hc->hw.r_tx0 = 0 | V_OUT_EN; + } else { +@@ -3888,12 +3902,12 @@ hfcmulti_initmode(struct dchannel *dch) + HFC_outb(hc, R_TX_FR0, 0x00); + HFC_outb(hc, R_TX_FR1, 0xf8); + +- if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) ++ if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[pt]].cfg)) + HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); + + HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); + +- if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) ++ if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[pt]].cfg)) + HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); + + if (dch->dev.D.protocol == ISDN_P_NT_E1) { +@@ -3957,7 +3971,7 @@ hfcmulti_initmode(struct dchannel *dch) + plxsd_checksync(hc, 0); + } + } else { +- i = dch->slot; ++ /* ST */ + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; +@@ -3973,8 +3987,6 @@ hfcmulti_initmode(struct dchannel *dch) + hc->chan[i - 1].slot_rx = -1; + hc->chan[i - 1].conf = -1; + mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); +- /* ST */ +- pt = hc->chan[i].port; + /* select interface */ + HFC_outb(hc, R_ST_SEL, pt); + /* undocumented: delay after R_ST_SEL */ +@@ -4557,6 +4569,8 @@ release_port(struct hfc_multi *hc, struct dchannel *dch) + } + /* free channels */ + for (i = 0; i <= 31; i++) { ++ if (!((1 << i) & hc->bmask[pt])) /* skip unused channel */ ++ continue; + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG +@@ -4680,12 +4694,13 @@ release_card(struct hfc_multi *hc) + } + + static int +-init_e1_port(struct hfc_multi *hc, struct hm_map *m) ++init_e1_port(struct hfc_multi *hc, struct hm_map *m, int pt) + { + struct dchannel *dch; + struct bchannel *bch; + int ch, ret = 0; + char name[MISDN_MAX_IDLEN]; ++ int bcount = 0; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) +@@ -4698,13 +4713,12 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; +- dch->dev.nrbchan = (hc->dslot) ? 30 : 31; +- dch->slot = hc->dslot; +- hc->chan[hc->dslot].dch = dch; +- hc->chan[hc->dslot].port = 0; +- hc->chan[hc->dslot].nt_timer = -1; ++ dch->slot = hc->dnum[pt]; ++ hc->chan[hc->dnum[pt]].dch = dch; ++ hc->chan[hc->dnum[pt]].port = pt; ++ hc->chan[hc->dnum[pt]].nt_timer = -1; + for (ch = 1; ch <= 31; ch++) { +- if (ch == hc->dslot) /* skip dchannel */ ++ if (!((1 << ch) & hc->bmask[pt])) /* skip unused channel */ + continue; + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { +@@ -4733,7 +4747,10 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + hc->chan[ch].bch = bch; + hc->chan[ch].port = 0; + set_channelmap(bch->nr, dch->dev.channelmap); ++ bcount++; + } ++ dch->dev.nrbchan = bcount; ++#warning todo: must be set globally, and must be a seperate function + /* set optical line type */ + if (port[Port_cnt] & 0x001) { + if (!m->opticalsupport) { +@@ -4749,7 +4766,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + __func__, + HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_OPTICAL, +- &hc->chan[hc->dslot].cfg); ++ &hc->chan[hc->dnum[pt]].cfg); + } + } + /* set LOS report */ +@@ -4759,7 +4776,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + "LOS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_LOS, +- &hc->chan[hc->dslot].cfg); ++ &hc->chan[hc->dnum[pt]].cfg); + } + /* set AIS report */ + if (port[Port_cnt] & 0x008) { +@@ -4768,7 +4785,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + "AIS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_AIS, +- &hc->chan[hc->dslot].cfg); ++ &hc->chan[hc->dnum[pt]].cfg); + } + /* set SLIP report */ + if (port[Port_cnt] & 0x010) { +@@ -4778,7 +4795,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_SLIP, +- &hc->chan[hc->dslot].cfg); ++ &hc->chan[hc->dnum[pt]].cfg); + } + /* set RDI report */ + if (port[Port_cnt] & 0x020) { +@@ -4788,7 +4805,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_RDI, +- &hc->chan[hc->dslot].cfg); ++ &hc->chan[hc->dnum[pt]].cfg); + } + /* set CRC-4 Mode */ + if (!(port[Port_cnt] & 0x100)) { +@@ -4797,7 +4814,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + " card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_CRC4, +- &hc->chan[hc->dslot].cfg); ++ &hc->chan[hc->dnum[pt]].cfg); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn off CRC4" +@@ -4829,20 +4846,23 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) + } + /* set elastic jitter buffer */ + if (port[Port_cnt] & 0x3000) { +- hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3; ++ hc->chan[hc->dnum[pt]].jitter = (port[Port_cnt]>>12) & 0x3; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set elastic " + "buffer to %d: card(%d) port(%d)\n", +- __func__, hc->chan[hc->dslot].jitter, ++ __func__, hc->chan[hc->dnum[pt]].jitter, + HFC_cnt + 1, 1); + } else +- hc->chan[hc->dslot].jitter = 2; /* default */ +- snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); ++ hc->chan[hc->dnum[pt]].jitter = 2; /* default */ ++ if (hc->ports > 1) ++ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d-%d", HFC_cnt + 1, pt+1); ++ else ++ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); + if (ret) + goto free_chan; +- hc->created[0] = 1; ++ hc->created[pt] = 1; + return ret; + free_chan: + release_port(hc, dch); +@@ -5009,18 +5029,30 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, + hc->id = HFC_cnt; + hc->pcm = pcm[HFC_cnt]; + hc->io_mode = iomode[HFC_cnt]; ++#warning todo: rework module parameters for customizing e1 fragments.... yea, let''s call it: fragments + if (dslot[HFC_cnt] < 0 && hc->ctype == HFC_TYPE_E1) { +- hc->dslot = 0; ++ hc->dnum[0] = 0; + printk(KERN_INFO "HFC-E1 card has disabled D-channel, but " + "31 B-channels\n"); + } + if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32 + && hc->ctype == HFC_TYPE_E1) { +- hc->dslot = dslot[HFC_cnt]; ++ hc->dnum[0] = dslot[HFC_cnt]; + printk(KERN_INFO "HFC-E1 card has alternating D-channel on " + "time slot %d\n", dslot[HFC_cnt]); + } else +- hc->dslot = 16; ++ hc->dnum[0] = 16; ++ ++#warning todo HACK!!! just a small map of two "fragments" ++ if (hc->ctype == HFC_TYPE_E1) { ++ hc->dnum[0] = 1; ++ hc->bmask[0] = 0x0000003c; ++ hc->dnum[1] = 6; ++ hc->bmask[1] = 0x00000780; ++ hc->dnum[2] = 11; ++ hc->bmask[2] = 0x00007800; ++ hc->ports = 3; ++ } + + /* set chip specific features */ + hc->masterclk = -1; +@@ -5103,7 +5135,7 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, + goto free_card; + } + if (hc->ctype == HFC_TYPE_E1) +- ret_err = init_e1_port(hc, m); ++ ret_err = init_e1_port(hc, m, pt); + else + ret_err = init_multi_port(hc, pt); + if (debug & DEBUG_HFCMULTI_INIT) +@@ -5115,10 +5147,14 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, + if (ret_err) { + while (pt) { /* release already registered ports */ + pt--; +- release_port(hc, hc->chan[(pt << 2) + 2].dch); ++ if (hc->ctype == HFC_TYPE_E1) ++ release_port(hc, hc->chan[hc->dnum[pt]].dch); ++ else ++ release_port(hc, hc->chan[(pt << 2) + 2].dch); + } + goto free_card; + } ++#warning todo: count it right, add additional "fragment" counter... + Port_cnt++; + } + diff --git a/openbsc/.gitignore b/openbsc/.gitignore new file mode 100644 index 000000000..267c906ec --- /dev/null +++ b/openbsc/.gitignore @@ -0,0 +1,45 @@ +*.o +*.a +.deps +Makefile +Makefile.in +bscconfig.h +bscconfig.h.in +openbsc.pc +bsc_hack +bsc_msc_ip +bsc_mgcp +*.*~ +*.sw? + +#configure +aclocal.m4 +autom4te.cache/ +config.log +config.status +configure +depcomp +install-sh +missing +stamp-h1 + + + +# apps and app data +hlr.sqlite3 +bs11_config +ipaccess-config +ipaccess-find +ipaccess-firmware +ipaccess-proxy +isdnsync + +#tests +tests/channel/channel_test +tests/db/db_test +tests/debug/debug_test +tests/gsm0408/gsm0408_test +tests/sccp/sccp_test +tests/sms/sms_test +tests/timer/timer_test + diff --git a/openbsc/AUTHORS b/openbsc/AUTHORS new file mode 100644 index 000000000..daf60e447 --- /dev/null +++ b/openbsc/AUTHORS @@ -0,0 +1,7 @@ +Harald Welte <laforge@gnumonks.org> +Holger Freyther <zecke@selfish.org> +Jan Luebbe <jluebbe@debian.org> +Stefan Schmidt <stefan@datenfreihafen.org> +Daniel Willmann <daniel@totalueberwachung.de> +Andreas Eversberg <Andreas.Eversberg@versatel.de> +Sylvain Munaut <246tnt@gmail.com> diff --git a/openbsc/COPYING b/openbsc/COPYING new file mode 100644 index 000000000..d511905c1 --- /dev/null +++ b/openbsc/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/openbsc/Makefile.am b/openbsc/Makefile.am new file mode 100644 index 000000000..7acae7512 --- /dev/null +++ b/openbsc/Makefile.am @@ -0,0 +1,10 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +SUBDIRS = include src tests + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = openbsc.pc libsccp.pc + +#dist-hook: +# rm -rf `find $(distdir) -name .svn` diff --git a/openbsc/README b/openbsc/README new file mode 100644 index 000000000..51807bb44 --- /dev/null +++ b/openbsc/README @@ -0,0 +1,30 @@ +About OpenBSC +============= + +OpenBSC is a minimalistic implementation of the GSM Network, with +particular emphasis on the functionality typically provided by the BSC, +MSC, HLR, VLR and SMSC. + +Its currently supported interfaces towards the BTS are: + + * Classic A-bis over E1 using a mISDN based E1 interface. In other + words, you can connect existing GSM Base Transceiver Station (BTS) + through E1 to OpenBSC. So far, we have only tested the Siemens BS-11 + Test reports with other BTS are much appreciated! + + * A-bis over IP as used by the ip.access nanoBTS product family + +This project is still in its early days, and there are lots of areas where it +doesn't behave as per GSM spec. + + Harald Welte <laforge@gnumonks.org> + + +libosmocore +=========== + +Please note that as of March 2010, OpenBSC has a dependency to a library +called "libosmocore". You can obtain that library from + + git://git.osmocom.org/libosmocore.git + diff --git a/openbsc/configure.in b/openbsc/configure.in new file mode 100644 index 000000000..71ed10b5f --- /dev/null +++ b/openbsc/configure.in @@ -0,0 +1,55 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT + +AM_INIT_AUTOMAKE(openbsc, 0.0alpha1) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +dnl checks for libraries +AC_SEARCH_LIBS(crypt, crypt, + [LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])]) + +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +# The following test is taken from WebKit's webkit.m4 +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fvisibility=hidden " +AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) +AC_COMPILE_IFELSE([char foo;], + [ AC_MSG_RESULT([yes]) + SYMBOL_VISIBILITY="-fvisibility=hidden"], + AC_MSG_RESULT([no])) +CFLAGS="$saved_CFLAGS" +AC_SUBST(SYMBOL_VISIBILITY) + + +dnl Generate the output +AM_CONFIG_HEADER(bscconfig.h) + +AC_OUTPUT( + openbsc.pc + libsccp.pc + include/openbsc/Makefile + include/vty/Makefile + include/sccp/Makefile + include/Makefile + src/Makefile + tests/Makefile + tests/debug/Makefile + tests/gsm0408/Makefile + tests/db/Makefile + tests/channel/Makefile + tests/sccp/Makefile + Makefile) diff --git a/openbsc/contrib/bt.py b/openbsc/contrib/bt.py new file mode 100755 index 000000000..1b111efc8 --- /dev/null +++ b/openbsc/contrib/bt.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import os + +f = open("unbalanced") +lines = [] +for line in f: + lines.append(line) + +filenames = {} + +output = [] +for line in lines: + if "[0x" in line: + start = line.find("[") + end = line.find("]") + addr = line[start+1:end] + try: + file = filenames[addr] + except KeyError: + r = os.popen("addr2line -fs -e ./bsc_hack %s" % addr) + all = r.read().replace("\n", ",") + file = all + filenames[addr] = file + + line = line.replace(addr, file) + output.append(line) + +g = open("unbalanced.2", "w") +g.write("".join(output)) + + + diff --git a/openbsc/contrib/convert_to_enum.py b/openbsc/contrib/convert_to_enum.py new file mode 100755 index 000000000..bcd6f2cee --- /dev/null +++ b/openbsc/contrib/convert_to_enum.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# +# Convert ETSI documents to an enum +# + +import re, sys + +def convert(string): + string = string.strip().replace(" ", "").rjust(8, "0") + var = 0 + offset = 7 + for char in string: + assert offset >= 0 + var = var | (int(char) << offset) + offset = offset - 1 + + return var + +def string(name): + name = name.replace(" ", "_") + name = name.replace('"', "") + name = name.replace('/', '_') + name = name.replace('(', '_') + name = name.replace(')', '_') + return "%s_%s" % (sys.argv[2], name.upper()) + +file = open(sys.argv[1]) + + +for line in file: + m = re.match(r"[ \t]*(?P<value>[01 ]+)[ ]+(?P<name>[a-zA-Z /0-9()]+)", line[:-1]) + + if m: + print "\t%s\t\t= %d," % (string(m.groupdict()["name"]), convert(m.groupdict()["value"])) + else: + print line[:-1] diff --git a/openbsc/contrib/mgcp_server.py b/openbsc/contrib/mgcp_server.py new file mode 100755 index 000000000..cf3ef3845 --- /dev/null +++ b/openbsc/contrib/mgcp_server.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# Simple server for mgcp... send audit, receive response.. + +import socket, time + +MGCP_GATEWAY_PORT = 2427 +MGCP_CALLAGENT_PORT = 2727 + +rsip_resp = """200 321321332\r\n""" +audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n""" +crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n""" +dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n""" +mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 4400 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n""" + +def hexdump(src, length=8): + """Recipe is from http://code.activestate.com/recipes/142812/""" + result = [] + digits = 4 if isinstance(src, unicode) else 2 + for i in xrange(0, len(src), length): + s = src[i:i+length] + hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s]) + text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s]) + result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) ) + return b'\n'.join(result) + +server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT)) +server_socket.setblocking(0) + + +def send_receive(packet): + server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT)) + try: + data, addr = server_socket.recvfrom(4096) + print hexdump(data), addr + except socket.error: + pass + +def generate_tid(): + import random + return random.randint(0, 65123) + + + +i = 1 +while True: + send_receive(rsip_resp) + send_receive(audit_packet) + send_receive(crcx_packet % generate_tid() ) + send_receive(mdcx_packet % (generate_tid(), i)) + send_receive(dlcx_packet % (generate_tid(), i)) + i = i + 1 + + time.sleep(3) diff --git a/openbsc/doc/BS11-OML.txt b/openbsc/doc/BS11-OML.txt new file mode 100644 index 000000000..e5c3299c9 --- /dev/null +++ b/openbsc/doc/BS11-OML.txt @@ -0,0 +1,31 @@ +The Siemens BS-11 supports the following additional GSM 12.21 OML operations: + + +CREATE OBJECT + +abis_om_fom_hdr.obj_class can be +A3: +A5: ALCO, BBSIG, CCLK, GPSU, LI, PA +A8: EnvaBTSE +A9: BPORT + +the abis_om_obj_inst.trx_nr field indicates the index of object, whereas the +abis_om_fom_hdr.bts_nr indicates the type of the object. + +enum abis_bs11_objtype { + BS11_OBJ_ALCO = 0x01, + BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */ + BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */ + BS11_OBJ_CCLK = 0x04, + BS11_OBJ_GPSU = 0x06, + BS11_OBJ_LI = 0x07, + BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/ +}; + +In case of CREATE ENVABTSE, the abis_om_obj_inst.trx_nr indicates the EnvaBTSEx +number. + +In case of A9 (CREAETE BPORT), the abis_om_obj_inst.bts_nr indicates which BPORT +shall be used. + + diff --git a/openbsc/doc/call-routing.txt b/openbsc/doc/call-routing.txt new file mode 100644 index 000000000..3402f9e33 --- /dev/null +++ b/openbsc/doc/call-routing.txt @@ -0,0 +1,25 @@ +Call routing in OpenBSC + +Flow of events: + + # MO call initiated by MS, CHANNEL RQD, IMMEDIATE ASSIGN + # MS sends CC SETUP message, we assume already on TCH/H FACCH + # OpenBSC does a subscriber lookup based on the target extension + * If a subscriber is found: + # send CALL PROCEEDING message to MO + # page the MT subscriber and ask itI to ask for TCH/H + # once paging completes, we have the TCH/H for the MT end + # send SETUP to MT + # receive CALL CONFIRMED from MT + # set-up the TRAU mux mapping between the E1 subslots for both TCH/H + # receive ALERTING from MT, route ALERTING to MO + # receive CONNECT from MT, confirm to MT with CONNECT_ACK + # send a CONNECT message to MO, receive CONNECT_ACK from MO + * If subscriber is not found: + # send RELEASE COMPLETE with apropriate cause to MO (1: unalloacated 3: no route) + + + +Thoughts about RR/MM: + +* we allocate RR/MM entities on demand, when we need them diff --git a/openbsc/doc/channel_release.txt b/openbsc/doc/channel_release.txt new file mode 100644 index 000000000..bacf09c8d --- /dev/null +++ b/openbsc/doc/channel_release.txt @@ -0,0 +1,74 @@ + +GSM 04.08 7.1.7 / 9.1.7 RR CHANNEL RELESE + +RSL 08.58 3.4 / ? RLL Link Release Request + +RSL 08.58 4.6 / 8.4.5 DEACTivate SACCH + * Deactivate SACCH according to Channel Release Proc 04.08 + * to be sent after RR CHANNEL RELEASE is sent to MS + +RSL 08.58 4.7 / 8.4.14 RF CHANnel RELease + * tells the BTS to release a radio channel + * "when an activated radio channel is no longer needed" + * BTS responds with RF CHANnel RELease ACKnowledge + + +GSM 04.08 3.4.13: RR connection release procedure + +* network sends RR CHANNEL RELEASE to MS on the DCCH + * start T3109 + * deactivate SACCH +* MS disconnects main signalling link (by sending DISC) + * all other data links are disconnected by local end link release +* network receives DISC (BTS sends RLL REL IND to BSC) + * stop T3109 + * start T3111 +* when T3111 times out, the network can reuse the channls +* if T3109 times out, the network deactivates the channels + and can reuse them + * this probably means simply RF CHANnel RELease + + +== Implementation in OpenBSC == + +chan_alloc.c:lchan_auto_release() + * checks if use count still > 0 (abort) + * calls gsm48_send_rr_release() + * which calls rsl_deact_sacch() + * calls rsl_release_request() + * which sends RLL Link Release request + +RX of RELease INDication: + * call rsl_rf_chan_release() (send RF_CHAN_REL) + +RX of RELease CONFimem: + * call rsl_rf_chan_release() (send RF_CHAN_REL) + +* RX of RF_CHAN_REL_ACK + * call lchan_free() + * subscr_put() + * delete release_timer + + +=== Integration with SMS === + +* RX of CP_ERROR or unimplemented MT + * trigger trans_free() which will lchan_auto_release() + +* CP TC1* expired while waiting for CP-ACK + * trigger trans_free() which will lchan_auto_release() + +* RX of RP_ERROR + * trigger trans_free() which will lchan_auto_release() + +* TX of CP-ACK in MT DELIVER + * trigger trans_free() which will lchan_auto_release() + +* RX of CP-ACK in MO SUBMIT + * trigger trans_free() which will lchan_auto_release() + +* RX of RP-ACK in MT DELIVER (and no more messages) + * trigger rsl_release_request() for SAPI3 + +* RX of RP-SMMA in MT DELIVER (and no more messages) + * trigger rsl_release_request() for SAPI3 diff --git a/openbsc/doc/e1-data-model.txt b/openbsc/doc/e1-data-model.txt new file mode 100644 index 000000000..8594fe456 --- /dev/null +++ b/openbsc/doc/e1-data-model.txt @@ -0,0 +1,172 @@ +E1 related data model + +This data model describes the physical relationship of the individual +parts in the network, it is not the logical/protocol side of the GSM +network. + +A BTS is connected to the BSC by some physical link. It could be an actual +E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP. + +To further complicate the fact, multiple BTS can share one such pysical +link. On a single E1 line, we can easily accomodate up to three BTS with +two TRX each. + +Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's +view of a BTS connected to it. We call this 'bts_link'. A bts_link can be +* all the TCP and UDP streams of a Abis-over-IP BTS +* a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link +* a serial line exclusively used for OML messages (T-Link) + +A bts_link can be registered with the OpenBSC core at runtime. + +struct trx_link { + struct gsm_bts_trx *trx; +}; + +struct bts_link { + struct gsm_bts *bts; + struct trx_link trx_links[NUM_TRX]; +}; + +Interface from stack to input core: +====================================================================== +int abis_rsl_sendmsg(struct msgb *msg); + send a message through a RSL link to the TRX specified by the caller in + msg->trx. + +int abis_rsl_rcvmsg(struct msgb *msg); + receive a message from a RSL link from the TRX specified by the + caller in msg->trx. + +int abis_nm_sendmsg(struct msgb *msg); + send a message through a OML link to the BTS specified by the caller in + msg->trx->bts. The caller can just use bts->c0 to get the first TRX + in a BTS. (OML messages are not really sent to a TRX but to the BTS) + +int abis_nm_rcvmsg(struct msgb *msg); + receive a message from a OML link from the BTS specified by the caller + in msg->trx->bts. The caller can just use bts->c0 to get the first + TRX in a BTS. + +int abis_link_event(int event, void *data); + signal some event (such as layer 1 connect/disconnect) from the + input core to the stack. + +int subch_demux_in(mx, const u_int8_t *data, int len); + receive 'len' bytes from a given E1 timeslot (TRAU frames) + +int subchan_mux_out(mx, u_int8_t *data, int len); + obtain 'len' bytes of output data to be sent on E1 timeslot + +Intrface by Input Core for Input Plugins +====================================================================== + +int btslink_register_plugin(); + + +Configuration for the E1 input module +====================================================================== + +BTS + BTS number + number of TRX + OML link + E1 line number + timeslot number + [subslot number] + SAPI + TEI + for each TRX + RSL link + E1 line number + timeslot number + [subslot number] + SAPI + TEI + for each TS + E1 line number + timeslot number + subslot number + + +E1 input module data model +====================================================================== + + +enum e1inp_sign_type { + E1INP_SIGN_NONE, + E1INP_SIGN_OML, + E1INP_SIGN_RSL, +}; + +struct e1inp_sign_link { + /* list of signalling links */ + struct llist_head list; + + enum e1inp_sign_type type; + + /* trx for msg->trx of received msgs */ + struct gsm_bts_trx *trx; + + /* msgb queue of to-be-transmitted msgs */ + struct llist_head tx_list; + + /* SAPI and TEI on the E1 TS */ + u_int8_t sapi; + u_int8_t tei; +} + +enum e1inp_ts_type { + E1INP_TS_TYPE_NONE, + E1INP_TS_TYPE_SIGN, + E1INP_TS_TYPE_TRAU, +}; + +/* A timeslot in the E1 interface */ +struct e1inp_ts { + enum e1inp_ts_type type; + struct e1inp_line *line; + union { + struct { + struct llist_head sign_links; + } sign; + struct { + /* subchannel demuxer for frames from E1 */ + struct subch_demux demux; + /* subchannel muxer for frames to E1 */ + struct subch_mux mux; + } trau; + }; + union { + struct { + /* mISDN driver has one fd for each ts */ + struct bsc_fd; + } misdn; + } driver; +}; + +struct e1inp_line { + unsigned int num; + char *name; + + struct e1inp_ts ts[NR_E1_TS]; + + char *e1inp_driver; + void *driver_data; +}; + +/* Call from the Stack: configuration of this TS has changed */ +int e1inp_update_ts(struct e1inp_ts *ts); + +/* Receive a packet from the E1 driver */ +int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, + u_int8_t tei, u_int8_t sapi); + +/* Send a packet, callback function in the driver */ +int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg) + + +struct e1inp_driver { + const char *name; + int (*want_write)(struct e1inp_ts *ts); +}; diff --git a/openbsc/doc/gsm-hopping.txt b/openbsc/doc/gsm-hopping.txt new file mode 100644 index 000000000..591093999 --- /dev/null +++ b/openbsc/doc/gsm-hopping.txt @@ -0,0 +1,54 @@ +according to GSM 05.02: + +general parameters from CCCH: +* CA cell allocation of ARFCN's (System Information / BCCH) +* FN: TDMA frame number (t1,t2,t3') in SCH + +specific parameters from channel assignment: +* MA: mobile allocation, defines set of ARFCN's, up to 64 +* MAIO: index +* HSN: hopping sequence generator number (0..64) + + +hopping sequence generation (6.2.3): + +u_int8_t rntable[114] = { + 48, 98, 63, 1, 36, 95, 78, 102, 94, 73, + 0, 64, 25, 81, 76, 59, 124, 23, 104, 100, + 101, 47, 118, 85, 18, 56, 96, 86, 54, 2, + 80, 34, 127, 13, 6, 89, 57, 103, 12, 74, + 55, 111, 75, 38, 109, 71, 112, 29, 11, 88, + 87, 19, 3, 68, 110, 26, 33, 31, 8, 45, + 82, 58, 40, 107, 32, 5, 106, 92, 62, 67, + 77, 108, 122, 37, 60, 66, 121, 42, 51, 126, + 117, 114, 4, 90, 43, 52, 53, 113, 120, 72, + 16, 49, 7, 79, 119, 61, 22, 84, 9, 97, + 125, 99, 17, 123 +}; + +/* mai=0 represents lowest ARFCN in the MA */ + + +u_int8_t hopping_mai(u_int8_t hsn, u_int32_t fn, u_int8_t maio, + u_int8_t t1, u_int8_t t2, u_int8_t t3_) +{ + u_int8_t mai; + + if (hsn == 0) /* cyclic hopping */ + mai = (fn + maio) % n; + else { + u_int32_t m, m_, t_, s; + + m = t2 + rntable[(hsn xor (t1 % 64)) + t3]; + m_ = m % (2^NBIN); + t_ = t3 % (2^NBIN); + if (m_ < n then) + s = m_; + else + s = (m_ + t_) % n; + mai = (s + maio) % n; + } + + return mai; +} + diff --git a/openbsc/doc/handover.txt b/openbsc/doc/handover.txt new file mode 100644 index 000000000..ac19e8725 --- /dev/null +++ b/openbsc/doc/handover.txt @@ -0,0 +1,89 @@ +Ideas about a handover algorithm +====================================================================== + +This is mostly based on the results presented in Chapter 8 of "Performance +Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen +and Joeroen Wigard. + + +=== Reasons for performing handover === + +Section 2.1.1: Handover used in their CAPACITY simulation: + +1) Interference Handover + +Average RXLEV is satisfactory high, but average RXQUAL too low indicates +interference to the channel. Handover should be made. + +2) Bad Quality + +Averaged RXQUAL is lower than a threshold + +3) Low Level / Signal Strength + +Average RXLEV is lower than a threshold + +4) Distance Handover + +MS is too far away from a cell (measured by TA) + +5) Power budget / Better Cell + +RX Level of neighbor cell is at least "HO Margin dB" dB better than the +current serving cell. + +=== Ideal parameters for HO algorithm === + +Chapter 8, Section 2.2, Table 24: + +Window RXLEV averaging: 10 SACCH frames (no weighting) +Window RXQUAL averaging: 1 SACCH frame (no averaging) +Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm +Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5 +Interference Threshold: 1 of the last AV-RXLEV > -85 dBm & + 3 of the last 4 AV-RXQUAL values >= 5 +Power Budget: Level of neighbor cell > 3 dB better +Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?) +Distance Handover: Disabled +Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm +Evaluation rule 2: Level of candidate cell > 3dB better own cell +Timer Successful HO: 5 SACCH frames +Timer Unsuccessful HO: 1 SACCH frame + +In a non-frequency hopping case, RXQUAL threshold can be decreased to +RXLEV >= 4 + +When frequency hopping is enabled, the following additional parameters +should be introduced: + +* No intra-cell handover +* Use a HO Margin of 2dB + +=== Handover Channel Reservation === + +In loaded network, each cell should reserve some channels for handovers, +rather than using all of them for new call establishment. This reduces the +need to drop calls due to failing handovers, at the expense of failing new call +attempts. + +=== Dynamic HO Margin === + +The handover margin (hysteresis) should depend on the RXQUAL. Optimal results +were achieved with the following settings: +* RXQUAL <= 4: 9 dB +* RXQUAL == 5: 6 dB +* RXQUAL >= 6: 1 dB + + + +== Actual Handover on a protocol level == + +After the BSC has decided a handover shall be done, it has to + +# allocate a channel at the new BTS +# allocate a handover reference +# activate the channel on the BTS side using RSL CHANNEL ACTIVATION, + indicating the HO reference +# BTS responds with CHAN ACT ACK, including GSM frame number +# BSC sends 04.08 HO CMD to MS using old BTS + diff --git a/openbsc/doc/oml-interface.txt b/openbsc/doc/oml-interface.txt new file mode 100644 index 000000000..8ddcfea5c --- /dev/null +++ b/openbsc/doc/oml-interface.txt @@ -0,0 +1,21 @@ +oml interface design notes + +problems: + +* there is no way how to tag a command sent to the BTS, with the response + having the same tag to identify the originator of the command +* therefore, we can have e.g. both the BSC and the OML interface send a + SET ATTRIBUTE message, where the responses would end up at the wrong + query. + +the only possible solutions i can imagine: +* have some kind of exclusive locking, where the OML interface gets blocked + from the BSC and is exclusively assigned to the OML console until all commands + of the OML console have terminated. This can either be done explicitly + dynamically or on demand + +* use the OML interface synchronously, i.e. always wait for the response from + the BTS before + +* unilateral / unsolicited messages need to be broadcasted to both the BSC and + the OML console diff --git a/openbsc/include/Makefile.am b/openbsc/include/Makefile.am new file mode 100644 index 000000000..56b2a338a --- /dev/null +++ b/openbsc/include/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = openbsc vty sccp + +noinst_HEADERS = mISDNif.h compat_af_isdn.h diff --git a/openbsc/include/compat_af_isdn.h b/openbsc/include/compat_af_isdn.h new file mode 100644 index 000000000..56cbfb3f2 --- /dev/null +++ b/openbsc/include/compat_af_isdn.h @@ -0,0 +1,39 @@ +#ifdef MISDN_OLD_AF_COMPATIBILITY +#undef AF_ISDN +#undef PF_ISDN + +extern int AF_ISDN; +#define PF_ISDN AF_ISDN + +int AF_ISDN; + +#endif + +extern void init_af_isdn(void); + +#ifdef AF_COMPATIBILITY_FUNC +#ifdef MISDN_OLD_AF_COMPATIBILITY +void init_af_isdn(void) +{ + int s; + + /* test for new value */ + AF_ISDN = 34; + s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (s >= 0) { + close(s); + return; + } + AF_ISDN = 27; + s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (s >= 0) { + close(s); + return; + } +} +#else +void init_af_isdn(void) +{ +} +#endif +#endif diff --git a/openbsc/include/mISDNif.h b/openbsc/include/mISDNif.h new file mode 100644 index 000000000..8e065d24b --- /dev/null +++ b/openbsc/include/mISDNif.h @@ -0,0 +1,387 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE + * version 2.1 as published by the Free Software Foundation. + * + * This code 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 LESSER GENERAL PUBLIC LICENSE for more details. + * + */ + +#ifndef mISDNIF_H +#define mISDNIF_H + +#include <stdarg.h> +#ifdef linux +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/socket.h> +#else +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/socket.h> +#endif + +/* + * ABI Version 32 bit + * + * <8 bit> Major version + * - changed if any interface become backwards incompatible + * + * <8 bit> Minor version + * - changed if any interface is extended but backwards compatible + * + * <16 bit> Release number + * - should be incremented on every checkin + */ +#define MISDN_MAJOR_VERSION 1 +#define MISDN_MINOR_VERSION 1 +#define MISDN_RELEASE 20 + +/* primitives for information exchange + * generell format + * <16 bit 0 > + * <8 bit command> + * BIT 8 = 1 LAYER private + * BIT 7 = 1 answer + * BIT 6 = 1 DATA + * <8 bit target layer mask> + * + * Layer = 00 is reserved for general commands + Layer = 01 L2 -> HW + Layer = 02 HW -> L2 + Layer = 04 L3 -> L2 + Layer = 08 L2 -> L3 + * Layer = FF is reserved for broadcast commands + */ + +#define MISDN_CMDMASK 0xff00 +#define MISDN_LAYERMASK 0x00ff + +/* generell commands */ +#define OPEN_CHANNEL 0x0100 +#define CLOSE_CHANNEL 0x0200 +#define CONTROL_CHANNEL 0x0300 +#define CHECK_DATA 0x0400 + +/* layer 2 -> layer 1 */ +#define PH_ACTIVATE_REQ 0x0101 +#define PH_DEACTIVATE_REQ 0x0201 +#define PH_DATA_REQ 0x2001 +#define MPH_ACTIVATE_REQ 0x0501 +#define MPH_DEACTIVATE_REQ 0x0601 +#define MPH_INFORMATION_REQ 0x0701 +#define PH_CONTROL_REQ 0x0801 + +/* layer 1 -> layer 2 */ +#define PH_ACTIVATE_IND 0x0102 +#define PH_ACTIVATE_CNF 0x4102 +#define PH_DEACTIVATE_IND 0x0202 +#define PH_DEACTIVATE_CNF 0x4202 +#define PH_DATA_IND 0x2002 +#define PH_DATA_E_IND 0x3002 +#define MPH_ACTIVATE_IND 0x0502 +#define MPH_DEACTIVATE_IND 0x0602 +#define MPH_INFORMATION_IND 0x0702 +#define PH_DATA_CNF 0x6002 +#define PH_CONTROL_IND 0x0802 +#define PH_CONTROL_CNF 0x4802 + +/* layer 3 -> layer 2 */ +#define DL_ESTABLISH_REQ 0x1004 +#define DL_RELEASE_REQ 0x1104 +#define DL_DATA_REQ 0x3004 +#define DL_UNITDATA_REQ 0x3104 +#define DL_INFORMATION_REQ 0x0004 + +/* layer 2 -> layer 3 */ +#define DL_ESTABLISH_IND 0x1008 +#define DL_ESTABLISH_CNF 0x5008 +#define DL_RELEASE_IND 0x1108 +#define DL_RELEASE_CNF 0x5108 +#define DL_DATA_IND 0x3008 +#define DL_UNITDATA_IND 0x3108 +#define DL_INFORMATION_IND 0x0008 + +/* intern layer 2 managment */ +#define MDL_ASSIGN_REQ 0x1804 +#define MDL_ASSIGN_IND 0x1904 +#define MDL_REMOVE_REQ 0x1A04 +#define MDL_REMOVE_IND 0x1B04 +#define MDL_STATUS_UP_IND 0x1C04 +#define MDL_STATUS_DOWN_IND 0x1D04 +#define MDL_STATUS_UI_IND 0x1E04 +#define MDL_ERROR_IND 0x1F04 +#define MDL_ERROR_RSP 0x5F04 + +/* DL_INFORMATION_IND types */ +#define DL_INFO_L2_CONNECT 0x0001 +#define DL_INFO_L2_REMOVED 0x0002 + +/* PH_CONTROL types */ +/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ +#define DTMF_TONE_VAL 0x2000 +#define DTMF_TONE_MASK 0x007F +#define DTMF_TONE_START 0x2100 +#define DTMF_TONE_STOP 0x2200 +#define DTMF_HFC_COEF 0x4000 +#define DSP_CONF_JOIN 0x2403 +#define DSP_CONF_SPLIT 0x2404 +#define DSP_RECEIVE_OFF 0x2405 +#define DSP_RECEIVE_ON 0x2406 +#define DSP_ECHO_ON 0x2407 +#define DSP_ECHO_OFF 0x2408 +#define DSP_MIX_ON 0x2409 +#define DSP_MIX_OFF 0x240a +#define DSP_DELAY 0x240b +#define DSP_JITTER 0x240c +#define DSP_TXDATA_ON 0x240d +#define DSP_TXDATA_OFF 0x240e +#define DSP_TX_DEJITTER 0x240f +#define DSP_TX_DEJ_OFF 0x2410 +#define DSP_TONE_PATT_ON 0x2411 +#define DSP_TONE_PATT_OFF 0x2412 +#define DSP_VOL_CHANGE_TX 0x2413 +#define DSP_VOL_CHANGE_RX 0x2414 +#define DSP_BF_ENABLE_KEY 0x2415 +#define DSP_BF_DISABLE 0x2416 +#define DSP_BF_ACCEPT 0x2416 +#define DSP_BF_REJECT 0x2417 +#define DSP_PIPELINE_CFG 0x2418 +#define HFC_VOL_CHANGE_TX 0x2601 +#define HFC_VOL_CHANGE_RX 0x2602 +#define HFC_SPL_LOOP_ON 0x2603 +#define HFC_SPL_LOOP_OFF 0x2604 + +/* DSP_TONE_PATT_ON parameter */ +#define TONE_OFF 0x0000 +#define TONE_GERMAN_DIALTONE 0x0001 +#define TONE_GERMAN_OLDDIALTONE 0x0002 +#define TONE_AMERICAN_DIALTONE 0x0003 +#define TONE_GERMAN_DIALPBX 0x0004 +#define TONE_GERMAN_OLDDIALPBX 0x0005 +#define TONE_AMERICAN_DIALPBX 0x0006 +#define TONE_GERMAN_RINGING 0x0007 +#define TONE_GERMAN_OLDRINGING 0x0008 +#define TONE_AMERICAN_RINGPBX 0x000b +#define TONE_GERMAN_RINGPBX 0x000c +#define TONE_GERMAN_OLDRINGPBX 0x000d +#define TONE_AMERICAN_RINGING 0x000e +#define TONE_GERMAN_BUSY 0x000f +#define TONE_GERMAN_OLDBUSY 0x0010 +#define TONE_AMERICAN_BUSY 0x0011 +#define TONE_GERMAN_HANGUP 0x0012 +#define TONE_GERMAN_OLDHANGUP 0x0013 +#define TONE_AMERICAN_HANGUP 0x0014 +#define TONE_SPECIAL_INFO 0x0015 +#define TONE_GERMAN_GASSENBESETZT 0x0016 +#define TONE_GERMAN_AUFSCHALTTON 0x0016 + +/* MPH_INFORMATION_IND */ +#define L1_SIGNAL_LOS_OFF 0x0010 +#define L1_SIGNAL_LOS_ON 0x0011 +#define L1_SIGNAL_AIS_OFF 0x0012 +#define L1_SIGNAL_AIS_ON 0x0013 +#define L1_SIGNAL_RDI_OFF 0x0014 +#define L1_SIGNAL_RDI_ON 0x0015 +#define L1_SIGNAL_SLIP_RX 0x0020 +#define L1_SIGNAL_SLIP_TX 0x0021 + +/* + * protocol ids + * D channel 1-31 + * B channel 33 - 63 + */ + +#define ISDN_P_NONE 0 +#define ISDN_P_BASE 0 +#define ISDN_P_TE_S0 0x01 +#define ISDN_P_NT_S0 0x02 +#define ISDN_P_TE_E1 0x03 +#define ISDN_P_NT_E1 0x04 +#define ISDN_P_TE_UP0 0x05 +#define ISDN_P_NT_UP0 0x06 + +#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \ + (p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE)) +#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \ + (p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT)) +#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0)) +#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1)) +#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0)) + + +#define ISDN_P_LAPD_TE 0x10 +#define ISDN_P_LAPD_NT 0x11 + +#define ISDN_P_B_MASK 0x1f +#define ISDN_P_B_START 0x20 + +#define ISDN_P_B_RAW 0x21 +#define ISDN_P_B_HDLC 0x22 +#define ISDN_P_B_X75SLP 0x23 +#define ISDN_P_B_L2DTMF 0x24 +#define ISDN_P_B_L2DSP 0x25 +#define ISDN_P_B_L2DSPHDLC 0x26 + +#define OPTION_L2_PMX 1 +#define OPTION_L2_PTP 2 +#define OPTION_L2_FIXEDTEI 3 +#define OPTION_L2_CLEANUP 4 + +/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */ +#define MISDN_MAX_IDLEN 20 + +struct mISDNhead { + unsigned int prim; + unsigned int id; +} __attribute__((packed)); + +#define MISDN_HEADER_LEN sizeof(struct mISDNhead) +#define MAX_DATA_SIZE 2048 +#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN) +#define MAX_DFRAME_LEN 260 + +#define MISDN_ID_ADDR_MASK 0xFFFF +#define MISDN_ID_TEI_MASK 0xFF00 +#define MISDN_ID_SAPI_MASK 0x00FF +#define MISDN_ID_TEI_ANY 0x7F00 + +#define MISDN_ID_ANY 0xFFFF +#define MISDN_ID_NONE 0xFFFE + +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 + +#define MISDN_MAX_CHANNEL 127 +#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3) + +#define SOL_MISDN 0 + +struct sockaddr_mISDN { + sa_family_t family; + unsigned char dev; + unsigned char channel; + unsigned char sapi; + unsigned char tei; +}; + +struct mISDNversion { + unsigned char major; + unsigned char minor; + unsigned short release; +}; + +#define MAX_DEVICE_ID 63 + +struct mISDN_devinfo { + u_int id; + u_int Dprotocols; + u_int Bprotocols; + u_int protocol; + u_char channelmap[MISDN_CHMAP_SIZE]; + u_int nrbchan; + char name[MISDN_MAX_IDLEN]; +}; + +struct mISDN_devrename { + u_int id; + char name[MISDN_MAX_IDLEN]; +}; + +struct ph_info_ch { + int32_t protocol; + int64_t Flags; +}; + +struct ph_info_dch { + struct ph_info_ch ch; + int16_t state; + int16_t num_bch; +}; + +struct ph_info { + struct ph_info_dch dch; + struct ph_info_ch bch[]; +}; + +/* timer device ioctl */ +#define IMADDTIMER _IOR('I', 64, int) +#define IMDELTIMER _IOR('I', 65, int) +/* socket ioctls */ +#define IMGETVERSION _IOR('I', 66, int) +#define IMGETCOUNT _IOR('I', 67, int) +#define IMGETDEVINFO _IOR('I', 68, int) +#define IMCTRLREQ _IOR('I', 69, int) +#define IMCLEAR_L2 _IOR('I', 70, int) +#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename) + +static inline int +test_channelmap(u_int nr, u_char *map) +{ + if (nr <= MISDN_MAX_CHANNEL) + return map[nr >> 3] & (1 << (nr & 7)); + else + return 0; +} + +static inline void +set_channelmap(u_int nr, u_char *map) +{ + map[nr >> 3] |= (1 << (nr & 7)); +} + +static inline void +clear_channelmap(u_int nr, u_char *map) +{ + map[nr >> 3] &= ~(1 << (nr & 7)); +} + +/* CONTROL_CHANNEL parameters */ +#define MISDN_CTRL_GETOP 0x0000 +#define MISDN_CTRL_LOOP 0x0001 +#define MISDN_CTRL_CONNECT 0x0002 +#define MISDN_CTRL_DISCONNECT 0x0004 +#define MISDN_CTRL_PCMCONNECT 0x0010 +#define MISDN_CTRL_PCMDISCONNECT 0x0020 +#define MISDN_CTRL_SETPEER 0x0040 +#define MISDN_CTRL_UNSETPEER 0x0080 +#define MISDN_CTRL_RX_OFF 0x0100 +#define MISDN_CTRL_FILL_EMPTY 0x0200 +#define MISDN_CTRL_GETPEER 0x0400 +#define MISDN_CTRL_HW_FEATURES_OP 0x2000 +#define MISDN_CTRL_HW_FEATURES 0x2001 +#define MISDN_CTRL_HFC_OP 0x4000 +#define MISDN_CTRL_HFC_PCM_CONN 0x4001 +#define MISDN_CTRL_HFC_PCM_DISC 0x4002 +#define MISDN_CTRL_HFC_CONF_JOIN 0x4003 +#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004 +#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005 +#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006 +#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007 +#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008 + + +/* socket options */ +#define MISDN_TIME_STAMP 0x0001 + +struct mISDN_ctrl_req { + int op; + int channel; + int p1; + int p2; +}; + +/* muxer options */ +#define MISDN_OPT_ALL 1 +#define MISDN_OPT_TEIMGR 2 + +#endif /* mISDNIF_H */ diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am new file mode 100644 index 000000000..483997a9d --- /dev/null +++ b/openbsc/include/openbsc/Makefile.am @@ -0,0 +1,11 @@ +noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ + gsm_subscriber.h gsm_04_11.h debug.h signal.h \ + misdn.h chan_alloc.h telnet_interface.h paging.h \ + subchan_demux.h trau_frame.h e1_input.h trau_mux.h \ + ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \ + bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \ + silent_call.h mgcp.h meas_rep.h rest_octets.h \ + system_information.h handover.h mgcp_internal.h + +openbsc_HEADERS = gsm_04_08.h meas_rep.h +openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h new file mode 100644 index 000000000..45307e3c8 --- /dev/null +++ b/openbsc/include/openbsc/abis_nm.h @@ -0,0 +1,172 @@ +/* GSM Network Management messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2009 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. + * + */ + +#ifndef _NM_H +#define _NM_H + +#include <sys/types.h> +#include <osmocore/tlv.h> +#include <osmocore/protocol/gsm_12_21.h> + +struct cell_global_id { + u_int16_t mcc; + u_int16_t mnc; + u_int16_t lac; + u_int16_t ci; +}; + +/* The BCCH info from an ip.access test, in host byte order + * and already parsed... */ +struct ipac_bcch_info { + struct llist_head list; + + u_int16_t info_type; + u_int8_t freq_qual; + u_int16_t arfcn; + u_int8_t rx_lev; + u_int8_t rx_qual; + int16_t freq_err; + u_int16_t frame_offset; + u_int32_t frame_nr_offset; + u_int8_t bsic; + struct cell_global_id cgi; + u_int8_t ba_list_si2[16]; + u_int8_t ba_list_si2bis[16]; + u_int8_t ba_list_si2ter[16]; + u_int8_t ca_list_si1[16]; +}; + +extern const struct tlv_definition nm_att_tlvdef; + +/* PUBLIC */ + +struct msgb; + +struct abis_nm_cfg { + /* callback for unidirectional reports */ + int (*report_cb)(struct msgb *, + struct abis_om_fom_hdr *); + /* callback for software activate requests from BTS */ + int (*sw_act_req)(struct msgb *); +}; + +extern int abis_nm_rcvmsg(struct msgb *msg); + +int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_t *buf, int len); +int abis_nm_rx(struct msgb *msg); +int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2); +int abis_nm_chg_adm_state(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, + u_int8_t i1, u_int8_t i2, enum abis_nm_adm_state adm_state); +int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr, + u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot, + u_int8_t tei); +int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, + u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot); +int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, + u_int8_t e1_port, u_int8_t e1_timeslot, + u_int8_t e1_subslot); +int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len); +int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len); +int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb); +int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1, + u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len); +int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg); +int abis_nm_event_reports(struct gsm_bts *bts, int on); +int abis_nm_reset_resource(struct gsm_bts *bts); +int abis_nm_software_load(struct gsm_bts *bts, const char *fname, + u_int8_t win_size, int forced, + gsm_cbfn *cbfn, void *cb_data); +int abis_nm_software_load_status(struct gsm_bts *bts); +int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, + gsm_cbfn *cbfn, void *cb_data); + +int abis_nm_conn_mdrop_link(struct gsm_bts *bts, u_int8_t e1_port0, u_int8_t ts0, + u_int8_t e1_port1, u_int8_t ts1); + +int abis_nm_perform_test(struct gsm_bts *bts, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t test_nr, u_int8_t auton_report, + u_int8_t *phys_config, u_int16_t phys_config_len); + +int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan); + +/* Siemens / BS-11 specific */ +int abis_nm_bs11_reset_resource(struct gsm_bts *bts); +int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin); +int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, + u_int8_t idx, u_int8_t attr_len, const u_int8_t *attr); +int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, u_int8_t idx); +int abis_nm_bs11_create_bport(struct gsm_bts *bts, u_int8_t idx); +int abis_nm_bs11_delete_object(struct gsm_bts *bts, + enum abis_bs11_objtype type, u_int8_t idx); +int abis_nm_bs11_delete_bport(struct gsm_bts *bts, u_int8_t idx); +int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port, + u_int8_t e1_timeslot, u_int8_t e1_subslot, u_int8_t tei); +int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts); +int abis_nm_bs11_get_serno(struct gsm_bts *bts); +int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, u_int8_t level); +int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx); +int abis_nm_bs11_logon(struct gsm_bts *bts, u_int8_t level, const char *name, int on); +int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on); +int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on); +int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password); +int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked); +int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts); +int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value); +int abis_nm_bs11_get_cclk(struct gsm_bts *bts); +int abis_nm_bs11_get_state(struct gsm_bts *bts); +int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, + u_int8_t win_size, int forced, gsm_cbfn *cbfn); +int abis_nm_bs11_set_ext_time(struct gsm_bts *bts); +int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, u_int8_t bport, enum abis_bs11_line_cfg line_cfg); +int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect); +int abis_nm_bs11_restart(struct gsm_bts *bts); + +/* ip.access nanoBTS specific commands */ +int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type, + u_int8_t obj_class, u_int8_t bts_nr, + u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t *attr, int attr_len); +int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr, + int attr_len); +int abis_nm_ipaccess_restart(struct gsm_bts *bts); +int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t *attr, u_int8_t attr_len); +int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, + u_int32_t ip, u_int16_t port, u_int8_t stream); +void abis_nm_ipaccess_cgi(u_int8_t *buf, struct gsm_bts *bts); +int ipac_parse_bcch_info(struct ipac_bcch_info *binf, u_int8_t *buf); +const char *ipacc_testres_name(u_int8_t res); + +/* Functions calling into other code parts */ +enum nm_evt { + EVT_STATECHG_OPER, + EVT_STATECHG_ADM, +}; +int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state); + +const char *nm_opstate_name(u_int8_t os); +const char *nm_avail_name(u_int8_t avail); +int nm_is_running(struct gsm_nm_state *s); +#endif /* _NM_H */ diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h new file mode 100644 index 000000000..b280184e0 --- /dev/null +++ b/openbsc/include/openbsc/abis_rsl.h @@ -0,0 +1,87 @@ +/* GSM Radio Signalling Link messages on the A-bis interface + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2008 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. + * + */ + +#ifndef _RSL_H +#define _RSL_H + +#include <osmocore/protocol/gsm_08_58.h> + +#include <osmocore/msgb.h> + +int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type, + const u_int8_t *data, int len); +int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type, + const u_int8_t *data, int len); +int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, + u_int8_t act_type, + struct rsl_ie_chan_mode *chan_mode, + struct rsl_ie_chan_ident *chan_ident, + u_int8_t bs_power, u_int8_t ms_power, + u_int8_t ta); +int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, + u_int8_t ta, u_int8_t ho_ref); +int rsl_chan_mode_modify_req(struct gsm_lchan *ts); +int rsl_encryption_cmd(struct msgb *msg); +int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len, + u_int8_t *ms_ident, u_int8_t chan_needed); +int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_needed, + struct gsm_subscriber *subscr); +int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val); + +int rsl_data_request(struct msgb *msg, u_int8_t link_id); +int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id); +int rsl_relase_request(struct gsm_lchan *lchan, u_int8_t link_id); + +/* Siemens vendor-specific RSL extensions */ +int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci); + +/* ip.access specfic RSL extensions */ +int rsl_ipacc_crcx(struct gsm_lchan *lchan); +int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, + u_int16_t port, u_int8_t rtp_payload2); +int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan); +int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan); + +int abis_rsl_rcvmsg(struct msgb *msg); + +unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans, + int n_pag_blocks); +unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res); +u_int64_t str_to_imsi(const char *imsi_str); +u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan); +int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id); + +/* to be provided by external code */ +int abis_rsl_sendmsg(struct msgb *msg); +int rsl_deact_sacch(struct gsm_lchan *lchan); +int rsl_chan_release(struct gsm_lchan *lchan); + +/* BCCH related code */ +int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); +int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf); +int rsl_number_of_paging_subchannels(struct gsm_bts *bts); + +int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db); +int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm); + +#endif /* RSL_MT_H */ + diff --git a/openbsc/include/openbsc/bsc_rll.h b/openbsc/include/openbsc/bsc_rll.h new file mode 100644 index 000000000..b2898d1b0 --- /dev/null +++ b/openbsc/include/openbsc/bsc_rll.h @@ -0,0 +1,19 @@ +#ifndef _BSC_RLL_H +#define _BSC_RLL_H + +#include <openbsc/gsm_data.h> + +enum bsc_rllr_ind { + BSC_RLLR_IND_EST_CONF, + BSC_RLLR_IND_REL_IND, + BSC_RLLR_IND_ERR_IND, + BSC_RLLR_IND_TIMEOUT, +}; + +int rll_establish(struct gsm_lchan *lchan, u_int8_t link_id, + void (*cb)(struct gsm_lchan *, u_int8_t, void *, + enum bsc_rllr_ind), + void *data); +void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type); + +#endif /* _BSC_RLL_H */ diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h new file mode 100644 index 000000000..f564e9e4d --- /dev/null +++ b/openbsc/include/openbsc/chan_alloc.h @@ -0,0 +1,66 @@ +/* Management functions to allocate/release struct gsm_lchan */ +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ +#ifndef _CHAN_ALLOC_H +#define _CHAN_ALLOC_H + +#include "gsm_subscriber.h" + +/* Special allocator for C0 of BTS */ +struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan); + +/* Regular physical channel allocator */ +struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan); + +/* Regular physical channel (TS) */ +void ts_free(struct gsm_bts_trx_ts *ts); + +/* Find an allocated channel */ +struct gsm_lchan *lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr); + +/* Find an allocated channel for a specified subscriber */ +struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr); + +/* Allocate a logical channel (SDCCH, TCH, ...) */ +struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type); + +/* Free a logical channel (SDCCH, TCH, ...) */ +void lchan_free(struct gsm_lchan *lchan); + +/* Consider releasing the channel */ +int lchan_auto_release(struct gsm_lchan *lchan); + +struct load_counter { + unsigned int total; + unsigned int used; +}; + +struct pchan_load { + struct load_counter pchan[GSM_PCHAN_UNKNOWN]; +}; + +void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts); +void network_chan_load(struct pchan_load *pl, struct gsm_network *net); + +int trx_is_usable(struct gsm_bts_trx *trx); + +#endif /* _CHAN_ALLOC_H */ diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h new file mode 100644 index 000000000..df664dbc1 --- /dev/null +++ b/openbsc/include/openbsc/db.h @@ -0,0 +1,71 @@ +/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +#ifndef _DB_H +#define _DB_H + +#include <sys/types.h> + +#include <openbsc/gsm_subscriber.h> + +/* one time initialisation */ +int db_init(const char *name); +int db_prepare(); +int db_fini(); + +/* subscriber management */ +struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, + char *imsi); +struct gsm_subscriber* db_get_subscriber(struct gsm_network *net, + enum gsm_subscriber_field field, + const char *subscr); +int db_sync_subscriber(struct gsm_subscriber* subscriber); +int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber); +int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber); +int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* token); +int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char *imei); +int db_sync_equipment(struct gsm_equipment *equip); + +/* auth info */ +int get_authinfo_by_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr); +int set_authinfo_for_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr); +int get_authtuple_by_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr); +int set_authtuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr); + +/* SMS store-and-forward */ +int db_sms_store(struct gsm_sms *sms); +struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id); +struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id); +struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr); +int db_sms_mark_sent(struct gsm_sms *sms); + +/* APDU blob storage */ +int db_apdu_blob_store(struct gsm_subscriber *subscr, + u_int8_t apdu_id_flags, u_int8_t len, + u_int8_t *apdu); + +/* Statistics counter storage */ +int db_store_counter(struct counter *ctr); + +#endif /* _DB_H */ diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h new file mode 100644 index 000000000..4b67c61b8 --- /dev/null +++ b/openbsc/include/openbsc/debug.h @@ -0,0 +1,131 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#include <stdio.h> +#include <osmocore/linuxlist.h> + +#define DEBUG + +/* Debug Areas of the code */ +enum { + DRLL, + DCC, + DMM, + DRR, + DRSL, + DNM, + DMNCC, + DSMS, + DPAG, + DMEAS, + DMI, + DMIB, + DMUX, + DINP, + DSCCP, + DMSC, + DMGCP, + DHO, + DDB, + DREF, + Debug_LastEntry, +}; + +#ifdef DEBUG +#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args) +#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args) +#else +#define DEBUGP(xss, fmt, args...) +#define DEBUGPC(ss, fmt, args...) +#endif + + +#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; + +char *hexdump(const unsigned char *buf, int len); +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6))); + +/* new logging interface */ +#define LOGP(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args) +#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args) + +/* different levels */ +#define LOGL_DEBUG 1 /* debugging information */ +#define LOGL_INFO 3 +#define LOGL_NOTICE 5 /* abnormal/unexpected condition */ +#define LOGL_ERROR 7 /* error condition, requires user action */ +#define LOGL_FATAL 8 /* fatal, program aborted */ + +/* context */ +#define BSC_CTX_LCHAN 0 +#define BSC_CTX_SUBSCR 1 +#define BSC_CTX_BTS 2 +#define BSC_CTX_SCCP 3 + +/* target */ + +enum { + DEBUG_FILTER_IMSI = 1 << 0, + DEBUG_FILTER_ALL = 1 << 1, +}; + +struct debug_category { + int enabled; + int loglevel; +}; + +struct debug_target { + int filter_map; + char *imsi_filter; + + + struct debug_category categories[Debug_LastEntry]; + int use_color; + int print_timestamp; + int loglevel; + + union { + struct { + FILE *out; + } tgt_stdout; + + struct { + int priority; + } tgt_syslog; + + struct { + void *vty; + } tgt_vty; + }; + + void (*output) (struct debug_target *target, const char *string); + + struct llist_head entry; +}; + +/* use the above macros */ +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7))); +void debug_init(void); + +/* context management */ +void debug_reset_context(void); +void debug_set_context(int ctx, void *value); + +/* filter on the targets */ +void debug_set_imsi_filter(struct debug_target *target, const char *imsi); +void debug_set_all_filter(struct debug_target *target, int); +void debug_set_use_color(struct debug_target *target, int); +void debug_set_print_timestamp(struct debug_target *target, int); +void debug_set_log_level(struct debug_target *target, int log_level); +void debug_parse_category_mask(struct debug_target *target, const char* mask); +int debug_parse_level(const char *lvl); +int debug_parse_category(const char *category); +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level); + + +/* management of the targets */ +struct debug_target *debug_target_create(void); +struct debug_target *debug_target_create_stderr(void); +void debug_add_target(struct debug_target *target); +void debug_del_target(struct debug_target *target); +#endif /* _DEBUG_H */ diff --git a/openbsc/include/openbsc/e1_input.h b/openbsc/include/openbsc/e1_input.h new file mode 100644 index 000000000..1a3d9d6ad --- /dev/null +++ b/openbsc/include/openbsc/e1_input.h @@ -0,0 +1,168 @@ +#ifndef _E1_INPUT_H +#define _E1_INPUT_H + +#include <stdlib.h> +#include <netinet/in.h> + +#include <osmocore/linuxlist.h> +#include <openbsc/gsm_data.h> +#include <osmocore/msgb.h> +#include <osmocore/select.h> +#include <openbsc/subchan_demux.h> + +#define NUM_E1_TS 32 + +enum e1inp_sign_type { + E1INP_SIGN_NONE, + E1INP_SIGN_OML, + E1INP_SIGN_RSL, +}; +const char *e1inp_signtype_name(enum e1inp_sign_type tp); + +struct e1inp_ts; + +struct e1inp_sign_link { + /* list of signalling links */ + struct llist_head list; + + /* to which timeslot do we belong? */ + struct e1inp_ts *ts; + + enum e1inp_sign_type type; + + /* trx for msg->trx of received msgs */ + struct gsm_bts_trx *trx; + + /* msgb queue of to-be-transmitted msgs */ + struct llist_head tx_list; + + /* SAPI and TEI on the E1 TS */ + u_int8_t sapi; + u_int8_t tei; + + union { + struct { + u_int8_t channel; + } misdn; + } driver; +}; + +enum e1inp_ts_type { + E1INP_TS_TYPE_NONE, + E1INP_TS_TYPE_SIGN, + E1INP_TS_TYPE_TRAU, +}; +const char *e1inp_tstype_name(enum e1inp_ts_type tp); + +/* A timeslot in the E1 interface */ +struct e1inp_ts { + enum e1inp_ts_type type; + int num; + + /* to which line do we belong ? */ + struct e1inp_line *line; + + union { + struct { + /* list of all signalling links on this TS */ + struct llist_head sign_links; + /* timer when to dequeue next frame */ + struct timer_list tx_timer; + } sign; + struct { + /* subchannel demuxer for frames from E1 */ + struct subch_demux demux; + /* subchannel muxer for frames to E1 */ + struct subch_mux mux; + } trau; + }; + union { + struct { + /* mISDN driver has one fd for each ts */ + struct bsc_fd fd; + } misdn; + struct { + /* ip.access driver has one fd for each ts */ + struct bsc_fd fd; + } ipaccess; + + } driver; +}; + +struct e1inp_driver { + struct llist_head list; + const char *name; + int (*want_write)(struct e1inp_ts *ts); +}; + +struct e1inp_line { + struct llist_head list; + unsigned int num; + const char *name; + + /* array of timestlots */ + struct e1inp_ts ts[NUM_E1_TS]; + + struct e1inp_driver *driver; + void *driver_data; +}; + +/* register a driver with the E1 core */ +int e1inp_driver_register(struct e1inp_driver *drv); + +/* register a line with the E1 core */ +int e1inp_line_register(struct e1inp_line *line); + +/* ensure a certain line exists, return pointer to it */ +struct e1inp_line *e1inp_line_get_create(u_int8_t e1_nr); + +/* find a sign_link for given TEI and SAPI in a TS */ +struct e1inp_sign_link * +e1inp_lookup_sign_link(struct e1inp_ts *ts, u_int8_t tei, + u_int8_t sapi); + +/* create a new signalling link in a E1 timeslot */ +struct e1inp_sign_link * +e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, + struct gsm_bts_trx *trx, u_int8_t tei, + u_int8_t sapi); + +/* configure and initialize one e1inp_ts */ +int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line, + enum e1inp_ts_type type); + +/* Call from the Stack: configuration of this TS has changed */ +int e1inp_update_ts(struct e1inp_ts *ts); + +/* Receive a packet from the E1 driver */ +int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, + u_int8_t tei, u_int8_t sapi); + +/* called by driver if it wants to transmit on a given TS */ +struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts, + struct e1inp_sign_link **sign_link); + +/* called by driver in case some kind of link state event */ +int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi); + +/* Write LAPD frames to the fd. */ +void e1_set_pcap_fd(int fd); + +/* called by TRAU muxer to obtain the destination mux entity */ +struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr); + +void e1inp_sign_link_destroy(struct e1inp_sign_link *link); +int e1inp_line_update(struct e1inp_line *line); + +/* e1_config.c */ +int e1_reconfig_ts(struct gsm_bts_trx_ts *ts); +int e1_reconfig_trx(struct gsm_bts_trx *trx); +int e1_reconfig_bts(struct gsm_bts *bts); + +int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin); +int ipaccess_setup(struct gsm_network *gsmnet); + +extern struct llist_head e1inp_driver_list; +extern struct llist_head e1inp_line_list; + +#endif /* _E1_INPUT_H */ diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h new file mode 100644 index 000000000..c4018cd11 --- /dev/null +++ b/openbsc/include/openbsc/gsm_04_08.h @@ -0,0 +1,56 @@ +#ifndef _GSM_04_08_H +#define _GSM_04_08_H + +#include <openbsc/meas_rep.h> + +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/gsm48.h> + +struct msgb; +struct gsm_bts; +struct gsm_subscriber; +struct gsm_network; +struct gsm_trans; + +/* config options controlling the behaviour of the lower leves */ +void gsm0408_allow_everyone(int allow); + +int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id); +enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci); +enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci); + +int gsm48_tx_mm_info(struct gsm_lchan *lchan); +int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq); +int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan); +struct msgb *gsm48_msgb_alloc(void); +int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans); +int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len); + +int gsm48_send_rr_release(struct gsm_lchan *lchan); +int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); +int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id, + u_int8_t apdu_len, const u_int8_t *apdu); +int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_class); +int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, + u_int8_t power_command, u_int8_t ho_ref); + +int bsc_upqueue(struct gsm_network *net); + +int mncc_send(struct gsm_network *net, int msg_type, void *arg); + +/* convert a ASCII phone number to call-control BCD */ +int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, + int h_len, const char *input); +int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv, + int h_len); + +int send_siemens_mrpci(struct gsm_lchan *lchan, u_int8_t *classmark2_lv); +int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type); +int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr); + +int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode); +int gsm48_rx_rr_modif_ack(struct msgb *msg); +int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); + + +#endif diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h new file mode 100644 index 000000000..9badd3659 --- /dev/null +++ b/openbsc/include/openbsc/gsm_04_11.h @@ -0,0 +1,36 @@ +#ifndef _GSM_04_11_H +#define _GSM_04_11_H + +#include <osmocore/protocol/gsm_04_11.h> + +/* SMS deliver PDU */ +struct sms_deliver { + u_int8_t mti:2; /* message type indicator */ + u_int8_t mms:1; /* more messages to send */ + u_int8_t rp:1; /* reply path */ + u_int8_t udhi:1; /* user data header indicator */ + u_int8_t sri:1; /* status report indication */ + u_int8_t *orig_addr; /* originating address */ + u_int8_t pid; /* protocol identifier */ + u_int8_t dcs; /* data coding scheme */ + /* service centre time stamp */ + u_int8_t ud_len; /* user data length */ + u_int8_t *user_data; /* user data */ + + u_int8_t msg_ref; /* message reference */ + u_int8_t *smsc; +}; + +struct msgb; + +int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id); + +int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms); + +struct gsm_sms *sms_alloc(void); +void sms_free(struct gsm_sms *sms); + +void _gsm411_sms_trans_free(struct gsm_trans *trans); +int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, + struct gsm_sms *sms); +#endif diff --git a/openbsc/include/openbsc/gsm_04_80.h b/openbsc/include/openbsc/gsm_04_80.h new file mode 100644 index 000000000..b5ab1c6ea --- /dev/null +++ b/openbsc/include/openbsc/gsm_04_80.h @@ -0,0 +1,22 @@ +#ifndef _GSM_04_80_H +#define _GSM_04_80_H + +#include <osmocore/msgb.h> +#include <osmocore/protocol/gsm_04_80.h> + +#define MAX_LEN_USSD_STRING 31 + +struct ussd_request { + char text[MAX_LEN_USSD_STRING + 1]; + u_int8_t transaction_id; + u_int8_t invoke_id; +}; + +int gsm0480_decode_ussd_request(const struct msgb *msg, + struct ussd_request *request); +int gsm0480_send_ussd_response(const struct msgb *in_msg, const char* response_text, + const struct ussd_request *req); +int gsm0480_send_ussd_reject(const struct msgb *msg, + const struct ussd_request *request); + +#endif diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h new file mode 100644 index 000000000..88e7f16c3 --- /dev/null +++ b/openbsc/include/openbsc/gsm_data.h @@ -0,0 +1,706 @@ +#ifndef _GSM_DATA_H +#define _GSM_DATA_H + +#include <sys/types.h> + +enum gsm_phys_chan_config { + GSM_PCHAN_NONE, + GSM_PCHAN_CCCH, + GSM_PCHAN_CCCH_SDCCH4, + GSM_PCHAN_TCH_F, + GSM_PCHAN_TCH_H, + GSM_PCHAN_SDCCH8_SACCH8C, + GSM_PCHAN_PDCH, /* GPRS PDCH */ + GSM_PCHAN_TCH_F_PDCH, /* TCH/F if used, PDCH otherwise */ + GSM_PCHAN_UNKNOWN, +}; + +enum gsm_chan_t { + GSM_LCHAN_NONE, + GSM_LCHAN_SDCCH, + GSM_LCHAN_TCH_F, + GSM_LCHAN_TCH_H, + GSM_LCHAN_UNKNOWN, +}; + +/* RRLP mode of operation */ +enum rrlp_mode { + RRLP_MODE_NONE, + RRLP_MODE_MS_BASED, + RRLP_MODE_MS_PREF, + RRLP_MODE_ASS_PREF, +}; + +/* Channel Request reason */ +enum gsm_chreq_reason_t { + GSM_CHREQ_REASON_EMERG, + GSM_CHREQ_REASON_PAG, + GSM_CHREQ_REASON_CALL, + GSM_CHREQ_REASON_LOCATION_UPD, + GSM_CHREQ_REASON_OTHER, +}; + +#include <osmocore/timer.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/mncc.h> +#include <osmocore/tlv.h> +#include <osmocore/bitvec.h> +#include <osmocore/statistics.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/utils.h> + +#define TRX_NR_TS 8 +#define TS_MAX_LCHAN 8 + +#define HARDCODED_ARFCN 123 +#define HARDCODED_TSC 7 +#define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */ + +/* for multi-drop config */ +#define HARDCODED_BTS0_TS 1 +#define HARDCODED_BTS1_TS 6 +#define HARDCODED_BTS2_TS 11 + +enum gsm_hooks { + GSM_HOOK_NM_SWLOAD, + GSM_HOOK_RR_PAGING, +}; + +enum gsm_paging_event { + GSM_PAGING_SUCCEEDED, + GSM_PAGING_EXPIRED, + GSM_PAGING_OOM, +}; + +struct msgb; +typedef int gsm_cbfn(unsigned int hooknum, + unsigned int event, + struct msgb *msg, + void *data, void *param); + +/* + * Use the channel. As side effect the lchannel recycle timer + * will be started. + */ +#define LCHAN_RELEASE_TIMEOUT 20, 0 +#define use_lchan(lchan) \ + do { lchan->use_count++; \ + DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \ + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \ + lchan->nr, lchan->use_count); \ + bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0); + +#define put_lchan(lchan) \ + do { lchan->use_count--; \ + DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \ + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \ + lchan->nr, lchan->use_count); \ + } while(0); + + +/* communications link with a BTS */ +struct gsm_bts_link { + struct gsm_bts *bts; +}; + +/* Real authentication information containing Ki */ +enum gsm_auth_algo { + AUTH_ALGO_NONE, + AUTH_ALGO_XOR, + AUTH_ALGO_COMP128v1, +}; + +struct gsm_auth_info { + enum gsm_auth_algo auth_algo; + unsigned int a3a8_ki_len; + u_int8_t a3a8_ki[16]; +}; + +struct gsm_auth_tuple { + int use_count; + int key_seq; + u_int8_t rand[16]; + u_int8_t sres[4]; + u_int8_t kc[8]; +}; + + +struct gsm_lchan; +struct gsm_subscriber; +struct gsm_mncc; +struct rtp_socket; + +/* Network Management State */ +struct gsm_nm_state { + u_int8_t operational; + u_int8_t administrative; + u_int8_t availability; +}; + +/* + * LOCATION UPDATING REQUEST state + * + * Our current operation is: + * - Get imei/tmsi + * - Accept/Reject according to global policy + */ +struct gsm_loc_updating_operation { + struct timer_list updating_timer; + unsigned int waiting_for_imsi : 1; + unsigned int waiting_for_imei : 1; +}; + +/* Maximum number of neighbor cells whose average we track */ +#define MAX_NEIGH_MEAS 10 +/* Maximum size of the averaging window for neighbor cells */ +#define MAX_WIN_NEIGH_AVG 10 + +/* processed neighbor measurements for one cell */ +struct neigh_meas_proc { + u_int16_t arfcn; + u_int8_t bsic; + u_int8_t rxlev[MAX_WIN_NEIGH_AVG]; + unsigned int rxlev_cnt; + u_int8_t last_seen_nr; +}; + +#define MAX_A5_KEY_LEN (128/8) +#define RSL_ENC_ALG_A5(x) (x+1) + +/* is the data link established? who established it? */ +#define LCHAN_SAPI_UNUSED 0 +#define LCHAN_SAPI_MS 1 +#define LCHAN_SAPI_NET 2 + +/* state of a logical channel */ +enum gsm_lchan_state { + LCHAN_S_NONE, /* channel is not active */ + LCHAN_S_ACT_REQ, /* channel activatin requested */ + LCHAN_S_ACTIVE, /* channel is active and operational */ + LCHAN_S_REL_REQ, /* channel release has been requested */ + LCHAN_S_INACTIVE, /* channel is set inactive */ +}; + +struct gsm_lchan { + /* The TS that we're part of */ + struct gsm_bts_trx_ts *ts; + /* The logical subslot number in the TS */ + u_int8_t nr; + /* The logical channel type */ + enum gsm_chan_t type; + /* RSL channel mode */ + enum rsl_cmod_spd rsl_cmode; + /* If TCH, traffic channel mode */ + enum gsm48_chan_mode tch_mode; + /* State */ + enum gsm_lchan_state state; + /* Power levels for MS and BTS */ + u_int8_t bs_power; + u_int8_t ms_power; + /* Encryption information */ + struct { + u_int8_t alg_id; + u_int8_t key_len; + u_int8_t key[MAX_A5_KEY_LEN]; + } encr; + /* Are we part of a special "silent" call */ + int silent_call; + + /* AMR bits */ + struct gsm48_multi_rate_conf mr_conf; + + /* To whom we are allocated at the moment */ + struct gsm_subscriber *subscr; + + /* Timer started to release the channel */ + struct timer_list release_timer; + + struct timer_list T3101; + + /* Established data link layer services */ + u_int8_t sapis[8]; + + /* + * Operations that have a state and might be pending + */ + struct gsm_loc_updating_operation *loc_operation; + + /* use count. how many users use this channel */ + unsigned int use_count; + + /* cache of last measurement reports on this lchan */ + struct gsm_meas_rep meas_rep[6]; + int meas_rep_idx; + + /* table of neighbor cell measurements */ + struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS]; + + struct { + u_int32_t bound_ip; + u_int32_t connect_ip; + u_int16_t bound_port; + u_int16_t connect_port; + u_int16_t conn_id; + u_int8_t rtp_payload2; + u_int8_t speech_mode; + struct rtp_socket *rtp_socket; + } abis_ip; +}; + +struct gsm_e1_subslot { + /* Number of E1 link */ + u_int8_t e1_nr; + /* Number of E1 TS inside E1 link */ + u_int8_t e1_ts; + /* Sub-slot within the E1 TS, 0xff if full TS */ + u_int8_t e1_ts_ss; +}; + +#define BTS_TRX_F_ACTIVATED 0x0001 +/* One Timeslot in a TRX */ +struct gsm_bts_trx_ts { + struct gsm_bts_trx *trx; + /* number of this timeslot at the TRX */ + u_int8_t nr; + + enum gsm_phys_chan_config pchan; + + unsigned int flags; + struct gsm_nm_state nm_state; + struct tlv_parsed nm_attr; + u_int8_t nm_chan_comb; + + /* To which E1 subslot are we connected */ + struct gsm_e1_subslot e1_link; + + struct gsm_lchan lchan[TS_MAX_LCHAN]; +}; + +/* One TRX in a BTS */ +struct gsm_bts_trx { + /* list header in bts->trx_list */ + struct llist_head list; + + struct gsm_bts *bts; + /* number of this TRX in the BTS */ + u_int8_t nr; + /* how do we talk RSL with this TRX? */ + struct gsm_e1_subslot rsl_e1_link; + u_int8_t rsl_tei; + struct e1inp_sign_link *rsl_link; + + struct gsm_nm_state nm_state; + struct tlv_parsed nm_attr; + struct { + struct gsm_nm_state nm_state; + } bb_transc; + + u_int16_t arfcn; + int nominal_power; /* in dBm */ + unsigned int max_power_red; /* in actual dB */ + + union { + struct { + struct { + struct gsm_nm_state nm_state; + } bbsig; + struct { + struct gsm_nm_state nm_state; + } pa; + } bs11; + }; + struct gsm_bts_trx_ts ts[TRX_NR_TS]; +}; + +enum gsm_bts_type { + GSM_BTS_TYPE_UNKNOWN, + GSM_BTS_TYPE_BS11, + GSM_BTS_TYPE_NANOBTS, +}; + +struct gsm_bts_model { + struct llist_head list; + + enum gsm_bts_type type; + const char *name; + + struct tlv_definition nm_att_tlvdef; +}; + +/** + * A pending paging request + */ +struct gsm_paging_request { + /* list_head for list of all paging requests */ + struct llist_head entry; + /* the subscriber which we're paging. Later gsm_paging_request + * should probably become a part of the gsm_subscriber struct? */ + struct gsm_subscriber *subscr; + /* back-pointer to the BTS on which we are paging */ + struct gsm_bts *bts; + /* what kind of channel type do we ask the MS to establish */ + int chan_type; + + /* Timer 3113: how long do we try to page? */ + struct timer_list T3113; + + /* callback to be called in case paging completes */ + gsm_cbfn *cbfn; + void *cbfn_param; +}; + +/* + * This keeps track of the paging status of one BTS. It + * includes a number of pending requests, a back pointer + * to the gsm_bts, a timer and some more state. + */ +struct gsm_bts_paging_state { + /* pending requests */ + struct llist_head pending_requests; + struct gsm_paging_request *last_request; + struct gsm_bts *bts; + + struct timer_list work_timer; + + /* load */ + u_int16_t available_slots; +}; + +struct gsm_envabtse { + struct gsm_nm_state nm_state; +}; + +struct gsm_bts_gprs_nsvc { + struct gsm_bts *bts; + int id; + u_int16_t nsvci; + u_int16_t local_port; + u_int16_t remote_port; + u_int32_t remote_ip; + struct gsm_nm_state nm_state; +}; + +/* One BTS */ +struct gsm_bts { + /* list header in net->bts_list */ + struct llist_head list; + + struct gsm_network *network; + /* number of ths BTS in network */ + u_int8_t nr; + /* Cell Identity */ + u_int16_t cell_identity; + /* location area code of this BTS */ + u_int16_t location_area_code; + /* Training Sequence Code */ + u_int8_t tsc; + /* Base Station Identification Code (BSIC) */ + u_int8_t bsic; + /* type of BTS */ + enum gsm_bts_type type; + struct gsm_bts_model *model; + enum gsm_band band; + /* should the channel allocator allocate channels from high TRX to TRX0, + * rather than starting from TRX0 and go upwards? */ + int chan_alloc_reverse; + /* maximum Tx power that the MS is permitted to use in this cell */ + int ms_max_power; + + /* how do we talk OML with this TRX? */ + struct gsm_e1_subslot oml_e1_link; + u_int8_t oml_tei; + struct e1inp_sign_link *oml_link; + + /* Abis network management O&M handle */ + struct abis_nm_h *nmh; + struct gsm_nm_state nm_state; + struct tlv_parsed nm_attr; + + /* number of this BTS on given E1 link */ + u_int8_t bts_nr; + + /* paging state and control */ + struct gsm_bts_paging_state paging; + + /* CCCH is on C0 */ + struct gsm_bts_trx *c0; + + struct { + struct gsm_nm_state nm_state; + } site_mgr; + + /* parameters from which we build SYSTEM INFORMATION */ + struct { + struct gsm48_rach_control rach_control; + u_int8_t ncc_permitted; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_cell_options cell_options; + struct gsm48_control_channel_descr chan_desc; + struct bitvec neigh_list; + struct bitvec cell_alloc; + struct { + /* bitmask large enough for all possible ARFCN's */ + u_int8_t neigh_list[1024/8]; + u_int8_t cell_alloc[1024/8]; + } data; + } si_common; + + /* ip.accesss Unit ID's have Site/BTS/TRX layout */ + union { + struct { + u_int16_t site_id; + u_int16_t bts_id; + u_int32_t flags; + } ip_access; + struct { + struct { + struct gsm_nm_state nm_state; + } cclk; + struct { + struct gsm_nm_state nm_state; + } rack; + struct gsm_envabtse envabtse[4]; + } bs11; + }; + + /* Not entirely sure how ip.access specific this is */ + struct { + int enabled; + struct { + struct gsm_nm_state nm_state; + u_int16_t nsei; + } nse; + struct { + struct gsm_nm_state nm_state; + u_int16_t bvci; + } cell; + struct gsm_bts_gprs_nsvc nsvc[2]; + u_int8_t rac; + } gprs; + + /* transceivers */ + int num_trx; + struct llist_head trx_list; +}; + +/* Some statistics of our network */ +struct gsmnet_stats { + struct { + struct counter *total; + struct counter *no_channel; + } chreq; + struct { + struct counter *attempted; + struct counter *no_channel; /* no channel available */ + struct counter *timeout; /* T3103 timeout */ + struct counter *completed; /* HO COMPL received */ + struct counter *failed; /* HO FAIL received */ + } handover; + struct { + struct counter *attach; + struct counter *normal; + struct counter *periodic; + struct counter *detach; + } loc_upd_type; + struct { + struct counter *reject; + struct counter *accept; + } loc_upd_resp; + struct { + struct counter *attempted; + struct counter *detached; + struct counter *completed; + struct counter *expired; + } paging; + struct { + struct counter *submitted; /* MO SMS submissions */ + struct counter *no_receiver; + struct counter *delivered; /* MT SMS deliveries */ + struct counter *rp_err_mem; + struct counter *rp_err_other; + } sms; + struct { + struct counter *dialled; /* total number of dialled calls */ + struct counter *alerted; /* we alerted the other end */ + struct counter *connected;/* how many calls were accepted */ + } call; +}; + +enum gsm_auth_policy { + GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */ + GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */ + GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */ +}; + +#define GSM_T3101_DEFAULT 10 +#define GSM_T3113_DEFAULT 60 + +struct gsm_network { + /* global parameters */ + u_int16_t country_code; + u_int16_t network_code; + char *name_long; + char *name_short; + enum gsm_auth_policy auth_policy; + enum gsm48_reject_value reject_cause; + int a5_encryption; + int neci; + int send_mm_info; + struct { + int active; + /* Window RXLEV averaging */ + unsigned int win_rxlev_avg; /* number of SACCH frames */ + /* Window RXQUAL averaging */ + unsigned int win_rxqual_avg; /* number of SACCH frames */ + /* Window RXLEV neighbouring cells averaging */ + unsigned int win_rxlev_avg_neigh; /* number of SACCH frames */ + + /* how often should we check for power budget HO */ + unsigned int pwr_interval; /* SACCH frames */ + /* how much better does a neighbor cell have to be ? */ + unsigned int pwr_hysteresis; /* dBm */ + /* maximum distacne before we try a handover */ + unsigned int max_distance; /* TA values */ + } handover; + + struct gsmnet_stats stats; + + /* layer 4 */ + int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg); + struct llist_head upqueue; + struct llist_head trans_list; + + unsigned int num_bts; + struct llist_head bts_list; + + /* timer values */ + int T3101; + int T3103; + int T3105; + int T3107; + int T3109; + int T3111; + int T3113; + int T3115; + int T3117; + int T3119; + int T3141; + + /* Radio Resource Location Protocol (TS 04.31) */ + struct { + enum rrlp_mode mode; + } rrlp; +}; + +#define SMS_HDR_SIZE 128 +#define SMS_TEXT_SIZE 256 +struct gsm_sms { + unsigned long long id; + struct gsm_subscriber *sender; + struct gsm_subscriber *receiver; + + unsigned long validity_minutes; + u_int8_t reply_path_req; + u_int8_t status_rep_req; + u_int8_t ud_hdr_ind; + u_int8_t protocol_id; + u_int8_t data_coding_scheme; + u_int8_t msg_ref; + char dest_addr[20+1]; /* DA LV is 12 bytes max, i.e. 10 bytes + * BCD == 20 bytes string */ + u_int8_t user_data_len; + u_int8_t user_data[SMS_TEXT_SIZE]; + + char text[SMS_TEXT_SIZE]; +}; + + +struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_code, + int (*mncc_recv)(struct gsm_network *, int, void *)); +struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, + u_int8_t tsc, u_int8_t bsic); +struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts); +int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type); + +struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num); + +/* Get reference to a neighbor cell on a given BCCH ARFCN */ +struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, + u_int16_t arfcn, u_int8_t bsic); + +struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num); + +const char *gsm_pchan_name(enum gsm_phys_chan_config c); +enum gsm_phys_chan_config gsm_pchan_parse(const char *name); +const char *gsm_lchant_name(enum gsm_chan_t c); +const char *gsm_chreq_name(enum gsm_chreq_reason_t c); +char *gsm_trx_name(struct gsm_bts_trx *trx); +char *gsm_ts_name(struct gsm_bts_trx_ts *ts); +char *gsm_lchan_name(struct gsm_lchan *lchan); +const char *gsm_lchans_name(enum gsm_lchan_state s); + +enum gsm_e1_event { + EVT_E1_NONE, + EVT_E1_TEI_UP, + EVT_E1_TEI_DN, +}; + +void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr, + u_int8_t e1_ts, u_int8_t e1_ts_ss); +enum gsm_bts_type parse_btstype(const char *arg); +const char *btstype2str(enum gsm_bts_type type); +struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr); +struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, + struct gsm_bts *start_bts); + +extern void *tall_bsc_ctx; +extern int ipacc_rtp_direct; + +static inline int is_ipaccess_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + return 1; + default: + break; + } + return 0; +} + +static inline int is_siemens_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + return 1; + default: + break; + } + + return 0; +} + + +enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); +const char *gsm_auth_policy_name(enum gsm_auth_policy policy); + +enum rrlp_mode rrlp_mode_parse(const char *arg); +const char *rrlp_mode_name(enum rrlp_mode mode); + +void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); + +/* A parsed GPRS routing area */ +struct gprs_ra_id { + u_int16_t mnc; + u_int16_t mcc; + u_int16_t lac; + u_int8_t rac; +}; + +int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts); +void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts); +struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan); + +int gsm_bts_model_register(struct gsm_bts_model *model); + +#endif diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h new file mode 100644 index 000000000..06539960e --- /dev/null +++ b/openbsc/include/openbsc/gsm_subscriber.h @@ -0,0 +1,95 @@ +#ifndef _GSM_SUBSCR_H +#define _GSM_SUBSCR_H + +#include <sys/types.h> +#include "gsm_data.h" +#include <osmocore/linuxlist.h> + +#define GSM_IMEI_LENGTH 17 +#define GSM_IMSI_LENGTH 17 +#define GSM_NAME_LENGTH 128 + +#define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */ +#define GSM_MIN_EXTEN 20000 +#define GSM_MAX_EXTEN 49999 + +/* reserved according to GSM 03.03 § 2.4 */ +#define GSM_RESERVED_TMSI 0xFFFFFFFF + + +#define GSM_SUBSCRIBER_FIRST_CONTACT 0x00000001 +#define tmsi_from_string(str) strtoul(str, NULL, 10) + +struct gsm_equipment { + long long unsigned int id; + char imei[GSM_IMEI_LENGTH]; + char name[GSM_NAME_LENGTH]; + + struct gsm48_classmark1 classmark1; + u_int8_t classmark2_len; + u_int8_t classmark2[3]; + u_int8_t classmark3_len; + u_int8_t classmark3[14]; +}; + +struct gsm_subscriber { + struct gsm_network *net; + long long unsigned int id; + char imsi[GSM_IMSI_LENGTH]; + u_int32_t tmsi; + u_int16_t lac; + char name[GSM_NAME_LENGTH]; + char extension[GSM_EXTENSION_LENGTH]; + int authorized; + + /* Temporary field which is not stored in the DB/HLR */ + u_int32_t flags; + + /* Every user can only have one equipment in use at any given + * point in time */ + struct gsm_equipment equipment; + + /* for internal management */ + int use_count; + struct llist_head entry; + + /* pending requests */ + int in_callback; + struct llist_head requests; +}; + +enum gsm_subscriber_field { + GSM_SUBSCRIBER_IMSI, + GSM_SUBSCRIBER_TMSI, + GSM_SUBSCRIBER_EXTENSION, + GSM_SUBSCRIBER_ID, +}; + +enum gsm_subscriber_update_reason { + GSM_SUBSCRIBER_UPDATE_ATTACHED, + GSM_SUBSCRIBER_UPDATE_DETACHED, + GSM_SUBSCRIBER_UPDATE_EQUIPMENT, +}; + +struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr); +struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr); +struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, + u_int32_t tmsi); +struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, + const char *imsi); +struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, + const char *ext); +struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, + unsigned long long id); +int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason); +void subscr_put_channel(struct gsm_lchan *lchan); +void subscr_get_channel(struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *param); + +char *subscr_name(struct gsm_subscriber *subscr); + +/* internal */ +struct gsm_subscriber *subscr_alloc(void); +extern struct llist_head active_subscribers; + +#endif /* _GSM_SUBSCR_H */ diff --git a/openbsc/include/openbsc/handover.h b/openbsc/include/openbsc/handover.h new file mode 100644 index 000000000..8ab1b0642 --- /dev/null +++ b/openbsc/include/openbsc/handover.h @@ -0,0 +1,8 @@ +#ifndef _HANDOVER_H +#define _HANDOVER_H +/* Hand over the specified logical channel to the specified new BTS. + * This is the main entry point for the actual handover algorithm, + * after it has decided it wants to initiate HO to a specific BTS */ +int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts); + +#endif /* _HANDOVER_H */ diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h new file mode 100644 index 000000000..86248aae5 --- /dev/null +++ b/openbsc/include/openbsc/ipaccess.h @@ -0,0 +1,107 @@ +#ifndef _IPACCESS_H +#define _IPACCESS_H + +#include "e1_input.h" +#include <osmocore/linuxlist.h> + +#define IPA_TCP_PORT_OML 3002 +#define IPA_TCP_PORT_RSL 3003 + +struct ipaccess_head { + u_int16_t len; /* network byte order */ + u_int8_t proto; + u_int8_t data[0]; +} __attribute__ ((packed)); + +enum ipaccess_proto { + IPAC_PROTO_RSL = 0x00, + IPAC_PROTO_IPACCESS = 0xfe, + IPAC_PROTO_SCCP = 0xfd, + IPAC_PROTO_OML = 0xff, +}; + +enum ipaccess_msgtype { + IPAC_MSGT_PING = 0x00, + IPAC_MSGT_PONG = 0x01, + IPAC_MSGT_ID_GET = 0x04, + IPAC_MSGT_ID_RESP = 0x05, + IPAC_MSGT_ID_ACK = 0x06, +}; + +enum ipaccess_id_tags { + IPAC_IDTAG_SERNR = 0x00, + IPAC_IDTAG_UNITNAME = 0x01, + IPAC_IDTAG_LOCATION1 = 0x02, + IPAC_IDTAG_LOCATION2 = 0x03, + IPAC_IDTAG_EQUIPVERS = 0x04, + IPAC_IDTAG_SWVERSION = 0x05, + IPAC_IDTAG_IPADDR = 0x06, + IPAC_IDTAG_MACADDR = 0x07, + IPAC_IDTAG_UNIT = 0x08, +}; + +int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa); + +/* + * methods for parsing and sending a message + */ +int ipaccess_rcvmsg_base(struct msgb *msg, struct bsc_fd *bfd); +struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error); +void ipaccess_prepend_header(struct msgb *msg, int proto); +int ipaccess_send_id_ack(int fd); +int ipaccess_send_id_req(int fd); + +int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len); + + +/* + * Firmware specific header + */ +struct sdp_firmware { + char magic[4]; + char more_magic[2]; + u_int16_t more_more_magic; + u_int32_t header_length; + u_int32_t file_length; + char sw_part[20]; + char text1[64]; + char time[12]; + char date[14]; + char text2[10]; + char version[20]; + u_int16_t table_offset; + /* stuff i don't know */ +} __attribute__((packed)); + +struct sdp_header_entry { + u_int16_t something1; + char text1[64]; + char time[12]; + char date[14]; + char text2[10]; + char version[20]; + u_int32_t length; + u_int32_t addr1; + u_int32_t addr2; + u_int32_t start; +} __attribute__((packed)); + +struct sdp_header_item { + struct sdp_header_entry header_entry; + struct llist_head entry; + off_t absolute_offset; +}; + +struct sdp_header { + struct sdp_firmware firmware_info; + + /* for more_magic a list of sdp_header_entry_list */ + struct llist_head header_list; + + /* the entry of the sdp_header */ + struct llist_head entry; +}; + +int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned base_offset, struct llist_head *list); + +#endif /* _IPACCESS_H */ diff --git a/openbsc/include/openbsc/meas_rep.h b/openbsc/include/openbsc/meas_rep.h new file mode 100644 index 000000000..3c2c8d1c1 --- /dev/null +++ b/openbsc/include/openbsc/meas_rep.h @@ -0,0 +1,85 @@ +#ifndef _MEAS_REP_H +#define _MEAS_REP_H + +#define MRC_F_PROCESSED 0x0001 + +/* extracted from a L3 measurement report IE */ +struct gsm_meas_rep_cell { + u_int8_t rxlev; + u_int8_t bsic; + u_int8_t neigh_idx; + u_int16_t arfcn; + unsigned int flags; +}; + +/* RX Level and RX Quality */ +struct gsm_rx_lev_qual { + u_int8_t rx_lev; + u_int8_t rx_qual; +}; + +/* unidirectional measumrement report */ +struct gsm_meas_rep_unidir { + struct gsm_rx_lev_qual full; + struct gsm_rx_lev_qual sub; +}; + +#define MEAS_REP_F_UL_DTX 0x01 +#define MEAS_REP_F_DL_VALID 0x02 +#define MEAS_REP_F_BA1 0x04 +#define MEAS_REP_F_DL_DTX 0x08 +#define MEAS_REP_F_MS_TO 0x10 +#define MEAS_REP_F_MS_L1 0x20 +#define MEAS_REP_F_FPC 0x40 + +/* parsed uplink and downlink measurement result */ +struct gsm_meas_rep { + /* back-pointer to the logical channel */ + struct gsm_lchan *lchan; + + /* number of the measurement report */ + u_int8_t nr; + /* flags, see MEAS_REP_F_* */ + unsigned int flags; + + /* uplink and downlink rxlev, rxqual; full and sub */ + struct gsm_meas_rep_unidir ul; + struct gsm_meas_rep_unidir dl; + + u_int8_t bs_power; + u_int8_t ms_timing_offset; + struct { + int8_t pwr; /* MS power in dBm */ + u_int8_t ta; /* MS timing advance */ + } ms_l1; + + /* neighbor measurement reports for up to 6 cells */ + int num_cell; + struct gsm_meas_rep_cell cell[6]; +}; + +enum meas_rep_field { + MEAS_REP_DL_RXLEV_FULL, + MEAS_REP_DL_RXLEV_SUB, + MEAS_REP_DL_RXQUAL_FULL, + MEAS_REP_DL_RXQUAL_SUB, + MEAS_REP_UL_RXLEV_FULL, + MEAS_REP_UL_RXLEV_SUB, + MEAS_REP_UL_RXQUAL_FULL, + MEAS_REP_UL_RXQUAL_SUB, +}; + +/* obtain an average over the last 'num' fields in the meas reps */ +int get_meas_rep_avg(const struct gsm_lchan *lchan, + enum meas_rep_field field, unsigned int num); + +/* Check if N out of M last values for FIELD are >= bd */ +int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, + enum meas_rep_field field, + unsigned int n, unsigned int m, int be); + +unsigned int calc_initial_idx(unsigned int array_size, + unsigned int meas_rep_idx, + unsigned int num_values); + +#endif /* _MEAS_REP_H */ diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h new file mode 100644 index 000000000..f7e800bd8 --- /dev/null +++ b/openbsc/include/openbsc/mgcp.h @@ -0,0 +1,122 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ + +/* + * (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. + * + */ + +#ifndef OPENBSC_MGCP_H +#define OPENBSC_MGCP_H + +#include <osmocore/msgb.h> + +#include <arpa/inet.h> + +#define RTP_PORT_DEFAULT 4000 + +/** + * Calculate the RTP audio port for the given multiplex + * and the direction. This allows a semi static endpoint + * to port calculation removing the need for the BSC + * and the MediaGateway to communicate. + * + * Port usage explained: + * base + (multiplex * 2) + 0 == local port to wait for network packets + * base + (multiplex * 2) + 1 == local port for rtcp + * + * The above port will receive packets from the BTS that need + * to be patched and forwarded to the network. + * The above port will receive packets from the network that + * need to be patched and forwarded to the BTS. + * + * We assume to have a static BTS IP address so we can differentiate + * network and BTS. + * + */ +static inline int rtp_calculate_port(int multiplex, int base) +{ + return base + (multiplex * 2); +} + + +/* + * Handling of MGCP Endpoints and the MGCP Config + */ +struct mgcp_endpoint; +struct mgcp_config; + +#define MGCP_ENDP_CRCX 1 +#define MGCP_ENDP_DLCX 2 +#define MGCP_ENDP_MDCX 3 + +/* + * what to do with the msg? + * - continue as usual? + * - reject and send a failure code? + * - defer? do not send anything + */ +#define MGCP_POLICY_CONT 4 +#define MGCP_POLICY_REJECT 5 +#define MGCP_POLICY_DEFER 6 + +typedef int (*mgcp_change)(struct mgcp_config *cfg, int endpoint, int state, int local_rtp); +typedef int (*mgcp_policy)(struct mgcp_config *cfg, int endpoint, int state, const char *transactio_id); + +struct mgcp_config { + int source_port; + char *local_ip; + char *source_addr; + unsigned int number_endpoints; + char *bts_ip; + + struct in_addr bts_in; + char *audio_name; + int audio_payload; + int audio_loop; + int early_bind; + int rtp_base_port; + + char *forward_ip; + int forward_port; + + mgcp_change change_cb; + mgcp_policy policy_cb; + void *data; + + struct mgcp_endpoint *endpoints; + unsigned int last_call_id; +}; + +/* config management */ +struct mgcp_config *mgcp_config_alloc(void); +int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg); +int mgcp_vty_init(void); +int mgcp_endpoints_allocate(struct mgcp_config *cfg); +int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port); +void mgcp_free_endp(struct mgcp_endpoint *endp); + +/* + * format helper functions + */ +struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); +struct msgb *mgcp_create_rsip(void); +struct msgb *mgcp_create_response_with_data(int code, const char *msg, const char *trans, const char *data); + + +#endif diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h new file mode 100644 index 000000000..10d0ca6ae --- /dev/null +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -0,0 +1,64 @@ +/* MGCP Private Data */ + +/* + * (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. + * + */ + +#ifndef OPENBSC_MGCP_DATA_H +#define OPENBSC_MGCP_DATA_H + +#include <osmocore/select.h> + +#define CI_UNUSED 0 + +struct mgcp_endpoint { + int ci; + char *callid; + char *local_options; + int conn_mode; + + int bts_payload_type; + int net_payload_type; + + /* the local rtp port we are binding to */ + int rtp_port; + + /* + * RTP mangling: + * - we get RTP and RTCP to us and need to forward to the BTS + * - we get RTP and RTCP from the BTS and forward to the network + */ + struct bsc_fd local_rtp; + struct bsc_fd local_rtcp; + + struct in_addr remote; + struct in_addr bts; + + /* in network byte order */ + int net_rtp, net_rtcp; + int bts_rtp, bts_rtcp; + + /* backpointer */ + struct mgcp_config *cfg; +}; + +#define ENDPOINT_NUMBER(endp) abs(endp - endp->cfg->endpoints) + +#endif diff --git a/openbsc/include/openbsc/misdn.h b/openbsc/include/openbsc/misdn.h new file mode 100644 index 000000000..6c38de752 --- /dev/null +++ b/openbsc/include/openbsc/misdn.h @@ -0,0 +1,29 @@ +/* (C) 2008 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. + * + */ + +#ifndef MISDN_H +#define MISDN_H + +#include "e1_input.h" + +int mi_setup(int cardnr, struct e1inp_line *line, int release_l2); +int _abis_nm_sendmsg(struct msgb *msg); +int mi_e1_line_update(struct e1inp_line *line); + +#endif diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h new file mode 100644 index 000000000..cb8339a79 --- /dev/null +++ b/openbsc/include/openbsc/mncc.h @@ -0,0 +1,159 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +#ifndef _MNCC_H +#define _MNCC_H + +#include <osmocore/linuxlist.h> +#include <osmocore/mncc.h> + +/* One end of a call */ +struct gsm_call { + struct llist_head entry; + + /* network handle */ + void *net; + + /* the 'local' transaction */ + u_int32_t callref; + /* the 'remote' transaction */ + u_int32_t remote_ref; +}; + +#define MNCC_SETUP_REQ 0x0101 +#define MNCC_SETUP_IND 0x0102 +#define MNCC_SETUP_RSP 0x0103 +#define MNCC_SETUP_CNF 0x0104 +#define MNCC_SETUP_COMPL_REQ 0x0105 +#define MNCC_SETUP_COMPL_IND 0x0106 +/* MNCC_REJ_* is perfomed via MNCC_REL_* */ +#define MNCC_CALL_CONF_IND 0x0107 +#define MNCC_CALL_PROC_REQ 0x0108 +#define MNCC_PROGRESS_REQ 0x0109 +#define MNCC_ALERT_REQ 0x010a +#define MNCC_ALERT_IND 0x010b +#define MNCC_NOTIFY_REQ 0x010c +#define MNCC_NOTIFY_IND 0x010d +#define MNCC_DISC_REQ 0x010e +#define MNCC_DISC_IND 0x010f +#define MNCC_REL_REQ 0x0110 +#define MNCC_REL_IND 0x0111 +#define MNCC_REL_CNF 0x0112 +#define MNCC_FACILITY_REQ 0x0113 +#define MNCC_FACILITY_IND 0x0114 +#define MNCC_START_DTMF_IND 0x0115 +#define MNCC_START_DTMF_RSP 0x0116 +#define MNCC_START_DTMF_REJ 0x0117 +#define MNCC_STOP_DTMF_IND 0x0118 +#define MNCC_STOP_DTMF_RSP 0x0119 +#define MNCC_MODIFY_REQ 0x011a +#define MNCC_MODIFY_IND 0x011b +#define MNCC_MODIFY_RSP 0x011c +#define MNCC_MODIFY_CNF 0x011d +#define MNCC_MODIFY_REJ 0x011e +#define MNCC_HOLD_IND 0x011f +#define MNCC_HOLD_CNF 0x0120 +#define MNCC_HOLD_REJ 0x0121 +#define MNCC_RETRIEVE_IND 0x0122 +#define MNCC_RETRIEVE_CNF 0x0123 +#define MNCC_RETRIEVE_REJ 0x0124 +#define MNCC_USERINFO_REQ 0x0125 +#define MNCC_USERINFO_IND 0x0126 +#define MNCC_REJ_REQ 0x0127 +#define MNCC_REJ_IND 0x0128 + +#define MNCC_BRIDGE 0x0200 +#define MNCC_FRAME_RECV 0x0201 +#define MNCC_FRAME_DROP 0x0202 +#define MNCC_LCHAN_MODIFY 0x0203 + +#define GSM_TCHF_FRAME 0x0300 +#define GSM_TCHF_FRAME_EFR 0x0301 + +#define GSM_MAX_FACILITY 128 +#define GSM_MAX_SSVERSION 128 +#define GSM_MAX_USERUSER 128 + +#define MNCC_F_BEARER_CAP 0x0001 +#define MNCC_F_CALLED 0x0002 +#define MNCC_F_CALLING 0x0004 +#define MNCC_F_REDIRECTING 0x0008 +#define MNCC_F_CONNECTED 0x0010 +#define MNCC_F_CAUSE 0x0020 +#define MNCC_F_USERUSER 0x0040 +#define MNCC_F_PROGRESS 0x0080 +#define MNCC_F_EMERGENCY 0x0100 +#define MNCC_F_FACILITY 0x0200 +#define MNCC_F_SSVERSION 0x0400 +#define MNCC_F_CCCAP 0x0800 +#define MNCC_F_KEYPAD 0x1000 +#define MNCC_F_SIGNAL 0x2000 + +struct gsm_mncc { + /* context based information */ + u_int32_t msg_type; + u_int32_t callref; + + /* which fields are present */ + u_int32_t fields; + + /* data derived informations (MNCC_F_ based) */ + struct gsm_mncc_bearer_cap bearer_cap; + struct gsm_mncc_number called; + struct gsm_mncc_number calling; + struct gsm_mncc_number redirecting; + struct gsm_mncc_number connected; + struct gsm_mncc_cause cause; + struct gsm_mncc_progress progress; + struct gsm_mncc_useruser useruser; + struct gsm_mncc_facility facility; + struct gsm_mncc_cccap cccap; + struct gsm_mncc_ssversion ssversion; + struct { + int sup; + int inv; + } clir; + int signal; + + /* data derived information, not MNCC_F based */ + int keypad; + int more; + int notify; /* 0..127 */ + int emergency; + char imsi[16]; + + unsigned char lchan_mode; +}; + +struct gsm_data_frame { + u_int32_t msg_type; + u_int32_t callref; + unsigned char data[0]; +}; + +char *get_mncc_name(int value); +int mncc_recv(struct gsm_network *net, int msg_type, void *arg); +void mncc_set_cause(struct gsm_mncc *data, int loc, int val); + +#endif diff --git a/openbsc/include/openbsc/openbscdefines.h b/openbsc/include/openbsc/openbscdefines.h new file mode 100644 index 000000000..082e5922e --- /dev/null +++ b/openbsc/include/openbsc/openbscdefines.h @@ -0,0 +1,35 @@ +/* + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +#ifndef OPENBSCDEFINES_H +#define OPENBSCDEFINES_H + +#ifdef BUILDING_ON_WINDOWS + #ifdef BUILDING_OPENBSC + #define BSC_API __declspec(dllexport) + #else + #define BSC_API __declspec(dllimport) + #endif +#else + #define BSC_API __attribute__((visibility("default"))) +#endif + +#endif diff --git a/openbsc/include/openbsc/paging.h b/openbsc/include/openbsc/paging.h new file mode 100644 index 000000000..6cbdca902 --- /dev/null +++ b/openbsc/include/openbsc/paging.h @@ -0,0 +1,46 @@ +/* Paging helper and manager.... */ +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +#ifndef PAGING_H +#define PAGING_H + +#include <stdlib.h> +#include <string.h> + +#include <osmocore/linuxlist.h> +#include "gsm_data.h" +#include "gsm_subscriber.h" +#include <osmocore/timer.h> + +/* call once for every gsm_bts... */ +void paging_init(struct gsm_bts *bts); + +/* schedule paging request */ +int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data); + +/* stop paging requests */ +void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, + struct gsm_lchan *lchan); + +/* update paging load */ +void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t); + +#endif diff --git a/openbsc/include/openbsc/rest_octets.h b/openbsc/include/openbsc/rest_octets.h new file mode 100644 index 000000000..4e72c0f87 --- /dev/null +++ b/openbsc/include/openbsc/rest_octets.h @@ -0,0 +1,122 @@ +#ifndef _REST_OCTETS_H +#define _REST_OCTETS_H + +#include <sys/types.h> +#include <openbsc/gsm_04_08.h> + +/* generate SI1 rest octets */ +int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos); + +struct gsm48_si_selection_params { + u_int16_t penalty_time:5, + temp_offs:3, + cell_resel_off:6, + cbq:1, + present:1; +}; + +struct gsm48_si_power_offset { + u_int8_t power_offset:2, + present:1; +}; + +struct gsm48_si3_gprs_ind { + u_int8_t si13_position:1, + ra_colour:3, + present:1; +}; + +struct gsm48_lsa_params { + u_int32_t prio_thr:3, + lsa_offset:3, + mcc:12, + mnc:12; + unsigned int present; +}; + +struct gsm48_si_ro_info { + struct gsm48_si_selection_params selection_params; + struct gsm48_si_power_offset power_offset; + u_int8_t si2ter_indicator; + u_int8_t early_cm_ctrl; + struct { + u_int8_t where:3, + present:1; + } scheduling; + struct gsm48_si3_gprs_ind gprs_ind; + + /* SI 4 specific */ + struct gsm48_lsa_params lsa_params; + u_int16_t cell_id; + u_int8_t break_ind; /* do we have SI7 + SI8 ? */ +}; + + +/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ +int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3); + +/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ +int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4); + +enum pbcch_carrier_type { + PBCCH_BCCH, + PBCCH_ARFCN, + PBCCH_MAIO +}; + +/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */ +enum gprs_nmo { + GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */ + GPRS_NMO_II = 1, /* all paging on CCCH */ + GPRS_NMO_III = 2, /* no paging coordination */ +}; + +struct gprs_cell_options { + enum gprs_nmo nmo; + /* T3168: wait for packet uplink assignment message */ + u_int32_t t3168; /* in milliseconds */ + /* T3192: wait for release of the TBF after reception of the final block */ + u_int32_t t3192; /* in milliseconds */ + u_int32_t drx_timer_max;/* in seconds */ + u_int32_t bs_cv_max; +}; + +/* TS 04.60 Table 12.9.2 */ +struct gprs_power_ctrl_pars { + u_int8_t alpha; + u_int8_t t_avg_w; + u_int8_t t_avg_t; + u_int8_t pc_meas_chan; + u_int8_t n_avg_i; +}; + +struct gsm48_si13_info { + struct gprs_cell_options cell_opts; + struct gprs_power_ctrl_pars pwr_ctrl_pars; + u_int8_t bcch_change_mark; + u_int8_t si_change_field; + u_int8_t pbcch_present; + + union { + struct { + u_int8_t rac; + u_int8_t spgc_ccch_sup; + u_int8_t net_ctrl_ord; + u_int8_t prio_acc_thr; + } no_pbcch; + struct { + u_int8_t psi1_rep_per; + u_int8_t pb; + u_int8_t tsc; + u_int8_t tn; + enum pbcch_carrier_type carrier_type; + u_int16_t arfcn; + u_int8_t maio; + } pbcch; + }; +}; + +/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ +int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13); + +#endif /* _REST_OCTETS_H */ diff --git a/openbsc/include/openbsc/rs232.h b/openbsc/include/openbsc/rs232.h new file mode 100644 index 000000000..61187ca62 --- /dev/null +++ b/openbsc/include/openbsc/rs232.h @@ -0,0 +1,9 @@ +#ifndef _RS232_H +#define _RS232_H + +int rs232_setup(const char *serial_port, unsigned int delay_ms, + struct gsm_bts *bts); + +int handle_serial_msg(struct msgb *msg); + +#endif /* _RS232_H */ diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h new file mode 100644 index 000000000..f82711a8e --- /dev/null +++ b/openbsc/include/openbsc/rtp_proxy.h @@ -0,0 +1,85 @@ +#ifndef _RTP_PROXY_H +#define _RTP_PROXY_H + +/* RTP proxy handling for ip.access nanoBTS */ + +/* (C) 2009 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 <netinet/in.h> + +#include <osmocore/linuxlist.h> +#include <osmocore/select.h> + +enum rtp_rx_action { + RTP_NONE, + RTP_PROXY, + RTP_RECV_UPSTREAM, +}; + +enum rtp_tx_action { + RTP_SEND_NONE, + RTP_SEND_DOWNSTREAM, +}; + +struct rtp_sub_socket { + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + + struct bsc_fd bfd; + /* linked list of to-be-transmitted msgb's */ + struct llist_head tx_queue; +}; + +struct rtp_socket { + struct llist_head list; + + struct rtp_sub_socket rtp; + struct rtp_sub_socket rtcp; + + /* what should we do on receive? */ + enum rtp_rx_action rx_action; + union { + struct { + struct rtp_socket *other_sock; + } proxy; + struct { + struct gsm_network *net; + u_int32_t callref; + } receive; + }; + enum rtp_tx_action tx_action; + struct { + u_int16_t sequence; + u_int32_t timestamp; + u_int32_t ssrc; + struct timeval last_tv; + } transmit; +}; + +struct rtp_socket *rtp_socket_create(void); +int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip); +int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port); +int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other); +int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, u_int32_t callref); +int rtp_socket_free(struct rtp_socket *rs); +int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame); + +#endif /* _RTP_PROXY_H */ diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h new file mode 100644 index 000000000..0c22869f6 --- /dev/null +++ b/openbsc/include/openbsc/signal.h @@ -0,0 +1,133 @@ +/* Generic signalling/notification infrastructure */ +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 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. + * + */ + +#ifndef OPENBSC_SIGNAL_H +#define OPENBSC_SIGNAL_H + +#include <stdlib.h> +#include <errno.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> + +#include <osmocore/signal.h> + +/* + * Signalling subsystems + */ +enum signal_subsystems { + SS_PAGING, + SS_SMS, + SS_ABISIP, + SS_NM, + SS_LCHAN, + SS_SUBSCR, + SS_SCALL, + SS_GLOBAL, +}; + +/* SS_PAGING signals */ +enum signal_paging { + S_PAGING_SUCCEEDED, + S_PAGING_EXPIRED, +}; + +/* SS_SMS signals */ +enum signal_sms { + S_SMS_SUBMITTED, /* A SMS has been successfully submitted to us */ + S_SMS_DELIVERED, /* A SMS has been successfully delivered to a MS */ + S_SMS_SMMA, /* A MS tells us it has more space available */ + S_SMS_MEM_EXCEEDED, /* A MS tells us it has no more space available */ +}; + +/* SS_ABISIP signals */ +enum signal_abisip { + S_ABISIP_CRCX_ACK, + S_ABISIP_MDCX_ACK, + S_ABISIP_DLCX_IND, +}; + +/* SS_NM signals */ +enum signal_nm { + S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */ + S_NM_FAIL_REP, /* GSM 12.21 failure event report */ + S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */ + S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */ + S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */ + S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */ + S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */ + S_NM_TEST_REP, /* GSM 12.21 Test Report */ +}; + +/* SS_LCHAN signals */ +enum signal_lchan { + /* + * The lchan got freed with an use_count != 0 and error + * recovery needs to be carried out from within the + * signal handler. + */ + S_LCHAN_UNEXPECTED_RELEASE, + S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */ + S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */ + S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */ + S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */ + S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */ + S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */ +}; + +/* SS_SUBSCR signals */ +enum signal_subscr { + S_SUBSCR_ATTACHED, + S_SUBSCR_DETACHED, + S_SUBSCR_IDENTITY, /* we've received some identity information */ +}; + +/* SS_SCALL signals */ +enum signal_scall { + S_SCALL_SUCCESS, + S_SCALL_EXPIRED, + S_SCALL_DETACHED, +}; + +enum signal_global { + S_GLOBAL_SHUTDOWN, +}; + +struct paging_signal_data { + struct gsm_subscriber *subscr; + struct gsm_bts *bts; + + /* NULL in case the paging didn't work */ + struct gsm_lchan *lchan; +}; + +struct scall_signal_data { + struct gsm_subscriber *subscr; + struct gsm_lchan *lchan; + void *data; +}; + +struct ipacc_ack_signal_data { + struct gsm_bts *bts; + u_int8_t msg_type; +}; + +#endif diff --git a/openbsc/include/openbsc/silent_call.h b/openbsc/include/openbsc/silent_call.h new file mode 100644 index 000000000..fefc5182d --- /dev/null +++ b/openbsc/include/openbsc/silent_call.h @@ -0,0 +1,10 @@ +#ifndef _SILENT_CALL_H +#define _SILENT_CALL_H + +extern int gsm_silent_call_start(struct gsm_subscriber *subscr, + void *data, int type); +extern int gsm_silent_call_stop(struct gsm_subscriber *subscr); +extern int silent_call_rx(struct msgb *msg); +extern int silent_call_reroute(struct msgb *msg); + +#endif /* _SILENT_CALL_H */ diff --git a/openbsc/include/openbsc/subchan_demux.h b/openbsc/include/openbsc/subchan_demux.h new file mode 100644 index 000000000..02fa02338 --- /dev/null +++ b/openbsc/include/openbsc/subchan_demux.h @@ -0,0 +1,102 @@ +#ifndef _SUBCH_DEMUX_H +#define _SUBCH_DEMUX_H +/* A E1 sub-channel (de)multiplexer with TRAU frame sync */ + +/* (C) 2009 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 <sys/types.h> +#include <osmocore/linuxlist.h> + +#define NR_SUBCH 4 +#define TRAU_FRAME_SIZE 40 +#define TRAU_FRAME_BITS (TRAU_FRAME_SIZE*8) + +/***********************************************************************/ +/* DEMULTIPLEXER */ +/***********************************************************************/ + +struct demux_subch { + u_int8_t out_bitbuf[TRAU_FRAME_BITS]; + u_int16_t out_idx; /* next bit to be written in out_bitbuf */ + /* number of consecutive zeros that we have received (for sync) */ + unsigned int consecutive_zeros; + /* are we in TRAU frame sync or not? */ + unsigned int in_sync; +}; + +struct subch_demux { + /* bitmask of currently active subchannels */ + u_int8_t chan_activ; + /* one demux_subch struct for every subchannel */ + struct demux_subch subch[NR_SUBCH]; + /* callback to be called once we have received a complete + * frame on a given subchannel */ + int (*out_cb)(struct subch_demux *dmx, int ch, u_int8_t *data, int len, + void *); + /* user-provided data, transparently passed to out_cb() */ + void *data; +}; + +/* initialize one demultiplexer instance */ +int subch_demux_init(struct subch_demux *dmx); + +/* feed 'len' number of muxed bytes into the demultiplexer */ +int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len); + +/* activate decoding/processing for one subchannel */ +int subch_demux_activate(struct subch_demux *dmx, int subch); + +/* deactivate decoding/processing for one subchannel */ +int subch_demux_deactivate(struct subch_demux *dmx, int subch); + +/***********************************************************************/ +/* MULTIPLEXER */ +/***********************************************************************/ + +/* one element in the tx_queue of a muxer sub-channel */ +struct subch_txq_entry { + struct llist_head list; + + unsigned int bit_len; /* total number of bits in 'bits' */ + unsigned int next_bit; /* next bit to be transmitted */ + + u_int8_t bits[0]; /* one bit per byte */ +}; + +struct mux_subch { + struct llist_head tx_queue; +}; + +/* structure representing one instance of the subchannel muxer */ +struct subch_mux { + struct mux_subch subch[NR_SUBCH]; +}; + +/* initialize a subchannel muxer instance */ +int subchan_mux_init(struct subch_mux *mx); + +/* request the output of 'len' multiplexed bytes */ +int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len); + +/* enqueue some data into one sub-channel of the muxer */ +int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data, + int len); + +#endif /* _SUBCH_DEMUX_H */ diff --git a/openbsc/include/openbsc/system_information.h b/openbsc/include/openbsc/system_information.h new file mode 100644 index 000000000..982a9ac63 --- /dev/null +++ b/openbsc/include/openbsc/system_information.h @@ -0,0 +1,6 @@ +#ifndef _SYSTEM_INFO_H +#define _SYSTEM_INFO_H + +int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type); + +#endif diff --git a/openbsc/include/openbsc/telnet_interface.h b/openbsc/include/openbsc/telnet_interface.h new file mode 100644 index 000000000..20e794b49 --- /dev/null +++ b/openbsc/include/openbsc/telnet_interface.h @@ -0,0 +1,43 @@ +/* minimalistic telnet/network interface it might turn into a wire interface */ +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +#ifndef TELNET_INTERFACE_H +#define TELNET_INTERFACE_H + +#include "gsm_data.h" +#include "debug.h" +#include <osmocore/select.h> + +#include <vty/vty.h> + +struct telnet_connection { + struct llist_head entry; + struct gsm_network *network; + struct bsc_fd fd; + struct vty *vty; + struct debug_target *dbg; +}; + + +void telnet_init(struct gsm_network *network, int port); + +int bsc_vty_init(struct gsm_network *net); + +#endif diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h new file mode 100644 index 000000000..50c3cc5da --- /dev/null +++ b/openbsc/include/openbsc/transaction.h @@ -0,0 +1,76 @@ +#ifndef _TRANSACT_H +#define _TRANSACT_H + +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> +#include <osmocore/linuxlist.h> +#include <openbsc/gsm_04_11.h> + +/* One transaction */ +struct gsm_trans { + /* Entry in list of all transactions */ + struct llist_head entry; + + /* The protocol within which we live */ + u_int8_t protocol; + + /* The current transaction ID */ + u_int8_t transaction_id; + + /* To whom we belong, unique identifier of remote MM entity */ + struct gsm_subscriber *subscr; + + /* The LCHAN that we're currently using to transmit messages */ + struct gsm_lchan *lchan; + + /* reference from MNCC or other application */ + u_int32_t callref; + + /* if traffic channel receive was requested */ + int tch_recv; + + union { + struct { + + /* current call state */ + int state; + + /* current timer and message queue */ + int Tcurrent; /* current CC timer */ + int T308_second; /* used to send release again */ + struct timer_list timer; + struct gsm_mncc msg; /* stores setup/disconnect/release message */ + } cc; + struct { + u_int8_t link_id; /* RSL Link ID to be used for this trans */ + int is_mt; /* is this a MO (0) or MT (1) transfer */ + enum gsm411_cp_state cp_state; + struct timer_list cp_timer; + + enum gsm411_rp_state rp_state; + + struct gsm_sms *sms; + } sms; + }; +}; + + + +struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr, + u_int8_t proto, u_int8_t trans_id); +struct gsm_trans *trans_find_by_callref(struct gsm_network *net, + u_int32_t callref); + +struct gsm_trans *trans_alloc(struct gsm_subscriber *subscr, + u_int8_t protocol, u_int8_t trans_id, + u_int32_t callref); +void trans_free(struct gsm_trans *trans); + +int trans_assign_trans_id(struct gsm_subscriber *subscr, + u_int8_t protocol, u_int8_t ti_flag); + +/* update all transactions to use a different LCHAN, e.g. + * after handover has succeeded */ +int trans_lchan_change(struct gsm_lchan *lchan_old, + struct gsm_lchan *lchan_new); +#endif diff --git a/openbsc/include/openbsc/trau_frame.h b/openbsc/include/openbsc/trau_frame.h new file mode 100644 index 000000000..5923d4ac1 --- /dev/null +++ b/openbsc/include/openbsc/trau_frame.h @@ -0,0 +1,65 @@ +#ifndef _TRAU_FRAME_H +#define _TRAU_FRAME_H +/* TRAU frame handling according to GSM TS 08.60 */ + +/* (C) 2009 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 <sys/types.h> + +/* 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */ +#define MAX_C_BITS 25 +/* 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */ +#define MAX_D_BITS 288 +/* for all speech frames */ +#define MAX_T_BITS 4 +/* for OM */ +#define MAX_S_BITS 6 +/* for E-data */ +#define MAX_M_BITS 2 + +struct decoded_trau_frame { + u_int8_t c_bits[MAX_C_BITS]; + u_int8_t d_bits[MAX_D_BITS]; + u_int8_t t_bits[MAX_T_BITS]; + u_int8_t s_bits[MAX_S_BITS]; + u_int8_t m_bits[MAX_M_BITS]; +}; + +#define TRAU_FT_FR_UP 0x02 /* 0 0 0 1 0 - 3.5.1.1.1 */ +#define TRAU_FT_FR_DOWN 0x1c /* 1 1 1 0 0 - 3.5.1.1.1 */ +#define TRAU_FT_EFR 0x1a /* 1 1 0 1 0 - 3.5.1.1.1 */ +#define TRAU_FT_AMR 0x06 /* 0 0 1 1 0 - 3.5.1.2 */ +#define TRAU_FT_OM_UP 0x07 /* 0 0 1 0 1 - 3.5.2 */ +#define TRAU_FT_OM_DOWN 0x1b /* 1 1 0 1 1 - 3.5.2 */ +#define TRAU_FT_DATA_UP 0x08 /* 0 1 0 0 0 - 3.5.3 */ +#define TRAU_FT_DATA_DOWN 0x16 /* 1 0 1 1 0 - 3.5.3 */ +#define TRAU_FT_D145_SYNC 0x14 /* 1 0 1 0 0 - 3.5.3 */ +#define TRAU_FT_EDATA 0x1f /* 1 1 1 1 1 - 3.5.4 */ +#define TRAU_FT_IDLE_UP 0x10 /* 1 0 0 0 0 - 3.5.5 */ +#define TRAU_FT_IDLE_DOWN 0x0e /* 0 1 1 1 0 - 3.5.5 */ + + +int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits); +int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr); +int trau_frame_up2down(struct decoded_trau_frame *fr); +u_int8_t *trau_idle_frame(void); + + +#endif /* _TRAU_FRAME_H */ diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h new file mode 100644 index 000000000..8deb708de --- /dev/null +++ b/openbsc/include/openbsc/trau_mux.h @@ -0,0 +1,49 @@ +/* Simple TRAU frame reflector to route voice calls */ + +/* (C) 2009 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. + * + */ + +/* The "TRAU mux map" defines which particular 16kbit sub-slot (in which E1 + * timeslot on which E1 interface) should be directly muxed to which other + * sub-slot. Entries in the mux map are always bi-directional. + * + * The idea of all this is to directly switch voice channels in the BSC + * from one phone to another. We do this right now since we don't support + * any external interface for voice channels, and in the future as an + * optimization to routing them externally. + */ + +/* map a TRAU mux map entry */ +int trau_mux_map(const struct gsm_e1_subslot *src, + const struct gsm_e1_subslot *dst); +int trau_mux_map_lchan(const struct gsm_lchan *src, + const struct gsm_lchan *dst); + +/* unmap a TRAU mux map entry */ +int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref); + +/* we get called by subchan_demux */ +int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, + const u_int8_t *trau_bits, int num_bits); + +/* add a trau receiver */ +int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref); + +/* send trau from application */ +int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); diff --git a/openbsc/include/openbsc/ussd.h b/openbsc/include/openbsc/ussd.h new file mode 100644 index 000000000..63ea31c5e --- /dev/null +++ b/openbsc/include/openbsc/ussd.h @@ -0,0 +1,10 @@ +#ifndef _USSD_H +#define _USSD_H + +/* Handler function for mobile-originated USSD messages */ + +#include <osmocore/msgb.h> + +int handle_rcv_ussd(struct msgb *msg); + +#endif diff --git a/openbsc/include/sccp/Makefile.am b/openbsc/include/sccp/Makefile.am new file mode 100644 index 000000000..6c8a51714 --- /dev/null +++ b/openbsc/include/sccp/Makefile.am @@ -0,0 +1,2 @@ +sccp_HEADERS = sccp_types.h sccp.h +sccpdir = $(includedir)/sccp diff --git a/openbsc/include/sccp/sccp.h b/openbsc/include/sccp/sccp.h new file mode 100644 index 000000000..643479adc --- /dev/null +++ b/openbsc/include/sccp/sccp.h @@ -0,0 +1,167 @@ +/* + * SCCP management code + * + * (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +#ifndef SCCP_H +#define SCCP_H + +#include <stdlib.h> + +#include <sys/socket.h> +#include <sys/types.h> + +#include "sccp_types.h" + +struct msgb; +struct sccp_system; + +enum { + SCCP_CONNECTION_STATE_NONE, + SCCP_CONNECTION_STATE_REQUEST, + SCCP_CONNECTION_STATE_CONFIRM, + SCCP_CONNECTION_STATE_ESTABLISHED, + SCCP_CONNECTION_STATE_RELEASE, + SCCP_CONNECTION_STATE_RELEASE_COMPLETE, + SCCP_CONNECTION_STATE_REFUSED, + SCCP_CONNECTION_STATE_SETUP_ERROR, +}; + +struct sockaddr_sccp { + sa_family_t sccp_family; /* AF_SCCP in the future??? */ + u_int8_t sccp_ssn; /* subssystem number for routing */ + + /* TODO fill in address indicator... if that is ever needed */ + + /* not sure about these */ + /* u_int8_t sccp_class; */ +}; + +/* + * parsed structure of an address + */ +struct sccp_address { + struct sccp_called_party_address address; + u_int8_t ssn; + u_int8_t poi[2]; +}; + +struct sccp_optional_data { + u_int8_t data_len; + u_int8_t data_start; +}; + +struct sccp_connection { + /* public */ + void *data_ctx; + void (*data_cb)(struct sccp_connection *conn, struct msgb *msg, unsigned int len); + + void *state_ctx; + void (*state_cb)(struct sccp_connection *, int old_state); + + struct sccp_source_reference source_local_reference; + struct sccp_source_reference destination_local_reference; + + int connection_state; + + /* private */ + /* list of active connections */ + struct llist_head list; + struct sccp_system *system; + int incoming; +}; + +/** + * system functionality to implement on top of any other transport layer: + * call sccp_system_incoming for incoming data (from the network) + * sccp will call outgoing whenever outgoing data exists + */ +int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *context); +int sccp_system_incoming(struct msgb *data); + +/** + * Send data on an existing connection + */ +int sccp_connection_write(struct sccp_connection *connection, struct msgb *data); +int sccp_connection_send_it(struct sccp_connection *connection); +int sccp_connection_close(struct sccp_connection *connection, int cause); +int sccp_connection_free(struct sccp_connection *connection); + +/** + * Create a new socket. Set your callbacks and then call bind to open + * the connection. + */ +struct sccp_connection *sccp_connection_socket(void); + +/** + * Open the connection and send additional data + */ +int sccp_connection_connect(struct sccp_connection *conn, + const struct sockaddr_sccp *sccp_called, + struct msgb *data); + +/** + * mostly for testing purposes only. Set the accept callback. + * TODO: add true routing information... in analogy to socket, bind, accept + */ +int sccp_connection_set_incoming(const struct sockaddr_sccp *sock, + int (*accept_cb)(struct sccp_connection *connection, void *data), + void *user_data); + +/** + * Send data in terms of unit data. A fixed address indicator will be used. + */ +int sccp_write(struct msgb *data, + const struct sockaddr_sccp *sock_sender, + const struct sockaddr_sccp *sock_target, int class); +int sccp_set_read(const struct sockaddr_sccp *sock, + int (*read_cb)(struct msgb *msgb, unsigned int, void *user_data), + void *user_data); + +/* generic sock addresses */ +extern const struct sockaddr_sccp sccp_ssn_bssap; + +/* helpers */ +u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref); +struct sccp_source_reference sccp_src_ref_from_int(u_int32_t); + +/** + * Below this are helper functions and structs for parsing SCCP messages + */ +struct sccp_parse_result { + struct sccp_address called; + struct sccp_address calling; + + /* point to the msg packet */ + struct sccp_source_reference *source_local_reference; + struct sccp_source_reference *destination_local_reference; + + /* data pointer */ + int data_len; +}; + +/* + * helper functions for the nat code + */ +int sccp_determine_msg_type(struct msgb *msg); +int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result); + +#endif diff --git a/openbsc/include/sccp/sccp_types.h b/openbsc/include/sccp/sccp_types.h new file mode 100644 index 000000000..42fda96ae --- /dev/null +++ b/openbsc/include/sccp/sccp_types.h @@ -0,0 +1,414 @@ +/* + * ITU Q.713 defined types for SCCP + * + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +#ifndef SCCP_TYPES_H +#define SCCP_TYPES_H + +#include <endian.h> + +/* Table 1/Q.713 - SCCP message types */ +enum sccp_message_types { + SCCP_MSG_TYPE_CR = 1, + SCCP_MSG_TYPE_CC = 2, + SCCP_MSG_TYPE_CREF = 3, + SCCP_MSG_TYPE_RLSD = 4, + SCCP_MSG_TYPE_RLC = 5, + SCCP_MSG_TYPE_DT1 = 6, + SCCP_MSG_TYPE_DT2 = 7, + SCCP_MSG_TYPE_AK = 8, + SCCP_MSG_TYPE_UDT = 9, + SCCP_MSG_TYPE_UDTS = 10, + SCCP_MSG_TYPE_ED = 11, + SCCP_MSG_TYPE_EA = 12, + SCCP_MSG_TYPE_RSR = 13, + SCCP_MSG_TYPE_RSC = 14, + SCCP_MSG_TYPE_ERR = 15, + SCCP_MSG_TYPE_IT = 16, + SCCP_MSG_TYPE_XUDT = 17, + SCCP_MSG_TYPE_XUDTS = 18, + SCCP_MSG_TYPE_LUDT = 19, + SCCP_MSG_TYPE_LUDTS = 20 +}; + +/* Table 2/Q.713 - SCCP parameter name codes */ +enum sccp_parameter_name_codes { + SCCP_PNC_END_OF_OPTIONAL = 0, + SCCP_PNC_DESTINATION_LOCAL_REFERENCE = 1, + SCCP_PNC_SOURCE_LOCAL_REFERENCE = 2, + SCCP_PNC_CALLED_PARTY_ADDRESS = 3, + SCCP_PNC_CALLING_PARTY_ADDRESS = 4, + SCCP_PNC_PROTOCOL_CLASS = 5, + SCCP_PNC_SEGMENTING = 6, + SCCP_PNC_RECEIVE_SEQ_NUMBER = 7, + SCCP_PNC_SEQUENCING = 8, + SCCP_PNC_CREDIT = 9, + SCCP_PNC_RELEASE_CAUSE = 10, + SCCP_PNC_RETURN_CAUSE = 11, + SCCP_PNC_RESET_CAUSE = 12, + SCCP_PNC_ERROR_CAUSE = 13, + SCCP_PNC_REFUSAL_CAUSE = 14, + SCCP_PNC_DATA = 15, + SCCP_PNC_SEGMENTATION = 16, + SCCP_PNC_HOP_COUNTER = 17, + SCCP_PNC_IMPORTANCE = 18, + SCCP_PNC_LONG_DATA = 19, +}; + +/* Figure 3/Q.713 Called/calling party address */ +enum { + SCCP_TITLE_IND_NONE = 0, + SCCP_TITLE_IND_NATURE_ONLY = 1, + SCCP_TITLE_IND_TRANSLATION_ONLY = 2, + SCCP_TITLE_IND_TRANS_NUM_ENC = 3, + SCCP_TITLE_IND_TRANS_NUM_ENC_NATURE = 4, +}; + +enum { + SCCP_CALL_ROUTE_ON_SSN = 1, + SCCP_CALL_ROUTE_ON_GT = 0, +}; + +struct sccp_called_party_address { +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t point_code_indicator : 1, + ssn_indicator : 1, + global_title_indicator : 4, + routing_indicator : 1, + reserved : 1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t reserved : 1, + routing_indicator : 1, + global_title_indicator : 4, + ssn_indicator : 1, + point_code_indicator : 1; +#endif + u_int8_t data[0]; +} __attribute__((packed)); + +/* indicator indicates presence in the above order */ + +/* Figure 6/Q.713 */ +struct sccp_signalling_point_code { + u_int8_t lsb; +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t msb : 6, + reserved : 2; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t reserved : 2, + msb : 6; +#endif +} __attribute__((packed)); + +/* SSN == subsystem number */ +enum sccp_subsystem_number { + SCCP_SSN_NOT_KNOWN_OR_USED = 0, + SCCP_SSN_MANAGEMENT = 1, + SCCP_SSN_RESERVED_ITU = 2, + SCCP_SSN_ISDN_USER_PART = 3, + SCCP_SSN_OMAP = 4, /* operation, maint and administration part */ + SCCP_SSN_MAP = 5, /* mobile application part */ + SCCP_SSN_HLR = 6, + SCCP_SSN_VLR = 7, + SCCP_SSN_MSC = 8, + SCCP_SSN_EIC = 9, /* equipent identifier centre */ + SCCP_SSN_AUC = 10, /* authentication centre */ + SCCP_SSN_ISDN_SUPPL_SERVICES = 11, + SCCP_SSN_RESERVED_INTL = 12, + SCCP_SSN_ISDN_EDGE_TO_EDGE = 13, + SCCP_SSN_TC_TEST_RESPONDER = 14, + + /* From GSM 03.03 8.2 */ + SCCP_SSN_BSSAP = 254, + SCCP_SSN_BSSOM = 253, +}; + +/* Q.713, 3.4.2.3 */ +enum { + SCCP_NAI_UNKNOWN = 0, + SCCP_NAI_SUBSCRIBER_NUMBER = 1, + SCCP_NAI_RESERVED_NATIONAL = 2, + SCCP_NAI_NATIONAL_SIGNIFICANT = 3, + SCCP_NAI_INTERNATIONAL = 4, +}; + +struct sccp_global_title { +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t nature_of_addr_ind : 7, + odd_even : 1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t odd_even : 1, + nature_of_addr_ind : 7; +#endif + u_int8_t data[0]; +} __attribute__((packed)); + +/* Q.713, 3.3 */ +struct sccp_source_reference { + u_int8_t octet1; + u_int8_t octet2; + u_int8_t octet3; +} __attribute__((packed)); + +/* Q.714, 3.6 */ +enum sccp_protocol_class { + SCCP_PROTOCOL_CLASS_0 = 0, + SCCP_PROTOCOL_CLASS_1 = 1, + SCCP_PROTOCOL_CLASS_2 = 2, + SCCP_PROTOCOL_CLASS_3 = 3, +}; + +/* bits 5-8 when class0, class1 is used */ +enum sccp_protocol_options { + SCCP_PROTOCOL_NO_SPECIAL = 0, + SCCP_PROTOCOL_RETURN_MESSAGE = 8, +}; + +enum sccp_release_cause { + SCCP_RELEASE_CAUSE_END_USER_ORIGINATED = 0, + SCCP_RELEASE_CAUSE_END_USER_CONGESTION = 1, + SCCP_RELEASE_CAUSE_END_USER_FAILURE = 2, + SCCP_RELEASE_CAUSE_SCCP_USER_ORIGINATED = 3, + SCCP_RELEASE_CAUSE_REMOTE_PROCEDURE_ERROR = 4, + SCCP_RELEASE_CAUSE_INCONSISTENT_CONN_DATA = 5, + SCCP_RELEASE_CAUSE_ACCESS_FAILURE = 6, + SCCP_RELEASE_CAUSE_ACCESS_CONGESTION = 7, + SCCP_RELEASE_CAUSE_SUBSYSTEM_FAILURE = 8, + SCCP_RELEASE_CAUSE_SUBSYSTEM_CONGESTION = 9, + SCCP_RELEASE_CAUSE_MTP_FAILURE = 10, + SCCP_RELEASE_CAUSE_NETWORK_CONGESTION = 11, + SCCP_RELEASE_CAUSE_EXPIRATION_RESET = 12, + SCCP_RELEASE_CAUSE_EXPIRATION_INACTIVE = 13, + SCCP_RELEASE_CAUSE_RESERVED = 14, + SCCP_RELEASE_CAUSE_UNQUALIFIED = 15, + SCCP_RELEASE_CAUSE_SCCP_FAILURE = 16, +}; + +enum sccp_return_cause { + SCCP_RETURN_CAUSE_NO_TRANSLATION_NATURE = 0, + SCCP_RETURN_CAUSE_NO_TRANSLATION = 1, + SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION = 2, + SCCP_RETURN_CAUSE_SUBSYSTEM_FAILURE = 3, + SCCP_RETURN_CAUSE_UNEQUIPPED_USER = 4, + SCCP_RETURN_CAUSE_MTP_FAILURE = 5, + SCCP_RETURN_CAUSE_NETWORK_CONGESTION = 6, + SCCP_RETURN_CAUSE_UNQUALIFIED = 7, + SCCP_RETURN_CAUSE_ERROR_IN_MSG_TRANSPORT = 8, + SCCP_RETURN_CAUSE_ERROR_IN_LOCAL_PROCESSING = 9, + SCCP_RETURN_CAUSE_DEST_CANNOT_PERFORM_REASSEMBLY = 10, + SCCP_RETURN_CAUSE_SCCP_FAILURE = 11, + SCCP_RETURN_CAUSE_HOP_COUNTER_VIOLATION = 12, + SCCP_RETURN_CAUSE_SEGMENTATION_NOT_SUPPORTED= 13, + SCCP_RETURN_CAUSE_SEGMENTATION_FAOLURE = 14 +}; + +enum sccp_reset_cause { + SCCP_RESET_CAUSE_END_USER_ORIGINATED = 0, + SCCP_RESET_CAUSE_SCCP_USER_ORIGINATED = 1, + SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PS = 2, + SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PR = 3, + SCCP_RESET_CAUSE_RPC_OUT_OF_WINDOW = 4, + SCCP_RESET_CAUSE_RPC_INCORRECT_PS = 5, + SCCP_RESET_CAUSE_RPC_GENERAL = 6, + SCCP_RESET_CAUSE_REMOTE_END_USER_OPERATIONAL= 7, + SCCP_RESET_CAUSE_NETWORK_OPERATIONAL = 8, + SCCP_RESET_CAUSE_ACCESS_OPERATIONAL = 9, + SCCP_RESET_CAUSE_NETWORK_CONGESTION = 10, + SCCP_RESET_CAUSE_RESERVED = 11, +}; + +enum sccp_error_cause { + SCCP_ERROR_LRN_MISMATCH_UNASSIGNED = 0, /* local reference number */ + SCCP_ERROR_LRN_MISMATCH_INCONSISTENT = 1, + SCCP_ERROR_POINT_CODE_MISMATCH = 2, + SCCP_ERROR_SERVICE_CLASS_MISMATCH = 3, + SCCP_ERROR_UNQUALIFIED = 4, +}; + +enum sccp_refusal_cause { + SCCP_REFUSAL_END_USER_ORIGINATED = 0, + SCCP_REFUSAL_END_USER_CONGESTION = 1, + SCCP_REFUSAL_END_USER_FAILURE = 2, + SCCP_REFUSAL_SCCP_USER_ORIGINATED = 3, + SCCP_REFUSAL_DESTINATION_ADDRESS_UKNOWN = 4, + SCCP_REFUSAL_DESTINATION_INACCESSIBLE = 5, + SCCP_REFUSAL_NET_QOS_NON_TRANSIENT = 6, + SCCP_REFUSAL_NET_QOS_TRANSIENT = 7, + SCCP_REFUSAL_ACCESS_FAILURE = 8, + SCCP_REFUSAL_ACCESS_CONGESTION = 9, + SCCP_REFUSAL_SUBSYSTEM_FAILURE = 10, + SCCP_REFUSAL_SUBSYTEM_CONGESTION = 11, + SCCP_REFUSAL_EXPIRATION = 12, + SCCP_REFUSAL_INCOMPATIBLE_USER_DATA = 13, + SCCP_REFUSAL_RESERVED = 14, + SCCP_REFUSAL_UNQUALIFIED = 15, + SCCP_REFUSAL_HOP_COUNTER_VIOLATION = 16, + SCCP_REFUSAL_SCCP_FAILURE = 17, + SCCP_REFUSAL_UNEQUIPPED_USER = 18, +}; + +/* + * messages... as of Q.713 Chapter 4 + */ +struct sccp_connection_request { + /* mandantory */ + u_int8_t type; + struct sccp_source_reference source_local_reference; + u_int8_t proto_class; + + + /* variable */ + u_int8_t variable_called; +#if VARIABLE + called_party_address +#endif + + /* optional */ + u_int8_t optional_start; + +#if OPTIONAL + credit 3 + callingparty var 4-n + data 3-130 + hop_counter 3 + importance 3 + end_of_optional 1 +#endif + + u_int8_t data[0]; +} __attribute__((packed)); + +struct sccp_connection_confirm { + /* mandantory */ + u_int8_t type; + struct sccp_source_reference destination_local_reference; + struct sccp_source_reference source_local_reference; + u_int8_t proto_class; + + /* optional */ + u_int8_t optional_start; + + /* optional */ +#if OPTIONAL + credit 3 + called party 4 + data 3-130 + importance 3 + end_of_optional 1 +#endif + + u_int8_t data[0]; +} __attribute__((packed)); + +struct sccp_connection_refused { + /* mandantory */ + u_int8_t type; + struct sccp_source_reference destination_local_reference; + u_int8_t cause; + + /* optional */ + u_int8_t optional_start; + + /* optional */ +#if OPTIONAL + called party 4 + data 3-130 + importance 3 + end_of_optional 1 +#endif + + u_int8_t data[0]; +} __attribute__((packed)); + +struct sccp_connection_released { + /* mandantory */ + u_int8_t type; + struct sccp_source_reference destination_local_reference; + struct sccp_source_reference source_local_reference; + u_int8_t release_cause; + + + /* optional */ + u_int8_t optional_start; + +#if OPTIONAL + data 3-130 + importance 3 + end_of_optional 1 +#endif + u_int8_t data[0]; +} __attribute__((packed)); + +struct sccp_connection_release_complete { + u_int8_t type; + struct sccp_source_reference destination_local_reference; + struct sccp_source_reference source_local_reference; +} __attribute__((packed)); + +struct sccp_data_form1 { + /* mandantory */ + u_int8_t type; + struct sccp_source_reference destination_local_reference; + u_int8_t segmenting; + + /* variable */ + u_int8_t variable_start; + +#if VARIABLE + data 2-256; +#endif + + u_int8_t data[0]; +} __attribute__((packed)); + + +struct sccp_data_unitdata { + /* mandantory */ + u_int8_t type; + u_int8_t proto_class; + + + /* variable */ + u_int8_t variable_called; + u_int8_t variable_calling; + u_int8_t variable_data; + +#if VARIABLE + called party address + calling party address +#endif + + u_int8_t data[0]; +} __attribute__((packed)); + +struct sccp_data_it { + /* mandantory */ + u_int8_t type; + struct sccp_source_reference destination_local_reference; + struct sccp_source_reference source_local_reference; + u_int8_t proto_class; + + u_int8_t sequencing[2]; + u_int8_t credit; +} __attribute__((packed)); + +#endif diff --git a/openbsc/include/vty/Makefile.am b/openbsc/include/vty/Makefile.am new file mode 100644 index 000000000..167476630 --- /dev/null +++ b/openbsc/include/vty/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = buffer.h command.h vector.h vty.h diff --git a/openbsc/include/vty/buffer.h b/openbsc/include/vty/buffer.h new file mode 100644 index 000000000..31519400f --- /dev/null +++ b/openbsc/include/vty/buffer.h @@ -0,0 +1,102 @@ +/* + * Buffering to output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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, or (at your + * option) any later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_BUFFER_H +#define _ZEBRA_BUFFER_H + +#include <sys/types.h> + +/* Create a new buffer. Memory will be allocated in chunks of the given + size. If the argument is 0, the library will supply a reasonable + default size suitable for buffering socket I/O. */ +struct buffer *buffer_new(size_t); + +/* Free all data in the buffer. */ +void buffer_reset(struct buffer *); + +/* This function first calls buffer_reset to release all buffered data. + Then it frees the struct buffer itself. */ +void buffer_free(struct buffer *); + +/* Add the given data to the end of the buffer. */ +extern void buffer_put(struct buffer *, const void *, size_t); +/* Add a single character to the end of the buffer. */ +extern void buffer_putc(struct buffer *, u_char); +/* Add a NUL-terminated string to the end of the buffer. */ +extern void buffer_putstr(struct buffer *, const char *); + +/* Combine all accumulated (and unflushed) data inside the buffer into a + single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note + that this function does not alter the state of the buffer, so the data + is still inside waiting to be flushed. */ +char *buffer_getstr(struct buffer *); + +/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */ +int buffer_empty(struct buffer *); + +typedef enum { + /* An I/O error occurred. The buffer should be destroyed and the + file descriptor should be closed. */ + BUFFER_ERROR = -1, + + /* The data was written successfully, and the buffer is now empty + (there is no pending data waiting to be flushed). */ + BUFFER_EMPTY = 0, + + /* There is pending data in the buffer waiting to be flushed. Please + try flushing the buffer when select indicates that the file descriptor + is writeable. */ + BUFFER_PENDING = 1 +} buffer_status_t; + +/* Try to write this data to the file descriptor. Any data that cannot + be written immediately is added to the buffer queue. */ +extern buffer_status_t buffer_write(struct buffer *, int fd, + const void *, size_t); + +/* This function attempts to flush some (but perhaps not all) of + the queued data to the given file descriptor. */ +extern buffer_status_t buffer_flush_available(struct buffer *, int fd); + +/* The following 2 functions (buffer_flush_all and buffer_flush_window) + are for use in lib/vty.c only. They should not be used elsewhere. */ + +/* Call buffer_flush_available repeatedly until either all data has been + flushed, or an I/O error has been encountered, or the operation would + block. */ +extern buffer_status_t buffer_flush_all(struct buffer *, int fd); + +/* Attempt to write enough data to the given fd to fill a window of the + given width and height (and remove the data written from the buffer). + + If !no_more, then a message saying " --More-- " is appended. + If erase is true, then first overwrite the previous " --More-- " message + with spaces. + + Any write error (including EAGAIN or EINTR) will cause this function + to return -1 (because the logic for handling the erase and more features + is too complicated to retry the write later). +*/ +extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width, + int height, int erase, int no_more); + +#endif /* _ZEBRA_BUFFER_H */ diff --git a/openbsc/include/vty/command.h b/openbsc/include/vty/command.h new file mode 100644 index 000000000..03b071f70 --- /dev/null +++ b/openbsc/include/vty/command.h @@ -0,0 +1,361 @@ +/* + * Zebra configuration command interface routine + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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, or (at your + * option) any later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_COMMAND_H +#define _ZEBRA_COMMAND_H + +#include <stdio.h> +#include <sys/types.h> +#include "vector.h" +#include "vty.h" + +/* Host configuration variable */ +struct host { + /* Host name of this router. */ + char *name; + + /* Password for vty interface. */ + char *password; + char *password_encrypt; + + /* Enable password */ + char *enable; + char *enable_encrypt; + + /* System wide terminal lines. */ + int lines; + + /* Log filename. */ + char *logfile; + + /* config file name of this host */ + char *config; + + /* Flags for services */ + int advanced; + int encrypt; + + /* Banner configuration. */ + const char *motd; + char *motdfile; +}; + +/* There are some command levels which called from command node. */ +enum node_type { + AUTH_NODE, /* Authentication mode of vty interface. */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + SERVICE_NODE, /* Service node. */ + DEBUG_NODE, /* Debug node. */ +#if 0 + AAA_NODE, /* AAA node. */ + KEYCHAIN_NODE, /* Key-chain node. */ + KEYCHAIN_KEY_NODE, /* Key-chain key node. */ + INTERFACE_NODE, /* Interface mode node. */ + ZEBRA_NODE, /* zebra connection node. */ + TABLE_NODE, /* rtm_table selection node. */ + RIP_NODE, /* RIP protocol mode node. */ + RIPNG_NODE, /* RIPng protocol mode node. */ + BGP_NODE, /* BGP protocol mode which includes BGP4+ */ + BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ + BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ + BGP_IPV6_NODE, /* BGP IPv6 address family */ + OSPF_NODE, /* OSPF protocol mode */ + OSPF6_NODE, /* OSPF protocol for IPv6 mode */ + ISIS_NODE, /* ISIS protocol mode */ + MASC_NODE, /* MASC for multicast. */ + IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ + IP_NODE, /* Static ip route node. */ + ACCESS_NODE, /* Access list node. */ + PREFIX_NODE, /* Prefix list node. */ + ACCESS_IPV6_NODE, /* Access list node. */ + PREFIX_IPV6_NODE, /* Prefix list node. */ + AS_LIST_NODE, /* AS list node. */ + COMMUNITY_LIST_NODE, /* Community list node. */ + RMAP_NODE, /* Route map node. */ + SMUX_NODE, /* SNMP configuration node. */ + DUMP_NODE, /* Packet dump node. */ + FORWARDING_NODE, /* IP forwarding node. */ +#endif + VTY_NODE, /* Vty node. */ + + GSMNET_NODE, + BTS_NODE, + TRX_NODE, + TS_NODE, + SUBSCR_NODE, + MGCP_NODE, +}; + +/* Node which has some commands and prompt string and configuration + function pointer . */ +struct cmd_node { + /* Node index. */ + enum node_type node; + + /* Prompt character at vty interface. */ + const char *prompt; + + /* Is this node's configuration goes to vtysh ? */ + int vtysh; + + /* Node's configuration write function */ + int (*func) (struct vty *); + + /* Vector of this node's command list. */ + vector cmd_vector; +}; + +enum { + CMD_ATTR_DEPRECATED = 1, + CMD_ATTR_HIDDEN, +}; + +/* Structure of command element. */ +struct cmd_element { + const char *string; /* Command specification by string. */ + int (*func) (struct cmd_element *, struct vty *, int, const char *[]); + const char *doc; /* Documentation of this command. */ + int daemon; /* Daemon to which this command belong. */ + vector strvec; /* Pointing out each description vector. */ + unsigned int cmdsize; /* Command index count. */ + char *config; /* Configuration string */ + vector subconfig; /* Sub configuration string */ + u_char attr; /* Command attributes */ +}; + +/* Command description structure. */ +struct desc { + const char *cmd; /* Command string. */ + const char *str; /* Command's description. */ +}; + +/* Return value of the commands. */ +#define CMD_SUCCESS 0 +#define CMD_WARNING 1 +#define CMD_ERR_NO_MATCH 2 +#define CMD_ERR_AMBIGUOUS 3 +#define CMD_ERR_INCOMPLETE 4 +#define CMD_ERR_EXEED_ARGC_MAX 5 +#define CMD_ERR_NOTHING_TODO 6 +#define CMD_COMPLETE_FULL_MATCH 7 +#define CMD_COMPLETE_MATCH 8 +#define CMD_COMPLETE_LIST_MATCH 9 +#define CMD_SUCCESS_DAEMON 10 + +/* Argc max counts. */ +#define CMD_ARGC_MAX 25 + +/* Turn off these macros when uisng cpp with extract.pl */ +#ifndef VTYSH_EXTRACT_PL + +/* helper defines for end-user DEFUN* macros */ +#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +#define DEFUN_CMD_FUNC_DECL(funcname) \ + static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \ + +#define DEFUN_CMD_FUNC_TEXT(funcname) \ + static int funcname \ + (struct cmd_element *self, struct vty *vty, int argc, const char *argv[]) + +/* DEFUN for vty command interafce. Little bit hacky ;-). */ +#define DEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \ + +/* DEFUN_NOSH for commands that vtysh should ignore */ +#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFUN(funcname, cmdname, cmdstr, helpstr) + +/* DEFSH for vtysh. */ +#define DEFSH(daemon, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \ + +/* DEFUN + DEFSH */ +#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +/* DEFUN + DEFSH with attributes */ +#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) + +/* ALIAS macro which define existing command's alias. */ +#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + +#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) + +#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0) + +#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0) + +#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) + +#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon) + +#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon) + +#endif /* VTYSH_EXTRACT_PL */ + +/* Some macroes */ +#define CMD_OPTION(S) ((S[0]) == '[') +#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<')) +#define CMD_VARARG(S) ((S[0]) == '.') +#define CMD_RANGE(S) ((S[0] == '<')) + +#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0)) +#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0)) +#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0)) +#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0)) + +/* Common descriptions. */ +#define SHOW_STR "Show running system information\n" +#define IP_STR "IP information\n" +#define IPV6_STR "IPv6 information\n" +#define NO_STR "Negate a command or set its defaults\n" +#define CLEAR_STR "Reset functions\n" +#define RIP_STR "RIP information\n" +#define BGP_STR "BGP information\n" +#define OSPF_STR "OSPF information\n" +#define NEIGHBOR_STR "Specify neighbor router\n" +#define DEBUG_STR "Debugging functions (see also 'undebug')\n" +#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" +#define ROUTER_STR "Enable a routing process\n" +#define AS_STR "AS number\n" +#define MBGP_STR "MBGP information\n" +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" +#define OUT_STR "Filter outgoing routing updates\n" +#define IN_STR "Filter incoming routing updates\n" +#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n" +#define OSPF6_NUMBER_STR "Specify by number\n" +#define INTERFACE_STR "Interface infomation\n" +#define IFNAME_STR "Interface name(e.g. ep0)\n" +#define IP6_STR "IPv6 Information\n" +#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" +#define OSPF6_ROUTER_STR "Enable a routing process\n" +#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" +#define SECONDS_STR "<1-65535> Seconds\n" +#define ROUTE_STR "Routing Table\n" +#define PREFIX_LIST_STR "Build a prefix list\n" +#define OSPF6_DUMP_TYPE_LIST \ +"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" +#define ISIS_STR "IS-IS information\n" +#define AREA_TAG_STR "[area tag]\n" + +#define CONF_BACKUP_EXT ".sav" + +/* IPv4 only machine should not accept IPv6 address for peer's IP + address. So we replace VTY command string like below. */ +#ifdef HAVE_IPV6 +#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " +#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " +#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n" +#else +#define NEIGHBOR_CMD "neighbor A.B.C.D " +#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " +#define NEIGHBOR_ADDR_STR "Neighbor address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" +#endif /* HAVE_IPV6 */ + +/* Prototypes. */ +void install_node(struct cmd_node *, int (*)(struct vty *)); +void install_default(enum node_type); +void install_element(enum node_type, struct cmd_element *); +void sort_node(); + +/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated + string with a space between each element (allocated using + XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ +char *argv_concat(const char **argv, int argc, int shift); + +vector cmd_make_strvec(const char *); +void cmd_free_strvec(vector); +vector cmd_describe_command(); +char **cmd_complete_command(); +const char *cmd_prompt(enum node_type); +int config_from_file(struct vty *, FILE *); +enum node_type node_parent(enum node_type); +int cmd_execute_command(vector, struct vty *, struct cmd_element **, int); +int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **); +void config_replace_string(struct cmd_element *, char *, ...); +void cmd_init(int); + +/* Export typical functions. */ +extern struct cmd_element config_end_cmd; +extern struct cmd_element config_exit_cmd; +extern struct cmd_element config_quit_cmd; +extern struct cmd_element config_help_cmd; +extern struct cmd_element config_list_cmd; +char *host_config_file(); +void host_config_set(const char *); + +void print_version(const char *); + +extern void *tall_vty_cmd_ctx; + +#endif /* _ZEBRA_COMMAND_H */ diff --git a/openbsc/include/vty/vector.h b/openbsc/include/vty/vector.h new file mode 100644 index 000000000..22a184d61 --- /dev/null +++ b/openbsc/include/vty/vector.h @@ -0,0 +1,64 @@ +/* + * Generic vector interface header. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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, or (at your option) any + * later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_VECTOR_H +#define _ZEBRA_VECTOR_H + +/* struct for vector */ +struct _vector { + unsigned int active; /* number of active slots */ + unsigned int alloced; /* number of allocated slot */ + void **index; /* index to data */ +}; +typedef struct _vector *vector; + +#define VECTOR_MIN_SIZE 1 + +/* (Sometimes) usefull macros. This macro convert index expression to + array expression. */ +/* Reference slot at given index, caller must ensure slot is active */ +#define vector_slot(V,I) ((V)->index[(I)]) +/* Number of active slots. + * Note that this differs from vector_count() as it the count returned + * will include any empty slots + */ +#define vector_active(V) ((V)->active) + +/* Prototypes. */ +vector vector_init(unsigned int size); +void vector_ensure(vector v, unsigned int num); +int vector_empty_slot(vector v); +int vector_set(vector v, void *val); +int vector_set_index(vector v, unsigned int i, void *val); +void vector_unset(vector v, unsigned int i); +unsigned int vector_count(vector v); +void vector_only_wrapper_free(vector v); +void vector_only_index_free(void *index); +void vector_free(vector v); +vector vector_copy(vector v); + +void *vector_lookup(vector, unsigned int); +void *vector_lookup_ensure(vector, unsigned int); + +extern void *tall_vty_vec_ctx; + +#endif /* _ZEBRA_VECTOR_H */ diff --git a/openbsc/include/vty/vty.h b/openbsc/include/vty/vty.h new file mode 100644 index 000000000..0441fc5f7 --- /dev/null +++ b/openbsc/include/vty/vty.h @@ -0,0 +1,151 @@ +#ifndef _VTY_H +#define _VTY_H + +#include <stdio.h> +#include <stdarg.h> + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define VTY_PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define VTY_PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +/* Does the I/O error indicate that the operation should be retried later? */ +#define ERRNO_IO_RETRY(EN) \ + (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) + +/* Vty read buffer size. */ +#define VTY_READ_BUFSIZ 512 + +#define VTY_BUFSIZ 512 +#define VTY_MAXHIST 20 + +/* Vty events */ +enum event { + VTY_SERV, + VTY_READ, + VTY_WRITE, + VTY_CLOSED, + VTY_TIMEOUT_RESET, +#ifdef VTYSH + VTYSH_SERV, + VTYSH_READ, + VTYSH_WRITE +#endif /* VTYSH */ +}; + +struct vty { + FILE *file; + + /* private data, specified by creator */ + void *priv; + + /* File descripter of this vty. */ + int fd; + + /* Is this vty connect to file or not */ + enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type; + + /* Node status of this vty */ + int node; + + /* Failure count */ + int fail; + + /* Output buffer. */ + struct buffer *obuf; + + /* Command input buffer */ + char *buf; + + /* Command cursor point */ + int cp; + + /* Command length */ + int length; + + /* Command max length. */ + int max; + + /* Histry of command */ + char *hist[VTY_MAXHIST]; + + /* History lookup current point */ + int hp; + + /* History insert end point */ + int hindex; + + /* For current referencing point of interface, route-map, + access-list etc... */ + void *index; + + /* For multiple level index treatment such as key chain and key. */ + void *index_sub; + + /* For escape character. */ + unsigned char escape; + + /* Current vty status. */ + enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status; + + /* IAC handling: was the last character received the IAC + * (interpret-as-command) escape character (and therefore the next + * character will be the command code)? Refer to Telnet RFC 854. */ + unsigned char iac; + + /* IAC SB (option subnegotiation) handling */ + unsigned char iac_sb_in_progress; + /* At the moment, we care only about the NAWS (window size) negotiation, + * and that requires just a 5-character buffer (RFC 1073): + * <NAWS char> <16-bit width> <16-bit height> */ +#define TELNET_NAWS_SB_LEN 5 + unsigned char sb_buf[TELNET_NAWS_SB_LEN]; + /* How many subnegotiation characters have we received? We just drop + * those that do not fit in the buffer. */ + size_t sb_len; + + /* Window width/height. */ + int width; + int height; + + /* Configure lines. */ + int lines; + + int monitor; + + /* In configure mode. */ + int config; +}; + +/* Small macro to determine newline is newline only or linefeed needed. */ +#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n") + +static inline char *vty_newline(struct vty *vty) +{ + return VTY_NEWLINE; +} + +/* Prototypes. */ +void vty_init (void); +int vty_read_config_file(const char *file_name); +void vty_init_vtysh (void); +void vty_reset (void); +struct vty *vty_new (void); +struct vty *vty_create (int vty_sock, void *priv); +int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3); +int vty_out_newline(struct vty *); +int vty_read(struct vty *vty); +//void vty_time_print (struct vty *, int); +void vty_close (struct vty *); +char *vty_get_cwd (void); +void vty_log (const char *level, const char *proto, const char *fmt, va_list); +int vty_config_lock (struct vty *); +int vty_config_unlock (struct vty *); +int vty_shell (struct vty *); +int vty_shell_serv (struct vty *); +void vty_hello (struct vty *); + +void *tall_vty_ctx; +#endif diff --git a/openbsc/libsccp.pc.in b/openbsc/libsccp.pc.in new file mode 100644 index 000000000..eda8d499c --- /dev/null +++ b/openbsc/libsccp.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: OpenBSC SCCP Lib +Description: OpenBSC SCCP Lib +Version: @VERSION@ +Libs: -L${libdir} -lsccp +Cflags: -I${includedir}/ diff --git a/openbsc/openbsc.pc.in b/openbsc/openbsc.pc.in new file mode 100644 index 000000000..aba07e296 --- /dev/null +++ b/openbsc/openbsc.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/ + +Name: OpenBSC +Description: OpenBSC base station controller +Requires: +Version: @VERSION@ +Libs: -L${libdir} -lopenbsc +Cflags: -I${includedir} diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am new file mode 100644 index 000000000..5dac9debd --- /dev/null +++ b/openbsc/src/Makefile.am @@ -0,0 +1,48 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) + +sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \ + isdnsync bsc_mgcp ipaccess-proxy +noinst_LIBRARIES = libbsc.a libmsc.a libvty.a +noinst_HEADERS = vty/cardshell.h + +bscdir = $(libdir) +bsc_LIBRARIES = libsccp.a + +libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \ + chan_alloc.c debug.c \ + gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \ + trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c \ + input/misdn.c input/ipaccess.c \ + talloc_ctx.c system_information.c rest_octets.c \ + rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \ + bts_unknown.c + +libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \ + mncc.c gsm_04_08.c gsm_04_11.c transaction.c \ + token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \ + handover_logic.c handover_decision.c meas_rep.c + +libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c + +libsccp_a_SOURCES = sccp/sccp.c + +bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c +bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT) + +bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c debug.c \ + rs232.c bts_siemens_bs11.c + +ipaccess_find_SOURCES = ipaccess/ipaccess-find.c + +ipaccess_config_SOURCES = ipaccess/ipaccess-config.c ipaccess/ipaccess-firmware.c +ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT) + +isdnsync_SOURCES = isdnsync.c + +bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \ + debug.c telnet_interface.c +bsc_mgcp_LDADD = libvty.a + +ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c new file mode 100644 index 000000000..1e5e1c87c --- /dev/null +++ b/openbsc/src/abis_nm.c @@ -0,0 +1,3002 @@ +/* GSM Network Management (OML) messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2009 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 <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <libgen.h> +#include <time.h> +#include <limits.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> +#include <openbsc/abis_nm.h> +#include <openbsc/misdn.h> +#include <openbsc/signal.h> + +#define OM_ALLOC_SIZE 1024 +#define OM_HEADROOM_SIZE 128 +#define IPACC_SEGMENT_SIZE 245 + +/* unidirectional messages from BTS to BSC */ +static const enum abis_nm_msgtype reports[] = { + NM_MT_SW_ACTIVATED_REP, + NM_MT_TEST_REP, + NM_MT_STATECHG_EVENT_REP, + NM_MT_FAILURE_EVENT_REP, +}; + +/* messages without ACK/NACK */ +static const enum abis_nm_msgtype no_ack_nack[] = { + NM_MT_MEAS_RES_REQ, + NM_MT_STOP_MEAS, + NM_MT_START_MEAS, +}; + +/* Messages related to software load */ +static const enum abis_nm_msgtype sw_load_msgs[] = { + NM_MT_LOAD_INIT_ACK, + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_SEG_ACK, + NM_MT_LOAD_ABORT, + NM_MT_LOAD_END_ACK, + NM_MT_LOAD_END_NACK, + //NM_MT_SW_ACT_REQ, + NM_MT_ACTIVATE_SW_ACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_SW_ACTIVATED_REP, +}; + +static const enum abis_nm_msgtype nacks[] = { + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_END_NACK, + NM_MT_SW_ACT_REQ_NACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_ESTABLISH_TEI_NACK, + NM_MT_CONN_TERR_SIGN_NACK, + NM_MT_DISC_TERR_SIGN_NACK, + NM_MT_CONN_TERR_TRAF_NACK, + NM_MT_DISC_TERR_TRAF_NACK, + NM_MT_CONN_MDROP_LINK_NACK, + NM_MT_DISC_MDROP_LINK_NACK, + NM_MT_SET_BTS_ATTR_NACK, + NM_MT_SET_RADIO_ATTR_NACK, + NM_MT_SET_CHAN_ATTR_NACK, + NM_MT_PERF_TEST_NACK, + NM_MT_SEND_TEST_REP_NACK, + NM_MT_STOP_TEST_NACK, + NM_MT_STOP_EVENT_REP_NACK, + NM_MT_REST_EVENT_REP_NACK, + NM_MT_CHG_ADM_STATE_NACK, + NM_MT_CHG_ADM_STATE_REQ_NACK, + NM_MT_REP_OUTST_ALARMS_NACK, + NM_MT_CHANGEOVER_NACK, + NM_MT_OPSTART_NACK, + NM_MT_REINIT_NACK, + NM_MT_SET_SITE_OUT_NACK, + NM_MT_CHG_HW_CONF_NACK, + NM_MT_GET_ATTR_NACK, + NM_MT_SET_ALARM_THRES_NACK, + NM_MT_BS11_BEGIN_DB_TX_NACK, + NM_MT_BS11_END_DB_TX_NACK, + NM_MT_BS11_CREATE_OBJ_NACK, + NM_MT_BS11_DELETE_OBJ_NACK, +}; + +static const char *nack_names[0xff] = { + [NM_MT_LOAD_INIT_NACK] = "SOFTWARE LOAD INIT", + [NM_MT_LOAD_END_NACK] = "SOFTWARE LOAD END", + [NM_MT_SW_ACT_REQ_NACK] = "SOFTWARE ACTIVATE REQUEST", + [NM_MT_ACTIVATE_SW_NACK] = "ACTIVATE SOFTWARE", + [NM_MT_ESTABLISH_TEI_NACK] = "ESTABLISH TEI", + [NM_MT_CONN_TERR_SIGN_NACK] = "CONNECT TERRESTRIAL SIGNALLING", + [NM_MT_DISC_TERR_SIGN_NACK] = "DISCONNECT TERRESTRIAL SIGNALLING", + [NM_MT_CONN_TERR_TRAF_NACK] = "CONNECT TERRESTRIAL TRAFFIC", + [NM_MT_DISC_TERR_TRAF_NACK] = "DISCONNECT TERRESTRIAL TRAFFIC", + [NM_MT_CONN_MDROP_LINK_NACK] = "CONNECT MULTI-DROP LINK", + [NM_MT_DISC_MDROP_LINK_NACK] = "DISCONNECT MULTI-DROP LINK", + [NM_MT_SET_BTS_ATTR_NACK] = "SET BTS ATTRIBUTE", + [NM_MT_SET_RADIO_ATTR_NACK] = "SET RADIO ATTRIBUTE", + [NM_MT_SET_CHAN_ATTR_NACK] = "SET CHANNEL ATTRIBUTE", + [NM_MT_PERF_TEST_NACK] = "PERFORM TEST", + [NM_MT_SEND_TEST_REP_NACK] = "SEND TEST REPORT", + [NM_MT_STOP_TEST_NACK] = "STOP TEST", + [NM_MT_STOP_EVENT_REP_NACK] = "STOP EVENT REPORT", + [NM_MT_REST_EVENT_REP_NACK] = "RESET EVENT REPORT", + [NM_MT_CHG_ADM_STATE_NACK] = "CHANGE ADMINISTRATIVE STATE", + [NM_MT_CHG_ADM_STATE_REQ_NACK] = "CHANGE ADMINISTRATIVE STATE REQUEST", + [NM_MT_REP_OUTST_ALARMS_NACK] = "REPORT OUTSTANDING ALARMS", + [NM_MT_CHANGEOVER_NACK] = "CHANGEOVER", + [NM_MT_OPSTART_NACK] = "OPSTART", + [NM_MT_REINIT_NACK] = "REINIT", + [NM_MT_SET_SITE_OUT_NACK] = "SET SITE OUTPUT", + [NM_MT_CHG_HW_CONF_NACK] = "CHANGE HARDWARE CONFIGURATION", + [NM_MT_GET_ATTR_NACK] = "GET ATTRIBUTE", + [NM_MT_SET_ALARM_THRES_NACK] = "SET ALARM THRESHOLD", + [NM_MT_BS11_BEGIN_DB_TX_NACK] = "BS11 BEGIN DATABASE TRANSMISSION", + [NM_MT_BS11_END_DB_TX_NACK] = "BS11 END DATABASE TRANSMISSION", + [NM_MT_BS11_CREATE_OBJ_NACK] = "BS11 CREATE OBJECT", + [NM_MT_BS11_DELETE_OBJ_NACK] = "BS11 DELETE OBJECT", +}; + +/* Chapter 9.4.36 */ +static const char *nack_cause_names[] = { + /* General Nack Causes */ + [NM_NACK_INCORR_STRUCT] = "Incorrect message structure", + [NM_NACK_MSGTYPE_INVAL] = "Invalid message type value", + [NM_NACK_OBJCLASS_INVAL] = "Invalid Object class value", + [NM_NACK_OBJCLASS_NOTSUPP] = "Object class not supported", + [NM_NACK_BTSNR_UNKN] = "BTS no. unknown", + [NM_NACK_TRXNR_UNKN] = "Baseband Transceiver no. unknown", + [NM_NACK_OBJINST_UNKN] = "Object Instance unknown", + [NM_NACK_ATTRID_INVAL] = "Invalid attribute identifier value", + [NM_NACK_ATTRID_NOTSUPP] = "Attribute identifier not supported", + [NM_NACK_PARAM_RANGE] = "Parameter value outside permitted range", + [NM_NACK_ATTRLIST_INCONSISTENT] = "Inconsistency in attribute list", + [NM_NACK_SPEC_IMPL_NOTSUPP] = "Specified implementation not supported", + [NM_NACK_CANT_PERFORM] = "Message cannot be performed", + /* Specific Nack Causes */ + [NM_NACK_RES_NOTIMPL] = "Resource not implemented", + [NM_NACK_RES_NOTAVAIL] = "Resource not available", + [NM_NACK_FREQ_NOTAVAIL] = "Frequency not available", + [NM_NACK_TEST_NOTSUPP] = "Test not supported", + [NM_NACK_CAPACITY_RESTR] = "Capacity restrictions", + [NM_NACK_PHYSCFG_NOTPERFORM] = "Physical configuration cannot be performed", + [NM_NACK_TEST_NOTINIT] = "Test not initiated", + [NM_NACK_PHYSCFG_NOTRESTORE] = "Physical configuration cannot be restored", + [NM_NACK_TEST_NOSUCH] = "No such test", + [NM_NACK_TEST_NOSTOP] = "Test cannot be stopped", + [NM_NACK_MSGINCONSIST_PHYSCFG] = "Message inconsistent with physical configuration", + [NM_NACK_FILE_INCOMPLETE] = "Complete file notreceived", + [NM_NACK_FILE_NOTAVAIL] = "File not available at destination", + [NM_NACK_FILE_NOTACTIVATE] = "File cannot be activate", + [NM_NACK_REQ_NOT_GRANT] = "Request not granted", + [NM_NACK_WAIT] = "Wait", + [NM_NACK_NOTH_REPORT_EXIST] = "Nothing reportable existing", + [NM_NACK_MEAS_NOTSUPP] = "Measurement not supported", + [NM_NACK_MEAS_NOTSTART] = "Measurement not started", +}; + +static char namebuf[255]; +static const char *nack_cause_name(u_int8_t cause) +{ + if (cause < ARRAY_SIZE(nack_cause_names) && nack_cause_names[cause]) + return nack_cause_names[cause]; + + snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause); + return namebuf; +} + +/* Chapter 9.4.16: Event Type */ +static const char *event_type_names[] = { + [NM_EVT_COMM_FAIL] = "communication failure", + [NM_EVT_QOS_FAIL] = "quality of service failure", + [NM_EVT_PROC_FAIL] = "processing failure", + [NM_EVT_EQUIP_FAIL] = "equipment failure", + [NM_EVT_ENV_FAIL] = "environment failure", +}; + +static const char *event_type_name(u_int8_t cause) +{ + if (cause < ARRAY_SIZE(event_type_names) && event_type_names[cause]) + return event_type_names[cause]; + + snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause); + return namebuf; +} + +/* Chapter 9.4.63: Perceived Severity */ +static const char *severity_names[] = { + [NM_SEVER_CEASED] = "failure ceased", + [NM_SEVER_CRITICAL] = "critical failure", + [NM_SEVER_MAJOR] = "major failure", + [NM_SEVER_MINOR] = "minor failure", + [NM_SEVER_WARNING] = "warning level failure", + [NM_SEVER_INDETERMINATE] = "indeterminate failure", +}; + +static const char *severity_name(u_int8_t cause) +{ + if (cause < ARRAY_SIZE(severity_names) && severity_names[cause]) + return severity_names[cause]; + + snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause); + return namebuf; +} + +/* Attributes that the BSC can set, not only get, according to Section 9.4 */ +static const enum abis_nm_attr nm_att_settable[] = { + NM_ATT_ADD_INFO, + NM_ATT_ADD_TEXT, + NM_ATT_DEST, + NM_ATT_EVENT_TYPE, + NM_ATT_FILE_DATA, + NM_ATT_GET_ARI, + NM_ATT_HW_CONF_CHG, + NM_ATT_LIST_REQ_ATTR, + NM_ATT_MDROP_LINK, + NM_ATT_MDROP_NEXT, + NM_ATT_NACK_CAUSES, + NM_ATT_OUTST_ALARM, + NM_ATT_PHYS_CONF, + NM_ATT_PROB_CAUSE, + NM_ATT_RAD_SUBC, + NM_ATT_SOURCE, + NM_ATT_SPEC_PROB, + NM_ATT_START_TIME, + NM_ATT_TEST_DUR, + NM_ATT_TEST_NO, + NM_ATT_TEST_REPORT, + NM_ATT_WINDOW_SIZE, + NM_ATT_SEVERITY, + NM_ATT_MEAS_RES, + NM_ATT_MEAS_TYPE, +}; + +const struct tlv_definition nm_att_tlvdef = { + .def = { + [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V }, + [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V }, + [NM_ATT_ADM_STATE] = { TLV_TYPE_TV }, + [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV }, + [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V }, + [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_BSIC] = { TLV_TYPE_TV }, + [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV }, + [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV }, + [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V }, + [NM_ATT_DEST] = { TLV_TYPE_TL16V }, + [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV }, + [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_HSN] = { TLV_TYPE_TV }, + [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V }, + [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV }, + [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V }, + [NM_ATT_MAIO] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V }, + [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_MAX_TA] = { TLV_TYPE_TV }, + [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV }, + [NM_ATT_NY1] = { TLV_TYPE_TV }, + [NM_ATT_OPER_STATE] = { TLV_TYPE_TV }, + [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V }, + [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V }, + [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV }, + [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV }, + [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV }, + [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV }, + [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SOURCE] = { TLV_TYPE_TL16V }, + [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV }, + [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 }, + [NM_ATT_TEI] = { TLV_TYPE_TV }, + [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_TEST_NO] = { TLV_TYPE_TV }, + [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V }, + [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV }, + [NM_ATT_TSC] = { TLV_TYPE_TV }, + [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_SEVERITY] = { TLV_TYPE_TV }, + [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V }, + [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV }, + [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, + }, +}; + +static const enum abis_nm_chan_comb chcomb4pchan[] = { + [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH, + [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCHComb, + [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull, + [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf, + [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH, + [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH, + [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH, + /* FIXME: bounds check */ +}; + +int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan) +{ + if (pchan < ARRAY_SIZE(chcomb4pchan)) + return chcomb4pchan[pchan]; + + return -EINVAL; +} + +int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_t *buf, int len) +{ + if (!bts->model) + return -EIO; + return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0); +} + +static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (arr[i] == mt) + return 1; + } + + return 0; +} + +#if 0 +/* is this msgtype the usual ACK/NACK type ? */ +static int is_ack_nack(enum abis_nm_msgtype mt) +{ + return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack)); +} +#endif + +/* is this msgtype a report ? */ +static int is_report(enum abis_nm_msgtype mt) +{ + return is_in_arr(mt, reports, ARRAY_SIZE(reports)); +} + +#define MT_ACK(x) (x+1) +#define MT_NACK(x) (x+2) + +static void fill_om_hdr(struct abis_om_hdr *oh, u_int8_t len) +{ + oh->mdisc = ABIS_OM_MDISC_FOM; + oh->placement = ABIS_OM_PLACEMENT_ONLY; + oh->sequence = 0; + oh->length = len; +} + +static void fill_om_fom_hdr(struct abis_om_hdr *oh, u_int8_t len, + u_int8_t msg_type, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr) +{ + struct abis_om_fom_hdr *foh = + (struct abis_om_fom_hdr *) oh->data; + + fill_om_hdr(oh, len+sizeof(*foh)); + foh->msg_type = msg_type; + foh->obj_class = obj_class; + foh->obj_inst.bts_nr = bts_nr; + foh->obj_inst.trx_nr = trx_nr; + foh->obj_inst.ts_nr = ts_nr; +} + +static struct msgb *nm_msgb_alloc(void) +{ + return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, + "OML"); +} + +/* Send a OML NM Message from BSC to BTS */ +int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) +{ + msg->trx = bts->c0; + + return _abis_nm_sendmsg(msg); +} + +static int abis_nm_rcvmsg_sw(struct msgb *mb); + +static const char *obj_class_name(u_int8_t oc) +{ + switch (oc) { + case NM_OC_SITE_MANAGER: + return "SITE MANAGER"; + case NM_OC_BTS: + return "BTS"; + case NM_OC_RADIO_CARRIER: + return "RADIO CARRIER"; + case NM_OC_BASEB_TRANSC: + return "BASEBAND TRANSCEIVER"; + case NM_OC_CHANNEL: + return "CHANNEL"; + case NM_OC_BS11_ADJC: + return "ADJC"; + case NM_OC_BS11_HANDOVER: + return "HANDOVER"; + case NM_OC_BS11_PWR_CTRL: + return "POWER CONTROL"; + case NM_OC_BS11_BTSE: + return "BTSE"; + case NM_OC_BS11_RACK: + return "RACK"; + case NM_OC_BS11_TEST: + return "TEST"; + case NM_OC_BS11_ENVABTSE: + return "ENVABTSE"; + case NM_OC_BS11_BPORT: + return "BPORT"; + case NM_OC_GPRS_NSE: + return "GPRS NSE"; + case NM_OC_GPRS_CELL: + return "GPRS CELL"; + case NM_OC_GPRS_NSVC: + return "GPRS NSVC"; + case NM_OC_BS11: + return "SIEMENSHW"; + } + + return "UNKNOWN"; +} + +const char *nm_opstate_name(u_int8_t os) +{ + switch (os) { + case NM_OPSTATE_DISABLED: + return "Disabled"; + case NM_OPSTATE_ENABLED: + return "Enabled"; + case NM_OPSTATE_NULL: + return "NULL"; + default: + return "RFU"; + } +} + +/* Chapter 9.4.7 */ +static const char *avail_names[] = { + "In test", + "Failed", + "Power off", + "Off line", + "<not used>", + "Dependency", + "Degraded", + "Not installed", +}; + +const char *nm_avail_name(u_int8_t avail) +{ + if (avail == 0xff) + return "OK"; + if (avail >= ARRAY_SIZE(avail_names)) + return "UNKNOWN"; + return avail_names[avail]; +} + +static struct value_string test_names[] = { + /* FIXME: standard test names */ + { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" }, + { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" }, + { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" }, + { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" }, + { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" }, + { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" }, + { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" }, + { 0, NULL } +}; + +const char *nm_adm_name(u_int8_t adm) +{ + switch (adm) { + case 1: + return "Locked"; + case 2: + return "Unlocked"; + case 3: + return "Shutdown"; + default: + return "<not used>"; + } +} + +int nm_is_running(struct gsm_nm_state *s) { + return (s->operational == NM_OPSTATE_ENABLED) && ( + (s->availability == NM_AVSTATE_OK) || + (s->availability == 0xff) + ); +} + +static void debugp_foh(struct abis_om_fom_hdr *foh) +{ + DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", + obj_class_name(foh->obj_class), foh->obj_class, + foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr); +} + +/* obtain the gsm_nm_state data structure for a given object instance */ +static struct gsm_nm_state * +objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class, + struct abis_om_obj_inst *obj_inst) +{ + struct gsm_bts_trx *trx; + struct gsm_nm_state *nm_state = NULL; + + switch (obj_class) { + case NM_OC_BTS: + nm_state = &bts->nm_state; + break; + case NM_OC_RADIO_CARRIER: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + nm_state = &trx->nm_state; + break; + case NM_OC_BASEB_TRANSC: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + nm_state = &trx->bb_transc.nm_state; + break; + case NM_OC_CHANNEL: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + if (obj_inst->ts_nr >= TRX_NR_TS) + return NULL; + nm_state = &trx->ts[obj_inst->ts_nr].nm_state; + break; + case NM_OC_SITE_MANAGER: + nm_state = &bts->site_mgr.nm_state; + break; + case NM_OC_BS11: + switch (obj_inst->bts_nr) { + case BS11_OBJ_CCLK: + nm_state = &bts->bs11.cclk.nm_state; + break; + case BS11_OBJ_BBSIG: + if (obj_inst->ts_nr > bts->num_trx) + return NULL; + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + nm_state = &trx->bs11.bbsig.nm_state; + break; + case BS11_OBJ_PA: + if (obj_inst->ts_nr > bts->num_trx) + return NULL; + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + nm_state = &trx->bs11.pa.nm_state; + break; + default: + return NULL; + } + case NM_OC_BS11_RACK: + nm_state = &bts->bs11.rack.nm_state; + break; + case NM_OC_BS11_ENVABTSE: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) + return NULL; + nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state; + break; + case NM_OC_GPRS_NSE: + nm_state = &bts->gprs.nse.nm_state; + break; + case NM_OC_GPRS_CELL: + nm_state = &bts->gprs.cell.nm_state; + break; + case NM_OC_GPRS_NSVC: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) + return NULL; + nm_state = &bts->gprs.nsvc[obj_inst->trx_nr].nm_state; + break; + } + return nm_state; +} + +/* obtain the in-memory data structure of a given object instance */ +static void * +objclass2obj(struct gsm_bts *bts, u_int8_t obj_class, + struct abis_om_obj_inst *obj_inst) +{ + struct gsm_bts_trx *trx; + void *obj = NULL; + + switch (obj_class) { + case NM_OC_BTS: + obj = bts; + break; + case NM_OC_RADIO_CARRIER: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + obj = trx; + break; + case NM_OC_BASEB_TRANSC: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + obj = &trx->bb_transc; + break; + case NM_OC_CHANNEL: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + if (obj_inst->ts_nr >= TRX_NR_TS) + return NULL; + obj = &trx->ts[obj_inst->ts_nr]; + break; + case NM_OC_SITE_MANAGER: + obj = &bts->site_mgr; + break; + case NM_OC_GPRS_NSE: + obj = &bts->gprs.nse; + break; + case NM_OC_GPRS_CELL: + obj = &bts->gprs.cell; + break; + case NM_OC_GPRS_NSVC: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) + return NULL; + obj = &bts->gprs.nsvc[obj_inst->trx_nr]; + break; + } + return obj; +} + +/* Update the administrative state of a given object in our in-memory data + * structures and send an event to the higher layer */ +static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class, + struct abis_om_obj_inst *obj_inst, u_int8_t adm_state) +{ + struct gsm_nm_state *nm_state, new_state; + void *obj; + int rc; + + obj = objclass2obj(bts, obj_class, obj_inst); + if (!obj) + return -EINVAL; + nm_state = objclass2nmstate(bts, obj_class, obj_inst); + if (!nm_state) + return -1; + + new_state = *nm_state; + new_state.administrative = adm_state; + + rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state); + + nm_state->administrative = adm_state; + + return rc; +} + +static int abis_nm_rx_statechg_rep(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct gsm_bts *bts = mb->trx->bts; + struct tlv_parsed tp; + struct gsm_nm_state *nm_state, new_state; + int rc; + + DEBUGPC(DNM, "STATE CHG: "); + + memset(&new_state, 0, sizeof(new_state)); + + nm_state = objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); + if (!nm_state) { + DEBUGPC(DNM, "unknown object class\n"); + return -EINVAL; + } + + new_state = *nm_state; + + abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); + if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { + new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); + DEBUGPC(DNM, "OP_STATE=%s ", nm_opstate_name(new_state.operational)); + } + if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { + if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) + new_state.availability = 0xff; + else + new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); + DEBUGPC(DNM, "AVAIL=%s(%02x) ", nm_avail_name(new_state.availability), + new_state.availability); + } else + new_state.availability = 0xff; + if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { + new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); + DEBUGPC(DNM, "ADM=%2s ", nm_adm_name(new_state.administrative)); + } + DEBUGPC(DNM, "\n"); + + if ((new_state.administrative != 0 && nm_state->administrative == 0) || + new_state.operational != nm_state->operational || + new_state.availability != nm_state->availability) { + /* Update the operational state of a given object in our in-memory data + * structures and send an event to the higher layer */ + void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst); + rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state); + nm_state->operational = new_state.operational; + nm_state->availability = new_state.availability; + if (nm_state->administrative == 0) + nm_state->administrative = new_state.administrative; + } +#if 0 + if (op_state == 1) { + /* try to enable objects that are disabled */ + abis_nm_opstart(bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr); + } +#endif + return 0; +} + +static int rx_fail_evt_rep(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct tlv_parsed tp; + + DEBUGPC(DNM, "Failure Event Report "); + + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); + + if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) + DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); + if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) + DEBUGPC(DNM, "Severity=%s ", severity_name(*TLVP_VAL(&tp, NM_ATT_SEVERITY))); + + DEBUGPC(DNM, "\n"); + + return 0; +} + +static int abis_nm_rcvmsg_report(struct msgb *mb) +{ + struct abis_om_fom_hdr *foh = msgb_l3(mb); + u_int8_t mt = foh->msg_type; + + debugp_foh(foh); + + //nmh->cfg->report_cb(mb, foh); + + switch (mt) { + case NM_MT_STATECHG_EVENT_REP: + return abis_nm_rx_statechg_rep(mb); + break; + case NM_MT_SW_ACTIVATED_REP: + DEBUGPC(DNM, "Software Activated Report\n"); + dispatch_signal(SS_NM, S_NM_SW_ACTIV_REP, mb); + break; + case NM_MT_FAILURE_EVENT_REP: + rx_fail_evt_rep(mb); + dispatch_signal(SS_NM, S_NM_FAIL_REP, mb); + break; + case NM_MT_TEST_REP: + DEBUGPC(DNM, "Test Report\n"); + dispatch_signal(SS_NM, S_NM_TEST_REP, mb); + break; + default: + DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt); + break; + + }; + + return 0; +} + +/* Activate the specified software into the BTS */ +static int ipacc_sw_activate(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, + u_int8_t i2, const u_int8_t *sw_desc, u_int8_t swdesc_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t len = swdesc_len; + u_int8_t *trailer; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); + + trailer = msgb_put(msg, swdesc_len); + memcpy(trailer, sw_desc, swdesc_len); + + return abis_nm_sendmsg(bts, msg); +} + +static int abis_nm_rx_sw_act_req(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct tlv_parsed tp; + const u_int8_t *sw_config; + int sw_config_len; + int file_id_len; + int ret; + + debugp_foh(foh); + + DEBUGPC(DNM, "SW Activate Request: "); + + DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); + + ret = abis_nm_sw_act_req_ack(mb->trx->bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr, 0, + foh->data, oh->length-sizeof(*foh)); + + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); + sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); + sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); + if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { + DEBUGP(DNM, "SW config not found! Can't continue.\n"); + return -EINVAL; + } else { + DEBUGP(DNM, "Found SW config: %s\n", hexdump(sw_config, sw_config_len)); + } + + if (sw_config[0] != NM_ATT_SW_DESCR) + DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n"); + if (sw_config[1] != NM_ATT_FILE_ID) + DEBUGP(DNM, "FILE_ID attribute identifier not found!\n"); + file_id_len = sw_config[2] * 256 + sw_config[3]; + + /* Assumes first SW file in list is the one to be activated */ + /* sw_config + 4 to skip over 2 attribute ID bytes and 16-bit length field */ + return ipacc_sw_activate(mb->trx->bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr, + sw_config + 4, + file_id_len); +} + +/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ +static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct tlv_parsed tp; + u_int8_t adm_state; + + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); + if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) + return -EINVAL; + + adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); + + return update_admstate(mb->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); +} + +static int abis_nm_rx_lmt_event(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct tlv_parsed tp; + + DEBUGP(DNM, "LMT Event "); + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); + if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && + TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { + u_int8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); + DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); + } + if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && + TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { + u_int8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); + DEBUGPC(DNM, "Level=%u ", level); + } + if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && + TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { + char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); + DEBUGPC(DNM, "Username=%s ", name); + } + DEBUGPC(DNM, "\n"); + /* FIXME: parse LMT LOGON TIME */ + return 0; +} + +/* Receive a OML NM Message from BTS */ +static int abis_nm_rcvmsg_fom(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + u_int8_t mt = foh->msg_type; + + /* check for unsolicited message */ + if (is_report(mt)) + return abis_nm_rcvmsg_report(mb); + + if (is_in_arr(mt, sw_load_msgs, ARRAY_SIZE(sw_load_msgs))) + return abis_nm_rcvmsg_sw(mb); + + if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks))) { + struct tlv_parsed tp; + + debugp_foh(foh); + + if (nack_names[mt]) + DEBUGPC(DNM, "%s NACK ", nack_names[mt]); + /* FIXME: NACK cause */ + else + DEBUGPC(DNM, "NACK 0x%02x ", mt); + + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + DEBUGPC(DNM, "CAUSE=%s\n", + nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + DEBUGPC(DNM, "\n"); + + dispatch_signal(SS_NM, S_NM_NACK, (void*) &mt); + return 0; + } +#if 0 + /* check if last message is to be acked */ + if (is_ack_nack(nmh->last_msgtype)) { + if (mt == MT_ACK(nmh->last_msgtype)) { + DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); + /* we got our ACK, continue sending the next msg */ + } else if (mt == MT_NACK(nmh->last_msgtype)) { + /* we got a NACK, signal this to the caller */ + DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); + /* FIXME: somehow signal this to the caller */ + } else { + /* really strange things happen */ + return -EINVAL; + } + } +#endif + + switch (mt) { + case NM_MT_CHG_ADM_STATE_ACK: + return abis_nm_rx_chg_adm_state_ack(mb); + break; + case NM_MT_SW_ACT_REQ: + return abis_nm_rx_sw_act_req(mb); + break; + case NM_MT_BS11_LMT_SESSION: + return abis_nm_rx_lmt_event(mb); + break; + case NM_MT_CONN_MDROP_LINK_ACK: + DEBUGP(DNM, "CONN MDROP LINK ACK\n"); + break; + case NM_MT_IPACC_RESTART_ACK: + dispatch_signal(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); + break; + case NM_MT_IPACC_RESTART_NACK: + dispatch_signal(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); + break; + } + + return 0; +} + +static int abis_nm_rx_ipacc(struct msgb *mb); + +static int abis_nm_rcvmsg_manuf(struct msgb *mb) +{ + int rc; + int bts_type = mb->trx->bts->type; + + switch (bts_type) { + case GSM_BTS_TYPE_NANOBTS: + rc = abis_nm_rx_ipacc(mb); + break; + default: + LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this " + "BTS type (%u)\n", bts_type); + rc = 0; + break; + } + + return rc; +} + +/* High-Level API */ +/* Entry-point where L2 OML from BTS enters the NM code */ +int abis_nm_rcvmsg(struct msgb *msg) +{ + struct abis_om_hdr *oh = msgb_l2(msg); + int rc = 0; + + /* Various consistency checks */ + if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { + LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", + oh->placement); + return -EINVAL; + } + if (oh->sequence != 0) { + LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", + oh->sequence); + return -EINVAL; + } +#if 0 + unsigned int l2_len = msg->tail - (u_int8_t *)msgb_l2(msg); + unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); + if (oh->length + hlen > l2_len) { + LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n", + oh->length + sizeof(*oh), l2_len); + return -EINVAL; + } + if (oh->length + hlen < l2_len) + LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); +#endif + msg->l3h = (unsigned char *)oh + sizeof(*oh); + + switch (oh->mdisc) { + case ABIS_OM_MDISC_FOM: + rc = abis_nm_rcvmsg_fom(msg); + break; + case ABIS_OM_MDISC_MANUF: + rc = abis_nm_rcvmsg_manuf(msg); + break; + case ABIS_OM_MDISC_MMI: + case ABIS_OM_MDISC_TRAU: + LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", + oh->mdisc); + break; + default: + LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", + oh->mdisc); + return -EINVAL; + } + + msgb_free(msg); + return rc; +} + +#if 0 +/* initialized all resources */ +struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) +{ + struct abis_nm_h *nmh; + + nmh = malloc(sizeof(*nmh)); + if (!nmh) + return NULL; + + nmh->cfg = cfg; + + return nmh; +} + +/* free all resources */ +void abis_nm_fini(struct abis_nm_h *nmh) +{ + free(nmh); +} +#endif + +/* Here we are trying to define a high-level API that can be used by + * the actual BSC implementation. However, the architecture is currently + * still under design. Ideally the calls to this API would be synchronous, + * while the underlying stack behind the APi runs in a traditional select + * based state machine. + */ + +/* 6.2 Software Load: */ +enum sw_state { + SW_STATE_NONE, + SW_STATE_WAIT_INITACK, + SW_STATE_WAIT_SEGACK, + SW_STATE_WAIT_ENDACK, + SW_STATE_WAIT_ACTACK, + SW_STATE_ERROR, +}; + +struct abis_nm_sw { + struct gsm_bts *bts; + gsm_cbfn *cbfn; + void *cb_data; + int forced; + + /* this will become part of the SW LOAD INITIATE */ + u_int8_t obj_class; + u_int8_t obj_instance[3]; + + u_int8_t file_id[255]; + u_int8_t file_id_len; + + u_int8_t file_version[255]; + u_int8_t file_version_len; + + u_int8_t window_size; + u_int8_t seg_in_window; + + int fd; + FILE *stream; + enum sw_state state; + int last_seg; +}; + +static struct abis_nm_sw g_sw; + +static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) +{ + if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { + msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + } else { + LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); + } +} + +/* 6.2.1 / 8.3.1: Load Data Initiate */ +static int sw_load_init(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t len = 3*2 + sw->file_id_len + sw->file_version_len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + sw_add_file_id_and_ver(sw, msg); + msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); + + return abis_nm_sendmsg(sw->bts, msg); +} + +static int is_last_line(FILE *stream) +{ + char next_seg_buf[256]; + long pos; + + /* check if we're sending the last line */ + pos = ftell(stream); + if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { + fseek(stream, pos, SEEK_SET); + return 1; + } + + fseek(stream, pos, SEEK_SET); + return 0; +} + +/* 6.2.2 / 8.3.2 Load Data Segment */ +static int sw_load_segment(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + char seg_buf[256]; + char *line_buf = seg_buf+2; + unsigned char *tlv; + u_int8_t len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + + switch (sw->bts->type) { + case GSM_BTS_TYPE_BS11: + if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { + perror("fgets reading segment"); + return -EINVAL; + } + seg_buf[0] = 0x00; + + /* check if we're sending the last line */ + sw->last_seg = is_last_line(sw->stream); + if (sw->last_seg) + seg_buf[1] = 0; + else + seg_buf[1] = 1 + sw->seg_in_window++; + + len = strlen(line_buf) + 2; + tlv = msgb_put(msg, TLV_GROSS_LEN(len)); + tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (u_int8_t *)seg_buf); + /* BS11 wants CR + LF in excess of the TLV length !?! */ + tlv[1] -= 2; + + /* we only now know the exact length for the OM hdr */ + len = strlen(line_buf)+2; + break; + case GSM_BTS_TYPE_NANOBTS: { + static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); + len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); + if (len < 0) { + perror("read failed"); + return -EINVAL; + } + + if (len != IPACC_SEGMENT_SIZE) + sw->last_seg = 1; + + ++sw->seg_in_window; + msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const u_int8_t *) seg_buf); + len += 3; + break; + } + default: + LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); + /* FIXME: Other BTS types */ + return -1; + } + + fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + return abis_nm_sendmsg(sw->bts, msg); +} + +/* 6.2.4 / 8.3.4 Load Data End */ +static int sw_load_end(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + sw_add_file_id_and_ver(sw, msg); + return abis_nm_sendmsg(sw->bts, msg); +} + +/* Activate the specified software into the BTS */ +static int sw_activate(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + /* FIXME: this is BS11 specific format */ + msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + + return abis_nm_sendmsg(sw->bts, msg); +} + +struct sdp_firmware { + char magic[4]; + char more_magic[4]; + unsigned int header_length; + unsigned int file_length; +} __attribute__ ((packed)); + +static int parse_sdp_header(struct abis_nm_sw *sw) +{ + struct sdp_firmware firmware_header; + int rc; + struct stat stat; + + rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); + if (rc != sizeof(firmware_header)) { + LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); + return -1; + } + + if (strncmp(firmware_header.magic, " SDP", 4) != 0) { + LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); + return -1; + } + + if (firmware_header.more_magic[0] != 0x10 || + firmware_header.more_magic[1] != 0x02 || + firmware_header.more_magic[2] != 0x00 || + firmware_header.more_magic[3] != 0x00) { + LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); + return -1; + } + + + if (fstat(sw->fd, &stat) == -1) { + LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); + return -1; + } + + if (ntohl(firmware_header.file_length) != stat.st_size) { + LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); + return -1; + } + + /* go back to the start as we checked the whole filesize.. */ + lseek(sw->fd, 0l, SEEK_SET); + LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n" + "There might be checksums in the file that are not\n" + "verified and incomplete firmware might be flashed.\n" + "There is absolutely no WARRANTY that flashing will\n" + "work.\n"); + return 0; +} + +static int sw_open_file(struct abis_nm_sw *sw, const char *fname) +{ + char file_id[12+1]; + char file_version[80+1]; + int rc; + + sw->fd = open(fname, O_RDONLY); + if (sw->fd < 0) + return sw->fd; + + switch (sw->bts->type) { + case GSM_BTS_TYPE_BS11: + sw->stream = fdopen(sw->fd, "r"); + if (!sw->stream) { + perror("fdopen"); + return -1; + } + /* read first line and parse file ID and VERSION */ + rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", + file_id, file_version); + if (rc != 2) { + perror("parsing header line of software file"); + return -1; + } + strcpy((char *)sw->file_id, file_id); + sw->file_id_len = strlen(file_id); + strcpy((char *)sw->file_version, file_version); + sw->file_version_len = strlen(file_version); + /* rewind to start of file */ + rewind(sw->stream); + break; + case GSM_BTS_TYPE_NANOBTS: + /* TODO: extract that from the filename or content */ + rc = parse_sdp_header(sw); + if (rc < 0) { + fprintf(stderr, "Could not parse the ipaccess SDP header\n"); + return -1; + } + + strcpy((char *)sw->file_id, "id"); + sw->file_id_len = 3; + strcpy((char *)sw->file_version, "version"); + sw->file_version_len = 8; + break; + default: + /* We don't know how to treat them yet */ + close(sw->fd); + return -EINVAL; + } + + return 0; +} + +static void sw_close_file(struct abis_nm_sw *sw) +{ + switch (sw->bts->type) { + case GSM_BTS_TYPE_BS11: + fclose(sw->stream); + break; + default: + close(sw->fd); + break; + } +} + +/* Fill the window */ +static int sw_fill_window(struct abis_nm_sw *sw) +{ + int rc; + + while (sw->seg_in_window < sw->window_size) { + rc = sw_load_segment(sw); + if (rc < 0) + return rc; + if (sw->last_seg) + break; + } + return 0; +} + +/* callback function from abis_nm_rcvmsg() handler */ +static int abis_nm_rcvmsg_sw(struct msgb *mb) +{ + struct abis_om_fom_hdr *foh = msgb_l3(mb); + int rc = -1; + struct abis_nm_sw *sw = &g_sw; + enum sw_state old_state = sw->state; + + //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); + + switch (sw->state) { + case SW_STATE_WAIT_INITACK: + switch (foh->msg_type) { + case NM_MT_LOAD_INIT_ACK: + /* fill window with segments */ + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_INIT_ACK, mb, + sw->cb_data, NULL); + rc = sw_fill_window(sw); + sw->state = SW_STATE_WAIT_SEGACK; + break; + case NM_MT_LOAD_INIT_NACK: + if (sw->forced) { + DEBUGP(DNM, "FORCED: Ignoring Software Load " + "Init NACK\n"); + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_INIT_ACK, mb, + sw->cb_data, NULL); + rc = sw_fill_window(sw); + sw->state = SW_STATE_WAIT_SEGACK; + } else { + DEBUGP(DNM, "Software Load Init NACK\n"); + /* FIXME: cause */ + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_INIT_NACK, mb, + sw->cb_data, NULL); + sw->state = SW_STATE_ERROR; + } + break; + } + break; + case SW_STATE_WAIT_SEGACK: + switch (foh->msg_type) { + case NM_MT_LOAD_SEG_ACK: + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_SEG_ACK, mb, + sw->cb_data, NULL); + sw->seg_in_window = 0; + if (!sw->last_seg) { + /* fill window with more segments */ + rc = sw_fill_window(sw); + sw->state = SW_STATE_WAIT_SEGACK; + } else { + /* end the transfer */ + sw->state = SW_STATE_WAIT_ENDACK; + rc = sw_load_end(sw); + } + break; + case NM_MT_LOAD_ABORT: + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_ABORT, mb, + sw->cb_data, NULL); + break; + } + break; + case SW_STATE_WAIT_ENDACK: + switch (foh->msg_type) { + case NM_MT_LOAD_END_ACK: + sw_close_file(sw); + DEBUGP(DNM, "Software Load End (BTS %u)\n", + sw->bts->nr); + sw->state = SW_STATE_NONE; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_END_ACK, mb, + sw->cb_data, NULL); + rc = 0; + break; + case NM_MT_LOAD_END_NACK: + if (sw->forced) { + DEBUGP(DNM, "FORCED: Ignoring Software Load" + "End NACK\n"); + sw->state = SW_STATE_NONE; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_END_ACK, mb, + sw->cb_data, NULL); + } else { + DEBUGP(DNM, "Software Load End NACK\n"); + /* FIXME: cause */ + sw->state = SW_STATE_ERROR; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_END_NACK, mb, + sw->cb_data, NULL); + } + break; + } + case SW_STATE_WAIT_ACTACK: + switch (foh->msg_type) { + case NM_MT_ACTIVATE_SW_ACK: + /* we're done */ + DEBUGP(DNM, "Activate Software DONE!\n"); + sw->state = SW_STATE_NONE; + rc = 0; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_ACTIVATE_SW_ACK, mb, + sw->cb_data, NULL); + break; + case NM_MT_ACTIVATE_SW_NACK: + DEBUGP(DNM, "Activate Software NACK\n"); + /* FIXME: cause */ + sw->state = SW_STATE_ERROR; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_ACTIVATE_SW_NACK, mb, + sw->cb_data, NULL); + break; + } + case SW_STATE_NONE: + switch (foh->msg_type) { + case NM_MT_ACTIVATE_SW_ACK: + rc = 0; + break; + } + break; + case SW_STATE_ERROR: + break; + } + + if (rc) + DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n", + foh->msg_type, old_state, sw->state); + + return rc; +} + +/* Load the specified software into the BTS */ +int abis_nm_software_load(struct gsm_bts *bts, const char *fname, + u_int8_t win_size, int forced, + gsm_cbfn *cbfn, void *cb_data) +{ + struct abis_nm_sw *sw = &g_sw; + int rc; + + DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", + bts->nr, fname); + + if (sw->state != SW_STATE_NONE) + return -EBUSY; + + sw->bts = bts; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + sw->obj_class = NM_OC_SITE_MANAGER; + sw->obj_instance[0] = 0xff; + sw->obj_instance[1] = 0xff; + sw->obj_instance[2] = 0xff; + break; + case GSM_BTS_TYPE_NANOBTS: + sw->obj_class = NM_OC_BASEB_TRANSC; + sw->obj_instance[0] = 0x00; + sw->obj_instance[1] = 0x00; + sw->obj_instance[2] = 0xff; + break; + case GSM_BTS_TYPE_UNKNOWN: + default: + LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); + return -1; + break; + } + sw->window_size = win_size; + sw->state = SW_STATE_WAIT_INITACK; + sw->cbfn = cbfn; + sw->cb_data = cb_data; + sw->forced = forced; + + rc = sw_open_file(sw, fname); + if (rc < 0) { + sw->state = SW_STATE_NONE; + return rc; + } + + return sw_load_init(sw); +} + +int abis_nm_software_load_status(struct gsm_bts *bts) +{ + struct abis_nm_sw *sw = &g_sw; + struct stat st; + int rc, percent; + + rc = fstat(sw->fd, &st); + if (rc < 0) { + perror("ERROR during stat"); + return rc; + } + + if (sw->stream) + percent = (ftell(sw->stream) * 100) / st.st_size; + else + percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; + return percent; +} + +/* Activate the specified software into the BTS */ +int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, + gsm_cbfn *cbfn, void *cb_data) +{ + struct abis_nm_sw *sw = &g_sw; + int rc; + + DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", + bts->nr, fname); + + if (sw->state != SW_STATE_NONE) + return -EBUSY; + + sw->bts = bts; + sw->obj_class = NM_OC_SITE_MANAGER; + sw->obj_instance[0] = 0xff; + sw->obj_instance[1] = 0xff; + sw->obj_instance[2] = 0xff; + sw->state = SW_STATE_WAIT_ACTACK; + sw->cbfn = cbfn; + sw->cb_data = cb_data; + + /* Open the file in order to fill some sw struct members */ + rc = sw_open_file(sw, fname); + if (rc < 0) { + sw->state = SW_STATE_NONE; + return rc; + } + sw_close_file(sw); + + return sw_activate(sw); +} + +static void fill_nm_channel(struct abis_nm_channel *ch, u_int8_t bts_port, + u_int8_t ts_nr, u_int8_t subslot_nr) +{ + ch->attrib = NM_ATT_ABIS_CHANNEL; + ch->bts_port = bts_port; + ch->timeslot = ts_nr; + ch->subslot = subslot_nr; +} + +int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr, + u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot, + u_int8_t tei) +{ + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + u_int8_t len = sizeof(*ch) + 2; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, + bts->bts_nr, trx_nr, 0xff); + + msgb_tv_put(msg, NM_ATT_TEI, tei); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + + return abis_nm_sendmsg(bts, msg); +} + +/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ +int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, + u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot) +{ + struct gsm_bts *bts = trx->bts; + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, + NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + + return abis_nm_sendmsg(bts, msg); +} + +#if 0 +int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, + struct abis_nm_abis_channel *chan) +{ +} +#endif + +int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, + u_int8_t e1_port, u_int8_t e1_timeslot, + u_int8_t e1_subslot) +{ + struct gsm_bts *bts = ts->trx->bts; + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, + NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + + DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", + gsm_ts_name(ts), + e1_port, e1_timeslot, e1_subslot); + + return abis_nm_sendmsg(bts, msg); +} + +#if 0 +int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, + struct abis_nm_abis_channel *chan, + u_int8_t subchan) +{ +} +#endif + +/* Chapter 8.6.1 */ +int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t *cur; + + DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); + cur = msgb_put(msg, attr_len); + memcpy(cur, attr, attr_len); + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.6.2 */ +int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t *cur; + + DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, + trx->bts->bts_nr, trx->nr, 0xff); + cur = msgb_put(msg, attr_len); + memcpy(cur, attr, attr_len); + + return abis_nm_sendmsg(trx->bts, msg); +} + +static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb) +{ + int i; + + /* As it turns out, the BS-11 has some very peculiar restrictions + * on the channel combinations it allows */ + switch (ts->trx->bts->type) { + case GSM_BTS_TYPE_BS11: + switch (chan_comb) { + case NM_CHANC_TCHHalf: + case NM_CHANC_TCHHalf2: + /* not supported */ + return -EINVAL; + case NM_CHANC_SDCCH: + /* only one SDCCH/8 per TRX */ + for (i = 0; i < TRX_NR_TS; i++) { + if (i == ts->nr) + continue; + if (ts->trx->ts[i].nm_chan_comb == + NM_CHANC_SDCCH) + return -EINVAL; + } + /* not allowed for TS0 of BCCH-TRX */ + if (ts->trx == ts->trx->bts->c0 && + ts->nr == 0) + return -EINVAL; + /* not on the same TRX that has a BCCH+SDCCH4 + * combination */ + if (ts->trx == ts->trx->bts->c0 && + (ts->trx->ts[0].nm_chan_comb == 5 || + ts->trx->ts[0].nm_chan_comb == 8)) + return -EINVAL; + break; + case NM_CHANC_mainBCCH: + case NM_CHANC_BCCHComb: + /* allowed only for TS0 of C0 */ + if (ts->trx != ts->trx->bts->c0 || + ts->nr != 0) + return -EINVAL; + break; + case NM_CHANC_BCCH: + /* allowed only for TS 2/4/6 of C0 */ + if (ts->trx != ts->trx->bts->c0) + return -EINVAL; + if (ts->nr != 2 && ts->nr != 4 && + ts->nr != 6) + return -EINVAL; + break; + case 8: /* this is not like 08.58, but in fact + * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */ + /* FIXME: only one CBCH allowed per cell */ + break; + } + break; + case GSM_BTS_TYPE_NANOBTS: + switch (ts->nr) { + case 0: + if (ts->trx->nr == 0) { + /* only on TRX0 */ + switch (chan_comb) { + case NM_CHANC_BCCH: + case NM_CHANC_mainBCCH: + case NM_CHANC_BCCHComb: + return 0; + break; + default: + return -EINVAL; + } + } else { + switch (chan_comb) { + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + default: + return -EINVAL; + } + } + break; + case 1: + if (ts->trx->nr == 0) { + switch (chan_comb) { + case NM_CHANC_SDCCH_CBCH: + if (ts->trx->ts[0].nm_chan_comb == + NM_CHANC_mainBCCH) + return 0; + return -EINVAL; + case NM_CHANC_SDCCH: + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + case NM_CHANC_IPAC_TCHFull_PDCH: + return 0; + } + } else { + switch (chan_comb) { + case NM_CHANC_SDCCH: + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + default: + return -EINVAL; + } + } + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + switch (chan_comb) { + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + case NM_CHANC_IPAC_PDCH: + case NM_CHANC_IPAC_TCHFull_PDCH: + if (ts->trx->nr == 0) + return 0; + else + return -EINVAL; + } + break; + } + return -EINVAL; + default: + /* unknown BTS type */ + return 0; + } + return 0; +} + +/* Chapter 8.6.3 */ +int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb) +{ + struct gsm_bts *bts = ts->trx->bts; + struct abis_om_hdr *oh; + u_int16_t arfcn = htons(ts->trx->arfcn); + u_int8_t zero = 0x00; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t len = 2 + 2; + + if (bts->type == GSM_BTS_TYPE_BS11) + len += 4 + 2 + 2 + 3; + + DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts)); + if (verify_chan_comb(ts, chan_comb) < 0) { + msgb_free(msg); + DEBUGP(DNM, "Invalid Channel Combination!!!\n"); + return -EINVAL; + } + ts->nm_chan_comb = chan_comb; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, + NM_OC_CHANNEL, bts->bts_nr, + ts->trx->nr, ts->nr); + /* FIXME: don't send ARFCN list, hopping sequence, mAIO, ...*/ + if (bts->type == GSM_BTS_TYPE_BS11) + msgb_tlv16_put(msg, NM_ATT_ARFCN_LIST, 1, &arfcn); + msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); + if (bts->type == GSM_BTS_TYPE_BS11) { + msgb_tv_put(msg, NM_ATT_HSN, 0x00); + msgb_tv_put(msg, NM_ATT_MAIO, 0x00); + } + msgb_tv_put(msg, NM_ATT_TSC, bts->tsc); /* training sequence */ + if (bts->type == GSM_BTS_TYPE_BS11) + msgb_tlv_put(msg, 0x59, 1, &zero); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1, + u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t msgtype = NM_MT_SW_ACT_REQ_ACK; + u_int8_t len = att_len; + + if (nack) { + len += 2; + msgtype = NM_MT_SW_ACT_REQ_NACK; + } + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3); + + if (attr) { + u_int8_t *ptr = msgb_put(msg, att_len); + memcpy(ptr, attr, att_len); + } + if (nack) + msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *rawmsg) +{ + struct msgb *msg = nm_msgb_alloc(); + struct abis_om_hdr *oh; + u_int8_t *data; + + oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); + fill_om_hdr(oh, len); + data = msgb_put(msg, len); + memcpy(data, rawmsg, len); + + return abis_nm_sendmsg(bts, msg); +} + +/* Siemens specific commands */ +static int __simple_cmd(struct gsm_bts *bts, u_int8_t msg_type) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER, + 0xff, 0xff, 0xff); + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.9.2 */ +int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2); + + debugp_foh((struct abis_om_fom_hdr *) oh->data); + DEBUGPC(DNM, "Sending OPSTART\n"); + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.8.5 */ +int abis_nm_chg_adm_state(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, + u_int8_t i1, u_int8_t i2, enum abis_nm_adm_state adm_state) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); + msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_conn_mdrop_link(struct gsm_bts *bts, u_int8_t e1_port0, u_int8_t ts0, + u_int8_t e1_port1, u_int8_t ts1) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t *attr; + + DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n", + e1_port0, ts0, e1_port1, ts1); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK, + NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00); + + attr = msgb_put(msg, 3); + attr[0] = NM_ATT_MDROP_LINK; + attr[1] = e1_port0; + attr[2] = ts0; + + attr = msgb_put(msg, 3); + attr[0] = NM_ATT_MDROP_NEXT; + attr[1] = e1_port1; + attr[2] = ts1; + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.7.1 */ +int abis_nm_perform_test(struct gsm_bts *bts, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t test_nr, u_int8_t auton_report, + u_int8_t *phys_config, u_int16_t phys_config_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + int len = 4; /* 2 TV attributes */ + + DEBUGP(DNM, "PEFORM TEST\n"); + + if (phys_config_len) + len += 3 + phys_config_len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_PERF_TEST, + obj_class, bts_nr, trx_nr, ts_nr); + msgb_tv_put(msg, NM_ATT_TEST_NO, test_nr); + msgb_tv_put(msg, NM_ATT_AUTON_REPORT, auton_report); + if (phys_config_len) + msgb_tl16v_put(msg, NM_ATT_PHYS_CONF, phys_config_len, + phys_config); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_event_reports(struct gsm_bts *bts, int on) +{ + if (on == 0) + return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); + else + return __simple_cmd(bts, NM_MT_REST_EVENT_REP); +} + +/* Siemens (or BS-11) specific commands */ + +int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) +{ + if (reconnect == 0) + return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); + else + return __simple_cmd(bts, NM_MT_BS11_RECONNECT); +} + +int abis_nm_bs11_restart(struct gsm_bts *bts) +{ + return __simple_cmd(bts, NM_MT_BS11_RESTART); +} + + +struct bs11_date_time { + u_int16_t year; + u_int8_t month; + u_int8_t day; + u_int8_t hour; + u_int8_t min; + u_int8_t sec; +} __attribute__((packed)); + + +void get_bs11_date_time(struct bs11_date_time *aet) +{ + time_t t; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + aet->sec = tm->tm_sec; + aet->min = tm->tm_min; + aet->hour = tm->tm_hour; + aet->day = tm->tm_mday; + aet->month = tm->tm_mon; + aet->year = htons(1900 + tm->tm_year); +} + +int abis_nm_bs11_reset_resource(struct gsm_bts *bts) +{ + return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); +} + +int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) +{ + if (begin) + return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); + else + return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); +} + +int abis_nm_bs11_create_object(struct gsm_bts *bts, + enum abis_bs11_objtype type, u_int8_t idx, + u_int8_t attr_len, const u_int8_t *attr) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t *cur; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, + NM_OC_BS11, type, 0, idx); + cur = msgb_put(msg, attr_len); + memcpy(cur, attr, attr_len); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_delete_object(struct gsm_bts *bts, + enum abis_bs11_objtype type, u_int8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, + NM_OC_BS11, type, 0, idx); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, u_int8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t zero = 0x00; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, + NM_OC_BS11_ENVABTSE, 0, idx, 0xff); + msgb_tlv_put(msg, 0x99, 1, &zero); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_create_bport(struct gsm_bts *bts, u_int8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, + idx, 0xff, 0xff); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_delete_bport(struct gsm_bts *bts, u_int8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT, + idx, 0xff, 0xff); + + return abis_nm_sendmsg(bts, msg); +} + +static const u_int8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; +int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, + 0xff, 0xff, 0xff); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); + + return abis_nm_sendmsg(bts, msg); +} + +/* like abis_nm_conn_terr_traf + set_tei */ +int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port, + u_int8_t e1_timeslot, u_int8_t e1_subslot, + u_int8_t tei) +{ + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, + NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + msgb_tv_put(msg, NM_ATT_TEI, tei); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, u_int8_t level) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, + NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); + msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); + + return abis_nm_sendmsg(trx->bts, msg); +} + +int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t attr = NM_ATT_BS11_TXPWR; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, + NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); + + return abis_nm_sendmsg(trx->bts, msg); +} + +int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t attr[] = { NM_ATT_BS11_PLL_MODE }; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, + NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_get_cclk(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, + NM_ATT_BS11_CCLK_TYPE }; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, + NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); + + return abis_nm_sendmsg(bts, msg); + +} + +//static const u_int8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; + +int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) +{ + return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); +} + +int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) +{ + return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); +} + +int abis_nm_bs11_logon(struct gsm_bts *bts, u_int8_t level, const char *name, int on) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + struct bs11_date_time bdt; + + get_bs11_date_time(&bdt); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + if (on) { + u_int8_t len = 3*2 + sizeof(bdt) + + 1 + strlen(name); + fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, + NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); + msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, + sizeof(bdt), (u_int8_t *) &bdt); + msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, + 1, &level); + msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, + strlen(name), (u_int8_t *)name); + } else { + fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, + NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); + } + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) +{ + struct abis_om_hdr *oh; + struct msgb *msg; + + if (strlen(password) != 10) + return -EINVAL; + + msg = nm_msgb_alloc(); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, + NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const u_int8_t *)password); + + return abis_nm_sendmsg(bts, msg); +} + +/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ +int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) +{ + struct abis_om_hdr *oh; + struct msgb *msg; + u_int8_t tlv_value; + + msg = nm_msgb_alloc(); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, + BS11_OBJ_LI, 0x00, 0x00); + + if (locked) + tlv_value = BS11_LI_PLL_LOCKED; + else + tlv_value = BS11_LI_PLL_STANDALONE; + + msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); + + return abis_nm_sendmsg(bts, msg); +} + +/* Set the calibration value of the PLL (work value/set value) + * It depends on the login which one is changed */ +int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) +{ + struct abis_om_hdr *oh; + struct msgb *msg; + u_int8_t tlv_value[2]; + + msg = nm_msgb_alloc(); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, + BS11_OBJ_TRX1, 0x00, 0x00); + + tlv_value[0] = value>>8; + tlv_value[1] = value&0xff; + + msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_get_state(struct gsm_bts *bts) +{ + return __simple_cmd(bts, NM_MT_BS11_GET_STATE); +} + +/* BS11 SWL */ + +void *tall_fle_ctx; + +struct abis_nm_bs11_sw { + struct gsm_bts *bts; + char swl_fname[PATH_MAX]; + u_int8_t win_size; + int forced; + struct llist_head file_list; + gsm_cbfn *user_cb; /* specified by the user */ +}; +static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; + +struct file_list_entry { + struct llist_head list; + char fname[PATH_MAX]; +}; + +struct file_list_entry *fl_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct file_list_entry, list); +} + +static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) +{ + char linebuf[255]; + struct llist_head *lh, *lh2; + FILE *swl; + int rc = 0; + + swl = fopen(bs11_sw->swl_fname, "r"); + if (!swl) + return -ENODEV; + + /* zero the stale file list, if any */ + llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { + llist_del(lh); + talloc_free(lh); + } + + while (fgets(linebuf, sizeof(linebuf), swl)) { + char file_id[12+1]; + char file_version[80+1]; + struct file_list_entry *fle; + static char dir[PATH_MAX]; + + if (strlen(linebuf) < 4) + continue; + + rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); + if (rc < 0) { + perror("ERR parsing SWL file"); + rc = -EINVAL; + goto out; + } + if (rc < 2) + continue; + + fle = talloc_zero(tall_fle_ctx, struct file_list_entry); + if (!fle) { + rc = -ENOMEM; + goto out; + } + + /* construct new filename */ + strncpy(dir, bs11_sw->swl_fname, sizeof(dir)); + strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); + strcat(fle->fname, "/"); + strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); + + llist_add_tail(&fle->list, &bs11_sw->file_list); + } + +out: + fclose(swl); + return rc; +} + +/* bs11 swload specific callback, passed to abis_nm core swload */ +static int bs11_swload_cbfn(unsigned int hook, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct abis_nm_bs11_sw *bs11_sw = data; + struct file_list_entry *fle; + int rc = 0; + + switch (event) { + case NM_MT_LOAD_END_ACK: + fle = fl_dequeue(&bs11_sw->file_list); + if (fle) { + /* start download the next file of our file list */ + rc = abis_nm_software_load(bs11_sw->bts, fle->fname, + bs11_sw->win_size, + bs11_sw->forced, + &bs11_swload_cbfn, bs11_sw); + talloc_free(fle); + } else { + /* activate the SWL */ + rc = abis_nm_software_activate(bs11_sw->bts, + bs11_sw->swl_fname, + bs11_swload_cbfn, + bs11_sw); + } + break; + case NM_MT_LOAD_SEG_ACK: + case NM_MT_LOAD_END_NACK: + case NM_MT_LOAD_INIT_ACK: + case NM_MT_LOAD_INIT_NACK: + case NM_MT_ACTIVATE_SW_NACK: + case NM_MT_ACTIVATE_SW_ACK: + default: + /* fallthrough to the user callback */ + if (bs11_sw->user_cb) + rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); + break; + } + + return rc; +} + +/* Siemens provides a SWL file that is a mere listing of all the other + * files that are part of a software release. We need to upload first + * the list file, and then each file that is listed in the list file */ +int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, + u_int8_t win_size, int forced, gsm_cbfn *cbfn) +{ + struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; + struct file_list_entry *fle; + int rc = 0; + + INIT_LLIST_HEAD(&bs11_sw->file_list); + bs11_sw->bts = bts; + bs11_sw->win_size = win_size; + bs11_sw->user_cb = cbfn; + bs11_sw->forced = forced; + + strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); + rc = bs11_read_swl_file(bs11_sw); + if (rc < 0) + return rc; + + /* dequeue next item in file list */ + fle = fl_dequeue(&bs11_sw->file_list); + if (!fle) + return -EINVAL; + + /* start download the next file of our file list */ + rc = abis_nm_software_load(bts, fle->fname, win_size, forced, + bs11_swload_cbfn, bs11_sw); + talloc_free(fle); + return rc; +} + +#if 0 +static u_int8_t req_attr_btse[] = { + NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, + NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, + NM_ATT_BS11_LMT_USER_NAME, + + 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, + + NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, + + NM_ATT_BS11_SW_LOAD_STORED }; + +static u_int8_t req_attr_btsm[] = { + NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, + NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, + NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, + NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; +#endif + +static u_int8_t req_attr[] = { + NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, + 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, + 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; + +int abis_nm_bs11_get_serno(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + /* SiemensHW CCTRL object */ + fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, + 0x03, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + struct bs11_date_time aet; + + get_bs11_date_time(&aet); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + /* SiemensHW CCTRL object */ + fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, + 0xff, 0xff, 0xff); + msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (u_int8_t *) &aet); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, u_int8_t bport, enum abis_bs11_line_cfg line_cfg) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + struct bs11_date_time aet; + + get_bs11_date_time(&aet); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT, + bport, 0xff, 0x02); + msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg); + + return abis_nm_sendmsg(bts, msg); +} + +/* ip.access nanoBTS specific commands */ +static const char ipaccess_magic[] = "com.ipaccess"; + + +static int abis_nm_rx_ipacc(struct msgb *msg) +{ + struct abis_om_hdr *oh = msgb_l2(msg); + struct abis_om_fom_hdr *foh; + u_int8_t idstrlen = oh->data[0]; + struct tlv_parsed tp; + struct ipacc_ack_signal_data signal; + + if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { + LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n"); + return -EINVAL; + } + + foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); + abis_nm_tlv_parse(&tp, msg->trx->bts, foh->data, oh->length-sizeof(*foh)); + + debugp_foh(foh); + + DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type); + + switch (foh->msg_type) { + case NM_MT_IPACC_RSL_CONNECT_ACK: + DEBUGPC(DNM, "RSL CONNECT ACK "); + if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) + DEBUGPC(DNM, "IP=%s ", + inet_ntoa(*((struct in_addr *) + TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP)))); + if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT)) + DEBUGPC(DNM, "PORT=%u ", + ntohs(*((u_int16_t *) + TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT)))); + if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID)) + DEBUGPC(DNM, "STREAM=0x%02x ", + *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID)); + DEBUGPC(DNM, "\n"); + break; + case NM_MT_IPACC_RSL_CONNECT_NACK: + LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + DEBUGPC(DNM, " CAUSE=%s\n", + nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + DEBUGPC(DNM, "\n"); + break; + case NM_MT_IPACC_SET_NVATTR_ACK: + DEBUGPC(DNM, "SET NVATTR ACK\n"); + /* FIXME: decode and show the actual attributes */ + break; + case NM_MT_IPACC_SET_NVATTR_NACK: + LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", + nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + LOGPC(DNM, LOGL_ERROR, "\n"); + break; + case NM_MT_IPACC_GET_NVATTR_ACK: + DEBUGPC(DNM, "GET NVATTR ACK\n"); + /* FIXME: decode and show the actual attributes */ + break; + case NM_MT_IPACC_GET_NVATTR_NACK: + LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", + nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + LOGPC(DNM, LOGL_ERROR, "\n"); + break; + case NM_MT_IPACC_SET_ATTR_ACK: + DEBUGPC(DNM, "SET ATTR ACK\n"); + break; + case NM_MT_IPACC_SET_ATTR_NACK: + LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", + nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + LOGPC(DNM, LOGL_ERROR, "\n"); + break; + default: + DEBUGPC(DNM, "unknown\n"); + break; + } + + /* signal handling */ + switch (foh->msg_type) { + case NM_MT_IPACC_RSL_CONNECT_NACK: + case NM_MT_IPACC_SET_NVATTR_NACK: + case NM_MT_IPACC_GET_NVATTR_NACK: + signal.bts = msg->trx->bts; + signal.msg_type = foh->msg_type; + dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal); + break; + case NM_MT_IPACC_SET_NVATTR_ACK: + signal.bts = msg->trx->bts; + signal.msg_type = foh->msg_type; + dispatch_signal(SS_NM, S_NM_IPACC_ACK, &signal); + break; + default: + break; + } + + return 0; +} + +/* send an ip-access manufacturer specific message */ +int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type, + u_int8_t obj_class, u_int8_t bts_nr, + u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t *attr, int attr_len) +{ + struct msgb *msg = nm_msgb_alloc(); + struct abis_om_hdr *oh; + struct abis_om_fom_hdr *foh; + u_int8_t *data; + + /* construct the 12.21 OM header, observe the erroneous length */ + oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); + fill_om_hdr(oh, sizeof(*foh) + attr_len); + oh->mdisc = ABIS_OM_MDISC_MANUF; + + /* add the ip.access magic */ + data = msgb_put(msg, sizeof(ipaccess_magic)+1); + *data++ = sizeof(ipaccess_magic); + memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); + + /* fill the 12.21 FOM header */ + foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh)); + foh->msg_type = msg_type; + foh->obj_class = obj_class; + foh->obj_inst.bts_nr = bts_nr; + foh->obj_inst.trx_nr = trx_nr; + foh->obj_inst.ts_nr = ts_nr; + + if (attr && attr_len) { + data = msgb_put(msg, attr_len); + memcpy(data, attr, attr_len); + } + + return abis_nm_sendmsg(bts, msg); +} + +/* set some attributes in NVRAM */ +int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr, + int attr_len) +{ + return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, + NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, + attr_len); +} + +int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, + u_int32_t ip, u_int16_t port, u_int8_t stream) +{ + struct in_addr ia; + u_int8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0, + NM_ATT_IPACC_DST_IP_PORT, 0, 0, + NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 }; + + int attr_len = sizeof(attr); + + ia.s_addr = htonl(ip); + attr[1] = stream; + attr[3] = port >> 8; + attr[4] = port & 0xff; + *(u_int32_t *)(attr+6) = ia.s_addr; + + /* if ip == 0, we use the default IP */ + if (ip == 0) + attr_len -= 5; + + DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", + inet_ntoa(ia), port, stream); + + return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, + NM_OC_BASEB_TRANSC, trx->bts->bts_nr, + trx->nr, 0xff, attr, attr_len); +} + +/* restart / reboot an ip.access nanoBTS */ +int abis_nm_ipaccess_restart(struct gsm_bts *bts) +{ + return __simple_cmd(bts, NM_MT_IPACC_RESTART); +} + +int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t *attr, u_int8_t attr_len) +{ + return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR, + obj_class, bts_nr, trx_nr, ts_nr, + attr, attr_len); +} + +void abis_nm_ipaccess_cgi(u_int8_t *buf, struct gsm_bts *bts) +{ + /* we simply reuse the GSM48 function and overwrite the RAC + * with the Cell ID */ + gsm48_ra_id_by_bts(buf, bts); + *((u_int16_t *)(buf + 5)) = htons(bts->cell_identity); +} + +void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) +{ + int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; + + trx->nm_state.administrative = new_state; + if (!trx->bts || !trx->bts->oml_link) + return; + + abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, + trx->bts->bts_nr, trx->nr, 0xff, + new_state); +} + +static const char *ipacc_testres_names[] = { + [NM_IPACC_TESTRES_SUCCESS] = "SUCCESS", + [NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT", + [NM_IPACC_TESTRES_NO_CHANS] = "NO CHANNELS", + [NM_IPACC_TESTRES_PARTIAL] = "PARTIAL", + [NM_IPACC_TESTRES_STOPPED] = "STOPPED", +}; + +const char *ipacc_testres_name(u_int8_t res) +{ + if (res < ARRAY_SIZE(ipacc_testres_names) && + ipacc_testres_names[res]) + return ipacc_testres_names[res]; + + return "unknown"; +} + +void ipac_parse_cgi(struct cell_global_id *cid, const u_int8_t *buf) +{ + cid->mcc = (buf[0] & 0xf) * 100; + cid->mcc += (buf[0] >> 4) * 10; + cid->mcc += (buf[1] & 0xf) * 1; + + if (buf[1] >> 4 == 0xf) { + cid->mnc = (buf[2] & 0xf) * 10; + cid->mnc += (buf[2] >> 4) * 1; + } else { + cid->mnc = (buf[2] & 0xf) * 100; + cid->mnc += (buf[2] >> 4) * 10; + cid->mnc += (buf[1] >> 4) * 1; + } + + cid->lac = ntohs(*((u_int16_t *)&buf[3])); + cid->ci = ntohs(*((u_int16_t *)&buf[5])); +} + +/* parse BCCH information IEI from wire format to struct ipac_bcch_info */ +int ipac_parse_bcch_info(struct ipac_bcch_info *binf, u_int8_t *buf) +{ + u_int8_t *cur = buf; + u_int16_t len; + + memset(binf, 0, sizeof(binf)); + + if (cur[0] != NM_IPAC_EIE_BCCH_INFO) + return -EINVAL; + cur++; + + len = ntohs(*(u_int16_t *)cur); + cur += 2; + + binf->info_type = ntohs(*(u_int16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) + binf->freq_qual = *cur >> 2; + + binf->arfcn = *cur++ & 3 << 8; + binf->arfcn |= *cur++; + + if (binf->info_type & IPAC_BINF_RXLEV) + binf->rx_lev = *cur & 0x3f; + cur++; + + if (binf->info_type & IPAC_BINF_RXQUAL) + binf->rx_qual = *cur & 0x7; + cur++; + + if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) + binf->freq_err = ntohs(*(u_int16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FRAME_OFFSET) + binf->frame_offset = ntohs(*(u_int16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) + binf->frame_nr_offset = ntohl(*(u_int32_t *)cur); + cur += 4; + + if (binf->info_type & IPAC_BINF_BSIC) + binf->bsic = *cur & 0x3f; + cur++; + + ipac_parse_cgi(&binf->cgi, cur); + cur += 7; + + if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) { + memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2)); + cur += sizeof(binf->ba_list_si2); + } + + if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) { + memcpy(binf->ba_list_si2bis, cur, + sizeof(binf->ba_list_si2bis)); + cur += sizeof(binf->ba_list_si2bis); + } + + if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) { + memcpy(binf->ba_list_si2ter, cur, + sizeof(binf->ba_list_si2ter)); + cur += sizeof(binf->ba_list_si2ter); + } + + return 0; +} diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c new file mode 100644 index 000000000..60e64cf8e --- /dev/null +++ b/openbsc/src/abis_rsl.c @@ -0,0 +1,1688 @@ +/* GSM Radio Signalling Link messages on the A-bis interface + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2008-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 <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_04_08.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/bsc_rll.h> +#include <openbsc/debug.h> +#include <osmocore/tlv.h> +#include <openbsc/paging.h> +#include <openbsc/signal.h> +#include <openbsc/meas_rep.h> +#include <openbsc/rtp_proxy.h> +#include <osmocore/rsl.h> + +#define RSL_ALLOC_SIZE 1024 +#define RSL_ALLOC_HEADROOM 128 + +#define MAX(a, b) (a) >= (b) ? (a) : (b) + +static u_int8_t mdisc_by_msgtype(u_int8_t msg_type) +{ + /* mask off the transparent bit ? */ + msg_type &= 0xfe; + + if ((msg_type & 0xf0) == 0x00) + return ABIS_RSL_MDISC_RLL; + if ((msg_type & 0xf0) == 0x10) { + if (msg_type >= 0x19 && msg_type <= 0x22) + return ABIS_RSL_MDISC_TRX; + else + return ABIS_RSL_MDISC_COM_CHAN; + } + if ((msg_type & 0xe0) == 0x20) + return ABIS_RSL_MDISC_DED_CHAN; + + return ABIS_RSL_MDISC_LOC; +} + +static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, + u_int8_t msg_type) +{ + dh->c.msg_discr = mdisc_by_msgtype(msg_type); + dh->c.msg_type = msg_type; + dh->ie_chan = RSL_IE_CHAN_NR; +} + +/* determine logical channel based on TRX and channel number IE */ +struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) +{ + struct gsm_lchan *lchan; + u_int8_t ts_nr = chan_nr & 0x07; + u_int8_t cbits = chan_nr >> 3; + u_int8_t lch_idx; + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + + if (cbits == 0x01) { + lch_idx = 0; /* TCH/F */ + if (ts->pchan != GSM_PCHAN_TCH_F && + ts->pchan != GSM_PCHAN_PDCH && + ts->pchan != GSM_PCHAN_TCH_F_PDCH) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x1e) == 0x02) { + lch_idx = cbits & 0x1; /* TCH/H */ + if (ts->pchan != GSM_PCHAN_TCH_H) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x1c) == 0x04) { + lch_idx = cbits & 0x3; /* SDCCH/4 */ + if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x18) == 0x08) { + lch_idx = cbits & 0x7; /* SDCCH/8 */ + if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { + lch_idx = 0; + if (ts->pchan != GSM_PCHAN_CCCH && + ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + /* FIXME: we should not return first sdcch4 !!! */ + } else { + LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); + return NULL; + } + + lchan = &ts->lchan[lch_idx]; + debug_set_context(BSC_CTX_LCHAN, lchan); + debug_set_context(BSC_CTX_SUBSCR, lchan->subscr); + + return lchan; +} + +/* See Table 10.5.25 of GSM04.08 */ +u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan) +{ + struct gsm_bts_trx_ts *ts = lchan->ts; + u_int8_t cbits, chan_nr; + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_PDCH: + case GSM_PCHAN_TCH_F_PDCH: + cbits = 0x01; + break; + case GSM_PCHAN_TCH_H: + cbits = 0x02; + cbits += lchan->nr; + break; + case GSM_PCHAN_CCCH_SDCCH4: + cbits = 0x04; + cbits += lchan->nr; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + cbits = 0x08; + cbits += lchan->nr; + break; + default: + case GSM_PCHAN_CCCH: + cbits = 0x10; + break; + } + + chan_nr = (cbits << 3) | (ts->nr & 0x7); + + return chan_nr; +} + +/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ +u_int64_t str_to_imsi(const char *imsi_str) +{ + u_int64_t ret; + + ret = strtoull(imsi_str, NULL, 10); + + return ret; +} + +/* Table 5 Clause 7 TS 05.02 */ +unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res) +{ + if (!bs_ccch_sdcch_comb) + return 9 - bs_ag_blks_res; + else + return 3 - bs_ag_blks_res; +} + +/* Chapter 6.5.2 of TS 05.02 */ +unsigned int get_ccch_group(u_int64_t imsi, unsigned int bs_cc_chans, + unsigned int n_pag_blocks) +{ + return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks; +} + +/* Chapter 6.5.2 of TS 05.02 */ +unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans, + int n_pag_blocks) +{ + return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks; +} + +static struct msgb *rsl_msgb_alloc(void) +{ + return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, + "RSL"); +} + +#define MACBLOCK_SIZE 23 +static void pad_macblock(u_int8_t *out, const u_int8_t *in, int len) +{ + memcpy(out, in, len); + + if (len < MACBLOCK_SIZE) + memset(out+len, 0x2b, MACBLOCK_SIZE-len); +} + +/* Chapter 9.3.7: Encryption Information */ +static int build_encr_info(u_int8_t *out, struct gsm_lchan *lchan) +{ + *out++ = lchan->encr.alg_id & 0xff; + if (lchan->encr.key_len) + memcpy(out, lchan->encr.key, lchan->encr.key_len); + return lchan->encr.key_len + 1; +} + +static void print_rsl_cause(int lvl, const u_int8_t *cause_v, u_int8_t cause_len) +{ + int i; + + LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", + cause_v[0], rsl_err_name(cause_v[0])); + for (i = 1; i < cause_len-1; i++) + LOGPC(DRSL, lvl, "%02x ", cause_v[i]); +} + +/* Send a BCCH_INFO message as per Chapter 8.5.1 */ +int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type, + const u_int8_t *data, int len) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); + init_dchan_hdr(dh, RSL_MT_BCCH_INFO); + dh->chan_nr = RSL_CHAN_BCCH; + + msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); + msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); + + msg->trx = trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type, + const u_int8_t *data, int len) +{ + struct abis_rsl_common_hdr *ch; + struct msgb *msg = rsl_msgb_alloc(); + + ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); + ch->msg_discr = ABIS_RSL_MDISC_TRX; + ch->msg_type = RSL_MT_SACCH_FILL; + + msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); + msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); + + msg->trx = trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + u_int8_t chan_nr = lchan2chan_nr(lchan); + + db = abs(db); + if (db > 30) + return -EINVAL; + + msg = rsl_msgb_alloc(); + + lchan->bs_power = db/2; + if (fpc) + lchan->bs_power |= 0x10; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + u_int8_t chan_nr = lchan2chan_nr(lchan); + int ctl_lvl; + + ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm); + if (ctl_lvl < 0) + return ctl_lvl; + + msg = rsl_msgb_alloc(); + + lchan->ms_power = ctl_lvl; + + if (fpc) + lchan->ms_power |= 0x20; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, + struct gsm_lchan *lchan) +{ + memset(cm, 0, sizeof(cm)); + + /* FIXME: what to do with data calls ? */ + cm->dtx_dtu = 0x00; + + /* set TCH Speech/Data */ + cm->spd_ind = lchan->rsl_cmode; + + if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && + lchan->tch_mode != GSM48_CMODE_SIGN) + LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " + "but tch_mode != signalling\n"); + + switch (lchan->type) { + case GSM_LCHAN_SDCCH: + cm->chan_rt = RSL_CMOD_CRT_SDCCH; + break; + case GSM_LCHAN_TCH_F: + cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; + break; + case GSM_LCHAN_TCH_H: + cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; + break; + case GSM_LCHAN_NONE: + case GSM_LCHAN_UNKNOWN: + default: + return -EINVAL; + } + + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + cm->chan_rate = 0; + break; + case GSM48_CMODE_SPEECH_V1: + cm->chan_rate = RSL_CMOD_SP_GSM1; + break; + case GSM48_CMODE_SPEECH_EFR: + cm->chan_rate = RSL_CMOD_SP_GSM2; + break; + case GSM48_CMODE_SPEECH_AMR: + cm->chan_rate = RSL_CMOD_SP_GSM3; + break; + case GSM48_CMODE_DATA_14k5: + cm->chan_rate = RSL_CMOD_SP_NT_14k5; + break; + case GSM48_CMODE_DATA_12k0: + cm->chan_rate = RSL_CMOD_SP_NT_12k0; + break; + case GSM48_CMODE_DATA_6k0: + cm->chan_rate = RSL_CMOD_SP_NT_6k0; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Chapter 8.4.1 */ +#if 0 +int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, + u_int8_t act_type, + struct rsl_ie_chan_mode *chan_mode, + struct rsl_ie_chan_ident *chan_ident, + u_int8_t bs_power, u_int8_t ms_power, + u_int8_t ta) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); + /* For compatibility with Phase 1 */ + msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode), + (u_int8_t *) chan_mode); + msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, + (u_int8_t *) chan_ident); +#if 0 + msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, + (u_int8_t *) &encr_info); +#endif + msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power); + msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power); + msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); + + msg->trx = trx; + + return abis_rsl_sendmsg(msg); +} +#endif + +int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, + u_int8_t ta, u_int8_t ho_ref) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + int rc; + + u_int8_t chan_nr = lchan2chan_nr(lchan); + u_int16_t arfcn = lchan->ts->trx->arfcn; + struct rsl_ie_chan_mode cm; + struct rsl_ie_chan_ident ci; + + rc = channel_mode_from_lchan(&cm, lchan); + if (rc < 0) + return rc; + + memset(&ci, 0, sizeof(ci)); + ci.chan_desc.iei = 0x64; + ci.chan_desc.chan_nr = chan_nr; + ci.chan_desc.oct3 = (lchan->ts->trx->bts->tsc << 5) | ((arfcn & 0x3ff) >> 8); + ci.chan_desc.oct4 = arfcn & 0xff; + + msg = rsl_msgb_alloc(); + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); + msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), + (u_int8_t *) &cm); + /* For compatibility with Phase 1 */ + msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, + (u_int8_t *) &ci); + + if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { + u_int8_t encr_info[MAX_A5_KEY_LEN+2]; + rc = build_encr_info(encr_info, lchan); + if (rc > 0) + msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); + } + + switch (act_type) { + case RSL_ACT_INTER_ASYNC: + case RSL_ACT_INTER_SYNC: + msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); + break; + default: + break; + } + + msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); + msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); + msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); + + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), + (u_int8_t *) &lchan->mr_conf); + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.4.9: Modify channel mode on BTS side */ +int rsl_chan_mode_modify_req(struct gsm_lchan *lchan) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + int rc; + + u_int8_t chan_nr = lchan2chan_nr(lchan); + struct rsl_ie_chan_mode cm; + + rc = channel_mode_from_lchan(&cm, lchan); + if (rc < 0) + return rc; + + msg = rsl_msgb_alloc(); + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ); + dh->chan_nr = chan_nr; + + msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), + (u_int8_t *) &cm); + + if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { + u_int8_t encr_info[MAX_A5_KEY_LEN+2]; + rc = build_encr_info(encr_info, lchan); + if (rc > 0) + msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); + } + + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), + (u_int8_t *) &lchan->mr_conf); + } + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.4.6: Send the encryption command with given L3 info */ +int rsl_encryption_cmd(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh; + struct gsm_lchan *lchan = msg->lchan; + u_int8_t chan_nr = lchan2chan_nr(lchan); + u_int8_t encr_info[MAX_A5_KEY_LEN+2]; + u_int8_t l3_len = msg->len; + int rc; + + /* First push the L3 IE tag and length */ + msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); + + /* then the link identifier (SAPI0, main sign link) */ + msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0); + + /* then encryption information */ + rc = build_encr_info(encr_info, lchan); + if (rc <= 0) + return rc; + msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info); + + /* and finally the DCHAN header */ + dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_ENCR_CMD); + dh->chan_nr = chan_nr; + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */ +int rsl_deact_sacch(struct gsm_lchan *lchan) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH); + dh->chan_nr = lchan2chan_nr(lchan); + + msg->lchan = lchan; + msg->trx = lchan->ts->trx; + + DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ +int rsl_rf_chan_release(struct gsm_lchan *lchan) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); + dh->chan_nr = lchan2chan_nr(lchan); + + msg->lchan = lchan; + msg->trx = lchan->ts->trx; + + DEBUGP(DRSL, "%s RF Channel Release CMD\n", gsm_lchan_name(lchan)); + + /* BTS will respond by RF CHAN REL ACK */ + return abis_rsl_sendmsg(msg); +} + +int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len, + u_int8_t *ms_ident, u_int8_t chan_needed) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_PAGING_CMD); + dh->chan_nr = RSL_CHAN_PCH_AGCH; + + msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); + msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); + msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); + + msg->trx = bts->c0; + + return abis_rsl_sendmsg(msg); +} + +int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_need, + struct gsm_subscriber *subscr) +{ +#if 0 + u_int8_t mi[128]; + unsigned int mi_len; + u_int8_t paging_group; +#endif + + return -1; +} + +int imsi_str2bcd(u_int8_t *bcd_out, const char *str_in) +{ + int i, len = strlen(str_in); + + for (i = 0; i < len; i++) { + int num = str_in[i] - 0x30; + if (num < 0 || num > 9) + return -1; + if (i % 2 == 0) + bcd_out[i/2] = num; + else + bcd_out[i/2] |= (num << 4); + } + + return 0; +} + +/* Chapter 8.5.6 */ +int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + u_int8_t buf[MACBLOCK_SIZE]; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); + dh->chan_nr = RSL_CHAN_PCH_AGCH; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); + break; + default: + /* If phase 2, construct a FULL_IMM_ASS_INFO */ + pad_macblock(buf, val, len); + msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf); + break; + } + + msg->trx = bts->c0; + + return abis_rsl_sendmsg(msg); +} + +/* Send Siemens specific MS RF Power Capability Indication */ +int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI); + dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + dh->chan_nr = lchan2chan_nr(lchan); + msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(u_int8_t *)mrpci); + + DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", + gsm_lchan_name(lchan), *(u_int8_t *)mrpci); + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + + +/* Send "DATA REQUEST" message with given L3 Info payload */ +/* Chapter 8.3.1 */ +int rsl_data_request(struct msgb *msg, u_int8_t link_id) +{ + if (msg->lchan == NULL) { + LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); + return -EINVAL; + } + + rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, lchan2chan_nr(msg->lchan), + link_id, 1); + + msg->trx = msg->lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* Send "ESTABLISH REQUEST" message with given L3 Info payload */ +/* Chapter 8.3.1 */ +int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id) +{ + struct msgb *msg; + + msg = rsl_rll_simple(RSL_MT_EST_REQ, lchan2chan_nr(lchan), + link_id, 0); + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.3.7 Request the release of multiframe mode of RLL connection. + This is what higher layers should call. The BTS then responds with + RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE, + which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls + lchan_free() */ +int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id) +{ + + struct msgb *msg; + + msg = rsl_rll_simple(RSL_MT_REL_REQ, lchan2chan_nr(lchan), + link_id, 0); + msgb_tv_put(msg, RSL_IE_RELEASE_MODE, 0); /* normal release */ + + lchan->state = LCHAN_S_REL_REQ; + /* FIXME: start some timer in case we don't receive a REL ACK ? */ + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.4.2: Channel Activate Acknowledge */ +static int rsl_rx_chan_act_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + + /* BTS has confirmed channel activation, we now need + * to assign the activated channel to the MS */ + if (rslh->ie_chan != RSL_IE_CHAN_NR) + return -EINVAL; + + if (msg->lchan->state != LCHAN_S_ACT_REQ) + LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", + gsm_lchan_name(msg->lchan), + gsm_lchans_name(msg->lchan->state)); + msg->lchan->state = LCHAN_S_ACTIVE; + + dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan); + + return 0; +} + +/* Chapter 8.4.3: Channel Activate NACK */ +static int rsl_rx_chan_act_nack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK", + gsm_lchan_name(msg->lchan)); + + /* BTS has rejected channel activation ?!? */ + if (dh->ie_chan != RSL_IE_CHAN_NR) + return -EINVAL; + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { + const u_int8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); + print_rsl_cause(LOGL_ERROR, cause, + TLVP_LEN(&tp, RSL_IE_CAUSE)); + if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) + msg->lchan->state = LCHAN_S_NONE; + } else + msg->lchan->state = LCHAN_S_NONE; + + LOGPC(DRSL, LOGL_ERROR, "\n"); + + dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan); + + lchan_free(msg->lchan); + return 0; +} + +/* Chapter 8.4.4: Connection Failure Indication */ +static int rsl_rx_conn_fail(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + /* FIXME: print which channel */ + LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING ", + gsm_lchan_name(msg->lchan)); + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) + print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), + TLVP_LEN(&tp, RSL_IE_CAUSE)); + + LOGPC(DRSL, LOGL_NOTICE, "\n"); + /* FIXME: only free it after channel release ACK */ + return rsl_rf_chan_release(msg->lchan); +} + +static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, + const char *prefix) +{ + DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", + prefix, rxlev2dbm(mru->full.rx_lev), + prefix, rxlev2dbm(mru->sub.rx_lev)); + DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", + prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); +} + +static void print_meas_rep(struct gsm_meas_rep *mr) +{ + int i; + + DEBUGP(DMEAS, "MEASUREMENT RESULT NR=%d ", mr->nr); + + if (mr->flags & MEAS_REP_F_DL_DTX) + DEBUGPC(DMEAS, "DTXd "); + + print_meas_rep_uni(&mr->ul, "ul"); + DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); + if (mr->flags & MEAS_REP_F_MS_TO) + DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); + + if (mr->flags & MEAS_REP_F_MS_L1) { + DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); + DEBUGPC(DMEAS, "L1_FPC=%u ", + mr->flags & MEAS_REP_F_FPC ? 1 : 0); + DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); + } + + if (mr->flags & MEAS_REP_F_UL_DTX) + DEBUGPC(DMEAS, "DTXu "); + if (mr->flags & MEAS_REP_F_BA1) + DEBUGPC(DMEAS, "BA1 "); + if (!(mr->flags & MEAS_REP_F_DL_VALID)) + DEBUGPC(DMEAS, "NOT VALID "); + else + print_meas_rep_uni(&mr->dl, "dl"); + + DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); + if (mr->num_cell == 7) + return; + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", + mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); + } +} + +static int rsl_rx_meas_res(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); + u_int8_t len; + const u_int8_t *val; + int rc; + + /* check if this channel is actually active */ + /* FIXME: maybe this check should be way more generic/centralized */ + if (msg->lchan->state != LCHAN_S_ACTIVE) { + LOGP(DRSL, LOGL_NOTICE, "%s: MEAS RES for inactive channel\n", + gsm_lchan_name(msg->lchan)); + return 0; + } + + memset(mr, 0, sizeof(*mr)); + mr->lchan = msg->lchan; + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || + !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || + !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) + return -EIO; + + /* Mandatory Parts */ + mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); + + len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); + val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); + if (len >= 3) { + if (val[0] & 0x40) + mr->flags |= MEAS_REP_F_DL_DTX; + mr->ul.full.rx_lev = val[0] & 0x3f; + mr->ul.sub.rx_lev = val[1] & 0x3f; + mr->ul.full.rx_qual = val[2]>>3 & 0x7; + mr->ul.sub.rx_qual = val[2] & 0x7; + } + + mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); + + /* Optional Parts */ + if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) + mr->ms_timing_offset = + *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET); + + if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { + val = TLVP_VAL(&tp, RSL_IE_L1_INFO); + mr->flags |= MEAS_REP_F_MS_L1; + mr->ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3); + if (val[0] & 0x04) + mr->flags |= MEAS_REP_F_FPC; + mr->ms_l1.ta = val[1]; + } + if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { + msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); + rc = gsm48_parse_meas_rep(mr, msg); + if (rc < 0) + return rc; + } + + print_meas_rep(mr); + + dispatch_signal(SS_LCHAN, S_LCHAN_MEAS_REP, mr); + + return 0; +} + +/* Chapter 8.4.7 */ +static int rsl_rx_hando_det(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) + DEBUGPC(DRSL, "access delay = %u\n", + *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); + else + DEBUGPC(DRSL, "\n"); + + dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_DETECT, msg->lchan); + + return 0; +} + +static int abis_rsl_rx_dchan(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + int rc = 0; + char *ts_name; + + msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + + switch (rslh->c.msg_type) { + case RSL_MT_CHAN_ACTIV_ACK: + DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); + rc = rsl_rx_chan_act_ack(msg); + break; + case RSL_MT_CHAN_ACTIV_NACK: + rc = rsl_rx_chan_act_nack(msg); + break; + case RSL_MT_CONN_FAIL: + rc = rsl_rx_conn_fail(msg); + break; + case RSL_MT_MEAS_RES: + rc = rsl_rx_meas_res(msg); + break; + case RSL_MT_HANDO_DET: + rc = rsl_rx_hando_det(msg); + break; + case RSL_MT_RF_CHAN_REL_ACK: + DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", ts_name); + if (msg->lchan->state != LCHAN_S_REL_REQ) + LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", + gsm_lchan_name(msg->lchan), + gsm_lchans_name(msg->lchan->state)); + msg->lchan->state = LCHAN_S_NONE; + lchan_free(msg->lchan); + break; + case RSL_MT_MODE_MODIFY_ACK: + DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); + break; + case RSL_MT_MODE_MODIFY_NACK: + LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_ACT_ACK: + DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_ACT_NACK: + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_DEACT_ACK: + DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_DEACT_NACK: + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); + break; + case RSL_MT_PHY_CONTEXT_CONF: + case RSL_MT_PREPROC_MEAS_RES: + case RSL_MT_TALKER_DET: + case RSL_MT_LISTENER_DET: + case RSL_MT_REMOTE_CODEC_CONF_REP: + case RSL_MT_MR_CODEC_MOD_ACK: + case RSL_MT_MR_CODEC_MOD_NACK: + case RSL_MT_MR_CODEC_MOD_PER: + LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " + "msg 0x%02x\n", ts_name, rslh->c.msg_type); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", + ts_name, rslh->c.msg_type); + return -EINVAL; + } + + return rc; +} + +static int rsl_rx_error_rep(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + struct tlv_parsed tp; + + LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(msg->trx)); + + rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); + + if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) + print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), + TLVP_LEN(&tp, RSL_IE_CAUSE)); + + LOGPC(DRSL, LOGL_ERROR, "\n"); + + return 0; +} + +static int abis_rsl_rx_trx(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_type) { + case RSL_MT_ERROR_REPORT: + rc = rsl_rx_error_rep(msg); + break; + case RSL_MT_RF_RES_IND: + /* interference on idle channels of TRX */ + //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(msg->trx)); + break; + case RSL_MT_OVERLOAD: + /* indicate CCCH / ACCH / processor overload */ + LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", + gsm_trx_name(msg->trx)); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " + "type 0x%02x\n", gsm_trx_name(msg->trx), rslh->msg_type); + return -EINVAL; + } + return rc; +} + +/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ +static void t3101_expired(void *data) +{ + struct gsm_lchan *lchan = data; + + rsl_rf_chan_release(lchan); +} + +/* MS has requested a channel on the RACH */ +static int rsl_rx_chan_rqd(struct msgb *msg) +{ + struct gsm_bts *bts = msg->trx->bts; + struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); + struct gsm48_req_ref *rqd_ref; + struct gsm48_imm_ass ia; + enum gsm_chan_t lctype; + enum gsm_chreq_reason_t chreq_reason; + struct gsm_lchan *lchan; + u_int8_t rqd_ta; + int ret; + + u_int16_t arfcn; + u_int8_t ts_number, subch; + + /* parse request reference to be used in immediate assign */ + if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) + return -EINVAL; + + rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; + + /* parse access delay and use as TA */ + if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) + return -EINVAL; + rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; + + /* determine channel type (SDCCH/TCH_F/TCH_H) based on + * request reference RA */ + lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci); + chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci); + + counter_inc(bts->network->stats.chreq.total); + + /* check availability / allocate channel */ + lchan = lchan_alloc(bts, lctype); + if (!lchan) { + LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", + msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); + counter_inc(bts->network->stats.chreq.no_channel); + /* FIXME: send some kind of reject ?!? */ + return -ENOMEM; + } + + if (lchan->state != LCHAN_S_NONE) + LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " + "in state %s\n", gsm_lchan_name(lchan), + gsm_lchans_name(lchan->state)); + lchan->state = LCHAN_S_ACT_REQ; + + ts_number = lchan->ts->nr; + arfcn = lchan->ts->trx->arfcn; + subch = lchan->nr; + + lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */ + lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); + lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ + lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; + lchan->tch_mode = GSM48_CMODE_SIGN; + rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0); + + /* create IMMEDIATE ASSIGN 04.08 messge */ + memset(&ia, 0, sizeof(ia)); + ia.l2_plen = 0x2d; + ia.proto_discr = GSM48_PDISC_RR; + ia.msg_type = GSM48_MT_RR_IMM_ASS; + ia.page_mode = GSM48_PM_SAME; + ia.chan_desc.chan_nr = lchan2chan_nr(lchan); + ia.chan_desc.h0.h = 0; + ia.chan_desc.h0.arfcn_high = arfcn >> 8; + ia.chan_desc.h0.arfcn_low = arfcn & 0xff; + ia.chan_desc.h0.tsc = bts->tsc; + /* use request reference extracted from CHAN_RQD */ + memcpy(&ia.req_ref, rqd_ref, sizeof(ia.req_ref)); + ia.timing_advance = rqd_ta; + ia.mob_alloc_len = 0; + + DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " + "r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch, + gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), + rqd_ref->ra); + + /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ + lchan->T3101.cb = t3101_expired; + lchan->T3101.data = lchan; + bsc_schedule_timer(&lchan->T3101, bts->network->T3101, 0); + + /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */ + ret = rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia); + + return ret; +} + +/* MS has requested a channel on the RACH */ +static int rsl_rx_ccch_load(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + u_int16_t pg_buf_space; + u_int16_t rach_slot_count = -1; + u_int16_t rach_busy_count = -1; + u_int16_t rach_access_count = -1; + + switch (rslh->data[0]) { + case RSL_IE_PAGING_LOAD: + pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; + paging_update_buffer_space(msg->trx->bts, pg_buf_space); + break; + case RSL_IE_RACH_LOAD: + if (msg->data_len >= 7) { + rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; + rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; + rach_access_count = rslh->data[6] << 8 | rslh->data[7]; + } + break; + default: + break; + } + + return 0; +} + +static int abis_rsl_rx_cchan(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + int rc = 0; + + msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); + + switch (rslh->c.msg_type) { + case RSL_MT_CHAN_RQD: + /* MS has requested a channel on the RACH */ + rc = rsl_rx_chan_rqd(msg); + break; + case RSL_MT_CCCH_LOAD_IND: + /* current load on the CCCH */ + rc = rsl_rx_ccch_load(msg); + break; + case RSL_MT_DELETE_IND: + /* CCCH overloaded, IMM_ASSIGN was dropped */ + case RSL_MT_CBCH_LOAD_IND: + /* current load on the CBCH */ + LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " + "type 0x%02x\n", rslh->c.msg_type); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " + "0x%02x\n", rslh->c.msg_type); + return -EINVAL; + } + + return rc; +} + +static int rsl_rx_rll_err_ind(struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + u_int8_t *rlm_cause = rllh->data; + + LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n", + gsm_lchan_name(msg->lchan), + get_value_string(rsl_rlm_cause_strs, rlm_cause[1])); + + rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); + + if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) + return rsl_rf_chan_release(msg->lchan); + + return 0; +} + +/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST + 0x02, 0x06, + 0x01, 0x20, + 0x02, 0x00, + 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ + +static int abis_rsl_rx_rll(struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int rc = 0; + char *ts_name; + u_int8_t sapi = rllh->link_id & 7; + + msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); + + switch (rllh->c.msg_type) { + case RSL_MT_DATA_IND: + DEBUGPC(DRLL, "DATA INDICATION\n"); + if (msgb_l2len(msg) > + sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && + rllh->data[0] == RSL_IE_L3_INFO) { + msg->l3h = &rllh->data[3]; + return gsm0408_rcvmsg(msg, rllh->link_id); + } + break; + case RSL_MT_EST_IND: + DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); + /* lchan is established, stop T3101 */ + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; + bsc_del_timer(&msg->lchan->T3101); + if (msgb_l2len(msg) > + sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && + rllh->data[0] == RSL_IE_L3_INFO) { + msg->l3h = &rllh->data[3]; + return gsm0408_rcvmsg(msg, rllh->link_id); + } + break; + case RSL_MT_EST_CONF: + DEBUGPC(DRLL, "ESTABLISH CONFIRM\n"); + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET; + rll_indication(msg->lchan, rllh->link_id, + BSC_RLLR_IND_EST_CONF); + break; + case RSL_MT_REL_IND: + /* BTS informs us of having received DISC from MS */ + DEBUGPC(DRLL, "RELEASE INDICATION\n"); + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; + rll_indication(msg->lchan, rllh->link_id, + BSC_RLLR_IND_REL_IND); + /* we can now releae the channel on the BTS/Abis side */ + /* FIXME: officially we need to start T3111 and wait for + * some grace period */ + rsl_rf_chan_release(msg->lchan); + break; + case RSL_MT_REL_CONF: + /* BTS informs us of having received UA from MS, + * in response to DISC that we've sent earlier */ + DEBUGPC(DRLL, "RELEASE CONFIRMATION\n"); + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; + /* we can now releae the channel on the BTS/Abis side */ + /* FIXME: officially we need to start T3111 and wait for + * some grace period */ + rsl_rf_chan_release(msg->lchan); + break; + case RSL_MT_ERROR_IND: + rc = rsl_rx_rll_err_ind(msg); + break; + case RSL_MT_UNIT_DATA_IND: + LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); + break; + default: + LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); + } + return rc; +} + +static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) +{ + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x00; + case GSM_LCHAN_TCH_H: + return 0x03; + default: + break; + } + case GSM48_CMODE_SPEECH_EFR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x01; + /* there's no half-rate EFR */ + default: + break; + } + case GSM48_CMODE_SPEECH_AMR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x02; + case GSM_LCHAN_TCH_H: + return 0x05; + default: + break; + } + default: + break; + } + LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " + "tch_mode == 0x%02x\n", lchan->tch_mode); + return 0; +} + +/* ip.access specific RSL extensions */ +static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) +{ + struct in_addr ip; + u_int16_t port, conn_id; + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { + ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_IP)); + DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); + lchan->abis_ip.bound_ip = ntohl(ip.s_addr); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { + port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_PORT)); + port = ntohs(port); + DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); + lchan->abis_ip.bound_port = port; + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { + conn_id = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_CONN_ID)); + conn_id = ntohs(conn_id); + DEBUGPC(DRSL, "CON_ID=%u ", conn_id); + lchan->abis_ip.conn_id = conn_id; + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { + lchan->abis_ip.rtp_payload2 = + *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); + DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", + lchan->abis_ip.rtp_payload2); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { + lchan->abis_ip.speech_mode = + *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); + DEBUGPC(DRSL, "speech_mode=0x%02x ", + lchan->abis_ip.speech_mode); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { + ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_IP)); + DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); + lchan->abis_ip.connect_ip = ntohl(ip.s_addr); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { + port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT)); + port = ntohs(port); + DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); + lchan->abis_ip.connect_port = port; + } +} + +int rsl_ipacc_crcx(struct gsm_lchan *lchan) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); + dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; + dh->chan_nr = lchan2chan_nr(lchan); + + /* 0x1- == receive-only, 0x-1 == EFR codec */ + lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); + msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); + + DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x\n", + gsm_lchan_name(lchan), lchan->abis_ip.speech_mode); + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, + u_int8_t rtp_payload2) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + u_int32_t *att_ip; + struct in_addr ia; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IPAC_MDCX); + dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; + dh->chan_nr = lchan2chan_nr(lchan); + + /* we need to store these now as MDCX_ACK does not return them :( */ + lchan->abis_ip.rtp_payload2 = rtp_payload2; + lchan->abis_ip.connect_port = port; + lchan->abis_ip.connect_ip = ip; + + /* 0x0- == both directions, 0x-1 == EFR codec */ + lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); + + ia.s_addr = htonl(ip); + DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d " + "speech_mode=0x%02x\n", gsm_lchan_name(lchan), inet_ntoa(ia), port, + rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); + + msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); + msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); + att_ip = (u_int32_t *) msgb_put(msg, sizeof(ip)); + *att_ip = ia.s_addr; + msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); + msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); + if (rtp_payload2) + msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* tell BTS to connect RTP stream to our local RTP socket */ +int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan) +{ + struct rtp_socket *rs = lchan->abis_ip.rtp_socket; + int rc; + + rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr), + ntohs(rs->rtp.sin_local.sin_port), + /* FIXME: use RTP payload of bound socket, not BTS*/ + lchan->abis_ip.rtp_payload2); + + return rc; +} + +int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IPAC_PDCH_ACT); + dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + dh->chan_nr = lchan2chan_nr(lchan); + + DEBUGP(DRSL, "%s IPAC_PDCH_ACT\n", gsm_lchan_name(lchan)); + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tv; + struct gsm_lchan *lchan = msg->lchan; + + /* the BTS has acknowledged a local bind, it now tells us the IP + * address and port number to which it has bound the given logical + * channel */ + + rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); + if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || + !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || + !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { + LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); + return -EINVAL; + } + + ipac_parse_rtp(lchan, &tv); + + /* in case we don't use direct BTS-to-BTS RTP */ + if (!ipacc_rtp_direct) { + int rc; + /* the BTS has successfully bound a TCH to a local ip/port, + * which means we can connect our UDP socket to it */ + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } + + lchan->abis_ip.rtp_socket = rtp_socket_create(); + if (!lchan->abis_ip.rtp_socket) + goto out_err; + + rc = rtp_socket_connect(lchan->abis_ip.rtp_socket, + lchan->abis_ip.bound_ip, + lchan->abis_ip.bound_port); + if (rc < 0) + goto out_err; + } + + dispatch_signal(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); + + return 0; +out_err: + return -EIO; +} + +static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tv; + struct gsm_lchan *lchan = msg->lchan; + + /* the BTS has acknowledged a remote connect request and + * it now tells us the IP address and port number to which it has + * connected the given logical channel */ + + rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); + ipac_parse_rtp(lchan, &tv); + dispatch_signal(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); + + return 0; +} + +static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tv; + struct gsm_lchan *lchan = msg->lchan; + + rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) + print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), + TLVP_LEN(&tv, RSL_IE_CAUSE)); + + /* the BTS tells us a RTP stream has been disconnected */ + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } + + dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); + + return 0; +} + +static int abis_rsl_rx_ipacc(struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + char *ts_name; + int rc = 0; + + msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + + switch (rllh->c.msg_type) { + case RSL_MT_IPAC_CRCX_ACK: + DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); + rc = abis_rsl_rx_ipacc_crcx_ack(msg); + break; + case RSL_MT_IPAC_CRCX_NACK: + /* somehow the BTS was unable to bind the lchan to its local + * port?!? */ + LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); + break; + case RSL_MT_IPAC_MDCX_ACK: + /* the BTS tells us that a connect operation was successful */ + DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); + rc = abis_rsl_rx_ipacc_mdcx_ack(msg); + break; + case RSL_MT_IPAC_MDCX_NACK: + /* somehow the BTS was unable to connect the lchan to a remote + * port */ + LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); + break; + case RSL_MT_IPAC_DLCX_IND: + DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); + rc = abis_rsl_rx_ipacc_dlcx_ind(msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", + rllh->c.msg_type); + break; + } + DEBUGPC(DRSL, "\n"); + + return rc; +} + + +/* Entry-point where L2 RSL from BTS enters */ +int abis_rsl_rcvmsg(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg) ; + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = abis_rsl_rx_rll(msg); + break; + case ABIS_RSL_MDISC_DED_CHAN: + rc = abis_rsl_rx_dchan(msg); + break; + case ABIS_RSL_MDISC_COM_CHAN: + rc = abis_rsl_rx_cchan(msg); + break; + case ABIS_RSL_MDISC_TRX: + rc = abis_rsl_rx_trx(msg); + break; + case ABIS_RSL_MDISC_LOC: + LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", + rslh->msg_discr); + break; + case ABIS_RSL_MDISC_IPACCESS: + rc = abis_rsl_rx_ipacc(msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " + "0x%02x\n", rslh->msg_discr); + return -EINVAL; + } + msgb_free(msg); + return rc; +} + +/* From Table 10.5.33 of GSM 04.08 */ +int rsl_number_of_paging_subchannels(struct gsm_bts *bts) +{ + if (bts->si_common.chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) { + return MAX(1, (3 - bts->si_common.chan_desc.bs_ag_blks_res)) + * (bts->si_common.chan_desc.bs_pa_mfrms + 2); + } else { + return (9 - bts->si_common.chan_desc.bs_ag_blks_res) + * (bts->si_common.chan_desc.bs_pa_mfrms + 2); + } +} diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c new file mode 100644 index 000000000..80f9ba956 --- /dev/null +++ b/openbsc/src/bs11_config.c @@ -0,0 +1,872 @@ +/* Siemens BS-11 microBTS configuration tool */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This software is based on ideas (but not code) of BS11Config + * (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de> + * + * 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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <fcntl.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/abis_nm.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <openbsc/debug.h> +#include <osmocore/select.h> +#include <openbsc/rs232.h> + +/* state of our bs11_config application */ +enum bs11cfg_state { + STATE_NONE, + STATE_LOGON_WAIT, + STATE_LOGON_ACK, + STATE_SWLOAD, + STATE_QUERY, +}; +static enum bs11cfg_state bs11cfg_state = STATE_NONE; +static char *command, *value; +struct timer_list status_timer; + +static const u_int8_t obj_li_attr[] = { + NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, + NM_ATT_BS11_L1_PROT_TYPE, 0x00, + NM_ATT_BS11_LINE_CFG, 0x00, +}; +static const u_int8_t obj_bbsig0_attr[] = { + NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, + NM_ATT_BS11_DIVERSITY, 0x01, 0x00, +}; +static const u_int8_t obj_pa0_attr[] = { + NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, +}; +static const char *trx1_password = "1111111111"; +#define TEI_OML 25 + +static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; + +static struct debug_target *stderr_target; + +/* dummy function to keep gsm_data.c happy */ +struct counter *counter_alloc(const char *name) +{ + return NULL; +} + +int handle_serial_msg(struct msgb *rx_msg); + +/* create all objects for an initial configuration */ +static int create_objects(struct gsm_bts *bts) +{ + fprintf(stdout, "Crating Objects for minimal config\n"); + abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), + obj_li_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, + sizeof(obj_bbsig0_attr), obj_bbsig0_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, + sizeof(obj_pa0_attr), obj_pa0_attr); + abis_nm_bs11_create_envaBTSE(bts, 0); + abis_nm_bs11_create_envaBTSE(bts, 1); + abis_nm_bs11_create_envaBTSE(bts, 2); + abis_nm_bs11_create_envaBTSE(bts, 3); + + abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); + + abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); + + sleep(1); + + abis_nm_bs11_set_trx1_pw(bts, trx1_password); + + sleep(1); + + return 0; +} + +static int create_trx1(struct gsm_bts *bts) +{ + u_int8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; + u_int8_t *cur = bbsig1_attr; + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); + + if (!trx) + trx = gsm_bts_trx_alloc(bts); + + fprintf(stdout, "Crating Objects for TRX1\n"); + + abis_nm_bs11_set_trx1_pw(bts, trx1_password); + + sleep(1); + + cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, + (u_int8_t *)trx1_password); + memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); + abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, + sizeof(bbsig1_attr), bbsig1_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, + sizeof(obj_pa0_attr), obj_pa0_attr); + abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); + + return 0; +} + +static char *serial_port = "/dev/ttyUSB0"; +static char *fname_safety = "BTSBMC76.SWI"; +static char *fname_software = "HS011106.SWL"; +static int delay_ms = 0; +static int win_size = 8; +static int param_disconnect = 0; +static int param_restart = 0; +static int param_forced = 0; +static struct gsm_bts *g_bts; + +static int file_is_readable(const char *fname) +{ + int rc; + struct stat st; + + rc = stat(fname, &st); + if (rc < 0) + return 0; + + if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) + return 1; + + return 0; +} + +static int percent; +static int percent_old; + +/* callback function passed to the ABIS OML code */ +static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, + void *data, void *param) +{ + if (hook != GSM_HOOK_NM_SWLOAD) + return 0; + + switch (event) { + case NM_MT_LOAD_INIT_ACK: + fprintf(stdout, "Software Load Initiate ACK\n"); + break; + case NM_MT_LOAD_INIT_NACK: + fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); + exit(5); + break; + case NM_MT_LOAD_END_ACK: + if (data) { + /* we did a safety load and must activate it */ + abis_nm_software_activate(g_bts, fname_safety, + swload_cbfn, g_bts); + sleep(5); + } + break; + case NM_MT_LOAD_END_NACK: + fprintf(stderr, "ERROR: Software Load End NACK\n"); + exit(3); + break; + case NM_MT_ACTIVATE_SW_NACK: + fprintf(stderr, "ERROR: Activate Software NACK\n"); + exit(4); + break; + case NM_MT_ACTIVATE_SW_ACK: + bs11cfg_state = STATE_NONE; + + break; + case NM_MT_LOAD_SEG_ACK: + percent = abis_nm_software_load_status(g_bts); + if (percent > percent_old) + printf("Software Download Progress: %d%%\n", percent); + percent_old = percent; + break; + } + return 0; +} + +static const char *bs11_link_state[] = { + [0x00] = "Down", + [0x01] = "Up", + [0x02] = "Restoring", +}; + +static const char *linkstate_name(u_int8_t linkstate) +{ + if (linkstate > ARRAY_SIZE(bs11_link_state)) + return "Unknown"; + + return bs11_link_state[linkstate]; +} + +static const char *mbccu_load[] = { + [0] = "No Load", + [1] = "Load BTSCAC", + [2] = "Load BTSDRX", + [3] = "Load BTSBBX", + [4] = "Load BTSARC", + [5] = "Load", +}; + +static const char *mbccu_load_name(u_int8_t linkstate) +{ + if (linkstate > ARRAY_SIZE(mbccu_load)) + return "Unknown"; + + return mbccu_load[linkstate]; +} + +static const char *bts_phase_name(u_int8_t phase) +{ + switch (phase) { + case BS11_STATE_WARM_UP: + case BS11_STATE_WARM_UP_2: + return "Warm Up"; + break; + case BS11_STATE_LOAD_SMU_SAFETY: + return "Load SMU Safety"; + break; + case BS11_STATE_LOAD_SMU_INTENDED: + return "Load SMU Intended"; + break; + case BS11_STATE_LOAD_MBCCU: + return "Load MBCCU"; + break; + case BS11_STATE_SOFTWARE_RQD: + return "Software required"; + break; + case BS11_STATE_WAIT_MIN_CFG: + case BS11_STATE_WAIT_MIN_CFG_2: + return "Wait minimal config"; + break; + case BS11_STATE_MAINTENANCE: + return "Maintenance"; + break; + case BS11_STATE_NORMAL: + return "Normal"; + break; + case BS11_STATE_ABIS_LOAD: + return "Abis load"; + break; + default: + return "Unknown"; + break; + } +} + +static const char *trx_power_name(u_int8_t pwr) +{ + switch (pwr) { + case BS11_TRX_POWER_GSM_2W: + return "2W (GSM)"; + case BS11_TRX_POWER_GSM_250mW: + return "250mW (GSM)"; + case BS11_TRX_POWER_GSM_80mW: + return "80mW (GSM)"; + case BS11_TRX_POWER_GSM_30mW: + return "30mW (GSM)"; + case BS11_TRX_POWER_DCS_3W: + return "3W (DCS)"; + case BS11_TRX_POWER_DCS_1W6: + return "1.6W (DCS)"; + case BS11_TRX_POWER_DCS_500mW: + return "500mW (DCS)"; + case BS11_TRX_POWER_DCS_160mW: + return "160mW (DCS)"; + default: + return "unknown value"; + } +} + +static const char *pll_mode_name(u_int8_t mode) +{ + switch (mode) { + case BS11_LI_PLL_LOCKED: + return "E1 Locked"; + case BS11_LI_PLL_STANDALONE: + return "Standalone"; + default: + return "unknown"; + } +} + +static const char *cclk_acc_name(u_int8_t acc) +{ + switch (acc) { + case 0: + /* Out of the demanded +/- 0.05ppm */ + return "Medium"; + case 1: + /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ + return "High"; + default: + return "unknown"; + } +} + +static const char *obj_name(struct abis_om_fom_hdr *foh) +{ + static char retbuf[256]; + + retbuf[0] = 0; + + switch (foh->obj_class) { + case NM_OC_BS11: + strcat(retbuf, "BS11 "); + switch (foh->obj_inst.bts_nr) { + case BS11_OBJ_PA: + sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", + foh->obj_inst.ts_nr); + break; + case BS11_OBJ_LI: + sprintf(retbuf+strlen(retbuf), "Line Interface "); + break; + case BS11_OBJ_CCLK: + sprintf(retbuf+strlen(retbuf), "CCLK "); + break; + } + break; + case NM_OC_SITE_MANAGER: + strcat(retbuf, "SITE MANAGER "); + break; + } + return retbuf; +} + +static void print_state(struct tlv_parsed *tp) +{ + if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { + u_int8_t phase, mbccu; + if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { + phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); + printf("PHASE: %u %-20s ", phase & 0xf, + bts_phase_name(phase)); + } + if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { + mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); + printf("MBCCU0: %-11s MBCCU1: %-11s ", + mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); + } + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && + TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { + u_int8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); + printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); + } + printf("\n"); +} + +static int print_attr(struct tlv_parsed *tp) +{ + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { + printf("\tBS-11 ESN PCB Serial Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { + printf("\tBS-11 ESN Hardware Code Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { + printf("\tBS-11 ESN Firmware Code Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); + } +#if 0 + if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { + printf("BS-11 Boot Software Version: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); + } +#endif + if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && + TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { + const u_int8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); + printf("\tE1 Channel: Port=%u Timeslot=%u ", + chan[0], chan[1]); + if (chan[2] == 0xff) + printf("(Full Slot)\n"); + else + printf("Subslot=%u\n", chan[2]); + } + if (TLVP_PRESENT(tp, NM_ATT_TEI)) + printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); + if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && + TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { + printf("\tTRX Power: %s\n", + trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && + TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { + printf("\tPLL Mode: %s\n", + pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && + TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { + const u_int8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); + printf("\tPLL Set Value=%d, Work Value=%d\n", + vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && + TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { + const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); + printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && + TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { + const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); + printf("\tCCLK Type=%d\n", *acc); + } + + + return 0; +} + +static void cmd_query(void) +{ + struct gsm_bts_trx *trx = g_bts->c0; + + bs11cfg_state = STATE_QUERY; + abis_nm_bs11_get_serno(g_bts); + abis_nm_bs11_get_oml_tei_ts(g_bts); + abis_nm_bs11_get_pll_mode(g_bts); + abis_nm_bs11_get_cclk(g_bts); + abis_nm_bs11_get_trx_power(trx); + trx = gsm_bts_trx_num(g_bts, 1); + if (trx) + abis_nm_bs11_get_trx_power(trx); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; +} + +/* handle a response from the BTS to a GET STATE command */ +static int handle_state_resp(enum abis_bs11_phase state) +{ + int rc = 0; + + switch (state) { + case BS11_STATE_WARM_UP: + case BS11_STATE_LOAD_SMU_SAFETY: + case BS11_STATE_LOAD_SMU_INTENDED: + case BS11_STATE_LOAD_MBCCU: + break; + case BS11_STATE_SOFTWARE_RQD: + bs11cfg_state = STATE_SWLOAD; + /* send safety load. Use g_bts as private 'param' + * argument, so our swload_cbfn can distinguish + * a safety load from a regular software */ + if (file_is_readable(fname_safety)) + rc = abis_nm_software_load(g_bts, fname_safety, + win_size, param_forced, + swload_cbfn, g_bts); + else + fprintf(stderr, "No valid Safety Load file \"%s\"\n", + fname_safety); + break; + case BS11_STATE_WAIT_MIN_CFG: + case BS11_STATE_WAIT_MIN_CFG_2: + bs11cfg_state = STATE_SWLOAD; + rc = create_objects(g_bts); + break; + case BS11_STATE_MAINTENANCE: + if (command) { + if (!strcmp(command, "disconnect")) + abis_nm_bs11_factory_logon(g_bts, 0); + else if (!strcmp(command, "reconnect")) + rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); + else if (!strcmp(command, "software") + && bs11cfg_state != STATE_SWLOAD) { + bs11cfg_state = STATE_SWLOAD; + /* send software (FIXME: over A-bis?) */ + if (file_is_readable(fname_software)) + rc = abis_nm_bs11_load_swl(g_bts, fname_software, + win_size, param_forced, + swload_cbfn); + else + fprintf(stderr, "No valid Software file \"%s\"\n", + fname_software); + } else if (!strcmp(command, "delete-trx1")) { + printf("Locing BBSIG and PA objects of TRX1\n"); + abis_nm_chg_adm_state(g_bts, NM_OC_BS11, + BS11_OBJ_BBSIG, 0, 1, + NM_STATE_LOCKED); + abis_nm_chg_adm_state(g_bts, NM_OC_BS11, + BS11_OBJ_PA, 0, 1, + NM_STATE_LOCKED); + sleep(1); + printf("Deleting BBSIG and PA objects of TRX1\n"); + abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); + abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "create-trx1")) { + create_trx1(g_bts); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-e1-locked")) { + abis_nm_bs11_set_pll_locked(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-standalone")) { + abis_nm_bs11_set_pll_locked(g_bts, 0); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-setvalue")) { + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-workvalue")) { + /* To set the work value we need to login as FIELD */ + abis_nm_bs11_factory_logon(g_bts, 0); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 1); + sleep(1); + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "oml-tei")) { + abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); + command = NULL; + } else if (!strcmp(command, "restart")) { + abis_nm_bs11_restart(g_bts); + command = NULL; + } else if (!strcmp(command, "query")) { + cmd_query(); + } else if (!strcmp(command, "create-bport1")) { + abis_nm_bs11_create_bport(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "delete-bport1")) { + abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); + sleep(1); + abis_nm_bs11_delete_bport(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport0-star")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport0-multidrop")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } + } + break; + case BS11_STATE_NORMAL: + if (command) { + if (!strcmp(command, "reconnect")) + abis_nm_bs11_factory_logon(g_bts, 0); + else if (!strcmp(command, "disconnect")) + abis_nm_bs11_bsc_disconnect(g_bts, 0); + else if (!strcmp(command, "query")) { + cmd_query(); + } + } else if (param_disconnect) { + param_disconnect = 0; + abis_nm_bs11_bsc_disconnect(g_bts, 0); + if (param_restart) { + param_restart = 0; + abis_nm_bs11_restart(g_bts); + } + } + break; + default: + break; + } + return rc; +} + +/* handle a fully-received message/packet from the RS232 port */ +int handle_serial_msg(struct msgb *rx_msg) +{ + struct abis_om_hdr *oh; + struct abis_om_fom_hdr *foh; + struct tlv_parsed tp; + int rc = -1; + +#if 0 + if (rx_msg->len < LAPD_HDR_LEN + + sizeof(struct abis_om_fom_hdr) + + sizeof(struct abis_om_hdr)) { + if (!memcmp(rx_msg->data + 2, too_fast, + sizeof(too_fast))) { + fprintf(stderr, "BS11 tells us we're too " + "fast, try --delay bigger than %u\n", + delay_ms); + return -E2BIG; + } else + fprintf(stderr, "unknown BS11 message\n"); + } +#endif + + oh = (struct abis_om_hdr *) msgb_l2(rx_msg); + foh = (struct abis_om_fom_hdr *) oh->data; + switch (foh->msg_type) { + case NM_MT_BS11_LMT_LOGON_ACK: + printf("LMT LOGON: ACK\n\n"); + if (bs11cfg_state == STATE_NONE) + bs11cfg_state = STATE_LOGON_ACK; + rc = abis_nm_bs11_get_state(g_bts); + break; + case NM_MT_BS11_LMT_LOGOFF_ACK: + printf("LMT LOGOFF: ACK\n"); + exit(0); + break; + case NM_MT_BS11_GET_STATE_ACK: + rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); + print_state(&tp); + if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && + TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) + rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); + break; + case NM_MT_GET_ATTR_RESP: + printf("\n%sATTRIBUTES:\n", obj_name(foh)); + abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); + rc = print_attr(&tp); + //hexdump(foh->data, oh->length-sizeof(*foh)); + break; + case NM_MT_BS11_SET_ATTR_ACK: + printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", + foh->obj_class, foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); + rc = 0; + break; + case NM_MT_BS11_SET_ATTR_NACK: + printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", + foh->obj_class, foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); + break; + default: + rc = abis_nm_rcvmsg(rx_msg); + } + if (rc < 0) { + perror("ERROR in main loop"); + //break; + } + if (rc == 1) + return rc; + + switch (bs11cfg_state) { + case STATE_NONE: + abis_nm_bs11_factory_logon(g_bts, 1); + break; + case STATE_LOGON_ACK: + bsc_schedule_timer(&status_timer, 5, 0); + break; + default: + break; + } + + return rc; +} + +int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) +{ + return 0; +} + +void status_timer_cb(void *data) +{ + abis_nm_bs11_get_state(g_bts); +} + +static void print_banner(void) +{ + printf("bs11_config (C) 2009 by Harald Welte and Dieter Spaar\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); +} + +static void print_help(void) +{ + printf("bs11_config [options] [command]\n"); + printf("\nSupported options:\n"); + printf("\t-h --help\t\t\tPrint this help text\n"); + printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n"); + printf("\t-s --software <file>\t\tSpecify Software file\n"); + printf("\t-S --safety <file>\t\tSpecify Safety Load file\n"); + printf("\t-d --delay <ms>\t\t\tSpecify delay in milliseconds\n"); + printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); + printf("\t-w --win-size <num>\t\tSpecify Window Size\n"); + printf("\t-f --forced\t\t\tForce Software Load\n"); + printf("\nSupported commands:\n"); + printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); + printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); + printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); + printf("\trestart\t\t\tRestart the BTS\n"); + printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); + printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); + printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); + printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); + printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); + printf("\tpll-setvalue <value>\tSet the PLL set value\n"); + printf("\tpll-workvalue <value>\tSet the PLL work value\n"); + printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); + printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); + printf("\tbport0-multiport\tSet BPORT0 line config to multiport\n"); + printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); + printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); +} + +static void handle_options(int argc, char **argv) +{ + int option_index = 0; + print_banner(); + + while (1) { + int c; + static struct option long_options[] = { + { "help", 0, 0, 'h' }, + { "port", 1, 0, 'p' }, + { "software", 1, 0, 's' }, + { "safety", 1, 0, 'S' }, + { "delay", 1, 0, 'd' }, + { "disconnect", 0, 0, 'D' }, + { "win-size", 1, 0, 'w' }, + { "forced", 0, 0, 'f' }, + { "restart", 0, 0, 'r' }, + { "debug", 1, 0, 'b'}, + }; + + c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + case 'p': + serial_port = optarg; + break; + case 'b': + debug_parse_category_mask(stderr_target, optarg); + break; + case 's': + fname_software = optarg; + break; + case 'S': + fname_safety = optarg; + break; + case 'd': + delay_ms = atoi(optarg); + break; + case 'w': + win_size = atoi(optarg); + break; + case 'D': + param_disconnect = 1; + break; + case 'f': + param_forced = 1; + break; + case 'r': + param_disconnect = 1; + param_restart = 1; + break; + default: + break; + } + } + if (optind < argc) + command = argv[optind]; + if (optind+1 < argc) + value = argv[optind+1]; + +} + +static int num_sigint; + +static void signal_handler(int signal) +{ + fprintf(stdout, "\nsignal %u received\n", signal); + + switch (signal) { + case SIGINT: + num_sigint++; + abis_nm_bs11_factory_logon(g_bts, 0); + if (num_sigint >= 3) + exit(0); + break; + } +} + +int main(int argc, char **argv) +{ + struct gsm_network *gsmnet; + int rc; + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + handle_options(argc, argv); + bts_model_bs11_init(); + + gsmnet = gsm_network_init(1, 1, NULL); + if (!gsmnet) { + fprintf(stderr, "Unable to allocate gsm network\n"); + exit(1); + } + g_bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_BS11, HARDCODED_TSC, + HARDCODED_BSIC); + + rc = rs232_setup(serial_port, delay_ms, g_bts); + if (rc < 0) { + fprintf(stderr, "Problem setting up serial port\n"); + exit(1); + } + + signal(SIGINT, &signal_handler); + + abis_nm_bs11_factory_logon(g_bts, 1); + //abis_nm_bs11_get_serno(g_bts); + + status_timer.cb = status_timer_cb; + + while (1) { + bsc_select_main(0); + } + + abis_nm_bs11_factory_logon(g_bts, 0); + + exit(0); +} diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c new file mode 100644 index 000000000..49c9d36ef --- /dev/null +++ b/openbsc/src/bsc_hack.c @@ -0,0 +1,243 @@ +/* A hackish minimal BSC (+MSC +HLR) implementation */ + +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <unistd.h> +#include <time.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/stat.h> + +#define _GNU_SOURCE +#include <getopt.h> + +#include <openbsc/db.h> +#include <osmocore/select.h> +#include <openbsc/debug.h> +#include <openbsc/e1_input.h> +#include <osmocore/talloc.h> +#include <openbsc/signal.h> + +/* MCC and MNC for the Location Area Identifier */ +static struct debug_target *stderr_target; +struct gsm_network *bsc_gsmnet = 0; +static const char *database_name = "hlr.sqlite3"; +static const char *config_file = "openbsc.cfg"; + + +/* timer to store statistics */ +#define DB_SYNC_INTERVAL 60, 0 +static struct timer_list db_sync_timer; + +extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *), + const char *cfg_file); +extern int bsc_shutdown_net(struct gsm_network *net); + +static void create_pcap_file(char *file) +{ + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); + + if (fd < 0) { + perror("Failed to open file for pcap"); + return; + } + + e1_set_pcap_fd(fd); +} + +static void print_usage() +{ + printf("Usage: bsc_hack\n"); +} + +static void print_help() +{ + printf(" Some useful help...\n"); + printf(" -h --help this text\n"); + printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); + printf(" -s --disable-color\n"); + printf(" -c --config-file filename The config file to use.\n"); + printf(" -l --database db-name The database to use\n"); + printf(" -p --pcap file The filename of the pcap file\n"); + printf(" -T --timestamp Prefix every log line with a timestamp\n"); + printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n"); +} + +static void handle_options(int argc, char** argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"config-file", 1, 0, 'c'}, + {"disable-color", 0, 0, 's'}, + {"database", 1, 0, 'l'}, + {"authorize-everyone", 0, 0, 'a'}, + {"pcap", 1, 0, 'p'}, + {"timestamp", 0, 0, 'T'}, + {"rtp-proxy", 0, 0, 'P'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:sl:ar:p:TPc:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 's': + debug_set_use_color(stderr_target, 0); + break; + case 'd': + debug_parse_category_mask(stderr_target, optarg); + break; + case 'l': + database_name = strdup(optarg); + break; + case 'c': + config_file = strdup(optarg); + break; + case 'p': + create_pcap_file(optarg); + break; + case 'T': + debug_set_print_timestamp(stderr_target, 1); + break; + case 'P': + ipacc_rtp_direct = 0; + break; + default: + /* ignore */ + break; + } + } +} + +extern void *tall_vty_ctx; +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + bsc_shutdown_net(bsc_gsmnet); + dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL); + sleep(3); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_bsc_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; + default: + break; + } +} + +/* timer handling */ +static int _db_store_counter(struct counter *counter, void *data) +{ + return db_store_counter(counter); +} + +static void db_sync_timer_cb(void *data) +{ + /* store counters to database and re-schedule */ + counters_for_each(_db_store_counter, NULL); + bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); +} + +extern int bts_model_unknown_init(void); +extern int bts_model_bs11_init(void); +extern int bts_model_nanobts_init(void); + +int main(int argc, char **argv) +{ + int rc; + + debug_init(); + tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); + talloc_ctx_init(); + on_dso_load_token(); + on_dso_load_rrlp(); + on_dso_load_ho_dec(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + + bts_model_unknown_init(); + bts_model_bs11_init(); + bts_model_nanobts_init(); + + /* enable filters */ + debug_set_all_filter(stderr_target, 1); + + /* parse options */ + handle_options(argc, argv); + + /* seed the PRNG */ + srand(time(NULL)); + + if (db_init(database_name)) { + printf("DB: Failed to init database. Please check the option settings.\n"); + return -1; + } + printf("DB: Database initialized.\n"); + + if (db_prepare()) { + printf("DB: Failed to prepare database.\n"); + return -1; + } + printf("DB: Database prepared.\n"); + + /* setup the timer */ + db_sync_timer.cb = db_sync_timer_cb; + db_sync_timer.data = NULL; + bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); + + rc = bsc_bootstrap_network(mncc_recv, config_file); + if (rc < 0) + exit(1); + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + signal(SIGPIPE, SIG_IGN); + + while (1) { + bsc_upqueue(bsc_gsmnet); + debug_reset_context(); + bsc_select_main(0); + } +} diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c new file mode 100644 index 000000000..f3436621f --- /dev/null +++ b/openbsc/src/bsc_init.c @@ -0,0 +1,1016 @@ +/* A hackish minimal BSC (+MSC +HLR) implementation */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <openbsc/gsm_data.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/abis_nm.h> +#include <openbsc/debug.h> +#include <openbsc/misdn.h> +#include <openbsc/telnet_interface.h> +#include <openbsc/system_information.h> +#include <openbsc/paging.h> +#include <openbsc/signal.h> +#include <osmocore/talloc.h> + +/* global pointer to the gsm network data structure */ +extern struct gsm_network *bsc_gsmnet; + +static void patch_nm_tables(struct gsm_bts *bts); + +/* The following definitions are for OM and NM packets that we cannot yet + * generate by code but we just pass on */ + +// BTS Site Manager, SET ATTRIBUTES + +/* + Object Class: BTS Site Manager + Instance 1: FF + Instance 2: FF + Instance 3: FF +SET ATTRIBUTES + sAbisExternalTime: 2007/09/08 14:36:11 + omLAPDRelTimer: 30sec + shortLAPDIntTimer: 5sec + emergencyTimer1: 10 minutes + emergencyTimer2: 0 minutes +*/ + +unsigned char msg_1[] = +{ + NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xFF, 0xFF, 0xFF, + NM_ATT_BS11_ABIS_EXT_TIME, 0x07, + 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE, + 0x02, + 0x00, 0x1E, + NM_ATT_BS11_SH_LAPD_INT_TIMER, + 0x01, 0x05, + 0x42, 0x02, 0x00, 0x0A, + 0x44, 0x02, 0x00, 0x00 +}; + +// BTS, SET BTS ATTRIBUTES + +/* + Object Class: BTS + BTS relat. Number: 0 + Instance 2: FF + Instance 3: FF +SET BTS ATTRIBUTES + bsIdentityCode / BSIC: + PLMN_colour_code: 7h + BS_colour_code: 7h + BTS Air Timer T3105: 4 ,unit 10 ms + btsIsHopping: FALSE + periodCCCHLoadIndication: 1sec + thresholdCCCHLoadIndication: 0% + cellAllocationNumber: 00h = GSM 900 + enableInterferenceClass: 00h = Disabled + fACCHQual: 6 (FACCH stealing flags minus 1) + intaveParameter: 31 SACCH multiframes + interferenceLevelBoundaries: + Interference Boundary 1: 0Ah + Interference Boundary 2: 0Fh + Interference Boundary 3: 14h + Interference Boundary 4: 19h + Interference Boundary 5: 1Eh + mSTxPwrMax: 11 + GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm + DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm + PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm + 30=33dBm, 31=32dBm + ny1: + Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20 + powerOutputThresholds: + Out Power Fault Threshold: -10 dB + Red Out Power Threshold: - 6 dB + Excessive Out Power Threshold: 5 dB + rACHBusyThreshold: -127 dBm + rACHLoadAveragingSlots: 250 ,number of RACH burst periods + rfResourceIndicationPeriod: 125 SACCH multiframes + T200: + SDCCH: 044 in 5 ms + FACCH/Full rate: 031 in 5 ms + FACCH/Half rate: 041 in 5 ms + SACCH with TCH SAPI0: 090 in 10 ms + SACCH with SDCCH: 090 in 10 ms + SDCCH with SAPI3: 090 in 5 ms + SACCH with TCH SAPI3: 135 in 10 ms + tSync: 9000 units of 10 msec + tTrau: 9000 units of 10 msec + enableUmLoopTest: 00h = disabled + enableExcessiveDistance: 00h = Disabled + excessiveDistance: 64km + hoppingMode: 00h = baseband hopping + cellType: 00h = Standard Cell + BCCH ARFCN / bCCHFrequency: 1 +*/ + +static unsigned char bs11_attr_bts[] = +{ + NM_ATT_BSIC, HARDCODED_BSIC, + NM_ATT_BTS_AIR_TIMER, 0x04, + NM_ATT_BS11_BTSLS_HOPPING, 0x00, + NM_ATT_CCCH_L_I_P, 0x01, + NM_ATT_CCCH_L_T, 0x00, + NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM, + NM_ATT_BS11_ENA_INTERF_CLASS, 0x01, + NM_ATT_BS11_FACCH_QUAL, 0x06, + /* interference avg. period in numbers of SACCH multifr */ + NM_ATT_INTAVE_PARAM, 0x1F, + NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, + NM_ATT_CCCH_L_T, 0x23, + NM_ATT_GSM_TIME, 0x28, 0x00, + NM_ATT_ADM_STATE, 0x03, + NM_ATT_RACH_B_THRESH, 0x7F, + NM_ATT_LDAVG_SLOTS, 0x00, 0xFA, + NM_ATT_BS11_RF_RES_IND_PER, 0x7D, + NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87, + NM_ATT_BS11_TSYNC, 0x23, 0x28, + NM_ATT_BS11_TTRAU, 0x23, 0x28, + NM_ATT_TEST_DUR, 0x01, 0x00, + NM_ATT_OUTST_ALARM, 0x01, 0x00, + NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40, + NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00, + NM_ATT_BS11_PLL, 0x01, 0x00, + NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/, +}; + +// Handover Recognition, SET ATTRIBUTES + +/* +Illegal Contents GSM Formatted O&M Msg + Object Class: Handover Recognition + BTS relat. Number: 0 + Instance 2: FF + Instance 3: FF +SET ATTRIBUTES + enableDelayPowerBudgetHO: 00h = Disabled + enableDistanceHO: 00h = Disabled + enableInternalInterCellHandover: 00h = Disabled + enableInternalIntraCellHandover: 00h = Disabled + enablePowerBudgetHO: 00h = Disabled + enableRXLEVHO: 00h = Disabled + enableRXQUALHO: 00h = Disabled + hoAveragingDistance: 8 SACCH multiframes + hoAveragingLev: + A_LEV_HO: 8 SACCH multiframes + W_LEV_HO: 1 SACCH multiframes + hoAveragingPowerBudget: 16 SACCH multiframes + hoAveragingQual: + A_QUAL_HO: 8 SACCH multiframes + W_QUAL_HO: 2 SACCH multiframes + hoLowerThresholdLevDL: (10 - 110) dBm + hoLowerThresholdLevUL: (5 - 110) dBm + hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8% + hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8% + hoThresholdLevDLintra : (20 - 110) dBm + hoThresholdLevULintra: (20 - 110) dBm + hoThresholdMsRangeMax: 20 km + nCell: 06h + timerHORequest: 3 ,unit 2 SACCH multiframes +*/ + +unsigned char msg_3[] = +{ + NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, + 0xD0, 0x00, /* enableDelayPowerBudgetHO */ + 0x64, 0x00, /* enableDistanceHO */ + 0x67, 0x00, /* enableInternalInterCellHandover */ + 0x68, 0x00, /* enableInternalInterCellHandover */ + 0x6A, 0x00, /* enablePowerBudgetHO */ + 0x6C, 0x00, /* enableRXLEVHO */ + 0x6D, 0x00, /* enableRXQUALHO */ + 0x6F, 0x08, /* hoAveragingDistance */ + 0x70, 0x08, 0x01, /* hoAveragingLev */ + 0x71, 0x10, 0x10, 0x10, + 0x72, 0x08, 0x02, /* hoAveragingQual */ + 0x73, 0x0A, /* hoLowerThresholdLevDL */ + 0x74, 0x05, /* hoLowerThresholdLevUL */ + 0x75, 0x06, /* hoLowerThresholdQualDL */ + 0x76, 0x06, /* hoLowerThresholdQualUL */ + 0x78, 0x14, /* hoThresholdLevDLintra */ + 0x79, 0x14, /* hoThresholdLevULintra */ + 0x7A, 0x14, /* hoThresholdMsRangeMax */ + 0x7D, 0x06, /* nCell */ + NM_ATT_BS11_TIMER_HO_REQUEST, 0x03, + 0x20, 0x01, 0x00, + 0x45, 0x01, 0x00, + 0x48, 0x01, 0x00, + 0x5A, 0x01, 0x00, + 0x5B, 0x01, 0x05, + 0x5E, 0x01, 0x1A, + 0x5F, 0x01, 0x20, + 0x9D, 0x01, 0x00, + 0x47, 0x01, 0x00, + 0x5C, 0x01, 0x64, + 0x5D, 0x01, 0x1E, + 0x97, 0x01, 0x20, + 0xF7, 0x01, 0x3C, +}; + +// Power Control, SET ATTRIBUTES + +/* + Object Class: Power Control + BTS relat. Number: 0 + Instance 2: FF + Instance 3: FF +SET ATTRIBUTES + enableMsPowerControl: 00h = Disabled + enablePowerControlRLFW: 00h = Disabled + pcAveragingLev: + A_LEV_PC: 4 SACCH multiframes + W_LEV_PC: 1 SACCH multiframes + pcAveragingQual: + A_QUAL_PC: 4 SACCH multiframes + W_QUAL_PC: 2 SACCH multiframes + pcLowerThresholdLevDL: 0Fh + pcLowerThresholdLevUL: 0Ah + pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4% + pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4% + pcRLFThreshold: 0Ch + pcUpperThresholdLevDL: 14h + pcUpperThresholdLevUL: 0Fh + pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2% + pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2% + powerConfirm: 2 ,unit 2 SACCH multiframes + powerControlInterval: 2 ,unit 2 SACCH multiframes + powerIncrStepSize: 02h = 4 dB + powerRedStepSize: 01h = 2 dB + radioLinkTimeoutBs: 64 SACCH multiframes + enableBSPowerControl: 00h = disabled +*/ + +unsigned char msg_4[] = +{ + NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, + NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00, + NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00, + 0x7E, 0x04, 0x01, /* pcAveragingLev */ + 0x7F, 0x04, 0x02, /* pcAveragingQual */ + 0x80, 0x0F, /* pcLowerThresholdLevDL */ + 0x81, 0x0A, /* pcLowerThresholdLevUL */ + 0x82, 0x05, /* pcLowerThresholdQualDL */ + 0x83, 0x05, /* pcLowerThresholdQualUL */ + 0x84, 0x0C, /* pcRLFThreshold */ + 0x85, 0x14, /* pcUpperThresholdLevDL */ + 0x86, 0x0F, /* pcUpperThresholdLevUL */ + 0x87, 0x04, /* pcUpperThresholdQualDL */ + 0x88, 0x04, /* pcUpperThresholdQualUL */ + 0x89, 0x02, /* powerConfirm */ + 0x8A, 0x02, /* powerConfirmInterval */ + 0x8B, 0x02, /* powerIncrStepSize */ + 0x8C, 0x01, /* powerRedStepSize */ + 0x8D, 0x40, /* radioLinkTimeoutBs */ + 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl +}; + + +// Transceiver, SET TRX ATTRIBUTES (TRX 0) + +/* + Object Class: Transceiver + BTS relat. Number: 0 + Tranceiver number: 0 + Instance 3: FF +SET TRX ATTRIBUTES + aRFCNList (HEX): 0001 + txPwrMaxReduction: 00h = 30dB + radioMeasGran: 254 SACCH multiframes + radioMeasRep: 01h = enabled + memberOfEmergencyConfig: 01h = TRUE + trxArea: 00h = TRX doesn't belong to a concentric cell +*/ + +static unsigned char bs11_attr_radio[] = +{ + NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, + NM_ATT_RF_MAXPOWR_R, 0x00, + NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0x05, + NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01, + NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01, + NM_ATT_BS11_TRX_AREA, 0x01, 0x00, +}; + +static unsigned char nanobts_attr_bts[] = { + NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, + /* interference avg. period in numbers of SACCH multifr */ + NM_ATT_INTAVE_PARAM, 0x06, + /* conn fail based on SACCH error rate */ + NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10, + NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, + NM_ATT_MAX_TA, 0x3f, + NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */ + NM_ATT_CCCH_L_T, 10, /* percent */ + NM_ATT_CCCH_L_I_P, 1, /* seconds */ + NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */ + NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */ + NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */ + NM_ATT_NY1, 10, /* 10 retransmissions of physical config */ + NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, + NM_ATT_BSIC, HARDCODED_BSIC, + NM_ATT_IPACC_CGI, 0, 7, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00, +}; + +static unsigned char nanobts_attr_radio[] = { + NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */ + NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, +}; + +static unsigned char nanobts_attr_nse[] = { + NM_ATT_IPACC_NSEI, 0, 2, 0x03, 0x9d, /* NSEI 925 */ + NM_ATT_IPACC_NS_CFG, 0, 7, 3, /* (un)blocking timer (Tns-block) */ + 3, /* (un)blocking retries */ + 3, /* reset timer (Tns-reset) */ + 3, /* reset retries */ + 30, /* test timer (Tns-test) */ + 3, /* alive timer (Tns-alive) */ + 10, /* alive retrires */ + NM_ATT_IPACC_BSSGP_CFG, 0, 11, + 3, /* blockimg timer (T1) */ + 3, /* blocking retries */ + 3, /* unblocking retries */ + 3, /* reset timer */ + 3, /* reset retries */ + 10, /* suspend timer (T3) in 100ms */ + 3, /* suspend retries */ + 10, /* resume timer (T4) in 100ms */ + 3, /* resume retries */ + 10, /* capability update timer (T5) */ + 3, /* capability update retries */ +}; + +static unsigned char nanobts_attr_cell[] = { + NM_ATT_IPACC_RAC, 0, 1, 1, /* routing area code */ + NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2, + 5, /* repeat time (50ms) */ + 3, /* repeat count */ + NM_ATT_IPACC_BVCI, 0, 2, 0x03, 0x9d, /* BVCI 925 */ + NM_ATT_IPACC_RLC_CFG, 0, 9, + 20, /* T3142 */ + 5, /* T3169 */ + 5, /* T3191 */ + 200, /* T3193 */ + 5, /* T3195 */ + 10, /* N3101 */ + 4, /* N3103 */ + 8, /* N3105 */ + 15, /* RLC CV countdown */ + NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, + NM_ATT_IPACC_RLC_CFG_2, 0, 5, + 0x00, 250, + 0x00, 250, + 2, /* MCS2 */ +#if 0 + /* EDGE model only, breaks older models. + * Should inquire the BTS capabilities */ + NM_ATT_IPACC_RLC_CFG_3, 0, 1, + 2, /* MCS2 */ +#endif +}; + +static unsigned char nanobts_attr_nsvc0[] = { + NM_ATT_IPACC_NSVCI, 0, 2, 0x03, 0x9d, /* 925 */ + NM_ATT_IPACC_NS_LINK_CFG, 0, 8, + 0x59, 0xd8, /* remote udp port (23000) */ + 192, 168, 100, 11, /* remote ip address */ + 0x59, 0xd8, /* local udp port (23000) */ +}; + +/* Callback function to be called whenever we get a GSM 12.21 state change event */ +int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) +{ + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_bts_gprs_nsvc *nsvc; + + /* This event-driven BTS setup is currently only required on nanoBTS */ + + /* EVT_STATECHG_ADM is called after we call chg_adm_state() and would create + * endless loop */ + if (evt != EVT_STATECHG_OPER) + return 0; + + switch (obj_class) { + case NM_OC_SITE_MANAGER: + bts = container_of(obj, struct gsm_bts, site_mgr); + if ((new_state->operational == 2 && + new_state->availability == NM_AVSTATE_OK) || + (new_state->operational == 1 && + new_state->availability == NM_AVSTATE_OFF_LINE)) + abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff); + break; + case NM_OC_BTS: + bts = obj; + if (new_state->availability == NM_AVSTATE_DEPENDENCY) { + patch_nm_tables(bts); + abis_nm_set_bts_attr(bts, nanobts_attr_bts, + sizeof(nanobts_attr_bts)); + abis_nm_chg_adm_state(bts, obj_class, + bts->bts_nr, 0xff, 0xff, + NM_STATE_UNLOCKED); + abis_nm_opstart(bts, obj_class, + bts->bts_nr, 0xff, 0xff); + } + break; + case NM_OC_CHANNEL: + ts = obj; + trx = ts->trx; + if (new_state->operational == 1 && + new_state->availability == NM_AVSTATE_DEPENDENCY) { + patch_nm_tables(trx->bts); + enum abis_nm_chan_comb ccomb = + abis_nm_chcomb4pchan(ts->pchan); + abis_nm_set_channel_attr(ts, ccomb); + abis_nm_chg_adm_state(trx->bts, obj_class, + trx->bts->bts_nr, trx->nr, ts->nr, + NM_STATE_UNLOCKED); + abis_nm_opstart(trx->bts, obj_class, + trx->bts->bts_nr, trx->nr, ts->nr); + } + break; + case NM_OC_RADIO_CARRIER: + trx = obj; + if (new_state->operational == 1 && + new_state->availability == NM_AVSTATE_OK) + abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, + trx->nr, 0xff); + break; + case NM_OC_GPRS_NSE: + bts = container_of(obj, struct gsm_bts, gprs.nse); + if (!bts->gprs.enabled) + break; + if (new_state->availability == 5) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + 0xff, 0xff, nanobts_attr_nse, + sizeof(nanobts_attr_nse)); + abis_nm_opstart(bts, obj_class, bts->bts_nr, + 0xff, 0xff); + abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, + 0xff, 0xff, NM_STATE_UNLOCKED); + } + break; + case NM_OC_GPRS_CELL: + bts = container_of(obj, struct gsm_bts, gprs.cell); + if (!bts->gprs.enabled) + break; + if (new_state->availability == 5) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + 0, 0xff, nanobts_attr_cell, + sizeof(nanobts_attr_cell)); + abis_nm_opstart(bts, obj_class, bts->bts_nr, + 0, 0xff); + abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, + 0, 0xff, NM_STATE_UNLOCKED); + } + break; + case NM_OC_GPRS_NSVC: + nsvc = obj; + bts = nsvc->bts; + if (!bts->gprs.enabled) + break; + /* We skip NSVC1 since we only use NSVC0 */ + if (nsvc->id == 1) + break; + if (new_state->availability == NM_AVSTATE_OFF_LINE) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + nsvc->id, 0xff, + nanobts_attr_nsvc0, + sizeof(nanobts_attr_nsvc0)); + abis_nm_opstart(bts, obj_class, bts->bts_nr, + nsvc->id, 0xff); + abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, + nsvc->id, 0xff, + NM_STATE_UNLOCKED); + } + default: + break; + } + return 0; +} + +/* Callback function to be called every time we receive a 12.21 SW activated report */ +static int sw_activ_rep(struct msgb *mb) +{ + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct gsm_bts *bts = mb->trx->bts; + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + + if (!trx) + return -EINVAL; + + switch (foh->obj_class) { + case NM_OC_BASEB_TRANSC: + abis_nm_chg_adm_state(trx->bts, foh->obj_class, + trx->bts->bts_nr, trx->nr, 0xff, + NM_STATE_UNLOCKED); + abis_nm_opstart(trx->bts, foh->obj_class, + trx->bts->bts_nr, trx->nr, 0xff); + /* TRX software is active, tell it to initiate RSL Link */ + abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei); + break; + case NM_OC_RADIO_CARRIER: { + /* + * Locking the radio carrier will make it go + * offline again and we would come here. The + * framework should determine that there was + * no change and avoid recursion. + * + * This code is here to make sure that on start + * a TRX remains locked. + */ + int rc_state = trx->nm_state.administrative; + /* Patch ARFCN into radio attribute */ + nanobts_attr_radio[5] &= 0xf0; + nanobts_attr_radio[5] |= trx->arfcn >> 8; + nanobts_attr_radio[6] = trx->arfcn & 0xff; + abis_nm_set_radio_attr(trx, nanobts_attr_radio, + sizeof(nanobts_attr_radio)); + abis_nm_chg_adm_state(trx->bts, foh->obj_class, + trx->bts->bts_nr, trx->nr, 0xff, + rc_state); + abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, + trx->nr, 0xff); + break; + } + } + return 0; +} + +/* Callback function for NACK on the OML NM */ +static int oml_msg_nack(u_int8_t mt) +{ + if (mt == NM_MT_SET_BTS_ATTR_NACK) { + LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. " + "Was the bts type and frequency properly specified?\n"); + exit(-1); + } + + return 0; +} + +/* Callback function to be called every time we receive a signal from NM */ +static int nm_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + u_int8_t *msg_type; + + switch (signal) { + case S_NM_SW_ACTIV_REP: + return sw_activ_rep(signal_data); + case S_NM_NACK: + msg_type = signal_data; + return oml_msg_nack(*msg_type); + default: + break; + } + return 0; +} + +static void bootstrap_om_nanobts(struct gsm_bts *bts) +{ + /* We don't do callback based bootstrapping, but event driven (see above) */ +} + +static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) +{ + enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); + struct gsm_e1_subslot *e1l = &ts->e1_link; + + abis_nm_set_channel_attr(ts, ccomb); + + if (is_ipaccess_bts(ts->trx->bts)) + return; + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, + e1l->e1_ts_ss); + break; + default: + break; + } +} + +static void nm_reconfig_trx(struct gsm_bts_trx *trx) +{ + struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; + int i; + + patch_nm_tables(trx->bts); + + switch (trx->bts->type) { + case GSM_BTS_TYPE_BS11: + /* FIXME: discover this by fetching an attribute */ +#if 0 + trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ +#else + trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ +#endif + abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, + e1l->e1_ts_ss); + abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, + e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); + + /* Set Radio Attributes */ + if (trx == trx->bts->c0) + abis_nm_set_radio_attr(trx, bs11_attr_radio, + sizeof(bs11_attr_radio)); + else { + u_int8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; + u_int8_t arfcn_low = trx->arfcn & 0xff; + u_int8_t arfcn_high = (trx->arfcn >> 8) & 0x0f; + memcpy(trx1_attr_radio, bs11_attr_radio, + sizeof(trx1_attr_radio)); + + /* patch ARFCN into TRX Attributes */ + trx1_attr_radio[2] &= 0xf0; + trx1_attr_radio[2] |= arfcn_high; + trx1_attr_radio[3] = arfcn_low; + + abis_nm_set_radio_attr(trx, trx1_attr_radio, + sizeof(trx1_attr_radio)); + } + break; + case GSM_BTS_TYPE_NANOBTS: + switch (trx->bts->band) { + case GSM_BAND_850: + case GSM_BAND_900: + trx->nominal_power = 20; + break; + case GSM_BAND_1800: + case GSM_BAND_1900: + trx->nominal_power = 23; + break; + default: + LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n", + gsm_band_name(trx->bts->band)); + break; + } + break; + default: + break; + } + + for (i = 0; i < TRX_NR_TS; i++) + nm_reconfig_ts(&trx->ts[i]); +} + +static void nm_reconfig_bts(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + patch_nm_tables(bts); + abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ + abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts)); + abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ + abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ + break; + default: + break; + } + + llist_for_each_entry(trx, &bts->trx_list, list) + nm_reconfig_trx(trx); +} + +static void bootstrap_om_bs11(struct gsm_bts *bts) +{ + /* stop sending event reports */ + abis_nm_event_reports(bts, 0); + + /* begin DB transmission */ + abis_nm_bs11_db_transmission(bts, 1); + + /* end DB transmission */ + abis_nm_bs11_db_transmission(bts, 0); + + /* Reset BTS Site manager resource */ + abis_nm_bs11_reset_resource(bts); + + /* begin DB transmission */ + abis_nm_bs11_db_transmission(bts, 1); + + /* reconfigure BTS with all TRX and all TS */ + nm_reconfig_bts(bts); + + /* end DB transmission */ + abis_nm_bs11_db_transmission(bts, 0); + + /* Reset BTS Site manager resource */ + abis_nm_bs11_reset_resource(bts); + + /* restart sending event reports */ + abis_nm_event_reports(bts, 1); +} + +static void bootstrap_om(struct gsm_bts *bts) +{ + LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + bootstrap_om_bs11(bts); + break; + case GSM_BTS_TYPE_NANOBTS: + bootstrap_om_nanobts(bts); + break; + default: + LOGP(DNM, LOGL_ERROR, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type); + } +} + +static int shutdown_om(struct gsm_bts *bts) +{ + LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr); + + /* stop sending event reports */ + abis_nm_event_reports(bts, 0); + + /* begin DB transmission */ + abis_nm_bs11_db_transmission(bts, 1); + + /* end DB transmission */ + abis_nm_bs11_db_transmission(bts, 0); + + /* Reset BTS Site manager resource */ + abis_nm_bs11_reset_resource(bts); + + return 0; +} + +int bsc_shutdown_net(struct gsm_network *net) +{ + struct gsm_bts *bts; + + llist_for_each_entry(bts, &net->bts_list, list) { + int rc; + rc = shutdown_om(bts); + if (rc < 0) + return rc; + } + + return 0; +} + +/* set all system information types */ +static int set_system_infos(struct gsm_bts_trx *trx) +{ + int i, rc; + u_int8_t si_tmp[23]; + struct gsm_bts *bts = trx->bts; + + bts->si_common.cell_sel_par.ms_txpwr_max_ccch = + ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); + bts->si_common.cell_sel_par.neci = bts->network->neci; + + if (trx == trx->bts->c0) { + for (i = 1; i <= 4; i++) { + rc = gsm_generate_si(si_tmp, trx->bts, i); + if (rc < 0) + goto err_out; + DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc)); + rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp)); + } + if (bts->gprs.enabled) { + i = 13; + rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13); + if (rc < 0) + goto err_out; + DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc)); + rsl_bcch_info(trx, RSL_SYSTEM_INFO_13, si_tmp, rc); + } + } + + i = 5; + rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_5); + if (rc < 0) + goto err_out; + DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc)); + rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si_tmp, rc); + + i = 6; + rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_6); + if (rc < 0) + goto err_out; + DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc)); + rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si_tmp, rc); + + return 0; +err_out: + LOGP(DRR, LOGL_ERROR, "Cannot generate SI %u for BTS %u, most likely " + "a problem with neighbor cell list generation\n", + i, trx->bts->nr); + return rc; +} + +/* + * Patch the various SYSTEM INFORMATION tables to update + * the LAI + */ +static void patch_nm_tables(struct gsm_bts *bts) +{ + u_int8_t arfcn_low = bts->c0->arfcn & 0xff; + u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; + + /* patch ARFCN into BTS Attributes */ + bs11_attr_bts[69] &= 0xf0; + bs11_attr_bts[69] |= arfcn_high; + bs11_attr_bts[70] = arfcn_low; + nanobts_attr_bts[42] &= 0xf0; + nanobts_attr_bts[42] |= arfcn_high; + nanobts_attr_bts[43] = arfcn_low; + + /* patch ARFCN into TRX Attributes */ + bs11_attr_radio[2] &= 0xf0; + bs11_attr_radio[2] |= arfcn_high; + bs11_attr_radio[3] = arfcn_low; + + /* patch BSIC */ + bs11_attr_bts[1] = bts->bsic; + nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic; + + /* patch CGI */ + abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts); + + /* patch the power reduction */ + bs11_attr_radio[5] = bts->c0->max_power_red / 2; + nanobts_attr_radio[1] = bts->c0->max_power_red / 2; + + /* patch NSEI */ + nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8; + nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff; + + /* patch NSVCI */ + nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8; + nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff; + + /* patch IP address as SGSN IP */ + *(u_int16_t *)(nanobts_attr_nsvc0+8) = + htons(bts->gprs.nsvc[0].remote_port); + *(u_int32_t *)(nanobts_attr_nsvc0+10) = + htonl(bts->gprs.nsvc[0].remote_ip); + *(u_int16_t *)(nanobts_attr_nsvc0+14) = + htons(bts->gprs.nsvc[0].local_port); + + /* patch BVCI */ + nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8; + nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff; + /* patch RAC */ + nanobts_attr_cell[3] = bts->gprs.rac; + +} + +static void bootstrap_rsl(struct gsm_bts_trx *trx) +{ + LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " + "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n", + trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, + bsc_gsmnet->network_code, trx->bts->location_area_code, + trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc); + set_system_infos(trx); +} + +void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) +{ + switch (event) { + case EVT_E1_TEI_UP: + switch (type) { + case E1INP_SIGN_OML: + bootstrap_om(trx->bts); + break; + case E1INP_SIGN_RSL: + bootstrap_rsl(trx); + break; + default: + break; + } + break; + case EVT_E1_TEI_DN: + LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n"); + /* FIXME: deal with TEI or L1 link loss */ + break; + default: + break; + } +} + +static int bootstrap_bts(struct gsm_bts *bts) +{ + switch (bts->band) { + case GSM_BAND_1800: + if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { + LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n"); + return -EINVAL; + } + break; + case GSM_BAND_1900: + if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { + LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n"); + return -EINVAL; + } + break; + case GSM_BAND_900: + if (bts->c0->arfcn < 1 || + (bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || + bts->c0->arfcn > 1023) { + LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-124, 955-1023.\n"); + return -EINVAL; + } + break; + default: + LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n"); + return -EINVAL; + } + + if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL && + !bts->si_common.rach_control.cell_bar) + LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' " + "network on a BTS that is not barred. This " + "configuration is likely to interfere with production " + "GSM networks and should only be used in a RF " + "shielded environment such as a faraday cage!\n\n"); + + /* Control Channel Description */ + bts->si_common.chan_desc.att = 1; + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; + bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; + /* T3212 is set from vty/config */ + + /* some defaults for our system information */ + bts->si_common.cell_options.radio_link_timeout = 2; /* 12 */ + bts->si_common.cell_options.dtx = 2; /* MS shall not use upplink DTX */ + bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ + + bts->si_common.cell_sel_par.acs = 0; + + bts->si_common.ncc_permitted = 0xff; + + paging_init(bts); + + return 0; +} + +int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, int, void *), + const char *config_file) +{ + struct gsm_bts *bts; + int rc; + + /* initialize our data structures */ + bsc_gsmnet = gsm_network_init(1, 1, mncc_recv); + if (!bsc_gsmnet) + return -ENOMEM; + + bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); + bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); + + telnet_init(bsc_gsmnet, 4242); + rc = vty_read_config_file(config_file); + if (rc < 0) { + LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + register_signal_handler(SS_NM, nm_sig_cb, NULL); + + llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { + bootstrap_bts(bts); + if (!is_ipaccess_bts(bts)) + rc = e1_reconfig_bts(bts); + + if (rc < 0) + exit (1); + } + + /* initialize nanoBTS support omce */ + rc = ipaccess_setup(bsc_gsmnet); + + return 0; +} diff --git a/openbsc/src/bsc_rll.c b/openbsc/src/bsc_rll.c new file mode 100644 index 000000000..e9d6f252a --- /dev/null +++ b/openbsc/src/bsc_rll.c @@ -0,0 +1,118 @@ +/* GSM BSC Radio Link Layer API + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2009 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 <openbsc/debug.h> +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <osmocore/linuxlist.h> +#include <openbsc/bsc_rll.h> +#include <openbsc/gsm_data.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/abis_rsl.h> + +struct bsc_rll_req { + struct llist_head list; + struct timer_list timer; + + struct gsm_lchan *lchan; + u_int8_t link_id; + + void (*cb)(struct gsm_lchan *lchan, u_int8_t link_id, + void *data, enum bsc_rllr_ind); + void *data; +}; + +/* we only compare C1, C2 and SAPI */ +#define LINKID_MASK 0xC7 + +static LLIST_HEAD(bsc_rll_reqs); + +static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type) +{ + llist_del(&rllr->list); + put_lchan(rllr->lchan); + rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type); + talloc_free(rllr); +} + +static void timer_cb(void *_rllr) +{ + struct bsc_rll_req *rllr = _rllr; + + complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT); +} + +/* establish a RLL connection with given SAPI / priority */ +int rll_establish(struct gsm_lchan *lchan, u_int8_t sapi, + void (*cb)(struct gsm_lchan *, u_int8_t, void *, + enum bsc_rllr_ind), + void *data) +{ + struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); + u_int8_t link_id; + if (!rllr) + return -ENOMEM; + + link_id = sapi; + + /* If we are a TCH and not in signalling mode, we need to + * indicate that the new RLL connection is to be made on the SACCH */ + if ((lchan->type == GSM_LCHAN_TCH_F || + lchan->type == GSM_LCHAN_TCH_H) && + lchan->rsl_cmode != RSL_CMOD_SPD_SIGN) + link_id |= 0x40; + + use_lchan(lchan); + rllr->lchan = lchan; + rllr->link_id = link_id; + rllr->cb = cb; + rllr->data = data; + + llist_add(&rllr->list, &bsc_rll_reqs); + + rllr->timer.cb = &timer_cb; + rllr->timer.data = rllr; + + bsc_schedule_timer(&rllr->timer, 10, 0); + + /* send the RSL RLL ESTablish REQuest */ + return rsl_establish_request(rllr->lchan, rllr->link_id); +} + +/* Called from RSL code in case we have received an indication regarding + * any RLL link */ +void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type) +{ + struct bsc_rll_req *rllr, *rllr2; + + llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { + if (rllr->lchan == lchan && + (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) { + bsc_del_timer(&rllr->timer); + complete_rllr(rllr, type); + return; + } + } +} diff --git a/openbsc/src/bts_ipaccess_nanobts.c b/openbsc/src/bts_ipaccess_nanobts.c new file mode 100644 index 000000000..cb48ea98a --- /dev/null +++ b/openbsc/src/bts_ipaccess_nanobts.c @@ -0,0 +1,84 @@ +/* ip.access nanoBTS specific code */ + +/* (C) 2009-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 <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/tlv.h> +#include <openbsc/abis_nm.h> + +static struct gsm_bts_model model_nanobts = { + .type = GSM_BTS_TYPE_NANOBTS, + .nm_att_tlvdef = { + .def = { + /* ip.access specifics */ + [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, + [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_TV, }, + [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, + [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, + [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, + }, + }, +}; + +int bts_model_nanobts_init(void) +{ + return gsm_bts_model_register(&model_nanobts); +} diff --git a/openbsc/src/bts_siemens_bs11.c b/openbsc/src/bts_siemens_bs11.c new file mode 100644 index 000000000..c966825ee --- /dev/null +++ b/openbsc/src/bts_siemens_bs11.c @@ -0,0 +1,66 @@ +/* Siemens BS-11 specific code */ + +/* (C) 2009-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 <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/tlv.h> +#include <openbsc/abis_nm.h> + +static struct gsm_bts_model model_bs11 = { + .type = GSM_BTS_TYPE_BS11, + .nm_att_tlvdef = { + .def = { + [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV }, + /* BS11 specifics */ + [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, + [0xd5] = { TLV_TYPE_TLV }, + [0xa8] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, + [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, + [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, + [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, + [0x95] = { TLV_TYPE_FIXED, 2 }, + }, + }, +}; + +int bts_model_bs11_init(void) +{ + return gsm_bts_model_register(&model_bs11); +} diff --git a/openbsc/src/bts_unknown.c b/openbsc/src/bts_unknown.c new file mode 100644 index 000000000..aac5d99c8 --- /dev/null +++ b/openbsc/src/bts_unknown.c @@ -0,0 +1,40 @@ +/* Generic BTS - VTY code tries to allocate this BTS before type is known */ + +/* (C) 2010 by Daniel Willmann <daniel@totalueberwachung.de> + * + * 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 <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/tlv.h> +#include <openbsc/abis_nm.h> + +static struct gsm_bts_model model_unknown = { + .type = GSM_BTS_TYPE_UNKNOWN, + .nm_att_tlvdef = { + .def = { + }, + }, +}; + +int bts_model_unknown_init(void) +{ + return gsm_bts_model_register(&model_unknown); +} diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c new file mode 100644 index 000000000..2e885241c --- /dev/null +++ b/openbsc/src/chan_alloc.c @@ -0,0 +1,418 @@ +/* GSM Channel allocation routines + * + * (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/abis_nm.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/debug.h> +#include <openbsc/signal.h> + +static void auto_release_channel(void *_lchan); + +static int ts_is_usable(struct gsm_bts_trx_ts *ts) +{ + /* FIXME: How does this behave for BS-11 ? */ + if (is_ipaccess_bts(ts->trx->bts)) { + if (!nm_is_running(&ts->nm_state)) + return 0; + } + + return 1; +} + +int trx_is_usable(struct gsm_bts_trx *trx) +{ + /* FIXME: How does this behave for BS-11 ? */ + if (is_ipaccess_bts(trx->bts)) { + if (!nm_is_running(&trx->nm_state) || + !nm_is_running(&trx->bb_transc.nm_state)) + return 0; + } + + return 1; +} + +struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx *trx = bts->c0; + struct gsm_bts_trx_ts *ts = &trx->ts[0]; + + if (pchan != GSM_PCHAN_CCCH && + pchan != GSM_PCHAN_CCCH_SDCCH4) + return NULL; + + if (ts->pchan != GSM_PCHAN_NONE) + return NULL; + + ts->pchan = pchan; + + return ts; +} + +/* Allocate a physical channel (TS) */ +struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan) +{ + int j; + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + int from, to; + + if (!trx_is_usable(trx)) + continue; + + /* the following constraints are pure policy, + * no requirement to put this restriction in place */ + if (trx == bts->c0) { + /* On the first TRX we run one CCCH and one SDCCH8 */ + switch (pchan) { + case GSM_PCHAN_CCCH: + case GSM_PCHAN_CCCH_SDCCH4: + from = 0; to = 0; + break; + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + from = 1; to = 7; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + default: + return NULL; + } + } else { + /* Every secondary TRX is configured for TCH/F + * and TCH/H only */ + switch (pchan) { + case GSM_PCHAN_SDCCH8_SACCH8C: + from = 1; to = 1; + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + from = 1; to = 7; + break; + default: + return NULL; + } + } + + for (j = from; j <= to; j++) { + struct gsm_bts_trx_ts *ts = &trx->ts[j]; + + if (!ts_is_usable(ts)) + continue; + + if (ts->pchan == GSM_PCHAN_NONE) { + ts->pchan = pchan; + /* set channel attribute on OML */ + abis_nm_set_channel_attr(ts, abis_nm_chcomb4pchan(pchan)); + return ts; + } + } + } + return NULL; +} + +/* Free a physical channel (TS) */ +void ts_free(struct gsm_bts_trx_ts *ts) +{ + ts->pchan = GSM_PCHAN_NONE; +} + +static const u_int8_t subslots_per_pchan[] = { + [GSM_PCHAN_NONE] = 0, + [GSM_PCHAN_CCCH] = 0, + [GSM_PCHAN_CCCH_SDCCH4] = 4, + [GSM_PCHAN_TCH_F] = 1, + [GSM_PCHAN_TCH_H] = 2, + [GSM_PCHAN_SDCCH8_SACCH8C] = 8, + /* FIXME: what about dynamic TCH_F_TCH_H ? */ +}; + +static struct gsm_lchan * +_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx_ts *ts; + int j, ss; + + if (!trx_is_usable(trx)) + return NULL; + + for (j = 0; j < 8; j++) { + ts = &trx->ts[j]; + if (!ts_is_usable(ts)) + continue; + if (ts->pchan != pchan) + continue; + /* check if all sub-slots are allocated yet */ + for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { + struct gsm_lchan *lc = &ts->lchan[ss]; + if (lc->type == GSM_LCHAN_NONE && + lc->state == LCHAN_S_NONE) + return lc; + } + } + return NULL; +} + +static struct gsm_lchan * +_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_lchan *lc; + + if (bts->chan_alloc_reverse) { + llist_for_each_entry_reverse(trx, &bts->trx_list, list) { + lc = _lc_find_trx(trx, pchan); + if (lc) + return lc; + } + } else { + llist_for_each_entry(trx, &bts->trx_list, list) { + lc = _lc_find_trx(trx, pchan); + if (lc) + return lc; + } + } + + /* we cannot allocate more of these */ + if (pchan == GSM_PCHAN_CCCH_SDCCH4) + return NULL; + + /* if we've reached here, we need to allocate a new physical + * channel for the logical channel type requested */ + ts = ts_alloc(bts, pchan); + if (!ts) { + /* no more radio resources */ + return NULL; + } + return &ts->lchan[0]; +} + +/* Allocate a logical channel */ +struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type) +{ + struct gsm_lchan *lchan = NULL; + enum gsm_phys_chan_config first, second; + + switch (type) { + case GSM_LCHAN_SDCCH: + if (bts->chan_alloc_reverse) { + first = GSM_PCHAN_SDCCH8_SACCH8C; + second = GSM_PCHAN_CCCH_SDCCH4; + } else { + first = GSM_PCHAN_CCCH_SDCCH4; + second = GSM_PCHAN_SDCCH8_SACCH8C; + } + + lchan = _lc_find_bts(bts, first); + if (lchan == NULL) + lchan = _lc_find_bts(bts, second); + break; + case GSM_LCHAN_TCH_F: + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + break; + case GSM_LCHAN_TCH_H: + lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H); + /* If we don't have TCH/H available, fall-back to TCH/F */ + if (!lchan) { + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + type = GSM_LCHAN_TCH_F; + } + break; + default: + LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); + } + + if (lchan) { + lchan->type = type; + lchan->use_count = 0; + + /* clear sapis */ + memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); + + /* clear multi rate config */ + memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf)); + + /* Configure the time and start it so it will be closed */ + lchan->release_timer.cb = auto_release_channel; + lchan->release_timer.data = lchan; + bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); + } + + return lchan; +} + +/* Free a logical channel */ +void lchan_free(struct gsm_lchan *lchan) +{ + int i; + + lchan->type = GSM_LCHAN_NONE; + if (lchan->subscr) { + subscr_put(lchan->subscr); + lchan->subscr = NULL; + } + + /* We might kill an active channel... */ + if (lchan->use_count != 0) { + dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan); + lchan->use_count = 0; + } + + /* stop the timer */ + bsc_del_timer(&lchan->release_timer); + bsc_del_timer(&lchan->T3101); + + /* clear cached measuement reports */ + lchan->meas_rep_idx = 0; + for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { + lchan->meas_rep[i].flags = 0; + lchan->meas_rep[i].nr = 0; + } + for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) + lchan->neigh_meas[i].arfcn = 0; + + lchan->silent_call = 0; + + /* FIXME: ts_free() the timeslot, if we're the last logical + * channel using it */ +} + +/* Consider releasing the channel now */ +int lchan_auto_release(struct gsm_lchan *lchan) +{ + if (lchan->use_count > 0) { + return 0; + } + + /* Assume we have GSM04.08 running and send a release */ + if (lchan->subscr) { + gsm48_send_rr_release(lchan); + } + + /* spoofed? message */ + if (lchan->use_count < 0) + LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n", + lchan->use_count); + + DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan)); + rsl_release_request(lchan, 0); + return 1; +} + +/* Auto release the channel when the use count is zero */ +static void auto_release_channel(void *_lchan) +{ + struct gsm_lchan *lchan = _lchan; + + if (!lchan_auto_release(lchan)) + bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); +} + +struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { + struct gsm_bts_trx *trx; + int ts_no, lchan_no; + + llist_for_each_entry(trx, &bts->trx_list, list) { + for (ts_no = 0; ts_no < 8; ++ts_no) { + for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) { + struct gsm_lchan *lchan = + &trx->ts[ts_no].lchan[lchan_no]; + if (subscr == lchan->subscr) + return lchan; + } + } + } + + return NULL; +} + +struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr) +{ + struct gsm_bts *bts; + struct gsm_network *net = subscr->net; + struct gsm_lchan *lchan; + + llist_for_each_entry(bts, &net->bts_list, list) { + lchan = lchan_find(bts, subscr); + if (lchan) + return lchan; + } + + return NULL; +} + +void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + int i; + + /* skip administratively deactivated tranxsceivers */ + if (!nm_is_running(&trx->nm_state) || + !nm_is_running(&trx->bb_transc.nm_state)) + continue; + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + struct load_counter *pl = &cl->pchan[ts->pchan]; + int j; + + /* skip administratively deactivated timeslots */ + if (!nm_is_running(&ts->nm_state)) + continue; + + for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) { + struct gsm_lchan *lchan = &ts->lchan[j]; + + pl->total++; + + switch (lchan->state) { + case LCHAN_S_NONE: + break; + default: + pl->used++; + break; + } + } + } + } +} + +void network_chan_load(struct pchan_load *pl, struct gsm_network *net) +{ + struct gsm_bts *bts; + + memset(pl, 0, sizeof(*pl)); + + llist_for_each_entry(bts, &net->bts_list, list) + bts_chan_load(pl, bts); +} diff --git a/openbsc/src/db.c b/openbsc/src/db.c new file mode 100644 index 000000000..10c1d6d4c --- /dev/null +++ b/openbsc/src/db.c @@ -0,0 +1,1179 @@ +/* Simple HLR/VLR database backend using dbi */ +/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 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 <openbsc/gsm_data.h> +#include <openbsc/gsm_04_11.h> +#include <openbsc/db.h> +#include <osmocore/talloc.h> +#include <openbsc/debug.h> +#include <osmocore/statistics.h> + +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <dbi/dbi.h> + +static char *db_basename = NULL; +static char *db_dirname = NULL; +static dbi_conn conn; + +static char *create_stmts[] = { + "CREATE TABLE IF NOT EXISTS Meta (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "key TEXT UNIQUE NOT NULL, " + "value TEXT NOT NULL" + ")", + "INSERT OR IGNORE INTO Meta " + "(key, value) " + "VALUES " + "('revision', '2')", + "CREATE TABLE IF NOT EXISTS Subscriber (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "updated TIMESTAMP NOT NULL, " + "imsi NUMERIC UNIQUE NOT NULL, " + "name TEXT, " + "extension TEXT UNIQUE, " + "authorized INTEGER NOT NULL DEFAULT 0, " + "tmsi TEXT UNIQUE, " + "lac INTEGER NOT NULL DEFAULT 0" + ")", + "CREATE TABLE IF NOT EXISTS AuthToken (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "subscriber_id INTEGER UNIQUE NOT NULL, " + "created TIMESTAMP NOT NULL, " + "token TEXT UNIQUE NOT NULL" + ")", + "CREATE TABLE IF NOT EXISTS Equipment (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "updated TIMESTAMP NOT NULL, " + "name TEXT, " + "classmark1 NUMERIC, " + "classmark2 BLOB, " + "classmark3 BLOB, " + "imei NUMERIC UNIQUE NOT NULL" + ")", + "CREATE TABLE IF NOT EXISTS EquipmentWatch (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "updated TIMESTAMP NOT NULL, " + "subscriber_id NUMERIC NOT NULL, " + "equipment_id NUMERIC NOT NULL, " + "UNIQUE (subscriber_id, equipment_id) " + ")", + "CREATE TABLE IF NOT EXISTS SMS (" + /* metadata, not part of sms */ + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "sent TIMESTAMP, " + "sender_id INTEGER NOT NULL, " + "receiver_id INTEGER NOT NULL, " + "deliver_attempts INTEGER NOT NULL DEFAULT 0, " + /* data directly copied/derived from SMS */ + "valid_until TIMESTAMP, " + "reply_path_req INTEGER NOT NULL, " + "status_rep_req INTEGER NOT NULL, " + "protocol_id INTEGER NOT NULL, " + "data_coding_scheme INTEGER NOT NULL, " + "ud_hdr_ind INTEGER NOT NULL, " + "dest_addr TEXT, " + "user_data BLOB, " /* TP-UD */ + /* additional data, interpreted from SMS */ + "header BLOB, " /* UD Header */ + "text TEXT " /* decoded UD after UDH */ + ")", + "CREATE TABLE IF NOT EXISTS VLR (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "updated TIMESTAMP NOT NULL, " + "subscriber_id NUMERIC UNIQUE NOT NULL, " + "last_bts NUMERIC NOT NULL " + ")", + "CREATE TABLE IF NOT EXISTS ApduBlobs (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "apdu_id_flags INTEGER NOT NULL, " + "subscriber_id INTEGER NOT NULL, " + "apdu BLOB " + ")", + "CREATE TABLE IF NOT EXISTS Counters (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "timestamp TIMESTAMP NOT NULL, " + "value INTEGER NOT NULL, " + "name TEXT NOT NULL " + ")", + "CREATE TABLE IF NOT EXISTS AuthKeys (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "subscriber_id INTEGER UNIQUE NOT NULL, " + "algorithm_id INTEGER NOT NULL, " + "a3a8_ki BLOB " + ")", + "CREATE TABLE IF NOT EXISTS AuthTuples (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "subscriber_id NUMERIC UNIQUE NOT NULL, " + "issued TIMESTAMP NOT NULL, " + "use_count INTEGER NOT NULL DEFAULT 0, " + "key_seq INTEGER NOT NULL, " + "rand BLOB NOT NULL, " + "sres BLOB NOT NULL, " + "kc BLOB NOT NULL " + ")", +}; + +void db_error_func(dbi_conn conn, void *data) +{ + const char *msg; + dbi_conn_error(conn, &msg); + LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); +} + +static int check_db_revision(void) +{ + dbi_result result; + const char *rev; + + result = dbi_conn_query(conn, + "SELECT value FROM Meta WHERE key='revision'"); + if (!result) + return -EINVAL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -EINVAL; + } + rev = dbi_result_get_string(result, "value"); + if (!rev || atoi(rev) != 2) { + dbi_result_free(result); + return -EINVAL; + } + + dbi_result_free(result); + return 0; +} + +int db_init(const char *name) +{ + dbi_initialize(NULL); + + conn = dbi_conn_new("sqlite3"); + if (conn == NULL) { + LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n"); + return 1; + } + + dbi_conn_error_handler( conn, db_error_func, NULL ); + + /* MySQL + dbi_conn_set_option(conn, "host", "localhost"); + dbi_conn_set_option(conn, "username", "your_name"); + dbi_conn_set_option(conn, "password", "your_password"); + dbi_conn_set_option(conn, "dbname", "your_dbname"); + dbi_conn_set_option(conn, "encoding", "UTF-8"); + */ + + /* SqLite 3 */ + db_basename = strdup(name); + db_dirname = strdup(name); + dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); + dbi_conn_set_option(conn, "dbname", basename(db_basename)); + + if (dbi_conn_connect(conn) < 0) + goto out_err; + + return 0; + +out_err: + free(db_dirname); + free(db_basename); + db_dirname = db_basename = NULL; + return -1; +} + + +int db_prepare() +{ + dbi_result result; + int i; + + for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { + result = dbi_conn_query(conn, create_stmts[i]); + if (!result) { + LOGP(DDB, LOGL_ERROR, + "Failed to create some table.\n"); + return 1; + } + dbi_result_free(result); + } + + if (check_db_revision() < 0) { + LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, " + "please update your database schema\n"); + return -1; + } + + return 0; +} + +int db_fini() +{ + dbi_conn_close(conn); + dbi_shutdown(); + + if (db_dirname) + free(db_dirname); + if (db_basename) + free(db_basename); + return 0; +} + +struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi) +{ + dbi_result result; + struct gsm_subscriber *subscr; + + /* Is this subscriber known in the db? */ + subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); + if (subscr) { + result = dbi_conn_queryf(conn, + "UPDATE Subscriber set updated = datetime('now') " + "WHERE imsi = %s " , imsi); + if (!result) + LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); + else + dbi_result_free(result); + return subscr; + } + + subscr = subscr_alloc(); + subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; + if (!subscr) + return NULL; + result = dbi_conn_queryf(conn, + "INSERT INTO Subscriber " + "(imsi, created, updated) " + "VALUES " + "(%s, datetime('now'), datetime('now')) ", + imsi + ); + if (!result) + LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n"); + subscr->net = net; + subscr->id = dbi_conn_sequence_last(conn, NULL); + strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); + dbi_result_free(result); + LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); + db_subscriber_alloc_exten(subscr); + return subscr; +} + +static int get_equipment_by_subscr(struct gsm_subscriber *subscr) +{ + dbi_result result; + const char *string; + unsigned char cm1; + const unsigned char *cm2, *cm3; + struct gsm_equipment *equip = &subscr->equipment; + + result = dbi_conn_queryf(conn, + "SELECT equipment.* FROM Equipment,EquipmentWatch " + "WHERE EquipmentWatch.equipment_id=Equipment.id " + "AND EquipmentWatch.subscriber_id = %llu " + "ORDER BY updated DESC", subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + equip->id = dbi_result_get_ulonglong(result, "id"); + + string = dbi_result_get_string(result, "imei"); + if (string) + strncpy(equip->imei, string, sizeof(equip->imei)); + + string = dbi_result_get_string(result, "classmark1"); + if (string) + cm1 = atoi(string) & 0xff; + equip->classmark1 = *((struct gsm48_classmark1 *) &cm1); + + equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); + cm2 = dbi_result_get_binary(result, "classmark2"); + if (equip->classmark2_len > sizeof(equip->classmark2)) + equip->classmark2_len = sizeof(equip->classmark2); + memcpy(equip->classmark2, cm2, equip->classmark2_len); + + equip->classmark3_len = dbi_result_get_field_length(result, "classmark3"); + cm3 = dbi_result_get_binary(result, "classmark3"); + if (equip->classmark3_len > sizeof(equip->classmark3)) + equip->classmark3_len = sizeof(equip->classmark3); + memcpy(equip->classmark3, cm3, equip->classmark3_len); + + dbi_result_free(result); + + return 0; +} + +int get_authinfo_by_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + const unsigned char *a3a8_ki; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthKeys WHERE subscriber_id=%u", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); + ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); + a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); + if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) + ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki_len); + memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); + + dbi_result_free(result); + + return 0; +} + +int set_authinfo_for_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + struct gsm_auth_info ainfo_old; + int rc, upd; + unsigned char *ki_str; + + /* Deletion ? */ + if (ainfo == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthKeys WHERE subscriber_id=%u", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = get_authinfo_by_subscr(&ainfo_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthKeys " + "(subscriber_id, algorithm_id, a3a8_ki) " + "VALUES (%u, %u, %s)", + subscr->id, ainfo->auth_algo, ki_str); + } else { + result = dbi_conn_queryf(conn, + "UPDATE AuthKeys " + "SET algorithm_id=%u, a3a8_ki=%s " + "WHERE subscriber_id=%u", + ainfo->auth_algo, ki_str, subscr->id); + } + + free(ki_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + +int get_authtuple_by_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int len; + const unsigned char *blob; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthTuples WHERE subscriber_id=%u", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + memset(atuple, 0, sizeof(atuple)); + + atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); + atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); + + len = dbi_result_get_field_length(result, "rand"); + if (len != sizeof(atuple->rand)) + goto err_size; + + blob = dbi_result_get_binary(result, "rand"); + memcpy(atuple->rand, blob, len); + + len = dbi_result_get_field_length(result, "sres"); + if (len != sizeof(atuple->sres)) + goto err_size; + + blob = dbi_result_get_binary(result, "sres"); + memcpy(atuple->sres, blob, len); + + len = dbi_result_get_field_length(result, "kc"); + if (len != sizeof(atuple->kc)) + goto err_size; + + blob = dbi_result_get_binary(result, "kc"); + memcpy(atuple->kc, blob, len); + + dbi_result_free(result); + + return 0; + +err_size: + dbi_result_free(result); + return -EIO; +} + +int set_authtuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int rc, upd; + struct gsm_auth_tuple atuple_old; + unsigned char *rand_str, *sres_str, *kc_str; + + /* Deletion ? */ + if (atuple == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthTuples WHERE subscriber_id=%u", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = get_authtuple_by_subscr(&atuple_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + atuple->rand, sizeof(atuple->rand), &rand_str); + dbi_conn_quote_binary_copy(conn, + atuple->sres, sizeof(atuple->sres), &sres_str); + dbi_conn_quote_binary_copy(conn, + atuple->kc, sizeof(atuple->kc), &kc_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthTuples " + "(subscriber_id, issued, use_count, " + "key_seq, rand, sres, kc) " + "VALUES (%u, datetime('now'), %u, " + "%u, %s, %s, %s ) ", + subscr->id, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str); + } else { + char *issued = atuple->key_seq == atuple_old.key_seq ? + "issued" : "datetime('now')"; + result = dbi_conn_queryf(conn, + "UPDATE AuthKeys " + "SET issued=%s, use_count=%u, " + "key_seq=%u, rand=%s, sres=%s, kc=%s " + "WHERE subscriber_id = %u", + issued, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str, subscr->id); + } + + free(rand_str); + free(sres_str); + free(kc_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + +#define BASE_QUERY "SELECT * FROM Subscriber " +struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, + enum gsm_subscriber_field field, + const char *id) +{ + dbi_result result; + const char *string; + char *quoted; + struct gsm_subscriber *subscr; + + switch (field) { + case GSM_SUBSCRIBER_IMSI: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE imsi = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_TMSI: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE tmsi = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_EXTENSION: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE extension = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_ID: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE id = %s ", quoted); + free(quoted); + break; + default: + LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); + return NULL; + } + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); + return NULL; + } + if (!dbi_result_next_row(result)) { + DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", + field, id); + dbi_result_free(result); + return NULL; + } + + subscr = subscr_alloc(); + subscr->net = net; + subscr->id = dbi_result_get_ulonglong(result, "id"); + string = dbi_result_get_string(result, "imsi"); + if (string) + strncpy(subscr->imsi, string, GSM_IMSI_LENGTH); + + string = dbi_result_get_string(result, "tmsi"); + if (string) + subscr->tmsi = tmsi_from_string(string); + + string = dbi_result_get_string(result, "name"); + if (string) + strncpy(subscr->name, string, GSM_NAME_LENGTH); + + string = dbi_result_get_string(result, "extension"); + if (string) + strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); + + subscr->lac = dbi_result_get_uint(result, "lac"); + subscr->authorized = dbi_result_get_uint(result, "authorized"); + DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", + subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, + subscr->lac, subscr->authorized); + dbi_result_free(result); + + get_equipment_by_subscr(subscr); + + return subscr; +} + +int db_sync_subscriber(struct gsm_subscriber *subscriber) +{ + dbi_result result; + char tmsi[14]; + char *q_tmsi; + + if (subscriber->tmsi != GSM_RESERVED_TMSI) { + sprintf(tmsi, "%u", subscriber->tmsi); + dbi_conn_quote_string_copy(conn, + tmsi, + &q_tmsi); + } else + q_tmsi = strdup("NULL"); + + result = dbi_conn_queryf(conn, + "UPDATE Subscriber " + "SET updated = datetime('now'), " + "name = '%s', " + "extension = '%s', " + "authorized = %i, " + "tmsi = %s, " + "lac = %i " + "WHERE imsi = %s ", + subscriber->name, + subscriber->extension, + subscriber->authorized, + q_tmsi, + subscriber->lac, + subscriber->imsi); + + free(q_tmsi); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n"); + return 1; + } + + dbi_result_free(result); + + return 0; +} + +int db_sync_equipment(struct gsm_equipment *equip) +{ + dbi_result result; + unsigned char *cm2, *cm3; + u_int8_t classmark1; + + memcpy(&classmark1, &equip->classmark1, sizeof(classmark1)); + DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x", + equip->imei, classmark1); + if (equip->classmark2_len) + DEBUGPC(DDB, ", classmark2=%s", + hexdump(equip->classmark2, equip->classmark2_len)); + if (equip->classmark3_len) + DEBUGPC(DDB, ", classmark3=%s", + hexdump(equip->classmark3, equip->classmark3_len)); + DEBUGPC(DDB, "\n"); + + dbi_conn_quote_binary_copy(conn, equip->classmark2, + equip->classmark2_len, &cm2); + dbi_conn_quote_binary_copy(conn, equip->classmark3, + equip->classmark3_len, &cm3); + + result = dbi_conn_queryf(conn, + "UPDATE Equipment SET " + "updated = datetime('now'), " + "classmark1 = %u, " + "classmark2 = %s, " + "classmark3 = %s " + "WHERE imei = '%s' ", + classmark1, cm2, cm3, equip->imei); + + free(cm2); + free(cm3); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n"); + return -EIO; + } + + dbi_result_free(result); + return 0; +} + +int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber) +{ + dbi_result result = NULL; + char tmsi[14]; + char* tmsi_quoted; + + for (;;) { + subscriber->tmsi = rand(); + if (subscriber->tmsi == GSM_RESERVED_TMSI) + continue; + + sprintf(tmsi, "%u", subscriber->tmsi); + dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted); + result = dbi_conn_queryf(conn, + "SELECT * FROM Subscriber " + "WHERE tmsi = %s ", + tmsi_quoted); + + free(tmsi_quoted); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new TMSI.\n"); + return 1; + } + if (dbi_result_get_numrows(result)) { + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", + subscriber->tmsi, subscriber->imsi); + return db_sync_subscriber(subscriber); + } + dbi_result_free(result); + } + return 0; +} + +int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber) +{ + dbi_result result = NULL; + u_int32_t try; + + for (;;) { + try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN); + result = dbi_conn_queryf(conn, + "SELECT * FROM Subscriber " + "WHERE extension = %i", + try + ); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new extension.\n"); + return 1; + } + if (dbi_result_get_numrows(result)){ + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + break; + } + dbi_result_free(result); + } + sprintf(subscriber->extension, "%i", try); + DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); + return db_sync_subscriber(subscriber); +} +/* + * try to allocate a new unique token for this subscriber and return it + * via a parameter. if the subscriber already has a token, return + * an error. + */ + +int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, u_int32_t *token) +{ + dbi_result result; + u_int32_t try; + + for (;;) { + try = rand(); + if (!try) /* 0 is an invalid token */ + continue; + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthToken " + "WHERE subscriber_id = %llu OR token = \"%08X\" ", + subscriber->id, try); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " + "while allocating new token.\n"); + return 1; + } + if (dbi_result_get_numrows(result)) { + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + break; + } + dbi_result_free(result); + } + result = dbi_conn_queryf(conn, + "INSERT INTO AuthToken " + "(subscriber_id, created, token) " + "VALUES " + "(%llu, datetime('now'), \"%08X\") ", + subscriber->id, try); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " + "IMSI %s.\n", try, subscriber->imsi); + return 1; + } + dbi_result_free(result); + *token = try; + DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); + + return 0; +} + +int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) +{ + unsigned long long equipment_id, watch_id; + dbi_result result; + + strncpy(subscriber->equipment.imei, imei, + sizeof(subscriber->equipment.imei)-1), + + result = dbi_conn_queryf(conn, + "INSERT OR IGNORE INTO Equipment " + "(imei, created, updated) " + "VALUES " + "(%s, datetime('now'), datetime('now')) ", + imei); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); + return 1; + } + + equipment_id = 0; + if (dbi_result_get_numrows_affected(result)) { + equipment_id = dbi_conn_sequence_last(conn, NULL); + } + dbi_result_free(result); + + if (equipment_id) + DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); + else { + result = dbi_conn_queryf(conn, + "SELECT id FROM Equipment " + "WHERE imei = %s ", + imei + ); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); + return 1; + } + if (!dbi_result_next_row(result)) { + LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); + dbi_result_free(result); + return 1; + } + equipment_id = dbi_result_get_ulonglong(result, "id"); + dbi_result_free(result); + } + + result = dbi_conn_queryf(conn, + "INSERT OR IGNORE INTO EquipmentWatch " + "(subscriber_id, equipment_id, created, updated) " + "VALUES " + "(%llu, %llu, datetime('now'), datetime('now')) ", + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); + return 1; + } + + watch_id = 0; + if (dbi_result_get_numrows_affected(result)) + watch_id = dbi_conn_sequence_last(conn, NULL); + + dbi_result_free(result); + if (watch_id) + DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); + else { + result = dbi_conn_queryf(conn, + "UPDATE EquipmentWatch " + "SET updated = datetime('now') " + "WHERE subscriber_id = %llu AND equipment_id = %llu ", + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); + return 1; + } + dbi_result_free(result); + DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); + } + + return 0; +} + +/* store an [unsent] SMS to the database */ +int db_sms_store(struct gsm_sms *sms) +{ + dbi_result result; + char *q_text, *q_daddr; + unsigned char *q_udata; + char *validity_timestamp = "2222-2-2"; + + /* FIXME: generate validity timestamp based on validity_minutes */ + + dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); + dbi_conn_quote_string_copy(conn, (char *)sms->dest_addr, &q_daddr); + dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len, + &q_udata); + /* FIXME: correct validity period */ + result = dbi_conn_queryf(conn, + "INSERT INTO SMS " + "(created, sender_id, receiver_id, valid_until, " + "reply_path_req, status_rep_req, protocol_id, " + "data_coding_scheme, ud_hdr_ind, dest_addr, " + "user_data, text) VALUES " + "(datetime('now'), %llu, %llu, %u, " + "%u, %u, %u, %u, %u, %s, %s, %s)", + sms->sender->id, + sms->receiver ? sms->receiver->id : 0, validity_timestamp, + sms->reply_path_req, sms->status_rep_req, sms->protocol_id, + sms->data_coding_scheme, sms->ud_hdr_ind, + q_daddr, q_udata, q_text); + free(q_text); + free(q_daddr); + free(q_udata); + + if (!result) + return -EIO; + + dbi_result_free(result); + return 0; +} + +static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result) +{ + struct gsm_sms *sms = sms_alloc(); + long long unsigned int sender_id, receiver_id; + const char *text, *daddr; + const unsigned char *user_data; + + if (!sms) + return NULL; + + sms->id = dbi_result_get_ulonglong(result, "id"); + + sender_id = dbi_result_get_ulonglong(result, "sender_id"); + sms->sender = subscr_get_by_id(net, sender_id); + + receiver_id = dbi_result_get_ulonglong(result, "receiver_id"); + sms->receiver = subscr_get_by_id(net, receiver_id); + + /* FIXME: validity */ + /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ + sms->reply_path_req = dbi_result_get_uint(result, "reply_path_req"); + sms->status_rep_req = dbi_result_get_uint(result, "status_rep_req"); + sms->ud_hdr_ind = dbi_result_get_uint(result, "ud_hdr_ind"); + sms->protocol_id = dbi_result_get_uint(result, "protocol_id"); + sms->data_coding_scheme = dbi_result_get_uint(result, + "data_coding_scheme"); + /* sms->msg_ref is temporary and not stored in DB */ + + daddr = dbi_result_get_string(result, "dest_addr"); + if (daddr) { + strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr)); + sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0'; + } + + sms->user_data_len = dbi_result_get_field_length(result, "user_data"); + user_data = dbi_result_get_binary(result, "user_data"); + if (sms->user_data_len > sizeof(sms->user_data)) + sms->user_data_len = (u_int8_t) sizeof(sms->user_data); + memcpy(sms->user_data, user_data, sms->user_data_len); + + text = dbi_result_get_string(result, "text"); + if (text) { + strncpy(sms->text, text, sizeof(sms->text)); + sms->text[sizeof(sms->text)-1] = '\0'; + } + return sms; +} + +/* retrieve the next unsent SMS with ID >= min_id */ +struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT * FROM SMS,Subscriber " + "WHERE sms.id >= %llu AND sms.sent is NULL " + "AND sms.receiver_id = subscriber.id " + "AND subscriber.lac > 0 " + "ORDER BY sms.id LIMIT 1", + min_id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(net, result); + + dbi_result_free(result); + + return sms; +} + +struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT * FROM SMS,Subscriber " + "WHERE sms.receiver_id >= %llu AND sms.sent is NULL " + "AND sms.receiver_id = subscriber.id " + "AND subscriber.lac > 0 " + "ORDER BY sms.receiver_id, id LIMIT 1", + min_subscr_id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(net, result); + + dbi_result_free(result); + + return sms; +} + +/* retrieve the next unsent SMS for a given subscriber */ +struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT * FROM SMS,Subscriber " + "WHERE sms.receiver_id = %llu AND sms.sent is NULL " + "AND sms.receiver_id = subscriber.id " + "AND subscriber.lac > 0 " + "ORDER BY sms.id LIMIT 1", + subscr->id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(subscr->net, result); + + dbi_result_free(result); + + return sms; +} + +/* mark a given SMS as read */ +int db_sms_mark_sent(struct gsm_sms *sms) +{ + dbi_result result; + + result = dbi_conn_queryf(conn, + "UPDATE SMS " + "SET sent = datetime('now') " + "WHERE id = %llu", sms->id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id); + return 1; + } + + dbi_result_free(result); + return 0; +} + +/* increase the number of attempted deliveries */ +int db_sms_inc_deliver_attempts(struct gsm_sms *sms) +{ + dbi_result result; + + result = dbi_conn_queryf(conn, + "UPDATE SMS " + "SET deliver_attempts = deliver_attempts + 1 " + "WHERE id = %llu", sms->id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for " + "SMS %llu.\n", sms->id); + return 1; + } + + dbi_result_free(result); + return 0; +} + +int db_apdu_blob_store(struct gsm_subscriber *subscr, + u_int8_t apdu_id_flags, u_int8_t len, + u_int8_t *apdu) +{ + dbi_result result; + unsigned char *q_apdu; + + dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu); + + result = dbi_conn_queryf(conn, + "INSERT INTO ApduBlobs " + "(created,subscriber_id,apdu_id_flags,apdu) VALUES " + "(datetime('now'),%llu,%u,%s)", + subscr->id, apdu_id_flags, q_apdu); + + free(q_apdu); + + if (!result) + return -EIO; + + dbi_result_free(result); + return 0; +} + +int db_store_counter(struct counter *ctr) +{ + dbi_result result; + char *q_name; + + dbi_conn_quote_string_copy(conn, ctr->name, &q_name); + + result = dbi_conn_queryf(conn, + "INSERT INTO Counters " + "(timestamp,name,value) VALUES " + "(datetime('now'),%s,%lu)", q_name, ctr->value); + + free(q_name); + + if (!result) + return -EIO; + + dbi_result_free(result); + return 0; +} diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c new file mode 100644 index 000000000..3bb5309a6 --- /dev/null +++ b/openbsc/src/debug.c @@ -0,0 +1,427 @@ +/* Debugging/Logging support code */ +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.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 <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <time.h> +#include <errno.h> + +#include <openbsc/debug.h> +#include <osmocore/talloc.h> +#include <osmocore/utils.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> + +/* default categories */ +static struct debug_category default_categories[Debug_LastEntry] = { + [DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMM] = { .enabled = 1, .loglevel = LOGL_INFO }, + [DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE }, +}; + +struct debug_info { + const char *name; + const char *color; + const char *description; + int number; + int position; +}; + +struct debug_context { + struct gsm_lchan *lchan; + struct gsm_subscriber *subscr; + struct gsm_bts *bts; +}; + +static struct debug_context debug_context; +static void *tall_dbg_ctx = NULL; +static LLIST_HEAD(target_list); + +#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \ + { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER }, + +static const struct debug_info debug_info[] = { + DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "") + DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "") + DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "") + DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "") + DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "") + DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "") + DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "") + DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "") + DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "") + DEBUG_CATEGORY(DINP, "DINP", "", "") + DEBUG_CATEGORY(DMI, "DMI", "", "") + DEBUG_CATEGORY(DMIB, "DMIB", "", "") + DEBUG_CATEGORY(DMUX, "DMUX", "", "") + DEBUG_CATEGORY(DMEAS, "DMEAS", "", "") + DEBUG_CATEGORY(DSCCP, "DSCCP", "", "") + DEBUG_CATEGORY(DMSC, "DMSC", "", "") + DEBUG_CATEGORY(DMGCP, "DMGCP", "", "") + DEBUG_CATEGORY(DHO, "DHO", "", "") + DEBUG_CATEGORY(DDB, "DDB", "", "") + DEBUG_CATEGORY(DREF, "DREF", "", "") +}; + +static const struct value_string loglevel_strs[] = { + { 0, "EVERYTHING" }, + { 1, "DEBUG" }, + { 3, "INFO" }, + { 5, "NOTICE" }, + { 7, "ERROR" }, + { 8, "FATAL" }, + { 0, NULL }, +}; + +int debug_parse_level(const char *lvl) +{ + return get_string_value(loglevel_strs, lvl); +} + +int debug_parse_category(const char *category) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + if (!strcasecmp(debug_info[i].name+1, category)) + return debug_info[i].number; + } + + return -EINVAL; +} + +/* + * Parse the category mask. + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... + */ +void debug_parse_category_mask(struct debug_target* target, const char *_mask) +{ + int i = 0; + char *mask = strdup(_mask); + char *category_token = NULL; + + /* Disable everything to enable it afterwards */ + for (i = 0; i < ARRAY_SIZE(target->categories); ++i) + target->categories[i].enabled = 0; + + category_token = strtok(mask, ":"); + do { + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + char* colon = strstr(category_token, ","); + int length = strlen(category_token); + + if (colon) + length = colon - category_token; + + if (strncasecmp(debug_info[i].name, category_token, length) == 0) { + int number = debug_info[i].number; + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categories[number].enabled = 1; + target->categories[number].loglevel = level; + } + } + } while ((category_token = strtok(NULL, ":"))); + + free(mask); +} + +static const char* color(int subsys) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + if (debug_info[i].number == subsys) + return debug_info[i].color; + } + + return ""; +} + +static void _output(struct debug_target *target, unsigned int subsys, char *file, int line, + int cont, const char *format, va_list ap) +{ + char col[30]; + char sub[30]; + char tim[30]; + char buf[4096]; + char final[4096]; + + /* prepare the data */ + col[0] = '\0'; + sub[0] = '\0'; + tim[0] = '\0'; + buf[0] = '\0'; + + /* are we using color */ + if (target->use_color) { + snprintf(col, sizeof(col), "%s", color(subsys)); + col[sizeof(col)-1] = '\0'; + } + vsnprintf(buf, sizeof(buf), format, ap); + buf[sizeof(buf)-1] = '\0'; + + if (!cont) { + if (target->print_timestamp) { + char *timestr; + time_t tm; + tm = time(NULL); + timestr = ctime(&tm); + timestr[strlen(timestr)-1] = '\0'; + snprintf(tim, sizeof(tim), "%s ", timestr); + tim[sizeof(tim)-1] = '\0'; + } + snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line); + sub[sizeof(sub)-1] = '\0'; + } + + snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf); + final[sizeof(final)-1] = '\0'; + target->output(target, final); +} + + +static void _debugp(unsigned int subsys, int level, char *file, int line, + int cont, const char *format, va_list ap) +{ + struct debug_target *tar; + + llist_for_each_entry(tar, &target_list, entry) { + struct debug_category *category; + int output = 0; + + category = &tar->categories[subsys]; + /* subsystem is not supposed to be debugged */ + if (!category->enabled) + continue; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + continue; + + /* Check the category log level */ + if (category->loglevel != 0 && level < category->loglevel) + continue; + + /* + * Apply filters here... if that becomes messy we will need to put + * filters in a list and each filter will say stop, continue, output + */ + if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) { + output = 1; + } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0 + && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) { + output = 1; + } + + if (output) { + /* FIXME: copying the va_list is an ugly workaround against a bug + * hidden somewhere in _output. If we do not copy here, the first + * call to _output() will corrupt the va_list contents, and any + * further _output() calls with the same va_list will segfault */ + va_list bp; + va_copy(bp, ap); + _output(tar, subsys, file, line, cont, format, bp); + va_end(bp); + } + } +} + +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap); + va_end(ap); +} + +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _debugp(subsys, level, file, line, cont, format, ap); + va_end(ap); +} + +static char hexd_buff[4096]; + +char *hexdump(const unsigned char *buf, int len) +{ + int i; + char *cur = hexd_buff; + + hexd_buff[0] = 0; + for (i = 0; i < len; i++) { + int len_remain = sizeof(hexd_buff) - (cur - hexd_buff); + int rc = snprintf(cur, len_remain, "%02x ", buf[i]); + if (rc <= 0) + break; + cur += rc; + } + hexd_buff[sizeof(hexd_buff)-1] = 0; + return hexd_buff; +} + + + +void debug_add_target(struct debug_target *target) +{ + llist_add_tail(&target->entry, &target_list); +} + +void debug_del_target(struct debug_target *target) +{ + llist_del(&target->entry); +} + +void debug_reset_context(void) +{ + memset(&debug_context, 0, sizeof(debug_context)); +} + +/* currently we are not reffing these */ +void debug_set_context(int ctx, void *value) +{ + switch (ctx) { + case BSC_CTX_LCHAN: + debug_context.lchan = (struct gsm_lchan *) value; + break; + case BSC_CTX_SUBSCR: + debug_context.subscr = (struct gsm_subscriber *) value; + break; + case BSC_CTX_BTS: + debug_context.bts = (struct gsm_bts *) value; + break; + case BSC_CTX_SCCP: + break; + default: + break; + } +} + +void debug_set_imsi_filter(struct debug_target *target, const char *imsi) +{ + if (imsi) { + target->filter_map |= DEBUG_FILTER_IMSI; + target->imsi_filter = talloc_strdup(target, imsi); + } else if (target->imsi_filter) { + target->filter_map &= ~DEBUG_FILTER_IMSI; + talloc_free(target->imsi_filter); + target->imsi_filter = NULL; + } +} + +void debug_set_all_filter(struct debug_target *target, int all) +{ + if (all) + target->filter_map |= DEBUG_FILTER_ALL; + else + target->filter_map &= ~DEBUG_FILTER_ALL; +} + +void debug_set_use_color(struct debug_target *target, int use_color) +{ + target->use_color = use_color; +} + +void debug_set_print_timestamp(struct debug_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +void debug_set_log_level(struct debug_target *target, int log_level) +{ + target->loglevel = log_level; +} + +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level) +{ + if (category >= Debug_LastEntry) + return; + target->categories[category].enabled = !!enable; + target->categories[category].loglevel = level; +} + +static void _stderr_output(struct debug_target *target, const char *log) +{ + fprintf(target->tgt_stdout.out, "%s", log); + fflush(target->tgt_stdout.out); +} + +struct debug_target *debug_target_create(void) +{ + struct debug_target *target; + + target = talloc_zero(tall_dbg_ctx, struct debug_target); + if (!target) + return NULL; + + INIT_LLIST_HEAD(&target->entry); + memcpy(target->categories, default_categories, sizeof(default_categories)); + target->use_color = 1; + target->print_timestamp = 0; + target->loglevel = 0; + return target; +} + +struct debug_target *debug_target_create_stderr(void) +{ + struct debug_target *target; + + target = debug_target_create(); + if (!target) + return NULL; + + target->tgt_stdout.out = stderr; + target->output = _stderr_output; + return target; +} + +void debug_init(void) +{ + tall_dbg_ctx = talloc_named_const(NULL, 1, "debug"); +} diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c new file mode 100644 index 000000000..50fbceccd --- /dev/null +++ b/openbsc/src/e1_config.c @@ -0,0 +1,236 @@ +#include <string.h> +#include <errno.h> + +#include <netinet/in.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/e1_input.h> +#include <openbsc/trau_frame.h> +#include <openbsc/trau_mux.h> +#include <openbsc/misdn.h> +#include <openbsc/ipaccess.h> +#include <osmocore/talloc.h> +#include <openbsc/debug.h> + +#define SAPI_L2ML 0 +#define SAPI_OML 62 +#define SAPI_RSL 0 /* 63 ? */ + +/* The e1_reconfig_*() functions below tale the configuration present in the + * bts/trx/ts data structures and ensure the E1 configuration reflects the + * timeslot/subslot/TEI configuration */ + +int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) +{ + struct gsm_e1_subslot *e1_link = &ts->e1_link; + struct e1inp_line *line; + struct e1inp_ts *e1_ts; + + DEBUGP(DMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); + + if (!e1_link->e1_ts) + return 0; + + line = e1inp_line_get_create(e1_link->e1_nr); + if (!line) + return -ENOMEM; + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + e1_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config(e1_ts, line, E1INP_TS_TYPE_TRAU); + subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); + break; + default: + break; + } + + return 0; +} + +int e1_reconfig_trx(struct gsm_bts_trx *trx) +{ + struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; + struct e1inp_ts *sign_ts; + struct e1inp_line *line; + struct e1inp_sign_link *rsl_link; + int i; + + if (!e1_link->e1_ts) + return -EINVAL; + + /* RSL Link */ + line = e1inp_line_get_create(e1_link->e1_nr); + if (!line) + return -ENOMEM; + sign_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config(sign_ts, line, E1INP_TS_TYPE_SIGN); + rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, + trx, trx->rsl_tei, SAPI_RSL); + if (!rsl_link) + return -ENOMEM; + if (trx->rsl_link) + e1inp_sign_link_destroy(trx->rsl_link); + trx->rsl_link = rsl_link; + + for (i = 0; i < TRX_NR_TS; i++) + e1_reconfig_ts(&trx->ts[i]); + + return 0; +} + +int e1_reconfig_bts(struct gsm_bts *bts) +{ + struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; + struct e1inp_ts *sign_ts; + struct e1inp_line *line; + struct e1inp_sign_link *oml_link; + struct gsm_bts_trx *trx; + + DEBUGP(DMI, "e1_reconfig_bts(%u)\n", bts->nr); + + if (!e1_link->e1_ts) + return -EINVAL; + + /* OML link */ + line = e1inp_line_get_create(e1_link->e1_nr); + if (!line) + return -ENOMEM; + sign_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config(sign_ts, line, E1INP_TS_TYPE_SIGN); + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, + bts->c0, bts->oml_tei, SAPI_OML); + if (!oml_link) + return -ENOMEM; + if (bts->oml_link) + e1inp_sign_link_destroy(bts->oml_link); + bts->oml_link = oml_link; + + llist_for_each_entry(trx, &bts->trx_list, list) + e1_reconfig_trx(trx); + + /* notify E1 input something has changed */ + return e1inp_line_update(line); +} + +#if 0 +/* do some compiled-in configuration for our BTS/E1 setup */ +int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) +{ + struct e1inp_line *line; + struct e1inp_ts *sign_ts; + struct e1inp_sign_link *oml_link, *rsl_link; + struct gsm_bts_trx *trx = bts->c0; + int base_ts; + + switch (bts->nr) { + case 0: + /* First BTS uses E1 TS 01,02,03,04,05 */ + base_ts = HARDCODED_BTS0_TS - 1; + break; + case 1: + /* Second BTS uses E1 TS 06,07,08,09,10 */ + base_ts = HARDCODED_BTS1_TS - 1; + break; + case 2: + /* Third BTS uses E1 TS 11,12,13,14,15 */ + base_ts = HARDCODED_BTS2_TS - 1; + default: + return -EINVAL; + } + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) + return -ENOMEM; + + /* create E1 timeslots for signalling and TRAU frames */ + e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN); + e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU); + e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU); + + /* create signalling links for TS1 */ + sign_ts = &line->ts[base_ts+1-1]; + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, + trx, TEI_OML, SAPI_OML); + rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, + trx, TEI_RSL, SAPI_RSL); + + /* create back-links from bts/trx */ + bts->oml_link = oml_link; + trx->rsl_link = rsl_link; + + /* enable subchannel demuxer on TS2 */ + subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3); + + /* enable subchannel demuxer on TS3 */ + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0); + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3); + + trx = gsm_bts_trx_num(bts, 1); + if (trx) { + /* create E1 timeslots for TRAU frames of TRX1 */ + e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU); + e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU); + + /* create RSL signalling link for TRX1 */ + sign_ts = &line->ts[base_ts+1-1]; + rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, + trx, TEI_RSL+1, SAPI_RSL); + /* create back-links from trx */ + trx->rsl_link = rsl_link; + + /* enable subchannel demuxer on TS2 */ + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0); + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3); + + /* enable subchannel demuxer on TS3 */ + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0); + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3); + } + + return mi_setup(cardnr, line, release_l2); +} +#endif + +/* configure pseudo E1 line in ip.access style and connect to BTS */ +int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin) +{ + struct e1inp_line *line; + struct e1inp_ts *sign_ts, *rsl_ts; + struct e1inp_sign_link *oml_link, *rsl_link; + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) + return -ENOMEM; + + /* create E1 timeslots for signalling and TRAU frames */ + e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); + e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN); + + /* create signalling links for TS1 */ + sign_ts = &line->ts[1-1]; + rsl_ts = &line->ts[2-1]; + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, + bts->c0, 0xff, 0); + rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL, + bts->c0, 0, 0); + + /* create back-links from bts/trx */ + bts->oml_link = oml_link; + bts->c0->rsl_link = rsl_link; + + /* default port at BTS for incoming connections is 3006 */ + if (sin->sin_port == 0) + sin->sin_port = htons(3006); + + return ipaccess_connect(line, sin); +} diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c new file mode 100644 index 000000000..c20359c09 --- /dev/null +++ b/openbsc/src/e1_input.c @@ -0,0 +1,555 @@ +/* OpenBSC Abis interface to E1 */ + +/* (C) 2008-2009 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <mISDNif.h> + +//#define AF_COMPATIBILITY_FUNC +//#include <compat_af_isdn.h> +#ifndef AF_ISDN +#define AF_ISDN 34 +#define PF_ISDN AF_ISDN +#endif + +#include <osmocore/select.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/e1_input.h> +#include <openbsc/abis_nm.h> +#include <openbsc/abis_rsl.h> +#include <osmocore/linuxlist.h> +#include <openbsc/subchan_demux.h> +#include <openbsc/trau_frame.h> +#include <openbsc/trau_mux.h> +#include <osmocore/talloc.h> +#include <openbsc/signal.h> +#include <openbsc/misdn.h> + +#define NUM_E1_TS 32 + +/* list of all E1 drivers */ +LLIST_HEAD(e1inp_driver_list); + +/* list of all E1 lines */ +LLIST_HEAD(e1inp_line_list); + +static void *tall_sigl_ctx; + +/* to be implemented, e.g. by bsc_hack.c */ +void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx); + +/* + * pcap writing of the misdn load + * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat + */ +#define DLT_LINUX_LAPD 177 +#define PCAP_INPUT 0 +#define PCAP_OUTPUT 1 + +struct pcap_hdr { + u_int32_t magic_number; + u_int16_t version_major; + u_int16_t version_minor; + int32_t thiszone; + u_int32_t sigfigs; + u_int32_t snaplen; + u_int32_t network; +} __attribute__((packed)); + +struct pcaprec_hdr { + u_int32_t ts_sec; + u_int32_t ts_usec; + u_int32_t incl_len; + u_int32_t orig_len; +} __attribute__((packed)); + +struct fake_linux_lapd_header { + u_int16_t pkttype; + u_int16_t hatype; + u_int16_t halen; + u_int64_t addr; + int16_t protocol; +} __attribute__((packed)); + +struct lapd_header { + u_int8_t ea1 : 1; + u_int8_t cr : 1; + u_int8_t sapi : 6; + u_int8_t ea2 : 1; + u_int8_t tei : 7; + u_int8_t control_foo; /* fake UM's ... */ +} __attribute__((packed)); + +static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2, hatype_offset); +static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4, halen_offset); +static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6, addr_offset); +static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14, proto_offset); +static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size); + + +static int pcap_fd = -1; + +void e1_set_pcap_fd(int fd) +{ + int ret; + struct pcap_hdr header = { + .magic_number = 0xa1b2c3d4, + .version_major = 2, + .version_minor = 4, + .thiszone = 0, + .sigfigs = 0, + .snaplen = 65535, + .network = DLT_LINUX_LAPD, + }; + + pcap_fd = fd; + ret = write(pcap_fd, &header, sizeof(header)); +} + +/* This currently only works for the D-Channel */ +static void write_pcap_packet(int direction, int sapi, int tei, + struct msgb *msg) { + if (pcap_fd < 0) + return; + + int ret; + time_t cur_time; + struct tm *tm; + int mi_head = (direction==PCAP_INPUT) ? MISDN_HEADER_LEN : 0; + + struct fake_linux_lapd_header header = { + .pkttype = 4, + .hatype = 0, + .halen = 0, + .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1, + .protocol = ntohs(48), + }; + + struct lapd_header lapd_header = { + .ea1 = 0, + .cr = direction == PCAP_OUTPUT ? 1 : 0, + .sapi = sapi & 0x3F, + .ea2 = 1, + .tei = tei & 0x7F, + .control_foo = 0x03 /* UI */, + }; + + struct pcaprec_hdr payload_header = { + .ts_sec = 0, + .ts_usec = 0, + .incl_len = msg->len + sizeof(struct fake_linux_lapd_header) + + sizeof(struct lapd_header) + - mi_head, + .orig_len = msg->len + sizeof(struct fake_linux_lapd_header) + + sizeof(struct lapd_header) + - mi_head, + }; + + + cur_time = time(NULL); + tm = localtime(&cur_time); + payload_header.ts_sec = mktime(tm); + + ret = write(pcap_fd, &payload_header, sizeof(payload_header)); + ret = write(pcap_fd, &header, sizeof(header)); + ret = write(pcap_fd, &lapd_header, sizeof(lapd_header)); + ret = write(pcap_fd, msg->data + mi_head, + msg->len - mi_head); +} + +static const char *sign_types[] = { + [E1INP_SIGN_NONE] = "None", + [E1INP_SIGN_OML] = "OML", + [E1INP_SIGN_RSL] = "RSL", +}; +const char *e1inp_signtype_name(enum e1inp_sign_type tp) +{ + if (tp >= ARRAY_SIZE(sign_types)) + return "undefined"; + return sign_types[tp]; +} + +static const char *ts_types[] = { + [E1INP_TS_TYPE_NONE] = "None", + [E1INP_TS_TYPE_SIGN] = "Signalling", + [E1INP_TS_TYPE_TRAU] = "TRAU", +}; + +const char *e1inp_tstype_name(enum e1inp_ts_type tp) +{ + if (tp >= ARRAY_SIZE(ts_types)) + return "undefined"; + return ts_types[tp]; +} + +/* callback when a TRAU frame was received */ +static int subch_cb(struct subch_demux *dmx, int ch, u_int8_t *data, int len, + void *_priv) +{ + struct e1inp_ts *e1i_ts = _priv; + struct gsm_e1_subslot src_ss; + + src_ss.e1_nr = e1i_ts->line->num; + src_ss.e1_ts = e1i_ts->num; + src_ss.e1_ts_ss = ch; + + return trau_mux_input(&src_ss, data, len); +} + +int abis_rsl_sendmsg(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link; + struct e1inp_driver *e1inp_driver; + struct e1inp_ts *e1i_ts; + + msg->l2h = msg->data; + + if (!msg->trx) { + LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL: %s\n", + hexdump(msg->data, msg->len)); + talloc_free(msg); + return -EINVAL; + } else if (!msg->trx->rsl_link) { + LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx->rsl_link == NULL: %s\n", + hexdump(msg->data, msg->len)); + talloc_free(msg); + return -EIO; + } + + sign_link = msg->trx->rsl_link; + e1i_ts = sign_link->ts; + if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { + /* notify the driver we have something to write */ + e1inp_driver = sign_link->ts->line->driver; + e1inp_driver->want_write(e1i_ts); + } + msgb_enqueue(&sign_link->tx_list, msg); + + /* dump it */ + write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); + + return 0; +} + +int _abis_nm_sendmsg(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link; + struct e1inp_driver *e1inp_driver; + struct e1inp_ts *e1i_ts; + + msg->l2h = msg->data; + + if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) { + LOGP(DRSL, LOGL_ERROR, "nm_sendmsg: msg->trx == NULL\n"); + return -EINVAL; + } + + sign_link = msg->trx->bts->oml_link; + e1i_ts = sign_link->ts; + if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { + /* notify the driver we have something to write */ + e1inp_driver = sign_link->ts->line->driver; + e1inp_driver->want_write(e1i_ts); + } + msgb_enqueue(&sign_link->tx_list, msg); + + /* dump it */ + write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); + + return 0; +} + +/* Timeslot */ + +/* configure and initialize one e1inp_ts */ +int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line, + enum e1inp_ts_type type) +{ + if (ts->type == type && ts->line && line) + return 0; + + ts->type = type; + ts->line = line; + + switch (type) { + case E1INP_TS_TYPE_SIGN: + INIT_LLIST_HEAD(&ts->sign.sign_links); + break; + case E1INP_TS_TYPE_TRAU: + subchan_mux_init(&ts->trau.mux); + ts->trau.demux.out_cb = subch_cb; + ts->trau.demux.data = ts; + subch_demux_init(&ts->trau.demux); + break; + default: + LOGP(DMI, LOGL_ERROR, "unsupported E1 timeslot type %u\n", + ts->type); + return -EINVAL; + } + return 0; +} + +static struct e1inp_line *e1inp_line_get(u_int8_t e1_nr) +{ + struct e1inp_line *e1i_line; + + /* iterate over global list of e1 lines */ + llist_for_each_entry(e1i_line, &e1inp_line_list, list) { + if (e1i_line->num == e1_nr) + return e1i_line; + } + return NULL; +} + +struct e1inp_line *e1inp_line_get_create(u_int8_t e1_nr) +{ + struct e1inp_line *line; + int i; + + line = e1inp_line_get(e1_nr); + if (line) + return line; + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) + return NULL; + + line->num = e1_nr; + for (i = 0; i < NUM_E1_TS; i++) { + line->ts[i].num = i+1; + line->ts[i].line = line; + } + llist_add_tail(&line->list, &e1inp_line_list); + + return line; +} + +static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr) +{ + struct e1inp_line *e1i_line; + + e1i_line = e1inp_line_get(e1_nr); + if (!e1i_line) + return NULL; + + return &e1i_line->ts[ts_nr-1]; +} + +struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr) +{ + struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr); + + if (!e1i_ts) + return NULL; + + return &e1i_ts->trau.mux; +} + +/* Signalling Link */ + +struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i, + u_int8_t tei, u_int8_t sapi) +{ + struct e1inp_sign_link *link; + + llist_for_each_entry(link, &e1i->sign.sign_links, list) { + if (link->sapi == sapi && link->tei == tei) + return link; + } + + return NULL; +} + +/* create a new signalling link in a E1 timeslot */ + +struct e1inp_sign_link * +e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, + struct gsm_bts_trx *trx, u_int8_t tei, + u_int8_t sapi) +{ + struct e1inp_sign_link *link; + + if (ts->type != E1INP_TS_TYPE_SIGN) + return NULL; + + link = talloc_zero(tall_sigl_ctx, struct e1inp_sign_link); + if (!link) + return NULL; + + link->ts = ts; + link->type = type; + INIT_LLIST_HEAD(&link->tx_list); + link->trx = trx; + link->tei = tei; + link->sapi = sapi; + + llist_add_tail(&link->list, &ts->sign.sign_links); + + return link; +} + +void e1inp_sign_link_destroy(struct e1inp_sign_link *link) +{ + llist_del(&link->list); + talloc_free(link); +} + +/* the E1 driver tells us he has received something on a TS */ +int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, + u_int8_t tei, u_int8_t sapi) +{ + struct e1inp_sign_link *link; + int ret; + + switch (ts->type) { + case E1INP_TS_TYPE_SIGN: + /* consult the list of signalling links */ + write_pcap_packet(PCAP_INPUT, sapi, tei, msg); + link = e1inp_lookup_sign_link(ts, tei, sapi); + if (!link) { + LOGP(DMI, LOGL_ERROR, "didn't find signalling link for " + "tei %d, sapi %d\n", tei, sapi); + return -EINVAL; + } + + debug_set_context(BSC_CTX_BTS, link->trx->bts); + switch (link->type) { + case E1INP_SIGN_OML: + msg->trx = link->trx; + ret = abis_nm_rcvmsg(msg); + break; + case E1INP_SIGN_RSL: + msg->trx = link->trx; + ret = abis_rsl_rcvmsg(msg); + break; + default: + ret = -EINVAL; + LOGP(DMI, LOGL_ERROR, "unknown link type %u\n", link->type); + break; + } + break; + case E1INP_TS_TYPE_TRAU: + ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg)); + break; + default: + ret = -EINVAL; + LOGP(DMI, LOGL_ERROR, "unknown TS type %u\n", ts->type); + break; + } + + return ret; +} + +#define TSX_ALLOC_SIZE 4096 + +/* called by driver if it wants to transmit on a given TS */ +struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts, + struct e1inp_sign_link **sign_link) +{ + struct e1inp_sign_link *link; + struct msgb *msg = NULL; + int len; + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_SIGN: + /* FIXME: implement this round robin */ + llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) { + msg = msgb_dequeue(&link->tx_list); + if (msg) { + if (sign_link) + *sign_link = link; + break; + } + } + break; + case E1INP_TS_TYPE_TRAU: + msg = msgb_alloc(TSX_ALLOC_SIZE, "TRAU_TX"); + if (!msg) + return NULL; + len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40); + msgb_put(msg, 40); + break; + default: + LOGP(DMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type); + return NULL; + } + return msg; +} + +/* called by driver in case some kind of link state event */ +int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi) +{ + struct e1inp_sign_link *link; + + link = e1inp_lookup_sign_link(ts, tei, sapi); + if (!link) + return -EINVAL; + + /* FIXME: report further upwards */ + input_event(evt, link->type, link->trx); + return 0; +} + +/* register a driver with the E1 core */ +int e1inp_driver_register(struct e1inp_driver *drv) +{ + llist_add_tail(&drv->list, &e1inp_driver_list); + return 0; +} + +int e1inp_line_update(struct e1inp_line *line) +{ + return mi_e1_line_update(line); +} + +static int e1i_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + if (subsys != SS_GLOBAL || + signal != S_GLOBAL_SHUTDOWN) + return 0; + + if (pcap_fd) { + close(pcap_fd); + pcap_fd = -1; + } + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_e1_inp(void) +{ + tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1, + "e1inp_sign_link"); + register_signal_handler(SS_GLOBAL, e1i_sig_cb, NULL); +} diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c new file mode 100644 index 000000000..fb90ae3de --- /dev/null +++ b/openbsc/src/gsm_04_08.c @@ -0,0 +1,2920 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <netinet/in.h> + +#include <openbsc/db.h> +#include <osmocore/msgb.h> +#include <osmocore/bitvec.h> +#include <osmocore/tlv.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/gsm_04_11.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/paging.h> +#include <openbsc/signal.h> +#include <openbsc/trau_frame.h> +#include <openbsc/trau_mux.h> +#include <openbsc/rtp_proxy.h> +#include <osmocore/talloc.h> +#include <osmocore/gsm48.h> +#include <openbsc/transaction.h> +#include <openbsc/ussd.h> +#include <openbsc/silent_call.h> + +void *tall_locop_ctx; + +int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi); +static int gsm48_tx_simple(struct gsm_lchan *lchan, + u_int8_t pdisc, u_int8_t msg_type); +static void schedule_reject(struct gsm_lchan *lchan); + +struct gsm_lai { + u_int16_t mcc; + u_int16_t mnc; + u_int16_t lac; +}; + +static u_int32_t new_callref = 0x80000001; + +static int authorize_subscriber(struct gsm_loc_updating_operation *loc, + struct gsm_subscriber *subscriber) +{ + if (!subscriber) + return 0; + + /* + * Do not send accept yet as more information should arrive. Some + * phones will not send us the information and we will have to check + * what we want to do with that. + */ + if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) + return 0; + + switch (subscriber->net->auth_policy) { + case GSM_AUTH_POLICY_CLOSED: + return subscriber->authorized; + case GSM_AUTH_POLICY_TOKEN: + if (subscriber->authorized) + return subscriber->authorized; + return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); + case GSM_AUTH_POLICY_ACCEPT_ALL: + return 1; + default: + return 0; + } +} + +static void release_loc_updating_req(struct gsm_lchan *lchan) +{ + if (!lchan->loc_operation) + return; + + bsc_del_timer(&lchan->loc_operation->updating_timer); + talloc_free(lchan->loc_operation); + lchan->loc_operation = 0; + put_lchan(lchan); +} + +static void allocate_loc_updating_req(struct gsm_lchan *lchan) +{ + use_lchan(lchan); + release_loc_updating_req(lchan); + + lchan->loc_operation = talloc_zero(tall_locop_ctx, + struct gsm_loc_updating_operation); +} + +static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg) +{ + if (authorize_subscriber(lchan->loc_operation, lchan->subscr)) { + int rc; + + db_subscriber_alloc_tmsi(lchan->subscr); + release_loc_updating_req(lchan); + rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi); + if (lchan->ts->trx->bts->network->send_mm_info) { + /* send MM INFO with network name */ + rc = gsm48_tx_mm_info(msg->lchan); + } + + /* call subscr_update after putting the loc_upd_acc + * in the transmit queue, since S_SUBSCR_ATTACHED might + * trigger further action like SMS delivery */ + subscr_update(lchan->subscr, msg->trx->bts, + GSM_SUBSCRIBER_UPDATE_ATTACHED); + /* try to close channel ASAP */ + lchan_auto_release(lchan); + return rc; + } + + return 0; +} + +static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_trans *trans, *temp; + + if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE) + return 0; + + /* + * Cancel any outstanding location updating request + * operation taking place on the lchan. + */ + struct gsm_lchan *lchan = (struct gsm_lchan *)signal_data; + if (!lchan) + return 0; + + release_loc_updating_req(lchan); + + /* Free all transactions that are associated with the released lchan */ + /* FIXME: this is not neccessarily the right thing to do, we should + * only set trans->lchan to NULL and wait for another lchan to be + * established to the same MM entity (phone/subscriber) */ + llist_for_each_entry_safe(trans, temp, &lchan->ts->trx->bts->network->trans_list, entry) { + if (trans->lchan == lchan) + trans_free(trans); + } + + return 0; +} + +/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ +int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; + gh->data[0] = cause; + + LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " + "LAC=%u BTS=%u\n", lchan->subscr ? + subscr_name(lchan->subscr) : "unknown", + lchan->ts->trx->bts->location_area_code, lchan->ts->trx->bts->nr); + + counter_inc(bts->network->stats.loc_upd_resp.reject); + + return gsm48_sendmsg(msg, NULL); +} + +/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ +int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_loc_area_id *lai; + u_int8_t *mid; + + msg->lchan = lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; + + lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); + gsm48_generate_lai(lai, bts->network->country_code, + bts->network->network_code, bts->location_area_code); + + mid = msgb_put(msg, GSM48_MID_TMSI_LEN); + gsm48_generate_mid_from_tmsi(mid, tmsi); + + DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); + + counter_inc(bts->network->stats.loc_upd_resp.accept); + + return gsm48_sendmsg(msg, NULL); +} + +/* Transmit Chapter 9.2.10 Identity Request */ +static int mm_tx_identity_req(struct gsm_lchan *lchan, u_int8_t id_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_ID_REQ; + gh->data[0] = id_type; + + return gsm48_sendmsg(msg, NULL); +} + + +/* Parse Chapter 9.2.11 Identity Response */ +static int mm_rx_id_resp(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm_lchan *lchan = msg->lchan; + struct gsm_bts *bts = lchan->ts->trx->bts; + struct gsm_network *net = bts->network; + u_int8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; + char mi_string[GSM48_MI_SIZE]; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); + DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data); + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + /* look up subscriber based on IMSI, create if not found */ + if (!lchan->subscr) { + lchan->subscr = subscr_get_by_imsi(net, mi_string); + if (!lchan->subscr) + lchan->subscr = db_create_subscriber(net, mi_string); + } + if (lchan->loc_operation) + lchan->loc_operation->waiting_for_imsi = 0; + break; + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + /* update subscribe <-> IMEI mapping */ + if (lchan->subscr) { + db_subscriber_assoc_imei(lchan->subscr, mi_string); + db_sync_equipment(&lchan->subscr->equipment); + } + if (lchan->loc_operation) + lchan->loc_operation->waiting_for_imei = 0; + break; + } + + /* Check if we can let the mobile station enter */ + return gsm0408_authorize(lchan, msg); +} + + +static void loc_upd_rej_cb(void *data) +{ + struct gsm_lchan *lchan = data; + struct gsm_bts *bts = lchan->ts->trx->bts; + + release_loc_updating_req(lchan); + gsm0408_loc_upd_rej(lchan, bts->network->reject_cause); + lchan_auto_release(lchan); +} + +static void schedule_reject(struct gsm_lchan *lchan) +{ + lchan->loc_operation->updating_timer.cb = loc_upd_rej_cb; + lchan->loc_operation->updating_timer.data = lchan; + bsc_schedule_timer(&lchan->loc_operation->updating_timer, 5, 0); +} + +static const char *lupd_name(u_int8_t type) +{ + switch (type) { + case GSM48_LUPD_NORMAL: + return "NORMAL"; + case GSM48_LUPD_PERIODIC: + return "PEROIDOC"; + case GSM48_LUPD_IMSI_ATT: + return "IMSI ATTACH"; + default: + return "UNKNOWN"; + } +} + +/* Chapter 9.2.15: Receive Location Updating Request */ +static int mm_rx_loc_upd_req(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_loc_upd_req *lu; + struct gsm_subscriber *subscr = NULL; + struct gsm_lchan *lchan = msg->lchan; + struct gsm_bts *bts = lchan->ts->trx->bts; + u_int8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + int rc; + + lu = (struct gsm48_loc_upd_req *) gh->data; + + mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); + + DEBUGPC(DMM, "mi_type=0x%02x MI(%s) type=%s ", mi_type, mi_string, + lupd_name(lu->type)); + + dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); + + switch (lu->type) { + case GSM48_LUPD_NORMAL: + counter_inc(bts->network->stats.loc_upd_type.normal); + break; + case GSM48_LUPD_IMSI_ATT: + counter_inc(bts->network->stats.loc_upd_type.attach); + break; + case GSM48_LUPD_PERIODIC: + counter_inc(bts->network->stats.loc_upd_type.periodic); + break; + } + + /* + * Pseudo Spoof detection: Just drop a second/concurrent + * location updating request. + */ + if (lchan->loc_operation) { + DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", + lchan->loc_operation); + gsm0408_loc_upd_rej(lchan, GSM48_REJECT_PROTOCOL_ERROR); + return 0; + } + + allocate_loc_updating_req(lchan); + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + DEBUGPC(DMM, "\n"); + /* we always want the IMEI, too */ + rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI); + lchan->loc_operation->waiting_for_imei = 1; + + /* look up subscriber based on IMSI, create if not found */ + subscr = subscr_get_by_imsi(bts->network, mi_string); + if (!subscr) { + subscr = db_create_subscriber(bts->network, mi_string); + } + break; + case GSM_MI_TYPE_TMSI: + DEBUGPC(DMM, "\n"); + /* we always want the IMEI, too */ + rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI); + lchan->loc_operation->waiting_for_imei = 1; + + /* look up the subscriber based on TMSI, request IMSI if it fails */ + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + if (!subscr) { + /* send IDENTITY REQUEST message to get IMSI */ + rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI); + lchan->loc_operation->waiting_for_imsi = 1; + } + break; + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + /* no sim card... FIXME: what to do ? */ + DEBUGPC(DMM, "unimplemented mobile identity type\n"); + break; + default: + DEBUGPC(DMM, "unknown mobile identity type\n"); + break; + } + + /* schedule the reject timer */ + schedule_reject(lchan); + + if (!subscr) { + DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n"); + /* FIXME: request id? close channel? */ + return -EINVAL; + } + + lchan->subscr = subscr; + lchan->subscr->equipment.classmark1 = lu->classmark1; + + /* check if we can let the subscriber into our network immediately + * or if we need to wait for identity responses. */ + return gsm0408_authorize(lchan, msg); +} + +#if 0 +static u_int8_t to_bcd8(u_int8_t val) +{ + return ((val / 10) << 4) | (val % 10); +} +#endif + +/* Section 9.2.15a */ +int gsm48_tx_mm_info(struct gsm_lchan *lchan) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm_network *net = lchan->ts->trx->bts->network; + u_int8_t *ptr8; + int name_len, name_pad; +#if 0 + time_t cur_t; + struct tm* cur_time; + int tz15min; +#endif + + msg->lchan = lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_INFO; + + if (net->name_long) { +#if 0 + name_len = strlen(net->name_long); + /* 10.5.3.5a */ + ptr8 = msgb_put(msg, 3); + ptr8[0] = GSM48_IE_NAME_LONG; + ptr8[1] = name_len*2 +1; + ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ + + ptr16 = (u_int16_t *) msgb_put(msg, name_len*2); + for (i = 0; i < name_len; i++) + ptr16[i] = htons(net->name_long[i]); + + /* FIXME: Use Cell Broadcast, not UCS-2, since + * UCS-2 is only supported by later revisions of the spec */ +#endif + name_len = (strlen(net->name_long)*7)/8; + name_pad = (8 - strlen(net->name_long)*7)%8; + if (name_pad > 0) + name_len++; + /* 10.5.3.5a */ + ptr8 = msgb_put(msg, 3); + ptr8[0] = GSM48_IE_NAME_LONG; + ptr8[1] = name_len +1; + ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ + + ptr8 = msgb_put(msg, name_len); + gsm_7bit_encode(ptr8, net->name_long); + + } + + if (net->name_short) { +#if 0 + name_len = strlen(net->name_short); + /* 10.5.3.5a */ + ptr8 = (u_int8_t *) msgb_put(msg, 3); + ptr8[0] = GSM48_IE_NAME_SHORT; + ptr8[1] = name_len*2 + 1; + ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ + + ptr16 = (u_int16_t *) msgb_put(msg, name_len*2); + for (i = 0; i < name_len; i++) + ptr16[i] = htons(net->name_short[i]); +#endif + name_len = (strlen(net->name_short)*7)/8; + name_pad = (8 - strlen(net->name_short)*7)%8; + if (name_pad > 0) + name_len++; + /* 10.5.3.5a */ + ptr8 = (u_int8_t *) msgb_put(msg, 3); + ptr8[0] = GSM48_IE_NAME_SHORT; + ptr8[1] = name_len +1; + ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ + + ptr8 = msgb_put(msg, name_len); + gsm_7bit_encode(ptr8, net->name_short); + + } + +#if 0 + /* Section 10.5.3.9 */ + cur_t = time(NULL); + cur_time = gmtime(&cur_t); + ptr8 = msgb_put(msg, 8); + ptr8[0] = GSM48_IE_NET_TIME_TZ; + ptr8[1] = to_bcd8(cur_time->tm_year % 100); + ptr8[2] = to_bcd8(cur_time->tm_mon); + ptr8[3] = to_bcd8(cur_time->tm_mday); + ptr8[4] = to_bcd8(cur_time->tm_hour); + ptr8[5] = to_bcd8(cur_time->tm_min); + ptr8[6] = to_bcd8(cur_time->tm_sec); + /* 02.42: coded as BCD encoded signed value in units of 15 minutes */ + tz15min = (cur_time->tm_gmtoff)/(60*15); + ptr8[7] = to_bcd8(tz15min); + if (tz15min < 0) + ptr8[7] |= 0x80; +#endif + + DEBUGP(DMM, "-> MM INFO\n"); + + return gsm48_sendmsg(msg, NULL); +} + +/* Section 9.2.2 */ +int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); + + DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", hexdump(rand, 16)); + + msg->lchan = lchan; + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_AUTH_REQ; + + ar->key_seq = key_seq; + + /* 16 bytes RAND parameters */ + if (rand) + memcpy(ar->rand, rand, 16); + + return gsm48_sendmsg(msg, NULL); +} + +/* Section 9.2.1 */ +int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan) +{ + DEBUGP(DMM, "-> AUTH REJECT\n"); + return gsm48_tx_simple(lchan, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); +} + +static int gsm48_tx_mm_serv_ack(struct gsm_lchan *lchan) +{ + DEBUGP(DMM, "-> CM SERVICE ACK\n"); + return gsm48_tx_simple(lchan, GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_ACC); +} + +/* 9.2.6 CM service reject */ +static int gsm48_tx_mm_serv_rej(struct gsm_lchan *lchan, + enum gsm48_reject_value value) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + + msg->lchan = lchan; + use_lchan(lchan); + + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; + gh->data[0] = value; + DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); + + return gsm48_sendmsg(msg, NULL); +} + +/* + * Handle CM Service Requests + * a) Verify that the packet is long enough to contain the information + * we require otherwsie reject with INCORRECT_MESSAGE + * b) Try to parse the TMSI. If we do not have one reject + * c) Check that we know the subscriber with the TMSI otherwise reject + * with a HLR cause + * d) Set the subscriber on the gsm_lchan and accept + */ +static int gsm48_rx_mm_serv_req(struct msgb *msg) +{ + u_int8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct gsm_subscriber *subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_service_request *req = + (struct gsm48_service_request *)gh->data; + /* unfortunately in Phase1 the classmar2 length is variable */ + u_int8_t classmark2_len = gh->data[1]; + u_int8_t *classmark2 = gh->data+2; + u_int8_t mi_len = *(classmark2 + classmark2_len); + u_int8_t *mi = (classmark2 + classmark2_len + 1); + + DEBUGP(DMM, "<- CM SERVICE REQUEST "); + if (msg->data_len < sizeof(struct gsm48_service_request*)) { + DEBUGPC(DMM, "wrong sized message\n"); + return gsm48_tx_mm_serv_rej(msg->lchan, + GSM48_REJECT_INCORRECT_MESSAGE); + } + + if (msg->data_len < req->mi_len + 6) { + DEBUGPC(DMM, "does not fit in packet\n"); + return gsm48_tx_mm_serv_rej(msg->lchan, + GSM48_REJECT_INCORRECT_MESSAGE); + } + + mi_type = mi[0] & GSM_MI_TYPE_MASK; + if (mi_type != GSM_MI_TYPE_TMSI) { + DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type); + return gsm48_tx_mm_serv_rej(msg->lchan, + GSM48_REJECT_INCORRECT_MESSAGE); + } + + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); + DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n", + req->cm_service_type, mi_type, mi_string); + + dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); + + if (is_siemens_bts(bts)) + send_siemens_mrpci(msg->lchan, classmark2-1); + + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + + /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */ + if (!subscr) + return gsm48_tx_mm_serv_rej(msg->lchan, + GSM48_REJECT_IMSI_UNKNOWN_IN_HLR); + + if (!msg->lchan->subscr) + msg->lchan->subscr = subscr; + else if (msg->lchan->subscr == subscr) + subscr_put(subscr); /* lchan already has a ref, don't need another one */ + else { + DEBUGP(DMM, "<- CM Channel already owned by someone else?\n"); + subscr_put(subscr); + } + + subscr->equipment.classmark2_len = classmark2_len; + memcpy(subscr->equipment.classmark2, classmark2, classmark2_len); + db_sync_equipment(&subscr->equipment); + + return gsm48_tx_mm_serv_ack(msg->lchan); +} + +static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg) +{ + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_imsi_detach_ind *idi = + (struct gsm48_imsi_detach_ind *) gh->data; + u_int8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; + char mi_string[GSM48_MI_SIZE]; + struct gsm_subscriber *subscr = NULL; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); + DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ", + mi_type, mi_string); + + counter_inc(bts->network->stats.loc_upd_type.detach); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + subscr = subscr_get_by_imsi(bts->network, mi_string); + break; + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + /* no sim card... FIXME: what to do ? */ + DEBUGPC(DMM, "unimplemented mobile identity type\n"); + break; + default: + DEBUGPC(DMM, "unknown mobile identity type\n"); + break; + } + + if (subscr) { + subscr_update(subscr, msg->trx->bts, + GSM_SUBSCRIBER_UPDATE_DETACHED); + DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr)); + + subscr->equipment.classmark1 = idi->classmark1; + db_sync_equipment(&subscr->equipment); + + subscr_put(subscr); + } else + DEBUGP(DMM, "Unknown Subscriber ?!?\n"); + + /* FIXME: iterate over all transactions and release them, + * imagine an IMSI DETACH happening during an active call! */ + + /* subscriber is detached: should we release lchan? */ + lchan_auto_release(msg->lchan); + + return 0; +} + +static int gsm48_rx_mm_status(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); + + return 0; +} + +/* Receive a GSM 04.08 Mobility Management (MM) message */ +static int gsm0408_rcv_mm(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (gh->msg_type & 0xbf) { + case GSM48_MT_MM_LOC_UPD_REQUEST: + DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); + rc = mm_rx_loc_upd_req(msg); + break; + case GSM48_MT_MM_ID_RESP: + rc = mm_rx_id_resp(msg); + break; + case GSM48_MT_MM_CM_SERV_REQ: + rc = gsm48_rx_mm_serv_req(msg); + break; + case GSM48_MT_MM_STATUS: + rc = gsm48_rx_mm_status(msg); + break; + case GSM48_MT_MM_TMSI_REALL_COMPL: + DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", + msg->lchan->subscr ? + subscr_name(msg->lchan->subscr) : + "unknown subscriber"); + break; + case GSM48_MT_MM_IMSI_DETACH_IND: + rc = gsm48_rx_mm_imsi_detach_ind(msg); + break; + case GSM48_MT_MM_CM_REEST_REQ: + DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); + break; + case GSM48_MT_MM_AUTH_RESP: + DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n"); + break; + default: + LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", + gh->msg_type); + break; + } + + return rc; +} + +/* Receive a PAGING RESPONSE message from the MS */ +static int gsm48_rx_rr_pag_resp(struct msgb *msg) +{ + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t *classmark2_lv = gh->data + 1; + u_int8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + struct gsm_subscriber *subscr = NULL; + int rc = 0; + + gsm48_paging_extract_mi(msg, mi_string, &mi_type); + DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + subscr = subscr_get_by_imsi(bts->network, mi_string); + break; + } + + if (!subscr) { + DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); + /* FIXME: request id? close channel? */ + return -EINVAL; + } + DEBUGP(DRR, "<- Channel was requested by %s\n", + subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi); + + subscr->equipment.classmark2_len = *classmark2_lv; + memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv); + db_sync_equipment(&subscr->equipment); + + rc = gsm48_handle_paging_resp(msg, subscr); + return rc; +} + +static int gsm48_rx_rr_classmark(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm_subscriber *subscr = msg->lchan->subscr; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + u_int8_t cm2_len, cm3_len = 0; + u_int8_t *cm2, *cm3 = NULL; + + DEBUGP(DRR, "CLASSMARK CHANGE "); + + /* classmark 2 */ + cm2_len = gh->data[0]; + cm2 = &gh->data[1]; + DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); + + if (payload_len > cm2_len + 1) { + /* we must have a classmark3 */ + if (gh->data[cm2_len+1] != 0x20) { + DEBUGPC(DRR, "ERR CM3 TAG\n"); + return -EINVAL; + } + if (cm2_len > 3) { + DEBUGPC(DRR, "CM2 too long!\n"); + return -EINVAL; + } + + cm3_len = gh->data[cm2_len+2]; + cm3 = &gh->data[cm2_len+3]; + if (cm3_len > 14) { + DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); + return -EINVAL; + } + DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); + } + if (subscr) { + subscr->equipment.classmark2_len = cm2_len; + memcpy(subscr->equipment.classmark2, cm2, cm2_len); + if (cm3) { + subscr->equipment.classmark3_len = cm3_len; + memcpy(subscr->equipment.classmark3, cm3, cm3_len); + } + db_sync_equipment(&subscr->equipment); + } + + return 0; +} + +static int gsm48_rx_rr_status(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DRR, "STATUS rr_cause = %s\n", + rr_cause_name(gh->data[0])); + + return 0; +} + +static int gsm48_rx_rr_meas_rep(struct msgb *msg) +{ + struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan); + + /* This shouldn't actually end up here, as RSL treats + * L3 Info of 08.58 MEASUREMENT REPORT different by calling + * directly into gsm48_parse_meas_rep */ + DEBUGP(DMEAS, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); + gsm48_parse_meas_rep(meas_rep, msg); + + return 0; +} + +static int gsm48_rx_rr_app_info(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t apdu_id_flags; + u_int8_t apdu_len; + u_int8_t *apdu_data; + + apdu_id_flags = gh->data[0]; + apdu_len = gh->data[1]; + apdu_data = gh->data+2; + + DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s", + apdu_id_flags, apdu_len, hexdump(apdu_data, apdu_len)); + + return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data); +} + +/* Chapter 9.1.16 Handover complete */ +static int gsm48_rx_rr_ho_compl(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n", + rr_cause_name(gh->data[0])); + + dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, msg->lchan); + /* FIXME: release old channel */ + + return 0; +} + +/* Chapter 9.1.17 Handover Failure */ +static int gsm48_rx_rr_ho_fail(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DRR, "HANDOVER FAILED cause = %s\n", + rr_cause_name(gh->data[0])); + + dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, msg->lchan); + /* FIXME: release allocated new channel */ + + return 0; +} + +/* Receive a GSM 04.08 Radio Resource (RR) message */ +static int gsm0408_rcv_rr(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (gh->msg_type) { + case GSM48_MT_RR_CLSM_CHG: + rc = gsm48_rx_rr_classmark(msg); + break; + case GSM48_MT_RR_GPRS_SUSP_REQ: + DEBUGP(DRR, "GRPS SUSPEND REQUEST\n"); + break; + case GSM48_MT_RR_PAG_RESP: + rc = gsm48_rx_rr_pag_resp(msg); + break; + case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: + rc = gsm48_rx_rr_modif_ack(msg); + break; + case GSM48_MT_RR_STATUS: + rc = gsm48_rx_rr_status(msg); + break; + case GSM48_MT_RR_MEAS_REP: + rc = gsm48_rx_rr_meas_rep(msg); + break; + case GSM48_MT_RR_APP_INFO: + rc = gsm48_rx_rr_app_info(msg); + break; + case GSM48_MT_RR_CIPH_M_COMPL: + DEBUGP(DRR, "CIPHERING MODE COMPLETE\n"); + /* FIXME: check for MI (if any) */ + break; + case GSM48_MT_RR_HANDO_COMPL: + rc = gsm48_rx_rr_ho_compl(msg); + break; + case GSM48_MT_RR_HANDO_FAIL: + rc = gsm48_rx_rr_ho_fail(msg); + break; + default: + LOGP(DRR, LOGL_NOTICE, "Unimplemented " + "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type); + break; + } + + return rc; +} + +int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id, + u_int8_t apdu_len, const u_int8_t *apdu) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = lchan; + + DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", + apdu_id, apdu_len); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_APP_INFO; + gh->data[0] = apdu_id; + gh->data[1] = apdu_len; + memcpy(gh->data+2, apdu, apdu_len); + + return gsm48_sendmsg(msg, NULL); +} + +/* Call Control */ + +/* The entire call control code is written in accordance with Figure 7.10c + * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE + * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY + * it for voice */ + +static void new_cc_state(struct gsm_trans *trans, int state) +{ + if (state > 31 || state < 0) + return; + + DEBUGP(DCC, "new state %s -> %s\n", + cc_state_names[trans->cc.state], cc_state_names[state]); + + trans->cc.state = state; +} + +static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + u_int8_t *cause, *call_state; + + gh->msg_type = GSM48_MT_CC_STATUS; + + cause = msgb_put(msg, 3); + cause[0] = 2; + cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; + cause[2] = 0x80 | 30; /* response to status inquiry */ + + call_state = msgb_put(msg, 1); + call_state[0] = 0xc0 | 0x00; + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_tx_simple(struct gsm_lchan *lchan, + u_int8_t pdisc, u_int8_t msg_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + msg->lchan = lchan; + + gh->proto_discr = pdisc; + gh->msg_type = msg_type; + + return gsm48_sendmsg(msg, NULL); +} + +static void gsm48_stop_cc_timer(struct gsm_trans *trans) +{ + if (bsc_timer_pending(&trans->cc.timer)) { + DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); + bsc_del_timer(&trans->cc.timer); + trans->cc.Tcurrent = 0; + } +} + +static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, + int msg_type, struct gsm_mncc *mncc) +{ + struct msgb *msg; + + if (trans) + if (trans->lchan) + DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " + "Sending '%s' to MNCC.\n", + trans->lchan->ts->trx->bts->nr, + trans->lchan->ts->trx->nr, + trans->lchan->ts->nr, trans->transaction_id, + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + else + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Sending '%s' to MNCC.\n", + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + else + DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " + "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); + + mncc->msg_type = msg_type; + + msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); + if (!msg) + return -ENOMEM; + memcpy(msg->data, mncc, sizeof(struct gsm_mncc)); + msgb_enqueue(&net->upqueue, msg); + + return 0; +} + +int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, + u_int32_t callref, int location, int value) +{ + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(rel)); + rel.callref = callref; + mncc_set_cause(&rel, location, value); + return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); +} + +/* Call Control Specific transaction release. + * gets called by trans_free, DO NOT CALL YOURSELF! */ +void _gsm48_cc_trans_free(struct gsm_trans *trans) +{ + gsm48_stop_cc_timer(trans); + + /* send release to L4, if callref still exists */ + if (trans->callref) { + /* Ressource unavailable */ + mncc_release_ind(trans->subscr->net, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + } + if (trans->cc.state != GSM_CSTATE_NULL) + new_cc_state(trans, GSM_CSTATE_NULL); + if (trans->lchan) + trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref); +} + +static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); + +/* call-back from paging the B-end of the connection */ +static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_lchan, void *param) +{ + struct gsm_lchan *lchan = _lchan; + struct gsm_subscriber *subscr = param; + struct gsm_trans *transt, *tmp; + struct gsm_network *net; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + if (!subscr) + return -EINVAL; + net = subscr->net; + if (!net) { + DEBUGP(DCC, "Error Network not set!\n"); + return -EINVAL; + } + + /* check all tranactions (without lchan) for subscriber */ + llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) { + if (transt->subscr != subscr || transt->lchan) + continue; + switch (event) { + case GSM_PAGING_SUCCEEDED: + if (!lchan) // paranoid + break; + DEBUGP(DCC, "Paging subscr %s succeeded!\n", + subscr->extension); + /* Assign lchan */ + if (!transt->lchan) { + transt->lchan = lchan; + use_lchan(lchan); + } + /* send SETUP request to called party */ + gsm48_cc_tx_setup(transt, &transt->cc.msg); + break; + case GSM_PAGING_EXPIRED: + DEBUGP(DCC, "Paging subscr %s expired!\n", + subscr->extension); + /* Temporarily out of order */ + mncc_release_ind(transt->subscr->net, transt, + transt->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + transt->callref = 0; + trans_free(transt); + break; + } + } + return 0; +} + +static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable); + +/* some other part of the code sends us a signal */ +static int handle_abisip_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_lchan *lchan = signal_data; + int rc; + struct gsm_network *net; + struct gsm_trans *trans; + + if (subsys != SS_ABISIP) + return 0; + + /* in case we use direct BTS-to-BTS RTP */ + if (ipacc_rtp_direct) + return 0; + + switch (signal) { + case S_ABISIP_CRCX_ACK: + /* check if any transactions on this lchan still have + * a tch_recv_mncc request pending */ + net = lchan->ts->trx->bts->network; + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->lchan == lchan && trans->tch_recv) { + DEBUGP(DCC, "pending tch_recv_mncc request\n"); + tch_recv_mncc(net, trans->callref, 1); + } + } + break; + } + + return 0; +} + +/* map two ipaccess RTP streams onto each other */ +static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; + int rc; + + DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", + bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); + + if (bts->type != remote_bts->type) { + DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n"); + return -EINVAL; + } + + // todo: map between different bts types + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + if (!ipacc_rtp_direct) { + /* connect the TCH's to our RTP proxy */ + rc = rsl_ipacc_mdcx_to_rtpsock(lchan); + if (rc < 0) + return rc; + rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan); +#warning do we need a check of rc here? + + /* connect them with each other */ + rtp_socket_proxy(lchan->abis_ip.rtp_socket, + remote_lchan->abis_ip.rtp_socket); + } else { + /* directly connect TCH RTP streams to each other */ + rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip, + remote_lchan->abis_ip.bound_port, + remote_lchan->abis_ip.rtp_payload2); + if (rc < 0) + return rc; + rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip, + lchan->abis_ip.bound_port, + lchan->abis_ip.rtp_payload2); + } + break; + case GSM_BTS_TYPE_BS11: + trau_mux_map_lchan(lchan, remote_lchan); + break; + default: + DEBUGP(DCC, "Unknown BTS type %u\n", bts->type); + return -EINVAL; + } + + return 0; +} + +/* bridge channels of two transactions */ +static int tch_bridge(struct gsm_network *net, u_int32_t *refs) +{ + struct gsm_trans *trans1 = trans_find_by_callref(net, refs[0]); + struct gsm_trans *trans2 = trans_find_by_callref(net, refs[1]); + + if (!trans1 || !trans2) + return -EIO; + + if (!trans1->lchan || !trans2->lchan) + return -EIO; + + /* through-connect channel */ + return tch_map(trans1->lchan, trans2->lchan); +} + +/* enable receive of channels to MNCC upqueue */ +static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable) +{ + struct gsm_trans *trans; + struct gsm_lchan *lchan; + struct gsm_bts *bts; + int rc; + + /* Find callref */ + trans = trans_find_by_callref(net, callref); + if (!trans) + return -EIO; + if (!trans->lchan) + return 0; + lchan = trans->lchan; + bts = lchan->ts->trx->bts; + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + if (ipacc_rtp_direct) { + DEBUGP(DCC, "Error: RTP proxy is disabled\n"); + return -EINVAL; + } + /* in case, we don't have a RTP socket yet, we note this + * in the transaction and try later */ + if (!lchan->abis_ip.rtp_socket) { + trans->tch_recv = enable; + DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); + return 0; + } + if (enable) { + /* connect the TCH's to our RTP proxy */ + rc = rsl_ipacc_mdcx_to_rtpsock(lchan); + if (rc < 0) + return rc; + /* assign socket to application interface */ + rtp_socket_upstream(lchan->abis_ip.rtp_socket, + net, callref); + } else + rtp_socket_upstream(lchan->abis_ip.rtp_socket, + net, 0); + break; + case GSM_BTS_TYPE_BS11: + if (enable) + return trau_recv_lchan(lchan, callref); + return trau_mux_unmap(NULL, callref); + break; + default: + DEBUGP(DCC, "Unknown BTS type %u\n", bts->type); + return -EINVAL; + } + + return 0; +} + +static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) +{ + DEBUGP(DCC, "-> STATUS ENQ\n"); + return gsm48_cc_tx_status(trans, msg); +} + +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); + +static void gsm48_cc_timeout(void *arg) +{ + struct gsm_trans *trans = arg; + int disconnect = 0, release = 0; + int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER; + int mo_location = GSM48_CAUSE_LOC_USER; + int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + int l4_location = GSM48_CAUSE_LOC_PRN_S_LU; + struct gsm_mncc mo_rel, l4_rel; + + memset(&mo_rel, 0, sizeof(struct gsm_mncc)); + mo_rel.callref = trans->callref; + memset(&l4_rel, 0, sizeof(struct gsm_mncc)); + l4_rel.callref = trans->callref; + + switch(trans->cc.Tcurrent) { + case 0x303: + release = 1; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; + break; + case 0x310: + disconnect = 1; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; + break; + case 0x313: + disconnect = 1; + /* unknown, did not find it in the specs */ + break; + case 0x301: + disconnect = 1; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; + break; + case 0x308: + if (!trans->cc.T308_second) { + /* restart T308 a second time */ + gsm48_cc_tx_release(trans, &trans->cc.msg); + trans->cc.T308_second = 1; + break; /* stay in release state */ + } + trans_free(trans); + return; +// release = 1; +// l4_cause = 14; +// break; + case 0x306: + release = 1; + mo_cause = trans->cc.msg.cause.value; + mo_location = trans->cc.msg.cause.location; + break; + case 0x323: + disconnect = 1; + break; + default: + release = 1; + } + + if (release && trans->callref) { + /* process release towards layer 4 */ + mncc_release_ind(trans->subscr->net, trans, trans->callref, + l4_location, l4_cause); + trans->callref = 0; + } + + if (disconnect && trans->callref) { + /* process disconnect towards layer 4 */ + mncc_set_cause(&l4_rel, l4_location, l4_cause); + mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &l4_rel); + } + + /* process disconnect towards mobile station */ + if (disconnect || release) { + mncc_set_cause(&mo_rel, mo_location, mo_cause); + mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; + mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; + mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; + mo_rel.cause.diag_len = 3; + + if (disconnect) + gsm48_cc_tx_disconnect(trans, &mo_rel); + if (release) + gsm48_cc_tx_release(trans, &mo_rel); + } + +} + +static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, + int sec, int micro) +{ + DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); + trans->cc.timer.cb = gsm48_cc_timeout; + trans->cc.timer.data = trans; + bsc_schedule_timer(&trans->cc.timer, sec, micro); + trans->cc.Tcurrent = current; +} + +static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t msg_type = gh->msg_type & 0xbf; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc setup; + + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* emergency setup is identified by msg_type */ + if (msg_type == GSM48_MT_CC_EMERG_SETUP) + setup.emergency = 1; + + /* use subscriber as calling party number */ + if (trans->subscr) { + setup.fields |= MNCC_F_CALLING; + strncpy(setup.calling.number, trans->subscr->extension, + sizeof(setup.calling.number)-1); + strncpy(setup.imsi, trans->subscr->imsi, + sizeof(setup.imsi)-1); + } + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + setup.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&setup.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + setup.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&setup.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* called party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { + setup.fields |= MNCC_F_CALLED; + gsm48_decode_called(&setup.called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + setup.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&setup.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + setup.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&setup.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + /* CLIR suppression */ + if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) + setup.clir.sup = 1; + /* CLIR invocation */ + if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) + setup.clir.inv = 1; + /* cc cap */ + if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { + setup.fields |= MNCC_F_CCCAP; + gsm48_decode_cccap(&setup.cccap, + TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_INITIATED); + + LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", + subscr_name(trans->subscr), trans->subscr->extension, + setup.called.number); + + /* indicate setup to MNCC */ + mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup); + + /* MNCC code will modify the channel asynchronously, we should + * ipaccess-bind only after the modification has been made to the + * lchan->tch_mode */ + return 0; +} + +static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm_mncc *setup = arg; + int rc, trans_id; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + /* transaction id must not be assigned */ + if (trans->transaction_id != 0xff) { /* unasssigned */ + DEBUGP(DCC, "TX Setup with assigned transaction. " + "This is not allowed!\n"); + /* Temporarily out of order */ + rc = mncc_release_ind(trans->subscr->net, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + trans->callref = 0; + trans_free(trans); + return rc; + } + + /* Get free transaction_id */ + trans_id = trans_assign_trans_id(trans->subscr, GSM48_PDISC_CC, 0); + if (trans_id < 0) { + /* no free transaction ID */ + rc = mncc_release_ind(trans->subscr->net, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + trans->callref = 0; + trans_free(trans); + return rc; + } + trans->transaction_id = trans_id; + + gh->msg_type = GSM48_MT_CC_SETUP; + + gsm48_start_cc_timer(trans, 0x303, GSM48_T303); + + /* bearer capability */ + if (setup->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap); + /* facility */ + if (setup->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &setup->facility); + /* progress */ + if (setup->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &setup->progress); + /* calling party BCD number */ + if (setup->fields & MNCC_F_CALLING) + gsm48_encode_calling(msg, &setup->calling); + /* called party BCD number */ + if (setup->fields & MNCC_F_CALLED) + gsm48_encode_called(msg, &setup->called); + /* user-user */ + if (setup->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &setup->useruser); + /* redirecting party BCD number */ + if (setup->fields & MNCC_F_REDIRECTING) + gsm48_encode_redirecting(msg, &setup->redirecting); + /* signal */ + if (setup->fields & MNCC_F_SIGNAL) + gsm48_encode_signal(msg, setup->signal); + + new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc call_conf; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x310, GSM48_T310); + + memset(&call_conf, 0, sizeof(struct gsm_mncc)); + call_conf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); +#if 0 + /* repeat */ + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) + call_conf.repeat = 1; + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) + call_conf.repeat = 2; +#endif + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + call_conf.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&call_conf.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + call_conf.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&call_conf.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* cc cap */ + if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { + call_conf.fields |= MNCC_F_CCCAP; + gsm48_decode_cccap(&call_conf.cccap, + TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_CALL_CONF_IND, + &call_conf); +} + +static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *proceeding = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CALL_PROC; + + new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); + + /* bearer capability */ + if (proceeding->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); + /* facility */ + if (proceeding->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &proceeding->facility); + /* progress */ + if (proceeding->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &proceeding->progress); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc alerting; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x301, GSM48_T301); + + memset(&alerting, 0, sizeof(struct gsm_mncc)); + alerting.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + alerting.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&alerting.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + alerting.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&alerting.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + alerting.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&alerting.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_ALERT_IND, + &alerting); +} + +static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *alerting = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_ALERTING; + + /* facility */ + if (alerting->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &alerting->facility); + /* progress */ + if (alerting->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &alerting->progress); + /* user-user */ + if (alerting->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &alerting->useruser); + + new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *progress = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_PROGRESS; + + /* progress */ + gsm48_encode_progress(msg, 1, &progress->progress); + /* user-user */ + if (progress->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &progress->useruser); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *connect = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x313, GSM48_T313); + + /* facility */ + if (connect->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &connect->facility); + /* progress */ + if (connect->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &connect->progress); + /* connected number */ + if (connect->fields & MNCC_F_CONNECTED) + gsm48_encode_connected(msg, &connect->connected); + /* user-user */ + if (connect->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &connect->useruser); + + new_cc_state(trans, GSM_CSTATE_CONNECT_IND); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc connect; + + gsm48_stop_cc_timer(trans); + + memset(&connect, 0, sizeof(struct gsm_mncc)); + connect.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* use subscriber as connected party number */ + if (trans->subscr) { + connect.fields |= MNCC_F_CONNECTED; + strncpy(connect.connected.number, trans->subscr->extension, + sizeof(connect.connected.number)-1); + strncpy(connect.imsi, trans->subscr->imsi, + sizeof(connect.imsi)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + connect.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&connect.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + connect.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&connect.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + connect.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&connect.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_CNF, &connect); +} + + +static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc connect_ack; + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = trans->callref; + return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_COMPL_IND, + &connect_ack); +} + +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT_ACK; + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc disc; + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + disc.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&disc.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + disc.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&disc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + disc.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&disc.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + disc.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&disc.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &disc); + +} + +static struct gsm_mncc_cause default_cause = { + .location = GSM48_CAUSE_LOC_PRN_S_LU, + .coding = 0, + .rec = 0, + .rec_val = 0, + .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, + .diag_len = 0, + .diag = { 0 }, +}; + +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *disc = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_DISCONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x306, GSM48_T306); + + /* cause */ + if (disc->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &disc->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + /* facility */ + if (disc->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &disc->facility); + /* progress */ + if (disc->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &disc->progress); + /* user-user */ + if (disc->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &disc->useruser); + + /* store disconnect cause for T306 expiry */ + memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + int rc; + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + rel.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&rel.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { + /* release collision 5.4.5 */ + rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_CNF, &rel); + } else { + rc = gsm48_tx_simple(msg->lchan, + GSM48_PDISC_CC | (trans->transaction_id << 4), + GSM48_MT_CC_RELEASE_COMPL); + rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_IND, &rel); + } + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + trans_free(trans); + + return rc; +} + +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE; + + trans->callref = 0; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x308, GSM48_T308); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &rel->useruser); + + trans->cc.T308_second = 0; + memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); + + if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) + new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + int rc = 0; + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + rel.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&rel.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + if (trans->callref) { + switch (trans->cc.state) { + case GSM_CSTATE_CALL_PRESENT: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REJ_IND, &rel); + break; + case GSM_CSTATE_RELEASE_REQ: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REL_CNF, &rel); + /* FIXME: in case of multiple calls, we can't simply + * hang up here ! */ + lchan_auto_release(msg->lchan); + break; + default: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REL_IND, &rel); + } + } + + trans->callref = 0; + trans_free(trans); + + return rc; +} + +static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + trans->callref = 0; + + gsm48_stop_cc_timer(trans); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &rel->useruser); + + trans_free(trans); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc fac; + + memset(&fac, 0, sizeof(struct gsm_mncc)); + fac.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + fac.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&fac.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + fac.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&fac.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_FACILITY_IND, &fac); +} + +static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *fac = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_FACILITY; + + /* facility */ + gsm48_encode_facility(msg, 1, &fac->facility); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc hold; + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_IND, &hold); +} + +static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD_ACK; + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *hold_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD_REJ; + + /* cause */ + if (hold_rej->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &hold_rej->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc retrieve; + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_IND, + &retrieve); +} + +static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR_ACK; + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *retrieve_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR_REJ; + + /* cause */ + if (retrieve_rej->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &retrieve_rej->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* keypad facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { + dtmf.fields |= MNCC_F_KEYPAD; + gsm48_decode_keypad(&dtmf.keypad, + TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_IND, &dtmf); +} + +static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; + + /* keypad */ + if (dtmf->fields & MNCC_F_KEYPAD) + gsm48_encode_keypad(msg, dtmf->keypad); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; + + /* cause */ + if (dtmf->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &dtmf->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_STOP_DTMF_IND, &dtmf); +} + +static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_IND, &modify); +} + +static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY; + + gsm48_start_cc_timer(trans, 0x323, GSM48_T323); + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify); +} + +static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= GSM48_IE_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + modify.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&modify.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_REJ, &modify); +} + +static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + /* cause */ + gsm48_encode_cause(msg, 1, &modify->cause); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *notify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_NOTIFY; + + /* notify */ + gsm48_encode_notify(msg, notify->notify); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); +// struct tlv_parsed tp; + struct gsm_mncc notify; + + memset(¬ify, 0, sizeof(struct gsm_mncc)); + notify.callref = trans->callref; +// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); + if (payload_len >= 1) + gsm48_decode_notify(¬ify.notify, gh->data); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, ¬ify); +} + +static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *user = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_USER_INFO; + + /* user-user */ + if (user->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 1, &user->useruser); + /* more data */ + if (user->more) + gsm48_encode_more(msg); + + return gsm48_sendmsg(msg, trans); +} + +static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc user; + + memset(&user, 0, sizeof(struct gsm_mncc)); + user.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + user.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&user.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* more data */ + if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) + user.more = 1; + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user); +} + +static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *mode = arg; + + return gsm48_lchan_modify(trans->lchan, mode->lchan_mode); +} + +static struct downstate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, void *arg); +} downstatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ + MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ + MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ + MNCC_SETUP_RSP, gsm48_cc_tx_connect}, + {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ + MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ + MNCC_SETUP_REQ, gsm48_cc_tx_setup}, + {SBIT(GSM_CSTATE_CONNECT_REQUEST), + MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), + MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, + {ALL_STATES, + MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, + {ALL_STATES, + MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, + {ALL_STATES, + MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, + /* clearing */ + {SBIT(GSM_CSTATE_INITIATED), + MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ + MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ + MNCC_REL_REQ, gsm48_cc_tx_release}, + /* special */ + {ALL_STATES, + MNCC_LCHAN_MODIFY, _gsm48_lchan_modify}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + + +int mncc_send(struct gsm_network *net, int msg_type, void *arg) +{ + int i, rc = 0; + struct gsm_trans *trans = NULL, *transt; + struct gsm_lchan *lchan = NULL; + struct gsm_bts *bts = NULL; + struct gsm_mncc *data = arg, rel; + + /* handle special messages */ + switch(msg_type) { + case MNCC_BRIDGE: + return tch_bridge(net, arg); + case MNCC_FRAME_DROP: + return tch_recv_mncc(net, data->callref, 0); + case MNCC_FRAME_RECV: + return tch_recv_mncc(net, data->callref, 1); + case GSM_TCHF_FRAME: + /* Find callref */ + trans = trans_find_by_callref(net, data->callref); + if (!trans) + return -EIO; + if (!trans->lchan) + return 0; + if (trans->lchan->type != GSM_LCHAN_TCH_F) + return 0; + bts = trans->lchan->ts->trx->bts; + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + if (!trans->lchan->abis_ip.rtp_socket) + return 0; + return rtp_send_frame(trans->lchan->abis_ip.rtp_socket, arg); + case GSM_BTS_TYPE_BS11: + return trau_send_frame(trans->lchan, arg); + default: + DEBUGP(DCC, "Unknown BTS type %u\n", bts->type); + } + return -EINVAL; + } + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = data->callref; + + /* Find callref */ + trans = trans_find_by_callref(net, data->callref); + + /* Callref unknown */ + if (!trans) { + struct gsm_subscriber *subscr; + + if (msg_type != MNCC_SETUP_REQ) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unknown callref %d\n", data->called.number, + get_mncc_name(msg_type), data->callref); + /* Invalid call reference */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); + } + if (!data->called.number[0] && !data->imsi[0]) { + DEBUGP(DCC, "(bts - trx - ts - ti) " + "Received '%s' from MNCC with " + "no number or IMSI\n", get_mncc_name(msg_type)); + /* Invalid number */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INV_NR_FORMAT); + } + /* New transaction due to setup, find subscriber */ + if (data->called.number[0]) + subscr = subscr_get_by_extension(net, + data->called.number); + else + subscr = subscr_get_by_imsi(net, data->imsi); + /* If subscriber is not found */ + if (!subscr) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unknown subscriber %s\n", data->called.number, + get_mncc_name(msg_type), data->called.number); + /* Unknown subscriber */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); + } + /* If subscriber is not "attached" */ + if (!subscr->lac) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "detached subscriber %s\n", data->called.number, + get_mncc_name(msg_type), data->called.number); + subscr_put(subscr); + /* Temporarily out of order */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + } + /* Create transaction */ + trans = trans_alloc(subscr, GSM48_PDISC_CC, 0xff, data->callref); + if (!trans) { + DEBUGP(DCC, "No memory for trans.\n"); + subscr_put(subscr); + /* Ressource unavailable */ + mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + return -ENOMEM; + } + /* Find lchan */ + lchan = lchan_for_subscr(subscr); + /* If subscriber has no lchan */ + if (!lchan) { + /* find transaction with this subscriber already paging */ + llist_for_each_entry(transt, &net->trans_list, entry) { + /* Transaction of our lchan? */ + if (transt == trans || + transt->subscr != subscr) + continue; + DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unallocated channel, paging already " + "started.\n", bts->nr, + data->called.number, + get_mncc_name(msg_type)); + subscr_put(subscr); + trans_free(trans); + return 0; + } + /* store setup informations until paging was successfull */ + memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); + /* Trigger paging */ + paging_request(net, subscr, RSL_CHANNEED_TCH_F, + setup_trig_pag_evt, subscr); + subscr_put(subscr); + return 0; + } + /* Assign lchan */ + trans->lchan = lchan; + use_lchan(lchan); + subscr_put(subscr); + } + lchan = trans->lchan; + + /* if paging did not respond yet */ + if (!lchan) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC in paging state\n", + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NORM_CALL_CLEAR); + if (msg_type == MNCC_REL_REQ) + rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); + else + rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); + trans->callref = 0; + trans_free(trans); + return rc; + } + + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " + "Received '%s' from MNCC in state %d (%s)\n", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + trans->transaction_id, + (lchan->subscr)?(lchan->subscr->extension):"-", + get_mncc_name(msg_type), trans->cc.state, + cc_state_names[trans->cc.state]); + + /* Find function for current state and message */ + for (i = 0; i < DOWNSLLEN; i++) + if ((msg_type == downstatelist[i].type) + && ((1 << trans->cc.state) & downstatelist[i].states)) + break; + if (i == DOWNSLLEN) { + DEBUGP(DCC, "Message unhandled at this state.\n"); + return 0; + } + + rc = downstatelist[i].rout(trans, arg); + + return rc; +} + + +static struct datastate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, struct msgb *msg); +} datastatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ + GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, + {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ + GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, + {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ + GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ + GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, + {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ + GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, + {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ + GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, + /* signalling during call */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL), + GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, + {ALL_STATES, + GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, + {ALL_STATES, + GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, + {ALL_STATES, + GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, + /* clearing */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ + GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ + GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, + {ALL_STATES, /* 5.4.3.4 */ + GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct datastate)) + +static int gsm0408_rcv_cc(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t msg_type = gh->msg_type & 0xbf; + u_int8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ + struct gsm_lchan *lchan = msg->lchan; + struct gsm_trans *trans = NULL; + int i, rc = 0; + + if (msg_type & 0x80) { + DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); + return -EINVAL; + } + + /* Find transaction */ + trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_CC, transaction_id); + + DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " + "Received '%s' from MS in state %d (%s)\n", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + transaction_id, (lchan->subscr)?(lchan->subscr->extension):"-", + gsm48_cc_msg_names[msg_type], trans?(trans->cc.state):0, + cc_state_names[trans?(trans->cc.state):0]); + + /* Create transaction */ + if (!trans) { + DEBUGP(DCC, "Unknown transaction ID %x, " + "creating new trans.\n", transaction_id); + /* Create transaction */ + trans = trans_alloc(lchan->subscr, GSM48_PDISC_CC, + transaction_id, new_callref++); + if (!trans) { + DEBUGP(DCC, "No memory for trans.\n"); + rc = gsm48_tx_simple(msg->lchan, + GSM48_PDISC_CC | (transaction_id << 4), + GSM48_MT_CC_RELEASE_COMPL); + return -ENOMEM; + } + /* Assign transaction */ + trans->lchan = lchan; + use_lchan(lchan); + } + + /* find function for current state and message */ + for (i = 0; i < DATASLLEN; i++) + if ((msg_type == datastatelist[i].type) + && ((1 << trans->cc.state) & datastatelist[i].states)) + break; + if (i == DATASLLEN) { + DEBUGP(DCC, "Message unhandled at this state.\n"); + return 0; + } + + rc = datastatelist[i].rout(trans, msg); + + return rc; +} + +/* here we pass in a msgb from the RSL->RLL. We expect the l3 pointer to be set */ +int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t pdisc = gh->proto_discr & 0x0f; + int rc = 0; + + if (silent_call_reroute(msg)) + return silent_call_rx(msg); + + switch (pdisc) { + case GSM48_PDISC_CC: + rc = gsm0408_rcv_cc(msg); + break; + case GSM48_PDISC_MM: + rc = gsm0408_rcv_mm(msg); + break; + case GSM48_PDISC_RR: + rc = gsm0408_rcv_rr(msg); + break; + case GSM48_PDISC_SMS: + rc = gsm0411_rcv_sms(msg, link_id); + break; + case GSM48_PDISC_MM_GPRS: + case GSM48_PDISC_SM_GPRS: + LOGP(DRLL, LOGL_NOTICE, "Unimplemented " + "GSM 04.08 discriminator 0x%02x\n", pdisc); + break; + case GSM48_PDISC_NC_SS: + rc = handle_rcv_ussd(msg); + break; + default: + LOGP(DRLL, LOGL_NOTICE, "Unknown " + "GSM 04.08 discriminator 0x%02x\n", pdisc); + break; + } + + return rc; +} + +/* dequeue messages to layer 4 */ +int bsc_upqueue(struct gsm_network *net) +{ + struct gsm_mncc *mncc; + struct msgb *msg; + int work = 0; + + if (net) + while ((msg = msgb_dequeue(&net->upqueue))) { + mncc = (struct gsm_mncc *)msg->data; + if (net->mncc_recv) + net->mncc_recv(net, mncc->msg_type, mncc); + work = 1; /* work done */ + talloc_free(msg); + } + + return work; +} + +/* + * This will be ran by the linker when loading the DSO. We use it to + * do system initialization, e.g. registration of signal handlers. + */ +static __attribute__((constructor)) void on_dso_load_0408(void) +{ + register_signal_handler(SS_LCHAN, gsm0408_handle_lchan_signal, NULL); + register_signal_handler(SS_ABISIP, handle_abisip_signal, NULL); +} diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c new file mode 100644 index 000000000..68f34f409 --- /dev/null +++ b/openbsc/src/gsm_04_08_utils.c @@ -0,0 +1,629 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 + * utility functions + */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <netinet/in.h> + +#include <osmocore/msgb.h> +#include <osmocore/gsm48.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/transaction.h> +#include <openbsc/paging.h> +#include <openbsc/signal.h> + +#define GSM48_ALLOC_SIZE 1024 +#define GSM48_ALLOC_HEADROOM 128 + +/* should ip.access BTS use direct RTP streams between each other (1), + * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ +int ipacc_rtp_direct = 1; + +struct msgb *gsm48_msgb_alloc(void) +{ + return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, + "GSM 04.08"); +} + +int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; + + /* if we get passed a transaction reference, do some common + * work that the caller no longer has to do */ + if (trans) { + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + msg->lchan = trans->lchan; + } + + if (msg->lchan) { + msg->trx = msg->lchan->ts->trx; + + if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " + "Sending '%s' to MS.\n", msg->trx->bts->nr, + msg->trx->nr, msg->lchan->ts->nr, + gh->proto_discr & 0xf0, + gsm48_cc_msg_names[gh->msg_type & 0x3f]); + else + DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) " + "Sending 0x%02x to MS.\n", msg->trx->bts->nr, + msg->trx->nr, msg->lchan->ts->nr, + gh->proto_discr, gh->msg_type); + } + + msg->l3h = msg->data; + + return rsl_data_request(msg, 0); +} + +/* Section 9.1.8 / Table 9.9 */ +struct chreq { + u_int8_t val; + u_int8_t mask; + enum chreq_type type; +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ +static const struct chreq chreq_type_neci1[] = { + { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, + { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, + { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, + { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, + { 0xe0, 0xe0, CHREQ_T_SDCCH }, + { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, + { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, + { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, + { 0x10, 0xf0, CHREQ_T_SDCCH }, + { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, + { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, + { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, + { 0x67, 0xff, CHREQ_T_LMU }, + { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, + { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, + { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, + { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ +static const struct chreq chreq_type_neci0[] = { + { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, + { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, + { 0xe0, 0xe0, CHREQ_T_TCH_F }, + { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, + { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, + { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, + { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, + { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, + { 0x67, 0xff, CHREQ_T_LMU }, + { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, + { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, + { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, + { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, +}; + +static const enum gsm_chan_t ctype_by_chreq[] = { + [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, + [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, + [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, + [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, + [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, + [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, + [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, +}; + +static const enum gsm_chreq_reason_t reason_by_chreq[] = { + [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, + [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, + [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, +}; + +enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci) +{ + int i; + int length; + const struct chreq *chreq; + + if (neci) { + chreq = chreq_type_neci1; + length = ARRAY_SIZE(chreq_type_neci1); + } else { + chreq = chreq_type_neci0; + length = ARRAY_SIZE(chreq_type_neci0); + } + + + for (i = 0; i < length; i++) { + const struct chreq *chr = &chreq[i]; + if ((ra & chr->mask) == chr->val) + return ctype_by_chreq[chr->type]; + } + LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); + return GSM_LCHAN_SDCCH; +} + +enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci) +{ + int i; + int length; + const struct chreq *chreq; + + if (neci) { + chreq = chreq_type_neci1; + length = ARRAY_SIZE(chreq_type_neci1); + } else { + chreq = chreq_type_neci0; + length = ARRAY_SIZE(chreq_type_neci0); + } + + for (i = 0; i < length; i++) { + const struct chreq *chr = &chreq[i]; + if ((ra & chr->mask) == chr->val) + return reason_by_chreq[chr->type]; + } + LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); + return GSM_CHREQ_REASON_OTHER; +} + +/* 7.1.7 and 9.1.7: RR CHANnel RELease */ +int gsm48_send_rr_release(struct gsm_lchan *lchan) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + u_int8_t *cause; + + msg->lchan = lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CHAN_REL; + + cause = msgb_put(msg, 1); + cause[0] = GSM48_RR_CAUSE_NORMAL; + + DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", + lchan->nr, lchan->type); + + /* Send actual release request to MS */ + gsm48_sendmsg(msg, NULL); + /* FIXME: Start Timer T3109 */ + + /* Deactivate the SACCH on the BTS side */ + return rsl_deact_sacch(lchan); +} + +/* Convert Mobile Identity (10.5.1.4) to string */ +int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len) +{ + int i; + u_int8_t mi_type; + char *str_cur = string; + u_int32_t tmsi; + + mi_type = mi[0] & GSM_MI_TYPE_MASK; + + switch (mi_type) { + case GSM_MI_TYPE_NONE: + break; + case GSM_MI_TYPE_TMSI: + /* Table 10.5.4.3, reverse generate_mid_from_tmsi */ + if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) { + memcpy(&tmsi, &mi[1], 4); + tmsi = ntohl(tmsi); + return snprintf(string, str_len, "%u", tmsi); + } + break; + case GSM_MI_TYPE_IMSI: + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + *str_cur++ = bcd2char(mi[0] >> 4); + + for (i = 1; i < mi_len; i++) { + if (str_cur + 2 >= string + str_len) + return str_cur - string; + *str_cur++ = bcd2char(mi[i] & 0xf); + /* skip last nibble in last input byte when GSM_EVEN */ + if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD)) + *str_cur++ = bcd2char(mi[i] >> 4); + } + break; + default: + break; + } + *str_cur++ = '\0'; + + return str_cur - string; +} + + +int send_siemens_mrpci(struct gsm_lchan *lchan, + u_int8_t *classmark2_lv) +{ + struct rsl_mrpci mrpci; + + if (classmark2_lv[0] < 2) + return -EINVAL; + + mrpci.power_class = classmark2_lv[1] & 0x7; + mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1); + mrpci.vbs_capable = classmark2_lv[2] & (1 <<2); + mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3; + + return rsl_siemens_mrpci(lchan, &mrpci); +} + +int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t *classmark2_lv = gh->data + 1; + u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv; + *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; + + return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv); +} + +int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr) +{ + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t *classmark2_lv = gh->data + 1; + struct paging_signal_data sig_data; + + if (is_siemens_bts(bts)) + send_siemens_mrpci(msg->lchan, classmark2_lv); + + if (!msg->lchan->subscr) { + msg->lchan->subscr = subscr; + } else if (msg->lchan->subscr != subscr) { + LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); + subscr_put(subscr); + return -EINVAL; + } else { + DEBUGP(DRR, "<- Channel already owned by us\n"); + subscr_put(subscr); + subscr = msg->lchan->subscr; + } + + sig_data.subscr = subscr; + sig_data.bts = msg->lchan->ts->trx->bts; + sig_data.lchan = msg->lchan; + + bts->network->stats.paging.completed++; + + dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data); + + /* Stop paging on the bts we received the paging response */ + paging_request_stop(msg->trx->bts, subscr, msg->lchan); + return 0; +} + +/* Chapter 9.1.9: Ciphering Mode Command */ +int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + u_int8_t ciph_mod_set; + + msg->lchan = lchan; + + DEBUGP(DRR, "TX CIPHERING MODE CMD\n"); + + if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0)) + ciph_mod_set = 0; + else + ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CIPH_M_CMD; + gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf); + + return rsl_encryption_cmd(msg); +} + +static void gsm48_cell_desc(struct gsm48_cell_desc *cd, + const struct gsm_bts *bts) +{ + cd->ncc = (bts->bsic >> 3 & 0x7); + cd->bcc = (bts->bsic & 0x7); + cd->arfcn_hi = bts->c0->arfcn >> 8; + cd->arfcn_lo = bts->c0->arfcn & 0xff; +} + +static void gsm48_chan_desc(struct gsm48_chan_desc *cd, + const struct gsm_lchan *lchan) +{ + u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; + + cd->chan_nr = lchan2chan_nr(lchan); + cd->h0.tsc = lchan->ts->trx->bts->tsc; + cd->h0.h = 0; + cd->h0.arfcn_high = arfcn >> 8; + cd->h0.arfcn_low = arfcn & 0xff; +} + +/* Chapter 9.1.15: Handover Command */ +int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, + u_int8_t power_command, u_int8_t ho_ref) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_ho_cmd *ho = + (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); + + msg->lchan = old_lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_HANDO_CMD; + + /* mandatory bits */ + gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); + gsm48_chan_desc(&ho->chan_desc, new_lchan); + ho->ho_ref = ho_ref; + ho->power_command = power_command; + + /* FIXME: optional bits for type of synchronization? */ + + return gsm48_sendmsg(msg, NULL); +} + +/* Chapter 9.1.2: Assignment Command */ +int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_command) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_ass_cmd *ass = + (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); + + DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); + + msg->lchan = dest_lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_CMD; + + /* + * fill the channel information element, this code + * should probably be shared with rsl_rx_chan_rqd(), + * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5 + * 10.5.2.5.a have slightly different semantic for + * the chan_desc. But as long as multi-slot configurations + * are not used we seem to be fine. + */ + gsm48_chan_desc(&ass->chan_desc, lchan); + ass->power_command = power_command; + + msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); + + /* in case of multi rate we need to attach a config */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + if (lchan->mr_conf.ver == 0) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); + } else { + u_int8_t *data = msgb_put(msg, 4); + data[0] = GSM48_IE_MUL_RATE_CFG; + data[1] = 0x2; + memcpy(&data[2], &lchan->mr_conf, 2); + } + } + + return gsm48_sendmsg(msg, NULL); +} + +/* 9.1.5 Channel mode modify: Modify the mode on the MS side */ +int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_chan_mode_modify *cmm = + (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); + u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; + + DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); + + lchan->tch_mode = mode; + msg->lchan = lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; + + /* fill the channel information element, this code + * should probably be shared with rsl_rx_chan_rqd() */ + cmm->chan_desc.chan_nr = lchan2chan_nr(lchan); + cmm->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc; + cmm->chan_desc.h0.h = 0; + cmm->chan_desc.h0.arfcn_high = arfcn >> 8; + cmm->chan_desc.h0.arfcn_low = arfcn & 0xff; + cmm->mode = mode; + + /* in case of multi rate we need to attach a config */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + if (lchan->mr_conf.ver == 0) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); + } else { + u_int8_t *data = msgb_put(msg, 4); + data[0] = GSM48_IE_MUL_RATE_CFG; + data[1] = 0x2; + memcpy(&data[2], &lchan->mr_conf, 2); + } + } + + return gsm48_sendmsg(msg, NULL); +} + +int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode) +{ + int rc; + + rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode); + if (rc < 0) + return rc; + + return rc; +} + +int gsm48_rx_rr_modif_ack(struct msgb *msg) +{ + int rc; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_chan_mode_modify *mod = + (struct gsm48_chan_mode_modify *) gh->data; + + DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); + + if (mod->mode != msg->lchan->tch_mode) { + LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", + msg->lchan->tch_mode, mod->mode); + return -1; + } + + /* update the channel type */ + switch (mod->mode) { + case GSM48_CMODE_SIGN: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; + break; + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_AMR: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; + break; + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; + break; + } + + /* We've successfully modified the MS side of the channel, + * now go on to modify the BTS side of the channel */ + rc = rsl_chan_mode_modify_req(msg->lchan); + + /* FIXME: we not only need to do this after mode modify, but + * also after channel activation */ + if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) + rsl_ipacc_crcx(msg->lchan); + return rc; +} + +int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + u_int8_t *data = gh->data; + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct bitvec *nbv = &bts->si_common.neigh_list; + struct gsm_meas_rep_cell *mrc; + + if (gh->msg_type != GSM48_MT_RR_MEAS_REP) + return -EINVAL; + + if (data[0] & 0x80) + rep->flags |= MEAS_REP_F_BA1; + if (data[0] & 0x40) + rep->flags |= MEAS_REP_F_UL_DTX; + if ((data[1] & 0x40) == 0x00) + rep->flags |= MEAS_REP_F_DL_VALID; + + rep->dl.full.rx_lev = data[0] & 0x3f; + rep->dl.sub.rx_lev = data[1] & 0x3f; + rep->dl.full.rx_qual = (data[3] >> 4) & 0x7; + rep->dl.sub.rx_qual = (data[3] >> 1) & 0x7; + + rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); + if (rep->num_cell < 1 || rep->num_cell > 6) + return 0; + + /* an encoding nightmare in perfection */ + mrc = &rep->cell[0]; + mrc->rxlev = data[3] & 0x3f; + mrc->neigh_idx = data[4] >> 3; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); + if (rep->num_cell < 2) + return 0; + + mrc = &rep->cell[1]; + mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); + mrc->neigh_idx = (data[6] >> 2) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); + if (rep->num_cell < 3) + return 0; + + mrc = &rep->cell[2]; + mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); + mrc->neigh_idx = (data[8] >> 1) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); + if (rep->num_cell < 4) + return 0; + + mrc = &rep->cell[3]; + mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); + mrc->neigh_idx = data[10] & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[11] >> 2; + if (rep->num_cell < 5) + return 0; + + mrc = &rep->cell[4]; + mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); + mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = (data[13] >> 1) & 0x3f; + if (rep->num_cell < 6) + return 0; + + mrc = &rep->cell[5]; + mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); + mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[15] & 0x3f; + + return 0; +} + diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c new file mode 100644 index 000000000..881c3755a --- /dev/null +++ b/openbsc/src/gsm_04_11.c @@ -0,0 +1,1241 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2009 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <netinet/in.h> + +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/gsm_04_11.h> +#include <openbsc/gsm_04_08.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/signal.h> +#include <openbsc/db.h> +#include <osmocore/talloc.h> +#include <openbsc/transaction.h> +#include <openbsc/paging.h> +#include <openbsc/bsc_rll.h> +#include <openbsc/chan_alloc.h> + +#define GSM411_ALLOC_SIZE 1024 +#define GSM411_ALLOC_HEADROOM 128 + +#define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */ + +void *tall_gsms_ctx; +static u_int32_t new_callref = 0x40000001; + +static const struct value_string cp_cause_strs[] = { + { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" }, + { GSM411_CP_CAUSE_CONGESTION, "Congestion" }, + { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" }, + { GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" }, + { GSM411_CP_CAUSE_MSG_INCOMP_STATE, + "Message incompatible with protocol state" }, + { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" }, + { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, 0 } +}; + +static const struct value_string rp_cause_strs[] = { + { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" }, + { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" }, + { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" }, + { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" }, + { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" }, + { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" }, + { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" }, + { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" }, + { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" }, + { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" }, + { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" }, + { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" }, + { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" }, + /* valid only for MT */ + { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" }, + /* valid for both directions */ + { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" }, + { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" }, + { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" }, + { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" }, + { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, NULL } +}; + +struct gsm_sms *sms_alloc(void) +{ + return talloc_zero(tall_gsms_ctx, struct gsm_sms); +} + +void sms_free(struct gsm_sms *sms) +{ + /* drop references to subscriber structure */ + if (sms->sender) + subscr_put(sms->sender); + if (sms->receiver) + subscr_put(sms->receiver); + + talloc_free(sms); +} + +struct msgb *gsm411_msgb_alloc(void) +{ + return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM, + "GSM 04.11"); +} + +static int gsm411_sendmsg(struct msgb *msg, u_int8_t link_id) +{ + if (msg->lchan) + msg->trx = msg->lchan->ts->trx; + + msg->l3h = msg->data; + + DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len)); + + return rsl_data_request(msg, link_id); +} + +/* SMC TC1* is expired */ +static void cp_timer_expired(void *data) +{ + struct gsm_trans *trans = data; + + DEBUGP(DSMS, "SMC Timer TC1* is expired, calling trans_free()\n"); + /* FIXME: we need to re-transmit the last CP-DATA 1..3 times */ + trans_free(trans); +} + +/* Prefix msg with a 04.08/04.11 CP header */ +static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans, + u_int8_t msg_type) +{ + struct gsm48_hdr *gh; + + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + /* Outgoing needs the highest bit set */ + gh->proto_discr = trans->protocol | (trans->transaction_id<<4); + gh->msg_type = msg_type; + + /* assign the outgoing lchan */ + msg->lchan = trans->lchan; + + /* mobile originating */ + switch (gh->msg_type) { + case GSM411_MT_CP_DATA: + /* 5.2.3.1.2: enter MO-wait for CP-ack */ + /* 5.2.3.2.3: enter MT-wait for CP-ACK */ + trans->sms.cp_state = GSM411_CPS_WAIT_CP_ACK; + trans->sms.cp_timer.data = trans; + trans->sms.cp_timer.cb = cp_timer_expired; + /* 5.3.2.1: Set Timer TC1A */ + bsc_schedule_timer(&trans->sms.cp_timer, GSM411_TMR_TC1A); + DEBUGP(DSMS, "TX: CP-DATA "); + break; + case GSM411_MT_CP_ACK: + DEBUGP(DSMS, "TX: CP-ACK "); + break; + case GSM411_MT_CP_ERROR: + DEBUGP(DSMS, "TX: CP-ERROR "); + break; + } + + DEBUGPC(DSMS, "trans=%x\n", trans->transaction_id); + + return gsm411_sendmsg(msg, trans->sms.link_id); +} + +/* Prefix msg with a RP-DATA header and send as CP-DATA */ +static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans, + u_int8_t rp_msg_type, u_int8_t rp_msg_ref) +{ + struct gsm411_rp_hdr *rp; + u_int8_t len = msg->len; + + /* GSM 04.11 RP-DATA header */ + rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); + rp->len = len + 2; + rp->msg_type = rp_msg_type; + rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ + + return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA); +} + +/* Turn int into semi-octet representation: 98 => 0x89 */ +static u_int8_t bcdify(u_int8_t value) +{ + u_int8_t ret; + + ret = value / 10; + ret |= (value % 10) << 4; + + return ret; +} + +/* Turn semi-octet representation into int: 0x89 => 98 */ +static u_int8_t unbcdify(u_int8_t value) +{ + u_int8_t ret; + + if ((value & 0x0F) > 9 || (value >> 4) > 9) + LOGP(DSMS, LOGL_ERROR, + "unbcdify got too big nibble: 0x%02X\n", value); + + ret = (value&0x0F)*10; + ret += value>>4; + + return ret; +} + +/* Generate 03.40 TP-SCTS */ +static void gsm340_gen_scts(u_int8_t *scts, time_t time) +{ + struct tm *tm = localtime(&time); + + *scts++ = bcdify(tm->tm_year % 100); + *scts++ = bcdify(tm->tm_mon + 1); + *scts++ = bcdify(tm->tm_mday); + *scts++ = bcdify(tm->tm_hour); + *scts++ = bcdify(tm->tm_min); + *scts++ = bcdify(tm->tm_sec); + *scts++ = bcdify(tm->tm_gmtoff/(60*15)); +} + +/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ +static time_t gsm340_scts(u_int8_t *scts) +{ + struct tm tm; + + u_int8_t yr = unbcdify(*scts++); + + if (yr <= 80) + tm.tm_year = 100 + yr; + else + tm.tm_year = yr; + tm.tm_mon = unbcdify(*scts++) - 1; + tm.tm_mday = unbcdify(*scts++); + tm.tm_hour = unbcdify(*scts++); + tm.tm_min = unbcdify(*scts++); + tm.tm_sec = unbcdify(*scts++); + /* according to gsm 03.40 time zone is + "expressed in quarters of an hour" */ + tm.tm_gmtoff = unbcdify(*scts++) * 15*60; + + return mktime(&tm); +} + +/* Return the default validity period in minutes */ +static unsigned long gsm340_vp_default(void) +{ + unsigned long minutes; + /* Default validity: two days */ + minutes = 24 * 60 * 2; + return minutes; +} + +/* Decode validity period format 'relative' */ +static unsigned long gsm340_vp_relative(u_int8_t *sms_vp) +{ + /* Chapter 9.2.3.12.1 */ + u_int8_t vp; + unsigned long minutes; + + vp = *(sms_vp); + if (vp <= 143) + minutes = vp + 1 * 5; + else if (vp <= 167) + minutes = 12*60 + (vp-143) * 30; + else if (vp <= 196) + minutes = vp-166 * 60 * 24; + else + minutes = vp-192 * 60 * 24 * 7; + return minutes; +} + +/* Decode validity period format 'absolute' */ +static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp) +{ + /* Chapter 9.2.3.12.2 */ + time_t expires, now; + unsigned long minutes; + + expires = gsm340_scts(sms_vp); + now = mktime(gmtime(NULL)); + if (expires <= now) + minutes = 0; + else + minutes = (expires-now)/60; + return minutes; +} + +/* Decode validity period format 'relative in integer representation' */ +static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp) +{ + u_int8_t vp; + unsigned long minutes; + vp = *(sms_vp); + if (vp == 0) { + LOGP(DSMS, LOGL_ERROR, + "reserved relative_integer validity period\n"); + return gsm340_vp_default(); + } + minutes = vp/60; + return minutes; +} + +/* Decode validity period format 'relative in semi-octet representation' */ +static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp) +{ + unsigned long minutes; + minutes = unbcdify(*sms_vp++)*60; /* hours */ + minutes += unbcdify(*sms_vp++); /* minutes */ + minutes += unbcdify(*sms_vp++)/60; /* seconds */ + return minutes; +} + +/* decode validity period. return minutes */ +static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp) +{ + u_int8_t fi; /* functionality indicator */ + + switch (sms_vpf) { + case GSM340_TP_VPF_RELATIVE: + return gsm340_vp_relative(sms_vp); + case GSM340_TP_VPF_ABSOLUTE: + return gsm340_vp_absolute(sms_vp); + case GSM340_TP_VPF_ENHANCED: + /* Chapter 9.2.3.12.3 */ + fi = *sms_vp++; + /* ignore additional fi */ + if (fi & (1<<7)) sms_vp++; + /* read validity period format */ + switch (fi & 0b111) { + case 0b000: + return gsm340_vp_default(); /* no vpf specified */ + case 0b001: + return gsm340_vp_relative(sms_vp); + case 0b010: + return gsm340_vp_relative_integer(sms_vp); + case 0b011: + return gsm340_vp_relative_semioctet(sms_vp); + default: + /* The GSM spec says that the SC should reject any + unsupported and/or undefined values. FIXME */ + LOGP(DSMS, LOGL_ERROR, + "Reserved enhanced validity period format\n"); + return gsm340_vp_default(); + } + case GSM340_TP_VPF_NONE: + default: + return gsm340_vp_default(); + } +} + +/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ +enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs) +{ + u_int8_t cgbits = dcs >> 4; + enum sms_alphabet alpha = DCS_NONE; + + if ((cgbits & 0xc) == 0) { + if (cgbits & 2) + LOGP(DSMS, LOGL_NOTICE, + "Compressed SMS not supported yet\n"); + + switch ((dcs >> 2)&0x03) { + case 0: + alpha = DCS_7BIT_DEFAULT; + break; + case 1: + alpha = DCS_8BIT_DATA; + break; + case 2: + alpha = DCS_UCS2; + break; + } + } else if (cgbits == 0xc || cgbits == 0xd) + alpha = DCS_7BIT_DEFAULT; + else if (cgbits == 0xe) + alpha = DCS_UCS2; + else if (cgbits == 0xf) { + if (dcs & 4) + alpha = DCS_8BIT_DATA; + else + alpha = DCS_7BIT_DEFAULT; + } + + return alpha; +} + +static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms) +{ + if (db_sms_store(gsms) != 0) { + LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + } + /* dispatch a signal to tell higher level about it */ + dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms); + /* try delivering the SMS right now */ + //gsm411_send_sms_subscr(gsms->receiver, gsms); + + return 0; +} + +/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ +static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len, + struct gsm_subscriber *subscr) +{ + int len_in_bytes; + + oa[1] = 0xb9; /* networks-specific number, private numbering plan */ + + len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, subscr->extension); + + /* GSM 03.40 tells us the length is in 'useful semi-octets' */ + oa[0] = strlen(subscr->extension) & 0xff; + + return len_in_bytes; +} + +/* generate a msgb containing a TPDU derived from struct gsm_sms, + * returns total size of TPDU */ +static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms) +{ + u_int8_t *smsp; + u_int8_t oa[12]; /* max len per 03.40 */ + u_int8_t oa_len = 0; + u_int8_t octet_len; + unsigned int old_msg_len = msg->len; + + /* generate first octet with masked bits */ + smsp = msgb_put(msg, 1); + /* TP-MTI (message type indicator) */ + *smsp = GSM340_SMS_DELIVER_SC2MS; + /* TP-MMS (more messages to send) */ + if (0 /* FIXME */) + *smsp |= 0x04; + /* TP-SRI(deliver)/SRR(submit) */ + if (sms->status_rep_req) + *smsp |= 0x20; + /* TP-UDHI (indicating TP-UD contains a header) */ + if (sms->ud_hdr_ind) + *smsp |= 0x40; +#if 0 + /* TP-RP (indicating that a reply path exists) */ + if (sms-> + *smsp |= 0x80; +#endif + + /* generate originator address */ + oa_len = gsm340_gen_oa(oa, sizeof(oa), sms->sender); + smsp = msgb_put(msg, oa_len); + memcpy(smsp, oa, oa_len); + + /* generate TP-PID */ + smsp = msgb_put(msg, 1); + *smsp = sms->protocol_id; + + /* generate TP-DCS */ + smsp = msgb_put(msg, 1); + *smsp = sms->data_coding_scheme; + + /* generate TP-SCTS */ + smsp = msgb_put(msg, 7); + gsm340_gen_scts(smsp, time(NULL)); + + /* generate TP-UDL */ + smsp = msgb_put(msg, 1); + *smsp = sms->user_data_len; + + /* generate TP-UD */ + switch (gsm338_get_sms_alphabet(sms->data_coding_scheme)) { + case DCS_7BIT_DEFAULT: + octet_len = sms->user_data_len*7/8; + if (sms->user_data_len*7%8 != 0) + octet_len++; + /* Warning, user_data_len indicates the amount of septets + * (characters), we need amount of octets occupied */ + smsp = msgb_put(msg, octet_len); + memcpy(smsp, sms->user_data, octet_len); + break; + case DCS_UCS2: + case DCS_8BIT_DATA: + smsp = msgb_put(msg, sms->user_data_len); + memcpy(smsp, sms->user_data, sms->user_data_len); + break; + default: + LOGP(DSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n", + sms->data_coding_scheme); + break; + } + + return msg->len - old_msg_len; +} + +/* process an incoming TPDU (called from RP-DATA) + * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ +static int gsm340_rx_tpdu(struct msgb *msg) +{ + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + u_int8_t *smsp = msgb_sms(msg); + struct gsm_sms *gsms; + u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp; + u_int8_t *sms_vp; + u_int8_t da_len_bytes; + u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ + int rc = 0; + + counter_inc(bts->network->stats.sms.submitted); + + gsms = sms_alloc(); + if (!gsms) + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + + /* invert those fields where 0 means active/present */ + sms_mti = *smsp & 0x03; + sms_mms = !!(*smsp & 0x04); + sms_vpf = (*smsp & 0x18) >> 3; + gsms->status_rep_req = (*smsp & 0x20); + gsms->ud_hdr_ind = (*smsp & 0x40); + sms_rp = (*smsp & 0x80); + + smsp++; + gsms->msg_ref = *smsp++; + + /* length in bytes of the destination address */ + da_len_bytes = 2 + *smsp/2 + *smsp%2; + if (da_len_bytes > 12) { + LOGP(DSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); + rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; + goto out; + } + memset(address_lv, 0, sizeof(address_lv)); + memcpy(address_lv, smsp, da_len_bytes); + /* mangle first byte to reflect length in bytes, not digits */ + address_lv[0] = da_len_bytes - 1; + /* convert to real number */ + gsm48_decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1); + smsp += da_len_bytes; + + gsms->protocol_id = *smsp++; + gsms->data_coding_scheme = *smsp++; + + sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme); + + switch (sms_vpf) { + case GSM340_TP_VPF_RELATIVE: + sms_vp = smsp++; + break; + case GSM340_TP_VPF_ABSOLUTE: + case GSM340_TP_VPF_ENHANCED: + sms_vp = smsp; + /* the additional functionality indicator... */ + if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7)) + smsp++; + smsp += 7; + break; + case GSM340_TP_VPF_NONE: + sms_vp = 0; + break; + default: + LOGP(DSMS, LOGL_NOTICE, + "SMS Validity period not implemented: 0x%02x\n", sms_vpf); + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + } + gsms->user_data_len = *smsp++; + if (gsms->user_data_len) { + memcpy(gsms->user_data, smsp, gsms->user_data_len); + + switch (sms_alphabet) { + case DCS_7BIT_DEFAULT: + gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len); + break; + case DCS_8BIT_DATA: + case DCS_UCS2: + case DCS_NONE: + break; + } + } + + gsms->sender = subscr_get(msg->lchan->subscr); + + LOGP(DSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, " + "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, " + "UserDataLength: 0x%02x, UserData: \"%s\"\n", + subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref, + gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr, + gsms->user_data_len, + sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : + hexdump(gsms->user_data, gsms->user_data_len)); + + gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp); + + dispatch_signal(SS_SMS, 0, gsms); + + /* determine gsms->receiver based on dialled number */ + gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr); + if (!gsms->receiver) { + rc = 1; /* cause 1: unknown subscriber */ + counter_inc(bts->network->stats.sms.no_receiver); + goto out; + } + + switch (sms_mti) { + case GSM340_SMS_SUBMIT_MS2SC: + /* MS is submitting a SMS */ + rc = gsm340_rx_sms_submit(msg, gsms); + break; + case GSM340_SMS_COMMAND_MS2SC: + case GSM340_SMS_DELIVER_REP_MS2SC: + LOGP(DSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti); + rc = GSM411_RP_CAUSE_IE_NOTEXIST; + break; + default: + LOGP(DSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti); + rc = GSM411_RP_CAUSE_IE_NOTEXIST; + break; + } + + if (!rc && !gsms->receiver) + rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; + +out: + sms_free(gsms); + + return rc; +} + +static int gsm411_send_rp_ack(struct gsm_trans *trans, u_int8_t msg_ref) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + DEBUGP(DSMS, "TX: SMS RP ACK\n"); + + return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ACK_MT, msg_ref); +} + +static int gsm411_send_rp_error(struct gsm_trans *trans, + u_int8_t msg_ref, u_int8_t cause) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + msgb_tv_put(msg, 1, cause); + + LOGP(DSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, + get_value_string(rp_cause_strs, cause)); + + return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref); +} + +/* Receive a 04.11 TPDU inside RP-DATA / user data */ +static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph, + u_int8_t src_len, u_int8_t *src, + u_int8_t dst_len, u_int8_t *dst, + u_int8_t tpdu_len, u_int8_t *tpdu) +{ + int rc = 0; + + if (src_len && src) + LOGP(DSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); + + if (!dst_len || !dst || !tpdu_len || !tpdu) { + LOGP(DSMS, LOGL_ERROR, + "RP-DATA (MO) without DST or TPDU ?!?\n"); + gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_INV_MAND_INF); + return -EIO; + } + msg->smsh = tpdu; + + DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len)); + + rc = gsm340_rx_tpdu(msg); + if (rc == 0) + return gsm411_send_rp_ack(trans, rph->msg_ref); + else if (rc > 0) + return gsm411_send_rp_error(trans, rph->msg_ref, rc); + else + return rc; +} + +/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ +static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + u_int8_t src_len, dst_len, rpud_len; + u_int8_t *src = NULL, *dst = NULL , *rp_ud = NULL; + + /* in the MO case, this should always be zero length */ + src_len = rph->data[0]; + if (src_len) + src = &rph->data[1]; + + dst_len = rph->data[1+src_len]; + if (dst_len) + dst = &rph->data[1+src_len+1]; + + rpud_len = rph->data[1+src_len+1+dst_len]; + if (rpud_len) + rp_ud = &rph->data[1+src_len+1+dst_len+1]; + + DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", + src_len, dst_len, rpud_len); + return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst, + rpud_len, rp_ud); +} + +/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */ +static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_sms *sms = trans->sms.sms; + + /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it + * successfully received a SMS. We can now safely mark it as + * transmitted */ + + if (!trans->sms.is_mt) { + LOGP(DSMS, LOGL_ERROR, "RX RP-ACK on a MO transfer ?\n"); + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); + } + + if (!sms) { + LOGP(DSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n"); + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_PROTOCOL_ERR); + } + + /* mark this SMS as sent in database */ + db_sms_mark_sent(sms); + + dispatch_signal(SS_SMS, S_SMS_DELIVERED, sms); + + sms_free(sms); + trans->sms.sms = NULL; + + /* check for more messages for this subscriber */ + sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr); + if (sms) + gsm411_send_sms_lchan(msg->lchan, sms); + + /* free the transaction here */ + trans_free(trans); + + /* release channel if done */ + if (!sms) + rsl_release_request(msg->lchan, trans->sms.link_id); + + return 0; +} + +static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_network *net = trans->lchan->ts->trx->bts->network; + struct gsm_sms *sms = trans->sms.sms; + u_int8_t cause_len = rph->data[0]; + u_int8_t cause = rph->data[1]; + + /* Error in response to MT RP_DATA, i.e. the MS did not + * successfully receive the SMS. We need to investigate + * the cause and take action depending on it */ + + LOGP(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", + subscr_name(msg->lchan->subscr), cause_len, cause, + get_value_string(rp_cause_strs, cause)); + + if (!trans->sms.is_mt) { + LOGP(DSMS, LOGL_ERROR, "RX RP-ERR on a MO transfer ?\n"); +#if 0 + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); +#endif + } + + if (!sms) { + LOGP(DSMS, LOGL_ERROR, + "RX RP-ERR, but no sms in transaction?!?\n"); + return -EINVAL; +#if 0 + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_PROTOCOL_ERR); +#endif + } + + if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) { + /* MS has not enough memory to store the message. We need + * to store this in our database and wati for a SMMA message */ + /* FIXME */ + dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr); + counter_inc(net->stats.sms.rp_err_mem); + } else + counter_inc(net->stats.sms.rp_err_other); + + sms_free(sms); + trans->sms.sms = NULL; + + //trans_free(trans); + + return 0; +} + +static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_sms *sms; + int rc; + + rc = gsm411_send_rp_ack(trans, rph->msg_ref); + trans->sms.rp_state = GSM411_RPS_IDLE; + + /* MS tells us that it has memory for more SMS, we need + * to check if we have any pending messages for it and then + * transfer those */ + dispatch_signal(SS_SMS, S_SMS_SMMA, trans->subscr); + + /* check for more messages for this subscriber */ + sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr); + if (sms) + gsm411_send_sms_lchan(msg->lchan, sms); + else + rsl_release_request(msg->lchan, trans->sms.link_id); + + return rc; +} + +static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh, + struct gsm_trans *trans) +{ + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + u_int8_t msg_type = rp_data->msg_type & 0x07; + int rc = 0; + + switch (msg_type) { + case GSM411_MT_RP_DATA_MO: + DEBUGP(DSMS, "RX SMS RP-DATA (MO)\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK; + rc = gsm411_rx_rp_data(msg, trans, rp_data); + break; + case GSM411_MT_RP_ACK_MO: + DEBUGP(DSMS,"RX SMS RP-ACK (MO)\n"); + rc = gsm411_rx_rp_ack(msg, trans, rp_data); + break; + case GSM411_MT_RP_SMMA_MO: + DEBUGP(DSMS, "RX SMS RP-SMMA\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK; + rc = gsm411_rx_rp_smma(msg, trans, rp_data); + break; + case GSM411_MT_RP_ERROR_MO: + rc = gsm411_rx_rp_error(msg, trans, rp_data); + break; + default: + LOGP(DSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + rc = gsm411_send_rp_error(trans, rp_data->msg_ref, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); + break; + } + + return rc; +} + +/* send CP-ACK to given transaction */ +static int gsm411_tx_cp_ack(struct gsm_trans *trans) +{ + struct msgb *msg = gsm411_msgb_alloc(); + int rc; + + rc = gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK); + + if (trans->sms.is_mt) { + /* If this is a MT SMS DELIVER, we can clear transaction here */ + trans->sms.cp_state = GSM411_CPS_IDLE; + //trans_free(trans); + } + + return rc; +} + +static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause) +{ + struct msgb *msg = gsm411_msgb_alloc(); + u_int8_t *causep; + + LOGP(DSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause, + get_value_string(cp_cause_strs, cause)); + + causep = msgb_put(msg, 1); + *causep = cause; + + return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ERROR); +} + +/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */ +int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t msg_type = gh->msg_type; + u_int8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */ + struct gsm_lchan *lchan = msg->lchan; + struct gsm_trans *trans; + int rc = 0; + + if (!lchan->subscr) + return -EIO; + /* FIXME: send some error message */ + + DEBUGP(DSMS, "trans_id=%x ", transaction_id); + trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS, + transaction_id); + if (!trans) { + DEBUGPC(DSMS, "(new) "); + trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS, + transaction_id, new_callref++); + if (!trans) { + DEBUGPC(DSMS, "No memory for trans\n"); + /* FIXME: send some error message */ + return -ENOMEM; + } + trans->sms.cp_state = GSM411_CPS_IDLE; + trans->sms.rp_state = GSM411_RPS_IDLE; + trans->sms.is_mt = 0; + trans->sms.link_id = link_id; + + trans->lchan = lchan; + use_lchan(lchan); + } + + switch(msg_type) { + case GSM411_MT_CP_DATA: + DEBUGPC(DSMS, "RX SMS CP-DATA\n"); + + /* 5.4: For MO, if a CP-DATA is received for a new + * transaction, equals reception of an implicit + * last CP-ACK for previous transaction */ + if (trans->sms.cp_state == GSM411_CPS_IDLE) { + int i; + struct gsm_trans *ptrans; + + /* Scan through all remote initiated transactions */ + for (i=8; i<15; i++) { + if (i == transaction_id) + continue; + + ptrans = trans_find_by_id(lchan->subscr, + GSM48_PDISC_SMS, i); + if (!ptrans) + continue; + + DEBUGP(DSMS, "Implicit CP-ACK for trans_id=%x\n", i); + + /* Finish it for good */ + bsc_del_timer(&ptrans->sms.cp_timer); + ptrans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(ptrans); + } + } + + /* 5.2.3.1.3: MO state exists when SMC has received + * CP-DATA, including sending of the assoc. CP-ACK */ + /* 5.2.3.2.4: MT state exists when SMC has received + * CP-DATA, including sending of the assoc. CP-ACK */ + trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED; + + /* SMC instance acknowledges the CP-DATA frame */ + gsm411_tx_cp_ack(trans); + + rc = gsm411_rx_cp_data(msg, gh, trans); +#if 0 + /* Send CP-ACK or CP-ERORR in response */ + if (rc < 0) { + rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_NET_FAIL); + } else + rc = gsm411_tx_cp_ack(trans); +#endif + break; + case GSM411_MT_CP_ACK: + /* previous CP-DATA in this transaction was confirmed */ + DEBUGPC(DSMS, "RX SMS CP-ACK\n"); + /* 5.2.3.1.3: MO state exists when SMC has received CP-ACK */ + /* 5.2.3.2.4: MT state exists when SMC has received CP-ACK */ + trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED; + /* Stop TC1* after CP-ACK has been received */ + bsc_del_timer(&trans->sms.cp_timer); + + if (!trans->sms.is_mt) { + /* FIXME: we have sont one CP-DATA, which was now + * acknowledged. Check if we want to transfer more, + * i.e. multi-part message */ + trans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(trans); + } + break; + case GSM411_MT_CP_ERROR: + DEBUGPC(DSMS, "RX SMS CP-ERROR, cause %d (%s)\n", gh->data[0], + get_value_string(cp_cause_strs, gh->data[0])); + bsc_del_timer(&trans->sms.cp_timer); + trans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(trans); + break; + default: + DEBUGPC(DSMS, "RX Unimplemented CP msg_type: 0x%02x\n", msg_type); + rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST); + trans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(trans); + break; + } + + return rc; +} + +#if 0 +/* Test TPDU - ALL YOUR */ +static u_int8_t tpdu_test[] = { + 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24, + 0x32, 0x40, 0x1F, 0x41, 0x26, 0x13, 0x94, 0x7D, 0x56, 0xA5, 0x20, 0x28, + 0xF2, 0xE9, 0x2C, 0x82, 0x82, 0xD2, 0x22, 0x48, 0x58, 0x64, 0x3E, 0x9D, + 0x47, 0x10, 0xF5, 0x09, 0xAA, 0x4E, 0x01 +}; +#endif + +/* Take a SMS in gsm_sms structure and send it through an already + * existing lchan. We also assume that the caller ensured this lchan already + * has a SAPI3 RLL connection! */ +int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms) +{ + struct msgb *msg = gsm411_msgb_alloc(); + struct gsm_trans *trans; + u_int8_t *data, *rp_ud_len; + u_int8_t msg_ref = 42; + int transaction_id; + int rc; + + transaction_id = trans_assign_trans_id(lchan->subscr, GSM48_PDISC_SMS, 0); + if (transaction_id == -1) { + LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n"); + return -EBUSY; + } + + msg->lchan = lchan; + + DEBUGP(DSMS, "send_sms_lchan()\n"); + + /* FIXME: allocate transaction with message reference */ + trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS, + transaction_id, new_callref++); + if (!trans) { + LOGP(DSMS, LOGL_ERROR, "No memory for trans\n"); + /* FIXME: send some error message */ + return -ENOMEM; + } + trans->sms.cp_state = GSM411_CPS_IDLE; + trans->sms.rp_state = GSM411_RPS_IDLE; + trans->sms.is_mt = 1; + trans->sms.sms = sms; + trans->sms.link_id = UM_SAPI_SMS; /* FIXME: main or SACCH ? */ + + trans->lchan = lchan; + use_lchan(lchan); + + /* Hardcode SMSC Originating Address for now */ + data = (u_int8_t *)msgb_put(msg, 8); + data[0] = 0x07; /* originator length == 7 */ + data[1] = 0x91; /* type of number: international, ISDN */ + data[2] = 0x44; /* 447785016005 */ + data[3] = 0x77; + data[4] = 0x58; + data[5] = 0x10; + data[6] = 0x06; + data[7] = 0x50; + + /* Hardcoded Destination Address */ + data = (u_int8_t *)msgb_put(msg, 1); + data[0] = 0; /* destination length == 0 */ + + /* obtain a pointer for the rp_ud_len, so we can fill it later */ + rp_ud_len = (u_int8_t *)msgb_put(msg, 1); + +#if 1 + /* generate the 03.40 TPDU */ + rc = gsm340_gen_tpdu(msg, sms); + if (rc < 0) { + msgb_free(msg); + return rc; + } + + *rp_ud_len = rc; +#else + data = msgb_put(msg, sizeof(tpdu_test)); + memcpy(data, tpdu_test, sizeof(tpdu_test)); + *rp_ud_len = sizeof(tpdu_test); +#endif + + DEBUGP(DSMS, "TX: SMS DELIVER\n"); + + counter_inc(lchan->ts->trx->bts->network->stats.sms.delivered); + + return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref); + /* FIXME: enter 'wait for RP-ACK' state, start TR1N */ +} + +/* RLL SAPI3 establish callback. Now we have a RLL connection and + * can deliver the actual message */ +static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id, + void *_sms, enum bsc_rllr_ind type) +{ + struct gsm_sms *sms = _sms; + + DEBUGP(DSMS, "rll_ind_cb(lchan=%p, link_id=%u, sms=%p, type=%u\n", + lchan, link_id, sms, type); + + switch (type) { + case BSC_RLLR_IND_EST_CONF: + gsm411_send_sms_lchan(lchan, sms); + break; + case BSC_RLLR_IND_REL_IND: + case BSC_RLLR_IND_ERR_IND: + case BSC_RLLR_IND_TIMEOUT: + sms_free(sms); + break; + } +} + +/* paging callback. Here we get called if paging a subscriber has + * succeeded or failed. */ +static int paging_cb_send_sms(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_lchan, void *_sms) +{ + struct gsm_lchan *lchan = _lchan; + struct gsm_sms *sms = _sms; + int rc; + + DEBUGP(DSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p," + "lchan=%p, sms=%p)\n", hooknum, event, msg, lchan, sms); + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + switch (event) { + case GSM_PAGING_SUCCEEDED: + /* Paging aborted without lchan ?!? */ + if (!lchan) { + sms_free(sms); + rc = -EIO; + break; + } + /* Establish a SAPI3 RLL connection for SMS */ + rc = rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms); + break; + case GSM_PAGING_EXPIRED: + sms_free(sms); + rc = -ETIMEDOUT; + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +/* high-level function to send a SMS to a given subscriber. The function + * will take care of paging the subscriber, establishing the RLL SAPI3 + * connection, etc. */ +int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, + struct gsm_sms *sms) +{ + struct gsm_lchan *lchan; + int rc; + + /* check if we already have an open lchan to the subscriber. + * if yes, send the SMS this way */ + lchan = lchan_for_subscr(subscr); + if (lchan) + return rll_establish(lchan, UM_SAPI_SMS, + rll_ind_cb, sms); + + /* if not, we have to start paging */ + rc = paging_request(subscr->net, subscr, RSL_CHANNEED_SDCCH, + paging_cb_send_sms, sms); + if (rc <= 0) + sms_free(sms); + + return 0; +} + +static int subscr_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_subscriber *subscr; + struct gsm_lchan *lchan; + struct gsm_sms *sms; + + switch (signal) { + case S_SUBSCR_ATTACHED: + /* A subscriber has attached. Check if there are + * any pending SMS for him to be delivered */ + subscr = signal_data; + lchan = lchan_for_subscr(subscr); + if (!lchan) + break; + sms = db_sms_get_unsent_for_subscr(subscr); + if (!sms) + break; + /* Establish a SAPI3 RLL connection for SMS */ + rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms); + break; + default: + break; + } + return 0; +} + +void _gsm411_sms_trans_free(struct gsm_trans *trans) +{ + bsc_del_timer(&trans->sms.cp_timer); +} + +static __attribute__((constructor)) void on_dso_load_sms(void) +{ + register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL); +} diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c new file mode 100644 index 000000000..8271274f1 --- /dev/null +++ b/openbsc/src/gsm_04_80.c @@ -0,0 +1,330 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 2009 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/gsm_04_80.h> + +/* Forward declarations */ +static int parse_ussd(u_int8_t *ussd, struct ussd_request *req); +static int parse_ussd_info_elements(u_int8_t *ussd_ie, + struct ussd_request *req); +static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length, + struct ussd_request *req); +static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length, + struct ussd_request *req); +static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length, + struct ussd_request *req); + +static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_t tag) +{ + msgb->data -= 2; + msgb->data[0] = tag; + msgb->data[1] = msgb->len; + msgb->len += 2; + return msgb->data; +} + +static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, u_int8_t tag, + u_int8_t value) +{ + msgb->data -= 3; + msgb->len += 3; + msgb->data[0] = tag; + msgb->data[1] = 1; + msgb->data[2] = value; + return msgb->data; +} + + +/* Decode a mobile-originated USSD-request message */ +int gsm0480_decode_ussd_request(const struct msgb *msg, struct ussd_request *req) +{ + int rc = 0; + u_int8_t *parse_ptr = msgb_l3(msg); + + if ((*parse_ptr & 0x0F) == GSM48_PDISC_NC_SS) { + req->transaction_id = *parse_ptr & 0x70; + rc = parse_ussd(parse_ptr+1, req); + } + + if (!rc) + DEBUGP(DMM, "Error occurred while parsing received USSD!\n"); + + return rc; +} + +static int parse_ussd(u_int8_t *ussd, struct ussd_request *req) +{ + int rc = 1; + u_int8_t msg_type = ussd[0] & 0xBF; /* message-type - section 3.4 */ + + switch (msg_type) { + case GSM0480_MTYPE_RELEASE_COMPLETE: + DEBUGP(DMM, "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(ussd+1, req); + break; + default: + fprintf(stderr, "Unknown GSM 04.80 message-type field 0x%02x\n", + ussd[0]); + rc = 0; + break; + } + + return rc; +} + +static int parse_ussd_info_elements(u_int8_t *ussd_ie, struct ussd_request *req) +{ + int rc; + /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */ + u_int8_t iei = ussd_ie[0]; + u_int8_t iei_length = ussd_ie[1]; + + 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: + fprintf(stderr, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n", + iei); + rc = 0; + break; + } + + return rc; +} + +static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length, + struct ussd_request *req) +{ + int rc = 1; + u_int8_t offset = 0; + + do { + /* Component Type tag - table 3.7 */ + u_int8_t component_type = facility_ie[offset]; + u_int8_t component_length = facility_ie[offset+1]; + + 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: + fprintf(stderr, "Unknown GSM 04.80 Facility " + "Component Type 0x%02x\n", component_type); + rc = 0; + break; + } + offset += (component_length+2); + } while (offset < length); + + return rc; +} + +/* Parse an Invoke component - see table 3.3 */ +static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length, + struct ussd_request *req) +{ + int rc = 1; + u_int8_t offset; + + /* mandatory part */ + if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) { + fprintf(stderr, "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]; + + /* 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) { + u_int8_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: + fprintf(stderr, "GSM 04.80 operation code 0x%02x " + "is not yet handled\n", operation_code); + rc = 0; + break; + } + } else { + fprintf(stderr, "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(u_int8_t *uss_req_data, u_int8_t length, + struct ussd_request *req) +{ + int rc = 0; + int num_chars; + u_int8_t dcs; + + 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); + /* append null-terminator */ + req->text[num_chars+1] = 0; + rc = 1; + } + } + } + return rc; +} + +/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ +int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_text, + const struct ussd_request *req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + u_int8_t *ptr8; + int response_len; + + response_len = (strlen(response_text) * 7) / 8; + if (((strlen(response_text) * 7) % 8) != 0) + response_len += 1; + + msg->bts_link = in_msg->bts_link; + msg->lchan = in_msg->lchan; + + /* First put the payload text into the message */ + ptr8 = msgb_put(msg, response_len); + gsm_7bit_encode(ptr8, response_text); + + /* 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, req->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 | req->transaction_id + | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm48_sendmsg(msg, NULL); +} + +int gsm0480_send_ussd_reject(const struct msgb *in_msg, + const struct ussd_request *req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->bts_link = in_msg->bts_link; + msg->lchan = in_msg->lchan; + + /* First insert the problem code */ + msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, + GSM_0480_GEN_PROB_CODE_UNRECOGNISED); + + /* Before it insert the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + + /* Wrap this up as a Reject component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); + + /* 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; + gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm48_sendmsg(msg, NULL); +} diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c new file mode 100644 index 000000000..5314d1212 --- /dev/null +++ b/openbsc/src/gsm_data.c @@ -0,0 +1,567 @@ +/* (C) 2008-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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include <netinet/in.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/talloc.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/abis_nm.h> +#include <osmocore/statistics.h> + +void *tall_bsc_ctx; + +static LLIST_HEAD(bts_models); + +void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr, + u_int8_t e1_ts, u_int8_t e1_ts_ss) +{ + ts->e1_link.e1_nr = e1_nr; + ts->e1_link.e1_ts = e1_ts; + ts->e1_link.e1_ts_ss = e1_ts_ss; +} + +static const char *pchan_names[] = { + [GSM_PCHAN_NONE] = "NONE", + [GSM_PCHAN_CCCH] = "CCCH", + [GSM_PCHAN_CCCH_SDCCH4] = "CCCH+SDCCH4", + [GSM_PCHAN_TCH_F] = "TCH/F", + [GSM_PCHAN_TCH_H] = "TCH/H", + [GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8", + [GSM_PCHAN_PDCH] = "PDCH", + [GSM_PCHAN_TCH_F_PDCH] = "TCH/F_PDCH", + [GSM_PCHAN_UNKNOWN] = "UNKNOWN", +}; + +const char *gsm_pchan_name(enum gsm_phys_chan_config c) +{ + if (c >= ARRAY_SIZE(pchan_names)) + return "INVALID"; + + return pchan_names[c]; +} + +enum gsm_phys_chan_config gsm_pchan_parse(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pchan_names); i++) { + if (!strcasecmp(name, pchan_names[i])) + return i; + } + + return -1; +} + +static const char *lchan_names[] = { + [GSM_LCHAN_NONE] = "NONE", + [GSM_LCHAN_SDCCH] = "SDCCH", + [GSM_LCHAN_TCH_F] = "TCH/F", + [GSM_LCHAN_TCH_H] = "TCH/H", + [GSM_LCHAN_UNKNOWN] = "UNKNOWN", +}; + +const char *gsm_lchant_name(enum gsm_chan_t c) +{ + if (c >= ARRAY_SIZE(lchan_names)) + return "INVALID"; + + return lchan_names[c]; +} + +static const struct value_string lchan_s_names[] = { + { LCHAN_S_NONE, "NONE" }, + { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, + { LCHAN_S_ACTIVE, "ACTIVE" }, + { LCHAN_S_INACTIVE, "INACTIVE" }, + { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, + { 0, NULL }, +}; + +const char *gsm_lchans_name(enum gsm_lchan_state s) +{ + return get_value_string(lchan_s_names, s); +} + +static const char *chreq_names[] = { + [GSM_CHREQ_REASON_EMERG] = "EMERGENCY", + [GSM_CHREQ_REASON_PAG] = "PAGING", + [GSM_CHREQ_REASON_CALL] = "CALL", + [GSM_CHREQ_REASON_LOCATION_UPD] = "LOCATION_UPDATE", + [GSM_CHREQ_REASON_OTHER] = "OTHER", +}; + +const char *gsm_chreq_name(enum gsm_chreq_reason_t c) +{ + if (c >= ARRAY_SIZE(chreq_names)) + return "INVALID"; + + return chreq_names[c]; +} + +static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + llist_for_each_entry(model, &bts_models, list) { + if (model->type == type) + return model; + } + + return NULL; +} + +int gsm_bts_model_register(struct gsm_bts_model *model) +{ + if (bts_model_find(model->type)) + return -EEXIST; + + tlv_def_patch(&model->nm_att_tlvdef, &nm_att_tlvdef); + llist_add_tail(&model->list, &bts_models); + return 0; +} + + +struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); + int k; + + if (!trx) + return NULL; + + trx->bts = bts; + trx->nr = bts->num_trx++; + trx->nm_state.administrative = NM_STATE_UNLOCKED; + + for (k = 0; k < TRX_NR_TS; k++) { + struct gsm_bts_trx_ts *ts = &trx->ts[k]; + int l; + + ts->trx = trx; + ts->nr = k; + ts->pchan = GSM_PCHAN_NONE; + + for (l = 0; l < TS_MAX_LCHAN; l++) { + struct gsm_lchan *lchan; + lchan = &ts->lchan[l]; + + lchan->ts = ts; + lchan->nr = l; + lchan->type = GSM_LCHAN_NONE; + } + } + + if (trx->nr != 0) + trx->nominal_power = bts->c0->nominal_power; + + llist_add_tail(&trx->list, &bts->trx_list); + + return trx; +} + +struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, + u_int8_t tsc, u_int8_t bsic) +{ + struct gsm_bts *bts = talloc_zero(net, struct gsm_bts); + struct gsm_bts_model *model = bts_model_find(type); + int i; + + if (!bts) + return NULL; + + if (!model && type != GSM_BTS_TYPE_UNKNOWN) { + talloc_free(bts); + return NULL; + } + + bts->network = net; + bts->nr = net->num_bts++; + bts->type = type; + bts->model = model; + bts->tsc = tsc; + bts->bsic = bsic; + bts->num_trx = 0; + INIT_LLIST_HEAD(&bts->trx_list); + bts->ms_max_power = 15; /* dBm */ + bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ + bts->si_common.cell_sel_par.rxlev_acc_min = 0; + bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; + bts->si_common.neigh_list.data_len = + sizeof(bts->si_common.data.neigh_list); + bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; + bts->si_common.cell_alloc.data_len = + sizeof(bts->si_common.data.cell_alloc); + bts->si_common.rach_control.re = 1; /* no re-establishment */ + bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ + bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ + bts->si_common.rach_control.t2 = 4; /* no emergency calls */ + + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { + bts->gprs.nsvc[i].bts = bts; + bts->gprs.nsvc[i].id = i; + } + + /* create our primary TRX */ + bts->c0 = gsm_bts_trx_alloc(bts); + if (!bts->c0) { + talloc_free(bts); + return NULL; + } + bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; + + llist_add_tail(&bts->list, &net->bts_list); + + return bts; +} + +struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_code, + int (*mncc_recv)(struct gsm_network *, int, void *)) +{ + struct gsm_network *net; + + net = talloc_zero(tall_bsc_ctx, struct gsm_network); + if (!net) + return NULL; + + net->country_code = country_code; + net->network_code = network_code; + net->num_bts = 0; + net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; + net->T3101 = GSM_T3101_DEFAULT; + net->T3113 = GSM_T3113_DEFAULT; + /* FIXME: initialize all other timers! */ + + /* default set of handover parameters */ + net->handover.win_rxlev_avg = 10; + net->handover.win_rxqual_avg = 1; + net->handover.win_rxlev_avg_neigh = 10; + net->handover.pwr_interval = 6; + net->handover.pwr_hysteresis = 3; + net->handover.max_distance = 9999; + + INIT_LLIST_HEAD(&net->trans_list); + INIT_LLIST_HEAD(&net->upqueue); + INIT_LLIST_HEAD(&net->bts_list); + + net->stats.chreq.total = counter_alloc("net.chreq.total"); + net->stats.chreq.no_channel = counter_alloc("net.chreq.no_channel"); + net->stats.handover.attempted = counter_alloc("net.handover.attempted"); + net->stats.handover.no_channel = counter_alloc("net.handover.no_channel"); + net->stats.handover.timeout = counter_alloc("net.handover.timeout"); + net->stats.handover.completed = counter_alloc("net.handover.completed"); + net->stats.handover.failed = counter_alloc("net.handover.failed"); + net->stats.loc_upd_type.attach = counter_alloc("net.loc_upd_type.attach"); + net->stats.loc_upd_type.normal = counter_alloc("net.loc_upd_type.normal"); + net->stats.loc_upd_type.periodic = counter_alloc("net.loc_upd_type.periodic"); + net->stats.loc_upd_type.detach = counter_alloc("net.imsi_detach.count"); + net->stats.loc_upd_resp.reject = counter_alloc("net.loc_upd_resp.reject"); + net->stats.loc_upd_resp.accept = counter_alloc("net.loc_upd_resp.accept"); + net->stats.paging.attempted = counter_alloc("net.paging.attempted"); + net->stats.paging.detached = counter_alloc("net.paging.detached"); + net->stats.paging.completed = counter_alloc("net.paging.completed"); + net->stats.paging.expired = counter_alloc("net.paging.expired"); + net->stats.sms.submitted = counter_alloc("net.sms.submitted"); + net->stats.sms.no_receiver = counter_alloc("net.sms.no_receiver"); + net->stats.sms.delivered = counter_alloc("net.sms.delivered"); + net->stats.sms.rp_err_mem = counter_alloc("net.sms.rp_err_mem"); + net->stats.sms.rp_err_other = counter_alloc("net.sms.rp_err_other"); + net->stats.call.dialled = counter_alloc("net.call.dialled"); + net->stats.call.alerted = counter_alloc("net.call.alerted"); + net->stats.call.connected = counter_alloc("net.call.connected"); + + net->mncc_recv = mncc_recv; + + return net; +} + +struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) +{ + struct gsm_bts *bts; + + if (num >= net->num_bts) + return NULL; + + llist_for_each_entry(bts, &net->bts_list, list) { + if (bts->nr == num) + return bts; + } + + return NULL; +} + +/* Get reference to a neighbor cell on a given BCCH ARFCN */ +struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, + u_int16_t arfcn, u_int8_t bsic) +{ + struct gsm_bts *neigh; + /* FIXME: use some better heuristics here to determine which cell + * using this ARFCN really is closest to the target cell. For + * now we simply assume that each ARFCN will only be used by one + * cell */ + + llist_for_each_entry(neigh, &bts->network->bts_list, list) { + if (neigh->c0->arfcn == arfcn && + neigh->bsic == bsic) + return neigh; + } + + return NULL; +} + +struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num) +{ + struct gsm_bts_trx *trx; + + if (num >= bts->num_trx) + return NULL; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == num) + return trx; + } + + return NULL; +} + +static char ts2str[255]; + +char *gsm_trx_name(struct gsm_bts_trx *trx) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", + trx->bts->nr, trx->nr); + + return ts2str; +} + + +char *gsm_ts_name(struct gsm_bts_trx_ts *ts) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr); + + return ts2str; +} + +char *gsm_lchan_name(struct gsm_lchan *lchan) +{ + struct gsm_bts_trx_ts *ts = lchan->ts; + + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); + + return ts2str; +} + +static const char *bts_types[] = { + [GSM_BTS_TYPE_UNKNOWN] = "unknown", + [GSM_BTS_TYPE_BS11] = "bs11", + [GSM_BTS_TYPE_NANOBTS] = "nanobts", +}; + +enum gsm_bts_type parse_btstype(const char *arg) +{ + int i; + for (i = 0; i < ARRAY_SIZE(bts_types); i++) { + if (!strcmp(arg, bts_types[i])) + return i; + } + return GSM_BTS_TYPE_BS11; /* Default: BS11 */ +} + +const char *btstype2str(enum gsm_bts_type type) +{ + if (type > ARRAY_SIZE(bts_types)) + return "undefined"; + return bts_types[type]; +} + +struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == nr) + return trx; + } + return NULL; +} + +/* Search for a BTS in the given Location Area; optionally start searching + * with start_bts (for continuing to search after the first result) */ +struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, + struct gsm_bts *start_bts) +{ + int i; + struct gsm_bts *bts; + int skip = 0; + + if (start_bts) + skip = 1; + + for (i = 0; i < net->num_bts; i++) { + bts = gsm_bts_num(net, i); + + if (skip) { + if (start_bts == bts) + skip = 0; + continue; + } + + if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) + return bts; + } + return NULL; +} + +static const char *gsm_auth_policy_names[] = { + [GSM_AUTH_POLICY_CLOSED] = "closed", + [GSM_AUTH_POLICY_ACCEPT_ALL] = "accept-all", + [GSM_AUTH_POLICY_TOKEN] = "token", +}; + +enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) +{ + int i; + for (i = 0; i < ARRAY_SIZE(gsm_auth_policy_names); i++) { + if (!strcmp(arg, gsm_auth_policy_names[i])) + return i; + } + return GSM_AUTH_POLICY_CLOSED; +} + +const char *gsm_auth_policy_name(enum gsm_auth_policy policy) +{ + if (policy > ARRAY_SIZE(gsm_auth_policy_names)) + return "undefined"; + return gsm_auth_policy_names[policy]; +} + +/* this should not be here but in gsm_04_08... but that creates + in turn a dependency nightmare (abis_nm depending on 04_08, ...) */ +static int gsm48_construct_ra(u_int8_t *buf, const struct gprs_ra_id *raid) +{ + u_int16_t mcc = raid->mcc; + u_int16_t mnc = raid->mnc; + + buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4); + buf[1] = (mcc % 10); + + /* I wonder who came up with the stupidity of encoding the MNC + * differently depending on how many digits its decimal number has! */ + if (mnc < 100) { + buf[1] |= 0xf0; + buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4); + } else { + buf[1] |= (mnc % 10) << 4; + buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4); + } + + *(u_int16_t *)(buf+3) = htons(raid->lac); + + buf[5] = raid->rac; + + return 6; +} + +void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts) +{ + raid->mcc = bts->network->country_code; + raid->mnc = bts->network->network_code; + raid->lac = bts->location_area_code; + raid->rac = bts->gprs.rac; +} + +int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts) +{ + struct gprs_ra_id raid; + + gprs_ra_id_by_bts(&raid, bts); + + return gsm48_construct_ra(buf, &raid); +} + +static const char *rrlp_mode_names[] = { + [RRLP_MODE_NONE] = "none", + [RRLP_MODE_MS_BASED] = "ms-based", + [RRLP_MODE_MS_PREF] = "ms-preferred", + [RRLP_MODE_ASS_PREF] = "ass-preferred", +}; + +enum rrlp_mode rrlp_mode_parse(const char *arg) +{ + int i; + for (i = 0; i < ARRAY_SIZE(rrlp_mode_names); i++) { + if (!strcmp(arg, rrlp_mode_names[i])) + return i; + } + return RRLP_MODE_NONE; +} + +const char *rrlp_mode_name(enum rrlp_mode mode) +{ + if (mode > ARRAY_SIZE(rrlp_mode_names)) + return "none"; + return rrlp_mode_names[mode]; +} + +struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) +{ + struct gsm_meas_rep *meas_rep; + + meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; + memset(meas_rep, 0, sizeof(*meas_rep)); + meas_rep->lchan = lchan; + lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) + % ARRAY_SIZE(lchan->meas_rep); + + return meas_rep; +} + +int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + model = bts_model_find(type); + if (!model) + return -EINVAL; + + bts->type = type; + bts->model = model; + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + /* Set the default OML Stream ID to 0xff */ + bts->oml_tei = 0xff; + bts->c0->nominal_power = 23; + break; + case GSM_BTS_TYPE_BS11: + break; + } + + return 0; +} diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c new file mode 100644 index 000000000..692508753 --- /dev/null +++ b/openbsc/src/gsm_subscriber.c @@ -0,0 +1,130 @@ +/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ + +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> +#include <openbsc/signal.h> +#include <openbsc/db.h> + +extern struct llist_head *subscr_bsc_active_subscriber(void); + +char *subscr_name(struct gsm_subscriber *subscr) +{ + if (strlen(subscr->name)) + return subscr->name; + + return subscr->imsi; +} + +struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, + u_int32_t tmsi) +{ + char tmsi_string[14]; + struct gsm_subscriber *subscr; + + /* we might have a record in memory already */ + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (tmsi == subscr->tmsi) + return subscr_get(subscr); + } + + sprintf(tmsi_string, "%u", tmsi); + return db_get_subscriber(net, GSM_SUBSCRIBER_TMSI, tmsi_string); +} + +struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (strcmp(subscr->imsi, imsi) == 0) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); +} + +struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, + const char *ext) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (strcmp(subscr->extension, ext) == 0) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext); +} + +struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, + unsigned long long id) +{ + struct gsm_subscriber *subscr; + char buf[32]; + sprintf(buf, "%llu", id); + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (subscr->id == id) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); +} + + +int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) +{ + /* FIXME: Migrate pending requests from one BSC to another */ + switch (reason) { + case GSM_SUBSCRIBER_UPDATE_ATTACHED: + s->net = bts->network; + /* Indicate "attached to LAC" */ + s->lac = bts->location_area_code; + LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", + subscr_name(s), s->lac); + dispatch_signal(SS_SUBSCR, S_SUBSCR_ATTACHED, s); + break; + case GSM_SUBSCRIBER_UPDATE_DETACHED: + /* Only detach if we are currently in this area */ + if (bts->location_area_code == s->lac) + s->lac = GSM_LAC_RESERVED_DETACHED; + LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s)); + dispatch_signal(SS_SUBSCR, S_SUBSCR_DETACHED, s); + break; + default: + fprintf(stderr, "subscr_update with unknown reason: %d\n", + reason); + break; + }; + return db_sync_subscriber(s); +} + + diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c new file mode 100644 index 000000000..dee89c0bc --- /dev/null +++ b/openbsc/src/gsm_subscriber_base.c @@ -0,0 +1,213 @@ +/* The concept of a subscriber as seen by the BSC */ + +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include <osmocore/talloc.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/paging.h> +#include <openbsc/debug.h> + +LLIST_HEAD(active_subscribers); +void *tall_subscr_ctx; +void *tall_sub_req_ctx; + +/* for the gsm_subscriber.c */ +struct llist_head *subscr_bsc_active_subscriber(void) +{ + return &active_subscribers; +} + +/* + * Struct for pending channel requests. This is managed in the + * llist_head requests of each subscriber. The reference counting + * should work in such a way that a subscriber with a pending request + * remains in memory. + */ +struct subscr_request { + struct llist_head entry; + + /* back reference */ + struct gsm_subscriber *subscr; + + /* the requested channel type */ + int channel_type; + + /* the callback data */ + gsm_cbfn *cbfn; + void *param; +}; + +/* + * We got the channel assigned and can now hand this channel + * over to one of our callbacks. + */ +static int subscr_paging_cb(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct subscr_request *request; + struct gsm_subscriber *subscr = (struct gsm_subscriber *)param; + + /* There is no request anymore... */ + if (llist_empty(&subscr->requests)) + return -1; + + /* + * FIXME: What to do with paging requests coming during + * this callback? We must be sure to not start paging when + * we have an active connection to a subscriber and to make + * the subscr_put_channel work as required... + */ + request = (struct subscr_request *)subscr->requests.next; + llist_del(&request->entry); + subscr->in_callback = 1; + request->cbfn(hooknum, event, msg, data, request->param); + subscr->in_callback = 0; + + talloc_free(request); + return 0; +} + +static void subscr_send_paging_request(struct gsm_subscriber *subscr) +{ + struct subscr_request *request; + int rc; + + assert(!llist_empty(&subscr->requests)); + + request = (struct subscr_request *)subscr->requests.next; + rc = paging_request(subscr->net, subscr, request->channel_type, + subscr_paging_cb, subscr); + + /* paging failed, quit now */ + if (rc <= 0) { + subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, + NULL, NULL, request->param); + } +} + +struct gsm_subscriber *subscr_alloc(void) +{ + struct gsm_subscriber *s; + + s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); + if (!s) + return NULL; + + llist_add_tail(&s->entry, &active_subscribers); + s->use_count = 1; + s->tmsi = GSM_RESERVED_TMSI; + + INIT_LLIST_HEAD(&s->requests); + + return s; +} + +static void subscr_free(struct gsm_subscriber *subscr) +{ + llist_del(&subscr->entry); + talloc_free(subscr); +} + +struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) +{ + subscr->use_count++; + DEBUGP(DREF, "subscr %s usage increases usage to: %d\n", + subscr->extension, subscr->use_count); + return subscr; +} + +struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) +{ + subscr->use_count--; + DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n", + subscr->extension, subscr->use_count); + if (subscr->use_count <= 0) + subscr_free(subscr); + return NULL; +} + +void subscr_get_channel(struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *param) +{ + struct subscr_request *request; + + request = talloc(tall_sub_req_ctx, struct subscr_request); + if (!request) { + if (cbfn) + cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM, + NULL, NULL, param); + return; + } + + memset(request, 0, sizeof(*request)); + request->subscr = subscr; + request->channel_type = type; + request->cbfn = cbfn; + request->param = param; + + /* + * FIXME: We might be able to assign more than one + * channel, e.g. voice and SMS submit at the same + * time. + */ + if (!subscr->in_callback && llist_empty(&subscr->requests)) { + /* add to the list, send a request */ + llist_add_tail(&request->entry, &subscr->requests); + subscr_send_paging_request(subscr); + } else { + /* this will be picked up later, from subscr_put_channel */ + llist_add_tail(&request->entry, &subscr->requests); + } +} + +void subscr_put_channel(struct gsm_lchan *lchan) +{ + /* + * FIXME: Continue with other requests now... by checking + * the gsm_subscriber inside the gsm_lchan. Drop the ref count + * of the lchan after having asked the next requestee to handle + * the channel. + */ + /* + * FIXME: is the lchan is of a different type we could still + * issue an immediate assignment for another channel and then + * close this one. + */ + /* + * Currently we will drop the last ref of the lchan which + * will result in a channel release on RSL and we will start + * the paging. This should work most of the time as the MS + * will listen to the paging requests before we timeout + */ + + put_lchan(lchan); + + if (lchan->subscr && !llist_empty(&lchan->subscr->requests)) + subscr_send_paging_request(lchan->subscr); +} + diff --git a/openbsc/src/handover_decision.c b/openbsc/src/handover_decision.c new file mode 100644 index 000000000..efafca6e2 --- /dev/null +++ b/openbsc/src/handover_decision.c @@ -0,0 +1,298 @@ +/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This + * only implements the handover algorithm/decision, but not execution + * of it */ + +/* (C) 2009 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 <stdlib.h> +#include <errno.h> + +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/meas_rep.h> +#include <openbsc/signal.h> +#include <osmocore/talloc.h> +#include <openbsc/handover.h> +#include <osmocore/gsm_utils.h> + +/* issue handover to a cell identified by ARFCN and BSIC */ +static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, + u_int16_t arfcn, u_int8_t bsic) +{ + struct gsm_bts *new_bts; + + /* resolve the gsm_bts structure for the best neighbor */ + new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); + if (!new_bts) { + LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " + "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); + return -EINVAL; + } + + /* and actually try to handover to that cell */ + return bsc_handover_start(lchan, new_bts); +} + +/* did we get a RXLEV for a given cell in the given report? */ +static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, + u_int16_t arfcn, u_int8_t bsic) +{ + int i; + + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + + /* search for matching report */ + if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) + continue; + + mrc->flags |= MRC_F_PROCESSED; + return mrc->rxlev; + } + return -ENODEV; +} + +/* obtain averaged rxlev for given neighbor */ +static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) +{ + unsigned int i, idx; + int avg = 0; + + idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), + nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), + window); + + for (i = 0; i < window; i++) { + int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); + + avg += nmp->rxlev[j]; + } + + return avg / window; +} + +/* find empty or evict bad neighbor */ +static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) +{ + int j, worst = 999999; + struct neigh_meas_proc *nmp_worst; + + /* first try to find an empty/unused slot */ + for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; + if (!nmp->arfcn) + return nmp; + } + + /* no empty slot found. evict worst neighbor from list */ + for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; + int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); + if (avg < worst) { + worst = avg; + nmp_worst = nmp; + } + } + + return nmp_worst; +} + +/* process neighbor cell measurement reports */ +static void process_meas_neigh(struct gsm_meas_rep *mr) +{ + int i, j, idx; + + /* for each reported cell, try to update global state */ + for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; + unsigned int idx; + int rxlev; + + /* skip unused entries */ + if (!nmp->arfcn) + continue; + + rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + if (rxlev >= 0) { + nmp->rxlev[idx] = rxlev; + nmp->last_seen_nr = mr->nr; + } else + nmp->rxlev[idx] = 0; + nmp->rxlev_cnt++; + } + + /* iterate over list of reported cells, check if we did not + * process all of them */ + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + struct neigh_meas_proc *nmp; + + if (mrc->flags & MRC_F_PROCESSED) + continue; + + nmp = find_evict_neigh(mr->lchan); + + nmp->arfcn = mrc->arfcn; + nmp->bsic = mrc->bsic; + + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + nmp->rxlev[idx] = mrc->rxlev; + nmp->rxlev_cnt++; + nmp->last_seen_nr = mr->nr; + + mrc->flags |= MRC_F_PROCESSED; + } +} + +/* attempt to do a handover */ +static int attempt_handover(struct gsm_meas_rep *mr) +{ + struct gsm_network *net = mr->lchan->ts->trx->bts->network; + struct neigh_meas_proc *best_cell = NULL; + unsigned int best_better_db = 0; + int i, rc; + + /* find the best cell in this report that is at least RXLEV_HYST + * better than the current serving cell */ + + for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; + int avg, better; + + /* skip empty slots */ + if (nmp->arfcn == 0) + continue; + + /* caculate average rxlev for this cell over the window */ + avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); + + /* check if hysteresis is fulfilled */ + if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) + continue; + + better = avg - mr->dl.full.rx_lev; + if (better > best_better_db) { + best_cell = nmp; + best_better_db = better; + } + } + + if (!best_cell) + return 0; + + LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", + gsm_ts_name(mr->lchan->ts), best_cell->arfcn); + if (!net->handover.active) { + LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); + return 0; + } + + rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); + switch (rc) { + case 0: + LOGPC(DHO, LOGL_INFO, "Starting handover\n"); + break; + case -ENOSPC: + LOGPC(DHO, LOGL_INFO, "No channel available\n"); + break; + case -EBUSY: + LOGPC(DHO, LOGL_INFO, "Handover already active\n"); + break; + default: + LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); + } + return rc; +} + +/* process an already parsed measurement report and decide if we want to + * attempt a handover */ +static int process_meas_rep(struct gsm_meas_rep *mr) +{ + struct gsm_network *net = mr->lchan->ts->trx->bts->network; + int av_rxlev; + + /* we currently only do handover for TCH channels */ + switch (mr->lchan->type) { + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + break; + default: + return 0; + } + + /* parse actual neighbor cell info */ + if (mr->num_cell > 0 && mr->num_cell < 7) + process_meas_neigh(mr); + + av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, + net->handover.win_rxlev_avg); + + /* Interference HO */ + if (rxlev2dbm(av_rxlev) > -85 && + meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Bad Quality */ + if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Low Level */ + if (rxlev2dbm(av_rxlev) <= -110) + return attempt_handover(mr); + + /* Distance */ + if (mr->ms_l1.ta > net->handover.max_distance) + return attempt_handover(mr); + + /* Power Budget AKA Better Cell */ + if ((mr->nr % net->handover.pwr_interval) == 0) + return attempt_handover(mr); + + return 0; + +} + +static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_meas_rep *mr; + + if (subsys != SS_LCHAN) + return 0; + + switch (signal) { + case S_LCHAN_MEAS_REP: + mr = signal_data; + process_meas_rep(mr); + break; + } + + return 0; +} + +void on_dso_load_ho_dec(void) +{ + register_signal_handler(SS_LCHAN, ho_dec_sig_cb, NULL); +} diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c new file mode 100644 index 000000000..bd4c563f0 --- /dev/null +++ b/openbsc/src/handover_logic.c @@ -0,0 +1,376 @@ +/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not + * actually implement the handover algorithm/decision, but executes a + * handover decision */ + +/* (C) 2009 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <netinet/in.h> + +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/signal.h> +#include <osmocore/talloc.h> +#include <openbsc/transaction.h> +#include <openbsc/rtp_proxy.h> + +struct bsc_handover { + struct llist_head list; + + struct gsm_lchan *old_lchan; + struct gsm_lchan *new_lchan; + + struct timer_list T3103; + + u_int8_t ho_ref; +}; + +static LLIST_HEAD(bsc_handovers); + +static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + llist_for_each_entry(ho, &bsc_handovers, list) { + if (ho->new_lchan == new_lchan) + return ho; + } + + return NULL; +} + +static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) +{ + struct bsc_handover *ho; + + llist_for_each_entry(ho, &bsc_handovers, list) { + if (ho->old_lchan == old_lchan) + return ho; + } + + return NULL; +} + +/* Hand over the specified logical channel to the specified new BTS. + * This is the main entry point for the actual handover algorithm, + * after it has decided it wants to initiate HO to a specific BTS */ +int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) +{ + struct gsm_lchan *new_lchan; + struct bsc_handover *ho; + static u_int8_t ho_ref; + int rc; + + /* don't attempt multiple handovers for the same lchan at + * the same time */ + if (bsc_ho_by_old_lchan(old_lchan)) + return -EBUSY; + + DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", + old_lchan->ts->trx->bts->nr, bts->nr); + + counter_inc(bts->network->stats.handover.attempted); + + new_lchan = lchan_alloc(bts, old_lchan->type); + if (!new_lchan) { + LOGP(DHO, LOGL_NOTICE, "No free channel\n"); + counter_inc(bts->network->stats.handover.no_channel); + return -ENOSPC; + } + + ho = talloc_zero(NULL, struct bsc_handover); + if (!ho) { + LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); + lchan_free(new_lchan); + return -ENOMEM; + } + ho->old_lchan = old_lchan; + ho->new_lchan = new_lchan; + ho->ho_ref = ho_ref++; + + /* copy some parameters from old lchan */ + memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); + new_lchan->ms_power = old_lchan->ms_power; + new_lchan->bs_power = old_lchan->bs_power; + new_lchan->rsl_cmode = old_lchan->rsl_cmode; + new_lchan->tch_mode = old_lchan->tch_mode; + new_lchan->subscr = subscr_get(old_lchan->subscr); + + /* FIXME: do we have a better idea of the timing advance? */ + rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0, + ho->ho_ref); + if (rc < 0) { + LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); + talloc_free(ho); + lchan_free(new_lchan); + return rc; + } + + llist_add(&ho->list, &bsc_handovers); + /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ + + return 0; +} + +/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */ +static void ho_T3103_cb(void *_ho) +{ + struct bsc_handover *ho = _ho; + struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; + + DEBUGP(DHO, "HO T3103 expired\n"); + counter_inc(net->stats.handover.timeout); + + lchan_free(ho->new_lchan); + llist_del(&ho->list); + talloc_free(ho); +} + +/* RSL has acknowledged activation of the new lchan */ +static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + int rc; + + /* we need to check if this channel activation is related to + * a handover at all (and if, which particular handover) */ + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) + return -ENODEV; + + DEBUGP(DHO, "handover activate ack, send HO Command\n"); + + /* we can now send the 04.08 HANDOVER COMMAND to the MS + * using the old lchan */ + + rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); + + /* start T3103. We can continue either with T3103 expiration, + * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ + ho->T3103.cb = ho_T3103_cb; + ho->T3103.data = ho; + bsc_schedule_timer(&ho->T3103, 10, 0); + + /* create a RTP connection */ + if (is_ipaccess_bts(new_lchan->ts->trx->bts)) + rsl_ipacc_crcx(new_lchan); + + return 0; +} + +/* RSL has not acknowledged activation of the new lchan */ +static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + llist_del(&ho->list); + talloc_free(ho); + + /* FIXME: maybe we should try to allocate a new LCHAN here? */ + + return 0; +} + +/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ +static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) +{ + struct gsm_network *net = new_lchan->ts->trx->bts->network; + struct bsc_handover *ho; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN " + "%u->%u\n", subscr_name(ho->old_lchan->subscr), + ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr, + ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn); + + counter_inc(net->stats.handover.completed); + + bsc_del_timer(&ho->T3103); + + /* update lchan pointer of transaction */ + trans_lchan_change(ho->old_lchan, new_lchan); + + ho->old_lchan->state = LCHAN_S_INACTIVE; + lchan_auto_release(ho->old_lchan); + + /* do something to re-route the actual speech frames ! */ + + llist_del(&ho->list); + talloc_free(ho); + + return 0; +} + +/* GSM 04.08 HANDOVER FAIL has been received */ +static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) +{ + struct gsm_network *net = old_lchan->ts->trx->bts->network; + struct bsc_handover *ho; + + ho = bsc_ho_by_old_lchan(old_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + counter_inc(net->stats.handover.failed); + + bsc_del_timer(&ho->T3103); + llist_del(&ho->list); + put_lchan(ho->new_lchan); + talloc_free(ho); + + return 0; +} + +/* GSM 08.58 HANDOVER DETECT has been received */ +static int ho_rsl_detect(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + /* FIXME: do we actually want to do something here ? */ + + return 0; +} + +static int ho_ipac_crcx_ack(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + struct rtp_socket *old_rs, *new_rs, *other_rs; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + /* it is perfectly normal, we have CRCX even in non-HO cases */ + return 0; + } + + if (ipacc_rtp_direct) { + LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n"); + return 0; + } + + /* RTP Proxy mode */ + new_rs = new_lchan->abis_ip.rtp_socket; + old_rs = ho->old_lchan->abis_ip.rtp_socket; + + if (!new_rs) { + LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n"); + return -EIO; + } + + rsl_ipacc_mdcx_to_rtpsock(new_lchan); + + if (!old_rs) { + LOGP(DHO, LOGL_ERROR, "no RTP socekt for old_lchan\n"); + return -EIO; + } + + /* copy rx_action and reference to other sock */ + new_rs->rx_action = old_rs->rx_action; + new_rs->tx_action = old_rs->tx_action; + new_rs->transmit = old_rs->transmit; + + switch (ho->old_lchan->abis_ip.rtp_socket->rx_action) { + case RTP_PROXY: + other_rs = old_rs->proxy.other_sock; + rtp_socket_proxy(new_rs, other_rs); + /* delete reference to other end socket to prevent + * rtp_socket_free() from removing the inverse reference */ + old_rs->proxy.other_sock = NULL; + break; + case RTP_RECV_UPSTREAM: + new_rs->receive = old_rs->receive; + break; + case RTP_NONE: + break; + } + + return 0; +} + +static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_lchan *lchan; + + switch (subsys) { + case SS_LCHAN: + lchan = signal_data; + switch (signal) { + case S_LCHAN_ACTIVATE_ACK: + return ho_chan_activ_ack(lchan); + case S_LCHAN_ACTIVATE_NACK: + return ho_chan_activ_nack(lchan); + case S_LCHAN_HANDOVER_DETECT: + return ho_rsl_detect(lchan); + case S_LCHAN_HANDOVER_COMPL: + return ho_gsm48_ho_compl(lchan); + case S_LCHAN_HANDOVER_FAIL: + return ho_gsm48_ho_fail(lchan); + } + break; + case SS_ABISIP: + lchan = signal_data; + switch (signal) { + case S_ABISIP_CRCX_ACK: + return ho_ipac_crcx_ack(lchan); + break; + } + break; + default: + break; + } + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_ho_logic(void) +{ + register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL); + register_signal_handler(SS_ABISIP, ho_logic_sig_cb, NULL); +} diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c new file mode 100644 index 000000000..943a5e88d --- /dev/null +++ b/openbsc/src/input/ipaccess.c @@ -0,0 +1,701 @@ +/* OpenBSC Abis input driver for ip.access */ + +/* (C) 2009 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> + +#include <osmocore/select.h> +#include <osmocore/tlv.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/abis_nm.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/subchan_demux.h> +#include <openbsc/e1_input.h> +#include <openbsc/ipaccess.h> +#include <osmocore/talloc.h> + +/* data structure for one E1 interface with A-bis */ +struct ia_e1_handle { + struct bsc_fd listen_fd; + struct bsc_fd rsl_listen_fd; + struct gsm_network *gsmnet; +}; + +static struct ia_e1_handle *e1h; + + +#define TS1_ALLOC_SIZE 300 + +static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; +static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; +static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET, + 0x01, IPAC_IDTAG_UNIT, + 0x01, IPAC_IDTAG_MACADDR, + 0x01, IPAC_IDTAG_LOCATION1, + 0x01, IPAC_IDTAG_LOCATION2, + 0x01, IPAC_IDTAG_EQUIPVERS, + 0x01, IPAC_IDTAG_SWVERSION, + 0x01, IPAC_IDTAG_UNITNAME, + 0x01, IPAC_IDTAG_SERNR, + }; + +static const char *idtag_names[] = { + [IPAC_IDTAG_SERNR] = "Serial_Number", + [IPAC_IDTAG_UNITNAME] = "Unit_Name", + [IPAC_IDTAG_LOCATION1] = "Location_1", + [IPAC_IDTAG_LOCATION2] = "Location_2", + [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", + [IPAC_IDTAG_SWVERSION] = "Software_Version", + [IPAC_IDTAG_IPADDR] = "IP_Address", + [IPAC_IDTAG_MACADDR] = "MAC_Address", + [IPAC_IDTAG_UNIT] = "Unit_ID", +}; + +static const char *ipac_idtag_name(int tag) +{ + if (tag >= ARRAY_SIZE(idtag_names)) + return "unknown"; + + return idtag_names[tag]; +} + +int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) +{ + u_int8_t t_len; + u_int8_t t_tag; + u_int8_t *cur = buf; + + while (cur < buf + len) { + t_len = *cur++; + t_tag = *cur++; + + DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur); + + dec->lv[t_tag].len = t_len; + dec->lv[t_tag].val = cur; + + cur += t_len; + } + return 0; +} + +struct gsm_bts *find_bts_by_unitid(struct gsm_network *net, + u_int16_t site_id, u_int16_t bts_id) +{ + struct gsm_bts *bts; + + llist_for_each_entry(bts, &net->bts_list, list) { + + if (!is_ipaccess_bts(bts)) + continue; + + if (bts->ip_access.site_id == site_id && + bts->ip_access.bts_id == bts_id) + return bts; + } + + return NULL; +} + +static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id, + u_int16_t *trx_id) +{ + unsigned long ul; + char *endptr; + const char *nptr; + + nptr = str; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (site_id) + *site_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (bts_id) + *bts_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (trx_id) + *trx_id = ul & 0xffff; + + return 0; +} + +/* send the id ack */ +int ipaccess_send_id_ack(int fd) +{ + return write(fd, id_ack, sizeof(id_ack)); +} + +int ipaccess_send_id_req(int fd) +{ + return write(fd, id_req, sizeof(id_req)); +} + +/* base handling of the ip.access protocol */ +int ipaccess_rcvmsg_base(struct msgb *msg, + struct bsc_fd *bfd) +{ + u_int8_t msg_type = *(msg->l2h); + int ret = 0; + + switch (msg_type) { + case IPAC_MSGT_PING: + ret = write(bfd->fd, pong, sizeof(pong)); + break; + case IPAC_MSGT_PONG: + DEBUGP(DMI, "PONG!\n"); + break; + case IPAC_MSGT_ID_ACK: + DEBUGP(DMI, "ID_ACK? -> ACK!\n"); + ret = ipaccess_send_id_ack(bfd->fd); + break; + } + return 0; +} + +static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, + struct bsc_fd *bfd) +{ + struct tlv_parsed tlvp; + u_int8_t msg_type = *(msg->l2h); + u_int16_t site_id = 0, bts_id = 0, trx_id = 0; + struct gsm_bts *bts; + + /* handle base messages */ + ipaccess_rcvmsg_base(msg, bfd); + + switch (msg_type) { + case IPAC_MSGT_ID_RESP: + DEBUGP(DMI, "ID_RESP "); + /* parse tags, search for Unit ID */ + ipaccess_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2, + msgb_l2len(msg)-2); + DEBUGP(DMI, "\n"); + + if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) + break; + + /* lookup BTS, create sign_link, ... */ + parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), + &site_id, &bts_id, &trx_id); + bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id); + if (!bts) { + LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for " + " %u/%u/%u, disconnecting\n", site_id, bts_id, + trx_id); + return -EIO; + } + DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id); + if (bfd->priv_nr == 1) { + bts->oml_link = e1inp_sign_link_create(&line->ts[1-1], + E1INP_SIGN_OML, bts->c0, + bts->oml_tei, 0); + } else if (bfd->priv_nr == 2) { + struct e1inp_ts *e1i_ts; + struct bsc_fd *newbfd; + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id); + + bfd->data = line = bts->oml_link->ts->line; + e1i_ts = &line->ts[2+trx_id - 1]; + newbfd = &e1i_ts->driver.ipaccess.fd; + e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN); + + trx->rsl_link = e1inp_sign_link_create(e1i_ts, + E1INP_SIGN_RSL, trx, + trx->rsl_tei, 0); + /* get rid of our old temporary bfd */ + memcpy(newbfd, bfd, sizeof(*newbfd)); + newbfd->priv_nr = 2+trx_id; + bsc_unregister_fd(bfd); + bsc_register_fd(newbfd); + talloc_free(bfd); + } + break; + } + return 0; +} + +#define OML_UP 0x0001 +#define RSL_UP 0x0002 + +/* + * read one ipa message from the socket + * return NULL in case of error + */ +struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "Abis/IP"); + struct ipaccess_head *hh; + int len, ret = 0; + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + /* first read our 3-byte header */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, 3, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msgb_put(msg, ret); + + /* then read te length as specified in header */ + msg->l2h = msg->data + sizeof(*hh); + len = ntohs(hh->len); + ret = recv(bfd->fd, msg->l2h, len, 0); + if (ret < len) { + LOGP(DINP, LOGL_ERROR, "short read!\n"); + msgb_free(msg); + *error = -EIO; + return NULL; + } + msgb_put(msg, ret); + + return msg; +} + +static int handle_ts1_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *link; + struct msgb *msg; + struct ipaccess_head *hh; + int ret = 0, error; + + msg = ipaccess_read_msg(bfd, &error); + if (!msg) { + if (error == 0) { + link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0); + if (link) { + link->trx->bts->ip_access.flags = 0; + LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n", + link->trx->bts->nr); + } else + LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n"); + e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL); + e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + } + return error; + } + + DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), msgb_l2len(msg))); + + hh = (struct ipaccess_head *) msg->data; + if (hh->proto == IPAC_PROTO_IPACCESS) { + ret = ipaccess_rcvmsg(line, msg, bfd); + if (ret < 0) { + e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL); + e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + } + msgb_free(msg); + return ret; + } + /* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg() + * might have free'd it !!! */ + + link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0); + if (!link) { + LOGP(DINP, LOGL_ERROR, "no matching signalling link for " + "hh->proto=0x%02x\n", hh->proto); + msgb_free(msg); + return -EIO; + } + msg->trx = link->trx; + + switch (link->type) { + case E1INP_SIGN_RSL: + if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) { + e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi); + msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr); + } + ret = abis_rsl_rcvmsg(msg); + break; + case E1INP_SIGN_OML: + if (!(msg->trx->bts->ip_access.flags & OML_UP)) { + e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi); + msg->trx->bts->ip_access.flags |= OML_UP; + } + ret = abis_nm_rcvmsg(msg); + break; + default: + LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto); + msgb_free(msg); + break; + } + return ret; +} + +void ipaccess_prepend_header(struct msgb *msg, int proto) +{ + struct ipaccess_head *hh; + + /* prepend the ip.access header */ + hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh)); + hh->len = htons(msg->len - sizeof(*hh)); + hh->proto = proto; +} + +static int ts_want_write(struct e1inp_ts *e1i_ts) +{ + e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE; + + return 0; +} + +static void timeout_ts1_write(void *data) +{ + struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; + + /* trigger write of ts1, due to tx delay timer */ + ts_want_write(e1i_ts); +} + +static int handle_ts1_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *sign_link; + struct msgb *msg; + u_int8_t proto; + int ret; + + bfd->when &= ~BSC_FD_WRITE; + + /* get the next msg for this timeslot */ + msg = e1inp_tx_ts(e1i_ts, &sign_link); + if (!msg) { + /* no message after tx delay timer */ + return 0; + } + + switch (sign_link->type) { + case E1INP_SIGN_OML: + proto = IPAC_PROTO_OML; + break; + case E1INP_SIGN_RSL: + proto = IPAC_PROTO_RSL; + break; + default: + msgb_free(msg); + bfd->when |= BSC_FD_WRITE; /* come back for more msg */ + return -EINVAL; + } + + msg->l2h = msg->data; + ipaccess_prepend_header(msg, sign_link->tei); + + DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(msg->l2h, msgb_l2len(msg))); + + ret = send(bfd->fd, msg->data, msg->len, 0); + msgb_free(msg); + + /* set tx delay timer for next event */ + e1i_ts->sign.tx_timer.cb = timeout_ts1_write; + e1i_ts->sign.tx_timer.data = e1i_ts; + bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100000); + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + unsigned int idx = ts_nr-1; + struct e1inp_ts *e1i_ts; + int rc = 0; + + /* In case of early RSL we might not yet have a line */ + + if (line) + e1i_ts = &line->ts[idx]; + + if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) { + if (what & BSC_FD_READ) + rc = handle_ts1_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_ts1_write(bfd); + } else + LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type); + + return rc; +} + + +struct e1inp_driver ipaccess_driver = { + .name = "ip.access", + .want_write = ts_want_write, +}; + +/* callback of the OML listening filedescriptor */ +static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + int ret; + int idx = 0; + struct e1inp_line *line; + struct e1inp_ts *e1i_ts; + struct bsc_fd *bfd; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + if (!(what & BSC_FD_READ)) + return 0; + + ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (ret < 0) { + perror("accept"); + return ret; + } + LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n", + inet_ntoa(sa.sin_addr)); + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) { + close(ret); + return -ENOMEM; + } + line->driver = &ipaccess_driver; + //line->driver_data = e1h; + /* create virrtual E1 timeslots for signalling */ + e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); + + e1i_ts = &line->ts[idx]; + + bfd = &e1i_ts->driver.ipaccess.fd; + bfd->fd = ret; + bfd->data = line; + bfd->priv_nr = 1; + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(line); + return ret; + } + + /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ + ret = ipaccess_send_id_req(bfd->fd); + + return ret; + //return e1inp_line_register(line); +} + +static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + struct bsc_fd *bfd; + int ret; + + if (!(what & BSC_FD_READ)) + return 0; + + bfd = talloc_zero(tall_bsc_ctx, struct bsc_fd); + if (!bfd) + return -ENOMEM; + + /* Some BTS has connected to us, but we don't know yet which line + * (as created by the OML link) to associate it with. Thus, we + * allocate a temporary bfd until we have received ID from BTS */ + + bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (bfd->fd < 0) { + perror("accept"); + return bfd->fd; + } + LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr)); + bfd->priv_nr = 2; + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(bfd); + return ret; + } + /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ + ret = write(bfd->fd, id_req, sizeof(id_req)); + + return 0; +} + +static int make_sock(struct bsc_fd *bfd, u_int16_t port, + int (*cb)(struct bsc_fd *fd, unsigned int what)) +{ + struct sockaddr_in addr; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + //bfd->data = line; + + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); + return -EIO; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n", + strerror(errno)); + close(bfd->fd); + return -EIO; + } + + ret = listen(bfd->fd, 1); + if (ret < 0) { + perror("listen"); + close(bfd->fd); + return ret; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register_listen_fd"); + close(bfd->fd); + return ret; + } + return 0; +} + +/* Actively connect to a BTS. Currently used by ipaccess-config.c */ +int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) +{ + struct e1inp_ts *e1i_ts = &line->ts[0]; + struct bsc_fd *bfd = &e1i_ts->driver.ipaccess.fd; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ | BSC_FD_WRITE; + bfd->data = line; + bfd->priv_nr = 1; + + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); + return -EIO; + } + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not connect socket\n"); + close(bfd->fd); + return ret; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + close(bfd->fd); + return ret; + } + + line->driver = &ipaccess_driver; + + return ret; + //return e1inp_line_register(line); +} + +int ipaccess_setup(struct gsm_network *gsmnet) +{ + int ret; + + /* register the driver with the core */ + /* FIXME: do this in the plugin initializer function */ + ret = e1inp_driver_register(&ipaccess_driver); + if (ret) + return ret; + + e1h = talloc_zero(tall_bsc_ctx, struct ia_e1_handle); + if (!e1h) + return -ENOMEM; + + e1h->gsmnet = gsmnet; + + /* Listen for OML connections */ + ret = make_sock(&e1h->listen_fd, IPA_TCP_PORT_OML, listen_fd_cb); + if (ret < 0) + return ret; + + /* Listen for RSL connections */ + ret = make_sock(&e1h->rsl_listen_fd, IPA_TCP_PORT_RSL, rsl_listen_fd_cb); + + return ret; +} diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c new file mode 100644 index 000000000..56930d498 --- /dev/null +++ b/openbsc/src/input/misdn.c @@ -0,0 +1,536 @@ +/* OpenBSC Abis input driver for mISDNuser */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <mISDNif.h> + +//#define AF_COMPATIBILITY_FUNC +//#include <compat_af_isdn.h> +#ifndef AF_ISDN +#define AF_ISDN 34 +#define PF_ISDN AF_ISDN +#endif + +#include <osmocore/select.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/abis_nm.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/subchan_demux.h> +#include <openbsc/e1_input.h> +#include <osmocore/talloc.h> + +#define TS1_ALLOC_SIZE 300 + +struct prim_name { + unsigned int prim; + const char *name; +}; + +const struct prim_name prim_names[] = { + { PH_CONTROL_IND, "PH_CONTROL_IND" }, + { PH_DATA_IND, "PH_DATA_IND" }, + { PH_DATA_CNF, "PH_DATA_CNF" }, + { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" }, + { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" }, + { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" }, + { DL_RELEASE_IND, "DL_RELEASE_IND" }, + { DL_RELEASE_CNF, "DL_RELEASE_CNF" }, + { DL_DATA_IND, "DL_DATA_IND" }, + { DL_UNITDATA_IND, "DL_UNITDATA_IND" }, + { DL_INFORMATION_IND, "DL_INFORMATION_IND" }, + { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" }, + { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" }, +}; + +const char *get_prim_name(unsigned int prim) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(prim_names); i++) { + if (prim_names[i].prim == prim) + return prim_names[i].name; + } + + return "UNKNOWN"; +} + +static int handle_ts1_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *link; + struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "mISDN TS1"); + struct sockaddr_mISDN l2addr; + struct mISDNhead *hh; + socklen_t alen; + int ret; + + if (!msg) + return -ENOMEM; + + hh = (struct mISDNhead *) msg->data; + + alen = sizeof(l2addr); + ret = recvfrom(bfd->fd, msg->data, 300, 0, + (struct sockaddr *) &l2addr, &alen); + if (ret < 0) { + fprintf(stderr, "recvfrom error %s\n", strerror(errno)); + return ret; + } + + if (alen != sizeof(l2addr)) { + fprintf(stderr, "%s error len\n", __func__); + return -EINVAL; + } + + msgb_put(msg, ret); + + DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n", + alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei); + + DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n", + ret, hh->prim, hh->id, get_prim_name(hh->prim)); + + switch (hh->prim) { + case DL_INFORMATION_IND: + /* mISDN tells us which channel number is allocated for this + * tuple of (SAPI, TEI). */ + DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); + if (!link) { + DEBUGPC(DMI, "mISDN message for unknown sign_link\n"); + msgb_free(msg); + return -EINVAL; + } + /* save the channel number in the driver private struct */ + link->driver.misdn.channel = l2addr.channel; + break; + case DL_ESTABLISH_IND: + DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + ret = e1inp_event(e1i_ts, EVT_E1_TEI_UP, l2addr.tei, l2addr.sapi); + break; + case DL_RELEASE_IND: + DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + ret = e1inp_event(e1i_ts, EVT_E1_TEI_DN, l2addr.tei, l2addr.sapi); + break; + case DL_DATA_IND: + case DL_UNITDATA_IND: + msg->l2h = msg->data + MISDN_HEADER_LEN; + DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); + ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi); + break; + case PH_ACTIVATE_IND: + DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + break; + case PH_DEACTIVATE_IND: + DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + break; + default: + break; + } + return ret; +} + +static int ts_want_write(struct e1inp_ts *e1i_ts) +{ + /* We never include the mISDN B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + if (e1i_ts->type == E1INP_TS_TYPE_TRAU) + return 0; + + e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE; + + return 0; +} + +static void timeout_ts1_write(void *data) +{ + struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; + + /* trigger write of ts1, due to tx delay timer */ + ts_want_write(e1i_ts); +} + +static int handle_ts1_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *sign_link; + struct sockaddr_mISDN sa; + struct msgb *msg; + struct mISDNhead *hh; + u_int8_t *l2_data; + int ret; + + bfd->when &= ~BSC_FD_WRITE; + + /* get the next msg for this timeslot */ + msg = e1inp_tx_ts(e1i_ts, &sign_link); + if (!msg) { + /* no message after tx delay timer */ + return 0; + } + + l2_data = msg->data; + + /* prepend the mISDNhead */ + hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh)); + hh->prim = DL_DATA_REQ; + + DEBUGP(DMI, "TX TEI(%d) SAPI(%d): %s\n", sign_link->tei, + sign_link->sapi, hexdump(l2_data, msg->len - MISDN_HEADER_LEN)); + + /* construct the sockaddr */ + sa.family = AF_ISDN; + sa.sapi = sign_link->sapi; + sa.dev = sign_link->tei; + sa.channel = sign_link->driver.misdn.channel; + + ret = sendto(bfd->fd, msg->data, msg->len, 0, + (struct sockaddr *)&sa, sizeof(sa)); + if (ret < 0) + fprintf(stderr, "%s sendto failed %d\n", __func__, ret); + msgb_free(msg); + + /* set tx delay timer for next event */ + e1i_ts->sign.tx_timer.cb = timeout_ts1_write; + e1i_ts->sign.tx_timer.data = e1i_ts; + bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000); + + return ret; +} + +#define BCHAN_TX_GRAN 160 +/* write to a B channel TS */ +static int handle_tsX_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct mISDNhead *hh; + u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)]; + struct subch_mux *mx = &e1i_ts->trau.mux; + int ret; + + hh = (struct mISDNhead *) tx_buf; + hh->prim = PH_DATA_REQ; + + subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN); + + DEBUGP(DMIB, "BCHAN TX: %s\n", + hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN)); + + ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0); + if (ret < sizeof(*hh) + BCHAN_TX_GRAN) + DEBUGP(DMIB, "send returns %d instead of %lu\n", ret, + sizeof(*hh) + BCHAN_TX_GRAN); + + return ret; +} + +#define TSX_ALLOC_SIZE 4096 +/* FIXME: read from a B channel TS */ +static int handle_tsX_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE, "mISDN TSx"); + struct mISDNhead *hh; + int ret; + + if (!msg) + return -ENOMEM; + + hh = (struct mISDNhead *) msg->data; + + ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0); + if (ret < 0) { + fprintf(stderr, "recvfrom error %s\n", strerror(errno)); + return ret; + } + + msgb_put(msg, ret); + + if (hh->prim != PH_CONTROL_IND) + DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n", + ret, hh->prim, hh->id, get_prim_name(hh->prim)); + + switch (hh->prim) { + case PH_DATA_IND: + msg->l2h = msg->data + MISDN_HEADER_LEN; + DEBUGP(DMIB, "BCHAN RX: %s\n", + hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); + ret = e1inp_rx_ts(e1i_ts, msg, 0, 0); + break; + case PH_ACTIVATE_IND: + case PH_DATA_CNF: + /* physical layer indicates that data has been sent, + * we thus can send some more data */ + ret = handle_tsX_write(bfd); + default: + break; + } + /* FIXME: why do we free signalling msgs in the caller, and trau not? */ + msgb_free(msg); + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + unsigned int idx = ts_nr-1; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + int rc = 0; + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_SIGN: + if (what & BSC_FD_READ) + rc = handle_ts1_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_ts1_write(bfd); + break; + case E1INP_TS_TYPE_TRAU: + if (what & BSC_FD_READ) + rc = handle_tsX_read(bfd); + /* We never include the mISDN B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + break; + default: + fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); + break; + } + + return rc; +} + +static int activate_bchan(struct e1inp_line *line, int ts, int act) +{ + struct mISDNhead hh; + int ret; + unsigned int idx = ts-1; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; + + fprintf(stdout, "activate bchan\n"); + if (act) + hh.prim = PH_ACTIVATE_REQ; + else + hh.prim = PH_DEACTIVATE_REQ; + + hh.id = MISDN_ID_ANY; + ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0); + if (ret < 0) { + fprintf(stdout, "could not send ACTIVATE_RQ %s\n", + strerror(errno)); + } + + return ret; +} + +struct e1inp_driver misdn_driver = { + .name = "mISDNuser", + .want_write = ts_want_write, +}; + +static int mi_e1_setup(struct e1inp_line *line, int release_l2) +{ + int ts, ret; + + /* TS0 is CRC4, don't need any fd for it */ + for (ts = 1; ts < NUM_E1_TS; ts++) { + unsigned int idx = ts-1; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; + struct sockaddr_mISDN addr; + + bfd->data = line; + bfd->priv_nr = ts; + bfd->cb = misdn_fd_cb; + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_NONE: + continue; + break; + case E1INP_TS_TYPE_SIGN: + bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT); + bfd->when = BSC_FD_READ; + break; + case E1INP_TS_TYPE_TRAU: + bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); + /* We never include the mISDN B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + bfd->when = BSC_FD_READ; + break; + } + + if (bfd->fd < 0) { + fprintf(stderr, "%s could not open socket %s\n", + __func__, strerror(errno)); + return bfd->fd; + } + + memset(&addr, 0, sizeof(addr)); + addr.family = AF_ISDN; + addr.dev = line->num; + switch (e1i_ts->type) { + case E1INP_TS_TYPE_SIGN: + addr.channel = 0; + /* SAPI not supported yet in kernel */ + //addr.sapi = e1inp_ts->sign.sapi; + addr.sapi = 0; + addr.tei = GROUP_TEI; + break; + case E1INP_TS_TYPE_TRAU: + addr.channel = ts; + break; + default: + DEBUGP(DMI, "unsupported E1 TS type: %u\n", + e1i_ts->type); + break; + } + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + fprintf(stderr, "could not bind l2 socket %s\n", + strerror(errno)); + return -EIO; + } + + if (e1i_ts->type == E1INP_TS_TYPE_SIGN) { + ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2); + if (ret < 0) { + fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno)); + return -EIO; + } + } + + /* FIXME: only activate B-Channels once we start to + * use them to conserve CPU power */ + if (e1i_ts->type == E1INP_TS_TYPE_TRAU) + activate_bchan(line, ts, 1); + + ret = bsc_register_fd(bfd); + if (ret < 0) { + fprintf(stderr, "could not register FD: %s\n", + strerror(ret)); + return ret; + } + } + + return 0; +} + +int mi_e1_line_update(struct e1inp_line *line) +{ + struct mISDN_devinfo devinfo; + int sk, ret, cnt; + + if (!line->driver) { + /* this must be the first update */ + line->driver = &misdn_driver; + } else { + /* this is a subsequent update */ + /* FIXME: first close all sockets */ + fprintf(stderr, "incremental line updates not supported yet\n"); + return 0; + } + + if (line->driver != &misdn_driver) + return -EINVAL; + + /* open the ISDN card device */ + sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (sk < 0) { + fprintf(stderr, "%s could not open socket %s\n", + __func__, strerror(errno)); + return sk; + } + + ret = ioctl(sk, IMGETCOUNT, &cnt); + if (ret) { + fprintf(stderr, "%s error getting interf count: %s\n", + __func__, strerror(errno)); + close(sk); + return -ENODEV; + } + //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s"); + printf("%d device%s found\n", cnt, (cnt==1)?"":"s"); +#if 1 + devinfo.id = line->num; + ret = ioctl(sk, IMGETDEVINFO, &devinfo); + if (ret < 0) { + fprintf(stdout, "error getting info for device %d: %s\n", + line->num, strerror(errno)); + return -ENODEV; + } + fprintf(stdout, " id: %d\n", devinfo.id); + fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols); + fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols); + fprintf(stdout, " protocol: %d\n", devinfo.protocol); + fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan); + fprintf(stdout, " name: %s\n", devinfo.name); +#endif + + if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) { + fprintf(stderr, "error: card is not of type E1 (NT-mode)\n"); + return -EINVAL; + } + + ret = mi_e1_setup(line, 1); + if (ret) + return ret; + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_sms(void) +{ + /* register the driver with the core */ + e1inp_driver_register(&misdn_driver); +} diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c new file mode 100644 index 000000000..93fe44236 --- /dev/null +++ b/openbsc/src/ipaccess/ipaccess-config.c @@ -0,0 +1,729 @@ +/* ip.access nanoBTS configuration tool */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2009 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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + + +#include <osmocore/select.h> +#include <osmocore/timer.h> +#include <openbsc/ipaccess.h> +#include <openbsc/gsm_data.h> +#include <openbsc/e1_input.h> +#include <openbsc/abis_nm.h> +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <osmocore/talloc.h> + +static struct gsm_network *gsmnet; + +static int net_listen_testnr; +static int restart; +static char *prim_oml_ip; +static char *unit_id; +static u_int16_t nv_flags; +static u_int16_t nv_mask; +static char *software = NULL; +static int sw_load_state = 0; +static int oml_state = 0; +static int dump_files = 0; + +struct sw_load { + u_int8_t file_id[255]; + u_int8_t file_id_len; + + u_int8_t file_version[255]; + u_int8_t file_version_len; +}; + +static void *tall_ctx_config = NULL; +static struct sw_load *sw_load1 = NULL; +static struct sw_load *sw_load2 = NULL; + +/* +static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; +static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; +*/ + +/* + * Callback function for NACK on the OML NM + * + * Currently we send the config requests but don't check the + * result. The nanoBTS will send us a NACK when we did something the + * BTS didn't like. + */ +static int ipacc_msg_nack(u_int8_t mt) +{ + fprintf(stderr, "Failure to set attribute. This seems fatal\n"); + exit(-1); + return 0; +} + +static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts *bts) +{ + if (sw_load_state == 1) { + fprintf(stderr, "The new software is activaed.\n"); + + if (restart) { + abis_nm_ipaccess_restart(bts); + } else { + exit(0); + } + } else if (oml_state == 1) { + fprintf(stderr, "Set the primary OML IP.\n"); + if (restart) { + abis_nm_ipaccess_restart(bts); + } else { + exit(0); + } + } + + return 0; +} + +struct ipacc_ferr_elem { + int16_t freq_err; + u_int8_t freq_qual; + u_int8_t arfcn; +} __attribute__((packed)); + +struct ipacc_cusage_elem { + u_int16_t arfcn:10, + rxlev:6; +} __attribute__ ((packed)); + +static int test_rep(void *_msg) +{ + struct msgb *msg = _msg; + struct abis_om_fom_hdr *foh = msgb_l3(msg); + u_int16_t test_rep_len, ferr_list_len; + struct ipacc_ferr_elem *ife; + struct ipac_bcch_info binfo; + int i, rc; + + DEBUGP(DNM, "TEST REPORT: "); + + if (foh->data[0] != NM_ATT_TEST_NO || + foh->data[2] != NM_ATT_TEST_REPORT) + return -EINVAL; + + DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]); + /* data[2] == NM_ATT_TEST_REPORT */ + /* data[3..4]: test_rep_len */ + test_rep_len = ntohs(*(u_int16_t *) &foh->data[3]); + /* data[5]: ip.access test result */ + DEBUGPC(DNM, "test_res=%s\n", ipacc_testres_name(foh->data[5])); + + /* data[6]: ip.access nested IE. 3 == freq_err_list */ + switch (foh->data[6]) { + case NM_IPAC_EIE_FREQ_ERR_LIST: + /* data[7..8]: length of ferr_list */ + ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]); + + /* data[9...]: frequency error list elements */ + for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) { + ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i); + DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n", + ife->arfcn, ntohs(ife->freq_err)); + } + break; + case NM_IPAC_EIE_CHAN_USE_LIST: + /* data[7..8]: length of ferr_list */ + ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]); + + /* data[9...]: channel usage list elements */ + for (i = 0; i < ferr_list_len; i+= 2) { + u_int16_t *cu_ptr = (u_int16_t *)(foh->data + 9 + i); + u_int16_t cu = ntohs(*cu_ptr); + DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", + cu & 0x3ff, cu >> 10); + } + break; + case NM_IPAC_EIE_BCCH_INFO_TYPE: + break; + case NM_IPAC_EIE_BCCH_INFO: + rc = ipac_parse_bcch_info(&binfo, foh->data+6); + if (rc < 0) { + DEBUGP(DNM, "BCCH Info parsing failed\n"); + break; + } + DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d\n", + binfo.arfcn, binfo.rx_lev, binfo.rx_qual, + binfo.cgi.mcc, binfo.cgi.mnc, + binfo.cgi.lac, binfo.cgi.ci); + break; + default: + break; + } + + return 0; +} + +static int nm_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct ipacc_ack_signal_data *ipacc_data; + + switch (signal) { + case S_NM_IPACC_NACK: + ipacc_data = signal_data; + return ipacc_msg_nack(ipacc_data->msg_type); + case S_NM_IPACC_ACK: + ipacc_data = signal_data; + return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->bts); + case S_NM_TEST_REP: + return test_rep(signal_data); + case S_NM_IPACC_RESTART_ACK: + printf("The BTS has acked the restart. Exiting.\n"); + exit(0); + break; + case S_NM_IPACC_RESTART_NACK: + printf("The BTS has nacked the restart. Exiting.\n"); + exit(0); + break; + default: + break; + } + + return 0; +} + +/* callback function passed to the ABIS OML code */ +static int percent; +static int percent_old; +static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, + void *data, void *param) +{ + struct msgb *msg; + struct gsm_bts *bts; + + if (hook != GSM_HOOK_NM_SWLOAD) + return 0; + + bts = (struct gsm_bts *) data; + + switch (event) { + case NM_MT_LOAD_INIT_ACK: + fprintf(stdout, "Software Load Initiate ACK\n"); + break; + case NM_MT_LOAD_INIT_NACK: + fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); + exit(5); + break; + case NM_MT_LOAD_END_ACK: + fprintf(stderr, "LOAD END ACK..."); + /* now make it the default */ + sw_load_state = 1; + + msg = msgb_alloc(1024, "sw: nvattr"); + msg->l2h = msgb_put(msg, 3); + msg->l3h = &msg->l2h[3]; + + /* activate software */ + if (sw_load1) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len, + sw_load1->file_version); + } + + if (sw_load2) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len, + sw_load2->file_version); + } + + /* fill in the data */ + msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; + msg->l2h[1] = msgb_l3len(msg) >> 8; + msg->l2h[2] = msgb_l3len(msg) & 0xff; + printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); + abis_nm_ipaccess_set_nvattr(bts->c0, msg->l2h, msgb_l2len(msg)); + msgb_free(msg); + break; + case NM_MT_LOAD_END_NACK: + fprintf(stderr, "ERROR: Software Load End NACK\n"); + exit(3); + break; + case NM_MT_ACTIVATE_SW_NACK: + fprintf(stderr, "ERROR: Activate Software NACK\n"); + exit(4); + break; + case NM_MT_ACTIVATE_SW_ACK: + break; + case NM_MT_LOAD_SEG_ACK: + percent = abis_nm_software_load_status(bts); + if (percent > percent_old) + printf("Software Download Progress: %d%%\n", percent); + percent_old = percent; + break; + case NM_MT_LOAD_ABORT: + fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); + exit(6); + break; + } + return 0; +} + +static void bootstrap_om(struct gsm_bts *bts) +{ + int len; + static u_int8_t buf[1024]; + u_int8_t *cur = buf; + + printf("OML link established\n"); + + if (unit_id) { + len = strlen(unit_id); + if (len > sizeof(buf)-10) + return; + buf[0] = NM_ATT_IPACC_UNIT_ID; + buf[1] = (len+1) >> 8; + buf[2] = (len+1) & 0xff; + memcpy(buf+3, unit_id, len); + buf[3+len] = 0; + printf("setting Unit ID to '%s'\n", unit_id); + abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1); + } + if (prim_oml_ip) { + struct in_addr ia; + + if (!inet_aton(prim_oml_ip, &ia)) { + fprintf(stderr, "invalid IP address: %s\n", + prim_oml_ip); + return; + } + + /* 0x88 + IP + port */ + len = 1 + sizeof(ia) + 2; + + *cur++ = NM_ATT_IPACC_PRIM_OML_CFG_LIST; + *cur++ = (len) >> 8; + *cur++ = (len) & 0xff; + *cur++ = 0x88; + memcpy(cur, &ia, sizeof(ia)); + cur += sizeof(ia); + *cur++ = 0; + *cur++ = 0; + printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); + oml_state = 1; + abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len); + } + if (nv_mask) { + len = 4; + + *cur++ = NM_ATT_IPACC_NV_FLAGS; + *cur++ = (len) >> 8; + *cur++ = (len) & 0xff; + *cur++ = nv_flags & 0xff; + *cur++ = nv_mask & 0xff; + *cur++ = nv_flags >> 8; + *cur++ = nv_mask >> 8; + printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", + nv_flags, nv_mask); + abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len); + } + + if (restart && !prim_oml_ip && !software) { + printf("restarting BTS\n"); + abis_nm_ipaccess_restart(bts); + } + +} + +void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) +{ + switch (event) { + case EVT_E1_TEI_UP: + switch (type) { + case E1INP_SIGN_OML: + bootstrap_om(trx->bts); + break; + case E1INP_SIGN_RSL: + /* FIXME */ + break; + default: + break; + } + break; + case EVT_E1_TEI_DN: + fprintf(stderr, "Lost some E1 TEI link\n"); + /* FIXME: deal with TEI or L1 link loss */ + break; + default: + break; + } +} + +int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) +{ + if (evt == EVT_STATECHG_OPER && + obj_class == NM_OC_RADIO_CARRIER && + new_state->availability == 3) { + struct gsm_bts_trx *trx = obj; + + if (net_listen_testnr) { + u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 }; + abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff, + net_listen_testnr, 1, + phys_config, sizeof(phys_config)); + } else if (software) { + int rc; + printf("Attempting software upload with '%s'\n", software); + rc = abis_nm_software_load(trx->bts, software, 19, 0, swload_cbfn, trx->bts); + if (rc < 0) { + fprintf(stderr, "Failed to start software load\n"); + exit(-3); + } + } + } + return 0; +} + +static struct sw_load *create_swload(struct sdp_header *header) +{ + struct sw_load *load; + + load = talloc_zero(tall_ctx_config, struct sw_load); + + strncpy((char *)load->file_id, header->firmware_info.sw_part, 20); + load->file_id_len = strlen(header->firmware_info.sw_part) + 1; + + strncpy((char *)load->file_version, header->firmware_info.version, 20); + load->file_version_len = strlen(header->firmware_info.version) + 1; + + return load; +} + +static int find_sw_load_params(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return -1; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + return -1; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + if (close(fd) != 0) { + perror("Close failed.\n"); + return -1; + } + + /* try to find what we are looking for */ + llist_for_each_entry(header, entry, entry) { + if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { + sw_load1 = create_swload(header); + } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { + sw_load2 = create_swload(header); + } + } + + if (!sw_load1 || !sw_load2) { + fprintf(stderr, "Did not find data.\n"); + talloc_free(tall_firm_ctx); + return -1; + } + + talloc_free(tall_firm_ctx); + return 0; +} + +static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd) +{ + int out_fd; + int copied; + char filename[4096]; + off_t target; + + if (!dump_files) + return; + + if (sub_entry->header_entry.something1 == 0) + return; + + snprintf(filename, sizeof(filename), "part.%d", part++); + out_fd = open(filename, O_WRONLY | O_CREAT, 0660); + if (out_fd < 0) { + perror("Can not dump firmware"); + return; + } + + target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4; + if (lseek(fd, target, SEEK_SET) != target) { + perror("seek failed"); + close(out_fd); + return; + } + + for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) { + char c; + if (read(fd, &c, sizeof(c)) != sizeof(c)) { + perror("copy failed"); + break; + } + + if (write(out_fd, &c, sizeof(c)) != sizeof(c)) { + perror("write failed"); + break; + } + } + + close(out_fd); +} + +static void analyze_firmware(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct sdp_header_item *sub_entry; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + int part = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + printf("Opening possible firmware '%s'\n", filename); + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + return; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + + llist_for_each_entry(header, entry, entry) { + printf("Printing header information:\n"); + printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); + printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); + printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); + printf("sw_part: %.20s\n", header->firmware_info.sw_part); + printf("text1: %.64s\n", header->firmware_info.text1); + printf("time: %.12s\n", header->firmware_info.time); + printf("date: %.14s\n", header->firmware_info.date); + printf("text2: %.10s\n", header->firmware_info.text2); + printf("version: %.20s\n", header->firmware_info.version); + printf("subitems...\n"); + + llist_for_each_entry(sub_entry, &header->header_list, entry) { + printf("\tsomething1: %u\n", sub_entry->header_entry.something1); + printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); + printf("\ttime: %.12s\n", sub_entry->header_entry.time); + printf("\tdate: %.14s\n", sub_entry->header_entry.date); + printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); + printf("\tversion: %.20s\n", sub_entry->header_entry.version); + printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); + printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); + printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); + printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); + printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset); + printf("\n\n"); + + dump_entry(sub_entry, part++, fd); + } + printf("\n\n"); + } + + if (close(fd) != 0) { + perror("Close failed.\n"); + return; + } + + talloc_free(tall_firm_ctx); +} + +static void print_usage(void) +{ + printf("Usage: ipaccess-config\n"); +} + +static void print_help(void) +{ + printf(" -u --unit-id UNIT_ID\n"); + printf(" -o --oml-ip ip\n"); + printf(" -r --restart\n"); + printf(" -n flags/mask\tSet NVRAM attributes.\n"); + printf(" -l --listen testnr \tPerform specified test number\n"); + printf(" -h --help this text\n"); + printf(" -s --stream-id ID\n"); + printf(" -d --software firmware\n"); + printf(" -f --firmware firmware Provide firmware information\n"); + printf(" -w --write-firmware. This will dump the firmware parts to the filesystem. Use with -f.\n"); +} + +int main(int argc, char **argv) +{ + struct gsm_bts *bts; + struct sockaddr_in sin; + int rc, option_index = 0, stream_id = 0xff; + struct debug_target *stderr_target; + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + debug_set_log_level(stderr_target, 0); + debug_parse_category_mask(stderr_target, "DNM,0"); + bts_model_nanobts_init(); + + printf("ipaccess-config (C) 2009 by Harald Welte\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); + + while (1) { + int c; + unsigned long ul; + char *slash; + static struct option long_options[] = { + { "unit-id", 1, 0, 'u' }, + { "oml-ip", 1, 0, 'o' }, + { "restart", 0, 0, 'r' }, + { "help", 0, 0, 'h' }, + { "listen", 1, 0, 'l' }, + { "stream-id", 1, 0, 's' }, + { "software", 1, 0, 'd' }, + { "firmware", 1, 0, 'f' }, + { "write-firmware", 0, 0, 'w' }, + }; + + c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:w", long_options, + &option_index); + + if (c == -1) + break; + + switch (c) { + case 'u': + unit_id = optarg; + break; + case 'o': + prim_oml_ip = optarg; + break; + case 'r': + restart = 1; + break; + case 'n': + slash = strchr(optarg, '/'); + if (!slash) + exit(2); + ul = strtoul(optarg, NULL, 16); + nv_flags = ul & 0xffff; + ul = strtoul(slash+1, NULL, 16); + nv_mask = ul & 0xffff; + break; + case 'l': + net_listen_testnr = atoi(optarg); + break; + case 's': + stream_id = atoi(optarg); + break; + case 'd': + software = strdup(optarg); + if (find_sw_load_params(optarg) != 0) + exit(0); + break; + case 'f': + analyze_firmware(optarg); + exit(0); + case 'w': + dump_files = 1; + break; + case 'h': + print_usage(); + print_help(); + exit(0); + } + }; + + if (optind >= argc) { + fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); + exit(2); + } + + gsmnet = gsm_network_init(1, 1, NULL); + if (!gsmnet) + exit(1); + + bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC, + HARDCODED_BSIC); + /* ip.access supports up to 4 chained TRX */ + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + bts->oml_tei = stream_id; + + register_signal_handler(SS_NM, nm_sig_cb, NULL); + printf("Trying to connect to ip.access BTS ...\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(argv[optind], &sin.sin_addr); + rc = ia_config_connect(bts, &sin); + if (rc < 0) { + perror("Error connecting to the BTS"); + exit(1); + } + + while (1) { + rc = bsc_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + diff --git a/openbsc/src/ipaccess/ipaccess-find.c b/openbsc/src/ipaccess/ipaccess-find.c new file mode 100644 index 000000000..01f8a2d8d --- /dev/null +++ b/openbsc/src/ipaccess/ipaccess-find.c @@ -0,0 +1,208 @@ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + + +#include <osmocore/select.h> +#include <osmocore/timer.h> +#include <openbsc/ipaccess.h> +#include <openbsc/gsm_data.h> + +static const char *idtag_names[] = { + [IPAC_IDTAG_SERNR] = "Serial Number", + [IPAC_IDTAG_UNITNAME] = "Unit Name", + [IPAC_IDTAG_LOCATION1] = "Location 1", + [IPAC_IDTAG_LOCATION2] = "Location 2", + [IPAC_IDTAG_EQUIPVERS] = "Equipment Version", + [IPAC_IDTAG_SWVERSION] = "Software Version", + [IPAC_IDTAG_IPADDR] = "IP Address", + [IPAC_IDTAG_MACADDR] = "MAC Address", + [IPAC_IDTAG_UNIT] = "Unit ID", +}; + +static const char *ipac_idtag_name(int tag) +{ + if (tag >= ARRAY_SIZE(idtag_names)) + return "unknown"; + + return idtag_names[tag]; +} + +static int udp_sock(const char *ifname) +{ + int fd, rc, bc = 1; + struct sockaddr_in sa; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) + return fd; + + if (ifname) { + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, + strlen(ifname)); + if (rc < 0) + goto err; + } + + sa.sin_family = AF_INET; + sa.sin_port = htons(3006); + sa.sin_addr.s_addr = INADDR_ANY; + + rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); + if (rc < 0) + goto err; + + rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc)); + if (rc < 0) + goto err; + +#if 0 + /* we cannot bind, since the response packets don't come from + * the broadcast address */ + sa.sin_family = AF_INET; + sa.sin_port = htons(3006); + inet_aton("255.255.255.255", &sa.sin_addr); + + rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa)); + if (rc < 0) + goto err; +#endif + return fd; + +err: + close(fd); + return rc; +} + +const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00, + IPAC_MSGT_ID_GET, + 0x01, IPAC_IDTAG_MACADDR, + 0x01, IPAC_IDTAG_IPADDR, + 0x01, IPAC_IDTAG_UNIT, + 0x01, IPAC_IDTAG_LOCATION1, + 0x01, IPAC_IDTAG_LOCATION2, + 0x01, IPAC_IDTAG_EQUIPVERS, + 0x01, IPAC_IDTAG_SWVERSION, + 0x01, IPAC_IDTAG_UNITNAME, + 0x01, IPAC_IDTAG_SERNR, + }; + + +static int bcast_find(int fd) +{ + struct sockaddr_in sa; + + sa.sin_family = AF_INET; + sa.sin_port = htons(3006); + inet_aton("255.255.255.255", &sa.sin_addr); + + return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa)); +} + +static int parse_response(unsigned char *buf, int len) +{ + u_int8_t t_len; + u_int8_t t_tag; + u_int8_t *cur = buf; + + while (cur < buf + len) { + t_len = *cur++; + t_tag = *cur++; + + printf("%s='%s' ", ipac_idtag_name(t_tag), cur); + + cur += t_len; + } + printf("\n"); + return 0; +} + +static int read_response(int fd) +{ + unsigned char buf[255]; + struct sockaddr_in sa; + int len; + socklen_t sa_len = sizeof(sa); + + len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); + if (len < 0) + return len; + + /* 2 bytes length, 1 byte protocol (0xfe) */ + if (buf[2] != 0xfe) + return 0; + + if (buf[4] != IPAC_MSGT_ID_RESP) + return 0; + + return parse_response(buf+6, len-6); +} + +static int bfd_cb(struct bsc_fd *bfd, unsigned int flags) +{ + if (flags & BSC_FD_READ) + return read_response(bfd->fd); + if (flags & BSC_FD_WRITE) { + bfd->when &= ~BSC_FD_WRITE; + return bcast_find(bfd->fd); + } + return 0; +} + +static struct timer_list timer; + +static void timer_cb(void *_data) +{ + struct bsc_fd *bfd = _data; + + bfd->when |= BSC_FD_WRITE; + + bsc_schedule_timer(&timer, 5, 0); +} + +int main(int argc, char **argv) +{ + struct bsc_fd bfd; + char *ifname; + int rc; + + printf("ipaccess-find (C) 2009 by Harald Welte\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); + + if (argc < 2) { + fprintf(stdout, "you might need to specify the outgoing\n" + " network interface, e.g. ``%s eth0''\n", argv[0]); + } + + ifname = argv[1]; + bfd.cb = bfd_cb; + bfd.when = BSC_FD_READ | BSC_FD_WRITE; + bfd.fd = udp_sock(ifname); + if (bfd.fd < 0) { + perror("Cannot create local socket for broadcast udp"); + exit(1); + } + + bsc_register_fd(&bfd); + + timer.cb = timer_cb; + timer.data = &bfd; + + bsc_schedule_timer(&timer, 5, 0); + + printf("Trying to find ip.access BTS by broadcast UDP...\n"); + + while (1) { + rc = bsc_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + diff --git a/openbsc/src/ipaccess/ipaccess-firmware.c b/openbsc/src/ipaccess/ipaccess-firmware.c new file mode 100644 index 000000000..bc40c1e47 --- /dev/null +++ b/openbsc/src/ipaccess/ipaccess-firmware.c @@ -0,0 +1,136 @@ +/* Routines for parsing an ipacces SDP firmware file */ + +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <openbsc/debug.h> +#include <openbsc/ipaccess.h> +#include <osmocore/talloc.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define PART_LENGTH 138 + +static_assert(sizeof(struct sdp_header_entry) == 138, right_entry); +static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length); + +/* more magic, the second "int" in the header */ +static char more_magic[] = { 0x10, 0x02 }; + +int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list) +{ + struct sdp_firmware *firmware_header = 0; + struct sdp_header *header; + char buf[4096]; + int rc, i; + u_int16_t table_size; + u_int16_t table_offset; + off_t table_start; + + + rc = read(fd, buf, sizeof(*firmware_header)); + if (rc < 0) { + perror("Can not read header start."); + return -1; + } + + firmware_header = (struct sdp_firmware *) &buf[0]; + if (strncmp(firmware_header->magic, " SDP", 4) != 0) { + fprintf(stderr, "Wrong magic.\n"); + return -1; + } + + if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) { + fprintf(stderr, "Wrong more magic. Got: 0x%x %x %x %x\n", + firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff, + firmware_header->more_magic[2] & 0xff, firmware_header->more_magic[3] & 0xff); + return -1; + } + + + if (ntohl(firmware_header->file_length) != st_size) { + fprintf(stderr, "The filesize and the header do not match.\n"); + return -1; + } + + /* add the firmware */ + header = talloc_zero(list, struct sdp_header); + header->firmware_info = *firmware_header; + INIT_LLIST_HEAD(&header->header_list); + llist_add(&header->entry, list); + + table_offset = ntohs(firmware_header->table_offset); + table_start = lseek(fd, table_offset, SEEK_CUR); + if (table_start == -1) { + fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset); + return -1; + } + + if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) { + fprintf(stderr, "The table size could not be read.\n"); + return -1; + } + + table_size = ntohs(table_size); + + if (table_size % PART_LENGTH != 0) { + fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size); + return -1; + } + + /* look into each firmware now */ + for (i = 0; i < table_size / PART_LENGTH; ++i) { + struct sdp_header_entry entry; + struct sdp_header_item *header_entry; + unsigned int offset = table_start + 2; + offset += i * 138; + + if (lseek(fd, offset, SEEK_SET) != offset) { + fprintf(stderr, "Can not seek to the offset: %u.\n", offset); + return -1; + } + + rc = read(fd, &entry, sizeof(entry)); + if (rc != sizeof(entry)) { + fprintf(stderr, "Can not read the header entry.\n"); + return -1; + } + + header_entry = talloc_zero(header, struct sdp_header_item); + header_entry->header_entry = entry; + header_entry->absolute_offset = base_offset; + llist_add(&header_entry->entry, &header->header_list); + + /* now we need to find the SDP file... */ + offset = ntohl(entry.start) + 4 + base_offset; + if (lseek(fd, offset, SEEK_SET) != offset) { + perror("can't seek to sdp"); + return -1; + } + + + ipaccess_analyze_file(fd, ntohl(entry.length), offset, list); + } + + return 0; +} + diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c new file mode 100644 index 000000000..217e0bdf1 --- /dev/null +++ b/openbsc/src/ipaccess/ipaccess-proxy.c @@ -0,0 +1,1127 @@ +/* OpenBSC Abis/IP proxy ip.access nanoBTS */ + +/* (C) 2009 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/select.h> +#include <osmocore/tlv.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/ipaccess.h> +#include <osmocore/talloc.h> + +static struct debug_target *stderr_target; + +/* one instance of an ip.access protocol proxy */ +struct ipa_proxy { + /* socket where we listen for incoming OML from BTS */ + struct bsc_fd oml_listen_fd; + /* socket where we listen for incoming RSL from BTS */ + struct bsc_fd rsl_listen_fd; + /* list of BTS's (struct ipa_bts_conn */ + struct llist_head bts_list; + /* the BSC reconnect timer */ + struct timer_list reconn_timer; +}; + +/* global pointer to the proxy structure */ +static struct ipa_proxy *ipp; + +struct ipa_proxy_conn { + struct bsc_fd fd; + struct llist_head tx_queue; + struct ipa_bts_conn *bts_conn; +}; + +#define MAX_TRX 4 + +/* represents a particular BTS in our proxy */ +struct ipa_bts_conn { + /* list of BTS's (ipa_proxy->bts_list) */ + struct llist_head list; + /* back pointer to the proxy which we belong to */ + struct ipa_proxy *ipp; + /* the unit ID as determined by CCM */ + struct { + u_int16_t site_id; + u_int16_t bts_id; + } unit_id; + + /* incoming connections from BTS */ + struct ipa_proxy_conn *oml_conn; + struct ipa_proxy_conn *rsl_conn[MAX_TRX]; + + /* outgoing connections to BSC */ + struct ipa_proxy_conn *bsc_oml_conn; + struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX]; + + /* UDP sockets for BTS and BSC injection */ + struct bsc_fd udp_bts_fd; + struct bsc_fd udp_bsc_fd; + + char *id_tags[0xff]; + u_int8_t *id_resp; + unsigned int id_resp_len; +}; + +enum ipp_fd_type { + OML_FROM_BTS = 1, + RSL_FROM_BTS = 2, + OML_TO_BSC = 3, + RSL_TO_BSC = 4, + UDP_TO_BTS = 5, + UDP_TO_BSC = 6, +}; + +/* some of the code against we link from OpenBSC needs this */ +void *tall_bsc_ctx; + +static char *listen_ipaddr; +static char *bsc_ipaddr; + +#define PROXY_ALLOC_SIZE 300 + +static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; +static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; +static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET, + 0x01, IPAC_IDTAG_UNIT, + 0x01, IPAC_IDTAG_MACADDR, + 0x01, IPAC_IDTAG_LOCATION1, + 0x01, IPAC_IDTAG_LOCATION2, + 0x01, IPAC_IDTAG_EQUIPVERS, + 0x01, IPAC_IDTAG_SWVERSION, + 0x01, IPAC_IDTAG_UNITNAME, + 0x01, IPAC_IDTAG_SERNR, + }; + +static const char *idtag_names[] = { + [IPAC_IDTAG_SERNR] = "Serial_Number", + [IPAC_IDTAG_UNITNAME] = "Unit_Name", + [IPAC_IDTAG_LOCATION1] = "Location_1", + [IPAC_IDTAG_LOCATION2] = "Location_2", + [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", + [IPAC_IDTAG_SWVERSION] = "Software_Version", + [IPAC_IDTAG_IPADDR] = "IP_Address", + [IPAC_IDTAG_MACADDR] = "MAC_Address", + [IPAC_IDTAG_UNIT] = "Unit_ID", +}; + +static const char *ipac_idtag_name(int tag) +{ + if (tag >= ARRAY_SIZE(idtag_names)) + return "unknown"; + + return idtag_names[tag]; +} + +static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) +{ + u_int8_t t_len; + u_int8_t t_tag; + u_int8_t *cur = buf; + + while (cur < buf + len) { + t_len = *cur++; + t_tag = *cur++; + + DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur); + + dec->lv[t_tag].len = t_len; + dec->lv[t_tag].val = cur; + + cur += t_len; + } + return 0; +} + +static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id, + u_int16_t *trx_id) +{ + unsigned long ul; + char *endptr; + const char *nptr; + + nptr = str; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (site_id) + *site_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (bts_id) + *bts_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (trx_id) + *trx_id = ul & 0xffff; + + return 0; +} + +static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp, + u_int16_t site_id, + u_int16_t bts_id) +{ + struct ipa_bts_conn *ipbc; + + llist_for_each_entry(ipbc, &ipp->bts_list, list) { + if (ipbc->unit_id.site_id == site_id && + ipbc->unit_id.bts_id == bts_id) + return ipbc; + } + + return NULL; +} + +struct ipa_proxy_conn *alloc_conn(void) +{ + struct ipa_proxy_conn *ipc; + + ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn); + if (!ipc) + return NULL; + + INIT_LLIST_HEAD(&ipc->tx_queue); + + return ipc; +} + +static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp) +{ + unsigned int i, len; + + for (i = 0; i <= 0xff; i++) { + if (!TLVP_PRESENT(tlvp, i)) + continue; + + len = TLVP_LEN(tlvp, i); +#if 0 + if (!ipbc->id_tags[i]) + ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len); + else +#endif + ipbc->id_tags[i] = talloc_realloc_size(ipbc, + ipbc->id_tags[i], len); + if (!ipbc->id_tags[i]) + return -ENOMEM; + + memset(ipbc->id_tags[i], 0, len); + //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len); + } + return 0; +} + + +static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data); + +#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id) + +static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line, + struct ipa_bts_conn *ipbc, u_int8_t trx_id) +{ + if (ipbc) + debugp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id, + ipbc->unit_id.bts_id, trx_id); + else + debugp2(ss, lvl, file, line, 0, "unknown "); +} + +/* UDP socket handling */ + +static int make_sock(struct bsc_fd *bfd, u_int16_t port, int proto, int priv_nr, + int (*cb)(struct bsc_fd *fd, unsigned int what), + void *data) +{ + struct sockaddr_in addr; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_DGRAM, proto); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + bfd->data = data; + bfd->priv_nr = priv_nr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not bind socket: %s\n", + strerror(errno)); + return -EIO; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register UDP fd"); + return ret; + } + return 0; +} + +static int handle_udp_read(struct bsc_fd *bfd) +{ + struct ipa_bts_conn *ipbc = bfd->data; + struct ipa_proxy_conn *other_conn = NULL; + struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP"); + struct ipaccess_head *hh; + int ret; + + /* with UDP sockets, we cannot read partial packets but have to read + * all of it in one go */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, msg->data_len, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno)); + msgb_free(msg); + return ret; + } + if (ret == 0) { + DEBUGP(DINP, "UDP peer disappeared, dead socket\n"); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + msgb_free(msg); + return -EIO; + } + if (ret < sizeof(*hh)) { + DEBUGP(DINP, "could not even read header!?!\n"); + msgb_free(msg); + return -EIO; + } + msgb_put(msg, ret); + msg->l2h = msg->data + sizeof(*hh); + DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len)); + + if (hh->len != msg->len - sizeof(*hh)) { + DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n", + msg->len, msg->len - 3, hh->len); + msgb_free(msg); + return -EIO; + } + + switch (bfd->priv_nr & 0xff) { + case UDP_TO_BTS: + /* injection towards BTS */ + switch (hh->proto) { + case IPAC_PROTO_RSL: + /* FIXME: what to do about TRX > 0 */ + other_conn = ipbc->rsl_conn[0]; + break; + default: + DEBUGP(DINP, "Unknown protocol 0x%02x, sending to " + "OML FD\n", hh->proto); + /* fall through */ + case IPAC_PROTO_IPACCESS: + case IPAC_PROTO_OML: + other_conn = ipbc->oml_conn; + break; + } + break; + case UDP_TO_BSC: + /* injection towards BSC */ + switch (hh->proto) { + case IPAC_PROTO_RSL: + /* FIXME: what to do about TRX > 0 */ + other_conn = ipbc->bsc_rsl_conn[0]; + break; + default: + DEBUGP(DINP, "Unknown protocol 0x%02x, sending to " + "OML FD\n", hh->proto); + case IPAC_PROTO_IPACCESS: + case IPAC_PROTO_OML: + other_conn = ipbc->bsc_oml_conn; + break; + } + break; + default: + DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr); + break; + } + + if (other_conn) { + /* enqueue the message for TX on the respective FD */ + msgb_enqueue(&other_conn->tx_queue, msg); + other_conn->fd.when |= BSC_FD_WRITE; + } else + msgb_free(msg); + + return 0; +} + +static int handle_udp_write(struct bsc_fd *bfd) +{ + /* not implemented yet */ + bfd->when &= ~BSC_FD_WRITE; + + return -EIO; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_udp_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_udp_write(bfd); + + return rc; +} + + +static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd, + u_int16_t site_id, u_int16_t bts_id, + u_int16_t trx_id, struct tlv_parsed *tlvp, + struct msgb *msg) +{ + struct ipa_bts_conn *ipbc; + u_int16_t udp_port; + int ret = 0; + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ", + site_id, bts_id, trx_id); + + /* OML needs to be established before RSL */ + if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) { + DEBUGPC(DINP, "Not a OML connection ?!?\n"); + return -EIO; + } + + /* allocate new BTS connection data structure */ + ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn); + if (!ipbc) { + ret = -ENOMEM; + goto err_out; + } + + DEBUGPC(DINP, "Created BTS Conn data structure\n"); + ipbc->ipp = ipp; + ipbc->unit_id.site_id = site_id; + ipbc->unit_id.bts_id = bts_id; + ipbc->oml_conn = ipc; + ipc->bts_conn = ipbc; + + /* store the content of the ID TAGS for later reference */ + store_idtags(ipbc, tlvp); + ipbc->id_resp_len = msg->len; + ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len); + memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len); + + /* Create OML TCP connection towards BSC */ + sin.sin_port = htons(IPA_TCP_PORT_OML); + ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); + if (!ipbc->bsc_oml_conn) { + ret = -EIO; + goto err_bsc_conn; + } + + DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n", + site_id, bts_id, trx_id); + + /* Create UDP socket for BTS packet injection */ + udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100); + ret = make_sock(&ipbc->udp_bts_fd, udp_port, IPPROTO_UDP, + UDP_TO_BTS, udp_fd_cb, ipbc); + if (ret < 0) + goto err_udp_bts; + DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection " + "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port); + + /* Create UDP socket for BSC packet injection */ + udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100); + ret = make_sock(&ipbc->udp_bsc_fd, udp_port, IPPROTO_UDP, + UDP_TO_BSC, udp_fd_cb, ipbc); + if (ret < 0) + goto err_udp_bsc; + DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection " + "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port); + llist_add(&ipbc->list, &ipp->bts_list); + + return 0; + +err_udp_bsc: + bsc_unregister_fd(&ipbc->udp_bts_fd); +err_udp_bts: + bsc_unregister_fd(&ipbc->bsc_oml_conn->fd); + close(ipbc->bsc_oml_conn->fd.fd); + talloc_free(ipbc->bsc_oml_conn); + ipbc->bsc_oml_conn = NULL; +err_bsc_conn: + talloc_free(ipbc->id_resp); + talloc_free(ipbc); +#if 0 + bsc_unregister_fd(bfd); + close(bfd->fd); + talloc_free(bfd); +#endif +err_out: + return ret; +} + +static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg, + struct bsc_fd *bfd) +{ + struct tlv_parsed tlvp; + u_int8_t msg_type = *(msg->l2h); + u_int16_t site_id, bts_id, trx_id; + struct ipa_bts_conn *ipbc; + int ret = 0; + + switch (msg_type) { + case IPAC_MSGT_PING: + ret = write(bfd->fd, pong, sizeof(pong)); + if (ret < 0) + return ret; + if (ret < sizeof(pong)) { + DEBUGP(DINP, "short write\n"); + return -EIO; + } + break; + case IPAC_MSGT_PONG: + DEBUGP(DMI, "PONG!\n"); + break; + case IPAC_MSGT_ID_RESP: + DEBUGP(DMI, "ID_RESP "); + /* parse tags, search for Unit ID */ + ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2, + msgb_l2len(msg)-2); + DEBUGP(DMI, "\n"); + + if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { + LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n"); + return -EIO; + } + + /* lookup BTS, create sign_link, ... */ + parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), + &site_id, &bts_id, &trx_id); + ipbc = find_bts_by_unitid(ipp, site_id, bts_id); + if (!ipbc) { + /* We have not found an ipbc (per-bts proxy instance) + * for this BTS yet. The first connection of a new BTS must + * be a OML connection. We allocate the associated data structures, + * and try to connect to the remote end */ + + return ipbc_alloc_connect(ipc, bfd, site_id, bts_id, + trx_id, &tlvp, msg); + /* if this fails, the caller will clean up bfd */ + } else { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + DEBUGP(DINP, "Identified BTS %u/%u/%u\n", + site_id, bts_id, trx_id); + + if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { + LOGP(DINP, LOGL_ERROR, "Second OML connection from " + "same BTS ?!?\n"); + return 0; + } + + if (trx_id > MAX_TRX) { + LOGP(DINP, LOGL_ERROR, "We don't support more " + "than %u TRX\n", MAX_TRX); + return -EINVAL; + } + + ipc->bts_conn = ipbc; + /* store TRX number in higher 8 bit of the bfd private number */ + bfd->priv_nr |= trx_id << 8; + ipbc->rsl_conn[trx_id] = ipc; + + /* Create RSL TCP connection towards BSC */ + sin.sin_port = htons(IPA_TCP_PORT_RSL); + ipbc->bsc_rsl_conn[trx_id] = + connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc); + if (!ipbc->bsc_oml_conn) + return -EIO; + DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n", + site_id, bts_id, trx_id); + } + break; + case IPAC_MSGT_ID_GET: + DEBUGP(DMI, "ID_GET\n"); + if ((bfd->priv_nr & 0xff) != OML_TO_BSC && + (bfd->priv_nr & 0xff) != RSL_TO_BSC) { + DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n"); + return -EIO; + } + ipbc = ipc->bts_conn; + if (!ipbc) { + DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); + return -EIO; + } + ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); + break; + case IPAC_MSGT_ID_ACK: + DEBUGP(DMI, "ID_ACK? -> ACK!\n"); + ret = write(bfd->fd, id_ack, sizeof(id_ack)); + break; + } + return 0; +} + +struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); + struct ipaccess_head *hh; + int len, ret = 0; + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + /* first read our 3-byte header */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, 3, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msgb_put(msg, ret); + + /* then read te length as specified in header */ + msg->l2h = msg->data + sizeof(*hh); + len = ntohs(hh->len); + ret = recv(bfd->fd, msg->l2h, len, 0); + if (ret < len) { + LOGP(DINP, LOGL_ERROR, "short read!\n"); + msgb_free(msg); + *error = -EIO; + return NULL; + } + msgb_put(msg, ret); + + return msg; +} + +static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, + unsigned int priv_nr) +{ + struct ipa_proxy_conn *bsc_conn; + unsigned int trx_id = priv_nr >> 8; + + switch (priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + bsc_conn = ipbc->bsc_oml_conn; + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + bsc_conn = ipbc->oml_conn; + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + bsc_conn = ipbc->rsl_conn[trx_id]; + break; + default: + bsc_conn = NULL; + break; + } + return bsc_conn; +} + +static void reconn_tmr_cb(void *data) +{ + struct ipa_proxy *ipp = data; + struct ipa_bts_conn *ipbc; + struct sockaddr_in sin; + int i; + + DEBUGP(DINP, "Running reconnect timer\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + llist_for_each_entry(ipbc, &ipp->bts_list, list) { + /* if OML to BSC is dead, try to restore it */ + if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { + sin.sin_port = htons(IPA_TCP_PORT_OML); + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n"); + ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); + if (!ipbc->bsc_oml_conn) + goto reschedule; + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n"); + } + /* if we (still) don't have a OML connection, skip RSL */ + if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) + continue; + + for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { + unsigned int priv_nr; + /* don't establish RSL links which we don't have */ + if (!ipbc->rsl_conn[i]) + continue; + if (ipbc->bsc_rsl_conn[i]) + continue; + priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; + priv_nr &= ~0xff; + priv_nr |= RSL_TO_BSC; + sin.sin_port = htons(IPA_TCP_PORT_RSL); + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); + ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); + if (!ipbc->bsc_rsl_conn) + goto reschedule; + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n"); + } + } + return; + +reschedule: + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); +} + +static void handle_dead_socket(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ + struct ipa_proxy_conn *bsc_conn; /* remote conn */ + struct ipa_bts_conn *ipbc = ipc->bts_conn; + unsigned int trx_id = bfd->priv_nr >> 8; + struct msgb *msg, *msg2; + + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* FIXME: clear tx_queue, remove all references, etc. */ + llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) + msgb_free(msg); + + switch (bfd->priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + ipbc->oml_conn = NULL; + bsc_conn = ipbc->bsc_oml_conn; + /* close the connection to the BSC */ + bsc_unregister_fd(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_oml_conn = NULL; + /* FIXME: do we need to delete the entire ipbc ? */ + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + ipbc->rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + /* close the connection to the BSC */ + bsc_unregister_fd(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_rsl_conn[trx_id] = NULL; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + ipbc->bsc_oml_conn = NULL; + bsc_conn = ipbc->oml_conn; + /* start reconnect timer */ + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + ipbc->bsc_rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->rsl_conn[trx_id]; + /* start reconnect timer */ + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); + break; + default: + bsc_conn = NULL; + break; + } + + talloc_free(ipc); +} + +static int handle_tcp_read(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct ipa_proxy_conn *bsc_conn; + struct msgb *msg; + struct ipaccess_head *hh; + int ret = 0; + char *btsbsc; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + msg = ipaccess_read_msg(bfd, &ret); + if (!msg) { + if (ret == 0) { + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "%s disappeared, " + "dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + return ret; + } + + msgb_put(msg, ret); + logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len)); + + hh = (struct ipaccess_head *) msg->data; + if (hh->proto == IPAC_PROTO_IPACCESS) { + ret = ipaccess_rcvmsg(ipc, msg, bfd); + if (ret < 0) { + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + } + /* we do not forward the CCM protocol through the + * proxy but rather terminate it ourselves */ + msgb_free(msg); + return ret; + } + + if (!ipbc) { + LOGP(DINP, LOGL_ERROR, + "received %s packet but no ipc->bts_conn?!?\n", btsbsc); + msgb_free(msg); + return -EIO; + } + + bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); + if (bsc_conn) { + /* enqueue packet towards BSC */ + msgb_enqueue(&bsc_conn->tx_queue, msg); + /* mark respective filedescriptor as 'we want to write' */ + bsc_conn->fd.when |= BSC_FD_WRITE; + } else { + logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); + LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, " + "since remote connection is dead\n", btsbsc); + msgb_free(msg); + } + + return ret; +} + +/* a TCP socket is ready to be written to */ +static int handle_tcp_write(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct llist_head *lh; + struct msgb *msg; + char *btsbsc; + int ret; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + + /* get the next msg for this timeslot */ + if (llist_empty(&ipc->tx_queue)) { + bfd->when &= ~BSC_FD_WRITE; + return 0; + } + lh = ipc->tx_queue.next; + llist_del(lh); + msg = llist_entry(lh, struct msgb, list); + + logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr, + hexdump(msg->data, msg->len)); + + ret = send(bfd->fd, msg->data, msg->len, 0); + msgb_free(msg); + + if (ret == 0) { + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) { + rc = handle_tcp_read(bfd); + if (rc < 0) + return rc; + } + if (what & BSC_FD_WRITE) + rc = handle_tcp_write(bfd); + + return rc; +} + +/* callback of the listening filedescriptor */ +static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + int ret; + struct ipa_proxy_conn *ipc; + struct bsc_fd *bfd; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + if (!(what & BSC_FD_READ)) + return 0; + + ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (ret < 0) { + perror("accept"); + return ret; + } + DEBUGP(DINP, "accept()ed new %s link from %s\n", + (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL", + inet_ntoa(sa.sin_addr)); + + ipc = alloc_conn(); + if (!ipc) { + close(ret); + return -ENOMEM; + } + + bfd = &ipc->fd; + bfd->fd = ret; + bfd->data = ipc; + bfd->priv_nr = listen_bfd->priv_nr; + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(ipc); + return ret; + } + + /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ + ret = write(bfd->fd, id_req, sizeof(id_req)); + + return 0; +} + +static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr, + int (*cb)(struct bsc_fd *fd, unsigned int what)) +{ + struct sockaddr_in addr; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + bfd->priv_nr = priv_nr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (!listen_ipaddr) + addr.sin_addr.s_addr = INADDR_ANY; + else + inet_aton(listen_ipaddr, &addr.sin_addr); + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not bind listen socket %s\n", + strerror(errno)); + return -EIO; + } + + ret = listen(bfd->fd, 1); + if (ret < 0) { + perror("listen"); + return ret; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register_listen_fd"); + return ret; + } + return 0; +} + +/* Actively connect to a BSC. */ +static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data) +{ + struct ipa_proxy_conn *ipc; + struct bsc_fd *bfd; + int ret, on = 1; + + ipc = alloc_conn(); + if (!ipc) + return NULL; + + ipc->bts_conn = data; + + bfd = &ipc->fd; + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ | BSC_FD_WRITE; + bfd->data = ipc; + bfd->priv_nr = priv_nr; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not connect socket\n"); + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + /* pre-fill tx_queue with identity request */ + ret = bsc_register_fd(bfd); + if (ret < 0) { + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + return ipc; +} + +static int ipaccess_proxy_setup(void) +{ + int ret; + + ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); + if (!ipp) + return -ENOMEM; + INIT_LLIST_HEAD(&ipp->bts_list); + ipp->reconn_timer.cb = reconn_tmr_cb; + ipp->reconn_timer.data = ipp; + + /* Listen for OML connections */ + ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML, + OML_FROM_BTS, listen_fd_cb); + if (ret < 0) + return ret; + + /* Listen for RSL connections */ + ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL, + RSL_FROM_BTS, listen_fd_cb); + + return ret; +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report_full(tall_bsc_ctx, stderr); + break; + default: + break; + } +} + +int main(int argc, char **argv) +{ + int rc; + + listen_ipaddr = "192.168.100.11"; + bsc_ipaddr = "192.168.100.239"; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + debug_parse_category_mask(stderr_target, "DINP:DMI"); + + rc = ipaccess_proxy_setup(); + if (rc < 0) + exit(1); + + signal(SIGUSR1, &signal_handler); + signal(SIGABRT, &signal_handler); + + while (1) { + bsc_select_main(0); + } +} diff --git a/openbsc/src/isdnsync.c b/openbsc/src/isdnsync.c new file mode 100644 index 000000000..d8819ac6b --- /dev/null +++ b/openbsc/src/isdnsync.c @@ -0,0 +1,192 @@ +/* isdnsync.c + * + * Author Andreas Eversberg <jolly@eversberg.eu> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include "mISDNif.h" +#define MISDN_OLD_AF_COMPATIBILITY +#define AF_COMPATIBILITY_FUNC +#include "compat_af_isdn.h" + +int card = 0; +int sock = -1; + +int mISDN_open(void) +{ + int fd, ret; + struct mISDN_devinfo devinfo; + struct sockaddr_mISDN l2addr; + + fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (fd < 0) { + fprintf(stderr, "could not open socket (%s)\n", strerror(errno)); + return fd; + } + devinfo.id = card; + ret = ioctl(fd, IMGETDEVINFO, &devinfo); + if (ret < 0) { + fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno)); + close(fd); + return ret; + } + close(fd); + if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) + && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) { + fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno)); + close(fd); + return ret; + } + fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE); + if (fd < 0) { + fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno)); + return fd; + } + l2addr.family = AF_ISDN; + l2addr.dev = card; + l2addr.channel = 0; + l2addr.sapi = 0; + l2addr.tei = 0; + ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr)); + if (ret < 0) { + fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno)); + close(fd); + return ret; + } + sock = fd; + + return sock; +} + + +void mISDN_handle(void) +{ + int ret; + fd_set rfd; + struct timeval tv; + struct sockaddr_mISDN addr; + socklen_t alen; + unsigned char buffer[2048]; + struct mISDNhead *hh = (struct mISDNhead *)buffer; + int l1 = 0, l2 = 0, tei = 0; + + while(1) { +again: + FD_ZERO(&rfd); + FD_SET(sock, &rfd); + tv.tv_sec = 2; + tv.tv_usec = 0; + ret = select(sock+1, &rfd, NULL, NULL, &tv); + if (ret < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno)); + break; + } + if (FD_ISSET(sock, &rfd)) { + alen = sizeof(addr); + ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen); + if (ret < 0) { + fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno)); + } else if (ret < MISDN_HEADER_LEN) { + fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__); + } else { + switch(hh->prim) { + case MPH_ACTIVATE_IND: + case PH_ACTIVATE_IND: + if (!l1) { + printf("PH_ACTIVATE\n"); + printf("*** Sync available from interface :-)\n"); + l1 = 1; + } + goto again; + break; + case MPH_DEACTIVATE_IND: + case PH_DEACTIVATE_IND: + if (l1) { + printf("PH_DEACTIVATE\n"); + printf("*** Lost sync on interface :-(\n"); + l1 = 0; + } + goto again; + break; + case DL_ESTABLISH_IND: + case DL_ESTABLISH_CNF: + printf("DL_ESTABLISH\n"); + l2 = 1; + goto again; + break; + case DL_RELEASE_IND: + case DL_RELEASE_CNF: + printf("DL_RELEASE\n"); + l2 = 0; + goto again; + break; + case DL_INFORMATION_IND: + printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi); + tei = 1; + break; + default: +// printf("prim %x\n", hh->prim); + goto again; + } + } + } + if (tei && !l2) { + hh->prim = DL_ESTABLISH_REQ; + printf("-> activating layer 2\n"); + sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen); + } + } +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc <= 1) + { + printf("Usage: %s <card>\n\n", argv[0]); + printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n"); + printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n"); + return(0); + } + + card = atoi(argv[1]); + + init_af_isdn(); + + if ((ret = mISDN_open() < 0)) + return(ret); + + mISDN_handle(); + + close(sock); + + return 0; +} diff --git a/openbsc/src/meas_rep.c b/openbsc/src/meas_rep.c new file mode 100644 index 000000000..4b9cc1a0c --- /dev/null +++ b/openbsc/src/meas_rep.c @@ -0,0 +1,114 @@ +/* Measurement Report Processing */ + +/* (C) 2009 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 <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/meas_rep.h> + +static int get_field(const struct gsm_meas_rep *rep, + enum meas_rep_field field) +{ + switch (field) { + case MEAS_REP_DL_RXLEV_FULL: + return rep->dl.full.rx_lev; + case MEAS_REP_DL_RXLEV_SUB: + return rep->dl.sub.rx_lev; + case MEAS_REP_DL_RXQUAL_FULL: + return rep->dl.full.rx_qual; + case MEAS_REP_DL_RXQUAL_SUB: + return rep->dl.sub.rx_qual; + case MEAS_REP_UL_RXLEV_FULL: + return rep->ul.full.rx_lev; + case MEAS_REP_UL_RXLEV_SUB: + return rep->ul.sub.rx_lev; + case MEAS_REP_UL_RXQUAL_FULL: + return rep->ul.full.rx_qual; + case MEAS_REP_UL_RXQUAL_SUB: + return rep->ul.sub.rx_qual; + } + + return 0; +} + + +unsigned int calc_initial_idx(unsigned int array_size, + unsigned int meas_rep_idx, + unsigned int num_values) +{ + int offs, idx; + + /* from which element do we need to start if we're interested + * in an average of 'num' elements */ + offs = meas_rep_idx - num_values; + + if (offs < 0) + idx = array_size + offs; + else + idx = offs; + + return idx; +} + +/* obtain an average over the last 'num' fields in the meas reps */ +int get_meas_rep_avg(const struct gsm_lchan *lchan, + enum meas_rep_field field, unsigned int num) +{ + unsigned int i, idx; + int avg = 0; + + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, num); + + for (i = 0; i < num; i++) { + int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep); + + avg += get_field(&lchan->meas_rep[j], field); + } + + return avg / num; +} + +/* Check if N out of M last values for FIELD are >= bd */ +int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, + enum meas_rep_field field, + unsigned int n, unsigned int m, int be) +{ + unsigned int i, idx; + int count = 0; + + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, m); + + for (i = 0; i < m; i++) { + int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep); + int val = get_field(&lchan->meas_rep[j], field); + + if (val >= be) + count++; + + if (count >= n) + return 1; + } + + return 0; +} diff --git a/openbsc/src/mgcp.cfg b/openbsc/src/mgcp.cfg new file mode 100644 index 000000000..678f54637 --- /dev/null +++ b/openbsc/src/mgcp.cfg @@ -0,0 +1,19 @@ +! +! MGCP configuration hand edited +! ! +password foo +! +line vty + no login +! +mgcp +! local ip 213.167.134.14 + bts ip 172.16.252.43 + bind ip 213.167.134.141 + bind port 2427 + bind early 1 + rtp base 4000 + sdp audio payload number 98 + sdp audio payload name AMR/8000 + number endpoints 31 + loop 1 diff --git a/openbsc/src/mgcp/mgcp_main.c b/openbsc/src/mgcp/mgcp_main.c new file mode 100644 index 000000000..cea0ba427 --- /dev/null +++ b/openbsc/src/mgcp/mgcp_main.c @@ -0,0 +1,216 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The main method to drive it as a standalone process */ + +/* + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <limits.h> +#include <unistd.h> + +#include <sys/socket.h> + +#include <openbsc/debug.h> +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> +#include <osmocore/select.h> +#include <openbsc/mgcp.h> +#include <openbsc/telnet_interface.h> + +/* this is here for the vty... it will never be called */ +void subscr_put() { abort(); } + +#define _GNU_SOURCE +#include <getopt.h> + +#warning "Make use of the rtp proxy code" + +static struct bsc_fd bfd; +static int first_request = 1; +static struct mgcp_config *cfg; + +static char *config_file = "mgcp.cfg"; + +/* used by msgb and mgcp */ +void *tall_bsc_ctx = NULL; + +static void print_help() +{ + printf("Some useful help...\n"); + printf(" -h --help is printing this text.\n"); + printf(" -c --config-file filename The config file to use.\n"); +} + +static void handle_options(int argc, char** argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"config-file", 1, 0, 'c'}, + {0, 0, 0, 0}, + }; + + c = getopt_long(argc, argv, "hc:", long_options, &option_index); + + if (c == -1) + break; + + switch(c) { + case 'h': + print_help(); + exit(0); + break; + case 'c': + config_file = talloc_strdup(tall_bsc_ctx, optarg); + break; + default: + /* ignore */ + break; + }; + } +} + +static int read_call_agent(struct bsc_fd *fd, unsigned int what) +{ + struct sockaddr_in addr; + socklen_t slen = sizeof(addr); + struct msgb *msg; + struct msgb *resp; + + msg = (struct msgb *) fd->data; + + /* read one less so we can use it as a \0 */ + int rc = recvfrom(bfd.fd, msg->data, msg->data_len - 1, 0, + (struct sockaddr *) &addr, &slen); + if (rc < 0) { + perror("Gateway failed to read"); + return -1; + } else if (slen > sizeof(addr)) { + fprintf(stderr, "Gateway received message from outerspace: %d %d\n", + slen, sizeof(addr)); + return -1; + } + + if (first_request) { + first_request = 0; + resp = mgcp_create_rsip(); + + if (resp) { + sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, + (struct sockaddr *) &addr, sizeof(addr)); + msgb_free(resp); + } + return 0; + } + + /* handle message now */ + msg->l2h = msgb_put(msg, rc); + resp = mgcp_handle_message(cfg, msg); + msgb_reset(msg); + + if (resp) { + sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr)); + msgb_free(resp); + } + return 0; +} + + +int main(int argc, char** argv) +{ + struct gsm_network dummy_network; + struct sockaddr_in addr; + int on = 1, rc; + struct debug_target *stderr_target; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + + cfg = mgcp_config_alloc(); + if (!cfg) + return -1; + + handle_options(argc, argv); + + telnet_init(&dummy_network, 4243); + rc = mgcp_parse_config(config_file, cfg); + if (rc < 0) + return rc; + + + /* we need to bind a socket */ + if (rc == 0) { + bfd.when = BSC_FD_READ; + bfd.cb = read_call_agent; + bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); + if (bfd.fd < 0) { + perror("Gateway failed to listen"); + return -1; + } + + setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(cfg->source_port); + inet_aton(cfg->source_addr, &addr.sin_addr); + + if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Gateway failed to bind"); + return -1; + } + + bfd.data = msgb_alloc(4096, "mgcp-msg"); + if (!bfd.data) { + fprintf(stderr, "Gateway memory error.\n"); + return -1; + } + + + if (bsc_register_fd(&bfd) != 0) { + DEBUGP(DMGCP, "Failed to register the fd\n"); + return -1; + } + + DEBUGP(DMGCP, "Configured for MGCP.\n"); + } + + /* initialisation */ + srand(time(NULL)); + + /* main loop */ + while (1) { + bsc_select_main(0); + } + + + return 0; +} diff --git a/openbsc/src/mgcp/mgcp_network.c b/openbsc/src/mgcp/mgcp_network.c new file mode 100644 index 000000000..b76ca4732 --- /dev/null +++ b/openbsc/src/mgcp/mgcp_network.c @@ -0,0 +1,255 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (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 <string.h> +#include <unistd.h> +#include <endian.h> + +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> + +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#warning "Make use of the rtp proxy code" + +/* according to rtp_proxy.c RFC 3550 */ +struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t csrc_count:4, + extension:1, + padding:1, + version:2; + u_int8_t payload_type:7, + marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t version:2, + padding:1, + extension:1, + csrc_count:4; + u_int8_t marker:1, + payload_type:7; +#endif + u_int16_t sequence; + u_int32_t timestamp; + u_int32_t ssrc; +} __attribute__((packed)); + + +enum { + DEST_NETWORK = 0, + DEST_BTS = 1, +}; + +enum { + PROTO_RTP, + PROTO_RTCP, +}; + + +static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) +{ + struct sockaddr_in out; + out.sin_family = AF_INET; + out.sin_port = port; + memcpy(&out.sin_addr, addr, sizeof(*addr)); + + return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out)); +} + +static void patch_payload(int payload, char *data, int len) +{ + struct rtp_hdr *rtp_hdr; + + if (len < sizeof(*rtp_hdr)) + return; + + rtp_hdr = (struct rtp_hdr *) data; + rtp_hdr->payload_type = payload; +} + +/* + * There is data coming. We will have to figure out if it + * came from the BTS or the MediaGateway of the MSC. On top + * of that we need to figure out if it was RTP or RTCP. + * + * Currently we do not communicate with the BSC so we have + * no idea where the BTS is listening for RTP and need to + * do the classic routing trick. Wait for the first packet + * from the BTS and then go ahead. + */ +static int rtp_data_cb(struct bsc_fd *fd, unsigned int what) +{ + char buf[4096]; + struct sockaddr_in addr; + socklen_t slen = sizeof(addr); + struct mgcp_endpoint *endp; + struct mgcp_config *cfg; + int rc, dest, proto; + + endp = (struct mgcp_endpoint *) fd->data; + cfg = endp->cfg; + + rc = recvfrom(fd->fd, &buf, sizeof(buf), 0, + (struct sockaddr *) &addr, &slen); + if (rc < 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x\n", + ENDPOINT_NUMBER(endp)); + return -1; + } + + /* do not forward aynthing... maybe there is a packet from the bts */ + if (endp->ci == CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp)); + return -1; + } + + /* + * Figure out where to forward it to. This code assumes that we + * have received the Connection Modify and know who is a legitimate + * partner. According to the spec we could attempt to forward even + * after the Create Connection but we will not as we are not really + * able to tell if this is legitimate. + */ + #warning "Slight spec violation. With connection mode recvonly we should attempt to forward." + dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 && + (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port) + ? DEST_BTS : DEST_NETWORK; + proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP; + + /* We have no idea who called us, maybe it is the BTS. */ + if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || cfg->forward_ip)) { + /* it was the BTS... */ + if (!cfg->bts_ip || memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0) { + if (fd == &endp->local_rtp) { + endp->bts_rtp = addr.sin_port; + } else { + endp->bts_rtcp = addr.sin_port; + } + + endp->bts = addr.sin_addr; + LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d\n", + ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp)); + } + } + + /* dispatch */ + if (cfg->audio_loop) + dest = !dest; + + if (dest == DEST_NETWORK) { + patch_payload(endp->net_payload_type, buf, rc); + return udp_send(fd->fd, &endp->remote, + proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp, + buf, rc); + } else { + patch_payload(endp->bts_payload_type, buf, rc); + return udp_send(fd->fd, &endp->bts, + proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp, + buf, rc); + } +} + +static int create_bind(const char *source_addr, struct bsc_fd *fd, int port) +{ + struct sockaddr_in addr; + int on = 1; + + fd->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd->fd < 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create UDP port.\n"); + return -1; + } + + setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + inet_aton(source_addr, &addr.sin_addr); + + if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + return -1; + } + + return 0; +} + +static int bind_rtp(struct mgcp_endpoint *endp) +{ + struct mgcp_config *cfg = endp->cfg; + + if (create_bind(cfg->source_addr, &endp->local_rtp, endp->rtp_port) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n", + cfg->source_addr, endp->rtp_port, ENDPOINT_NUMBER(endp)); + goto cleanup0; + } + + if (create_bind(cfg->source_addr, &endp->local_rtcp, endp->rtp_port + 1) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n", + cfg->source_addr, endp->rtp_port + 1, ENDPOINT_NUMBER(endp)); + goto cleanup1; + } + + endp->local_rtp.cb = rtp_data_cb; + endp->local_rtp.data = endp; + endp->local_rtp.when = BSC_FD_READ; + if (bsc_register_fd(&endp->local_rtp) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n", + endp->rtp_port, ENDPOINT_NUMBER(endp)); + goto cleanup2; + } + + endp->local_rtcp.cb = rtp_data_cb; + endp->local_rtcp.data = endp; + endp->local_rtcp.when = BSC_FD_READ; + if (bsc_register_fd(&endp->local_rtcp) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n", + endp->rtp_port + 1, ENDPOINT_NUMBER(endp)); + goto cleanup3; + } + + return 0; + +cleanup3: + bsc_unregister_fd(&endp->local_rtp); +cleanup2: + close(endp->local_rtcp.fd); + endp->local_rtcp.fd = -1; +cleanup1: + close(endp->local_rtp.fd); + endp->local_rtp.fd = -1; +cleanup0: + return -1; +} + +int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port) +{ + endp->rtp_port = rtp_port; + return bind_rtp(endp); +} diff --git a/openbsc/src/mgcp/mgcp_protocol.c b/openbsc/src/mgcp/mgcp_protocol.c new file mode 100644 index 000000000..f7ef5470d --- /dev/null +++ b/openbsc/src/mgcp/mgcp_protocol.c @@ -0,0 +1,745 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <limits.h> +#include <unistd.h> + +#include <openbsc/debug.h> +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> +#include <osmocore/select.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +enum mgcp_connection_mode { + MGCP_CONN_NONE = 0, + MGCP_CONN_RECV_ONLY = 1, + MGCP_CONN_SEND_ONLY = 2, + MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, +}; + +/** + * Macro for tokenizing MGCP messages and SDP in one go. + * + */ +#define MSG_TOKENIZE_START \ + line_start = 0; \ + for (i = 0; i < msgb_l3len(msg); ++i) { \ + /* we have a line end */ \ + if (msg->l3h[i] == '\n') { \ + /* skip the first line */ \ + if (line_start == 0) { \ + line_start = i + 1; \ + continue; \ + } \ + \ + /* check if we have a proper param */ \ + if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \ + } else if (i - line_start > 2 \ + && islower(msg->l3h[line_start]) \ + && msg->l3h[line_start + 1] == '=') { \ + } else if (i - line_start < 3 \ + || msg->l3h[line_start + 1] != ':' \ + || msg->l3h[line_start + 2] != ' ') \ + goto error; \ + \ + msg->l3h[i] = '\0'; \ + if (msg->l3h[i-1] == '\r') \ + msg->l3h[i-1] = '\0'; + +#define MSG_TOKENIZE_END \ + line_start = i + 1; \ + } \ + } + + +struct mgcp_msg_ptr { + unsigned int start; + unsigned int length; +}; + +struct mgcp_request { + char *name; + struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg); + char *debug_name; +}; + +#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \ + { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME }, + +static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg); + +static int generate_call_id(struct mgcp_config *cfg) +{ + int i; + + /* use the call id */ + ++cfg->last_call_id; + + /* handle wrap around */ + if (cfg->last_call_id == CI_UNUSED) + ++cfg->last_call_id; + + /* callstack can only be of size number_of_endpoints */ + /* verify that the call id is free, e.g. in case of overrun */ + for (i = 1; i < cfg->number_endpoints; ++i) + if (cfg->endpoints[i].ci == cfg->last_call_id) + return generate_call_id(cfg); + + return cfg->last_call_id; +} + +/* FIXIME/TODO: need to have a list of pending transactions and check that */ +static unsigned int generate_transaction_id() +{ + return abs(rand()); +} + +/* + * array of function pointers for handling various + * messages. In the future this might be binary sorted + * for performance reasons. + */ +static const struct mgcp_request mgcp_requests [] = { + MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint") + MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection") + MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection") + MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection") +}; + +static struct msgb *mgcp_msgb_alloc(void) +{ + struct msgb *msg; + msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); + if (!msg) + LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n"); + + return msg; +} + +struct msgb *mgcp_create_response_with_data(int code, const char *msg, const char *trans, + const char *data) +{ + int len; + struct msgb *res; + + res = mgcp_msgb_alloc(); + if (!res) + return NULL; + + if (data) { + len = snprintf((char *) res->data, 2048, "%d %s\n%s", code, trans, data); + } else { + len = snprintf((char *) res->data, 2048, "%d %s\n", code, trans); + } + + res->l2h = msgb_put(res, len); + LOGP(DMGCP, LOGL_DEBUG, "Sending response: code: %d for '%s'\n", code, res->l2h); + return res; +} + +static struct msgb *create_response(int code, const char *msg, const char *trans) +{ + return mgcp_create_response_with_data(code, msg, trans, NULL); +} + +static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, + const char *msg, const char *trans_id) +{ + const char *addr = endp->cfg->local_ip; + char sdp_record[4096]; + + if (!addr) + addr = endp->cfg->source_addr; + + snprintf(sdp_record, sizeof(sdp_record) - 1, + "I: %d\n\n" + "v=0\r\n" + "c=IN IP4 %s\r\n" + "m=audio %d RTP/AVP %d\r\n" + "a=rtpmap:%d %s\r\n", + endp->ci, addr, endp->rtp_port, + endp->bts_payload_type, endp->bts_payload_type, + endp->cfg->audio_name); + return mgcp_create_response_with_data(200, msg, trans_id, sdp_record); +} + +/* send a static record */ +struct msgb *mgcp_create_rsip(void) +{ + struct msgb *msg; + int len; + + msg = mgcp_msgb_alloc(); + if (!msg) + return NULL; + + len = snprintf((char *) msg->data, 2048, + "RSIP %u *@mgw MGCP 1.0\n" + "RM: restart\n", generate_transaction_id()); + msg->l2h = msgb_put(msg, len); + return msg; +} + +/* + * handle incoming messages: + * - this can be a command (four letters, space, transaction id) + * - or a response (three numbers, space, transaction id) + */ +struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) +{ + int code; + struct msgb *resp = NULL; + + if (msg->len < 4) { + LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->len); + return NULL; + } + + /* attempt to treat it as a response */ + if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) { + LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); + } else { + int i, handled = 0; + msg->l3h = &msg->l2h[4]; + for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) + if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) { + handled = 1; + resp = mgcp_requests[i].handle_request(cfg, msg); + break; + } + if (!handled) { + LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->data[0]); + } + } + + return resp; +} + +/* string tokenizer for the poor */ +static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length) +{ + int i, found = 0; + + int whitespace = 1; + for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) { + /* if we have a space we found an end */ + if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { + if (!whitespace) { + ++found; + whitespace = 1; + ptrs->length = i - ptrs->start - 1; + ++ptrs; + --ptrs_length; + } else { + /* skip any number of whitespace */ + } + + /* line end... stop */ + if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') + break; + } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { + /* line end, be done */ + break; + } else if (whitespace) { + whitespace = 0; + ptrs->start = i; + } + } + + if (ptrs_length == 0) + return -1; + return found; +} + +static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp) +{ + char *endptr = NULL; + unsigned int gw = INT_MAX; + + gw = strtoul(mgcp, &endptr, 16); + if (gw == 0 || gw >= cfg->number_endpoints || strcmp(endptr, "@mgw") != 0) { + LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp); + return NULL; + } + + return &cfg->endpoints[gw]; +} + +static int analyze_header(struct mgcp_config *cfg, struct msgb *msg, + struct mgcp_msg_ptr *ptr, int size, + const char **transaction_id, struct mgcp_endpoint **endp) +{ + int found; + + *transaction_id = "000000"; + + if (size < 3) { + LOGP(DMGCP, LOGL_ERROR, "Not enough space in ptr\n"); + return -1; + } + + found = find_msg_pointers(msg, ptr, size); + + if (found <= 3) { + LOGP(DMGCP, LOGL_ERROR, "Gateway: Not enough params. Found: %d\n", found); + return -1; + } + + /* + * replace the space with \0. the main method gurantess that + * we still have + 1 for null termination + */ + msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0'; + msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0'; + msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0'; + msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0'; + + if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0 + || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP version. Not handling: '%s' '%s'\n", + (const char *)&msg->l3h[ptr[3].start], + (const char *)&msg->l3h[ptr[2].start]); + return -1; + } + + *transaction_id = (const char *)&msg->l3h[ptr[0].start]; + *endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]); + return *endp == NULL; +} + +static int verify_call_id(const struct mgcp_endpoint *endp, + const char *callid) +{ + if (strcmp(endp->callid, callid) != 0) { + LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n", + ENDPOINT_NUMBER(endp), endp->callid, callid); + return -1; + } + + return 0; +} + +static int verify_ci(const struct mgcp_endpoint *endp, + const char *ci) +{ + if (atoi(ci) != endp->ci) { + LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n", + ENDPOINT_NUMBER(endp), endp->ci, ci); + return -1; + } + + return 0; +} + +static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, response; + const char *trans_id; + struct mgcp_endpoint *endp; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + response = 500; + else + response = 200; + + return create_response(response, "AUEP", trans_id); +} + +static int parse_conn_mode(const char* msg, int *conn_mode) +{ + int ret = 0; + if (strcmp(msg, "recvonly") == 0) + *conn_mode = MGCP_CONN_RECV_ONLY; + else if (strcmp(msg, "sendrecv") == 0) + *conn_mode = MGCP_CONN_RECV_SEND; + else { + LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); + ret = -1; + } + + return ret; +} + +static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 500; + int port; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_response(500, "CRCX", trans_id); + + if (endp->ci != CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(500, "CRCX", trans_id); + } + + /* parse CallID C: and LocalParameters L: */ + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'L': + endp->local_options = talloc_strdup(cfg->endpoints, + (const char *)&msg->l3h[line_start + 3]); + break; + case 'C': + endp->callid = talloc_strdup(cfg->endpoints, + (const char *)&msg->l3h[line_start + 3]); + break; + case 'M': + if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], + &endp->conn_mode) != 0) { + error_code = 517; + goto error2; + } + break; + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* initialize */ + endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0; + + /* set to zero until we get the info */ + memset(&endp->remote, 0, sizeof(endp->remote)); + + /* bind to the port now */ + port = rtp_calculate_port(ENDPOINT_NUMBER(endp), cfg->rtp_base_port); + if (cfg->early_bind) + endp->rtp_port = port; + else if (mgcp_bind_rtp_port(endp, port) != 0) + goto error2; + + /* assign a local call identifier or fail */ + endp->ci = generate_call_id(cfg); + if (endp->ci == CI_UNUSED) + goto error2; + + endp->bts_payload_type = cfg->audio_payload; + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, trans_id)) { + case MGCP_POLICY_REJECT: + LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n", + ENDPOINT_NUMBER(endp)); + mgcp_free_endp(endp); + return create_response(500, "CRCX", trans_id); + break; + case MGCP_POLICY_DEFER: + /* stop processing */ + return NULL; + break; + case MGCP_POLICY_CONT: + /* just continue */ + break; + } + } + + LOGP(DMGCP, LOGL_NOTICE, "Creating endpoint on: 0x%x CI: %u port: %u\n", + ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port); + if (cfg->change_cb) + cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, endp->rtp_port); + + return create_response_with_sdp(endp, "CRCX", trans_id); +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i); + return create_response(error_code, "CRCX", trans_id); + +error2: + LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(error_code, "CRCX", trans_id); +} + +static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 500; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_response(error_code, "MDCX", trans_id); + + if (endp->ci == CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(error_code, "MDCX", trans_id); + } + + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'C': { + if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'I': { + if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'L': + /* skip */ + break; + case 'M': + if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], + &endp->conn_mode) != 0) { + error_code = 517; + goto error3; + } + break; + case '\0': + /* SDP file begins */ + break; + case 'a': + case 'o': + case 's': + case 't': + case 'v': + /* skip these SDP attributes */ + break; + case 'm': { + int port; + int payload; + const char *param = (const char *)&msg->l3h[line_start]; + + if (sscanf(param, "m=audio %d RTP/AVP %d", &port, &payload) == 2) { + endp->net_rtp = htons(port); + endp->net_rtcp = htons(port + 1); + endp->net_payload_type = payload; + } + break; + } + case 'c': { + char ipv4[16]; + const char *param = (const char *)&msg->l3h[line_start]; + + if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) { + inet_aton(ipv4, &endp->remote); + } + break; + } + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, trans_id)) { + case MGCP_POLICY_REJECT: + LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n", + ENDPOINT_NUMBER(endp)); + return create_response(500, "MDCX", trans_id); + break; + case MGCP_POLICY_DEFER: + /* stop processing */ + return NULL; + break; + case MGCP_POLICY_CONT: + /* just continue */ + break; + } + } + + /* modify */ + LOGP(DMGCP, LOGL_NOTICE, "Modified endpoint on: 0x%x Server: %s:%u\n", + ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp)); + if (cfg->change_cb) + cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port); + return create_response_with_sdp(endp, "MDCX", trans_id); + +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]); + return create_response(error_code, "MDCX", trans_id); + +error3: + return create_response(error_code, "MDCX", trans_id); +} + +static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 500; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_response(error_code, "DLCX", trans_id); + + if (endp->ci == CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(error_code, "DLCX", trans_id); + } + + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'C': { + if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'I': { + if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, trans_id)) { + case MGCP_POLICY_REJECT: + LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n", + ENDPOINT_NUMBER(endp)); + return create_response(500, "DLCX", trans_id); + break; + case MGCP_POLICY_DEFER: + /* stop processing */ + return NULL; + break; + case MGCP_POLICY_CONT: + /* just continue */ + break; + } + } + + /* free the connection */ + mgcp_free_endp(endp); + if (cfg->change_cb) + cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port); + + return create_response(250, "DLCX", trans_id); + +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i); + return create_response(error_code, "DLCX", trans_id); + +error3: + return create_response(error_code, "DLCX", trans_id); +} + +struct mgcp_config *mgcp_config_alloc(void) +{ + struct mgcp_config *cfg; + + cfg = talloc_zero(NULL, struct mgcp_config); + if (!cfg) { + LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); + return NULL; + } + + cfg->source_port = 2427; + cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); + cfg->audio_name = talloc_strdup(cfg, "GSM-EFR/8000"); + cfg->audio_payload = 97; + cfg->rtp_base_port = RTP_PORT_DEFAULT; + + return cfg; +} + +int mgcp_endpoints_allocate(struct mgcp_config *cfg) +{ + int i; + + /* Initialize all endpoints */ + cfg->endpoints = _talloc_zero_array(cfg, + sizeof(struct mgcp_endpoint), + cfg->number_endpoints, "endpoints"); + if (!cfg->endpoints) + return -1; + + for (i = 0; i < cfg->number_endpoints; ++i) { + cfg->endpoints[i].local_rtp.fd = -1; + cfg->endpoints[i].local_rtcp.fd = -1; + cfg->endpoints[i].ci = CI_UNUSED; + cfg->endpoints[i].cfg = cfg; + cfg->endpoints[i].net_payload_type = -1; + cfg->endpoints[i].bts_payload_type = -1; + } + + return 0; +} + +void mgcp_free_endp(struct mgcp_endpoint *endp) +{ + LOGP(DMGCP, LOGL_NOTICE, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); + endp->ci= CI_UNUSED; + + if (endp->callid) { + talloc_free(endp->callid); + endp->callid = NULL; + } + + if (endp->local_options) { + talloc_free(endp->local_options); + endp->callid = NULL; + } + + if (!endp->cfg->early_bind) { + bsc_unregister_fd(&endp->local_rtp); + bsc_unregister_fd(&endp->local_rtcp); + } + + endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0; + endp->net_payload_type = endp->bts_payload_type = -1; +} diff --git a/openbsc/src/mgcp/mgcp_vty.c b/openbsc/src/mgcp/mgcp_vty.c new file mode 100644 index 000000000..f13b3cfa7 --- /dev/null +++ b/openbsc/src/mgcp/mgcp_vty.c @@ -0,0 +1,339 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (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 <sys/types.h> + +#include <osmocore/talloc.h> + +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include <vty/command.h> +#include <vty/vty.h> + +static struct mgcp_config *g_cfg = NULL; + +/* + * vty code for mgcp below + */ +struct cmd_node mgcp_node = { + MGCP_NODE, + "%s(mgcp)#", + 1, +}; + +static int config_write_mgcp(struct vty *vty) +{ + vty_out(vty, "mgcp%s", VTY_NEWLINE); + if (g_cfg->local_ip) + vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE); + if (g_cfg->bts_ip) + vty_out(vty, " bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE); + vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE); + vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE); + vty_out(vty, " bind early %u%s", !!g_cfg->early_bind, VTY_NEWLINE); + vty_out(vty, " rtp base %u%s", g_cfg->rtp_base_port, VTY_NEWLINE); + vty_out(vty, " sdp audio payload number %u%s", g_cfg->audio_payload, VTY_NEWLINE); + vty_out(vty, " sdp audio payload name %s%s", g_cfg->audio_name, VTY_NEWLINE); + vty_out(vty, " loop %u%s", !!g_cfg->audio_loop, VTY_NEWLINE); + vty_out(vty, " endpoints %u%s", g_cfg->number_endpoints, VTY_NEWLINE); + if (g_cfg->forward_ip) + vty_out(vty, " forward audio ip %s%s", g_cfg->forward_ip, VTY_NEWLINE); + if (g_cfg->forward_port != 0) + vty_out(vty, " forward audio port %d%s", g_cfg->forward_port, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp", + SHOW_STR "Display information about the MGCP Media Gateway") +{ + int i; + + vty_out(vty, "MGCP is up and running with %u endpoints:%s", g_cfg->number_endpoints - 1, VTY_NEWLINE); + for (i = 1; i < g_cfg->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &g_cfg->endpoints[i]; + vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s", + i, endp->ci, + ntohs(endp->net_rtp), ntohs(endp->net_rtcp), + ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp, + cfg_mgcp_cmd, + "mgcp", + "Configure the MGCP") +{ + vty->node = MGCP_NODE; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_local_ip, + cfg_mgcp_local_ip_cmd, + "local ip IP", + "Set the IP to be used in SDP records") +{ + if (g_cfg->local_ip) + talloc_free(g_cfg->local_ip); + g_cfg->local_ip = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bts_ip, + cfg_mgcp_bts_ip_cmd, + "bts ip IP", + "Set the IP of the BTS for RTP forwarding") +{ + if (g_cfg->bts_ip) + talloc_free(g_cfg->bts_ip); + g_cfg->bts_ip = talloc_strdup(g_cfg, argv[0]); + inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_ip, + cfg_mgcp_bind_ip_cmd, + "bind ip IP", + "Bind the MGCP to this local addr") +{ + if (g_cfg->source_addr) + talloc_free(g_cfg->source_addr); + g_cfg->source_addr = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_port, + cfg_mgcp_bind_port_cmd, + "bind port <0-65534>", + "Bind the MGCP to this port") +{ + unsigned int port = atoi(argv[0]); + if (port > 65534) { + vty_out(vty, "%% wrong bind port '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->source_port = port; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_early, + cfg_mgcp_bind_early_cmd, + "bind early (0|1)", + "Bind all RTP ports early") +{ + unsigned int bind = atoi(argv[0]); + if (bind != 0 && bind != 1) { + vty_out(vty, "%% param must be 0 or 1.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->early_bind = bind == 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_base_port, + cfg_mgcp_rtp_base_port_cmd, + "rtp base <0-65534>", + "Base port to use") +{ + unsigned int port = atoi(argv[0]); + if (port > 65534) { + vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->rtp_base_port = port; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_sdp_payload_number, + cfg_mgcp_sdp_payload_number_cmd, + "sdp audio payload number <1-255>", + "Set the audio codec to use") +{ + unsigned int payload = atoi(argv[0]); + if (payload > 255) { + vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->audio_payload = payload; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_sdp_payload_name, + cfg_mgcp_sdp_payload_name_cmd, + "sdp audio payload name NAME", + "Set the audio name to use") +{ + if (g_cfg->audio_name) + talloc_free(g_cfg->audio_name); + g_cfg->audio_name = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_loop, + cfg_mgcp_loop_cmd, + "loop (0|1)", + "Loop the audio") +{ + g_cfg->audio_loop = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_number_endp, + cfg_mgcp_number_endp_cmd, + "number endpoints <0-65534>", + "The number of endpoints to allocate. This is not dynamic.") +{ + /* + 1 as we start counting at one */ + g_cfg->number_endpoints = atoi(argv[0]) + 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_forward_ip, + cfg_mgcp_forward_ip_cmd, + "forward audio ip IP", + "Forward packets from and to the IP. This disables most of the MGCP feature.") +{ + if (g_cfg->forward_ip) + talloc_free(g_cfg->forward_ip); + g_cfg->forward_ip = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_forward_port, + cfg_mgcp_forward_port_cmd, + "forward audio port <1-15000>", + "Forward packets from and to the port. This disables most of the MGCP feature.") +{ + g_cfg->forward_port = atoi(argv[0]); + return CMD_SUCCESS; +} + +int mgcp_vty_init(void) +{ + install_element(VIEW_NODE, &show_mgcp_cmd); + + install_element(CONFIG_NODE, &cfg_mgcp_cmd); + install_node(&mgcp_node, config_write_mgcp); + install_default(MGCP_NODE); + install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd); + install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd); + install_element(MGCP_NODE, &cfg_mgcp_loop_cmd); + install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd); + install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd); + return 0; +} + +int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg) +{ + int i, rc; + + g_cfg = cfg; + rc = vty_read_config_file(config_file); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + + if (!g_cfg->bts_ip) + fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n"); + + if (mgcp_endpoints_allocate(g_cfg) != 0) { + fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", g_cfg->number_endpoints); + return -1; + } + + /* + * This application supports two modes. + * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX + * 2.) plain forwarding of RTP packets on the endpoints. + * both modes are mutual exclusive + */ + if (g_cfg->forward_ip) { + int port = g_cfg->rtp_base_port; + if (g_cfg->forward_port != 0) + port = g_cfg->forward_port; + + if (!g_cfg->early_bind) { + LOGP(DMGCP, LOGL_NOTICE, "Forwarding requires early bind.\n"); + return -1; + } + + /* + * Store the forward IP and assign a ci. For early bind + * the sockets will be created after this. + */ + for (i = 1; i < g_cfg->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &g_cfg->endpoints[i]; + inet_aton(g_cfg->forward_ip, &endp->remote); + endp->ci = CI_UNUSED + 23; + endp->net_rtp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port)); + endp->net_rtcp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port) + 1); + } + + LOGP(DMGCP, LOGL_NOTICE, "Configured for Audio Forwarding.\n"); + } + + /* early bind */ + if (g_cfg->early_bind) { + for (i = 1; i < g_cfg->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &g_cfg->endpoints[i]; + int rtp_port; + + rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), g_cfg->rtp_base_port); + if (mgcp_bind_rtp_port(endp, rtp_port) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); + return -1; + } + } + } + + return !!g_cfg->forward_ip; +} + +struct gsm_network; +int bsc_vty_init(struct gsm_network *dummy) +{ + cmd_init(1); + vty_init(); + + mgcp_vty_init(); + return 0; +} + diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c new file mode 100644 index 000000000..01d59aad1 --- /dev/null +++ b/openbsc/src/mncc.c @@ -0,0 +1,468 @@ +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de> + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> + +#include <openbsc/gsm_04_08.h> +#include <openbsc/debug.h> +#include <openbsc/mncc.h> +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> +#include <openbsc/transaction.h> +#include <openbsc/rtp_proxy.h> + +void *tall_call_ctx; + +static struct mncc_names { + char *name; + int value; +} mncc_names[] = { + {"MNCC_SETUP_REQ", 0x0101}, + {"MNCC_SETUP_IND", 0x0102}, + {"MNCC_SETUP_RSP", 0x0103}, + {"MNCC_SETUP_CNF", 0x0104}, + {"MNCC_SETUP_COMPL_REQ",0x0105}, + {"MNCC_SETUP_COMPL_IND",0x0106}, + {"MNCC_CALL_CONF_IND", 0x0107}, + {"MNCC_CALL_PROC_REQ", 0x0108}, + {"MNCC_PROGRESS_REQ", 0x0109}, + {"MNCC_ALERT_REQ", 0x010a}, + {"MNCC_ALERT_IND", 0x010b}, + {"MNCC_NOTIFY_REQ", 0x010c}, + {"MNCC_NOTIFY_IND", 0x010d}, + {"MNCC_DISC_REQ", 0x010e}, + {"MNCC_DISC_IND", 0x010f}, + {"MNCC_REL_REQ", 0x0110}, + {"MNCC_REL_IND", 0x0111}, + {"MNCC_REL_CNF", 0x0112}, + {"MNCC_FACILITY_REQ", 0x0113}, + {"MNCC_FACILITY_IND", 0x0114}, + {"MNCC_START_DTMF_IND", 0x0115}, + {"MNCC_START_DTMF_RSP", 0x0116}, + {"MNCC_START_DTMF_REJ", 0x0117}, + {"MNCC_STOP_DTMF_IND", 0x0118}, + {"MNCC_STOP_DTMF_RSP", 0x0119}, + {"MNCC_MODIFY_REQ", 0x011a}, + {"MNCC_MODIFY_IND", 0x011b}, + {"MNCC_MODIFY_RSP", 0x011c}, + {"MNCC_MODIFY_CNF", 0x011d}, + {"MNCC_MODIFY_REJ", 0x011e}, + {"MNCC_HOLD_IND", 0x011f}, + {"MNCC_HOLD_CNF", 0x0120}, + {"MNCC_HOLD_REJ", 0x0121}, + {"MNCC_RETRIEVE_IND", 0x0122}, + {"MNCC_RETRIEVE_CNF", 0x0123}, + {"MNCC_RETRIEVE_REJ", 0x0124}, + {"MNCC_USERINFO_REQ", 0x0125}, + {"MNCC_USERINFO_IND", 0x0126}, + {"MNCC_REJ_REQ", 0x0127}, + {"MNCC_REJ_IND", 0x0128}, + + {"MNCC_BRIDGE", 0x0200}, + {"MNCC_FRAME_RECV", 0x0201}, + {"MNCC_FRAME_DROP", 0x0202}, + {"MNCC_LCHAN_MODIFY", 0x0203}, + + {"GSM_TCH_FRAME", 0x0300}, + + {NULL, 0} }; + +static LLIST_HEAD(call_list); + +static u_int32_t new_callref = 0x00000001; + +char *get_mncc_name(int value) +{ + int i; + + for (i = 0; mncc_names[i].name; i++) { + if (mncc_names[i].value == value) + return mncc_names[i].name; + } + + return "MNCC_Unknown"; +} + +static void free_call(struct gsm_call *call) +{ + llist_del(&call->entry); + DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref); + talloc_free(call); +} + + +struct gsm_call *get_call_ref(u_int32_t callref) +{ + struct gsm_call *callt; + + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) + return callt; + } + return NULL; +} + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val) +{ + data->fields |= MNCC_F_CAUSE; + data->cause.location = loc; + data->cause.value = val; +} + +/* on incoming call, look up database and send setup to remote subscr. */ +static int mncc_setup_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *setup) +{ + struct gsm_mncc mncc; + struct gsm_call *remote; + + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + + /* already have remote call */ + if (call->remote_ref) + return 0; + + /* transfer mode 1 would be packet mode, which was never specified */ + if (setup->bearer_cap.mode != 0) { + LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " + "packet mode\n", call->callref); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + goto out_reject; + } + + /* we currently only do speech */ + if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { + LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " + "voice calls\n", call->callref); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + goto out_reject; + } + + /* create remote call */ + if (!(remote = talloc(tall_call_ctx, struct gsm_call))) { + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + goto out_reject; + } + llist_add_tail(&remote->entry, &call_list); + remote->net = call->net; + remote->callref = new_callref++; + DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", + call->callref, remote->callref); + + /* link remote call */ + call->remote_ref = remote->callref; + remote->remote_ref = call->callref; + + /* modify mode */ + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR; + DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); + mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc); + + /* send call proceeding */ + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); + mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc); + + /* send setup to remote */ +// setup->fields |= MNCC_F_SIGNAL; +// setup->signal = GSM48_SIGNAL_DIALTONE; + setup->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref); + return mncc_send(remote->net, MNCC_SETUP_REQ, setup); + +out_reject: + mncc_send(call->net, MNCC_REJ_REQ, &mncc); + free_call(call); + return 0; +} + +static int mncc_alert_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *alert) +{ + struct gsm_call *remote; + + /* send alerting to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + alert->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref); + return mncc_send(remote->net, MNCC_ALERT_REQ, alert); +} + +static int mncc_notify_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *notify) +{ + struct gsm_call *remote; + + /* send notify to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + notify->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref); + return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify); +} + +static int mncc_setup_cnf(struct gsm_call *call, int msg_type, + struct gsm_mncc *connect) +{ + struct gsm_mncc connect_ack, frame_recv; + struct gsm_network *net = call->net; + struct gsm_call *remote; + u_int32_t refs[2]; + + /* acknowledge connect */ + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = call->callref; + DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); + mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); + + /* send connect message to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + connect->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); + mncc_send(remote->net, MNCC_SETUP_RSP, connect); + + /* bridge tch */ + refs[0] = call->callref; + refs[1] = call->remote_ref; + DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); + + /* in direct mode, we always have to bridge the channels */ + if (ipacc_rtp_direct) + return mncc_send(call->net, MNCC_BRIDGE, refs); + + /* proxy mode */ + if (!net->handover.active) { + /* in the no-handover case, we can bridge, i.e. use + * the old RTP proxy code */ + return mncc_send(call->net, MNCC_BRIDGE, refs); + } else { + /* in case of handover, we need to re-write the RTP + * SSRC, sequence and timestamp values and thus + * need to enable RTP receive for both directions */ + memset(&frame_recv, 0, sizeof(struct gsm_mncc)); + frame_recv.callref = call->callref; + mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv); + frame_recv.callref = call->remote_ref; + return mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv); + } +} + +static int mncc_disc_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *disc) +{ + struct gsm_call *remote; + + /* send release */ + DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", + call->callref, disc->cause.value); + mncc_send(call->net, MNCC_REL_REQ, disc); + + /* send disc to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + return 0; + } + disc->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", + remote->callref, disc->cause.value); + return mncc_send(remote->net, MNCC_DISC_REQ, disc); +} + +static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + struct gsm_call *remote; + + /* send release to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + free_call(call); + return 0; + } + rel->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", + call->callref, rel->cause.value); + mncc_send(remote->net, MNCC_REL_REQ, rel); + + free_call(call); + + return 0; +} + +static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + free_call(call); + return 0; +} + +/* receiving a TCH/F frame from the BSC code */ +static int mncc_rcv_tchf(struct gsm_call *call, int msg_type, + struct gsm_data_frame *dfr) +{ + struct gsm_trans *remote_trans; + + remote_trans = trans_find_by_callref(call->net, call->remote_ref); + + /* this shouldn't really happen */ + if (!remote_trans || !remote_trans->lchan) { + LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n"); + return -EIO; + } + + /* RTP socket of remote end has meanwhile died */ + if (!remote_trans->lchan->abis_ip.rtp_socket) + return -EIO; + + return rtp_send_frame(remote_trans->lchan->abis_ip.rtp_socket, dfr); +} + + +int mncc_recv(struct gsm_network *net, int msg_type, void *arg) +{ + struct gsm_mncc *data = arg; + int callref; + struct gsm_call *call = NULL, *callt; + int rc = 0; + + /* Special messages */ + switch(msg_type) { + } + + /* find callref */ + callref = data->callref; + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) { + call = callt; + break; + } + } + + /* create callref, if setup is received */ + if (!call) { + if (msg_type != MNCC_SETUP_IND) + return 0; /* drop */ + /* create call */ + if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) { + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = callref; + mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + mncc_send(net, MNCC_REL_REQ, &rel); + return 0; + } + llist_add_tail(&call->entry, &call_list); + call->net = net; + call->callref = callref; + DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref); + } + + switch (msg_type) { + case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: + break; + default: + DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, + get_mncc_name(msg_type)); + break; + } + + switch(msg_type) { + case MNCC_SETUP_IND: + rc = mncc_setup_ind(call, msg_type, arg); + break; + case MNCC_SETUP_CNF: + rc = mncc_setup_cnf(call, msg_type, arg); + break; + case MNCC_SETUP_COMPL_IND: + break; + case MNCC_CALL_CONF_IND: + /* we now need to MODIFY the channel */ + data->lchan_mode = GSM48_CMODE_SPEECH_EFR; + mncc_send(call->net, MNCC_LCHAN_MODIFY, data); + break; + case MNCC_ALERT_IND: + rc = mncc_alert_ind(call, msg_type, arg); + break; + case MNCC_NOTIFY_IND: + rc = mncc_notify_ind(call, msg_type, arg); + break; + case MNCC_DISC_IND: + rc = mncc_disc_ind(call, msg_type, arg); + break; + case MNCC_REL_IND: + case MNCC_REJ_IND: + rc = mncc_rel_ind(call, msg_type, arg); + break; + case MNCC_REL_CNF: + rc = mncc_rel_cnf(call, msg_type, arg); + break; + case MNCC_FACILITY_IND: + break; + case MNCC_START_DTMF_IND: + break; + case MNCC_STOP_DTMF_IND: + break; + case MNCC_MODIFY_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", + call->callref, data->cause.value); + rc = mncc_send(net, MNCC_MODIFY_REJ, data); + break; + case MNCC_MODIFY_CNF: + break; + case MNCC_HOLD_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", + call->callref, data->cause.value); + rc = mncc_send(net, MNCC_HOLD_REJ, data); + break; + case MNCC_RETRIEVE_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", + call->callref, data->cause.value); + rc = mncc_send(net, MNCC_RETRIEVE_REJ, data); + break; + case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: + rc = mncc_rcv_tchf(call, msg_type, arg); + break; + default: + LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); + break; + } + + return rc; +} diff --git a/openbsc/src/openbsc.cfg.1-1 b/openbsc/src/openbsc.cfg.1-1 new file mode 100644 index 000000000..a25804f63 --- /dev/null +++ b/openbsc/src/openbsc.cfg.1-1 @@ -0,0 +1,54 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type bs11 + band GSM900 + cell_identity 1 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 121 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 3 + diff --git a/openbsc/src/openbsc.cfg.1-2 b/openbsc/src/openbsc.cfg.1-2 new file mode 100644 index 000000000..84d50c75c --- /dev/null +++ b/openbsc/src/openbsc.cfg.1-2 @@ -0,0 +1,82 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type bs11 + band GSM900 + cell_identity 1 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 121 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config SDCCH8 + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 3 + trx 1 + arfcn 123 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 3 diff --git a/openbsc/src/openbsc.cfg.2-2 b/openbsc/src/openbsc.cfg.2-2 new file mode 100644 index 000000000..9ae800342 --- /dev/null +++ b/openbsc/src/openbsc.cfg.2-2 @@ -0,0 +1,148 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type bs11 + band GSM900 + cell_identity 1 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 121 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 3 + trx 1 + arfcn 123 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 3 + bts 1 + type bs11 + band GSM900 + location_area_code 2 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 1 timeslot 6 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 122 + max_power_red 0 + rsl e1 line 1 timeslot 6 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 1 timeslot 7 sub-slot 0 + timeslot 1 + phys_chan_config SDCCH8 + e1 line 1 timeslot 7 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 1 timeslot 7 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 1 timeslot 7 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 3 + trx 1 + arfcn 124 + max_power_red 0 + rsl e1 line 1 timeslot 6 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 3 diff --git a/openbsc/src/openbsc.cfg.nanobts b/openbsc/src/openbsc.cfg.nanobts new file mode 100644 index 000000000..a1ceaec79 --- /dev/null +++ b/openbsc/src/openbsc.cfg.nanobts @@ -0,0 +1,40 @@ +! +! OpenBSC configuration saved from vty +! +password foo +! +line vty + no login +! +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type nanobts + ip.access unit_id 1801 0 + band GSM1800 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + trx 0 + arfcn 514 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + timeslot 1 + phys_chan_config SDCCH8 + timeslot 2 + phys_chan_config TCH/F + timeslot 3 + phys_chan_config TCH/F + timeslot 4 + phys_chan_config TCH/F + timeslot 5 + phys_chan_config TCH/F + timeslot 6 + phys_chan_config TCH/F + timeslot 7 + phys_chan_config TCH/F diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c new file mode 100644 index 000000000..7c3750d66 --- /dev/null +++ b/openbsc/src/paging.c @@ -0,0 +1,339 @@ +/* Paging helper and manager.... */ +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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. + * + */ + +/* + * Relevant specs: + * 12.21: + * - 9.4.12 for CCCH Local Threshold + * + * 05.58: + * - 8.5.2 CCCH Load indication + * - 9.3.15 Paging Load + * + * Approach: + * - Send paging command to subscriber + * - On Channel Request we will remember the reason + * - After the ACK we will request the identity + * - Then we will send assign the gsm_subscriber and + * - and call a callback + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include <openbsc/paging.h> +#include <osmocore/talloc.h> +#include <openbsc/debug.h> +#include <openbsc/signal.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/gsm_data.h> + +void *tall_paging_ctx; + +static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *subscr) +{ + int ccch_conf; + int bs_cc_chans; + int blocks; + unsigned int group; + + ccch_conf = bts->si_common.chan_desc.ccch_conf; + bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf); + /* code word + 2, as 2 channels equals 0x0 */ + blocks = rsl_number_of_paging_subchannels(bts); + group = get_paging_group(str_to_imsi(subscr->imsi), + bs_cc_chans, blocks); + return group; +} + +/* + * Kill one paging request update the internal list... + */ +static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, + struct gsm_paging_request *to_be_deleted) +{ + /* Update the last_request if that is necessary */ + if (to_be_deleted == paging_bts->last_request) { + paging_bts->last_request = + (struct gsm_paging_request *)paging_bts->last_request->entry.next; + if (&to_be_deleted->entry == &paging_bts->pending_requests) + paging_bts->last_request = NULL; + } + + bsc_del_timer(&to_be_deleted->T3113); + llist_del(&to_be_deleted->entry); + subscr_put(to_be_deleted->subscr); + talloc_free(to_be_deleted); +} + +static void page_ms(struct gsm_paging_request *request) +{ + u_int8_t mi[128]; + unsigned int mi_len; + unsigned int page_group; + + DEBUGP(DPAG, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", + request->subscr->imsi, request->subscr->tmsi); + + if (request->subscr->tmsi == GSM_RESERVED_TMSI) + mi_len = gsm48_generate_mid_from_imsi(mi, request->subscr->imsi); + else + mi_len = gsm48_generate_mid_from_tmsi(mi, request->subscr->tmsi); + + page_group = calculate_group(request->bts, request->subscr); + rsl_paging_cmd(request->bts, page_group, mi_len, mi, + request->chan_type); +} + +static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts) +{ + paging_bts->last_request = + (struct gsm_paging_request *)paging_bts->last_request->entry.next; + if (&paging_bts->last_request->entry == &paging_bts->pending_requests) + paging_bts->last_request = NULL; +} + +/* + * This is kicked by the periodic PAGING LOAD Indicator + * coming from abis_rsl.c + * + * We attempt to iterate once over the list of items but + * only upto available_slots. + */ +static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) +{ + struct gsm_paging_request *initial_request = NULL; + struct gsm_paging_request *current_request = NULL; + + /* + * Determine if the pending_requests list is empty and + * return then. + */ + if (llist_empty(&paging_bts->pending_requests)) { + paging_bts->last_request = NULL; + /* since the list is empty, no need to reschedule the timer */ + return; + } + + if (!paging_bts->last_request) + paging_bts->last_request = + (struct gsm_paging_request *)paging_bts->pending_requests.next; + + assert(paging_bts->last_request); + initial_request = paging_bts->last_request; + current_request = initial_request; + + do { + /* handle the paging request now */ + page_ms(current_request); + paging_bts->available_slots--; + + /* + * move to the next item. We might wrap around + * this means last_request will be NULL and we just + * call paging_page_to_next again. It it guranteed + * that the list is not empty. + */ + paging_move_to_next(paging_bts); + if (!paging_bts->last_request) + paging_bts->last_request = + (struct gsm_paging_request *)paging_bts->pending_requests.next; + current_request = paging_bts->last_request; + } while (paging_bts->available_slots > 0 + && initial_request != current_request); + + bsc_schedule_timer(&paging_bts->work_timer, 1, 0); +} + +static void paging_worker(void *data) +{ + struct gsm_bts_paging_state *paging_bts = data; + + paging_handle_pending_requests(paging_bts); +} + +void paging_init(struct gsm_bts *bts) +{ + bts->paging.bts = bts; + INIT_LLIST_HEAD(&bts->paging.pending_requests); + bts->paging.work_timer.cb = paging_worker; + bts->paging.work_timer.data = &bts->paging; + + /* Large number, until we get a proper message */ + bts->paging.available_slots = 100; +} + +static int paging_pending_request(struct gsm_bts_paging_state *bts, + struct gsm_subscriber *subscr) { + struct gsm_paging_request *req; + + llist_for_each_entry(req, &bts->pending_requests, entry) { + if (subscr == req->subscr) + return 1; + } + + return 0; +} + +static void paging_T3113_expired(void *data) +{ + struct gsm_paging_request *req = (struct gsm_paging_request *)data; + struct paging_signal_data sig_data; + void *cbfn_param; + gsm_cbfn *cbfn; + + DEBUGP(DPAG, "T3113 expired for request %p (%s)\n", + req, req->subscr->imsi); + + sig_data.subscr = req->subscr; + sig_data.bts = req->bts; + sig_data.lchan = NULL; + + /* must be destroyed before calling cbfn, to prevent double free */ + cbfn_param = req->cbfn_param; + cbfn = req->cbfn; + paging_remove_request(&req->bts->paging, req); + + counter_inc(req->bts->network->stats.paging.expired); + + dispatch_signal(SS_PAGING, S_PAGING_EXPIRED, &sig_data); + if (cbfn) + cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL, + cbfn_param); +} + +static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data) +{ + struct gsm_bts_paging_state *bts_entry = &bts->paging; + struct gsm_paging_request *req; + + if (paging_pending_request(bts_entry, subscr)) { + DEBUGP(DPAG, "Paging request already pending\n"); + return -EEXIST; + } + + DEBUGP(DPAG, "Start paging of subscriber %llu on bts %d.\n", + subscr->id, bts->nr); + req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); + req->subscr = subscr_get(subscr); + req->bts = bts; + req->chan_type = type; + req->cbfn = cbfn; + req->cbfn_param = data; + req->T3113.cb = paging_T3113_expired; + req->T3113.data = req; + bsc_schedule_timer(&req->T3113, bts->network->T3113, 0); + llist_add_tail(&req->entry, &bts_entry->pending_requests); + + if (!bsc_timer_pending(&bts_entry->work_timer)) + bsc_schedule_timer(&bts_entry->work_timer, 1, 0); + + return 0; +} + +int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data) +{ + struct gsm_bts *bts = NULL; + int num_pages = 0; + + counter_inc(network->stats.paging.attempted); + + /* start paging subscriber on all BTS within Location Area */ + do { + int rc; + + bts = gsm_bts_by_lac(network, subscr->lac, bts); + if (!bts) + break; + + /* skip all currently inactive TRX */ + if (!trx_is_usable(bts->c0)) + continue; + + num_pages++; + + /* Trigger paging, pass any error to caller */ + rc = _paging_request(bts, subscr, type, cbfn, data); + if (rc < 0) + return rc; + } while (1); + + if (num_pages == 0) + counter_inc(network->stats.paging.detached); + + return num_pages; +} + + +/* we consciously ignore the type of the request here */ +static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, + struct gsm_lchan *lchan) +{ + struct gsm_bts_paging_state *bts_entry = &bts->paging; + struct gsm_paging_request *req, *req2; + + llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, + entry) { + if (req->subscr == subscr) { + if (lchan && req->cbfn) { + DEBUGP(DPAG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); + req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, + NULL, lchan, req->cbfn_param); + } else + DEBUGP(DPAG, "Stop paging on bts %d silently.\n", bts->nr); + paging_remove_request(&bts->paging, req); + break; + } + } +} + +/* Stop paging on all other bts' */ +void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr, + struct gsm_lchan *lchan) +{ + struct gsm_bts *bts = NULL; + + if (_bts) + _paging_request_stop(_bts, subscr, lchan); + + do { + /* + * FIXME: Don't use the lac of the subscriber... + * as it might have magically changed the lac.. use the + * location area of the _bts as reconfiguration of the + * network is probably happening less often. + */ + bts = gsm_bts_by_lac(subscr->net, subscr->lac, bts); + if (!bts) + break; + + /* Stop paging */ + if (bts != _bts) + _paging_request_stop(bts, subscr, NULL); + } while (1); +} + +void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots) +{ + bts->paging.available_slots = free_slots; +} diff --git a/openbsc/src/rest_octets.c b/openbsc/src/rest_octets.c new file mode 100644 index 000000000..16996cec2 --- /dev/null +++ b/openbsc/src/rest_octets.c @@ -0,0 +1,396 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, + * rest octet handling according to + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2009 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 <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/bitvec.h> +#include <openbsc/rest_octets.h> + +/* generate SI1 rest octets */ +int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 1; + + if (nch_pos) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, *nch_pos, 5); + } else + bitvec_set_bit(&bv, L); + + bitvec_spare_padding(&bv, 7); + return bv.data_len; +} + +/* Append selection parameters to bitvec */ +static void append_selection_params(struct bitvec *bv, + const struct gsm48_si_selection_params *sp) +{ + if (sp->present) { + bitvec_set_bit(bv, H); + bitvec_set_bit(bv, sp->cbq); + bitvec_set_uint(bv, sp->cell_resel_off, 6); + bitvec_set_uint(bv, sp->temp_offs, 3); + bitvec_set_uint(bv, sp->penalty_time, 5); + } else + bitvec_set_bit(bv, L); +} + +/* Append power offset to bitvec */ +static void append_power_offset(struct bitvec *bv, + const struct gsm48_si_power_offset *po) +{ + if (po->present) { + bitvec_set_bit(bv, H); + bitvec_set_uint(bv, po->power_offset, 2); + } else + bitvec_set_bit(bv, L); +} + +/* Append GPRS indicator to bitvec */ +static void append_gprs_ind(struct bitvec *bv, + const struct gsm48_si3_gprs_ind *gi) +{ + if (gi->present) { + bitvec_set_bit(bv, H); + bitvec_set_uint(bv, gi->ra_colour, 3); + /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */ + bitvec_set_bit(bv, gi->si13_position); + } else + bitvec_set_bit(bv, L); +} + + +/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ +int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 4; + + /* Optional Selection Parameters */ + append_selection_params(&bv, &si3->selection_params); + + /* Optional Power Offset */ + append_power_offset(&bv, &si3->power_offset); + + /* Do we have a SI2ter on the BCCH? */ + if (si3->si2ter_indicator) + bitvec_set_bit(&bv, H); + else + bitvec_set_bit(&bv, L); + + /* Early Classmark Sending Control */ + if (si3->early_cm_ctrl) + bitvec_set_bit(&bv, H); + else + bitvec_set_bit(&bv, L); + + /* Do we have a SI Type 9 on the BCCH? */ + if (si3->scheduling.present) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, si3->scheduling.where, 3); + } else + bitvec_set_bit(&bv, L); + + /* GPRS Indicator */ + append_gprs_ind(&bv, &si3->gprs_ind); + + bitvec_spare_padding(&bv, (bv.data_len*8)-1); + return bv.data_len; +} + +static int append_lsa_params(struct bitvec *bv, + const struct gsm48_lsa_params *lsa_params) +{ + /* FIXME */ +} + +/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ +int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 10; /* FIXME: up to ? */ + + /* SI4 Rest Octets O */ + append_selection_params(&bv, &si4->selection_params); + append_power_offset(&bv, &si4->power_offset); + append_gprs_ind(&bv, &si4->gprs_ind); + + if (0 /* FIXME */) { + /* H and SI4 Rest Octets S */ + bitvec_set_bit(&bv, H); + + /* LSA Parameters */ + if (si4->lsa_params.present) { + bitvec_set_bit(&bv, H); + append_lsa_params(&bv, &si4->lsa_params); + } else + bitvec_set_bit(&bv, L); + + /* Cell Identity */ + if (1) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, si4->cell_id, 16); + } else + bitvec_set_bit(&bv, L); + + /* LSA ID Information */ + if (0) { + bitvec_set_bit(&bv, H); + /* FIXME */ + } else + bitvec_set_bit(&bv, L); + } else { + /* L and break indicator */ + bitvec_set_bit(&bv, L); + bitvec_set_bit(&bv, si4->break_ind ? H : L); + } + + return bv.data_len; +} + +/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: + < GPRS Mobile Allocation IE > ::= + < HSN : bit (6) > + { 0 | 1 < RFL number list : < RFL number list struct > > } + { 0 < MA_LENGTH : bit (6) > + < MA_BITMAP: bit (val(MA_LENGTH) + 1) > + | 1 { 0 | 1 <ARFCN index list : < ARFCN index list struct > > } } ; + + < RFL number list struct > :: = + < RFL_NUMBER : bit (4) > + { 0 | 1 < RFL number list struct > } ; + < ARFCN index list struct > ::= + < ARFCN_INDEX : bit(6) > + { 0 | 1 < ARFCN index list struct > } ; + */ +static int append_gprs_mobile_alloc(struct bitvec *bv) +{ + /* Hopping Sequence Number */ + bitvec_set_uint(bv, 0, 6); + + if (0) { + /* We want to use a RFL number list */ + bitvec_set_bit(bv, 1); + /* FIXME: RFL number list */ + } else + bitvec_set_bit(bv, 0); + + if (0) { + /* We want to use a MA_BITMAP */ + bitvec_set_bit(bv, 0); + /* FIXME: MA_LENGTH, MA_BITMAP, ... */ + } else { + bitvec_set_bit(bv, 1); + if (0) { + /* We want to provide an ARFCN index list */ + bitvec_set_bit(bv, 1); + /* FIXME */ + } else + bitvec_set_bit(bv, 0); + } + return 0; +} + +static int encode_t3192(unsigned int t3192) +{ + if (t3192 == 0) + return 3; + else if (t3192 <= 80) + return 4; + else if (t3192 <= 120) + return 5; + else if (t3192 <= 160) + return 6; + else if (t3192 <= 200) + return 7; + else if (t3192 <= 500) + return 0; + else if (t3192 <= 1000) + return 1; + else if (t3192 <= 1500) + return 2; + else + return -EINVAL; +} + +static int encode_drx_timer(unsigned int drx) +{ + if (drx == 0) + return 0; + else if (drx == 1) + return 1; + else if (drx == 2) + return 2; + else if (drx <= 4) + return 3; + else if (drx <= 8) + return 4; + else if (drx <= 16) + return 5; + else if (drx <= 32) + return 6; + else if (drx <= 64) + return 7; + else + return -EINVAL; +} + +/* GPRS Cell Options as per TS 04.60 Chapter 12.24 + < GPRS Cell Options IE > ::= + < NMO : bit(2) > + < T3168 : bit(3) > + < T3192 : bit(3) > + < DRX_TIMER_MAX: bit(3) > + < ACCESS_BURST_TYPE: bit > + < CONTROL_ACK_TYPE : bit > + < BS_CV_MAX: bit(4) > + { 0 | 1 < PAN_DEC : bit(3) > + < PAN_INC : bit(3) > + < PAN_MAX : bit(3) > + { 0 | 1 < Extension Length : bit(6) > + < bit (val(Extension Length) + 1 + & { < Extension Information > ! { bit ** = <no string> } } ; + < Extension Information > ::= + { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > + < BEP_PERIOD : bit(4) > } + < PFC_FEATURE_MODE : bit > + < DTM_SUPPORT : bit > + <BSS_PAGING_COORDINATION: bit > + <spare bit > ** ; + */ +static int append_gprs_cell_opt(struct bitvec *bv, + const struct gprs_cell_options *gco) +{ + int t3192, drx_timer_max; + + t3192 = encode_t3192(gco->t3192); + if (t3192 < 0) + return t3192; + + drx_timer_max = encode_drx_timer(gco->drx_timer_max); + if (drx_timer_max < 0) + return drx_timer_max; + + bitvec_set_uint(bv, gco->nmo, 2); + bitvec_set_uint(bv, gco->t3168 / 500, 3); + bitvec_set_uint(bv, t3192, 3); + bitvec_set_uint(bv, drx_timer_max, 3); + /* ACCESS_BURST_TYPE: Hard-code 8bit */ + bitvec_set_bit(bv, 0); + /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */ + bitvec_set_bit(bv, 1); + bitvec_set_uint(bv, gco->bs_cv_max, 4); + + /* hard-code no PAN_{DEC,INC,MAX} */ + bitvec_set_bit(bv, 0); + + /* no extension information (EDGE) */ + bitvec_set_bit(bv, 0); + + return 0; +} + +static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, + const struct gprs_power_ctrl_pars *pcp) +{ + bitvec_set_uint(bv, pcp->alpha, 4); + bitvec_set_uint(bv, pcp->t_avg_w, 5); + bitvec_set_uint(bv, pcp->t_avg_t, 5); + bitvec_set_uint(bv, pcp->pc_meas_chan, 1); + bitvec_set_uint(bv, pcp->n_avg_i, 4); +} + +/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ +int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 20; + + if (0) { + /* No rest octets */ + bitvec_set_bit(&bv, L); + } else { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, si13->bcch_change_mark, 3); + bitvec_set_uint(&bv, si13->si_change_field, 4); + if (1) { + bitvec_set_bit(&bv, 0); + } else { + bitvec_set_bit(&bv, 1); + bitvec_set_uint(&bv, si13->bcch_change_mark, 2); + append_gprs_mobile_alloc(&bv); + } + if (!si13->pbcch_present) { + /* PBCCH not present in cell */ + bitvec_set_bit(&bv, 0); + bitvec_set_uint(&bv, si13->no_pbcch.rac, 8); + bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup); + bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3); + bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2); + append_gprs_cell_opt(&bv, &si13->cell_opts); + append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars); + } else { + /* PBCCH present in cell */ + bitvec_set_bit(&bv, 1); + bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4); + /* PBCCH Descripiton */ + bitvec_set_uint(&bv, si13->pbcch.pb, 4); + bitvec_set_uint(&bv, si13->pbcch.tsc, 3); + bitvec_set_uint(&bv, si13->pbcch.tn, 3); + switch (si13->pbcch.carrier_type) { + case PBCCH_BCCH: + bitvec_set_bit(&bv, 0); + bitvec_set_bit(&bv, 0); + break; + case PBCCH_ARFCN: + bitvec_set_bit(&bv, 0); + bitvec_set_bit(&bv, 1); + bitvec_set_uint(&bv, si13->pbcch.arfcn, 10); + break; + case PBCCH_MAIO: + bitvec_set_bit(&bv, 1); + bitvec_set_uint(&bv, si13->pbcch.maio, 6); + break; + } + } + } + bitvec_spare_padding(&bv, (bv.data_len*8)-1); + return bv.data_len; +} diff --git a/openbsc/src/rrlp.c b/openbsc/src/rrlp.c new file mode 100644 index 000000000..35044518c --- /dev/null +++ b/openbsc/src/rrlp.c @@ -0,0 +1,106 @@ +/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ + +/* (C) 2009 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 <sys/types.h> + +#include <openbsc/gsm_04_08.h> +#include <openbsc/signal.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/chan_alloc.h> + +/* RRLP msPositionReq, nsBased, + * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ +static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; + +/* RRLP msPositionReq, msBasedPref, + Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ +static const u_int8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; + +/* RRLP msPositionReq, msAssistedPref, + Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ +static const u_int8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; + +static int send_rrlp_req(struct gsm_lchan *lchan) +{ + struct gsm_network *net = lchan->ts->trx->bts->network; + const u_int8_t *req; + + switch (net->rrlp.mode) { + case RRLP_MODE_MS_BASED: + req = ms_based_pos_req; + break; + case RRLP_MODE_MS_PREF: + req = ms_pref_pos_req; + break; + case RRLP_MODE_ASS_PREF: + req = ass_pref_pos_req; + break; + case RRLP_MODE_NONE: + default: + return 0; + } + + return gsm48_send_rr_app_info(lchan, 0x00, + sizeof(ms_based_pos_req), req); +} + +static int subscr_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_subscriber *subscr; + struct gsm_lchan *lchan; + + switch (signal) { + case S_SUBSCR_ATTACHED: + /* A subscriber has attached. */ + subscr = signal_data; + lchan = lchan_for_subscr(subscr); + if (!lchan) + break; + send_rrlp_req(lchan); + break; + } + return 0; +} + +static int paging_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct paging_signal_data *psig_data = signal_data; + + switch (signal) { + case S_PAGING_SUCCEEDED: + /* A subscriber has attached. */ + send_rrlp_req(psig_data->lchan); + break; + case S_PAGING_EXPIRED: + break; + } + return 0; +} + +void on_dso_load_rrlp(void) +{ + register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL); + register_signal_handler(SS_PAGING, paging_sig_cb, NULL); +} diff --git a/openbsc/src/rs232.c b/openbsc/src/rs232.c new file mode 100644 index 000000000..36af59cbf --- /dev/null +++ b/openbsc/src/rs232.c @@ -0,0 +1,249 @@ +/* OpenBSC BS-11 T-Link interface using POSIX serial port */ + +/* (C) 2008-2009 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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <termios.h> +#include <fcntl.h> + +#include <osmocore/select.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/rs232.h> + +/* adaption layer from GSM 08.59 + 12.21 to RS232 */ + +struct serial_handle { + struct bsc_fd fd; + struct llist_head tx_queue; + + struct msgb *rx_msg; + unsigned int rxmsg_bytes_missing; + + unsigned int delay_ms; + struct gsm_bts *bts; +}; + +/* FIXME: this needs to go */ +static struct serial_handle _ser_handle, *ser_handle = &_ser_handle; + +#define LAPD_HDR_LEN 10 + +static int handle_ser_write(struct bsc_fd *bfd); + +/* callback from abis_nm */ +int _abis_nm_sendmsg(struct msgb *msg) +{ + struct serial_handle *sh = ser_handle; + u_int8_t *lapd; + unsigned int len; + + msg->l2h = msg->data; + + /* prepend LAPD header */ + lapd = msgb_push(msg, LAPD_HDR_LEN); + + len = msg->len - 2; + + lapd[0] = (len >> 8) & 0xff; + lapd[1] = len & 0xff; /* length of bytes startign at lapd[2] */ + lapd[2] = 0x00; + lapd[3] = 0x07; + lapd[4] = 0x01; + lapd[5] = 0x3e; + lapd[6] = 0x00; + lapd[7] = 0x00; + lapd[8] = msg->len - 10; /* length of bytes starting at lapd[10] */ + lapd[9] = lapd[8] ^ 0x38; + + msgb_enqueue(&sh->tx_queue, msg); + sh->fd.when |= BSC_FD_WRITE; + + /* we try to immediately send */ + handle_ser_write(&sh->fd); + + return 0; +} + +/* select.c callback in case we can write to the RS232 */ +static int handle_ser_write(struct bsc_fd *bfd) +{ + struct serial_handle *sh = bfd->data; + struct msgb *msg; + int written; + + msg = msgb_dequeue(&sh->tx_queue); + if (!msg) { + bfd->when &= ~BSC_FD_WRITE; + return 0; + } + + DEBUGP(DMI, "RS232 TX: %s\n", hexdump(msg->data, msg->len)); + + /* send over serial line */ + written = write(bfd->fd, msg->data, msg->len); + if (written < msg->len) { + perror("short write:"); + msgb_free(msg); + return -1; + } + + msgb_free(msg); + usleep(sh->delay_ms*1000); + + return 0; +} + +#define SERIAL_ALLOC_SIZE 300 + +/* select.c callback in case we can read from the RS232 */ +static int handle_ser_read(struct bsc_fd *bfd) +{ + struct serial_handle *sh = bfd->data; + struct msgb *msg; + int rc = 0; + + if (!sh->rx_msg) { + sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE, "RS232 Rx"); + sh->rx_msg->l2h = NULL; + sh->rx_msg->trx = sh->bts->c0; + } + msg = sh->rx_msg; + + /* first read two byes to obtain length */ + if (msg->len < 2) { + rc = read(sh->fd.fd, msg->tail, 2 - msg->len); + if (rc < 0) { + perror("ERROR reading from serial port"); + msgb_free(msg); + return rc; + } + msgb_put(msg, rc); + + if (msg->len >= 2) { + /* parse LAPD payload length */ + if (msg->data[0] != 0) + fprintf(stderr, "Suspicious header byte 0: 0x%02x\n", + msg->data[0]); + + sh->rxmsg_bytes_missing = msg->data[0] << 8; + sh->rxmsg_bytes_missing += msg->data[1]; + + if (sh->rxmsg_bytes_missing < LAPD_HDR_LEN -2) + fprintf(stderr, "Invalid length in hdr: %u\n", + sh->rxmsg_bytes_missing); + } + } else { + /* try to read as many of the missing bytes as are available */ + rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing); + if (rc < 0) { + perror("ERROR reading from serial port"); + msgb_free(msg); + return rc; + } + msgb_put(msg, rc); + sh->rxmsg_bytes_missing -= rc; + + if (sh->rxmsg_bytes_missing == 0) { + /* we have one complete message now */ + sh->rx_msg = NULL; + + if (msg->len > LAPD_HDR_LEN) + msg->l2h = msg->data + LAPD_HDR_LEN; + + DEBUGP(DMI, "RS232 RX: %s\n", hexdump(msg->data, msg->len)); + rc = handle_serial_msg(msg); + } + } + + return rc; +} + +/* select.c callback */ +static int serial_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_ser_read(bfd); + + if (rc < 0) + return rc; + + if (what & BSC_FD_WRITE) + rc = handle_ser_write(bfd); + + return rc; +} + +int rs232_setup(const char *serial_port, unsigned int delay_ms, + struct gsm_bts *bts) +{ + int rc, serial_fd; + struct termios tio; + + serial_fd = open(serial_port, O_RDWR); + if (serial_fd < 0) { + perror("cannot open serial port:"); + return serial_fd; + } + + /* set baudrate */ + rc = tcgetattr(serial_fd, &tio); + if (rc < 0) { + perror("tcgetattr()"); + return rc; + } + cfsetispeed(&tio, B19200); + cfsetospeed(&tio, B19200); + tio.c_cflag |= (CREAD | CLOCAL | CS8); + tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); + tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + tio.c_iflag |= (INPCK | ISTRIP); + tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR); + tio.c_oflag &= ~(OPOST); + rc = tcsetattr(serial_fd, TCSADRAIN, &tio); + if (rc < 0) { + perror("tcsetattr()"); + return rc; + } + + INIT_LLIST_HEAD(&ser_handle->tx_queue); + ser_handle->fd.fd = serial_fd; + ser_handle->fd.when = BSC_FD_READ; + ser_handle->fd.cb = serial_fd_cb; + ser_handle->fd.data = ser_handle; + ser_handle->delay_ms = delay_ms; + ser_handle->bts = bts; + rc = bsc_register_fd(&ser_handle->fd); + if (rc < 0) { + fprintf(stderr, "could not register FD: %s\n", + strerror(rc)); + return rc; + } + + return 0; +} diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c new file mode 100644 index 000000000..9f2e2fd76 --- /dev/null +++ b/openbsc/src/rtp_proxy.c @@ -0,0 +1,724 @@ +/* RTP proxy handling for ip.access nanoBTS */ + +/* (C) 2009 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 <endian.h> +#include <errno.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/time.h> /* gettimeofday() */ +#include <unistd.h> /* get..() */ +#include <time.h> /* clock() */ +#include <sys/utsname.h> /* uname() */ + +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> +#include <osmocore/msgb.h> +#include <osmocore/select.h> +#include <openbsc/debug.h> +#include <openbsc/rtp_proxy.h> + +static LLIST_HEAD(rtp_sockets); + +/* should we mangle the CNAME inside SDES of RTCP packets? We disable + * this by default, as it seems to be not needed */ +static int mangle_rtcp_cname = 0; + +enum rtp_bfd_priv { + RTP_PRIV_NONE, + RTP_PRIV_RTP, + RTP_PRIV_RTCP +}; + +#define RTP_ALLOC_SIZE 1500 + +/* according to RFC 1889 */ +struct rtcp_hdr { + u_int8_t byte0; + u_int8_t type; + u_int16_t length; +} __attribute__((packed)); + +#define RTCP_TYPE_SDES 202 + +#define RTCP_IE_CNAME 1 + +/* according to RFC 3550 */ +struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t csrc_count:4, + extension:1, + padding:1, + version:2; + u_int8_t payload_type:7, + marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t version:2, + padding:1, + extension:1, + csrc_count:4; + u_int8_t marker:1, + payload_type:7; +#endif + u_int16_t sequence; + u_int32_t timestamp; + u_int32_t ssrc; +} __attribute__((packed)); + +struct rtp_x_hdr { + u_int16_t by_profile; + u_int16_t length; +} __attribute__((packed)); + +#define RTP_VERSION 2 + +#define RTP_PT_GSM_FULL 3 +#define RTP_PT_GSM_EFR 97 + +/* decode an rtp frame and create a new buffer with payload */ +static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data) +{ + struct msgb *new_msg; + struct gsm_data_frame *frame; + struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data; + struct rtp_x_hdr *rtpxh; + u_int8_t *payload; + int payload_len; + int msg_type; + int x_len; + + if (msg->len < 12) { + DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n", + msg->len); + return -EINVAL; + } + if (rtph->version != RTP_VERSION) { + DEBUGPC(DMUX, "received RTP version %d not supported.\n", + rtph->version); + return -EINVAL; + } + payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2); + payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2); + if (payload_len < 0) { + DEBUGPC(DMUX, "received RTP frame too short (len = %d, " + "csrc count = %d)\n", msg->len, rtph->csrc_count); + return -EINVAL; + } + if (rtph->extension) { + if (payload_len < sizeof(struct rtp_x_hdr)) { + DEBUGPC(DMUX, "received RTP frame too short for " + "extension header\n"); + return -EINVAL; + } + rtpxh = (struct rtp_x_hdr *)payload; + x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr); + payload += x_len; + payload_len -= x_len; + if (payload_len < 0) { + DEBUGPC(DMUX, "received RTP frame too short, " + "extension header exceeds frame length\n"); + return -EINVAL; + } + } + if (rtph->padding) { + if (payload_len < 0) { + DEBUGPC(DMUX, "received RTP frame too short for " + "padding length\n"); + return -EINVAL; + } + payload_len -= payload[payload_len - 1]; + if (payload_len < 0) { + DEBUGPC(DMUX, "received RTP frame with padding " + "greater than payload\n"); + return -EINVAL; + } + } + + switch (rtph->payload_type) { + case RTP_PT_GSM_FULL: + msg_type = GSM_TCHF_FRAME; + if (payload_len != 33) { + DEBUGPC(DMUX, "received RTP full rate frame with " + "payload length != 32 (len = %d)\n", + payload_len); + return -EINVAL; + } + break; + case RTP_PT_GSM_EFR: + msg_type = GSM_TCHF_FRAME_EFR; + break; + default: + DEBUGPC(DMUX, "received RTP frame with unknown payload " + "type %d\n", rtph->payload_type); + return -EINVAL; + } + + new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len, + "GSM-DATA"); + if (!new_msg) + return -ENOMEM; + frame = (struct gsm_data_frame *)(new_msg->data); + frame->msg_type = msg_type; + frame->callref = callref; + memcpy(frame->data, payload, payload_len); + msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len); + + *data = new_msg; + return 0; +} + +/* "to - from" */ +static void tv_difference(struct timeval *diff, const struct timeval *from, + const struct timeval *__to) +{ + struct timeval _to = *__to, *to = &_to; + + if (to->tv_usec < from->tv_usec) { + to->tv_sec -= 1; + to->tv_usec += 1000000; + } + + diff->tv_usec = to->tv_usec - from->tv_usec; + diff->tv_sec = to->tv_sec - from->tv_sec; +} + +/* encode and send a rtp frame */ +int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame) +{ + struct rtp_sub_socket *rss = &rs->rtp; + struct msgb *msg; + struct rtp_hdr *rtph; + int payload_type; + int payload_len; + int duration; /* in samples */ + + if (rs->tx_action != RTP_SEND_DOWNSTREAM) { + /* initialize sequences */ + rs->tx_action = RTP_SEND_DOWNSTREAM; + rs->transmit.ssrc = rand(); + rs->transmit.sequence = random(); + rs->transmit.timestamp = random(); + } + + switch (frame->msg_type) { + case GSM_TCHF_FRAME: + payload_type = RTP_PT_GSM_FULL; + payload_len = 33; + duration = 160; + break; + case GSM_TCHF_FRAME_EFR: + payload_type = RTP_PT_GSM_EFR; + payload_len = 31; + duration = 160; + break; + default: + DEBUGPC(DMUX, "unsupported message type %d\n", + frame->msg_type); + return -EINVAL; + } + + { + struct timeval tv, tv_diff; + long int usec_diff, frame_diff; + + gettimeofday(&tv, NULL); + tv_difference(&tv_diff, &rs->transmit.last_tv, &tv); + rs->transmit.last_tv = tv; + + usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec; + frame_diff = (usec_diff / 20000); + + if (abs(frame_diff) > 1) { + long int frame_diff_excess = frame_diff - 1; + + LOGP(DMUX, LOGL_NOTICE, + "Correcting frame difference of %ld frames\n", frame_diff_excess); + rs->transmit.sequence += frame_diff_excess; + rs->transmit.timestamp += frame_diff_excess * duration; + } + } + + msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL"); + if (!msg) + return -ENOMEM; + rtph = (struct rtp_hdr *)msg->data; + rtph->version = RTP_VERSION; + rtph->padding = 0; + rtph->extension = 0; + rtph->csrc_count = 0; + rtph->marker = 0; + rtph->payload_type = payload_type; + rtph->sequence = htons(rs->transmit.sequence++); + rtph->timestamp = htonl(rs->transmit.timestamp); + rs->transmit.timestamp += duration; + rtph->ssrc = htonl(rs->transmit.ssrc); + memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len); + msgb_put(msg, sizeof(struct rtp_hdr) + payload_len); + msgb_enqueue(&rss->tx_queue, msg); + rss->bfd.when |= BSC_FD_WRITE; + + return 0; +} + +/* iterate over all chunks in one RTCP message, look for CNAME IEs and + * replace all of those with 'new_cname' */ +static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh, + u_int16_t *rtcp_len, const char *new_cname) +{ + u_int8_t *rtcp_end; + u_int8_t *cur = (u_int8_t *) rh; + u_int8_t tag, len = 0; + + rtcp_end = cur + *rtcp_len; + /* move cur to end of RTP header */ + cur += sizeof(*rh); + + /* iterate over Chunks */ + while (cur+4 < rtcp_end) { + /* skip four bytes SSRC/CSRC */ + cur += 4; + + /* iterate over IE's inside the chunk */ + while (cur+1 < rtcp_end) { + tag = *cur++; + if (tag == 0) { + /* end of chunk, skip additional zero */ + while (*cur++ == 0) { } + break; + } + len = *cur++; + + if (tag == RTCP_IE_CNAME) { + /* we've found the CNAME, lets mangle it */ + if (len < strlen(new_cname)) { + /* we need to make more space */ + int increase = strlen(new_cname) - len; + + msgb_push(msg, increase); + memmove(cur+len+increase, cur+len, + rtcp_end - (cur+len)); + /* FIXME: we have to respect RTCP + * padding/alignment rules! */ + len += increase; + *(cur-1) += increase; + rtcp_end += increase; + *rtcp_len += increase; + } + /* copy new CNAME into message */ + memcpy(cur, new_cname, strlen(new_cname)); + /* FIXME: zero the padding in case new CNAME + * is smaller than old one !!! */ + } + cur += len; + } + } + + return 0; +} + +static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs) +{ + struct rtp_sub_socket *rss = &rs->rtcp; + struct rtcp_hdr *rtph; + u_int16_t old_len; + int rc; + + if (!mangle_rtcp_cname) + return 0; + + printf("RTCP\n"); + /* iterate over list of RTCP messages */ + rtph = (struct rtcp_hdr *)msg->data; + while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) { + old_len = (ntohs(rtph->length) + 1) * 4; + if ((void *)rtph + old_len > (void *)msg->data + msg->len) { + DEBUGPC(DMUX, "received RTCP packet too short for " + "length element\n"); + return -EINVAL; + } + if (rtph->type == RTCP_TYPE_SDES) { + char new_cname[255]; + strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr), + sizeof(new_cname)); + new_cname[sizeof(new_cname)-1] = '\0'; + rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len, + new_cname); + if (rc < 0) + return rc; + } + rtph = (void *)rtph + old_len; + } + + return 0; +} + +/* read from incoming RTP/RTCP socket */ +static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss) +{ + int rc; + struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP"); + struct msgb *new_msg; + struct rtp_sub_socket *other_rss; + + if (!msg) + return -ENOMEM; + + rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE); + if (rc <= 0) { + rss->bfd.when &= ~BSC_FD_READ; + return rc; + } + + msgb_put(msg, rc); + + switch (rs->rx_action) { + case RTP_PROXY: + if (!rs->proxy.other_sock) { + rc = -EIO; + goto out_free; + } + if (rss->bfd.priv_nr == RTP_PRIV_RTP) + other_rss = &rs->proxy.other_sock->rtp; + else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { + other_rss = &rs->proxy.other_sock->rtcp; + /* modify RTCP SDES CNAME */ + rc = rtcp_mangle(msg, rs); + if (rc < 0) + goto out_free; + } else { + rc = -EINVAL; + goto out_free; + } + msgb_enqueue(&other_rss->tx_queue, msg); + other_rss->bfd.when |= BSC_FD_WRITE; + break; + + case RTP_RECV_UPSTREAM: + if (!rs->receive.callref || !rs->receive.net) { + rc = -EIO; + goto out_free; + } + if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { + if (!mangle_rtcp_cname) { + msgb_free(msg); + break; + } + /* modify RTCP SDES CNAME */ + rc = rtcp_mangle(msg, rs); + if (rc < 0) + goto out_free; + msgb_enqueue(&rss->tx_queue, msg); + rss->bfd.when |= BSC_FD_WRITE; + break; + } + if (rss->bfd.priv_nr != RTP_PRIV_RTP) { + rc = -EINVAL; + goto out_free; + } + rc = rtp_decode(msg, rs->receive.callref, &new_msg); + if (rc < 0) + goto out_free; + msgb_free(msg); + msgb_enqueue(&rs->receive.net->upqueue, new_msg); + break; + + case RTP_NONE: /* if socket exists, but disabled by app */ + msgb_free(msg); + break; + } + + return 0; + +out_free: + msgb_free(msg); + return rc; +} + +/* write from tx_queue to RTP/RTCP socket */ +static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss) +{ + struct msgb *msg; + int written; + + msg = msgb_dequeue(&rss->tx_queue); + if (!msg) { + rss->bfd.when &= ~BSC_FD_WRITE; + return 0; + } + + written = write(rss->bfd.fd, msg->data, msg->len); + if (written < msg->len) { + LOGP(DMIB, LOGL_ERROR, "short write"); + msgb_free(msg); + return -EIO; + } + + msgb_free(msg); + + return 0; +} + + +/* callback for the select.c:bfd_* layer */ +static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags) +{ + struct rtp_socket *rs = bfd->data; + struct rtp_sub_socket *rss; + + switch (bfd->priv_nr) { + case RTP_PRIV_RTP: + rss = &rs->rtp; + break; + case RTP_PRIV_RTCP: + rss = &rs->rtcp; + break; + default: + return -EINVAL; + } + + if (flags & BSC_FD_READ) + rtp_socket_read(rs, rss); + + if (flags & BSC_FD_WRITE) + rtp_socket_write(rs, rss); + + return 0; +} + +static void init_rss(struct rtp_sub_socket *rss, + struct rtp_socket *rs, int fd, int priv_nr) +{ + /* initialize bfd */ + rss->bfd.fd = fd; + rss->bfd.data = rs; + rss->bfd.priv_nr = priv_nr; + rss->bfd.cb = rtp_bfd_cb; +} + +struct rtp_socket *rtp_socket_create(void) +{ + int rc; + struct rtp_socket *rs; + + DEBUGP(DMUX, "rtp_socket_create(): "); + + rs = talloc_zero(tall_bsc_ctx, struct rtp_socket); + if (!rs) + return NULL; + + INIT_LLIST_HEAD(&rs->rtp.tx_queue); + INIT_LLIST_HEAD(&rs->rtcp.tx_queue); + + rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (rc < 0) + goto out_free; + + init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP); + rc = bsc_register_fd(&rs->rtp.bfd); + if (rc < 0) + goto out_rtp_socket; + + rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (rc < 0) + goto out_rtp_bfd; + + init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP); + rc = bsc_register_fd(&rs->rtcp.bfd); + if (rc < 0) + goto out_rtcp_socket; + + DEBUGPC(DMUX, "success\n"); + + rc = rtp_socket_bind(rs, INADDR_ANY); + if (rc < 0) + goto out_rtcp_bfd; + + return rs; + +out_rtcp_bfd: + bsc_unregister_fd(&rs->rtcp.bfd); +out_rtcp_socket: + close(rs->rtcp.bfd.fd); +out_rtp_bfd: + bsc_unregister_fd(&rs->rtp.bfd); +out_rtp_socket: + close(rs->rtp.bfd.fd); +out_free: + talloc_free(rs); + DEBUGPC(DMUX, "failed\n"); + return NULL; +} + +static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip, + u_int16_t port) +{ + int rc; + socklen_t alen = sizeof(rss->sin_local); + + rss->sin_local.sin_family = AF_INET; + rss->sin_local.sin_addr.s_addr = htonl(ip); + rss->sin_local.sin_port = htons(port); + rss->bfd.when |= BSC_FD_READ; + + rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, + sizeof(rss->sin_local)); + if (rc < 0) + return rc; + + /* retrieve the address we actually bound to, in case we + * passed INADDR_ANY as IP address */ + return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, + &alen); +} + +#define RTP_PORT_BASE 30000 +static unsigned int next_udp_port = RTP_PORT_BASE; + +/* bind a RTP socket to a local address */ +int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip) +{ + int rc = -EIO; + struct in_addr ia; + + ia.s_addr = htonl(ip); + DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs, + inet_ntoa(ia)); + + /* try to bind to a consecutive pair of ports */ + for (next_udp_port = next_udp_port % 0xffff; + next_udp_port < 0xffff; next_udp_port += 2) { + rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port); + if (rc != 0) + continue; + + rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1); + if (rc == 0) + break; + } + if (rc < 0) { + DEBUGPC(DMUX, "failed\n"); + return rc; + } + + ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr; + DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n", + inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port)); + return ntohs(rs->rtp.sin_local.sin_port); +} + +static int rtp_sub_socket_connect(struct rtp_sub_socket *rss, + u_int32_t ip, u_int16_t port) +{ + int rc; + socklen_t alen = sizeof(rss->sin_local); + + rss->sin_remote.sin_family = AF_INET; + rss->sin_remote.sin_addr.s_addr = htonl(ip); + rss->sin_remote.sin_port = htons(port); + + rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote, + sizeof(rss->sin_remote)); + if (rc < 0) + return rc; + + return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, + &alen); +} + +/* 'connect' a RTP socket to a remote peer */ +int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port) +{ + int rc; + struct in_addr ia; + + ia.s_addr = htonl(ip); + DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n", + rs, inet_ntoa(ia), port); + + rc = rtp_sub_socket_connect(&rs->rtp, ip, port); + if (rc < 0) + return rc; + + return rtp_sub_socket_connect(&rs->rtcp, ip, port+1); +} + +/* bind two RTP/RTCP sockets together */ +int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other) +{ + DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n", + this, other); + + this->rx_action = RTP_PROXY; + this->proxy.other_sock = other; + + other->rx_action = RTP_PROXY; + other->proxy.other_sock = this; + + return 0; +} + +/* bind RTP/RTCP socket to application */ +int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, + u_int32_t callref) +{ + DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n", + this, callref); + + if (callref) { + this->rx_action = RTP_RECV_UPSTREAM; + this->receive.net = net; + this->receive.callref = callref; + } else + this->rx_action = RTP_NONE; + + return 0; +} + +static void free_tx_queue(struct rtp_sub_socket *rss) +{ + struct msgb *msg; + + while ((msg = msgb_dequeue(&rss->tx_queue))) + msgb_free(msg); +} + +int rtp_socket_free(struct rtp_socket *rs) +{ + DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs); + + /* make sure we don't leave references dangling to us */ + if (rs->rx_action == RTP_PROXY && + rs->proxy.other_sock) + rs->proxy.other_sock->proxy.other_sock = NULL; + + bsc_unregister_fd(&rs->rtp.bfd); + close(rs->rtp.bfd.fd); + free_tx_queue(&rs->rtp); + + bsc_unregister_fd(&rs->rtcp.bfd); + close(rs->rtcp.bfd.fd); + free_tx_queue(&rs->rtcp); + + talloc_free(rs); + + return 0; +} diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c new file mode 100644 index 000000000..b1da2c721 --- /dev/null +++ b/openbsc/src/sccp/sccp.c @@ -0,0 +1,1346 @@ +/* + * SCCP management code + * + * (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 <string.h> + +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <osmocore/talloc.h> + +#include <sccp/sccp.h> + + +static void *tall_sccp_ctx; +static LLIST_HEAD(sccp_connections); + +#define SCCP_MSG_SIZE 4096 +#define SCCP_MSG_HEADROOM 128 + +/* global data */ +const struct sockaddr_sccp sccp_ssn_bssap = { + .sccp_family = 0, + .sccp_ssn = SCCP_SSN_BSSAP, +}; + +struct sccp_system { + /* layer3 -> layer2 */ + int (*write_data)(struct msgb *data, void *context); + void *write_context; +}; + + +static struct sccp_system sccp_system = { + .write_data = NULL, +}; + +struct sccp_data_callback { + /* connection based */ + int (*accept_cb)(struct sccp_connection *, void *); + void *accept_context; + + /* connection less */ + int (*read_cb)(struct msgb *, unsigned int, void *); + void *read_context; + + u_int8_t ssn; + struct llist_head callback; +}; + +static LLIST_HEAD(sccp_callbacks); + +static struct sccp_data_callback *_find_ssn(u_int8_t ssn) +{ + struct sccp_data_callback *cb; + + llist_for_each_entry(cb, &sccp_callbacks, callback) { + if (cb->ssn == ssn) + return cb; + } + + /* need to add one */ + cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback); + if (!cb) { + DEBUGP(DSCCP, "Failed to allocate sccp callback.\n"); + return NULL; + } + + cb->ssn = ssn; + llist_add_tail(&cb->callback, &sccp_callbacks); + return cb; +} + + +static int _send_msg(struct msgb *msg) +{ + return sccp_system.write_data(msg, sccp_system.write_context); +} + +/* + * parsing routines + */ +static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb *msgb) +{ + struct sccp_called_party_address *party; + + int room = msgb_l2len(msgb) - offset; + u_int8_t read = 0; + u_int8_t length; + + if (room <= 0) { + DEBUGP(DSCCP, "Not enough room for an address: %u\n", room); + return -1; + } + + length = msgb->l2h[offset]; + if (room <= length) { + DEBUGP(DSCCP, "Not enough room for optional data %u %u\n", room, length); + return -1; + } + + + party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1); + if (party->point_code_indicator) { + if (length <= read + 2) { + DEBUGP(DSCCP, "POI does not fit %u\n", length); + return -1; + } + + + memcpy(&addr->poi, &party->data[read], 2); + read += 2; + } + + if (party->ssn_indicator) { + if (length <= read + 1) { + DEBUGP(DSCCP, "SSN does not fit %u\n", length); + return -1; + } + + addr->ssn = party->data[read]; + read += 1; + } + + if (party->global_title_indicator) { + DEBUGP(DSCCP, "GTI not supported %u\n", *(u_int8_t *)party); + return -1; + } + + addr->address = *party; + return 0; +} + +static int check_address(struct sccp_address *addr) +{ + /* ignore point_code_indicator... it should be zero... but */ + if (addr->address.ssn_indicator != 1 + || addr->address.global_title_indicator == 1 + || addr->address.routing_indicator != 1) { + DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + *(u_int8_t *)&addr->address, addr->ssn); + return -1; + } + + return 0; +} + +static int _sccp_parse_optional_data(const int offset, + struct msgb *msgb, struct sccp_optional_data *data) +{ + u_int16_t room = msgb_l2len(msgb) - offset; + u_int16_t read = 0; + + while (room > read) { + u_int8_t type = msgb->l2h[offset + read]; + if (type == SCCP_PNC_END_OF_OPTIONAL) + return 0; + + if (read + 1 >= room) { + DEBUGP(DSCCP, "no place for length\n"); + return 0; + } + + u_int8_t length = msgb->l2h[offset + read + 1]; + read += 2 + length; + + + if (room <= read) { + DEBUGP(DSCCP, "no space for the data: type: %d read: %d room: %d l2: %d\n", + type, read, room, msgb_l2len(msgb)); + return 0; + } + + if (type == SCCP_PNC_DATA) { + data->data_len = length; + data->data_start = offset + read - length; + } + + } + + return -1; +} + +int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *result) +{ + static const u_int32_t header_size = + sizeof(struct sccp_connection_request); + static const u_int32_t optional_offset = + offsetof(struct sccp_connection_request, optional_start); + static const u_int32_t called_offset = + offsetof(struct sccp_connection_request, variable_called); + + struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->l2h; + struct sccp_optional_data optional_data; + + /* header check */ + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + /* copy out the calling and called address. Add the offset */ + if (copy_address(&result->called, called_offset + req->variable_called, msgb) != 0) + return -1; + + if (check_address(&result->called) != 0) { + DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + *(u_int8_t *)&result->called.address, result->called.ssn); + return -1; + } + + result->source_local_reference = &req->source_local_reference; + + /* + * parse optional data. + */ + memset(&optional_data, 0, sizeof(optional_data)); + if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) { + DEBUGP(DSCCP, "parsing of optional data failed.\n"); + return -1; + } + + if (optional_data.data_len != 0) { + msgb->l3h = &msgb->l2h[optional_data.data_start]; + result->data_len = optional_data.data_len; + } else { + result->data_len = 0; + } + + return 0; +} + +int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result *result) +{ + static int header_size = sizeof(struct sccp_connection_released); + static int optional_offset = offsetof(struct sccp_connection_released, optional_start); + + struct sccp_optional_data optional_data; + struct sccp_connection_released *rls = (struct sccp_connection_released *) msgb->l2h; + + /* we don't have enough size for the struct */ + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb > header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + memset(&optional_data, 0, sizeof(optional_data)); + if (_sccp_parse_optional_data(optional_offset + rls->optional_start, msgb, &optional_data) != 0) { + DEBUGP(DSCCP, "parsing of optional data failed.\n"); + return -1; + } + + result->source_local_reference = &rls->source_local_reference; + result->destination_local_reference = &rls->destination_local_reference; + + if (optional_data.data_len != 0) { + msgb->l3h = &msgb->l2h[optional_data.data_start]; + result->data_len = optional_data.data_len; + } else { + result->data_len = 0; + } + + return 0; +} + +int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *result) +{ + static const u_int32_t header_size = + sizeof(struct sccp_connection_refused); + static int optional_offset = offsetof(struct sccp_connection_refused, optional_start); + + struct sccp_optional_data optional_data; + struct sccp_connection_refused *ref; + + /* header check */ + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + ref = (struct sccp_connection_refused *) msgb->l2h; + + result->destination_local_reference = &ref->destination_local_reference; + + memset(&optional_data, 0, sizeof(optional_data)); + if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) { + DEBUGP(DSCCP, "parsing of optional data failed.\n"); + return -1; + } + + /* optional data */ + if (optional_data.data_len != 0) { + msgb->l3h = &msgb->l2h[optional_data.data_start]; + result->data_len = optional_data.data_len; + } else { + result->data_len = 0; + } + + return 0; +} + +int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *result) +{ + static u_int32_t header_size = + sizeof(struct sccp_connection_confirm); + static const u_int32_t optional_offset = + offsetof(struct sccp_connection_confirm, optional_start); + + struct sccp_optional_data optional_data; + struct sccp_connection_confirm *con; + + /* header check */ + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + con = (struct sccp_connection_confirm *) msgb->l2h; + result->destination_local_reference = &con->destination_local_reference; + result->source_local_reference = &con->source_local_reference; + + memset(&optional_data, 0, sizeof(optional_data)); + if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) { + DEBUGP(DSCCP, "parsing of optional data failed.\n"); + return -1; + } + + if (optional_data.data_len != 0) { + msgb->l3h = &msgb->l2h[optional_data.data_start]; + result->data_len = optional_data.data_len; + } else { + result->data_len = 0; + } + + return 0; +} + +int _sccp_parse_connection_release_complete(struct msgb *msgb, struct sccp_parse_result *result) +{ + static int header_size = sizeof(struct sccp_connection_release_complete); + + struct sccp_connection_release_complete *cmpl; + + /* header check */ + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + cmpl = (struct sccp_connection_release_complete *) msgb->l2h; + result->source_local_reference = &cmpl->source_local_reference; + result->destination_local_reference = &cmpl->destination_local_reference; + + return 0; +} + +int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *result) +{ + static int header_size = sizeof(struct sccp_data_form1); + static int variable_offset = offsetof(struct sccp_data_form1, variable_start); + + struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *)msgb->l2h; + + /* we don't have enough size for the struct */ + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb > header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + if (dt1->segmenting != 0) { + DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting); + return -1; + } + + result->destination_local_reference = &dt1->destination_local_reference; + + /* some more size checks in here */ + if (msgb_l2len(msgb) < variable_offset + dt1->variable_start + 1) { + DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n", + msgb_l2len(msgb), dt1->variable_start); + return -1; + } + + result->data_len = msgb->l2h[variable_offset + dt1->variable_start]; + msgb->l3h = &msgb->l2h[dt1->variable_start + variable_offset + 1]; + + if (msgb_l3len(msgb) < result->data_len) { + DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n", + msgb_l3len(msgb), result->data_len); + return -1; + } + + return 0; +} + +int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result) +{ + static const u_int32_t header_size = sizeof(struct sccp_data_unitdata); + static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called); + static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling); + static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data); + + struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h; + + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + /* copy out the calling and called address. Add the off */ + if (copy_address(&result->called, called_offset + udt->variable_called, msgb) != 0) + return -1; + + if (check_address(&result->called) != 0) { + DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + *(u_int8_t *)&result->called.address, result->called.ssn); + return -1; + } + + if (copy_address(&result->calling, calling_offset + udt->variable_calling, msgb) != 0) + return -1; + + if (check_address(&result->calling) != 0) { + DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + *(u_int8_t *)&result->called.address, result->called.ssn); + } + + /* we don't have enough size for the data */ + if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) { + DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n", + msgb_l2len(msgb), header_size, udt->variable_data); + return -1; + } + + + msgb->l3h = &udt->data[udt->variable_data]; + result->data_len = msgb_l3len(msgb); + + if (msgb_l3len(msgb) != msgb->l3h[-1]) { + DEBUGP(DSCCP, "msgb is truncated is: %u should: %u\n", + msgb_l3len(msgb), msgb->l3h[-1]); + return -1; + } + + return 0; +} + +static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result) +{ + static const u_int32_t header_size = sizeof(struct sccp_data_it); + + struct sccp_data_it *it; + + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + it = (struct sccp_data_it *) msgb->l2h; + result->data_len = 0; + result->source_local_reference = &it->source_local_reference; + result->destination_local_reference = &it->destination_local_reference; + return 0; +} + + +/* + * Send UDT. Currently we have a fixed address... + */ +static int _sccp_send_data(int class, const struct sockaddr_sccp *in, + const struct sockaddr_sccp *out, struct msgb *payload) +{ + struct sccp_data_unitdata *udt; + u_int8_t *data; + int ret; + + if (msgb_l3len(payload) > 256) { + DEBUGP(DSCCP, "The payload is too big for one udt\n"); + return -1; + } + + struct msgb *msg = msgb_alloc_headroom(SCCP_MSG_SIZE, + SCCP_MSG_HEADROOM, "sccp: udt"); + msg->l2h = &msg->data[0]; + udt = (struct sccp_data_unitdata *)msgb_put(msg, sizeof(*udt)); + + udt->type = SCCP_MSG_TYPE_UDT; + udt->proto_class = class; + udt->variable_called = 3; + udt->variable_calling = 5; + udt->variable_data = 7; + + /* for variable data we start with a size and the data */ + data = msgb_put(msg, 1 + 2); + data[0] = 2; + data[1] = 0x42; + data[2] = out->sccp_ssn; + + data = msgb_put(msg, 1 + 2); + data[0] = 2; + data[1] = 0x42; + data[2] = in->sccp_ssn; + + /* copy the payload */ + data = msgb_put(msg, 1 + msgb_l3len(payload)); + data[0] = msgb_l3len(payload); + memcpy(&data[1], payload->l3h, msgb_l3len(payload)); + + ret = _send_msg(msg); + msgb_free(msg); + + return ret; +} + +static int _sccp_handle_read(struct msgb *msgb) +{ + struct sccp_data_callback *cb; + struct sccp_parse_result result; + + if (_sccp_parse_udt(msgb, &result) != 0) + return -1; + + cb = _find_ssn(result.called.ssn); + if (!cb || !cb->read_cb) { + DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", result.called.ssn); + return -1; + } + + /* sanity check */ + return cb->read_cb(msgb, msgb_l3len(msgb), cb->read_context); +} + +/* + * handle connection orientated methods + */ +static int source_local_reference_is_free(struct sccp_source_reference *reference) +{ + struct sccp_connection *connection; + + llist_for_each_entry(connection, &sccp_connections, list) { + if (memcmp(reference, &connection->source_local_reference, sizeof(*reference)) == 0) + return -1; + } + + return 0; +} + +static int destination_local_reference_is_free(struct sccp_source_reference *reference) +{ + struct sccp_connection *connection; + + llist_for_each_entry(connection, &sccp_connections, list) { + if (memcmp(reference, &connection->destination_local_reference, sizeof(*reference)) == 0) + return -1; + } + + return 0; +} + +static int assign_source_local_reference(struct sccp_connection *connection) +{ + static u_int32_t last_ref = 0x30000; + int wrapped = 0; + + do { + struct sccp_source_reference reference; + reference.octet1 = (last_ref >> 0) & 0xff; + reference.octet2 = (last_ref >> 8) & 0xff; + reference.octet3 = (last_ref >> 16) & 0xff; + + ++last_ref; + /* do not use the reversed word and wrap around */ + if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { + DEBUGP(DSCCP, "Wrapped searching for a free code\n"); + last_ref = 0; + ++wrapped; + } + + if (source_local_reference_is_free(&reference) == 0) { + connection->source_local_reference = reference; + return 0; + } + } while (wrapped != 2); + + DEBUGP(DSCCP, "Finding a free reference failed\n"); + return -1; +} + +static void _sccp_set_connection_state(struct sccp_connection *connection, int new_state) +{ + int old_state = connection->connection_state; + + connection->connection_state = new_state; + if (connection->state_cb) + connection->state_cb(connection, old_state); +} + +static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause) +{ + struct msgb *msgb; + struct sccp_connection_refused *ref; + u_int8_t *data; + int ret; + + msgb = msgb_alloc_headroom(SCCP_MSG_SIZE, + SCCP_MSG_HEADROOM, "sccp ref"); + msgb->l2h = &msgb->data[0]; + + ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref)); + ref->type = SCCP_MSG_TYPE_CREF; + memcpy(&ref->destination_local_reference, src_ref, + sizeof(struct sccp_source_reference)); + ref->cause = cause; + ref->optional_start = 1; + + data = msgb_put(msgb, 1); + data[0] = SCCP_PNC_END_OF_OPTIONAL; + + ret = _send_msg(msgb); + msgb_free(msgb); + return ret; +} + +static int _sccp_send_connection_confirm(struct sccp_connection *connection) +{ + struct msgb *response; + struct sccp_connection_confirm *confirm; + u_int8_t *optional_data; + int ret; + + if (assign_source_local_reference(connection) != 0) + return -1; + + response = msgb_alloc_headroom(SCCP_MSG_SIZE, + SCCP_MSG_HEADROOM, "sccp confirm"); + response->l2h = &response->data[0]; + + confirm = (struct sccp_connection_confirm *) msgb_put(response, sizeof(*confirm)); + + confirm->type = SCCP_MSG_TYPE_CC; + memcpy(&confirm->destination_local_reference, + &connection->destination_local_reference, + sizeof(connection->destination_local_reference)); + memcpy(&confirm->source_local_reference, + &connection->source_local_reference, + sizeof(connection->source_local_reference)); + confirm->proto_class = 2; + confirm->optional_start = 1; + + optional_data = (u_int8_t *) msgb_put(response, 1); + optional_data[0] = SCCP_PNC_END_OF_OPTIONAL; + + ret = _send_msg(response); + msgb_free(response); + + _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED); + return ret; +} + +static int _sccp_send_connection_request(struct sccp_connection *connection, + const struct sockaddr_sccp *called, struct msgb *msg) +{ + struct msgb *request; + struct sccp_connection_request *req; + u_int8_t *data; + u_int8_t extra_size = 3 + 1; + int ret; + + + if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) { + DEBUGP(DSCCP, "Invalid amount of data... %d\n", msgb_l3len(msg)); + return -1; + } + + /* try to find a id */ + if (assign_source_local_reference(connection) != 0) { + DEBUGP(DSCCP, "Assigning a local reference failed.\n"); + _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR); + return -1; + } + + + if (msg) + extra_size += 2 + msgb_l3len(msg); + request = msgb_alloc_headroom(SCCP_MSG_SIZE, + SCCP_MSG_HEADROOM, "sccp connection request"); + request->l2h = &request->data[0]; + req = (struct sccp_connection_request *) msgb_put(request, sizeof(*req)); + + req->type = SCCP_MSG_TYPE_CR; + memcpy(&req->source_local_reference, &connection->source_local_reference, + sizeof(connection->source_local_reference)); + req->proto_class = 2; + req->variable_called = 2; + req->optional_start = 4; + + /* write the called party address */ + data = msgb_put(request, 1 + 2); + data[0] = 2; + data[1] = 0x42; + data[2] = called->sccp_ssn; + + /* write the payload */ + if (msg) { + data = msgb_put(request, 2 + msgb_l3len(msg)); + data[0] = SCCP_PNC_DATA; + data[1] = msgb_l3len(msg); + memcpy(&data[2], msg->l3h, msgb_l3len(msg)); + } + + data = msgb_put(request, 1); + data[0] = SCCP_PNC_END_OF_OPTIONAL; + + llist_add_tail(&connection->list, &sccp_connections); + _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REQUEST); + + ret = _send_msg(request); + msgb_free(request); + + return ret; +} + +static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data) +{ + struct msgb *msgb; + struct sccp_data_form1 *dt1; + u_int8_t *data; + int extra_size; + int ret; + + if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) { + DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n"); + return -1; + } + + extra_size = 1 + msgb_l3len(_data); + msgb = msgb_alloc_headroom(SCCP_MSG_SIZE, + SCCP_MSG_HEADROOM, "sccp dt1"); + msgb->l2h = &msgb->data[0]; + + dt1 = (struct sccp_data_form1 *) msgb_put(msgb, sizeof(*dt1)); + dt1->type = SCCP_MSG_TYPE_DT1; + memcpy(&dt1->destination_local_reference, &conn->destination_local_reference, + sizeof(struct sccp_source_reference)); + dt1->segmenting = 0; + + /* copy the data */ + dt1->variable_start = 1; + data = msgb_put(msgb, extra_size); + data[0] = extra_size - 1; + memcpy(&data[1], _data->l3h, extra_size - 1); + + ret = _send_msg(msgb); + msgb_free(msgb); + + return ret; +} + +static int _sccp_send_connection_it(struct sccp_connection *conn) +{ + struct msgb *msgb; + struct sccp_data_it *it; + int ret; + + msgb = msgb_alloc_headroom(SCCP_MSG_SIZE, + SCCP_MSG_HEADROOM, "sccp it"); + msgb->l2h = &msgb->data[0]; + it = (struct sccp_data_it *) msgb_put(msgb, sizeof(*it)); + it->type = SCCP_MSG_TYPE_IT; + memcpy(&it->destination_local_reference, &conn->destination_local_reference, + sizeof(struct sccp_source_reference)); + memcpy(&it->source_local_reference, &conn->source_local_reference, + sizeof(struct sccp_source_reference)); + + it->proto_class = 0x2; + it->sequencing[0] = it->sequencing[1] = 0; + it->credit = 0; + + ret = _send_msg(msgb); + msgb_free(msgb); + return ret; +} + +static int _sccp_send_connection_released(struct sccp_connection *conn, int cause) +{ + struct msgb *msg; + struct sccp_connection_released *rel; + u_int8_t *data; + int ret; + + msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM, + "sccp: connection released"); + msg->l2h = &msg->data[0]; + rel = (struct sccp_connection_released *) msgb_put(msg, sizeof(*rel)); + rel->type = SCCP_MSG_TYPE_RLSD; + rel->release_cause = cause; + + /* copy the source references */ + memcpy(&rel->destination_local_reference, &conn->destination_local_reference, + sizeof(struct sccp_source_reference)); + memcpy(&rel->source_local_reference, &conn->source_local_reference, + sizeof(struct sccp_source_reference)); + + data = msgb_put(msg, 1); + data[0] = SCCP_PNC_END_OF_OPTIONAL; + + _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE); + ret = _send_msg(msg); + msgb_free(msg); + + return ret; +} + +/* + * Open a connection. The following is going to happen: + * + * - Verify the packet, e.g. that we have no other connection + * that id. + * - Ask the user if he wants to accept the connection + * - Try to open the connection by assigning a source local reference + * and sending the packet + */ +static int _sccp_handle_connection_request(struct msgb *msgb) +{ + struct sccp_parse_result result; + + struct sccp_data_callback *cb; + struct sccp_connection *connection; + + if (_sccp_parse_connection_request(msgb, &result) != 0) + return -1; + + cb = _find_ssn(result.called.ssn); + if (!cb || !cb->accept_cb) { + DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", result.called.ssn); + return -1; + } + + /* check if the system wants this connection */ + connection = talloc_zero(tall_sccp_ctx, struct sccp_connection); + if (!connection) { + DEBUGP(DSCCP, "Allocation failed\n"); + return -1; + } + + /* + * sanity checks: + * - Is the source_local_reference in any other connection? + * then will call accept, assign a "destination" local reference + * and send a connection confirm, otherwise we will send a refuseed + * one.... + */ + if (destination_local_reference_is_free(result.source_local_reference) != 0) { + DEBUGP(DSCCP, "Need to reject connection with existing reference\n"); + _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE); + talloc_free(connection); + return -1; + } + + connection->incoming = 1; + connection->destination_local_reference = *result.source_local_reference; + + if (cb->accept_cb(connection, cb->accept_context) != 0) { + _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_END_USER_ORIGINATED); + _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED); + talloc_free(connection); + return 0; + } + + + llist_add_tail(&connection->list, &sccp_connections); + + if (_sccp_send_connection_confirm(connection) != 0) { + DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n"); + + _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE); + _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED); + llist_del(&connection->list); + talloc_free(connection); + + return -1; + } + + /* + * If we have data let us forward things. + */ + if (result.data_len != 0 && connection->data_cb) { + connection->data_cb(connection, msgb, result.data_len); + } + + return 0; +} + +/* Handle the release confirmed */ +static int _sccp_handle_connection_release_complete(struct msgb *msgb) +{ + struct sccp_parse_result result; + struct sccp_connection *conn; + + if (_sccp_parse_connection_release_complete(msgb, &result) != 0) + return -1; + + /* find the connection */ + llist_for_each_entry(conn, &sccp_connections, list) { + if (conn->data_cb + && memcmp(&conn->source_local_reference, + result.destination_local_reference, + sizeof(conn->source_local_reference)) == 0 + && memcmp(&conn->destination_local_reference, + result.source_local_reference, + sizeof(conn->destination_local_reference)) == 0) { + goto found; + } + } + + + DEBUGP(DSCCP, "Release complete of unknown connection\n"); + return -1; + +found: + llist_del(&conn->list); + _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE_COMPLETE); + return 0; +} + +/* Handle the Data Form 1 message */ +static int _sccp_handle_connection_dt1(struct msgb *msgb) +{ + struct sccp_parse_result result; + struct sccp_connection *conn; + + if (_sccp_parse_connection_dt1(msgb, &result) != 0) + return -1; + + /* lookup if we have a connection with the given reference */ + llist_for_each_entry(conn, &sccp_connections, list) { + if (conn->data_cb + && memcmp(&conn->source_local_reference, + result.destination_local_reference, + sizeof(conn->source_local_reference)) == 0) { + goto found; + } + } + + DEBUGP(DSCCP, "No connection found for dt1 data\n"); + return -1; + +found: + conn->data_cb(conn, msgb, result.data_len); + return 0; +} + +/* confirm a connection release */ +static int _sccp_send_connection_release_complete(struct sccp_connection *connection) +{ + struct msgb *msgb; + struct sccp_connection_release_complete *rlc; + int ret; + + msgb = msgb_alloc_headroom(SCCP_MSG_SIZE, + SCCP_MSG_HEADROOM, "sccp rlc"); + msgb->l2h = &msgb->data[0]; + + rlc = (struct sccp_connection_release_complete *) msgb_put(msgb, sizeof(*rlc)); + rlc->type = SCCP_MSG_TYPE_RLC; + memcpy(&rlc->destination_local_reference, + &connection->destination_local_reference, sizeof(struct sccp_source_reference)); + memcpy(&rlc->source_local_reference, + &connection->source_local_reference, sizeof(struct sccp_source_reference)); + + ret = _send_msg(msgb); + msgb_free(msgb); + + /* + * Remove from the list of active connections and set the state. User code + * should now free the entry. + */ + llist_del(&connection->list); + _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_RELEASE_COMPLETE); + + return ret; +} + +/* connection released, send a released confirm */ +static int _sccp_handle_connection_released(struct msgb *msgb) +{ + struct sccp_parse_result result; + struct sccp_connection *conn; + + if (_sccp_parse_connection_released(msgb, &result) == -1) + return -1; + + /* lookup if we have a connection with the given reference */ + llist_for_each_entry(conn, &sccp_connections, list) { + if (conn->data_cb + && memcmp(&conn->source_local_reference, + result.destination_local_reference, + sizeof(conn->source_local_reference)) == 0 + && memcmp(&conn->destination_local_reference, + result.source_local_reference, + sizeof(conn->destination_local_reference)) == 0) { + goto found; + } + } + + + DEBUGP(DSCCP, "Unknown connection was released.\n"); + return -1; + + /* we have found a connection */ +found: + /* optional data */ + if (result.data_len != 0 && conn->data_cb) { + conn->data_cb(conn, msgb, result.data_len); + } + + /* generate a response */ + if (_sccp_send_connection_release_complete(conn) != 0) { + DEBUGP(DSCCP, "Sending release confirmed failed\n"); + return -1; + } + + return 0; +} + +static int _sccp_handle_connection_refused(struct msgb *msgb) +{ + struct sccp_parse_result result; + struct sccp_connection *conn; + + if (_sccp_parse_connection_refused(msgb, &result) != 0) + return -1; + + /* lookup if we have a connection with the given reference */ + llist_for_each_entry(conn, &sccp_connections, list) { + if (conn->incoming == 0 && conn->data_cb + && memcmp(&conn->source_local_reference, + result.destination_local_reference, + sizeof(conn->source_local_reference)) == 0) { + goto found; + } + } + + DEBUGP(DSCCP, "Refused but no connection found\n"); + return -1; + +found: + /* optional data */ + if (result.data_len != 0 && conn->data_cb) { + conn->data_cb(conn, msgb, result.data_len); + } + + + llist_del(&conn->list); + _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_REFUSED); + return 0; +} + +static int _sccp_handle_connection_confirm(struct msgb *msgb) +{ + struct sccp_parse_result result; + struct sccp_connection *conn; + + if (_sccp_parse_connection_confirm(msgb, &result) != 0) + return -1; + + /* lookup if we have a connection with the given reference */ + llist_for_each_entry(conn, &sccp_connections, list) { + if (conn->incoming == 0 && conn->data_cb + && memcmp(&conn->source_local_reference, + result.destination_local_reference, + sizeof(conn->source_local_reference)) == 0) { + goto found; + } + } + + DEBUGP(DSCCP, "Confirmed but no connection found\n"); + return -1; + +found: + /* copy the addresses of the connection */ + conn->destination_local_reference = *result.source_local_reference; + _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_ESTABLISHED); + + /* optional data */ + if (result.data_len != 0 && conn->data_cb) { + conn->data_cb(conn, msgb, result.data_len); + } + + return 0; +} + + +int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *ctx) +{ + sccp_system.write_data = outgoing; + sccp_system.write_context = ctx; + + return 0; +} + +/* oh my god a real SCCP packet. need to dispatch it now */ +int sccp_system_incoming(struct msgb *msgb) +{ + if (msgb_l2len(msgb) < 1 ) { + DEBUGP(DSCCP, "Too short packet\n"); + return -1; + } + + int type = msgb->l2h[0]; + + switch(type) { + case SCCP_MSG_TYPE_CR: + return _sccp_handle_connection_request(msgb); + break; + case SCCP_MSG_TYPE_RLSD: + return _sccp_handle_connection_released(msgb); + break; + case SCCP_MSG_TYPE_CREF: + return _sccp_handle_connection_refused(msgb); + break; + case SCCP_MSG_TYPE_CC: + return _sccp_handle_connection_confirm(msgb); + break; + case SCCP_MSG_TYPE_RLC: + return _sccp_handle_connection_release_complete(msgb); + break; + case SCCP_MSG_TYPE_DT1: + return _sccp_handle_connection_dt1(msgb); + break; + case SCCP_MSG_TYPE_UDT: + return _sccp_handle_read(msgb); + break; + default: + DEBUGP(DSCCP, "unimplemented msg type: %d\n", type); + }; + + return -1; +} + +/* create a packet from the data */ +int sccp_connection_write(struct sccp_connection *connection, struct msgb *data) +{ + if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM + || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { + DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n", + connection, connection->connection_state); + return -1; + } + + return _sccp_send_connection_data(connection, data); +} + +/* + * Send a Inactivity Test message. The owner of the connection + * should start a timer and call this method regularily. Calling + * this every 60 seconds should be good enough. + */ +int sccp_connection_send_it(struct sccp_connection *connection) +{ + if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM + || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { + DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n", + connection, connection->connection_state); + return -1; + } + + return _sccp_send_connection_it(connection); +} + +/* send a connection release and wait for the connection released */ +int sccp_connection_close(struct sccp_connection *connection, int cause) +{ + if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM + || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { + DEBUGPC(DSCCP, "Can not close the connection. It was never opened: %p %d\n", + connection, connection->connection_state); + return -1; + } + + return _sccp_send_connection_released(connection, cause); +} + +int sccp_connection_free(struct sccp_connection *connection) +{ + if (connection->connection_state > SCCP_CONNECTION_STATE_NONE + && connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { + DEBUGP(DSCCP, "The connection needs to be released before it is freed"); + return -1; + } + + talloc_free(connection); + return 0; +} + +struct sccp_connection *sccp_connection_socket(void) +{ + return talloc_zero(tall_sccp_ctx, struct sccp_connection); +} + +int sccp_connection_connect(struct sccp_connection *conn, + const struct sockaddr_sccp *local, + struct msgb *data) +{ + return _sccp_send_connection_request(conn, local, data); +} + +int sccp_connection_set_incoming(const struct sockaddr_sccp *sock, + int (*accept_cb)(struct sccp_connection *, void *), void *context) +{ + struct sccp_data_callback *cb; + + if (!sock) + return -2; + + cb = _find_ssn(sock->sccp_ssn); + if (!cb) + return -1; + + cb->accept_cb = accept_cb; + cb->accept_context = context; + return 0; +} + +int sccp_write(struct msgb *data, const struct sockaddr_sccp *in, + const struct sockaddr_sccp *out, int class) +{ + return _sccp_send_data(class, in, out, data); +} + +int sccp_set_read(const struct sockaddr_sccp *sock, + int (*read_cb)(struct msgb *, unsigned int, void *), void *context) +{ + struct sccp_data_callback *cb; + + if (!sock) + return -2; + + cb = _find_ssn(sock->sccp_ssn); + if (!cb) + return -1; + + cb->read_cb = read_cb; + cb->read_context = context; + return 0; +} + +static_assert(sizeof(struct sccp_source_reference) <= sizeof(u_int32_t), enough_space); + +u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref) +{ + u_int32_t src_ref = 0; + memcpy(&src_ref, ref, sizeof(*ref)); + return src_ref; +} + +struct sccp_source_reference sccp_src_ref_from_int(u_int32_t int_ref) +{ + struct sccp_source_reference ref; + memcpy(&ref, &int_ref, sizeof(ref)); + return ref; +} + +int sccp_determine_msg_type(struct msgb *msg) +{ + if (msgb_l2len(msg) < 1) + return -1; + + return msg->l2h[0]; +} + +int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result) +{ + int type; + + if (msgb_l2len(msg) < 1) + return -1; + + type = msg->l2h[0]; + switch(type) { + case SCCP_MSG_TYPE_CR: + return _sccp_parse_connection_request(msg, result); + break; + case SCCP_MSG_TYPE_RLSD: + return _sccp_parse_connection_released(msg, result); + break; + case SCCP_MSG_TYPE_CREF: + return _sccp_parse_connection_refused(msg, result); + break; + case SCCP_MSG_TYPE_CC: + return _sccp_parse_connection_confirm(msg, result); + break; + case SCCP_MSG_TYPE_RLC: + return _sccp_parse_connection_release_complete(msg, result); + break; + case SCCP_MSG_TYPE_DT1: + return _sccp_parse_connection_dt1(msg, result); + break; + case SCCP_MSG_TYPE_UDT: + return _sccp_parse_udt(msg, result); + break; + case SCCP_MSG_TYPE_IT: + return _sccp_parse_it(msg, result); + break; + }; + + LOGP(DSCCP, LOGL_ERROR, "Unimplemented MSG Type: 0x%x\n", type); + return -1; +} + +static __attribute__((constructor)) void on_dso_load(void) +{ + tall_sccp_ctx = talloc_named_const(NULL, 1, "sccp"); +} + +static __attribute__((destructor)) void on_dso_unload(void) +{ + talloc_report_full(tall_sccp_ctx, stderr); +} diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c new file mode 100644 index 000000000..cada24e66 --- /dev/null +++ b/openbsc/src/silent_call.c @@ -0,0 +1,141 @@ +/* GSM silent call feature */ + +/* + * (C) 2009 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 <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <osmocore/msgb.h> +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <openbsc/paging.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/chan_alloc.h> + +/* paging of the requested subscriber has completed */ +static int paging_cb_silent(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_lchan, void *_data) +{ + struct gsm_lchan *lchan = _lchan; + struct scall_signal_data sigdata; + int rc; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + DEBUGP(DSMS, "paging_cb_silent: "); + + sigdata.lchan = lchan; + sigdata.data = _data; + + switch (event) { + case GSM_PAGING_SUCCEEDED: + DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n", + lchan->ts->nr, lchan->ts->trx->arfcn); + lchan->silent_call = 1; + /* increment lchan reference count */ + dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata); + use_lchan(lchan); + break; + case GSM_PAGING_EXPIRED: + DEBUGP(DSMS, "expired\n"); + dispatch_signal(SS_SCALL, S_SCALL_EXPIRED, &sigdata); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +/* receive a layer 3 message from a silent call */ +int silent_call_rx(struct msgb *msg) +{ + /* FIXME: do something like sending it through a UDP port */ + return 0; +} + +struct msg_match { + u_int8_t pdisc; + u_int8_t msg_type; +}; + +/* list of messages that are handled inside OpenBSC, even in a silent call */ +static const struct msg_match silent_call_accept[] = { + { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, + { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, +}; + +/* decide if we need to reroute a message as part of a silent call */ +int silent_call_reroute(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t pdisc = gh->proto_discr & 0x0f; + int i; + + /* if we're not part of a silent call, never reroute */ + if (!msg->lchan->silent_call) + return 0; + + /* check if we are a special message that is handled in openbsc */ + for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { + if (silent_call_accept[i].pdisc == pdisc && + silent_call_accept[i].msg_type == gh->msg_type) + return 0; + } + + /* otherwise, reroute */ + return 1; +} + + +/* initiate a silent call with a given subscriber */ +int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) +{ + int rc; + + rc = paging_request(subscr->net, subscr, type, + paging_cb_silent, data); + return rc; +} + +/* end a silent call with a given subscriber */ +int gsm_silent_call_stop(struct gsm_subscriber *subscr) +{ + struct gsm_lchan *lchan; + + lchan = lchan_for_subscr(subscr); + if (!lchan) + return -EINVAL; + + /* did we actually establish a silent call for this guy? */ + if (!lchan->silent_call) + return -EINVAL; + + put_lchan(lchan); + + return 0; +} diff --git a/openbsc/src/subchan_demux.c b/openbsc/src/subchan_demux.c new file mode 100644 index 000000000..0d6c1febe --- /dev/null +++ b/openbsc/src/subchan_demux.c @@ -0,0 +1,322 @@ +/* A E1 sub-channel (de)multiplexer with TRAU frame sync */ + +/* (C) 2009 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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <openbsc/subchan_demux.h> +#include <openbsc/trau_frame.h> +#include <openbsc/debug.h> +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> + +void *tall_tqe_ctx; + +static inline void append_bit(struct demux_subch *sch, u_int8_t bit) +{ + sch->out_bitbuf[sch->out_idx++] = bit; +} + +#define SYNC_HDR_BITS 16 +static const u_int8_t nullbytes[SYNC_HDR_BITS]; + +/* check if we have just completed the 16 bit zero sync header, + * in accordance with GSM TS 08.60 Chapter 4.8.1 */ +static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit) +{ + if (bit == 0) + sch->consecutive_zeros++; + else + sch->consecutive_zeros = 0; + + if (sch->consecutive_zeros >= SYNC_HDR_BITS) { + sch->consecutive_zeros = 0; + return 1; + } + + return 0; +} + +/* resynchronize to current location */ +static void resync_to_here(struct demux_subch *sch) +{ + memset(sch->out_bitbuf, 0, SYNC_HDR_BITS); + + /* set index in a way that we can continue receiving bits after + * the end of the SYNC header */ + sch->out_idx = SYNC_HDR_BITS; + sch->in_sync = 1; +} + +int subch_demux_init(struct subch_demux *dmx) +{ + int i; + + dmx->chan_activ = 0; + for (i = 0; i < NR_SUBCH; i++) { + struct demux_subch *sch = &dmx->subch[i]; + sch->out_idx = 0; + memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf)); + } + return 0; +} + +/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel, + * split it into the 16k subchannels */ +int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len) +{ + int i, c; + + /* we avoid partially filled bytes in outbuf */ + if (len % 4) + return -EINVAL; + + for (i = 0; i < len; i++) { + u_int8_t inbyte = data[i]; + + for (c = 0; c < NR_SUBCH; c++) { + struct demux_subch *sch = &dmx->subch[c]; + u_int8_t inbits; + u_int8_t bit; + + /* ignore inactive subchannels */ + if (!(dmx->chan_activ & (1 << c))) + continue; + + inbits = inbyte >> (c << 1); + + /* two bits for each subchannel */ + if (inbits & 0x01) + bit = 1; + else + bit = 0; + append_bit(sch, bit); + + if (sync_hdr_complete(sch, bit)) + resync_to_here(sch); + + if (inbits & 0x02) + bit = 1; + else + bit = 0; + append_bit(sch, bit); + + if (sync_hdr_complete(sch, bit)) + resync_to_here(sch); + + /* FIXME: verify the first bit in octet 2, 4, 6, ... + * according to TS 08.60 4.8.1 */ + + /* once we have reached TRAU_FRAME_BITS, call + * the TRAU frame handler callback function */ + if (sch->out_idx >= TRAU_FRAME_BITS) { + if (sch->in_sync) { + dmx->out_cb(dmx, c, sch->out_bitbuf, + sch->out_idx, dmx->data); + sch->in_sync = 0; + } + sch->out_idx = 0; + } + } + } + return i; +} + +int subch_demux_activate(struct subch_demux *dmx, int subch) +{ + if (subch >= NR_SUBCH) + return -EINVAL; + + dmx->chan_activ |= (1 << subch); + return 0; +} + +int subch_demux_deactivate(struct subch_demux *dmx, int subch) +{ + if (subch >= NR_SUBCH) + return -EINVAL; + + dmx->chan_activ &= ~(1 << subch); + return 0; +} + +/* MULTIPLEXER */ + +static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr) +{ + /* allocate and initialize with idle pattern */ + return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(), + TRAU_FRAME_BITS); +} + +/* return the requested number of bits from the specified subchannel */ +static int get_subch_bits(struct subch_mux *mx, int subch, + u_int8_t *bits, int num_requested) +{ + struct mux_subch *sch = &mx->subch[subch]; + int num_bits = 0; + + while (num_bits < num_requested) { + struct subch_txq_entry *txe; + int num_bits_left; + int num_bits_thistime; + + /* make sure we have a valid entry at top of tx queue. + * if not, add an idle frame */ + if (llist_empty(&sch->tx_queue)) + alloc_add_idle_frame(mx, subch); + + if (llist_empty(&sch->tx_queue)) + return -EIO; + + txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); + num_bits_left = txe->bit_len - txe->next_bit; + + if (num_bits_left < num_requested) + num_bits_thistime = num_bits_left; + else + num_bits_thistime = num_requested; + + /* pull the bits from the txe */ + memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime); + txe->next_bit += num_bits_thistime; + + /* free the tx_queue entry if it is fully consumed */ + if (txe->next_bit >= txe->bit_len) { + llist_del(&txe->list); + talloc_free(txe); + } + + /* increment global number of bits dequeued */ + num_bits += num_bits_thistime; + } + + return num_requested; +} + +/* compact an array of 8 single-bit bytes into one byte of 8 bits */ +static u_int8_t compact_bits(const u_int8_t *bits) +{ + u_int8_t ret = 0; + int i; + + for (i = 0; i < 8; i++) + ret |= (bits[i] ? 1 : 0) << i; + + return ret; +} + +/* obtain a single output byte from the subchannel muxer */ +static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte) +{ + u_int8_t bits[8]; + int rc; + + /* combine two bits of every subchan */ + rc = get_subch_bits(mx, 0, &bits[0], 2); + rc = get_subch_bits(mx, 1, &bits[2], 2); + rc = get_subch_bits(mx, 2, &bits[4], 2); + rc = get_subch_bits(mx, 3, &bits[6], 2); + + *byte = compact_bits(bits); + + return rc; +} + +/* Request the output of some muxed bytes from the subchan muxer */ +int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int rc; + rc = mux_output_byte(mx, &data[i]); + if (rc < 0) + break; + } + return i; +} + +static int llist_len(struct llist_head *head) +{ + struct llist_head *entry; + int i = 0; + + llist_for_each(entry, head) + i++; + + return i; +} + +/* evict the 'num_evict' number of oldest entries in the queue */ +static void tx_queue_evict(struct mux_subch *sch, int num_evict) +{ + struct subch_txq_entry *tqe; + int i; + + for (i = 0; i < num_evict; i++) { + if (llist_empty(&sch->tx_queue)) + return; + + tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); + llist_del(&tqe->list); + talloc_free(tqe); + } +} + +/* enqueue some data into the tx_queue of a given subchannel */ +int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data, + int len) +{ + struct mux_subch *sch = &mx->subch[s_nr]; + int list_len = llist_len(&sch->tx_queue); + struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx, + sizeof(*tqe) + len); + if (!tqe) + return -ENOMEM; + + tqe->bit_len = len; + memcpy(tqe->bits, data, len); + + if (list_len > 2) + tx_queue_evict(sch, list_len-2); + + llist_add_tail(&tqe->list, &sch->tx_queue); + + return 0; +} + +/* initialize one subchannel muxer instance */ +int subchan_mux_init(struct subch_mux *mx) +{ + int i; + + memset(mx, 0, sizeof(*mx)); + for (i = 0; i < NR_SUBCH; i++) { + struct mux_subch *sch = &mx->subch[i]; + INIT_LLIST_HEAD(&sch->tx_queue); + } + + return 0; +} diff --git a/openbsc/src/system_information.c b/openbsc/src/system_information.c new file mode 100644 index 000000000..3f9d60954 --- /dev/null +++ b/openbsc/src/system_information.c @@ -0,0 +1,473 @@ +/* GSM 04.08 System Information (SI) encoding and decoding + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 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 <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include <openbsc/gsm_04_08.h> +#include <openbsc/gsm_data.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/rest_octets.h> +#include <osmocore/bitvec.h> +#include <openbsc/debug.h> + +#define GSM48_CELL_CHAN_DESC_SIZE 16 +#define GSM_MACBLOCK_LEN 23 +#define GSM_MACBLOCK_PADDING 0x2b + +/* verify the sizes of the system information type structs */ + +/* rest octets are not part of the struct */ +static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size); +static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control); +static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size); +static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size); +static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size); +static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size); + +/* bs11 forgot the l2 len, 0-6 rest octets */ +static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size); +static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size); + +static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size); + +/* Frequency Lists as per TS 04.08 10.5.2.13 */ + +/* 10.5.2.13.2: Bit map 0 format */ +static int freq_list_bm0_set_arfcn(u_int8_t *chan_list, unsigned int arfcn) +{ + unsigned int byte, bit; + + if (arfcn > 124 || arfcn < 1) { + LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n"); + return -EINVAL; + } + + /* the bitmask is from 1..124, not from 0..123 */ + arfcn--; + + byte = arfcn / 8; + bit = arfcn % 8; + + chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit); + + return 0; +} + +/* 10.5.2.13.7: Variable bit map format */ +static int freq_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn) +{ + unsigned int byte, bit; + unsigned int min_arfcn; + unsigned int bitno; + + min_arfcn = (chan_list[0] & 1) << 9; + min_arfcn |= chan_list[1] << 1; + min_arfcn |= (chan_list[2] >> 7) & 1; + + /* The lower end of our bitmaks is always implicitly included */ + if (arfcn == min_arfcn) + return 0; + + if (arfcn < min_arfcn) { + LOGP(DRR, LOGL_ERROR, "arfcn(%u) < min(%u)\n", arfcn, min_arfcn); + return -EINVAL; + } + if (arfcn > min_arfcn + 111) { + LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); + return -EINVAL; + } + + bitno = (arfcn - min_arfcn); + byte = bitno / 8; + bit = bitno % 8; + + chan_list[2 + byte] |= 1 << (7 - bit); + + return 0; +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv, + const struct gsm_bts *bts) +{ + int i, rc, min = 1024, max = -1; + + memset(chan_list, 0, 16); + + /* GSM900-only handsets only support 'bit map 0 format' */ + if (bts->band == GSM_BAND_900) { + chan_list[0] = 0; + + for (i = 0; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i)) { + rc = freq_list_bm0_set_arfcn(chan_list, i); + if (rc < 0) + return rc; + } + } + return 0; + } + + /* We currently only support the 'Variable bitmap format' */ + chan_list[0] = 0x8e; + + for (i = 0; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i)) { + if (i < min) + min = i; + if (i > max) + max = i; + } + } + + if (max == -1) { + /* Empty set, use 'bit map 0 format' */ + chan_list[0] = 0; + return 0; + } + + if ((max - min) > 111) { + LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, " + "distance > 111\n", min, max); + return -EINVAL; + } + + chan_list[0] |= (min >> 9) & 1; + chan_list[1] = (min >> 1); + chan_list[2] = (min & 1) << 7; + + for (i = 0; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i)) { + rc = freq_list_bmrel_set_arfcn(chan_list, i); + if (rc < 0) + return rc; + } + } + + return 0; +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int generate_cell_chan_list(u_int8_t *chan_list, struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + struct bitvec *bv = &bts->si_common.cell_alloc; + + /* first we generate a bitvec of all TRX ARFCN's in our BTS */ + llist_for_each_entry(trx, &bts->trx_list, list) + bitvec_set_bit_pos(bv, trx->arfcn, 1); + + /* then we generate a GSM 04.08 frequency list from the bitvec */ + return bitvec2freq_list(chan_list, bv, bts); +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int generate_bcch_chan_list(u_int8_t *chan_list, struct gsm_bts *bts) +{ + struct gsm_bts *cur_bts; + struct bitvec *bv = &bts->si_common.neigh_list; + + /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ + llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { + if (cur_bts == bts) + continue; + bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); + } + + /* then we generate a GSM 04.08 frequency list from the bitvec */ + return bitvec2freq_list(chan_list, bv, bts); +} + +static int generate_si1(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_1 *si1 = + (struct gsm48_system_information_type_1 *) output; + + memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si1->header.l2_plen = (21 << 2) | 1; + si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si1->header.skip_indicator = 0; + si1->header.system_information = GSM48_MT_RR_SYSINFO_1; + + rc = generate_cell_chan_list(si1->cell_channel_description, bts); + if (rc < 0) + return rc; + + si1->rach_control = bts->si_common.rach_control; + + /* SI1 Rest Octets (10.5.2.32), contains NCH position */ + rc = rest_octets_si1(si1->rest_octets, NULL); + return sizeof(*si1) + rc; +} + +static int generate_si2(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_2 *si2 = + (struct gsm48_system_information_type_2 *) output; + + memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si2->header.l2_plen = (22 << 2) | 1; + si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si2->header.skip_indicator = 0; + si2->header.system_information = GSM48_MT_RR_SYSINFO_2; + + rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts); + if (rc < 0) + return rc; + + si2->ncc_permitted = bts->si_common.ncc_permitted; + si2->rach_control = bts->si_common.rach_control; + + return sizeof(*si2); +} + +static struct gsm48_si_ro_info si_info = { + .selection_params = { + .present = 0, + }, + .power_offset = { + .present = 0, + }, + .si2ter_indicator = 0, + .early_cm_ctrl = 1, + .scheduling = { + .present = 0, + }, + .gprs_ind = { + .si13_position = 0, + .ra_colour = 0, + .present = 1, + }, + .lsa_params = { + .present = 0, + }, + .cell_id = 0, /* FIXME: doesn't the bts have this? */ + .break_ind = 0, +}; + +static int generate_si3(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_3 *si3 = + (struct gsm48_system_information_type_3 *) output; + + memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si3->header.l2_plen = (18 << 2) | 1; + si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si3->header.skip_indicator = 0; + si3->header.system_information = GSM48_MT_RR_SYSINFO_3; + + si3->cell_identity = htons(bts->cell_identity); + gsm48_generate_lai(&si3->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si3->control_channel_desc = bts->si_common.chan_desc; + si3->cell_options = bts->si_common.cell_options; + si3->cell_sel_par = bts->si_common.cell_sel_par; + si3->rach_control = bts->si_common.rach_control; + + /* SI3 Rest Octets (10.5.2.34), containing + CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME + Power Offset, 2ter Indicator, Early Classmark Sending, + Scheduling if and WHERE, GPRS Indicator, SI13 position */ + rc = rest_octets_si3(si3->rest_octets, &si_info); + + return sizeof(*si3) + rc; +} + +static int generate_si4(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_4 *si4 = + (struct gsm48_system_information_type_4 *) output; + + /* length of all IEs present except SI4 rest octets and l2_plen */ + int l2_plen = sizeof(*si4) - 1; + + memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si4->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si4->header.skip_indicator = 0; + si4->header.system_information = GSM48_MT_RR_SYSINFO_4; + + gsm48_generate_lai(&si4->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si4->cell_sel_par = bts->si_common.cell_sel_par; + si4->rach_control = bts->si_common.rach_control; + + /* Optional: CBCH Channel Description + CBCH Mobile Allocation */ + + si4->header.l2_plen = (l2_plen << 2) | 1; + + /* SI4 Rest Octets (10.5.2.35), containing + Optional Power offset, GPRS Indicator, + Cell Identity, LSA ID, Selection Parameter */ + rc = rest_octets_si4(si4->data, &si_info); + + return sizeof(*si4) + rc; +} + +static int generate_si5(u_int8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_5 *si5; + int rc, l2_plen = 18; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + if (is_ipaccess_bts(bts)) { + *output++ = (l2_plen << 2) | 1; + l2_plen++; + } + + si5 = (struct gsm48_system_information_type_5 *) output; + + /* l2 pseudo length, not part of msg: 18 */ + si5->rr_protocol_discriminator = GSM48_PDISC_RR; + si5->skip_indicator = 0; + si5->system_information = GSM48_MT_RR_SYSINFO_5; + rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts); + if (rc < 0) + return rc; + + /* 04.08 9.1.37: L2 Pseudo Length of 18 */ + return l2_plen; +} + +static int generate_si6(u_int8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_6 *si6; + int l2_plen = 11; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + if (is_ipaccess_bts(bts)) { + *output++ = (l2_plen << 2) | 1; + l2_plen++; + } + + si6 = (struct gsm48_system_information_type_6 *) output; + + /* l2 pseudo length, not part of msg: 11 */ + si6->rr_protocol_discriminator = GSM48_PDISC_RR; + si6->skip_indicator = 0; + si6->system_information = GSM48_MT_RR_SYSINFO_6; + si6->cell_identity = htons(bts->cell_identity); + gsm48_generate_lai(&si6->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si6->cell_options = bts->si_common.cell_options; + si6->ncc_permitted = bts->si_common.ncc_permitted; + + /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ + + return l2_plen; +} + +static struct gsm48_si13_info si13_default = { + .cell_opts = { + .nmo = GPRS_NMO_III, + .t3168 = 1500, + .t3192 = 500, + .drx_timer_max = 3, + .bs_cv_max = 15, + }, + .pwr_ctrl_pars = { + .alpha = 10, /* a = 1.0 */ + .t_avg_w = 25, + .t_avg_t = 25, + .pc_meas_chan = 0, /* downling measured on CCCH */ + .n_avg_i = 15, + }, + .bcch_change_mark = 1, + .si_change_field = 0, + .pbcch_present = 0, + { + .no_pbcch = { + .rac = 0, /* needs to be patched */ + .spgc_ccch_sup = 0, + .net_ctrl_ord = 0, + .prio_acc_thr = 6, + }, + }, +}; + +static int generate_si13(u_int8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_13 *si13 = + (struct gsm48_system_information_type_13 *) output; + int ret; + + memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si13->header.skip_indicator = 0; + si13->header.system_information = GSM48_MT_RR_SYSINFO_13; + + si13_default.no_pbcch.rac = bts->gprs.rac; + + ret = rest_octets_si13(si13->rest_octets, &si13_default); + if (ret < 0) + return ret; + + si13->header.l2_plen = ret & 0xff; + + return sizeof (*si13) + ret; +} + +int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type) +{ + si_info.gprs_ind.present = bts->gprs.enabled; + + switch (type) { + case RSL_SYSTEM_INFO_1: + return generate_si1(output, bts); + case RSL_SYSTEM_INFO_2: + return generate_si2(output, bts); + case RSL_SYSTEM_INFO_3: + return generate_si3(output, bts); + case RSL_SYSTEM_INFO_4: + return generate_si4(output, bts); + case RSL_SYSTEM_INFO_5: + return generate_si5(output, bts); + case RSL_SYSTEM_INFO_6: + return generate_si6(output, bts); + case RSL_SYSTEM_INFO_13: + return generate_si13(output, bts); + default: + return -EINVAL; + } + + return 0; +} diff --git a/openbsc/src/talloc_ctx.c b/openbsc/src/talloc_ctx.c new file mode 100644 index 000000000..6379e13db --- /dev/null +++ b/openbsc/src/talloc_ctx.c @@ -0,0 +1,36 @@ +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> + +extern void *tall_msgb_ctx; +extern void *tall_fle_ctx; +extern void *tall_locop_ctx; +extern void *tall_gsms_ctx; +extern void *tall_subscr_ctx; +extern void *tall_sub_req_ctx; +extern void *tall_call_ctx; +extern void *tall_paging_ctx; +extern void *tall_sigh_ctx; +extern void *tall_tqe_ctx; +extern void *tall_trans_ctx; +extern void *tall_map_ctx; +extern void *tall_upq_ctx; +extern void *tall_ctr_ctx; + +void talloc_ctx_init(void) +{ + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, + "bs11_file_list_entry"); + tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper"); + tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms"); + tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscriber"); + tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscr_request"); + tall_call_ctx = talloc_named_const(tall_bsc_ctx, 0, "gsm_call"); + tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request"); + tall_sigh_ctx = talloc_named_const(tall_bsc_ctx, 0, "signal_handler"); + tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry"); + tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction"); + tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry"); + tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry"); + tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); +} diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c new file mode 100644 index 000000000..805dd127d --- /dev/null +++ b/openbsc/src/telnet_interface.c @@ -0,0 +1,214 @@ +/* minimalistic telnet/network interface it might turn into a wire interface */ +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <sys/socket.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <openbsc/telnet_interface.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/gsm_04_11.h> +#include <osmocore/msgb.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/paging.h> +#include <openbsc/signal.h> +#include <osmocore/talloc.h> +#include <openbsc/debug.h> + +#include <vty/buffer.h> + +#define WRITE_CONNECTION(fd, msg...) \ + int ret; \ + char buf[4096]; \ + snprintf(buf, sizeof(buf), msg); \ + ret = write(fd, buf, strlen(buf)); + + +/* per connection data */ +LLIST_HEAD(active_connections); + +static void *tall_telnet_ctx; + +/* per network data */ +static int telnet_new_connection(struct bsc_fd *fd, unsigned int what); + +static struct bsc_fd server_socket = { + .when = BSC_FD_READ, + .cb = telnet_new_connection, + .priv_nr = 0, +}; + +void telnet_init(struct gsm_network *network, int port) { + struct sockaddr_in sock_addr; + int fd, on = 1; + + tall_telnet_ctx = talloc_named_const(tall_bsc_ctx, 1, + "telnet_connection"); + + bsc_vty_init(network); + + fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (fd < 0) { + LOGP(DNM, LOGL_ERROR, "Telnet interface socket creation failed\n"); + return; + } + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = htons(port); + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) { + LOGP(DNM, LOGL_ERROR, "Telnet interface failed to bind\n"); + return; + } + + if (listen(fd, 0) < 0) { + LOGP(DNM, LOGL_ERROR, "Telnet interface failed to listen\n"); + return; + } + + server_socket.data = network; + server_socket.fd = fd; + bsc_register_fd(&server_socket); +} + +static void print_welcome(int fd) { + int ret; + static char *msg = + "Welcome to the OpenBSC Control interface\n" + "Copyright (C) 2008-2010 Harald Welte\n" + "Contributions by Daniel Willmann, Jan Lübbe, " + "Stefan Schmidt, Holger Freyther, Andreas Eversberg\n\n" + "License GPLv2+: GNU GPL version 2 or later " + "<http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change " + "and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted " + "by law.\nType \"help\" to get a short introduction.\n"; + + ret = write(fd, msg, strlen(msg)); +} + +int telnet_close_client(struct bsc_fd *fd) { + struct telnet_connection *conn = (struct telnet_connection*)fd->data; + + close(fd->fd); + bsc_unregister_fd(fd); + + if (conn->dbg) { + debug_del_target(conn->dbg); + talloc_free(conn->dbg); + } + + llist_del(&conn->entry); + talloc_free(conn); + return 0; +} + +static int client_data(struct bsc_fd *fd, unsigned int what) +{ + struct telnet_connection *conn = fd->data; + int rc = 0; + + if (what & BSC_FD_READ) { + conn->fd.when &= ~BSC_FD_READ; + rc = vty_read(conn->vty); + } + + /* vty might have been closed from vithin vty_read() */ + if (!conn->vty) + return rc; + + if (what & BSC_FD_WRITE) { + rc = buffer_flush_all(conn->vty->obuf, fd->fd); + if (rc == BUFFER_EMPTY) + conn->fd.when &= ~BSC_FD_WRITE; + } + + return rc; +} + +static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) { + struct telnet_connection *connection; + struct sockaddr_in sockaddr; + socklen_t len = sizeof(sockaddr); + int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); + + if (new_connection < 0) { + LOGP(DNM, LOGL_ERROR, "telnet accept failed\n"); + return -1; + } + + + connection = talloc_zero(tall_telnet_ctx, struct telnet_connection); + connection->network = (struct gsm_network*)fd->data; + connection->fd.data = connection; + connection->fd.fd = new_connection; + connection->fd.when = BSC_FD_READ; + connection->fd.cb = client_data; + bsc_register_fd(&connection->fd); + llist_add_tail(&connection->entry, &active_connections); + + print_welcome(new_connection); + + connection->vty = vty_create(new_connection, connection); + if (!connection->vty) { + LOGP(DNM, LOGL_ERROR, "couldn't create VTY\n"); + return -1; + } + + return 0; +} + +/* callback from VTY code */ +void vty_event(enum event event, int sock, struct vty *vty) +{ + struct telnet_connection *connection = vty->priv; + struct bsc_fd *bfd = &connection->fd; + + if (vty->type != VTY_TERM) + return; + + switch (event) { + case VTY_READ: + bfd->when |= BSC_FD_READ; + break; + case VTY_WRITE: + bfd->when |= BSC_FD_WRITE; + break; + case VTY_CLOSED: + /* vty layer is about to free() vty */ + connection->vty = NULL; + telnet_close_client(bfd); + break; + default: + break; + } +} + diff --git a/openbsc/src/token_auth.c b/openbsc/src/token_auth.c new file mode 100644 index 000000000..7fefea5a4 --- /dev/null +++ b/openbsc/src/token_auth.c @@ -0,0 +1,156 @@ +/* SMS based token authentication for ad-hoc GSM networks */ + +/* (C) 2009 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 <stdio.h> +#include <osmocore/talloc.h> +#include <openbsc/signal.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_04_11.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/db.h> + +#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \ + "Your IMSI is %s, auth token is %08X, phone no is %s." + +extern struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, + const char *text); + +static char *build_sms_string(struct gsm_subscriber *subscr, u_int32_t token) +{ + char *sms_str; + unsigned int len; + + len = strlen(subscr->imsi) + 8 + strlen(TOKEN_SMS_TEXT); + sms_str = talloc_size(tall_bsc_ctx, len); + if (!sms_str) + return NULL; + + snprintf(sms_str, len, TOKEN_SMS_TEXT, subscr->imsi, token, + subscr->extension); + sms_str[len-1] = '\0'; + + return sms_str; +} + +static int token_subscr_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_subscriber *subscr = signal_data; + struct gsm_sms *sms; + int rc = 0; + + if (signal != S_SUBSCR_ATTACHED) + return 0; + + if (subscr->net->auth_policy != GSM_AUTH_POLICY_TOKEN) + return 0; + + if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) { + u_int32_t token; + char *sms_str; + + /* we've seen this subscriber for the first time. */ + rc = db_subscriber_alloc_token(subscr, &token); + if (rc != 0) { + rc = -EIO; + goto unauth; + } + + sms_str = build_sms_string(subscr, token); + if (!sms_str) { + rc = -ENOMEM; + goto unauth; + } + + sms = sms_from_text(subscr, sms_str); + talloc_free(sms_str); + if (!sms) { + rc = -ENOMEM; + goto unauth; + } + + rc = gsm411_send_sms_subscr(subscr, sms); + + /* FIXME: else, delete the subscirber from database */ +unauth: + + /* make sure we don't allow him in again unless he clicks the web UI */ + subscr->authorized = 0; + db_sync_subscriber(subscr); + if (rc) { + struct gsm_lchan *lchan = lchan_for_subscr(subscr); + if (lchan) { + u_int8_t auth_rand[16]; + /* kick the subscriber off the network */ + gsm48_tx_mm_auth_req(lchan, auth_rand, 0); + gsm48_tx_mm_auth_rej(lchan); + /* FIXME: close the channel early ?*/ + //gsm48_send_rr_Release(lchan); + } + } + } + + return rc; +} + +static int token_sms_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_sms *sms = signal_data; + struct gsm_lchan *lchan; + u_int8_t auth_rand[16]; + + + if (signal != S_SMS_DELIVERED) + return 0; + + + /* these are not the droids we've been looking for */ + if (!sms->receiver || + !(sms->receiver->flags & GSM_SUBSCRIBER_FIRST_CONTACT)) + return 0; + + + if (sms->receiver->net->auth_policy != GSM_AUTH_POLICY_TOKEN) + return 0; + + + lchan = lchan_for_subscr(sms->receiver); + if (lchan) { + /* kick the subscriber off the network */ + gsm48_tx_mm_auth_req(lchan, auth_rand, 0); + gsm48_tx_mm_auth_rej(lchan); + /* FIXME: close the channel early ?*/ + //gsm48_send_rr_Release(lchan); + } + + return 0; +} + +//static __attribute__((constructor)) void on_dso_load_token(void) +void on_dso_load_token(void) +{ + register_signal_handler(SS_SUBSCR, token_subscr_cb, NULL); + register_signal_handler(SS_SMS, token_sms_cb, NULL); +} diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c new file mode 100644 index 000000000..75a279ddc --- /dev/null +++ b/openbsc/src/transaction.c @@ -0,0 +1,171 @@ +/* GSM 04.07 Transaction handling */ + +/* (C) 2009 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 <openbsc/transaction.h> +#include <openbsc/gsm_data.h> +#include <openbsc/mncc.h> +#include <openbsc/debug.h> +#include <osmocore/talloc.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/mncc.h> +#include <openbsc/paging.h> + +void *tall_trans_ctx; + +void _gsm48_cc_trans_free(struct gsm_trans *trans); + +struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr, + u_int8_t proto, u_int8_t trans_id) +{ + struct gsm_trans *trans; + struct gsm_network *net = subscr->net; + + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->subscr == subscr && + trans->protocol == proto && + trans->transaction_id == trans_id) + return trans; + } + return NULL; +} + +struct gsm_trans *trans_find_by_callref(struct gsm_network *net, + u_int32_t callref) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->callref == callref) + return trans; + } + return NULL; +} + +struct gsm_trans *trans_alloc(struct gsm_subscriber *subscr, + u_int8_t protocol, u_int8_t trans_id, + u_int32_t callref) +{ + struct gsm_trans *trans; + + DEBUGP(DCC, "subscr=%p, subscr->net=%p\n", subscr, subscr->net); + + trans = talloc_zero(tall_trans_ctx, struct gsm_trans); + if (!trans) + return NULL; + + trans->subscr = subscr; + subscr_get(trans->subscr); + + trans->protocol = protocol; + trans->transaction_id = trans_id; + trans->callref = callref; + + llist_add_tail(&trans->entry, &subscr->net->trans_list); + + return trans; +} + +void trans_free(struct gsm_trans *trans) +{ + switch (trans->protocol) { + case GSM48_PDISC_CC: + _gsm48_cc_trans_free(trans); + break; + case GSM48_PDISC_SMS: + _gsm411_sms_trans_free(trans); + break; + } + + if (trans->lchan) + put_lchan(trans->lchan); + + if (!trans->lchan && trans->subscr && trans->subscr->net) { + /* Stop paging on all bts' */ + paging_request_stop(NULL, trans->subscr, NULL); + } + + if (trans->subscr) + subscr_put(trans->subscr); + + llist_del(&trans->entry); + + talloc_free(trans); +} + +/* allocate an unused transaction ID for the given subscriber + * in the given protocol using the ti_flag specified */ +int trans_assign_trans_id(struct gsm_subscriber *subscr, + u_int8_t protocol, u_int8_t ti_flag) +{ + struct gsm_network *net = subscr->net; + struct gsm_trans *trans; + unsigned int used_tid_bitmask = 0; + int i, j, h; + + if (ti_flag) + ti_flag = 0x8; + + /* generate bitmask of already-used TIDs for this (subscr,proto) */ + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->subscr != subscr || + trans->protocol != protocol || + trans->transaction_id == 0xff) + continue; + used_tid_bitmask |= (1 << trans->transaction_id); + } + + /* find a new one, trying to go in a 'circular' pattern */ + for (h = 6; h > 0; h--) + if (used_tid_bitmask & (1 << (h | ti_flag))) + break; + for (i = 0; i < 7; i++) { + j = ((h + i) % 7) | ti_flag; + if ((used_tid_bitmask & (1 << j)) == 0) + return j; + } + + return -1; +} + +/* update all transactions to use a different LCHAN, e.g. + * after handover has succeeded */ +int trans_lchan_change(struct gsm_lchan *lchan_old, + struct gsm_lchan *lchan_new) +{ + struct gsm_network *net = lchan_old->ts->trx->bts->network; + struct gsm_trans *trans; + int num = 0; + + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->lchan == lchan_old) { + /* drop old channel use cound */ + put_lchan(trans->lchan); + /* assign new channel */ + trans->lchan = lchan_new; + /* bump new channel use count */ + use_lchan(trans->lchan); + num++; + } + } + + return num; +} diff --git a/openbsc/src/trau_frame.c b/openbsc/src/trau_frame.c new file mode 100644 index 000000000..2bc61a513 --- /dev/null +++ b/openbsc/src/trau_frame.c @@ -0,0 +1,261 @@ +/* TRAU frame handling according to GSM TS 08.60 */ + +/* (C) 2009 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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <openbsc/trau_frame.h> +#include <openbsc/subchan_demux.h> +#include <openbsc/debug.h> + +static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num) +{ + int i; + u_int32_t ret = 0; + + for (i = offset; i < offset + num; i++) { + ret = ret << 1; + if (bitbuf[i]) + ret |= 1; + } + return ret; +} + +/* Decode according to 3.1.1 */ +static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) +{ + int i; + int d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits+0, trau_bits+17, 15); + /* C16 .. C21 */ + memcpy(fr->c_bits+15, trau_bits+310, 6); + /* T1 .. T4 */ + memcpy(fr->t_bits+0, trau_bits+316, 4); + /* D1 .. D255 */ + for (i = 32; i < 304; i+= 16) { + memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); + d_idx += 15; + } + /* D256 .. D260 */ + memcpy(fr->d_bits + d_idx, trau_bits + 305, 5); +} + +/* Decode according to 3.1.2 */ +static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) +{ + int i; + int d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits+0, trau_bits+17, 15); + /* C16 .. C25 */ + memcpy(fr->c_bits+15, trau_bits+33, 10); + /* T1 .. T4 */ + memcpy(fr->t_bits+0, trau_bits+316, 4); + /* D1 .. D5 */ + memcpy(fr->d_bits, trau_bits+43, 5); + /* D6 .. D245 */ + for (i = 48; i < 304; i += 16) { + memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); + d_idx += 15; + } + /* D246 .. D256 */ + memcpy(fr->d_bits + d_idx, trau_bits + 305, 11); +} + +int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) +{ + u_int8_t cbits5 = get_bits(trau_bits, 17, 5); + + switch (cbits5) { + case TRAU_FT_FR_UP: + case TRAU_FT_FR_DOWN: + case TRAU_FT_IDLE_UP: + case TRAU_FT_IDLE_DOWN: + case TRAU_FT_EFR: + decode_fr(fr, trau_bits); + break; + case TRAU_FT_AMR: + decode_amr(fr, trau_bits); + break; + case TRAU_FT_OM_UP: + case TRAU_FT_OM_DOWN: + case TRAU_FT_DATA_UP: + case TRAU_FT_DATA_DOWN: + case TRAU_FT_D145_SYNC: + case TRAU_FT_EDATA: + LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU " + "Frame Type 0x%02x\n", cbits5); + return -1; + break; + default: + LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU " + "Frame Type 0x%02x\n", cbits5); + return -1; + break; + } + + return 0; +} + +const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 }; +const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 }; + +/* modify an uplink TRAU frame so we can send it downlink */ +int trau_frame_up2down(struct decoded_trau_frame *fr) +{ + u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); + + switch (cbits5) { + case TRAU_FT_FR_UP: + memcpy(fr->c_bits, ft_fr_down_bits, 5); + /* clear time alignment */ + memset(fr->c_bits+5, 0, 6); + /* FIXME: SP / BFI in case of DTx */ + /* C12 .. C21 are spare and coded as '1' */ + memset(fr->c_bits+11, 0x01, 10); + break; + case TRAU_FT_EFR: + /* clear time alignment */ + memset(fr->c_bits+5, 0, 6); + /* FIXME: set UFE appropriately */ + /* FIXME: SP / BFI in case of DTx */ + break; + case TRAU_FT_IDLE_UP: + memcpy(fr->c_bits, ft_idle_down_bits, 5); + /* clear time alignment */ + memset(fr->c_bits+5, 0, 6); + /* FIXME: SP / BFI in case of DTx */ + /* C12 .. C21 are spare and coded as '1' */ + memset(fr->c_bits+11, 0x01, 10); + break; + case TRAU_FT_FR_DOWN: + case TRAU_FT_IDLE_DOWN: + case TRAU_FT_OM_DOWN: + case TRAU_FT_DATA_DOWN: + /* we cannot convert a downlink to a downlink frame */ + return -EINVAL; + break; + case TRAU_FT_AMR: + case TRAU_FT_OM_UP: + case TRAU_FT_DATA_UP: + case TRAU_FT_D145_SYNC: + case TRAU_FT_EDATA: + LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + default: + LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + } + + return 0; + +} + +static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) +{ + int i; + int d_idx = 0; + + trau_bits[16] = 1; + /* C1 .. C15 */ + memcpy(trau_bits+17, fr->c_bits+0, 15); + /* D1 .. D255 */ + for (i = 32; i < 304; i+= 16) { + trau_bits[i] = 1; + memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15); + d_idx += 15; + } + /* D256 .. D260 */ + trau_bits[304] = 1; + memcpy(trau_bits + 305, fr->d_bits + d_idx, 5); + /* C16 .. C21 */ + memcpy(trau_bits+310, fr->c_bits+15, 6); + + /* FIXME: handle timing adjustment */ + + /* T1 .. T4 */ + memcpy(trau_bits+316, fr->t_bits+0, 4); +} + + +int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) +{ + u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); + + /* 16 bits of sync header */ + memset(trau_bits, 0, 16); + + switch (cbits5) { + case TRAU_FT_FR_UP: + case TRAU_FT_FR_DOWN: + case TRAU_FT_IDLE_UP: + case TRAU_FT_IDLE_DOWN: + case TRAU_FT_EFR: + encode_fr(trau_bits, fr); + break; + case TRAU_FT_AMR: + case TRAU_FT_OM_UP: + case TRAU_FT_OM_DOWN: + case TRAU_FT_DATA_UP: + case TRAU_FT_DATA_DOWN: + case TRAU_FT_D145_SYNC: + case TRAU_FT_EDATA: + LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + default: + LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + } + + return 0; +} + +static struct decoded_trau_frame fr_idle_frame = { + .c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */ + .t_bits = { 1, 1, 1, 1 }, +}; +static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS]; +static int dbits_initted; + +u_int8_t *trau_idle_frame(void) +{ + /* only initialize during the first call */ + if (!dbits_initted) { + /* set all D-bits to 1 */ + memset(&fr_idle_frame.d_bits, 0x01, 260); + encode_fr(encoded_idle_frame, &fr_idle_frame); + } + return encoded_idle_frame; +} diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c new file mode 100644 index 000000000..f2fa5c023 --- /dev/null +++ b/openbsc/src/trau_mux.c @@ -0,0 +1,313 @@ +/* Simple TRAU frame reflector to route voice calls */ + +/* (C) 2009 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 <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/trau_frame.h> +#include <openbsc/trau_mux.h> +#include <openbsc/subchan_demux.h> +#include <openbsc/e1_input.h> +#include <openbsc/debug.h> +#include <osmocore/talloc.h> + +u_int8_t gsm_fr_map[] = { + 6, 6, 5, 5, 4, 4, 3, 3, + 7, 2, 2, 6, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 7, 2, 2, 6, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 7, 2, 2, 6, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 7, 2, 2, 6, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3 +}; + +struct map_entry { + struct llist_head list; + struct gsm_e1_subslot src, dst; +}; + +struct upqueue_entry { + struct llist_head list; + struct gsm_network *net; + struct gsm_e1_subslot src; + u_int32_t callref; +}; + +static LLIST_HEAD(ss_map); +static LLIST_HEAD(ss_upqueue); + +void *tall_map_ctx, *tall_upq_ctx; + +/* map one particular subslot to another subslot */ +int trau_mux_map(const struct gsm_e1_subslot *src, + const struct gsm_e1_subslot *dst) +{ + struct map_entry *me; + + me = talloc(tall_map_ctx, struct map_entry); + if (!me) { + LOGP(DMIB, LOGL_FATAL, "Out of memory\n"); + return -ENOMEM; + } + + DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) " + "and (e1=%u,ts=%u,ss=%u)\n", + src->e1_nr, src->e1_ts, src->e1_ts_ss, + dst->e1_nr, dst->e1_ts, dst->e1_ts_ss); + + /* make sure to get rid of any stale old mappings */ + trau_mux_unmap(src, 0); + trau_mux_unmap(dst, 0); + + memcpy(&me->src, src, sizeof(me->src)); + memcpy(&me->dst, dst, sizeof(me->dst)); + llist_add(&me->list, &ss_map); + + return 0; +} + +int trau_mux_map_lchan(const struct gsm_lchan *src, + const struct gsm_lchan *dst) +{ + struct gsm_e1_subslot *src_ss, *dst_ss; + + src_ss = &src->ts->e1_link; + dst_ss = &dst->ts->e1_link; + + return trau_mux_map(src_ss, dst_ss); +} + + +/* unmap one particular subslot from another subslot */ +int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref) +{ + struct map_entry *me, *me2; + struct upqueue_entry *ue, *ue2; + + if (ss) + llist_for_each_entry_safe(me, me2, &ss_map, list) { + if (!memcmp(&me->src, ss, sizeof(*ss)) || + !memcmp(&me->dst, ss, sizeof(*ss))) { + llist_del(&me->list); + return 0; + } + } + llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) { + if (ue->callref == callref) { + llist_del(&ue->list); + return 0; + } + if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) { + llist_del(&ue->list); + return 0; + } + } + return -ENOENT; +} + +/* look-up an enty in the TRAU mux map */ +static struct gsm_e1_subslot * +lookup_trau_mux_map(const struct gsm_e1_subslot *src) +{ + struct map_entry *me; + + llist_for_each_entry(me, &ss_map, list) { + if (!memcmp(&me->src, src, sizeof(*src))) + return &me->dst; + if (!memcmp(&me->dst, src, sizeof(*src))) + return &me->src; + } + return NULL; +} + +/* look-up an enty in the TRAU upqueue */ +struct upqueue_entry * +lookup_trau_upqueue(const struct gsm_e1_subslot *src) +{ + struct upqueue_entry *ue; + + llist_for_each_entry(ue, &ss_upqueue, list) { + if (!memcmp(&ue->src, src, sizeof(*src))) + return ue; + } + return NULL; +} + +static const u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 }; + +/* we get called by subchan_demux */ +int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, + const u_int8_t *trau_bits, int num_bits) +{ + struct decoded_trau_frame tf; + u_int8_t trau_bits_out[TRAU_FRAME_BITS]; + struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss); + struct subch_mux *mx; + struct upqueue_entry *ue; + int rc; + + /* decode TRAU, change it to downlink, re-encode */ + rc = decode_trau_frame(&tf, trau_bits); + if (rc) + return rc; + + if (!dst_e1_ss) { + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, k, l, o; + /* 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))) + DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n", + hexdump(tf.c_bits, sizeof(c_bits_check))); + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, + "GSM-DATA"); + 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_enqueue(&ue->net->upqueue, msg); + + return 0; + } + + mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); + if (!mx) + return -EINVAL; + + trau_frame_up2down(&tf); + encode_trau_frame(trau_bits_out, &tf); + + /* and send it to the muxer */ + return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, + TRAU_FRAME_BITS); +} + +/* add receiver instance for lchan and callref */ +int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref) +{ + struct gsm_e1_subslot *src_ss; + struct upqueue_entry *ue; + + ue = talloc(tall_upq_ctx, struct upqueue_entry); + if (!ue) + return -ENOMEM; + + src_ss = &lchan->ts->e1_link; + + DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) " + "and (callref 0x%x)\n", + src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss, + callref); + + /* make sure to get rid of any stale old mappings */ + trau_mux_unmap(src_ss, callref); + + memcpy(&ue->src, src_ss, sizeof(ue->src)); + ue->net = lchan->ts->trx->bts->network; + ue->callref = callref; + llist_add(&ue->list, &ss_upqueue); + + return 0; +} + +int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) +{ + u_int8_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); + if (!mx) + return -EINVAL; + + 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++; + } + break; + default: + DEBUGPC(DMUX, "unsupported message type %d\n", + frame->msg_type); + return -EINVAL; + } + + encode_trau_frame(trau_bits_out, &tf); + + /* and send it to the muxer */ + return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, + TRAU_FRAME_BITS); +} diff --git a/openbsc/src/ussd.c b/openbsc/src/ussd.c new file mode 100644 index 000000000..a3d11f080 --- /dev/null +++ b/openbsc/src/ussd.c @@ -0,0 +1,71 @@ +/* Network-specific handling of mobile-originated USSDs. */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 2009 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. + * + */ + +/* This module defines the network-specific handling of mobile-originated + USSD messages. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <openbsc/gsm_04_80.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> + +/* Declarations of USSD strings to be recognised */ +const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; + +/* Forward declarations of network-specific handler functions */ +static int send_own_number(const struct msgb *msg, const struct ussd_request *req); + + +/* Entrypoint - handler function common to all mobile-originated USSDs */ +int handle_rcv_ussd(struct msgb *msg) +{ + struct ussd_request req; + + gsm0480_decode_ussd_request(msg, &req); + if (req.text[0] == 0xFF) /* Release-Complete */ + return 0; + + if (strstr(USSD_TEXT_OWN_NUMBER, req.text) != NULL) { + DEBUGP(DMM, "USSD: Own number requested\n"); + return send_own_number(msg, &req); + } else { + DEBUGP(DMM, "Unhandled USSD %s\n", req.text); + return gsm0480_send_ussd_reject(msg, &req); + } +} + +/* A network-specific handler function */ +static int send_own_number(const struct msgb *msg, const struct ussd_request *req) +{ + char *own_number = msg->lchan->subscr->extension; + char response_string[GSM_EXTENSION_LENGTH + 20]; + + /* Need trailing CR as EOT character */ + snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); + return gsm0480_send_ussd_response(msg, response_string, req); +} diff --git a/openbsc/src/vty/buffer.c b/openbsc/src/vty/buffer.c new file mode 100644 index 000000000..195d06209 --- /dev/null +++ b/openbsc/src/vty/buffer.c @@ -0,0 +1,463 @@ +/* + * Buffering of output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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, or (at your + * option) any later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stddef.h> +#include <sys/uio.h> + +#include <osmocore/talloc.h> +#include <vty/buffer.h> +#include <vty/vty.h> + +/* Buffer master. */ +struct buffer { + /* Data list. */ + struct buffer_data *head; + struct buffer_data *tail; + + /* Size of each buffer_data chunk. */ + size_t size; +}; + +/* Data container. */ +struct buffer_data { + struct buffer_data *next; + + /* Location to add new data. */ + size_t cp; + + /* Pointer to data not yet flushed. */ + size_t sp; + + /* Actual data stream (variable length). */ + unsigned char data[0]; /* real dimension is buffer->size */ +}; + +/* It should always be true that: 0 <= sp <= cp <= size */ + +/* Default buffer size (used if none specified). It is rounded up to the + next page boundery. */ +#define BUFFER_SIZE_DEFAULT 4096 + +#define BUFFER_DATA_FREE(D) talloc_free((D)) + +/* Make new buffer. */ +struct buffer *buffer_new(size_t size) +{ + struct buffer *b; + + b = talloc_zero(tall_vty_ctx, struct buffer); + + if (size) + b->size = size; + else { + static size_t default_size; + if (!default_size) { + long pgsz = sysconf(_SC_PAGESIZE); + default_size = + ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz); + } + b->size = default_size; + } + + return b; +} + +/* Free buffer. */ +void buffer_free(struct buffer *b) +{ + buffer_reset(b); + talloc_free(b); +} + +/* Make string clone. */ +char *buffer_getstr(struct buffer *b) +{ + size_t totlen = 0; + struct buffer_data *data; + char *s; + char *p; + + for (data = b->head; data; data = data->next) + totlen += data->cp - data->sp; + if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr"))) + return NULL; + p = s; + for (data = b->head; data; data = data->next) { + memcpy(p, data->data + data->sp, data->cp - data->sp); + p += data->cp - data->sp; + } + *p = '\0'; + return s; +} + +/* Return 1 if buffer is empty. */ +int buffer_empty(struct buffer *b) +{ + return (b->head == NULL); +} + +/* Clear and free all allocated data. */ +void buffer_reset(struct buffer *b) +{ + struct buffer_data *data; + struct buffer_data *next; + + for (data = b->head; data; data = next) { + next = data->next; + BUFFER_DATA_FREE(data); + } + b->head = b->tail = NULL; +} + +/* Add buffer_data to the end of buffer. */ +static struct buffer_data *buffer_add(struct buffer *b) +{ + struct buffer_data *d; + + d = _talloc_zero(tall_vty_ctx, + offsetof(struct buffer_data, data[b->size]), + "buffer_add"); + if (!d) + return NULL; + d->cp = d->sp = 0; + d->next = NULL; + + if (b->tail) + b->tail->next = d; + else + b->head = d; + b->tail = d; + + return d; +} + +/* Write data to buffer. */ +void buffer_put(struct buffer *b, const void *p, size_t size) +{ + struct buffer_data *data = b->tail; + const char *ptr = p; + + /* We use even last one byte of data buffer. */ + while (size) { + size_t chunk; + + /* If there is no data buffer add it. */ + if (data == NULL || data->cp == b->size) + data = buffer_add(b); + + chunk = + ((size <= + (b->size - data->cp)) ? size : (b->size - data->cp)); + memcpy((data->data + data->cp), ptr, chunk); + size -= chunk; + ptr += chunk; + data->cp += chunk; + } +} + +/* Insert character into the buffer. */ +void buffer_putc(struct buffer *b, u_char c) +{ + buffer_put(b, &c, 1); +} + +/* Put string to the buffer. */ +void buffer_putstr(struct buffer *b, const char *c) +{ + buffer_put(b, c, strlen(c)); +} + +/* Keep flushing data to the fd until the buffer is empty or an error is + encountered or the operation would block. */ +buffer_status_t buffer_flush_all(struct buffer *b, int fd) +{ + buffer_status_t ret; + struct buffer_data *head; + size_t head_sp; + + if (!b->head) + return BUFFER_EMPTY; + head_sp = (head = b->head)->sp; + /* Flush all data. */ + while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) { + if ((b->head == head) && (head_sp == head->sp) + && (errno != EINTR)) + /* No data was flushed, so kernel buffer must be full. */ + return ret; + head_sp = (head = b->head)->sp; + } + + return ret; +} + +#if 0 +/* Flush enough data to fill a terminal window of the given scene (used only + by vty telnet interface). */ +buffer_status_t +buffer_flush_window(struct buffer * b, int fd, int width, int height, + int erase_flag, int no_more_flag) +{ + int nbytes; + int iov_alloc; + int iov_index; + struct iovec *iov; + struct iovec small_iov[3]; + char more[] = " --More-- "; + char erase[] = + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 + }; + struct buffer_data *data; + int column; + + if (!b->head) + return BUFFER_EMPTY; + + if (height < 1) { + zlog_warn + ("%s called with non-positive window height %d, forcing to 1", + __func__, height); + height = 1; + } else if (height >= 2) + height--; + if (width < 1) { + zlog_warn + ("%s called with non-positive window width %d, forcing to 1", + __func__, width); + width = 1; + } + + /* For erase and more data add two to b's buffer_data count. */ + if (b->head->next == NULL) { + iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]); + iov = small_iov; + } else { + iov_alloc = ((height * (width + 2)) / b->size) + 10; + iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov)); + } + iov_index = 0; + + /* Previously print out is performed. */ + if (erase_flag) { + iov[iov_index].iov_base = erase; + iov[iov_index].iov_len = sizeof erase; + iov_index++; + } + + /* Output data. */ + column = 1; /* Column position of next character displayed. */ + for (data = b->head; data && (height > 0); data = data->next) { + size_t cp; + + cp = data->sp; + while ((cp < data->cp) && (height > 0)) { + /* Calculate lines remaining and column position after displaying + this character. */ + if (data->data[cp] == '\r') + column = 1; + else if ((data->data[cp] == '\n') || (column == width)) { + column = 1; + height--; + } else + column++; + cp++; + } + iov[iov_index].iov_base = (char *)(data->data + data->sp); + iov[iov_index++].iov_len = cp - data->sp; + data->sp = cp; + + if (iov_index == iov_alloc) + /* This should not ordinarily happen. */ + { + iov_alloc *= 2; + if (iov != small_iov) { + zlog_warn("%s: growing iov array to %d; " + "width %d, height %d, size %lu", + __func__, iov_alloc, width, height, + (u_long) b->size); + iov = + XREALLOC(MTYPE_TMP, iov, + iov_alloc * sizeof(*iov)); + } else { + /* This should absolutely never occur. */ + zlog_err + ("%s: corruption detected: iov_small overflowed; " + "head %p, tail %p, head->next %p", + __func__, b->head, b->tail, b->head->next); + iov = + XMALLOC(MTYPE_TMP, + iov_alloc * sizeof(*iov)); + memcpy(iov, small_iov, sizeof(small_iov)); + } + } + } + + /* In case of `more' display need. */ + if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) { + iov[iov_index].iov_base = more; + iov[iov_index].iov_len = sizeof more; + iov_index++; + } +#ifdef IOV_MAX + /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g. + example: Solaris2.6 are defined IOV_MAX size at 16. */ + { + struct iovec *c_iov = iov; + nbytes = 0; /* Make sure it's initialized. */ + + while (iov_index > 0) { + int iov_size; + + iov_size = + ((iov_index > IOV_MAX) ? IOV_MAX : iov_index); + if ((nbytes = writev(fd, c_iov, iov_size)) < 0) { + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); + break; + } + + /* move pointer io-vector */ + c_iov += iov_size; + iov_index -= iov_size; + } + } +#else /* IOV_MAX */ + if ((nbytes = writev(fd, iov, iov_index)) < 0) + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); +#endif /* IOV_MAX */ + + /* Free printed buffer data. */ + while (b->head && (b->head->sp == b->head->cp)) { + struct buffer_data *del; + if (!(b->head = (del = b->head)->next)) + b->tail = NULL; + BUFFER_DATA_FREE(del); + } + + if (iov != small_iov) + XFREE(MTYPE_TMP, iov); + + return (nbytes < 0) ? BUFFER_ERROR : + (b->head ? BUFFER_PENDING : BUFFER_EMPTY); +} +#endif + +/* This function (unlike other buffer_flush* functions above) is designed +to work with non-blocking sockets. It does not attempt to write out +all of the queued data, just a "big" chunk. It returns 0 if it was +able to empty out the buffers completely, 1 if more flushing is +required later, or -1 on a fatal write error. */ +buffer_status_t buffer_flush_available(struct buffer * b, int fd) +{ + +/* These are just reasonable values to make sure a significant amount of +data is written. There's no need to go crazy and try to write it all +in one shot. */ +#ifdef IOV_MAX +#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX) +#else +#define MAX_CHUNKS 16 +#endif +#define MAX_FLUSH 131072 + + struct buffer_data *d; + size_t written; + struct iovec iov[MAX_CHUNKS]; + size_t iovcnt = 0; + size_t nbyte = 0; + + for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH); + d = d->next, iovcnt++) { + iov[iovcnt].iov_base = d->data + d->sp; + nbyte += (iov[iovcnt].iov_len = d->cp - d->sp); + } + + if (!nbyte) + /* No data to flush: should we issue a warning message? */ + return BUFFER_EMPTY; + + /* only place where written should be sign compared */ + if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) { + if (ERRNO_IO_RETRY(errno)) + /* Calling code should try again later. */ + return BUFFER_PENDING; + return BUFFER_ERROR; + } + + /* Free printed buffer data. */ + while (written > 0) { + struct buffer_data *d; + if (!(d = b->head)) + break; + if (written < d->cp - d->sp) { + d->sp += written; + return BUFFER_PENDING; + } + + written -= (d->cp - d->sp); + if (!(b->head = d->next)) + b->tail = NULL; + BUFFER_DATA_FREE(d); + } + + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; + +#undef MAX_CHUNKS +#undef MAX_FLUSH +} + +buffer_status_t +buffer_write(struct buffer * b, int fd, const void *p, size_t size) +{ + ssize_t nbytes; + +#if 0 + /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */ + + if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR)) + return BUFFER_ERROR; +#endif + if (b->head) + /* Buffer is not empty, so do not attempt to write the new data. */ + nbytes = 0; + else if ((nbytes = write(fd, p, size)) < 0) { + if (ERRNO_IO_RETRY(errno)) + nbytes = 0; + else + return BUFFER_ERROR; + } + /* Add any remaining data to the buffer. */ + { + size_t written = nbytes; + if (written < size) + buffer_put(b, ((const char *)p) + written, + size - written); + } + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; +} diff --git a/openbsc/src/vty/cardshell.h b/openbsc/src/vty/cardshell.h new file mode 100644 index 000000000..d963a3810 --- /dev/null +++ b/openbsc/src/vty/cardshell.h @@ -0,0 +1,5 @@ +#define QUAGGA_PROGNAME "OpenBSC" +#define QUAGGA_VERSION "0.01" +#define QUAGGA_COPYRIGHT "Harald Welte <laforge@gnumonks.org>" +#define CONFIGFILE_MASK 022 +#define SYSCONFDIR "/usr/local/etc" diff --git a/openbsc/src/vty/command.c b/openbsc/src/vty/command.c new file mode 100644 index 000000000..2faed35e4 --- /dev/null +++ b/openbsc/src/vty/command.c @@ -0,0 +1,3411 @@ +/* + $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $ + + Command interpreter routine for virtual terminal [aka TeletYpe] + Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + +This file is part of GNU Zebra. + +GNU Zebra 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, or (at your +option) any later version. + +GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "cardshell.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <errno.h> +#define _XOPEN_SOURCE +#include <unistd.h> +#include <assert.h> +#include <ctype.h> +#include <time.h> +#include <sys/time.h> +#include <sys/stat.h> + +//#include "memory.h" +//#include "log.h" +//#include <lib/version.h> +//#include "thread.h" +#include <vty/vector.h> +#include <vty/vty.h> +#include <vty/command.h> +//#include "workqueue.h" + +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> +#include <osmocore/talloc.h> + +void *tall_vty_cmd_ctx; + +/* Command vector which includes some level of command lists. Normally + each daemon maintains each own cmdvec. */ +vector cmdvec; + +/* Host information structure. */ +struct host host; + +/* Standard command node structures. */ +struct cmd_node auth_node = { + AUTH_NODE, + "Password: ", +}; + +struct cmd_node view_node = { + VIEW_NODE, + "%s> ", +}; + +struct cmd_node auth_enable_node = { + AUTH_ENABLE_NODE, + "Password: ", +}; + +struct cmd_node enable_node = { + ENABLE_NODE, + "%s# ", +}; + +struct cmd_node config_node = { + CONFIG_NODE, + "%s(config)# ", + 1 +}; + +/* Default motd string. */ +const char *default_motd = "\r\n\ +Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ +" QUAGGA_COPYRIGHT "\r\n\ +\r\n"; + +#if 0 +static struct facility_map { + int facility; + const char *name; + size_t match; +} syslog_facilities[] = { + { + LOG_KERN, "kern", 1}, { + LOG_USER, "user", 2}, { + LOG_MAIL, "mail", 1}, { + LOG_DAEMON, "daemon", 1}, { + LOG_AUTH, "auth", 1}, { + LOG_SYSLOG, "syslog", 1}, { + LOG_LPR, "lpr", 2}, { + LOG_NEWS, "news", 1}, { + LOG_UUCP, "uucp", 2}, { + LOG_CRON, "cron", 1}, +#ifdef LOG_FTP + { + LOG_FTP, "ftp", 1}, +#endif + { + LOG_LOCAL0, "local0", 6}, { + LOG_LOCAL1, "local1", 6}, { + LOG_LOCAL2, "local2", 6}, { + LOG_LOCAL3, "local3", 6}, { + LOG_LOCAL4, "local4", 6}, { + LOG_LOCAL5, "local5", 6}, { + LOG_LOCAL6, "local6", 6}, { + LOG_LOCAL7, "local7", 6}, { +0, NULL, 0},}; + +static const char *facility_name(int facility) +{ + struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (fm->facility == facility) + return fm->name; + return ""; +} + +static int facility_match(const char *str) +{ + struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (!strncmp(str, fm->name, fm->match)) + return fm->facility; + return -1; +} + +static int level_match(const char *s) +{ + int level; + + for (level = 0; zlog_priority[level] != NULL; level++) + if (!strncmp(s, zlog_priority[level], 2)) + return level; + return ZLOG_DISABLED; +} +#endif + +/* This is called from main when a daemon is invoked with -v or --version. */ +void print_version(const char *progname) +{ + printf("%s version %s\n", progname, QUAGGA_VERSION); + printf("%s\n", QUAGGA_COPYRIGHT); +} + +/* Utility function to concatenate argv argument into a single string + with inserting ' ' character between each argument. */ +char *argv_concat(const char **argv, int argc, int shift) +{ + int i; + size_t len; + char *str; + char *p; + + len = 0; + for (i = shift; i < argc; i++) + len += strlen(argv[i]) + 1; + if (!len) + return NULL; + p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat"); + for (i = shift; i < argc; i++) { + size_t arglen; + memcpy(p, argv[i], (arglen = strlen(argv[i]))); + p += arglen; + *p++ = ' '; + } + *(p - 1) = '\0'; + return str; +} + +/* Install top node of command vector. */ +void install_node(struct cmd_node *node, int (*func) (struct vty *)) +{ + vector_set_index(cmdvec, node->node, node); + node->func = func; + node->cmd_vector = vector_init(VECTOR_MIN_SIZE); +} + +/* Compare two command's string. Used in sort_node (). */ +static int cmp_node(const void *p, const void *q) +{ + struct cmd_element *a = *(struct cmd_element **)p; + struct cmd_element *b = *(struct cmd_element **)q; + + return strcmp(a->string, b->string); +} + +static int cmp_desc(const void *p, const void *q) +{ + struct desc *a = *(struct desc **)p; + struct desc *b = *(struct desc **)q; + + return strcmp(a->cmd, b->cmd); +} + +/* Sort each node's command element according to command string. */ +void sort_node() +{ + unsigned int i, j; + struct cmd_node *cnode; + vector descvec; + struct cmd_element *cmd_element; + + for (i = 0; i < vector_active(cmdvec); i++) + if ((cnode = vector_slot(cmdvec, i)) != NULL) { + vector cmd_vector = cnode->cmd_vector; + qsort(cmd_vector->index, vector_active(cmd_vector), + sizeof(void *), cmp_node); + + for (j = 0; j < vector_active(cmd_vector); j++) + if ((cmd_element = + vector_slot(cmd_vector, j)) != NULL + && vector_active(cmd_element->strvec)) { + descvec = + vector_slot(cmd_element->strvec, + vector_active + (cmd_element->strvec) - + 1); + qsort(descvec->index, + vector_active(descvec), + sizeof(void *), cmp_desc); + } + } +} + +/* Breaking up string into each command piece. I assume given + character is separated by a space character. Return value is a + vector which includes char ** data element. */ +vector cmd_make_strvec(const char *string) +{ + const char *cp, *start; + char *token; + int strlen; + vector strvec; + + if (string == NULL) + return NULL; + + cp = string; + + /* Skip white spaces. */ + while (isspace((int)*cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + if (*cp == '!' || *cp == '#') + return NULL; + + /* Prepare return vector. */ + strvec = vector_init(VECTOR_MIN_SIZE); + + /* Copy each command piece and set into vector. */ + while (1) { + start = cp; + while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') && + *cp != '\0') + cp++; + strlen = cp - start; + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec"); + memcpy(token, start, strlen); + *(token + strlen) = '\0'; + vector_set(strvec, token); + + while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') && + *cp != '\0') + cp++; + + if (*cp == '\0') + return strvec; + } +} + +/* Free allocated string vector. */ +void cmd_free_strvec(vector v) +{ + unsigned int i; + char *cp; + + if (!v) + return; + + for (i = 0; i < vector_active(v); i++) + if ((cp = vector_slot(v, i)) != NULL) + talloc_free(cp); + + vector_free(v); +} + +/* Fetch next description. Used in cmd_make_descvec(). */ +static char *cmd_desc_str(const char **string) +{ + const char *cp, *start; + char *token; + int strlen; + + cp = *string; + + if (cp == NULL) + return NULL; + + /* Skip white spaces. */ + while (isspace((int)*cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + start = cp; + + while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') + cp++; + + strlen = cp - start; + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str"); + memcpy(token, start, strlen); + *(token + strlen) = '\0'; + + *string = cp; + + return token; +} + +/* New string vector. */ +static vector cmd_make_descvec(const char *string, const char *descstr) +{ + int multiple = 0; + const char *sp; + char *token; + int len; + const char *cp; + const char *dp; + vector allvec; + vector strvec = NULL; + struct desc *desc; + + cp = string; + dp = descstr; + + if (cp == NULL) + return NULL; + + allvec = vector_init(VECTOR_MIN_SIZE); + + while (1) { + while (isspace((int)*cp) && *cp != '\0') + cp++; + + if (*cp == '(') { + multiple = 1; + cp++; + } + if (*cp == ')') { + multiple = 0; + cp++; + } + if (*cp == '|') { + if (!multiple) { + fprintf(stderr, "Command parse error!: %s\n", + string); + exit(1); + } + cp++; + } + + while (isspace((int)*cp) && *cp != '\0') + cp++; + + if (*cp == '(') { + multiple = 1; + cp++; + } + + if (*cp == '\0') + return allvec; + + sp = cp; + + while (! + (isspace((int)*cp) || *cp == '\r' || *cp == '\n' + || *cp == ')' || *cp == '|') && *cp != '\0') + cp++; + + len = cp - sp; + + token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec"); + memcpy(token, sp, len); + *(token + len) = '\0'; + + desc = talloc_zero(tall_vty_cmd_ctx, struct desc); + desc->cmd = token; + desc->str = cmd_desc_str(&dp); + + if (multiple) { + if (multiple == 1) { + strvec = vector_init(VECTOR_MIN_SIZE); + vector_set(allvec, strvec); + } + multiple++; + } else { + strvec = vector_init(VECTOR_MIN_SIZE); + vector_set(allvec, strvec); + } + vector_set(strvec, desc); + } +} + +/* Count mandantory string vector size. This is to determine inputed + command has enough command length. */ +static int cmd_cmdsize(vector strvec) +{ + unsigned int i; + int size = 0; + vector descvec; + struct desc *desc; + + for (i = 0; i < vector_active(strvec); i++) + if ((descvec = vector_slot(strvec, i)) != NULL) { + if ((vector_active(descvec)) == 1 + && (desc = vector_slot(descvec, 0)) != NULL) { + if (desc->cmd == NULL || CMD_OPTION(desc->cmd)) + return size; + else + size++; + } else + size++; + } + return size; +} + +/* Return prompt character of specified node. */ +const char *cmd_prompt(enum node_type node) +{ + struct cmd_node *cnode; + + cnode = vector_slot(cmdvec, node); + return cnode->prompt; +} + +/* Install a command into a node. */ +void install_element(enum node_type ntype, struct cmd_element *cmd) +{ + struct cmd_node *cnode; + + cnode = vector_slot(cmdvec, ntype); + + if (cnode == NULL) { + fprintf(stderr, + "Command node %d doesn't exist, please check it\n", + ntype); + exit(1); + } + + vector_set(cnode->cmd_vector, cmd); + + cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc); + cmd->cmdsize = cmd_cmdsize(cmd->strvec); +} + +#ifdef VTY_CRYPT_PW +static unsigned char itoa64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v & 0x3f]; + v >>= 6; + } +} + +static char *zencrypt(const char *passwd) +{ + char salt[6]; + struct timeval tv; + char *crypt(const char *, const char *); + + gettimeofday(&tv, 0); + + to64(&salt[0], random(), 3); + to64(&salt[3], tv.tv_usec, 3); + salt[5] = '\0'; + + return crypt(passwd, salt); +} +#endif + +/* This function write configuration of this host. */ +static int config_write_host(struct vty *vty) +{ + if (host.name) + vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE); + + if (host.encrypt) { + if (host.password_encrypt) + vty_out(vty, "password 8 %s%s", host.password_encrypt, + VTY_NEWLINE); + if (host.enable_encrypt) + vty_out(vty, "enable password 8 %s%s", + host.enable_encrypt, VTY_NEWLINE); + } else { + if (host.password) + vty_out(vty, "password %s%s", host.password, + VTY_NEWLINE); + if (host.enable) + vty_out(vty, "enable password %s%s", host.enable, + VTY_NEWLINE); + } + +#if 0 + if (zlog_default->default_lvl != LOG_DEBUG) { + vty_out(vty, "! N.B. The 'log trap' command is deprecated.%s", + VTY_NEWLINE); + vty_out(vty, "log trap %s%s", + zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); + } + + if (host.logfile + && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { + vty_out(vty, "log file %s", host.logfile); + if (zlog_default->maxlvl[ZLOG_DEST_FILE] != + zlog_default->default_lvl) + vty_out(vty, " %s", + zlog_priority[zlog_default-> + maxlvl[ZLOG_DEST_FILE]]); + vty_out(vty, "%s", VTY_NEWLINE); + } + + if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) { + vty_out(vty, "log stdout"); + if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != + zlog_default->default_lvl) + vty_out(vty, " %s", + zlog_priority[zlog_default-> + maxlvl[ZLOG_DEST_STDOUT]]); + vty_out(vty, "%s", VTY_NEWLINE); + } + + if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + vty_out(vty, "no log monitor%s", VTY_NEWLINE); + else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != + zlog_default->default_lvl) + vty_out(vty, "log monitor %s%s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]], + VTY_NEWLINE); + + if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { + vty_out(vty, "log syslog"); + if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != + zlog_default->default_lvl) + vty_out(vty, " %s", + zlog_priority[zlog_default-> + maxlvl[ZLOG_DEST_SYSLOG]]); + vty_out(vty, "%s", VTY_NEWLINE); + } + + if (zlog_default->facility != LOG_DAEMON) + vty_out(vty, "log facility %s%s", + facility_name(zlog_default->facility), VTY_NEWLINE); + + if (zlog_default->record_priority == 1) + vty_out(vty, "log record-priority%s", VTY_NEWLINE); +#endif + if (host.advanced) + vty_out(vty, "service advanced-vty%s", VTY_NEWLINE); + + if (host.encrypt) + vty_out(vty, "service password-encryption%s", VTY_NEWLINE); + + if (host.lines >= 0) + vty_out(vty, "service terminal-length %d%s", host.lines, + VTY_NEWLINE); + + if (host.motdfile) + vty_out(vty, "banner motd file %s%s", host.motdfile, + VTY_NEWLINE); + else if (!host.motd) + vty_out(vty, "no banner motd%s", VTY_NEWLINE); + + return 1; +} + +/* Utility function for getting command vector. */ +static vector cmd_node_vector(vector v, enum node_type ntype) +{ + struct cmd_node *cnode = vector_slot(v, ntype); + return cnode->cmd_vector; +} + +#if 0 +/* Filter command vector by symbol. This function is not actually used; + * should it be deleted? */ +static int cmd_filter_by_symbol(char *command, char *symbol) +{ + int i, lim; + + if (strcmp(symbol, "IPV4_ADDRESS") == 0) { + i = 0; + lim = strlen(command); + while (i < lim) { + if (! + (isdigit((int)command[i]) || command[i] == '.' + || command[i] == '/')) + return 1; + i++; + } + return 0; + } + if (strcmp(symbol, "STRING") == 0) { + i = 0; + lim = strlen(command); + while (i < lim) { + if (! + (isalpha((int)command[i]) || command[i] == '_' + || command[i] == '-')) + return 1; + i++; + } + return 0; + } + if (strcmp(symbol, "IFNAME") == 0) { + i = 0; + lim = strlen(command); + while (i < lim) { + if (!isalnum((int)command[i])) + return 1; + i++; + } + return 0; + } + return 0; +} +#endif + +/* Completion match types. */ +enum match_type { + no_match, + extend_match, + ipv4_prefix_match, + ipv4_match, + ipv6_prefix_match, + ipv6_match, + range_match, + vararg_match, + partly_match, + exact_match +}; + +static enum match_type cmd_ipv4_match(const char *str) +{ + const char *sp; + int dots = 0, nums = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) { + memset(buf, 0, sizeof(buf)); + sp = str; + while (*str != '\0') { + if (*str == '.') { + if (dots >= 3) + return no_match; + + if (*(str + 1) == '.') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy(buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + nums++; + + if (*str == '\0') + break; + + str++; + } + + if (nums < 4) + return partly_match; + + return exact_match; +} + +static enum match_type cmd_ipv4_prefix_match(const char *str) +{ + const char *sp; + int dots = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) { + memset(buf, 0, sizeof(buf)); + sp = str; + while (*str != '\0' && *str != '/') { + if (*str == '.') { + if (dots == 3) + return no_match; + + if (*(str + 1) == '.' || *(str + 1) == '/') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy(buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + if (dots == 3) { + if (*str == '/') { + if (*(str + 1) == '\0') + return partly_match; + + str++; + break; + } else if (*str == '\0') + return partly_match; + } + + if (*str == '\0') + return partly_match; + + str++; + } + + sp = str; + while (*str != '\0') { + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (atoi(sp) > 32) + return no_match; + + return exact_match; +} + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" +#define STATE_START 1 +#define STATE_COLON 2 +#define STATE_DOUBLE 3 +#define STATE_ADDR 4 +#define STATE_DOT 5 +#define STATE_SLASH 6 +#define STATE_MASK 7 + +#ifdef HAVE_IPV6 + +static enum match_type cmd_ipv6_match(const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + const char *sp = NULL; + struct sockaddr_in6 sin6_dummy; + int ret; + + if (str == NULL) + return partly_match; + + if (strspn(str, IPV6_ADDR_STR) != strlen(str)) + return no_match; + + /* use inet_pton that has a better support, + * for example inet_pton can support the automatic addresses: + * ::1.2.3.4 + */ + ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); + + if (ret == 1) + return exact_match; + + while (*str != '\0') { + switch (state) { + case STATE_START: + if (*str == ':') { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } else { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == ':') + state = STATE_DOUBLE; + else { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else { + if (*(str + 1) != '\0') + colons++; + sp = str + 1; + state = STATE_ADDR; + } + + double_colon++; + nums++; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '\0') { + if (str - sp > 3) + return no_match; + + nums++; + state = STATE_COLON; + } + if (*(str + 1) == '.') + state = STATE_DOT; + break; + case STATE_DOT: + state = STATE_ADDR; + break; + default: + break; + } + + if (nums > 8) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + +#if 0 + if (nums < 11) + return partly_match; +#endif /* 0 */ + + return exact_match; +} + +static enum match_type cmd_ipv6_prefix_match(const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + int mask; + const char *sp = NULL; + char *endptr = NULL; + + if (str == NULL) + return partly_match; + + if (strspn(str, IPV6_PREFIX_STR) != strlen(str)) + return no_match; + + while (*str != '\0' && state != STATE_MASK) { + switch (state) { + case STATE_START: + if (*str == ':') { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } else { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == '/') + return no_match; + else if (*(str + 1) == ':') + state = STATE_DOUBLE; + else { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else { + if (*(str + 1) != '\0' && *(str + 1) != '/') + colons++; + sp = str + 1; + + if (*(str + 1) == '/') + state = STATE_SLASH; + else + state = STATE_ADDR; + } + + double_colon++; + nums += 1; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '.' + || *(str + 1) == '\0' || *(str + 1) == '/') { + if (str - sp > 3) + return no_match; + + for (; sp <= str; sp++) + if (*sp == '/') + return no_match; + + nums++; + + if (*(str + 1) == ':') + state = STATE_COLON; + else if (*(str + 1) == '.') + state = STATE_DOT; + else if (*(str + 1) == '/') + state = STATE_SLASH; + } + break; + case STATE_DOT: + state = STATE_ADDR; + break; + case STATE_SLASH: + if (*(str + 1) == '\0') + return partly_match; + + state = STATE_MASK; + break; + default: + break; + } + + if (nums > 11) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + if (state < STATE_MASK) + return partly_match; + + mask = strtol(str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (mask < 0 || mask > 128) + return no_match; + +/* I don't know why mask < 13 makes command match partly. + Forgive me to make this comments. I Want to set static default route + because of lack of function to originate default in ospf6d; sorry + yasu + if (mask < 13) + return partly_match; +*/ + + return exact_match; +} + +#endif /* HAVE_IPV6 */ + +#define DECIMAL_STRLEN_MAX 10 + +static int cmd_range_match(const char *range, const char *str) +{ + char *p; + char buf[DECIMAL_STRLEN_MAX + 1]; + char *endptr = NULL; + unsigned long min, max, val; + + if (str == NULL) + return 1; + + val = strtoul(str, &endptr, 10); + if (*endptr != '\0') + return 0; + + range++; + p = strchr(range, '-'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + min = strtoul(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + range = p + 1; + p = strchr(range, '>'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + max = strtoul(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + if (val < min || val > max) + return 0; + + return 1; +} + +/* Make completion match and return match type flag. */ +static enum match_type +cmd_filter_by_completion(char *command, vector v, unsigned int index) +{ + unsigned int i; + const char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + if (index >= vector_active(cmd_element->strvec)) + vector_slot(v, i) = NULL; + else { + unsigned int j; + int matched = 0; + + descvec = + vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + str = desc->cmd; + + if (CMD_VARARG(str)) { + if (match_type < + vararg_match) + match_type = + vararg_match; + matched++; + } else if (CMD_RANGE(str)) { + if (cmd_range_match + (str, command)) { + if (match_type < + range_match) + match_type + = + range_match; + + matched++; + } + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6(str)) { + if (cmd_ipv6_match + (command)) { + if (match_type < + ipv6_match) + match_type + = + ipv6_match; + + matched++; + } + } else if (CMD_IPV6_PREFIX(str)) { + if (cmd_ipv6_prefix_match(command)) { + if (match_type < + ipv6_prefix_match) + match_type + = + ipv6_prefix_match; + + matched++; + } + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) { + if (cmd_ipv4_match + (command)) { + if (match_type < + ipv4_match) + match_type + = + ipv4_match; + + matched++; + } + } else if (CMD_IPV4_PREFIX(str)) { + if (cmd_ipv4_prefix_match(command)) { + if (match_type < + ipv4_prefix_match) + match_type + = + ipv4_prefix_match; + matched++; + } + } else + /* Check is this point's argument optional ? */ + if (CMD_OPTION(str) + || + CMD_VARIABLE(str)) { + if (match_type < + extend_match) + match_type = + extend_match; + matched++; + } else + if (strncmp + (command, str, + strlen(command)) == + 0) { + if (strcmp(command, str) + == 0) + match_type = + exact_match; + else { + if (match_type < + partly_match) + match_type + = + partly_match; + } + matched++; + } + } + if (!matched) + vector_slot(v, i) = NULL; + } + } + return match_type; +} + +/* Filter vector by command character with index. */ +static enum match_type +cmd_filter_by_string(char *command, vector v, unsigned int index) +{ + unsigned int i; + const char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + /* If given index is bigger than max string vector of command, + set NULL */ + if (index >= vector_active(cmd_element->strvec)) + vector_slot(v, i) = NULL; + else { + unsigned int j; + int matched = 0; + + descvec = + vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + str = desc->cmd; + + if (CMD_VARARG(str)) { + if (match_type < + vararg_match) + match_type = + vararg_match; + matched++; + } else if (CMD_RANGE(str)) { + if (cmd_range_match + (str, command)) { + if (match_type < + range_match) + match_type + = + range_match; + matched++; + } + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6(str)) { + if (cmd_ipv6_match + (command) == + exact_match) { + if (match_type < + ipv6_match) + match_type + = + ipv6_match; + matched++; + } + } else if (CMD_IPV6_PREFIX(str)) { + if (cmd_ipv6_prefix_match(command) == exact_match) { + if (match_type < + ipv6_prefix_match) + match_type + = + ipv6_prefix_match; + matched++; + } + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) { + if (cmd_ipv4_match + (command) == + exact_match) { + if (match_type < + ipv4_match) + match_type + = + ipv4_match; + matched++; + } + } else if (CMD_IPV4_PREFIX(str)) { + if (cmd_ipv4_prefix_match(command) == exact_match) { + if (match_type < + ipv4_prefix_match) + match_type + = + ipv4_prefix_match; + matched++; + } + } else if (CMD_OPTION(str) + || CMD_VARIABLE(str)) + { + if (match_type < + extend_match) + match_type = + extend_match; + matched++; + } else { + if (strcmp(command, str) + == 0) { + match_type = + exact_match; + matched++; + } + } + } + if (!matched) + vector_slot(v, i) = NULL; + } + } + return match_type; +} + +/* Check ambiguous match */ +static int +is_cmd_ambiguous(char *command, vector v, int index, enum match_type type) +{ + unsigned int i; + unsigned int j; + const char *str = NULL; + struct cmd_element *cmd_element; + const char *matched = NULL; + vector descvec; + struct desc *desc; + + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + int match = 0; + + descvec = vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + enum match_type ret; + + str = desc->cmd; + + switch (type) { + case exact_match: + if (! + (CMD_OPTION(str) + || CMD_VARIABLE(str)) +&& strcmp(command, str) == 0) + match++; + break; + case partly_match: + if (! + (CMD_OPTION(str) + || CMD_VARIABLE(str)) +&& strncmp(command, str, strlen(command)) == 0) { + if (matched + && strcmp(matched, + str) != 0) + return 1; /* There is ambiguous match. */ + else + matched = str; + match++; + } + break; + case range_match: + if (cmd_range_match + (str, command)) { + if (matched + && strcmp(matched, + str) != 0) + return 1; + else + matched = str; + match++; + } + break; +#ifdef HAVE_IPV6 + case ipv6_match: + if (CMD_IPV6(str)) + match++; + break; + case ipv6_prefix_match: + if ((ret = + cmd_ipv6_prefix_match + (command)) != no_match) { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; +#endif /* HAVE_IPV6 */ + case ipv4_match: + if (CMD_IPV4(str)) + match++; + break; + case ipv4_prefix_match: + if ((ret = + cmd_ipv4_prefix_match + (command)) != no_match) { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; + case extend_match: + if (CMD_OPTION(str) + || CMD_VARIABLE(str)) + match++; + break; + case no_match: + default: + break; + } + } + if (!match) + vector_slot(v, i) = NULL; + } + return 0; +} + +/* If src matches dst return dst string, otherwise return NULL */ +static const char *cmd_entry_function(const char *src, const char *dst) +{ + /* Skip variable arguments. */ + if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) || + CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst)) + return NULL; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + /* Matched with input string. */ + if (strncmp(src, dst, strlen(src)) == 0) + return dst; + + return NULL; +} + +/* If src matches dst return dst string, otherwise return NULL */ +/* This version will return the dst string always if it is + CMD_VARIABLE for '?' key processing */ +static const char *cmd_entry_function_desc(const char *src, const char *dst) +{ + if (CMD_VARARG(dst)) + return dst; + + if (CMD_RANGE(dst)) { + if (cmd_range_match(dst, src)) + return dst; + else + return NULL; + } +#ifdef HAVE_IPV6 + if (CMD_IPV6(dst)) { + if (cmd_ipv6_match(src)) + return dst; + else + return NULL; + } + + if (CMD_IPV6_PREFIX(dst)) { + if (cmd_ipv6_prefix_match(src)) + return dst; + else + return NULL; + } +#endif /* HAVE_IPV6 */ + + if (CMD_IPV4(dst)) { + if (cmd_ipv4_match(src)) + return dst; + else + return NULL; + } + + if (CMD_IPV4_PREFIX(dst)) { + if (cmd_ipv4_prefix_match(src)) + return dst; + else + return NULL; + } + + /* Optional or variable commands always match on '?' */ + if (CMD_OPTION(dst) || CMD_VARIABLE(dst)) + return dst; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + if (strncmp(src, dst, strlen(src)) == 0) + return dst; + else + return NULL; +} + +/* Check same string element existence. If it isn't there return + 1. */ +static int cmd_unique_string(vector v, const char *str) +{ + unsigned int i; + char *match; + + for (i = 0; i < vector_active(v); i++) + if ((match = vector_slot(v, i)) != NULL) + if (strcmp(match, str) == 0) + return 0; + return 1; +} + +/* Compare string to description vector. If there is same string + return 1 else return 0. */ +static int desc_unique_string(vector v, const char *str) +{ + unsigned int i; + struct desc *desc; + + for (i = 0; i < vector_active(v); i++) + if ((desc = vector_slot(v, i)) != NULL) + if (strcmp(desc->cmd, str) == 0) + return 1; + return 0; +} + +static int cmd_try_do_shortcut(enum node_type node, char *first_word) +{ + if (first_word != NULL && + node != AUTH_NODE && + node != VIEW_NODE && + node != AUTH_ENABLE_NODE && + node != ENABLE_NODE && 0 == strcmp("do", first_word)) + return 1; + return 0; +} + +/* '?' describe command support. */ +static vector +cmd_describe_command_real(vector vline, struct vty *vty, int *status) +{ + unsigned int i; + vector cmd_vector; +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + unsigned int index; + int ret; + enum match_type match; + char *command; + static struct desc desc_cr = { "<cr>", "" }; + + /* Set index. */ + if (vector_active(vline) == 0) { + *status = CMD_ERR_NO_MATCH; + return NULL; + } else + index = vector_active(vline) - 1; + + /* Make copy vector of current node's command vector. */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + /* Prepare match vector */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + + /* Filter commands. */ + /* Only words precedes current word will be checked in this loop. */ + for (i = 0; i < index; i++) + if ((command = vector_slot(vline, i))) { + match = + cmd_filter_by_completion(command, cmd_vector, i); + + if (match == vararg_match) { + struct cmd_element *cmd_element; + vector descvec; + unsigned int j, k; + + for (j = 0; j < vector_active(cmd_vector); j++) + if ((cmd_element = + vector_slot(cmd_vector, j)) != NULL + && + (vector_active + (cmd_element->strvec))) { + descvec = + vector_slot(cmd_element-> + strvec, + vector_active + (cmd_element-> + strvec) - 1); + for (k = 0; + k < vector_active(descvec); + k++) { + struct desc *desc = + vector_slot(descvec, + k); + vector_set(matchvec, + desc); + } + } + + vector_set(matchvec, &desc_cr); + vector_free(cmd_vector); + + return matchvec; + } + + if ((ret = + is_cmd_ambiguous(command, cmd_vector, i, + match)) == 1) { + vector_free(cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } else if (ret == 2) { + vector_free(cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } + + /* Prepare match vector */ + /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + + /* Make sure that cmd_vector is filtered based on current word */ + command = vector_slot(vline, index); + if (command) + match = cmd_filter_by_completion(command, cmd_vector, index); + + /* Make description vector. */ + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) { + const char *string = NULL; + vector strvec = cmd_element->strvec; + + /* if command is NULL, index may be equal to vector_active */ + if (command && index >= vector_active(strvec)) + vector_slot(cmd_vector, i) = NULL; + else { + /* Check if command is completed. */ + if (command == NULL + && index == vector_active(strvec)) { + string = "<cr>"; + if (!desc_unique_string + (matchvec, string)) + vector_set(matchvec, &desc_cr); + } else { + unsigned int j; + vector descvec = + vector_slot(strvec, index); + struct desc *desc; + + for (j = 0; j < vector_active(descvec); + j++) + if ((desc = + vector_slot(descvec, j))) { + string = + cmd_entry_function_desc + (command, + desc->cmd); + if (string) { + /* Uniqueness check */ + if (!desc_unique_string(matchvec, string)) + vector_set + (matchvec, + desc); + } + } + } + } + } + vector_free(cmd_vector); + + if (vector_slot(matchvec, 0) == NULL) { + vector_free(matchvec); + *status = CMD_ERR_NO_MATCH; + } else + *status = CMD_SUCCESS; + + return matchvec; +} + +vector cmd_describe_command(vector vline, struct vty * vty, int *status) +{ + vector ret; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_describe_command_real(shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + return cmd_describe_command_real(vline, vty, status); +} + +/* Check LCD of matched command. */ +static int cmd_lcd(char **matched) +{ + int i; + int j; + int lcd = -1; + char *s1, *s2; + char c1, c2; + + if (matched[0] == NULL || matched[1] == NULL) + return 0; + + for (i = 1; matched[i] != NULL; i++) { + s1 = matched[i - 1]; + s2 = matched[i]; + + for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) + if (c1 != c2) + break; + + if (lcd < 0) + lcd = j; + else { + if (lcd > j) + lcd = j; + } + } + return lcd; +} + +/* Command line completion support. */ +static char **cmd_complete_command_real(vector vline, struct vty *vty, + int *status) +{ + unsigned int i; + vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + unsigned int index; + char **match_str; + struct desc *desc; + vector descvec; + char *command; + int lcd; + + if (vector_active(vline) == 0) { + *status = CMD_ERR_NO_MATCH; + return NULL; + } else + index = vector_active(vline) - 1; + + /* First, filter by preceeding command string */ + for (i = 0; i < index; i++) + if ((command = vector_slot(vline, i))) { + enum match_type match; + int ret; + + /* First try completion match, if there is exactly match return 1 */ + match = + cmd_filter_by_completion(command, cmd_vector, i); + + /* If there is exact match then filter ambiguous match else check + ambiguousness. */ + if ((ret = + is_cmd_ambiguous(command, cmd_vector, i, + match)) == 1) { + vector_free(cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + /* + else if (ret == 2) + { + vector_free (cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + */ + } + + /* Prepare match vector. */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + + /* Now we got into completion */ + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i))) { + const char *string; + vector strvec = cmd_element->strvec; + + /* Check field length */ + if (index >= vector_active(strvec)) + vector_slot(cmd_vector, i) = NULL; + else { + unsigned int j; + + descvec = vector_slot(strvec, index); + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd))) + if (cmd_unique_string (matchvec, string)) + vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string)); + } + } + } + + /* We don't need cmd_vector any more. */ + vector_free(cmd_vector); + + /* No matched command */ + if (vector_slot(matchvec, 0) == NULL) { + vector_free(matchvec); + + /* In case of 'command \t' pattern. Do you need '?' command at + the end of the line. */ + if (vector_slot(vline, index) == '\0') + *status = CMD_ERR_NOTHING_TODO; + else + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + /* Only one matched */ + if (vector_slot(matchvec, 1) == NULL) { + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + *status = CMD_COMPLETE_FULL_MATCH; + return match_str; + } + /* Make it sure last element is NULL. */ + vector_set(matchvec, NULL); + + /* Check LCD of matched strings. */ + if (vector_slot(vline, index) != NULL) { + lcd = cmd_lcd((char **)matchvec->index); + + if (lcd) { + int len = strlen(vector_slot(vline, index)); + + if (len < lcd) { + char *lcdstr; + + lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1, + "complete-lcdstr"); + memcpy(lcdstr, matchvec->index[0], lcd); + lcdstr[lcd] = '\0'; + + /* match_str = (char **) &lcdstr; */ + + /* Free matchvec. */ + for (i = 0; i < vector_active(matchvec); i++) { + if (vector_slot(matchvec, i)) + talloc_free(vector_slot(matchvec, i)); + } + vector_free(matchvec); + + /* Make new matchvec. */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + vector_set(matchvec, lcdstr); + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + + *status = CMD_COMPLETE_MATCH; + return match_str; + } + } + } + + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + *status = CMD_COMPLETE_LIST_MATCH; + return match_str; +} + +char **cmd_complete_command(vector vline, struct vty *vty, int *status) +{ + char **ret; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_complete_command_real(shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + return cmd_complete_command_real(vline, vty, status); +} + +/* return parent node */ +/* MUST eventually converge on CONFIG_NODE */ +enum node_type vty_go_parent(struct vty *vty) +{ + assert(vty->node > CONFIG_NODE); + + switch (vty->node) { + case GSMNET_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case BTS_NODE: + vty->node = GSMNET_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts *bts = vty->index; + vty->index = bts->network; + } + break; + case TRX_NODE: + vty->node = BTS_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx *trx = vty->index; + vty->index = trx->bts; + } + break; + case TS_NODE: + vty->node = TRX_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx_ts *ts = vty->index; + vty->index = ts->trx; + } + break; + case SUBSCR_NODE: + vty->node = VIEW_NODE; + subscr_put(vty->index); + vty->index = NULL; + break; + default: + vty->node = CONFIG_NODE; + } + + return vty->node; +} + +/* Execute command by argument vline vector. */ +static int +cmd_execute_command_real(vector vline, struct vty *vty, + struct cmd_element **cmd) +{ + unsigned int i; + unsigned int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + const char *argv[CMD_ARGC_MAX]; + enum match_type match = 0; + int varflag; + char *command; + + /* Make copy of command elements. */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + for (index = 0; index < vector_active(vline); index++) + if ((command = vector_slot(vline, index))) { + int ret; + + match = + cmd_filter_by_completion(command, cmd_vector, + index); + + if (match == vararg_match) + break; + + ret = + is_cmd_ambiguous(command, cmd_vector, index, match); + + if (ret == 1) { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } else if (ret == 2) { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i))) { + if (match == vararg_match + || index >= cmd_element->cmdsize) { + matched_element = cmd_element; +#if 0 + printf("DEBUG: %s\n", cmd_element->string); +#endif + matched_count++; + } else { + incomplete_count++; + } + } + + /* Finish of using cmd_vector. */ + vector_free(cmd_vector); + + /* To execute command, matched_count must be 1. */ + if (matched_count == 0) { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_active(vline); i++) { + if (varflag) + argv[argc++] = vector_slot(vline, i); + else { + vector descvec = + vector_slot(matched_element->strvec, i); + + if (vector_active(descvec) == 1) { + struct desc *desc = vector_slot(descvec, 0); + + if (CMD_VARARG(desc->cmd)) + varflag = 1; + + if (varflag || CMD_VARIABLE(desc->cmd) + || CMD_OPTION(desc->cmd)) + argv[argc++] = vector_slot(vline, i); + } else + argv[argc++] = vector_slot(vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Execute matched command. */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +int +cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd, + int vtysh) +{ + int ret, saved_ret, tried = 0; + enum node_type onode; + void *oindex; + + onode = vty->node; + oindex = vty->index; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + vector shifted_vline; + unsigned int index; + + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_execute_command_real(shifted_vline, vty, cmd); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + saved_ret = ret = cmd_execute_command_real(vline, vty, cmd); + + if (vtysh) + return saved_ret; + + /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ + while (ret != CMD_SUCCESS && ret != CMD_WARNING + && vty->node > CONFIG_NODE) { + vty_go_parent(vty); + ret = cmd_execute_command_real(vline, vty, cmd); + tried = 1; + if (ret == CMD_SUCCESS || ret == CMD_WARNING) { + /* succesfull command, leave the node as is */ + return ret; + } + } + /* no command succeeded, reset the vty to the original node and + return the error for this node */ + if (tried) { + vty->node = onode; + vty->index = oindex; + } + return saved_ret; +} + +/* Execute command by argument readline. */ +int +cmd_execute_command_strict(vector vline, struct vty *vty, + struct cmd_element **cmd) +{ + unsigned int i; + unsigned int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + const char *argv[CMD_ARGC_MAX]; + int varflag; + enum match_type match = 0; + char *command; + + /* Make copy of command element */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + for (index = 0; index < vector_active(vline); index++) + if ((command = vector_slot(vline, index))) { + int ret; + + match = cmd_filter_by_string(vector_slot(vline, index), + cmd_vector, index); + + /* If command meets '.VARARG' then finish matching. */ + if (match == vararg_match) + break; + + ret = + is_cmd_ambiguous(command, cmd_vector, index, match); + if (ret == 1) { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + if (ret == 2) { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + for (i = 0; i < vector_active(cmd_vector); i++) + if (vector_slot(cmd_vector, i) != NULL) { + cmd_element = vector_slot(cmd_vector, i); + + if (match == vararg_match + || index >= cmd_element->cmdsize) { + matched_element = cmd_element; + matched_count++; + } else + incomplete_count++; + } + + /* Finish of using cmd_vector. */ + vector_free(cmd_vector); + + /* To execute command, matched_count must be 1. */ + if (matched_count == 0) { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_active(vline); i++) { + if (varflag) + argv[argc++] = vector_slot(vline, i); + else { + vector descvec = + vector_slot(matched_element->strvec, i); + + if (vector_active(descvec) == 1) { + struct desc *desc = vector_slot(descvec, 0); + + if (CMD_VARARG(desc->cmd)) + varflag = 1; + + if (varflag || CMD_VARIABLE(desc->cmd) + || CMD_OPTION(desc->cmd)) + argv[argc++] = vector_slot(vline, i); + } else + argv[argc++] = vector_slot(vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Now execute matched command */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +/* Configration make from file. */ +int config_from_file(struct vty *vty, FILE * fp) +{ + int ret; + vector vline; + + while (fgets(vty->buf, VTY_BUFSIZ, fp)) { + vline = cmd_make_strvec(vty->buf); + + /* In case of comment line */ + if (vline == NULL) + continue; + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict(vline, vty, NULL); + + /* Try again with setting node to CONFIG_NODE */ + while (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_NOTHING_TODO + && vty->node != CONFIG_NODE) { + vty_go_parent(vty); + ret = cmd_execute_command_strict(vline, vty, NULL); + } + + cmd_free_strvec(vline); + + if (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_NOTHING_TODO) + return ret; + } + return CMD_SUCCESS; +} + +/* Configration from terminal */ +DEFUN(config_terminal, + config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" "Configuration terminal\n") +{ + if (vty_config_lock(vty)) + vty->node = CONFIG_NODE; + else { + vty_out(vty, "VTY configuration is locked by other VTY%s", + VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* Enable command */ +DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") +{ + /* If enable password is NULL, change to ENABLE_NODE */ + if ((host.enable == NULL && host.enable_encrypt == NULL) || + vty->type == VTY_SHELL_SERV) + vty->node = ENABLE_NODE; + else + vty->node = AUTH_ENABLE_NODE; + + return CMD_SUCCESS; +} + +/* Disable command */ +DEFUN(disable, + config_disable_cmd, "disable", "Turn off privileged mode command\n") +{ + if (vty->node == ENABLE_NODE) + vty->node = VIEW_NODE; + return CMD_SUCCESS; +} + +/* Down vty node level. */ +DEFUN(config_exit, + config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + switch (vty->node) { + case GSMNET_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case BTS_NODE: + vty->node = GSMNET_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts *bts = vty->index; + vty->index = bts->network; + } + break; + case TRX_NODE: + vty->node = BTS_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx *trx = vty->index; + vty->index = trx->bts; + } + break; + case TS_NODE: + vty->node = TRX_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx_ts *ts = vty->index; + vty->index = ts->trx; + } + break; + case SUBSCR_NODE: + vty->node = VIEW_NODE; + subscr_put(vty->index); + vty->index = NULL; + break; + case VIEW_NODE: + case ENABLE_NODE: + if (0) //vty_shell (vty)) + exit(0); + else + vty->status = VTY_CLOSE; + break; + case CONFIG_NODE: + vty->node = ENABLE_NODE; + vty_config_unlock(vty); + break; + case VTY_NODE: + vty->node = CONFIG_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* quit is alias of exit. */ +ALIAS(config_exit, + config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") + +/* End of configuration. */ + DEFUN(config_end, + config_end_cmd, "end", "End current mode and change to enable mode.") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* Show version. */ +DEFUN(show_version, + show_version_cmd, "show version", SHOW_STR "Displays program version\n") +{ + vty_out(vty, "%s %s (%s).%s", QUAGGA_PROGNAME, QUAGGA_VERSION, + host.name ? host.name : "", VTY_NEWLINE); + vty_out(vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +DEFUN(config_help, + config_help_cmd, "help", "Description of the interactive help system\n") +{ + vty_out(vty, + "This VTY provides advanced help features. When you need help,%s\ +anytime at the command line please press '?'.%s\ +%s\ +If nothing matches, the help list will be empty and you must backup%s\ + until entering a '?' shows the available options.%s\ +Two styles of help are provided:%s\ +1. Full help is available when you are ready to enter a%s\ +command argument (e.g. 'show ?') and describes each possible%s\ +argument.%s\ +2. Partial help is provided when an abbreviated argument is entered%s\ + and you want to know what arguments match the input%s\ + (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +DEFUN(config_list, config_list_cmd, "list", "Print command list\n") +{ + unsigned int i; + struct cmd_node *cnode = vector_slot(cmdvec, vty->node); + struct cmd_element *cmd; + + for (i = 0; i < vector_active(cnode->cmd_vector); i++) + if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL + && !(cmd->attr == CMD_ATTR_DEPRECATED + || cmd->attr == CMD_ATTR_HIDDEN)) + vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Write current configuration into file. */ +DEFUN(config_write_file, + config_write_file_cmd, + "write file", + "Write running configuration to memory, network, or terminal\n" + "Write to configuration file\n") +{ + unsigned int i; + int fd; + struct cmd_node *node; + char *config_file; + char *config_file_tmp = NULL; + char *config_file_sav = NULL; + struct vty *file_vty; + + /* Check and see if we are operating under vtysh configuration */ + if (host.config == NULL) { + vty_out(vty, "Can't save to configuration file, using vtysh.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get filename. */ + config_file = host.config; + + config_file_sav = + _talloc_zero(tall_vty_cmd_ctx, + strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1, + "config_file_sav"); + strcpy(config_file_sav, config_file); + strcat(config_file_sav, CONF_BACKUP_EXT); + + config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8, + "config_file_tmp"); + sprintf(config_file_tmp, "%s.XXXXXX", config_file); + + /* Open file to configuration write. */ + fd = mkstemp(config_file_tmp); + if (fd < 0) { + vty_out(vty, "Can't open configuration file %s.%s", + config_file_tmp, VTY_NEWLINE); + talloc_free(config_file_tmp); + talloc_free(config_file_sav); + return CMD_WARNING; + } + + /* Make vty for configuration file. */ + file_vty = vty_new(); + file_vty->fd = fd; + file_vty->type = VTY_FILE; + + /* Config file header print. */ + vty_out(file_vty, "!\n! OpenBSC configuration saved from vty\n! "); + //vty_time_print (file_vty, 1); + vty_out(file_vty, "!\n"); + + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func) { + if ((*node->func) (file_vty)) + vty_out(file_vty, "!\n"); + } + vty_close(file_vty); + + if (unlink(config_file_sav) != 0) + if (errno != ENOENT) { + vty_out(vty, + "Can't unlink backup configuration file %s.%s", + config_file_sav, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + if (link(config_file, config_file_sav) != 0) { + vty_out(vty, "Can't backup old configuration file %s.%s", + config_file_sav, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + sync(); + if (unlink(config_file) != 0) { + vty_out(vty, "Can't unlink configuration file %s.%s", + config_file, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + if (link(config_file_tmp, config_file) != 0) { + vty_out(vty, "Can't save configuration file %s.%s", config_file, + VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + unlink(config_file_tmp); + sync(); + + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + + if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) { + vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s", + config_file, strerror(errno), errno, VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE); + return CMD_SUCCESS; +} + +ALIAS(config_write_file, + config_write_cmd, + "write", "Write running configuration to memory, network, or terminal\n") + + ALIAS(config_write_file, + config_write_memory_cmd, + "write memory", + "Write running configuration to memory, network, or terminal\n" + "Write configuration to the file (same as write file)\n") + + ALIAS(config_write_file, + copy_runningconfig_startupconfig_cmd, + "copy running-config startup-config", + "Copy configuration\n" + "Copy running config to... \n" + "Copy running config to startup config (same as write file)\n") + +/* Write current configuration into the terminal. */ + DEFUN(config_write_terminal, + config_write_terminal_cmd, + "write terminal", + "Write running configuration to memory, network, or terminal\n" + "Write to terminal\n") +{ + unsigned int i; + struct cmd_node *node; + + if (vty->type == VTY_SHELL_SERV) { + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func + && node->vtysh) { + if ((*node->func) (vty)) + vty_out(vty, "!%s", VTY_NEWLINE); + } + } else { + vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func) { + if ((*node->func) (vty)) + vty_out(vty, "!%s", VTY_NEWLINE); + } + vty_out(vty, "end%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* Write current configuration into the terminal. */ +ALIAS(config_write_terminal, + show_running_config_cmd, + "show running-config", SHOW_STR "running configuration\n") + +/* Write startup configuration into the terminal. */ + DEFUN(show_startup_config, + show_startup_config_cmd, + "show startup-config", SHOW_STR "Contentes of startup configuration\n") +{ + char buf[BUFSIZ]; + FILE *confp; + + confp = fopen(host.config, "r"); + if (confp == NULL) { + vty_out(vty, "Can't open configuration file [%s]%s", + host.config, VTY_NEWLINE); + return CMD_WARNING; + } + + while (fgets(buf, BUFSIZ, confp)) { + char *cp = buf; + + while (*cp != '\r' && *cp != '\n' && *cp != '\0') + cp++; + *cp = '\0'; + + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + } + + fclose(confp); + + return CMD_SUCCESS; +} + +/* Hostname configuration */ +DEFUN(config_hostname, + hostname_cmd, + "hostname WORD", + "Set system's network name\n" "This system's network name\n") +{ + if (!isalpha((int)*argv[0])) { + vty_out(vty, "Please specify string starting with alphabet%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.name) + talloc_free(host.name); + + host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(config_no_hostname, + no_hostname_cmd, + "no hostname [HOSTNAME]", + NO_STR "Reset system's network name\n" "Host name of this router\n") +{ + if (host.name) + talloc_free(host.name); + host.name = NULL; + return CMD_SUCCESS; +} + +/* VTY interface password set. */ +DEFUN(config_password, password_cmd, + "password (8|) WORD", + "Assign the terminal connection password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" "The HIDDEN line password string\n") +{ + /* Argument check. */ + if (argc == 0) { + vty_out(vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) { + if (*argv[0] == '8') { + if (host.password) + talloc_free(host.password); + host.password = NULL; + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); + return CMD_SUCCESS; + } else { + vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum((int)*argv[0])) { + vty_out(vty, + "Please specify string starting with alphanumeric%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.password) + talloc_free(host.password); + host.password = NULL; + +#ifdef VTY_CRYPT_PW + if (host.encrypt) { + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); + } else +#endif + host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS(config_password, password_text_cmd, + "password LINE", + "Assign the terminal connection password\n" + "The UNENCRYPTED (cleartext) line password\n") + +/* VTY enable password set. */ + DEFUN(config_enable_password, enable_password_cmd, + "enable password (8|) WORD", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" "The HIDDEN 'enable' password string\n") +{ + /* Argument check. */ + if (argc == 0) { + vty_out(vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Crypt type is specified. */ + if (argc == 2) { + if (*argv[0] == '8') { + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); + + return CMD_SUCCESS; + } else { + vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum((int)*argv[0])) { + vty_out(vty, + "Please specify string starting with alphanumeric%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + /* Plain password input. */ +#ifdef VTY_CRYPT_PW + if (host.encrypt) { + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); + } else +#endif + host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS(config_enable_password, + enable_password_text_cmd, + "enable password LINE", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "The UNENCRYPTED (cleartext) 'enable' password\n") + +/* VTY enable password delete. */ + DEFUN(no_config_enable_password, no_enable_password_cmd, + "no enable password", + NO_STR + "Modify enable password parameters\n" + "Assign the privileged level password\n") +{ + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} + +#ifdef VTY_CRYPT_PW +DEFUN(service_password_encrypt, + service_password_encrypt_cmd, + "service password-encryption", + "Set up miscellaneous service\n" "Enable encrypted passwords\n") +{ + if (host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 1; + + if (host.password) { + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password)); + } + if (host.enable) { + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable)); + } + + return CMD_SUCCESS; +} + +DEFUN(no_service_password_encrypt, + no_service_password_encrypt_cmd, + "no service password-encryption", + NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n") +{ + if (!host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 0; + + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} +#endif + +DEFUN(config_terminal_length, config_terminal_length_cmd, + "terminal length <0-512>", + "Set terminal line parameters\n" + "Set number of lines on a screen\n" + "Number of lines on screen (0 for no pausing)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol(argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') { + vty_out(vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty->lines = lines; + + return CMD_SUCCESS; +} + +DEFUN(config_terminal_no_length, config_terminal_no_length_cmd, + "terminal no length", + "Set terminal line parameters\n" + NO_STR "Set number of lines on a screen\n") +{ + vty->lines = -1; + return CMD_SUCCESS; +} + +DEFUN(service_terminal_length, service_terminal_length_cmd, + "service terminal-length <0-512>", + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol(argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') { + vty_out(vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + host.lines = lines; + + return CMD_SUCCESS; +} + +DEFUN(no_service_terminal_length, no_service_terminal_length_cmd, + "no service terminal-length [<0-512>]", + NO_STR + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + host.lines = -1; + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(do_echo, + echo_cmd, + "echo .MESSAGE", + "Echo a message back to the vty\n" "The message to echo\n") +{ + char *message; + + vty_out(vty, "%s%s", + ((message = + argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE); + if (message) + talloc_free(message); + return CMD_SUCCESS; +} + +#if 0 +DEFUN(config_logmsg, + config_logmsg_cmd, + "logmsg " LOG_LEVELS " .MESSAGE", + "Send a message to enabled logging destinations\n" + LOG_LEVEL_DESC "The message to send\n") +{ + int level; + char *message; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog(NULL, level, + ((message = argv_concat(argv, argc, 1)) ? message : "")); + if (message) + talloc_free(message); + return CMD_SUCCESS; +} + +DEFUN(show_logging, + show_logging_cmd, + "show logging", SHOW_STR "Show current logging configuration\n") +{ + struct zlog *zl = zlog_default; + + vty_out(vty, "Syslog logging: "); + if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s, facility %s, ident %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], + facility_name(zl->facility), zl->ident); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Stdout logging: "); + if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Monitor logging: "); + if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "File logging: "); + if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s, filename %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], + zl->filename); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Protocol name: %s%s", + zlog_proto_names[zl->protocol], VTY_NEWLINE); + vty_out(vty, "Record priority: %s%s", + (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(config_log_stdout, + config_log_stdout_cmd, + "log stdout", "Logging control\n" "Set stdout logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_stdout_level, + config_log_stdout_level_cmd, + "log stdout " LOG_LEVELS, + "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_STDOUT, level); + return CMD_SUCCESS; +} + +DEFUN(no_config_log_stdout, + no_config_log_stdout_cmd, + "no log stdout [LEVEL]", + NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +DEFUN(config_log_monitor, + config_log_monitor_cmd, + "log monitor", + "Logging control\n" "Set terminal line (monitor) logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_monitor_level, + config_log_monitor_level_cmd, + "log monitor " LOG_LEVELS, + "Logging control\n" + "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_MONITOR, level); + return CMD_SUCCESS; +} + +DEFUN(no_config_log_monitor, + no_config_log_monitor_cmd, + "no log monitor [LEVEL]", + NO_STR + "Logging control\n" + "Disable terminal line (monitor) logging\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +static int set_log_file(struct vty *vty, const char *fname, int loglevel) +{ + int ret; + char *p = NULL; + const char *fullpath; + + /* Path detection. */ + if (!IS_DIRECTORY_SEP(*fname)) { + char cwd[MAXPATHLEN + 1]; + cwd[MAXPATHLEN] = '\0'; + + if (getcwd(cwd, MAXPATHLEN) == NULL) { + zlog_err("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + + if ((p = _talloc_zero(tall_vcmd_ctx, + strlen(cwd) + strlen(fname) + 2), + "set_log_file") + == NULL) { + zlog_err("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + sprintf(p, "%s/%s", cwd, fname); + fullpath = p; + } else + fullpath = fname; + + ret = zlog_set_file(NULL, fullpath, loglevel); + + if (p) + talloc_free(p); + + if (!ret) { + vty_out(vty, "can't open logfile %s\n", fname); + return CMD_WARNING; + } + + if (host.logfile) + talloc_free(host.logfile); + + host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname); + + return CMD_SUCCESS; +} + +DEFUN(config_log_file, + config_log_file_cmd, + "log file FILENAME", + "Logging control\n" "Logging to file\n" "Logging filename\n") +{ + return set_log_file(vty, argv[0], zlog_default->default_lvl); +} + +DEFUN(config_log_file_level, + config_log_file_level_cmd, + "log file FILENAME " LOG_LEVELS, + "Logging control\n" + "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[1])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[0], level); +} + +DEFUN(no_config_log_file, + no_config_log_file_cmd, + "no log file [FILENAME]", + NO_STR + "Logging control\n" "Cancel logging to file\n" "Logging file name\n") +{ + zlog_reset_file(NULL); + + if (host.logfile) + talloc_free(host.logfile); + + host.logfile = NULL; + + return CMD_SUCCESS; +} + +ALIAS(no_config_log_file, + no_config_log_file_level_cmd, + "no log file FILENAME LEVEL", + NO_STR + "Logging control\n" + "Cancel logging to file\n" "Logging file name\n" "Logging level\n") + + DEFUN(config_log_syslog, + config_log_syslog_cmd, + "log syslog", "Logging control\n" "Set syslog logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_syslog_level, + config_log_syslog_level_cmd, + "log syslog " LOG_LEVELS, + "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level); + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(config_log_syslog_facility, + config_log_syslog_facility_cmd, + "log syslog facility " LOG_FACILITIES, + "Logging control\n" + "Logging goes to syslog\n" + "(Deprecated) Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_syslog, + no_config_log_syslog_cmd, + "no log syslog [LEVEL]", + NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +ALIAS(no_config_log_syslog, + no_config_log_syslog_facility_cmd, + "no log syslog facility " LOG_FACILITIES, + NO_STR + "Logging control\n" + "Logging goes to syslog\n" + "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) + + DEFUN(config_log_facility, + config_log_facility_cmd, + "log facility " LOG_FACILITIES, + "Logging control\n" + "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_facility, + no_config_log_facility_cmd, + "no log facility [FACILITY]", + NO_STR + "Logging control\n" + "Reset syslog facility to default (daemon)\n" "Syslog facility\n") +{ + zlog_default->facility = LOG_DAEMON; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(config_log_trap, + config_log_trap_cmd, + "log trap " LOG_LEVELS, + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) +{ + int new_level; + int i; + + if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog_default->default_lvl = new_level; + for (i = 0; i < ZLOG_NUM_DESTS; i++) + if (zlog_default->maxlvl[i] != ZLOG_DISABLED) + zlog_default->maxlvl[i] = new_level; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(no_config_log_trap, + no_config_log_trap_cmd, + "no log trap [LEVEL]", + NO_STR + "Logging control\n" + "Permit all logging information\n" "Logging level\n") +{ + zlog_default->default_lvl = LOG_DEBUG; + return CMD_SUCCESS; +} + +DEFUN(config_log_record_priority, + config_log_record_priority_cmd, + "log record-priority", + "Logging control\n" + "Log the priority of the message within the message\n") +{ + zlog_default->record_priority = 1; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_record_priority, + no_config_log_record_priority_cmd, + "no log record-priority", + NO_STR + "Logging control\n" + "Do not log the priority of the message within the message\n") +{ + zlog_default->record_priority = 0; + return CMD_SUCCESS; +} +#endif + +DEFUN(banner_motd_file, + banner_motd_file_cmd, + "banner motd file [FILE]", + "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n") +{ + if (host.motdfile) + talloc_free(host.motdfile); + host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(banner_motd_default, + banner_motd_default_cmd, + "banner motd default", + "Set banner string\n" "Strings for motd\n" "Default string\n") +{ + host.motd = default_motd; + return CMD_SUCCESS; +} + +DEFUN(no_banner_motd, + no_banner_motd_cmd, + "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n") +{ + host.motd = NULL; + if (host.motdfile) + talloc_free(host.motdfile); + host.motdfile = NULL; + return CMD_SUCCESS; +} + +/* Set config filename. Called from vty.c */ +void host_config_set(const char *filename) +{ + host.config = talloc_strdup(tall_vty_cmd_ctx, filename); +} + +void install_default(enum node_type node) +{ + install_element(node, &config_exit_cmd); + install_element(node, &config_quit_cmd); + install_element(node, &config_end_cmd); + install_element(node, &config_help_cmd); + install_element(node, &config_list_cmd); + + install_element(node, &config_write_terminal_cmd); + install_element(node, &config_write_file_cmd); + install_element(node, &config_write_memory_cmd); + install_element(node, &config_write_cmd); + install_element(node, &show_running_config_cmd); +} + +/* Initialize command interface. Install basic nodes and commands. */ +void cmd_init(int terminal) +{ + /* Allocate initial top vector of commands. */ + cmdvec = vector_init(VECTOR_MIN_SIZE); + + /* Default host value settings. */ + host.name = NULL; + host.password = NULL; + host.enable = NULL; + host.logfile = NULL; + host.config = NULL; + host.lines = -1; + host.motd = default_motd; + host.motdfile = NULL; + + /* Install top nodes. */ + install_node(&view_node, NULL); + install_node(&enable_node, NULL); + install_node(&auth_node, NULL); + install_node(&auth_enable_node, NULL); + install_node(&config_node, config_write_host); + + /* Each node's basic commands. */ + install_element(VIEW_NODE, &show_version_cmd); + if (terminal) { + install_element(VIEW_NODE, &config_list_cmd); + install_element(VIEW_NODE, &config_exit_cmd); + install_element(VIEW_NODE, &config_quit_cmd); + install_element(VIEW_NODE, &config_help_cmd); + install_element(VIEW_NODE, &config_enable_cmd); + install_element(VIEW_NODE, &config_terminal_length_cmd); + install_element(VIEW_NODE, &config_terminal_no_length_cmd); + install_element(VIEW_NODE, &echo_cmd); + } + + if (terminal) { + install_default(ENABLE_NODE); + install_element(ENABLE_NODE, &config_disable_cmd); + install_element(ENABLE_NODE, &config_terminal_cmd); + install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + } + install_element (ENABLE_NODE, &show_startup_config_cmd); + install_element(ENABLE_NODE, &show_version_cmd); + + if (terminal) { + install_element(ENABLE_NODE, &config_terminal_length_cmd); + install_element(ENABLE_NODE, &config_terminal_no_length_cmd); + install_element(ENABLE_NODE, &echo_cmd); + + install_default(CONFIG_NODE); + } + + install_element(CONFIG_NODE, &hostname_cmd); + install_element(CONFIG_NODE, &no_hostname_cmd); + + if (terminal) { + install_element(CONFIG_NODE, &password_cmd); + install_element(CONFIG_NODE, &password_text_cmd); + install_element(CONFIG_NODE, &enable_password_cmd); + install_element(CONFIG_NODE, &enable_password_text_cmd); + install_element(CONFIG_NODE, &no_enable_password_cmd); + +#ifdef VTY_CRYPT_PW + install_element(CONFIG_NODE, &service_password_encrypt_cmd); + install_element(CONFIG_NODE, &no_service_password_encrypt_cmd); +#endif + install_element(CONFIG_NODE, &banner_motd_default_cmd); + install_element(CONFIG_NODE, &banner_motd_file_cmd); + install_element(CONFIG_NODE, &no_banner_motd_cmd); + install_element(CONFIG_NODE, &service_terminal_length_cmd); + install_element(CONFIG_NODE, &no_service_terminal_length_cmd); + + } + srand(time(NULL)); +} diff --git a/openbsc/src/vty/vector.c b/openbsc/src/vty/vector.c new file mode 100644 index 000000000..db47ae59a --- /dev/null +++ b/openbsc/src/vty/vector.c @@ -0,0 +1,192 @@ +/* Generic vector interface routine + * Copyright (C) 1997 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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, or (at your option) any + * later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <stdlib.h> +#include <unistd.h> + +#include <vty/vector.h> +#include <vty/vty.h> +#include <osmocore/talloc.h> +#include <memory.h> + +void *tall_vty_vec_ctx; + +/* Initialize vector : allocate memory and return vector. */ +vector vector_init(unsigned int size) +{ + vector v = talloc_zero(tall_vty_vec_ctx, struct _vector); + if (!v) + return NULL; + + /* allocate at least one slot */ + if (size == 0) + size = 1; + + v->alloced = size; + v->active = 0; + v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size, + "vector_init:index"); + if (!v->index) { + talloc_free(v); + return NULL; + } + return v; +} + +void vector_only_wrapper_free(vector v) +{ + talloc_free(v); +} + +void vector_only_index_free(void *index) +{ + talloc_free(index); +} + +void vector_free(vector v) +{ + talloc_free(v->index); + talloc_free(v); +} + +vector vector_copy(vector v) +{ + unsigned int size; + vector new = talloc_zero(tall_vty_vec_ctx, struct _vector); + if (!new) + return NULL; + + new->active = v->active; + new->alloced = v->alloced; + + size = sizeof(void *) * (v->alloced); + new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index"); + if (!new->index) { + talloc_free(new); + return NULL; + } + memcpy(new->index, v->index, size); + + return new; +} + +/* Check assigned index, and if it runs short double index pointer */ +void vector_ensure(vector v, unsigned int num) +{ + if (v->alloced > num) + return; + + v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index, + sizeof(void *) * (v->alloced * 2)); + memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced); + v->alloced *= 2; + + if (v->alloced <= num) + vector_ensure(v, num); +} + +/* This function only returns next empty slot index. It dose not mean + the slot's index memory is assigned, please call vector_ensure() + after calling this function. */ +int vector_empty_slot(vector v) +{ + unsigned int i; + + if (v->active == 0) + return 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] == 0) + return i; + + return i; +} + +/* Set value to the smallest empty slot. */ +int vector_set(vector v, void *val) +{ + unsigned int i; + + i = vector_empty_slot(v); + vector_ensure(v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Set value to specified index slot. */ +int vector_set_index(vector v, unsigned int i, void *val) +{ + vector_ensure(v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Look up vector. */ +void *vector_lookup(vector v, unsigned int i) +{ + if (i >= v->active) + return NULL; + return v->index[i]; +} + +/* Lookup vector, ensure it. */ +void *vector_lookup_ensure(vector v, unsigned int i) +{ + vector_ensure(v, i); + return v->index[i]; +} + +/* Unset value at specified index slot. */ +void vector_unset(vector v, unsigned int i) +{ + if (i >= v->alloced) + return; + + v->index[i] = NULL; + + if (i + 1 == v->active) { + v->active--; + while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */ + } +} + +/* Count the number of not emplty slot. */ +unsigned int vector_count(vector v) +{ + unsigned int i; + unsigned count = 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] != NULL) + count++; + + return count; +} diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c new file mode 100644 index 000000000..1260f38c5 --- /dev/null +++ b/openbsc/src/vty/vty.c @@ -0,0 +1,1678 @@ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <termios.h> + +#include <sys/utsname.h> +#include <sys/param.h> + +#include <arpa/telnet.h> + +#include "cardshell.h" +#include <vty/vty.h> +#include <vty/command.h> +#include <vty/buffer.h> +#include <osmocore/talloc.h> + +/* our callback, located in telnet_interface.c */ +void vty_event(enum event event, int sock, struct vty *vty); + +extern struct host host; + +/* Vector which store each vty structure. */ +static vector vtyvec; + +vector Vvty_serv_thread; + +char *vty_cwd = NULL; + +/* Configure lock. */ +static int vty_config; + +static int no_password_check = 1; + +void *tall_vty_ctx; + +static void vty_clear_buf(struct vty *vty) +{ + memset(vty->buf, 0, vty->max); +} + +/* Allocate new vty struct. */ +struct vty *vty_new() +{ + struct vty *new = talloc_zero(tall_vty_ctx, struct vty); + + if (!new) + goto out; + + new->obuf = buffer_new(0); /* Use default buffer size. */ + if (!new->obuf) + goto out_new; + new->buf = _talloc_zero(tall_vty_ctx, VTY_BUFSIZ, "vty_new->buf"); + if (!new->buf) + goto out_obuf; + + new->max = VTY_BUFSIZ; + + return new; + +out_obuf: + buffer_free(new->obuf); +out_new: + talloc_free(new); + new = NULL; +out: + return new; +} + +/* Authentication of vty */ +static void vty_auth(struct vty *vty, char *buf) +{ + char *passwd = NULL; + enum node_type next_node = 0; + int fail; + char *crypt(const char *, const char *); + + switch (vty->node) { + case AUTH_NODE: +#ifdef VTY_CRYPT_PW + if (host.encrypt) + passwd = host.password_encrypt; + else +#endif + passwd = host.password; + if (host.advanced) + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + else + next_node = VIEW_NODE; + break; + case AUTH_ENABLE_NODE: +#ifdef VTY_CRYPT_PW + if (host.encrypt) + passwd = host.enable_encrypt; + else +#endif + passwd = host.enable; + next_node = ENABLE_NODE; + break; + } + + if (passwd) { +#ifdef VTY_CRYPT_PW + if (host.encrypt) + fail = strcmp(crypt(buf, passwd), passwd); + else +#endif + fail = strcmp(buf, passwd); + } else + fail = 1; + + if (!fail) { + vty->fail = 0; + vty->node = next_node; /* Success ! */ + } else { + vty->fail++; + if (vty->fail >= 3) { + if (vty->node == AUTH_NODE) { + vty_out(vty, + "%% Bad passwords, too many failures!%s", + VTY_NEWLINE); + vty->status = VTY_CLOSE; + } else { + /* AUTH_ENABLE_NODE */ + vty->fail = 0; + vty_out(vty, + "%% Bad enable passwords, too many failures!%s", + VTY_NEWLINE); + vty->node = VIEW_NODE; + } + } + } +} + +/* Close vty interface. */ +void vty_close(struct vty *vty) +{ + int i; + + if (vty->obuf) { + /* Flush buffer. */ + buffer_flush_all(vty->obuf, vty->fd); + + /* Free input buffer. */ + buffer_free(vty->obuf); + vty->obuf = NULL; + } + + /* Free command history. */ + for (i = 0; i < VTY_MAXHIST; i++) + if (vty->hist[i]) + talloc_free(vty->hist[i]); + + /* Unset vector. */ + vector_unset(vtyvec, vty->fd); + + /* Close socket. */ + if (vty->fd > 0) + close(vty->fd); + + if (vty->buf) { + talloc_free(vty->buf); + vty->buf = NULL; + } + + /* Check configure. */ + vty_config_unlock(vty); + + /* FIXME: memory leak. We need to call telnet_close_client() but don't + * have bfd */ + vty_event(VTY_CLOSED, vty->fd, vty); + + /* OK free vty. */ + talloc_free(vty); +} + +int vty_shell(struct vty *vty) +{ + return vty->type == VTY_SHELL ? 1 : 0; +} + + +/* VTY standard output function. */ +int vty_out(struct vty *vty, const char *format, ...) +{ + va_list args; + int len = 0; + int size = 1024; + char buf[1024]; + char *p = NULL; + + if (vty_shell(vty)) { + va_start(args, format); + vprintf(format, args); + va_end(args); + } else { + /* Try to write to initial buffer. */ + va_start(args, format); + len = vsnprintf(buf, sizeof buf, format, args); + va_end(args); + + /* Initial buffer is not enough. */ + if (len < 0 || len >= size) { + while (1) { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = talloc_realloc_size(tall_vty_ctx, p, size); + if (!p) + return -1; + + va_start(args, format); + len = vsnprintf(p, size, format, args); + va_end(args); + + if (len > -1 && len < size) + break; + } + } + + /* When initial buffer is enough to store all output. */ + if (!p) + p = buf; + + /* Pointer p must point out buffer. */ + buffer_put(vty->obuf, (u_char *) p, len); + + /* If p is not different with buf, it is allocated buffer. */ + if (p != buf) + talloc_free(p); + } + + vty_event(VTY_WRITE, vty->fd, vty); + + return len; +} + +int vty_out_newline(struct vty *vty) +{ + char *p = vty_newline(vty); + buffer_put(vty->obuf, p, strlen(p)); + return 0; +} + +int vty_config_lock(struct vty *vty) +{ + if (vty_config == 0) { + vty->config = 1; + vty_config = 1; + } + return vty->config; +} + +int vty_config_unlock(struct vty *vty) +{ + if (vty_config == 1 && vty->config == 1) { + vty->config = 0; + vty_config = 0; + } + return vty->config; +} + +/* Say hello to vty interface. */ +void vty_hello(struct vty *vty) +{ + if (host.motdfile) { + FILE *f; + char buf[4096]; + + f = fopen(host.motdfile, "r"); + if (f) { + while (fgets(buf, sizeof(buf), f)) { + char *s; + /* work backwards to ignore trailling isspace() */ + for (s = buf + strlen(buf); + (s > buf) && isspace(*(s - 1)); s--) ; + *s = '\0'; + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + } + fclose(f); + } else + vty_out(vty, "MOTD file not found%s", VTY_NEWLINE); + } else if (host.motd) + vty_out(vty, "%s", host.motd); +} + +/* Put out prompt and wait input from user. */ +static void vty_prompt(struct vty *vty) +{ + struct utsname names; + const char *hostname; + + if (vty->type == VTY_TERM) { + hostname = host.name; + if (!hostname) { + uname(&names); + hostname = names.nodename; + } + vty_out(vty, cmd_prompt(vty->node), hostname); + } +} + +/* Command execution over the vty interface. */ +static int vty_command(struct vty *vty, char *buf) +{ + int ret; + vector vline; + + /* Split readline string up into the vector */ + vline = cmd_make_strvec(buf); + + if (vline == NULL) + return CMD_SUCCESS; + + ret = cmd_execute_command(vline, vty, NULL, 0); + if (ret != CMD_SUCCESS) + switch (ret) { + case CMD_WARNING: + if (vty->type == VTY_FILE) + vty_out(vty, "Warning...%s", VTY_NEWLINE); + break; + case CMD_ERR_AMBIGUOUS: + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; + case CMD_ERR_NO_MATCH: + vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE); + break; + case CMD_ERR_INCOMPLETE: + vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; + } + cmd_free_strvec(vline); + + return ret; +} + +static const char telnet_backward_char = 0x08; +static const char telnet_space_char = ' '; + +/* Basic function to write buffer to vty. */ +static void vty_write(struct vty *vty, const char *buf, size_t nbytes) +{ + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + /* Should we do buffering here ? And make vty_flush (vty) ? */ + buffer_put(vty->obuf, buf, nbytes); +} + +/* Ensure length of input buffer. Is buffer is short, double it. */ +static void vty_ensure(struct vty *vty, int length) +{ + if (vty->max <= length) { + vty->max *= 2; + vty->buf = talloc_realloc_size(tall_vty_ctx, vty->buf, vty->max); + // FIXME: check return + } +} + +/* Basic function to insert character into vty. */ +static void vty_self_insert(struct vty *vty, char c) +{ + int i; + int length; + + vty_ensure(vty, vty->length + 1); + length = vty->length - vty->cp; + memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); + vty->buf[vty->cp] = c; + + vty_write(vty, &vty->buf[vty->cp], length + 1); + for (i = 0; i < length; i++) + vty_write(vty, &telnet_backward_char, 1); + + vty->cp++; + vty->length++; +} + +/* Self insert character 'c' in overwrite mode. */ +static void vty_self_insert_overwrite(struct vty *vty, char c) +{ + vty_ensure(vty, vty->length + 1); + vty->buf[vty->cp++] = c; + + if (vty->cp > vty->length) + vty->length++; + + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + vty_write(vty, &c, 1); +} + +/* Insert a word into vty interface with overwrite mode. */ +static void vty_insert_word_overwrite(struct vty *vty, char *str) +{ + int len = strlen(str); + vty_write(vty, str, len); + strcpy(&vty->buf[vty->cp], str); + vty->cp += len; + vty->length = vty->cp; +} + +/* Forward character. */ +static void vty_forward_char(struct vty *vty) +{ + if (vty->cp < vty->length) { + vty_write(vty, &vty->buf[vty->cp], 1); + vty->cp++; + } +} + +/* Backward character. */ +static void vty_backward_char(struct vty *vty) +{ + if (vty->cp > 0) { + vty->cp--; + vty_write(vty, &telnet_backward_char, 1); + } +} + +/* Move to the beginning of the line. */ +static void vty_beginning_of_line(struct vty *vty) +{ + while (vty->cp) + vty_backward_char(vty); +} + +/* Move to the end of the line. */ +static void vty_end_of_line(struct vty *vty) +{ + while (vty->cp < vty->length) + vty_forward_char(vty); +} + +/* Add current command line to the history buffer. */ +static void vty_hist_add(struct vty *vty) +{ + int index; + + if (vty->length == 0) + return; + + index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; + + /* Ignore the same string as previous one. */ + if (vty->hist[index]) + if (strcmp(vty->buf, vty->hist[index]) == 0) { + vty->hp = vty->hindex; + return; + } + + /* Insert history entry. */ + if (vty->hist[vty->hindex]) + talloc_free(vty->hist[vty->hindex]); + vty->hist[vty->hindex] = talloc_strdup(tall_vty_ctx, vty->buf); + + /* History index rotation. */ + vty->hindex++; + if (vty->hindex == VTY_MAXHIST) + vty->hindex = 0; + + vty->hp = vty->hindex; +} + +/* Get telnet window size. */ +static int +vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) +{ +#ifdef TELNET_OPTION_DEBUG + int i; + + for (i = 0; i < nbytes; i++) + { + switch (buf[i]) + { + case IAC: + vty_out (vty, "IAC "); + break; + case WILL: + vty_out (vty, "WILL "); + break; + case WONT: + vty_out (vty, "WONT "); + break; + case DO: + vty_out (vty, "DO "); + break; + case DONT: + vty_out (vty, "DONT "); + break; + case SB: + vty_out (vty, "SB "); + break; + case SE: + vty_out (vty, "SE "); + break; + case TELOPT_ECHO: + vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + break; + case TELOPT_SGA: + vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + break; + case TELOPT_NAWS: + vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%x ", buf[i]); + break; + } + } + vty_out (vty, "%s", VTY_NEWLINE); + +#endif /* TELNET_OPTION_DEBUG */ + + switch (buf[0]) + { + case SB: + vty->sb_len = 0; + vty->iac_sb_in_progress = 1; + return 0; + break; + case SE: + { + if (!vty->iac_sb_in_progress) + return 0; + + if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (vty->sb_buf[0]) + { + case TELOPT_NAWS: + if (vty->sb_len != TELNET_NAWS_SB_LEN) + vty_out(vty,"RFC 1073 violation detected: telnet NAWS option " + "should send %d characters, but we received %lu", + TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); + else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) + vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, " + "too small to handle the telnet NAWS option", + (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); + else + { + vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); + vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); +#ifdef TELNET_OPTION_DEBUG + vty_out(vty, "TELNET NAWS window size negotiation completed: " + "width %d, height %d%s", + vty->width, vty->height, VTY_NEWLINE); +#endif + } + break; + } + vty->iac_sb_in_progress = 0; + return 0; + break; + } + default: + break; + } + return 1; +} + +/* Execute current command line. */ +static int vty_execute(struct vty *vty) +{ + int ret; + + ret = CMD_SUCCESS; + + switch (vty->node) { + case AUTH_NODE: + case AUTH_ENABLE_NODE: + vty_auth(vty, vty->buf); + break; + default: + ret = vty_command(vty, vty->buf); + if (vty->type == VTY_TERM) + vty_hist_add(vty); + break; + } + + /* Clear command line buffer. */ + vty->cp = vty->length = 0; + vty_clear_buf(vty); + + if (vty->status != VTY_CLOSE) + vty_prompt(vty); + + return ret; +} + +/* Send WILL TELOPT_ECHO to remote server. */ +static void +vty_will_echo (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make suppress Go-Ahead telnet option. */ +static void +vty_will_suppress_go_ahead (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make don't use linemode over telnet. */ +static void +vty_dont_linemode (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Use window size. */ +static void +vty_do_window_size (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; + vty_out (vty, "%s", cmd); +} + +static void vty_kill_line_from_beginning(struct vty *); +static void vty_redraw_line(struct vty *); + +/* Print command line history. This function is called from + vty_next_line and vty_previous_line. */ +static void vty_history_print(struct vty *vty) +{ + int length; + + vty_kill_line_from_beginning(vty); + + /* Get previous line from history buffer */ + length = strlen(vty->hist[vty->hp]); + memcpy(vty->buf, vty->hist[vty->hp], length); + vty->cp = vty->length = length; + + /* Redraw current line */ + vty_redraw_line(vty); +} + +/* Show next command line history. */ +static void vty_next_line(struct vty *vty) +{ + int try_index; + + if (vty->hp == vty->hindex) + return; + + /* Try is there history exist or not. */ + try_index = vty->hp; + if (try_index == (VTY_MAXHIST - 1)) + try_index = 0; + else + try_index++; + + /* If there is not history return. */ + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print(vty); +} + +/* Show previous command line history. */ +static void vty_previous_line(struct vty *vty) +{ + int try_index; + + try_index = vty->hp; + if (try_index == 0) + try_index = VTY_MAXHIST - 1; + else + try_index--; + + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print(vty); +} + +/* This function redraw all of the command line character. */ +static void vty_redraw_line(struct vty *vty) +{ + vty_write(vty, vty->buf, vty->length); + vty->cp = vty->length; +} + +/* Forward word. */ +static void vty_forward_word(struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_forward_char(vty); + + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_forward_char(vty); +} + +/* Backward word without skipping training space. */ +static void vty_backward_pure_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char(vty); +} + +/* Backward word. */ +static void vty_backward_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_backward_char(vty); + + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char(vty); +} + +/* When '^D' is typed at the beginning of the line we move to the down + level. */ +static void vty_down_level(struct vty *vty) +{ + vty_out(vty, "%s", VTY_NEWLINE); + (*config_exit_cmd.func) (NULL, vty, 0, NULL); + vty_prompt(vty); + vty->cp = 0; +} + +/* When '^Z' is received from vty, move down to the enable mode. */ +static void vty_end_config(struct vty *vty) +{ + vty_out(vty, "%s", VTY_NEWLINE); + + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + + vty_prompt(vty); + vty->cp = 0; +} + +/* Delete a charcter at the current point. */ +static void vty_delete_char(struct vty *vty) +{ + int i; + int size; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + if (vty->length == 0) { + vty_down_level(vty); + return; + } + + if (vty->cp == vty->length) + return; /* completion need here? */ + + size = vty->length - vty->cp; + + vty->length--; + memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); + vty->buf[vty->length] = '\0'; + + vty_write(vty, &vty->buf[vty->cp], size - 1); + vty_write(vty, &telnet_space_char, 1); + + for (i = 0; i < size; i++) + vty_write(vty, &telnet_backward_char, 1); +} + +/* Delete a character before the point. */ +static void vty_delete_backward_char(struct vty *vty) +{ + if (vty->cp == 0) + return; + + vty_backward_char(vty); + vty_delete_char(vty); +} + +/* Kill rest of line from current point. */ +static void vty_kill_line(struct vty *vty) +{ + int i; + int size; + + size = vty->length - vty->cp; + + if (size == 0) + return; + + for (i = 0; i < size; i++) + vty_write(vty, &telnet_space_char, 1); + for (i = 0; i < size; i++) + vty_write(vty, &telnet_backward_char, 1); + + memset(&vty->buf[vty->cp], 0, size); + vty->length = vty->cp; +} + +/* Kill line from the beginning. */ +static void vty_kill_line_from_beginning(struct vty *vty) +{ + vty_beginning_of_line(vty); + vty_kill_line(vty); +} + +/* Delete a word before the point. */ +static void vty_forward_kill_word(struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_delete_char(vty); + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_delete_char(vty); +} + +/* Delete a word before the point. */ +static void vty_backward_kill_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_delete_backward_char(vty); + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_delete_backward_char(vty); +} + +/* Transpose chars before or at the point. */ +static void vty_transpose_chars(struct vty *vty) +{ + char c1, c2; + + /* If length is short or point is near by the beginning of line then + return. */ + if (vty->length < 2 || vty->cp < 1) + return; + + /* In case of point is located at the end of the line. */ + if (vty->cp == vty->length) { + c1 = vty->buf[vty->cp - 1]; + c2 = vty->buf[vty->cp - 2]; + + vty_backward_char(vty); + vty_backward_char(vty); + vty_self_insert_overwrite(vty, c1); + vty_self_insert_overwrite(vty, c2); + } else { + c1 = vty->buf[vty->cp]; + c2 = vty->buf[vty->cp - 1]; + + vty_backward_char(vty); + vty_self_insert_overwrite(vty, c1); + vty_self_insert_overwrite(vty, c2); + } +} + +/* Do completion at vty interface. */ +static void vty_complete_command(struct vty *vty) +{ + int i; + int ret; + char **matched = NULL; + vector vline; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + vline = cmd_make_strvec(vty->buf); + if (vline == NULL) + return; + + /* In case of 'help \t'. */ + if (isspace((int)vty->buf[vty->length - 1])) + vector_set(vline, '\0'); + + matched = cmd_complete_command(vline, vty, &ret); + + cmd_free_strvec(vline); + + vty_out(vty, "%s", VTY_NEWLINE); + switch (ret) { + case CMD_ERR_AMBIGUOUS: + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_ERR_NO_MATCH: + /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_COMPLETE_FULL_MATCH: + vty_prompt(vty); + vty_redraw_line(vty); + vty_backward_pure_word(vty); + vty_insert_word_overwrite(vty, matched[0]); + vty_self_insert(vty, ' '); + //talloc_free(matched[0]); + break; + case CMD_COMPLETE_MATCH: + vty_prompt(vty); + vty_redraw_line(vty); + vty_backward_pure_word(vty); + vty_insert_word_overwrite(vty, matched[0]); + talloc_free(matched[0]); + vector_only_index_free(matched); + return; + break; + case CMD_COMPLETE_LIST_MATCH: + for (i = 0; matched[i] != NULL; i++) { + if (i != 0 && ((i % 6) == 0)) + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%-10s ", matched[i]); + talloc_free(matched[i]); + } + vty_out(vty, "%s", VTY_NEWLINE); + + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_ERR_NOTHING_TODO: + vty_prompt(vty); + vty_redraw_line(vty); + break; + default: + break; + } + if (matched) + vector_only_index_free(matched); +} + +static void +vty_describe_fold(struct vty *vty, int cmd_width, + unsigned int desc_width, struct desc *desc) +{ + char *buf; + const char *cmd, *p; + int pos; + + cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; + + if (desc_width <= 0) { + vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str, + VTY_NEWLINE); + return; + } + + buf = _talloc_zero(tall_vty_ctx, strlen(desc->str) + 1, "describe_fold"); + if (!buf) + return; + + for (p = desc->str; strlen(p) > desc_width; p += pos + 1) { + for (pos = desc_width; pos > 0; pos--) + if (*(p + pos) == ' ') + break; + + if (pos == 0) + break; + + strncpy(buf, p, pos); + buf[pos] = '\0'; + vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); + + cmd = ""; + } + + vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); + + talloc_free(buf); +} + +/* Describe matched command function. */ +static void vty_describe_command(struct vty *vty) +{ + int ret; + vector vline; + vector describe; + unsigned int i, width, desc_width; + struct desc *desc, *desc_cr = NULL; + + vline = cmd_make_strvec(vty->buf); + + /* In case of '> ?'. */ + if (vline == NULL) { + vline = vector_init(1); + vector_set(vline, '\0'); + } else if (isspace((int)vty->buf[vty->length - 1])) + vector_set(vline, '\0'); + + describe = cmd_describe_command(vline, vty, &ret); + + vty_out(vty, "%s", VTY_NEWLINE); + + /* Ambiguous error. */ + switch (ret) { + case CMD_ERR_AMBIGUOUS: + cmd_free_strvec(vline); + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + return; + break; + case CMD_ERR_NO_MATCH: + cmd_free_strvec(vline); + vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + return; + break; + } + + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_active(describe); i++) + if ((desc = vector_slot(describe, i)) != NULL) { + unsigned int len; + + if (desc->cmd[0] == '\0') + continue; + + len = strlen(desc->cmd); + if (desc->cmd[0] == '.') + len--; + + if (width < len) + width = len; + } + + /* Get width of description string. */ + desc_width = vty->width - (width + 6); + + /* Print out description. */ + for (i = 0; i < vector_active(describe); i++) + if ((desc = vector_slot(describe, i)) != NULL) { + if (desc->cmd[0] == '\0') + continue; + + if (strcmp(desc->cmd, "<cr>") == 0) { + desc_cr = desc; + continue; + } + + if (!desc->str) + vty_out(vty, " %-s%s", + desc->cmd[0] == + '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen(desc->str)) + vty_out(vty, " %-*s %s%s", width, + desc->cmd[0] == + '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold(vty, width, desc_width, desc); + +#if 0 + vty_out(vty, " %-*s %s%s", width + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str ? desc->str : "", VTY_NEWLINE); +#endif /* 0 */ + } + + if ((desc = desc_cr)) { + if (!desc->str) + vty_out(vty, " %-s%s", + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen(desc->str)) + vty_out(vty, " %-*s %s%s", width, + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold(vty, width, desc_width, desc); + } + + cmd_free_strvec(vline); + vector_free(describe); + + vty_prompt(vty); + vty_redraw_line(vty); +} + +/* ^C stop current input and do not add command line to the history. */ +static void vty_stop_input(struct vty *vty) +{ + vty->cp = vty->length = 0; + vty_clear_buf(vty); + vty_out(vty, "%s", VTY_NEWLINE); + + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + vty_prompt(vty); + + /* Set history pointer to the latest one. */ + vty->hp = vty->hindex; +} + +#define CONTROL(X) ((X) - '@') +#define VTY_NORMAL 0 +#define VTY_PRE_ESCAPE 1 +#define VTY_ESCAPE 2 + +/* Escape character command map. */ +static void vty_escape_map(unsigned char c, struct vty *vty) +{ + switch (c) { + case ('A'): + vty_previous_line(vty); + break; + case ('B'): + vty_next_line(vty); + break; + case ('C'): + vty_forward_char(vty); + break; + case ('D'): + vty_backward_char(vty); + break; + default: + break; + } + + /* Go back to normal mode. */ + vty->escape = VTY_NORMAL; +} + +/* Quit print out to the buffer. */ +static void vty_buffer_reset(struct vty *vty) +{ + buffer_reset(vty->obuf); + vty_prompt(vty); + vty_redraw_line(vty); +} + +/* Read data via vty socket. */ +int vty_read(struct vty *vty) +{ + int i; + int nbytes; + unsigned char buf[VTY_READ_BUFSIZ]; + + int vty_sock = vty->fd; + + /* Read raw data from socket */ + if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { + if (nbytes < 0) { + if (ERRNO_IO_RETRY(errno)) { + vty_event(VTY_READ, vty_sock, vty); + return 0; + } + } + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + } + + for (i = 0; i < nbytes; i++) { + if (buf[i] == IAC) { + if (!vty->iac) { + vty->iac = 1; + continue; + } else { + vty->iac = 0; + } + } + + if (vty->iac_sb_in_progress && !vty->iac) { + if (vty->sb_len < sizeof(vty->sb_buf)) + vty->sb_buf[vty->sb_len] = buf[i]; + vty->sb_len++; + continue; + } + + if (vty->iac) { + /* In case of telnet command */ + int ret = 0; + ret = vty_telnet_option(vty, buf + i, nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + + if (vty->status == VTY_MORE) { + switch (buf[i]) { + case CONTROL('C'): + case 'q': + case 'Q': + vty_buffer_reset(vty); + break; +#if 0 /* More line does not work for "show ip bgp". */ + case '\n': + case '\r': + vty->status = VTY_MORELINE; + break; +#endif + default: + break; + } + continue; + } + + /* Escape character. */ + if (vty->escape == VTY_ESCAPE) { + vty_escape_map(buf[i], vty); + continue; + } + + /* Pre-escape status. */ + if (vty->escape == VTY_PRE_ESCAPE) { + switch (buf[i]) { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + vty_backward_word(vty); + vty->escape = VTY_NORMAL; + break; + case 'f': + vty_forward_word(vty); + vty->escape = VTY_NORMAL; + break; + case 'd': + vty_forward_kill_word(vty); + vty->escape = VTY_NORMAL; + break; + case CONTROL('H'): + case 0x7f: + vty_backward_kill_word(vty); + vty->escape = VTY_NORMAL; + break; + default: + vty->escape = VTY_NORMAL; + break; + } + continue; + } + + switch (buf[i]) { + case CONTROL('A'): + vty_beginning_of_line(vty); + break; + case CONTROL('B'): + vty_backward_char(vty); + break; + case CONTROL('C'): + vty_stop_input(vty); + break; + case CONTROL('D'): + vty_delete_char(vty); + break; + case CONTROL('E'): + vty_end_of_line(vty); + break; + case CONTROL('F'): + vty_forward_char(vty); + break; + case CONTROL('H'): + case 0x7f: + vty_delete_backward_char(vty); + break; + case CONTROL('K'): + vty_kill_line(vty); + break; + case CONTROL('N'): + vty_next_line(vty); + break; + case CONTROL('P'): + vty_previous_line(vty); + break; + case CONTROL('T'): + vty_transpose_chars(vty); + break; + case CONTROL('U'): + vty_kill_line_from_beginning(vty); + break; + case CONTROL('W'): + vty_backward_kill_word(vty); + break; + case CONTROL('Z'): + vty_end_config(vty); + break; + case '\n': + case '\r': + vty_out(vty, "%s", VTY_NEWLINE); + vty_execute(vty); + break; + case '\t': + vty_complete_command(vty); + break; + case '?': + if (vty->node == AUTH_NODE + || vty->node == AUTH_ENABLE_NODE) + vty_self_insert(vty, buf[i]); + else + vty_describe_command(vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') { + vty->escape = VTY_ESCAPE; + i++; + } else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + vty_self_insert(vty, buf[i]); + break; + } + } + + /* Check status. */ + if (vty->status == VTY_CLOSE) + vty_close(vty); + else { + vty_event(VTY_WRITE, vty_sock, vty); + vty_event(VTY_READ, vty_sock, vty); + } + return 0; +} + +/* Read up configuration file */ +static int +vty_read_file(FILE *confp) +{ + int ret; + struct vty *vty; + + vty = vty_new(); + vty->fd = 0; + vty->type = VTY_FILE; + vty->node = CONFIG_NODE; + + ret = config_from_file(vty, confp); + + if (ret != CMD_SUCCESS) { + switch (ret) { + case CMD_ERR_AMBIGUOUS: + fprintf(stderr, "Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf(stderr, "There is no such command.\n"); + break; + } + fprintf(stderr, "Error occurred during reading below " + "line:\n%s\n", vty->buf); + vty_close(vty); + return -EINVAL; + } + + vty_close(vty); + return 0; +} + +/* Create new vty structure. */ +struct vty * +vty_create (int vty_sock, void *priv) +{ + struct vty *vty; + + struct termios t; + + tcgetattr(vty_sock, &t); + cfmakeraw(&t); + tcsetattr(vty_sock, TCSANOW, &t); + + /* Allocate new vty structure and set up default values. */ + vty = vty_new (); + vty->fd = vty_sock; + vty->priv = priv; + vty->type = VTY_TERM; + if (no_password_check) + { + if (host.advanced) + vty->node = ENABLE_NODE; + else + vty->node = VIEW_NODE; + } + else + vty->node = AUTH_NODE; + vty->fail = 0; + vty->cp = 0; + vty_clear_buf (vty); + vty->length = 0; + memset (vty->hist, 0, sizeof (vty->hist)); + vty->hp = 0; + vty->hindex = 0; + vector_set_index (vtyvec, vty_sock, vty); + vty->status = VTY_NORMAL; + if (host.lines >= 0) + vty->lines = host.lines; + else + vty->lines = -1; + + if (! no_password_check) + { + /* Vty is not available if password isn't set. */ + if (host.password == NULL && host.password_encrypt == NULL) + { + vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + vty_close (vty); + return NULL; + } + } + + /* Say hello to the world. */ + vty_hello (vty); + if (! no_password_check) + vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + /* Setting up terminal. */ + vty_will_echo (vty); + vty_will_suppress_go_ahead (vty); + + vty_dont_linemode (vty); + vty_do_window_size (vty); + /* vty_dont_lflow_ahead (vty); */ + + vty_prompt (vty); + + /* Add read/write thread. */ + vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_READ, vty_sock, vty); + + return vty; +} + +DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n") +{ + unsigned int i; + struct vty *v; + + for (i = 0; i < vector_active(vtyvec); i++) + if ((v = vector_slot(vtyvec, i)) != NULL) + vty_out(vty, "%svty[%d] %s", + v->config ? "*" : " ", i, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Move to vty configuration mode. */ +DEFUN(line_vty, + line_vty_cmd, + "line vty", "Configure a terminal line\n" "Virtual terminal\n") +{ + vty->node = VTY_NODE; + return CMD_SUCCESS; +} + +/* vty login. */ +DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n") +{ + no_password_check = 0; + return CMD_SUCCESS; +} + +DEFUN(no_vty_login, + no_vty_login_cmd, "no login", NO_STR "Enable password checking\n") +{ + no_password_check = 1; + return CMD_SUCCESS; +} + +DEFUN(service_advanced_vty, + service_advanced_vty_cmd, + "service advanced-vty", + "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") +{ + host.advanced = 1; + return CMD_SUCCESS; +} + +DEFUN(no_service_advanced_vty, + no_service_advanced_vty_cmd, + "no service advanced-vty", + NO_STR + "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") +{ + host.advanced = 0; + return CMD_SUCCESS; +} + +DEFUN(terminal_monitor, + terminal_monitor_cmd, + "terminal monitor", + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 1; + return CMD_SUCCESS; +} + +DEFUN(terminal_no_monitor, + terminal_no_monitor_cmd, + "terminal no monitor", + "Set terminal line parameters\n" + NO_STR "Copy debug output to the current terminal line\n") +{ + vty->monitor = 0; + return CMD_SUCCESS; +} + +DEFUN(show_history, + show_history_cmd, + "show history", SHOW_STR "Display the session command history\n") +{ + int index; + + for (index = vty->hindex + 1; index != vty->hindex;) { + if (index == VTY_MAXHIST) { + index = 0; + continue; + } + + if (vty->hist[index] != NULL) + vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE); + + index++; + } + + return CMD_SUCCESS; +} + +/* Display current configuration. */ +static int vty_config_write(struct vty *vty) +{ + vty_out(vty, "line vty%s", VTY_NEWLINE); + + /* login */ + if (no_password_check) + vty_out(vty, " no login%s", VTY_NEWLINE); + + vty_out(vty, "!%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +struct cmd_node vty_node = { + VTY_NODE, + "%s(config-line)# ", + 1, +}; + +/* Reset all VTY status. */ +void vty_reset() +{ + unsigned int i; + struct vty *vty; + struct thread *vty_serv_thread; + + for (i = 0; i < vector_active(vtyvec); i++) + if ((vty = vector_slot(vtyvec, i)) != NULL) { + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + vty_close(vty); + } + + for (i = 0; i < vector_active(Vvty_serv_thread); i++) + if ((vty_serv_thread = + vector_slot(Vvty_serv_thread, i)) != NULL) { + //thread_cancel (vty_serv_thread); + vector_slot(Vvty_serv_thread, i) = NULL; + close(i); + } +} + +static void vty_save_cwd(void) +{ + char cwd[MAXPATHLEN]; + char *c ; + + c = getcwd(cwd, MAXPATHLEN); + + if (!c) { + if (chdir(SYSCONFDIR) != 0) + perror("chdir failed"); + if (getcwd(cwd, MAXPATHLEN) == NULL) + perror("getcwd failed"); + } + + vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd"); + strcpy(vty_cwd, cwd); +} + +char *vty_get_cwd() +{ + return vty_cwd; +} + +int vty_shell_serv(struct vty *vty) +{ + return vty->type == VTY_SHELL_SERV ? 1 : 0; +} + +void vty_init_vtysh() +{ + vtyvec = vector_init(VECTOR_MIN_SIZE); +} + +extern void *tall_bsc_ctx; +/* Install vty's own commands like `who' command. */ +void vty_init() +{ + tall_vty_ctx = talloc_named_const(NULL, 0, "vty"); + tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector"); + tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command"); + + /* For further configuration read, preserve current directory. */ + vty_save_cwd(); + + vtyvec = vector_init(VECTOR_MIN_SIZE); + + /* Install bgp top node. */ + install_node(&vty_node, vty_config_write); + + install_element(VIEW_NODE, &config_who_cmd); + install_element(VIEW_NODE, &show_history_cmd); + install_element(ENABLE_NODE, &config_who_cmd); + install_element(CONFIG_NODE, &line_vty_cmd); + install_element(CONFIG_NODE, &service_advanced_vty_cmd); + install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); + install_element(CONFIG_NODE, &show_history_cmd); + install_element(ENABLE_NODE, &terminal_monitor_cmd); + install_element(ENABLE_NODE, &terminal_no_monitor_cmd); + install_element(ENABLE_NODE, &show_history_cmd); + + install_default(VTY_NODE); + install_element(VTY_NODE, &vty_login_cmd); + install_element(VTY_NODE, &no_vty_login_cmd); +} + +int vty_read_config_file(const char *file_name) +{ + FILE *cfile; + int rc; + + cfile = fopen(file_name, "r"); + if (!cfile) + return -ENOENT; + + rc = vty_read_file(cfile); + fclose(cfile); + + host_config_set(file_name); + + return rc; +} diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c new file mode 100644 index 000000000..e8948697f --- /dev/null +++ b/openbsc/src/vty_interface.c @@ -0,0 +1,2051 @@ +/* OpenBSC interface to quagga VTY */ +/* (C) 2009-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 <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +#include <vty/command.h> +#include <vty/buffer.h> +#include <vty/vty.h> + +#include <arpa/inet.h> + +#include <osmocore/linuxlist.h> +#include <openbsc/gsm_data.h> +#include <openbsc/e1_input.h> +#include <openbsc/abis_nm.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/meas_rep.h> +#include <openbsc/db.h> +#include <osmocore/talloc.h> +#include <openbsc/telnet_interface.h> + +static struct gsm_network *gsmnet; + +struct cmd_node net_node = { + GSMNET_NODE, + "%s(network)#", + 1, +}; + +struct cmd_node bts_node = { + BTS_NODE, + "%s(bts)#", + 1, +}; + +struct cmd_node trx_node = { + TRX_NODE, + "%s(trx)#", + 1, +}; + +struct cmd_node ts_node = { + TS_NODE, + "%s(ts)#", + 1, +}; + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) +{ + vty_out(vty,"Oper '%s', Admin %u, Avail '%s'%s", + nm_opstate_name(nms->operational), nms->administrative, + nm_avail_name(nms->availability), VTY_NEWLINE); +} + +static void dump_pchan_load_vty(struct vty *vty, char *prefix, + const struct pchan_load *pl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { + const struct load_counter *lc = &pl->pchan[i]; + unsigned int percent; + + if (lc->total == 0) + continue; + + percent = (lc->used * 100) / lc->total; + + vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, + gsm_pchan_name(i), percent, lc->used, lc->total, + VTY_NEWLINE); + } +} + +static void net_dump_vty(struct vty *vty, struct gsm_network *net) +{ + struct pchan_load pl; + + vty_out(vty, "BSC is on Country Code %u, Network Code %u " + "and has %u BTS%s", net->country_code, net->network_code, + net->num_bts, VTY_NEWLINE); + vty_out(vty, " Long network name: '%s'%s", + net->name_long, VTY_NEWLINE); + vty_out(vty, " Short network name: '%s'%s", + net->name_short, VTY_NEWLINE); + vty_out(vty, " Authentication policy: %s%s", + gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE); + vty_out(vty, " Location updating reject cause: %u%s", + net->reject_cause, VTY_NEWLINE); + vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, + VTY_NEWLINE); + vty_out(vty, " NECI (TCH/H): %u%s", net->neci, + VTY_NEWLINE); + vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), + VTY_NEWLINE); + vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", + VTY_NEWLINE); + vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", + VTY_NEWLINE); + network_chan_load(&pl, net); + vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); + dump_pchan_load_vty(vty, " ", &pl); +} + +DEFUN(show_net, show_net_cmd, "show network", + SHOW_STR "Display information about a GSM NETWORK\n") +{ + struct gsm_network *net = gsmnet; + net_dump_vty(vty, net); + + return CMD_SUCCESS; +} + +static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) +{ + struct e1inp_line *line; + + if (!e1l) { + vty_out(vty, " None%s", VTY_NEWLINE); + return; + } + + line = e1l->ts->line; + + vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", + line->num, line->driver->name, e1l->ts->num, + e1inp_signtype_name(e1l->type), VTY_NEWLINE); + vty_out(vty, " E1 TEI %u, SAPI %u%s", + e1l->tei, e1l->sapi, VTY_NEWLINE); +} + +static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) +{ + struct pchan_load pl; + + vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " + "BSIC %u, TSC %u and %u TRX%s", + bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), + bts->cell_identity, + bts->location_area_code, bts->bsic, bts->tsc, + bts->num_trx, VTY_NEWLINE); + vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); + vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", + rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), + VTY_NEWLINE); + vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", + bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); + vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, + VTY_NEWLINE); + vty_out(vty, "RACH Max transmissions: %u%s", + rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), + VTY_NEWLINE); + if (bts->si_common.rach_control.cell_bar) + vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); + if (is_ipaccess_bts(bts)) + vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", + bts->ip_access.site_id, bts->ip_access.bts_id, + bts->oml_tei, VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &bts->nm_state); + vty_out(vty, " Site Mgr NM State: "); + net_dump_nmstate(vty, &bts->site_mgr.nm_state); + vty_out(vty, " Paging: FIXME pending requests, %u free slots%s", + bts->paging.available_slots, VTY_NEWLINE); + if (!is_ipaccess_bts(bts)) { + vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); + e1isl_dump_vty(vty, bts->oml_link); + } + /* FIXME: oml_link, chan_desc */ + memset(&pl, 0, sizeof(pl)); + bts_chan_load(&pl, bts); + vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); + dump_pchan_load_vty(vty, " ", &pl); +} + +DEFUN(show_bts, show_bts_cmd, "show bts [number]", + SHOW_STR "Display information about a BTS\n" + "BTS number") +{ + struct gsm_network *net = gsmnet; + int bts_nr; + + if (argc != 0) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr > net->num_bts) { + vty_out(vty, "%% can't find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); + return CMD_SUCCESS; + } + /* print all BTS's */ + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) + bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); + + return CMD_SUCCESS; +} + +/* utility functions */ +static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line, + const char *ts, const char *ss) +{ + e1_link->e1_nr = atoi(line); + e1_link->e1_ts = atoi(ts); + if (!strcmp(ss, "full")) + e1_link->e1_ts_ss = 255; + else + e1_link->e1_ts_ss = atoi(ss); +} + +static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link, + const char *prefix) +{ + if (!e1_link->e1_ts) + return; + + if (e1_link->e1_ts_ss == 255) + vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s", + prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE); + else + vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s", + prefix, e1_link->e1_nr, e1_link->e1_ts, + e1_link->e1_ts_ss, VTY_NEWLINE); +} + + +static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts) +{ + vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE); + if (ts->pchan != GSM_PCHAN_NONE) + vty_out(vty, " phys_chan_config %s%s", + gsm_pchan_name(ts->pchan), VTY_NEWLINE); + config_write_e1_link(vty, &ts->e1_link, " "); +} + +static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) +{ + int i; + + vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); + vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); + vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); + vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); + config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); + vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); + + for (i = 0; i < TRX_NR_TS; i++) + config_write_ts_single(vty, &trx->ts[i]); +} + +static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + int i; + + vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); + vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); + vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); + vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); + vty_out(vty, " location_area_code %u%s", bts->location_area_code, + VTY_NEWLINE); + vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE); + vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); + vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); + vty_out(vty, " cell reselection hysteresis %u%s", + bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); + vty_out(vty, " rxlev access min %u%s", + bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE); + if (bts->si_common.chan_desc.t3212) + vty_out(vty, " periodic location update %u%s", + bts->si_common.chan_desc.t3212 * 10, VTY_NEWLINE); + vty_out(vty, " channel allocator %s%s", + bts->chan_alloc_reverse ? "descending" : "ascending", + VTY_NEWLINE); + vty_out(vty, " rach tx integer %u%s", + bts->si_common.rach_control.tx_integer, VTY_NEWLINE); + vty_out(vty, " rach max transmission %u%s", + rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), + VTY_NEWLINE); + if (bts->si_common.rach_control.cell_bar) + vty_out(vty, " cell barred 1%s", VTY_NEWLINE); + if (is_ipaccess_bts(bts)) { + vty_out(vty, " ip.access unit_id %u %u%s", + bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); + vty_out(vty, " oml ip.access stream_id %u%s", bts->oml_tei, VTY_NEWLINE); + } else { + config_write_e1_link(vty, &bts->oml_e1_link, " oml "); + vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); + } + vty_out(vty, " gprs enabled %u%s", bts->gprs.enabled, VTY_NEWLINE); + if (bts->gprs.enabled) { + vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, + VTY_NEWLINE); + vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, + VTY_NEWLINE); + vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, + VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { + struct gsm_bts_gprs_nsvc *nsvc = + &bts->gprs.nsvc[i]; + struct in_addr ia; + + ia.s_addr = htonl(nsvc->remote_ip); + vty_out(vty, " gprs nsvc %u nsvci %u%s", i, + nsvc->nsvci, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u local udp port %u%s", i, + nsvc->local_port, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, + nsvc->remote_port, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u remote ip %s%s", i, + inet_ntoa(ia), VTY_NEWLINE); + } + } + + llist_for_each_entry(trx, &bts->trx_list, list) + config_write_trx_single(vty, trx); +} + +static int config_write_bts(struct vty *v) +{ + struct gsm_bts *bts; + + llist_for_each_entry(bts, &gsmnet->bts_list, list) + config_write_bts_single(v, bts); + + return CMD_SUCCESS; +} + +static int config_write_net(struct vty *vty) +{ + vty_out(vty, "network%s", VTY_NEWLINE); + vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); + vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); + vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); + vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); + vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); + vty_out(vty, " location updating reject cause %u%s", + gsmnet->reject_cause, VTY_NEWLINE); + vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); + vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); + vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), + VTY_NEWLINE); + vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); + vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); + vty_out(vty, " handover window rxlev averaging %u%s", + gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); + vty_out(vty, " handover window rxqual averaging %u%s", + gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); + vty_out(vty, " handover window rxlev neighbor averaging %u%s", + gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); + vty_out(vty, " handover power budget interval %u%s", + gsmnet->handover.pwr_interval, VTY_NEWLINE); + vty_out(vty, " handover power budget hysteresis %u%s", + gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); + vty_out(vty, " handover maximum distance %u%s", + gsmnet->handover.max_distance, VTY_NEWLINE); + vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); + vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); + vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); + vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); + vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); + vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); + vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); + vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); + vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); + vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); + vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) +{ + vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", + trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); + vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " + "resulting BS power: %d dBm%s", + trx->nominal_power, trx->max_power_red, + trx->nominal_power - trx->max_power_red, VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &trx->nm_state); + vty_out(vty, " Baseband Transceiver NM State: "); + net_dump_nmstate(vty, &trx->bb_transc.nm_state); + if (is_ipaccess_bts(trx->bts)) { + vty_out(vty, " ip.access stream ID: 0x%02x%s", + trx->rsl_tei, VTY_NEWLINE); + } else { + vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); + e1isl_dump_vty(vty, trx->rsl_link); + } +} + +DEFUN(show_trx, + show_trx_cmd, + "show trx [bts_nr] [trx_nr]", + SHOW_STR "Display information about a TRX\n") +{ + struct gsm_network *net = gsmnet; + struct gsm_bts *bts = NULL; + struct gsm_bts_trx *trx; + int bts_nr, trx_nr; + + if (argc >= 1) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts = gsm_bts_num(net, bts_nr); + } + if (argc >= 2) { + trx_nr = atoi(argv[1]); + if (trx_nr >= bts->num_trx) { + vty_out(vty, "%% can't find TRX '%s'%s", argv[1], + VTY_NEWLINE); + return CMD_WARNING; + } + trx = gsm_bts_trx_num(bts, trx_nr); + trx_dump_vty(vty, trx); + return CMD_SUCCESS; + } + if (bts) { + /* print all TRX in this BTS */ + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + trx = gsm_bts_trx_num(bts, trx_nr); + trx_dump_vty(vty, trx); + } + return CMD_SUCCESS; + } + + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { + bts = gsm_bts_num(net, bts_nr); + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + trx = gsm_bts_trx_num(bts, trx_nr); + trx_dump_vty(vty, trx); + } + } + + return CMD_SUCCESS; +} + + +static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts) +{ + vty_out(vty, "Timeslot %u of TRX %u in BTS %u, phys cfg %s%s", + ts->nr, ts->trx->nr, ts->trx->bts->nr, + gsm_pchan_name(ts->pchan), VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &ts->nm_state); + if (!is_ipaccess_bts(ts->trx->bts)) + vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", + ts->e1_link.e1_nr, ts->e1_link.e1_ts, + ts->e1_link.e1_ts_ss, VTY_NEWLINE); +} + +DEFUN(show_ts, + show_ts_cmd, + "show timeslot [bts_nr] [trx_nr] [ts_nr]", + SHOW_STR "Display information about a TS\n") +{ + struct gsm_network *net = gsmnet; + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + int bts_nr, trx_nr, ts_nr; + + if (argc >= 1) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts = gsm_bts_num(net, bts_nr); + } + if (argc >= 2) { + trx_nr = atoi(argv[1]); + if (trx_nr >= bts->num_trx) { + vty_out(vty, "%% can't find TRX '%s'%s", argv[1], + VTY_NEWLINE); + return CMD_WARNING; + } + trx = gsm_bts_trx_num(bts, trx_nr); + } + if (argc >= 3) { + ts_nr = atoi(argv[2]); + if (ts_nr >= TRX_NR_TS) { + vty_out(vty, "%% can't find TS '%s'%s", argv[2], + VTY_NEWLINE); + return CMD_WARNING; + } + ts = &trx->ts[ts_nr]; + ts_dump_vty(vty, ts); + return CMD_SUCCESS; + } + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { + bts = gsm_bts_num(net, bts_nr); + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + trx = gsm_bts_trx_num(bts, trx_nr); + for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { + ts = &trx->ts[ts_nr]; + ts_dump_vty(vty, ts); + } + } + } + + return CMD_SUCCESS; +} + +static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) +{ + int rc; + struct gsm_auth_info ainfo; + struct gsm_auth_tuple atuple; + + vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, + subscr->authorized, VTY_NEWLINE); + if (subscr->name) + vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); + if (subscr->extension) + vty_out(vty, " Extension: %s%s", subscr->extension, + VTY_NEWLINE); + if (subscr->imsi) + vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); + if (subscr->tmsi != GSM_RESERVED_TMSI) + vty_out(vty, " TMSI: %08X%s", subscr->tmsi, + VTY_NEWLINE); + + vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); +} + +static void meas_rep_dump_uni_vty(struct vty *vty, + struct gsm_meas_rep_unidir *mru, + const char *prefix, + const char *dir) +{ + vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", + prefix, dir, rxlev2dbm(mru->full.rx_lev), + dir, rxlev2dbm(mru->sub.rx_lev)); + vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", + dir, mru->full.rx_qual, dir, mru->sub.rx_qual, + VTY_NEWLINE); +} + +static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, + const char *prefix) +{ + vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); + vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, + mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", + mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", + mr->flags & MEAS_REP_F_FPC ? "FPC " : "", + mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", + VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_TO) + vty_out(vty, "%s MS Timing Offset: %u%s", prefix, + mr->ms_timing_offset, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_L1) + vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", + prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_DL_VALID) + meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); + meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); +} + +static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan) +{ + int idx; + + vty_out(vty, "Lchan %u in Timeslot %u of TRX %u in BTS %u, Type %s%s", + lchan->nr, lchan->ts->nr, lchan->ts->trx->nr, + lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type), + VTY_NEWLINE); + vty_out(vty, " Use Count: %u, State: %s%s", lchan->use_count, + gsm_lchans_name(lchan->state), VTY_NEWLINE); + vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", + lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red + - lchan->bs_power*2, + ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), + VTY_NEWLINE); + if (lchan->subscr) { + vty_out(vty, " Subscriber:%s", VTY_NEWLINE); + subscr_dump_vty(vty, lchan->subscr); + } else + vty_out(vty, " No Subscriber%s", VTY_NEWLINE); + if (is_ipaccess_bts(lchan->ts->trx->bts)) { + struct in_addr ia; + ia.s_addr = lchan->abis_ip.bound_ip; + vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", + inet_ntoa(ia), lchan->abis_ip.bound_port, + lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, + VTY_NEWLINE); + } + + /* we want to report the last measurement report */ + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); +} + +#if 0 +TODO: callref and remote callref of call must be resolved to get gsm_trans object +static void call_dump_vty(struct vty *vty, struct gsm_call *call) +{ + vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s", + call->type, call->state, call->transaction_id, VTY_NEWLINE); + + if (call->local_lchan) { + vty_out(vty, "Call Local Channel:%s", VTY_NEWLINE); + lchan_dump_vty(vty, call->local_lchan); + } else + vty_out(vty, "Call has no Local Channel%s", VTY_NEWLINE); + + if (call->remote_lchan) { + vty_out(vty, "Call Remote Channel:%s", VTY_NEWLINE); + lchan_dump_vty(vty, call->remote_lchan); + } else + vty_out(vty, "Call has no Remote Channel%s", VTY_NEWLINE); + + if (call->called_subscr) { + vty_out(vty, "Called Subscriber:%s", VTY_NEWLINE); + subscr_dump_vty(vty, call->called_subscr); + } else + vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE); +} +#endif + +DEFUN(show_lchan, + show_lchan_cmd, + "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", + SHOW_STR "Display information about a logical channel\n") +{ + struct gsm_network *net = gsmnet; + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_lchan *lchan; + int bts_nr, trx_nr, ts_nr, lchan_nr; + + if (argc >= 1) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts = gsm_bts_num(net, bts_nr); + } + if (argc >= 2) { + trx_nr = atoi(argv[1]); + if (trx_nr >= bts->num_trx) { + vty_out(vty, "%% can't find TRX %s%s", argv[1], + VTY_NEWLINE); + return CMD_WARNING; + } + trx = gsm_bts_trx_num(bts, trx_nr); + } + if (argc >= 3) { + ts_nr = atoi(argv[2]); + if (ts_nr >= TRX_NR_TS) { + vty_out(vty, "%% can't find TS %s%s", argv[2], + VTY_NEWLINE); + return CMD_WARNING; + } + ts = &trx->ts[ts_nr]; + } + if (argc >= 4) { + lchan_nr = atoi(argv[3]); + if (lchan_nr >= TS_MAX_LCHAN) { + vty_out(vty, "%% can't find LCHAN %s%s", argv[3], + VTY_NEWLINE); + return CMD_WARNING; + } + lchan = &ts->lchan[lchan_nr]; + lchan_dump_vty(vty, lchan); + return CMD_SUCCESS; + } + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { + bts = gsm_bts_num(net, bts_nr); + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + trx = gsm_bts_trx_num(bts, trx_nr); + for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { + ts = &trx->ts[ts_nr]; + for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; + lchan_nr++) { + lchan = &ts->lchan[lchan_nr]; + if (lchan->type == GSM_LCHAN_NONE) + continue; + lchan_dump_vty(vty, lchan); + } + } + } + } + + return CMD_SUCCESS; +} + +static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv) +{ + vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE); +} + +DEFUN(show_e1drv, + show_e1drv_cmd, + "show e1_driver", + SHOW_STR "Display information about available E1 drivers\n") +{ + struct e1inp_driver *drv; + + llist_for_each_entry(drv, &e1inp_driver_list, list) + e1drv_dump_vty(vty, drv); + + return CMD_SUCCESS; +} + +static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line) +{ + vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s", + line->num, line->name ? line->name : "", + line->driver->name, VTY_NEWLINE); +} + +DEFUN(show_e1line, + show_e1line_cmd, + "show e1_line [line_nr]", + SHOW_STR "Display information about a E1 line\n") +{ + struct e1inp_line *line; + + if (argc >= 1) { + int num = atoi(argv[0]); + llist_for_each_entry(line, &e1inp_line_list, list) { + if (line->num == num) { + e1line_dump_vty(vty, line); + return CMD_SUCCESS; + } + } + return CMD_WARNING; + } + + llist_for_each_entry(line, &e1inp_line_list, list) + e1line_dump_vty(vty, line); + + return CMD_SUCCESS; +} + +static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts) +{ + if (ts->type == E1INP_TS_TYPE_NONE) + return; + vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s", + ts->num, ts->line->num, e1inp_tstype_name(ts->type), + VTY_NEWLINE); +} + +DEFUN(show_e1ts, + show_e1ts_cmd, + "show e1_timeslot [line_nr] [ts_nr]", + SHOW_STR "Display information about a E1 timeslot\n") +{ + struct e1inp_line *line = NULL; + struct e1inp_ts *ts; + int ts_nr; + + if (argc == 0) { + llist_for_each_entry(line, &e1inp_line_list, list) { + for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { + ts = &line->ts[ts_nr]; + e1ts_dump_vty(vty, ts); + } + } + return CMD_SUCCESS; + } + if (argc >= 1) { + int num = atoi(argv[0]); + llist_for_each_entry(line, &e1inp_line_list, list) { + if (line->num == num) + break; + } + if (!line || line->num != num) { + vty_out(vty, "E1 line %s is invalid%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + if (argc >= 2) { + ts_nr = atoi(argv[1]); + if (ts_nr > NUM_E1_TS) { + vty_out(vty, "E1 timeslot %s is invalid%s", + argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + ts = &line->ts[ts_nr]; + e1ts_dump_vty(vty, ts); + return CMD_SUCCESS; + } else { + for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { + ts = &line->ts[ts_nr]; + e1ts_dump_vty(vty, ts); + } + return CMD_SUCCESS; + } + return CMD_SUCCESS; +} + +static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) +{ + vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); + subscr_dump_vty(vty, pag->subscr); +} + +static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) +{ + struct gsm_paging_request *pag; + + llist_for_each_entry(pag, &bts->paging.pending_requests, entry) + paging_dump_vty(vty, pag); +} + +DEFUN(show_paging, + show_paging_cmd, + "show paging [bts_nr]", + SHOW_STR "Display information about paging reuqests of a BTS\n") +{ + struct gsm_network *net = gsmnet; + struct gsm_bts *bts; + int bts_nr; + + if (argc >= 1) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts = gsm_bts_num(net, bts_nr); + bts_paging_dump_vty(vty, bts); + + return CMD_SUCCESS; + } + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { + bts = gsm_bts_num(net, bts_nr); + bts_paging_dump_vty(vty, bts); + } + + return CMD_SUCCESS; +} + +static void _vty_output(struct debug_target *tgt, const char *line) +{ + struct vty *vty = tgt->tgt_vty.vty; + vty_out(vty, "%s", line); + /* This is an ugly hack, but there is no easy way... */ + if (strchr(line, '\n')) + vty_out(vty, "\r"); +} + +struct debug_target *debug_target_create_vty(struct vty *vty) +{ + struct debug_target *target; + + target = debug_target_create(); + if (!target) + return NULL; + + target->tgt_vty.vty = vty; + target->output = _vty_output; + return target; +} + +DEFUN(enable_logging, + enable_logging_cmd, + "logging enable", + "Enables logging to this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (conn->dbg) { + vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + conn->dbg = debug_target_create_vty(vty); + if (!conn->dbg) + return CMD_WARNING; + + debug_add_target(conn->dbg); + return CMD_SUCCESS; +} + +DEFUN(logging_fltr_imsi, + logging_fltr_imsi_cmd, + "logging filter imsi IMSI", + "Print all messages related to a IMSI\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_imsi_filter(conn->dbg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(logging_fltr_all, + logging_fltr_all_cmd, + "logging filter all <0-1>", + "Print all messages to the console\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_all_filter(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_use_clr, + logging_use_clr_cmd, + "logging color <0-1>", + "Use color for printing messages\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_use_color(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_prnt_timestamp, + logging_prnt_timestamp_cmd, + "logging timestamp <0-1>", + "Print the timestamp of each message\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_print_timestamp(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +/* FIXME: those have to be kept in sync with the log levels and categories */ +#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)" +#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)" +DEFUN(logging_level, + logging_level_cmd, + "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, + "Set the log level for a specified category\n") +{ + struct telnet_connection *conn; + int category = debug_parse_category(argv[0]); + int level = debug_parse_level(argv[1]); + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (category < 0) { + vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (level < 0) { + vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + conn->dbg->categories[category].enabled = 1; + conn->dbg->categories[category].loglevel = level; + + return CMD_SUCCESS; +} + +DEFUN(logging_set_category_mask, + logging_set_category_mask_cmd, + "logging set debug mask MASK", + "Decide which categories to output.\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_parse_category_mask(conn->dbg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(logging_set_log_level, + logging_set_log_level_cmd, + "logging set log level <0-8>", + "Set the global log level. The value 0 implies no filtering.\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_log_level(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(diable_logging, + disable_logging_cmd, + "logging disable", + "Disables logging to this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_del_target(conn->dbg); + talloc_free(conn->dbg); + conn->dbg = NULL; + return CMD_SUCCESS; +} + +DEFUN(show_stats, + show_stats_cmd, + "show statistics", + SHOW_STR "Display network statistics\n") +{ + struct gsm_network *net = gsmnet; + + vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", + counter_get(net->stats.chreq.total), + counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); + vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", + counter_get(net->stats.loc_upd_type.attach), + counter_get(net->stats.loc_upd_type.normal), + counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); + vty_out(vty, "IMSI Detach Indications : %lu%s", + counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); + vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", + counter_get(net->stats.loc_upd_resp.accept), + counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); + vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", + counter_get(net->stats.paging.attempted), + counter_get(net->stats.paging.completed), + counter_get(net->stats.paging.expired), VTY_NEWLINE); + vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " + "%lu completed, %lu failed%s", + counter_get(net->stats.handover.attempted), + counter_get(net->stats.handover.no_channel), + counter_get(net->stats.handover.timeout), + counter_get(net->stats.handover.completed), + counter_get(net->stats.handover.failed), VTY_NEWLINE); + vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", + counter_get(net->stats.sms.submitted), + counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); + vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", + counter_get(net->stats.sms.delivered), + counter_get(net->stats.sms.rp_err_mem), + counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(cfg_net, + cfg_net_cmd, + "network", + "Configure the GSM network") +{ + vty->index = gsmnet; + vty->node = GSMNET_NODE; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_net_ncc, + cfg_net_ncc_cmd, + "network country code <1-999>", + "Set the GSM network country code") +{ + gsmnet->country_code = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_mnc, + cfg_net_mnc_cmd, + "mobile network code <1-999>", + "Set the GSM mobile network code") +{ + gsmnet->network_code = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_name_short, + cfg_net_name_short_cmd, + "short name NAME", + "Set the short GSM network name") +{ + if (gsmnet->name_short) + talloc_free(gsmnet->name_short); + + gsmnet->name_short = talloc_strdup(gsmnet, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_name_long, + cfg_net_name_long_cmd, + "long name NAME", + "Set the long GSM network name") +{ + if (gsmnet->name_long) + talloc_free(gsmnet->name_long); + + gsmnet->name_long = talloc_strdup(gsmnet, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_auth_policy, + cfg_net_auth_policy_cmd, + "auth policy (closed|accept-all|token)", + "Set the GSM network authentication policy\n") +{ + enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); + + gsmnet->auth_policy = policy; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_reject_cause, + cfg_net_reject_cause_cmd, + "location updating reject cause <2-111>", + "Set the reject cause of location updating reject\n") +{ + gsmnet->reject_cause = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_encryption, + cfg_net_encryption_cmd, + "encryption a5 (0|1|2)", + "Enable or disable encryption (A5) for this network\n") +{ + gsmnet->a5_encryption= atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_neci, + cfg_net_neci_cmd, + "neci (0|1)", + "Set if NECI of cell selection is to be set") +{ + gsmnet->neci = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, + "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", + "Set the Radio Resource Location Protocol Mode") +{ + gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, + "mm info (0|1)", + "Whether to send MM INFO after LOC UPD ACCEPT") +{ + gsmnet->send_mm_info = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_handover, cfg_net_handover_cmd, + "handover (0|1)", + "Whether or not to use in-call handover") +{ + int enable = atoi(argv[0]); + + if (enable && ipacc_rtp_direct) { + vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " + "is enabled by using the -P command line option%s", + VTY_NEWLINE); + return CMD_WARNING; + } + gsmnet->handover.active = enable; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, + "handover window rxlev averaging <1-10>", + "How many RxLev measurements are used for averaging") +{ + gsmnet->handover.win_rxlev_avg = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, + "handover window rxqual averaging <1-10>", + "How many RxQual measurements are used for averaging") +{ + gsmnet->handover.win_rxqual_avg = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, + "handover window rxlev neighbor averaging <1-10>", + "How many RxQual measurements are used for averaging") +{ + gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, + "handover power budget interval <1-99>", + "How often to check if we have a better cell (SACCH frames)") +{ + gsmnet->handover.pwr_interval = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, + "handover power budget hysteresis <0-999>", + "How many dB does a neighbor to be stronger to become a HO candidate") +{ + gsmnet->handover.pwr_hysteresis = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, + "handover maximum distance <0-9999>", + "How big is the maximum timing advance before HO is forced") +{ + gsmnet->handover.max_distance = atoi(argv[0]); + return CMD_SUCCESS; +} + +#define DECLARE_TIMER(number, doc) \ + DEFUN(cfg_net_T##number, \ + cfg_net_T##number##_cmd, \ + "timer t" #number " <0-65535>", \ + doc) \ +{ \ + int value = atoi(argv[0]); \ + \ + if (value < 0 || value > 65535) { \ + vty_out(vty, "Timer value %s out of range.%s", \ + argv[0], VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ + \ + gsmnet->T##number = value; \ + return CMD_SUCCESS; \ +} + +DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.") +DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.") +DECLARE_TIMER(3105, "Currently not used.") +DECLARE_TIMER(3107, "Currently not used.") +DECLARE_TIMER(3109, "Currently not used.") +DECLARE_TIMER(3111, "Currently not used.") +DECLARE_TIMER(3113, "Set the time to try paging a subscriber.") +DECLARE_TIMER(3115, "Currently not used.") +DECLARE_TIMER(3117, "Currently not used.") +DECLARE_TIMER(3119, "Currently not used.") +DECLARE_TIMER(3141, "Currently not used.") + + +/* per-BTS configuration */ +DEFUN(cfg_bts, + cfg_bts_cmd, + "bts BTS_NR", + "Select a BTS to configure\n") +{ + int bts_nr = atoi(argv[0]); + struct gsm_bts *bts; + + if (bts_nr > gsmnet->num_bts) { + vty_out(vty, "%% The next unused BTS number is %u%s", + gsmnet->num_bts, VTY_NEWLINE); + return CMD_WARNING; + } else if (bts_nr == gsmnet->num_bts) { + /* allocate a new one */ + bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_UNKNOWN, + HARDCODED_TSC, HARDCODED_BSIC); + } else + bts = gsm_bts_num(gsmnet, bts_nr); + + if (!bts) { + vty_out(vty, "%% Unable to allocate BTS %u%s", + gsmnet->num_bts, VTY_NEWLINE); + return CMD_WARNING; + } + + vty->index = bts; + vty->node = BTS_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_type, + cfg_bts_type_cmd, + "type TYPE", + "Set the BTS type\n") +{ + struct gsm_bts *bts = vty->index; + int rc; + + rc = gsm_set_bts_type(bts, parse_btstype(argv[0])); + if (rc < 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_band, + cfg_bts_band_cmd, + "band BAND", + "Set the frequency band of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int band = gsm_band_parse(argv[0]); + + if (band < 0) { + vty_out(vty, "%% BAND %d is not a valid GSM band%s", + band, VTY_NEWLINE); + return CMD_WARNING; + } + + bts->band = band; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ci, + cfg_bts_ci_cmd, + "cell_identity <0-65535>", + "Set the Cell identity of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int ci = atoi(argv[0]); + + if (ci < 0 || ci > 0xffff) { + vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s", + ci, VTY_NEWLINE); + return CMD_WARNING; + } + bts->cell_identity = ci; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_lac, + cfg_bts_lac_cmd, + "location_area_code <0-65535>", + "Set the Location Area Code (LAC) of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int lac = atoi(argv[0]); + + if (lac < 0 || lac > 0xffff) { + vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { + vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + bts->location_area_code = lac; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_tsc, + cfg_bts_tsc_cmd, + "training_sequence_code <0-255>", + "Set the Training Sequence Code (TSC) of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int tsc = atoi(argv[0]); + + if (tsc < 0 || tsc > 0xff) { + vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s", + tsc, VTY_NEWLINE); + return CMD_WARNING; + } + bts->tsc = tsc; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_bsic, + cfg_bts_bsic_cmd, + "base_station_id_code <0-63>", + "Set the Base Station Identity Code (BSIC) of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int bsic = atoi(argv[0]); + + if (bsic < 0 || bsic > 0x3f) { + vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s", + bsic, VTY_NEWLINE); + return CMD_WARNING; + } + bts->bsic = bsic; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_unit_id, + cfg_bts_unit_id_cmd, + "ip.access unit_id <0-65534> <0-255>", + "Set the ip.access BTS Unit ID of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int site_id = atoi(argv[0]); + int bts_id = atoi(argv[1]); + + if (!is_ipaccess_bts(bts)) { + vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->ip_access.site_id = site_id; + bts->ip_access.bts_id = bts_id; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_stream_id, + cfg_bts_stream_id_cmd, + "oml ip.access stream_id <0-255>", + "Set the ip.access Stream ID of the OML link of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int stream_id = atoi(argv[0]); + + if (!is_ipaccess_bts(bts)) { + vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->oml_tei = stream_id; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_oml_e1, + cfg_bts_oml_e1_cmd, + "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", + "E1 interface to be used for OML\n") +{ + struct gsm_bts *bts = vty->index; + + parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]); + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_oml_e1_tei, + cfg_bts_oml_e1_tei_cmd, + "oml e1 tei <0-63>", + "Set the TEI to be used for OML") +{ + struct gsm_bts *bts = vty->index; + + bts->oml_tei = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, + "channel allocator (ascending|descending)", + "Should the channel allocator allocate in reverse TRX order?") +{ + struct gsm_bts *bts = vty->index; + + if (!strcmp(argv[0], "ascending")) + bts->chan_alloc_reverse = 0; + else + bts->chan_alloc_reverse = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_rach_tx_integer, + cfg_bts_rach_tx_integer_cmd, + "rach tx integer <0-15>", + "Set the raw tx integer value in RACH Control parameters IE") +{ + struct gsm_bts *bts = vty->index; + bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf; + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_rach_max_trans, + cfg_bts_rach_max_trans_cmd, + "rach max transmission (1|2|4|7)", + "Set the maximum number of RACH burst transmissions") +{ + struct gsm_bts *bts = vty->index; + bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd, + "cell barred (0|1)", + "Should this cell be barred from access?") +{ + struct gsm_bts *bts = vty->index; + + bts->si_common.rach_control.cell_bar = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd, + "ms max power <0-40>", + "Maximum transmit power of the MS") +{ + struct gsm_bts *bts = vty->index; + + bts->ms_max_power = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd, + "cell reselection hysteresis <0-14>", + "Cell Re-Selection Hysteresis in dB") +{ + struct gsm_bts *bts = vty->index; + + bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd, + "rxlev access min <0-63>", + "Minimum RxLev needed for cell access (better than -110dBm)") +{ + struct gsm_bts *bts = vty->index; + + bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd, + "periodic location update <0-1530>", + "Periodic Location Updating Interval in Minutes") +{ + struct gsm_bts *bts = vty->index; + + bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 10; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, + "gprs cell bvci <0-65535>", + "GPRS BSSGP VC Identifier") +{ + struct gsm_bts *bts = vty->index; + + if (!bts->gprs.enabled) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.cell.bvci = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, + "gprs nsei <0-65535>", + "GPRS NS Entity Identifier") +{ + struct gsm_bts *bts = vty->index; + + if (!bts->gprs.enabled) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nse.nsei = atoi(argv[0]); + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, + "gprs nsvc <0-1> nsvci <0-65535>", + "GPRS NS VC Identifier") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + + if (!bts->gprs.enabled) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nsvc[idx].nsvci = atoi(argv[1]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, + "gprs nsvc <0-1> local udp port <0-65535>", + "GPRS NS Local UDP Port") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + + if (!bts->gprs.enabled) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nsvc[idx].local_port = atoi(argv[1]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, + "gprs nsvc <0-1> remote udp port <0-65535>", + "GPRS NS Remote UDP Port") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + + if (!bts->gprs.enabled) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nsvc[idx].remote_port = atoi(argv[1]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, + "gprs nsvc <0-1> remote ip A.B.C.D", + "GPRS NS Remote IP Address") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + struct in_addr ia; + + if (!bts->gprs.enabled) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + inet_aton(argv[1], &ia); + bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, + "gprs routing area <0-255>", + "GPRS Routing Area Code") +{ + struct gsm_bts *bts = vty->index; + + if (!bts->gprs.enabled) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.rac = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_enabled, cfg_bts_gprs_enabled_cmd, + "gprs enabled <0-1>", + "GPRS Enabled on this BTS") +{ + struct gsm_bts *bts = vty->index; + + bts->gprs.enabled = atoi(argv[0]); + + return CMD_SUCCESS; +} + + +/* per TRX configuration */ +DEFUN(cfg_trx, + cfg_trx_cmd, + "trx TRX_NR", + "Select a TRX to configure") +{ + int trx_nr = atoi(argv[0]); + struct gsm_bts *bts = vty->index; + struct gsm_bts_trx *trx; + + if (trx_nr > bts->num_trx) { + vty_out(vty, "%% The next unused TRX number in this BTS is %u%s", + bts->num_trx, VTY_NEWLINE); + return CMD_WARNING; + } else if (trx_nr == bts->num_trx) { + /* we need to allocate a new one */ + trx = gsm_bts_trx_alloc(bts); + } else + trx = gsm_bts_trx_num(bts, trx_nr); + + if (!trx) + return CMD_WARNING; + + vty->index = trx; + vty->node = TRX_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_arfcn, + cfg_trx_arfcn_cmd, + "arfcn <1-1024>", + "Set the ARFCN for this TRX\n") +{ + int arfcn = atoi(argv[0]); + struct gsm_bts_trx *trx = vty->index; + + /* FIXME: check if this ARFCN is supported by this TRX */ + + trx->arfcn = arfcn; + + /* FIXME: patch ARFCN into SYSTEM INFORMATION */ + /* FIXME: use OML layer to update the ARFCN */ + /* FIXME: use RSL layer to update SYSTEM INFORMATION */ + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_nominal_power, + cfg_trx_nominal_power_cmd, + "nominal power <0-100>", + "Nominal TRX RF Power in dB\n") +{ + struct gsm_bts_trx *trx = vty->index; + + trx->nominal_power = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_max_power_red, + cfg_trx_max_power_red_cmd, + "max_power_red <0-100>", + "Reduction of maximum BS RF Power in dB\n") +{ + int maxpwr_r = atoi(argv[0]); + struct gsm_bts_trx *trx = vty->index; + int upper_limit = 24; /* default 12.21 max power red. */ + + /* FIXME: check if our BTS type supports more than 12 */ + if (maxpwr_r < 0 || maxpwr_r > upper_limit) { + vty_out(vty, "%% Power %d dB is not in the valid range%s", + maxpwr_r, VTY_NEWLINE); + return CMD_WARNING; + } + if (maxpwr_r & 1) { + vty_out(vty, "%% Power %d dB is not an even value%s", + maxpwr_r, VTY_NEWLINE); + return CMD_WARNING; + } + + trx->max_power_red = maxpwr_r; + + /* FIXME: make sure we update this using OML */ + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_rsl_e1, + cfg_trx_rsl_e1_cmd, + "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", + "E1 interface to be used for RSL\n") +{ + struct gsm_bts_trx *trx = vty->index; + + parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_rsl_e1_tei, + cfg_trx_rsl_e1_tei_cmd, + "rsl e1 tei <0-63>", + "Set the TEI to be used for RSL") +{ + struct gsm_bts_trx *trx = vty->index; + + trx->rsl_tei = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_rf_locked, + cfg_trx_rf_locked_cmd, + "rf_locked (0|1)", + "Turn off RF of the TRX.\n") +{ + int locked = atoi(argv[0]); + struct gsm_bts_trx *trx = vty->index; + + gsm_trx_lock_rf(trx, locked); + return CMD_SUCCESS; +} + +/* per TS configuration */ +DEFUN(cfg_ts, + cfg_ts_cmd, + "timeslot <0-7>", + "Select a Timeslot to configure") +{ + int ts_nr = atoi(argv[0]); + struct gsm_bts_trx *trx = vty->index; + struct gsm_bts_trx_ts *ts; + + if (ts_nr >= TRX_NR_TS) { + vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s", + TRX_NR_TS, VTY_NEWLINE); + return CMD_WARNING; + } + + ts = &trx->ts[ts_nr]; + + vty->index = ts; + vty->node = TS_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_pchan, + cfg_ts_pchan_cmd, + "phys_chan_config PCHAN", + "Physical Channel configuration (TCH/SDCCH/...)") +{ + struct gsm_bts_trx_ts *ts = vty->index; + int pchanc; + + pchanc = gsm_pchan_parse(argv[0]); + if (pchanc < 0) + return CMD_WARNING; + + ts->pchan = pchanc; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_e1_subslot, + cfg_ts_e1_subslot_cmd, + "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", + "E1 sub-slot connected to this on-air timeslot") +{ + struct gsm_bts_trx_ts *ts = vty->index; + + parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); + + return CMD_SUCCESS; +} + +int bsc_vty_init(struct gsm_network *net) +{ + gsmnet = net; + + cmd_init(1); + vty_init(); + + install_element(VIEW_NODE, &show_net_cmd); + install_element(VIEW_NODE, &show_bts_cmd); + install_element(VIEW_NODE, &show_trx_cmd); + install_element(VIEW_NODE, &show_ts_cmd); + install_element(VIEW_NODE, &show_lchan_cmd); + + install_element(VIEW_NODE, &show_e1drv_cmd); + install_element(VIEW_NODE, &show_e1line_cmd); + install_element(VIEW_NODE, &show_e1ts_cmd); + + install_element(VIEW_NODE, &show_paging_cmd); + install_element(VIEW_NODE, &show_stats_cmd); + + install_element(VIEW_NODE, &enable_logging_cmd); + install_element(VIEW_NODE, &disable_logging_cmd); + install_element(VIEW_NODE, &logging_fltr_imsi_cmd); + install_element(VIEW_NODE, &logging_fltr_all_cmd); + install_element(VIEW_NODE, &logging_use_clr_cmd); + install_element(VIEW_NODE, &logging_prnt_timestamp_cmd); + install_element(VIEW_NODE, &logging_set_category_mask_cmd); + install_element(VIEW_NODE, &logging_level_cmd); + install_element(VIEW_NODE, &logging_set_log_level_cmd); + + install_element(CONFIG_NODE, &cfg_net_cmd); + install_node(&net_node, config_write_net); + install_default(GSMNET_NODE); + install_element(GSMNET_NODE, &cfg_net_ncc_cmd); + install_element(GSMNET_NODE, &cfg_net_mnc_cmd); + install_element(GSMNET_NODE, &cfg_net_name_short_cmd); + install_element(GSMNET_NODE, &cfg_net_name_long_cmd); + install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd); + install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd); + install_element(GSMNET_NODE, &cfg_net_encryption_cmd); + install_element(GSMNET_NODE, &cfg_net_neci_cmd); + install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd); + install_element(GSMNET_NODE, &cfg_net_mm_info_cmd); + install_element(GSMNET_NODE, &cfg_net_handover_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); + install_element(GSMNET_NODE, &cfg_net_T3101_cmd); + install_element(GSMNET_NODE, &cfg_net_T3103_cmd); + install_element(GSMNET_NODE, &cfg_net_T3105_cmd); + install_element(GSMNET_NODE, &cfg_net_T3107_cmd); + install_element(GSMNET_NODE, &cfg_net_T3109_cmd); + install_element(GSMNET_NODE, &cfg_net_T3111_cmd); + install_element(GSMNET_NODE, &cfg_net_T3113_cmd); + install_element(GSMNET_NODE, &cfg_net_T3115_cmd); + install_element(GSMNET_NODE, &cfg_net_T3117_cmd); + install_element(GSMNET_NODE, &cfg_net_T3119_cmd); + install_element(GSMNET_NODE, &cfg_net_T3141_cmd); + + install_element(GSMNET_NODE, &cfg_bts_cmd); + install_node(&bts_node, config_write_bts); + install_default(BTS_NODE); + install_element(BTS_NODE, &cfg_bts_type_cmd); + install_element(BTS_NODE, &cfg_bts_band_cmd); + install_element(BTS_NODE, &cfg_bts_ci_cmd); + install_element(BTS_NODE, &cfg_bts_lac_cmd); + install_element(BTS_NODE, &cfg_bts_tsc_cmd); + install_element(BTS_NODE, &cfg_bts_bsic_cmd); + install_element(BTS_NODE, &cfg_bts_unit_id_cmd); + install_element(BTS_NODE, &cfg_bts_stream_id_cmd); + install_element(BTS_NODE, &cfg_bts_oml_e1_cmd); + install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); + install_element(BTS_NODE, &cfg_bts_challoc_cmd); + install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd); + install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd); + install_element(BTS_NODE, &cfg_bts_cell_barred_cmd); + install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); + install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd); + install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd); + install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_enabled_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); + + install_element(BTS_NODE, &cfg_trx_cmd); + install_node(&trx_node, dummy_config_write); + install_default(TRX_NODE); + install_element(TRX_NODE, &cfg_trx_arfcn_cmd); + install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); + install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); + install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); + install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); + install_element(TRX_NODE, &cfg_trx_rf_locked_cmd); + + install_element(TRX_NODE, &cfg_ts_cmd); + install_node(&ts_node, dummy_config_write); + install_default(TS_NODE); + install_element(TS_NODE, &cfg_ts_pchan_cmd); + install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); + + bsc_vty_init_extra(net); + + return 0; +} diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c new file mode 100644 index 000000000..b824c3db6 --- /dev/null +++ b/openbsc/src/vty_interface_layer3.c @@ -0,0 +1,531 @@ +/* OpenBSC interface to quagga VTY */ +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 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 <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +#include <vty/command.h> +#include <vty/buffer.h> +#include <vty/vty.h> + +#include <arpa/inet.h> + +#include <osmocore/linuxlist.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/silent_call.h> +#include <openbsc/gsm_04_11.h> +#include <openbsc/e1_input.h> +#include <openbsc/abis_nm.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/db.h> +#include <osmocore/talloc.h> +#include <openbsc/signal.h> +#include <openbsc/debug.h> + +static struct gsm_network *gsmnet; + +struct cmd_node subscr_node = { + SUBSCR_NODE, + "%s(subscriber)#", + 1, +}; + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +static struct buffer *argv_to_buffer(int argc, const char *argv[], int base) +{ + struct buffer *b = buffer_new(1024); + int i; + + if (!b) + return NULL; + + for (i = base; i < argc; i++) { + buffer_putstr(b, argv[i]); + buffer_putc(b, ' '); + } + buffer_putc(b, '\0'); + + return b; +} + +static int hexparse(const char *str, u_int8_t *b, int max_len) + +{ + int i, l, v; + + l = strlen(str); + if ((l&1) || ((l>>1) > max_len)) + return -1; + + memset(b, 0x00, max_len); + + for (i=0; i<l; i++) { + char c = str[i]; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = 10 + (c - 'a'); + else if (c >= 'A' && c <= 'F') + v = 10 + (c - 'a'); + else + return -1; + b[i>>1] |= v << (i&1 ? 0 : 4); + } + + return i>>1; +} + +/* per-subscriber configuration */ +DEFUN(cfg_subscr, + cfg_subscr_cmd, + "subscriber IMSI", + "Select a Subscriber to configure\n") +{ + const char *imsi = argv[0]; + struct gsm_subscriber *subscr; + + subscr = subscr_get_by_imsi(gsmnet, imsi); + if (!subscr) { + vty_out(vty, "%% No subscriber for IMSI %s%s", + imsi, VTY_NEWLINE); + return CMD_WARNING; + } + + /* vty_go_parent should put this subscriber */ + vty->index = subscr; + vty->node = SUBSCR_NODE; + + return CMD_SUCCESS; +} + +static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr) +{ + int rc; + struct gsm_auth_info ainfo; + struct gsm_auth_tuple atuple; + + vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, + subscr->authorized, VTY_NEWLINE); + if (subscr->name) + vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); + if (subscr->extension) + vty_out(vty, " Extension: %s%s", subscr->extension, + VTY_NEWLINE); + if (subscr->imsi) + vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); + if (subscr->tmsi != GSM_RESERVED_TMSI) + vty_out(vty, " TMSI: %08X%s", subscr->tmsi, + VTY_NEWLINE); + + rc = get_authinfo_by_subscr(&ainfo, subscr); + if (!rc) { + vty_out(vty, " A3A8 algorithm id: %d%s", + ainfo.auth_algo, VTY_NEWLINE); + vty_out(vty, " A3A8 Ki: %s%s", + hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len), + VTY_NEWLINE); + } + + rc = get_authtuple_by_subscr(&atuple, subscr); + if (!rc) { + vty_out(vty, " A3A8 last tuple (used %d times):%s", + atuple.use_count, VTY_NEWLINE); + vty_out(vty, " seq # : %d%s", + atuple.key_seq, VTY_NEWLINE); + vty_out(vty, " RAND : %s%s", + hexdump(atuple.rand, sizeof(atuple.rand)), + VTY_NEWLINE); + vty_out(vty, " SRES : %s%s", + hexdump(atuple.sres, sizeof(atuple.sres)), + VTY_NEWLINE); + vty_out(vty, " Kc : %s%s", + hexdump(atuple.kc, sizeof(atuple.kc)), + VTY_NEWLINE); + } + + vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); +} + + +/* Subscriber */ +DEFUN(show_subscr, + show_subscr_cmd, + "show subscriber [IMSI]", + SHOW_STR "Display information about a subscriber\n") +{ + const char *imsi; + struct gsm_subscriber *subscr; + + if (argc >= 1) { + imsi = argv[0]; + subscr = subscr_get_by_imsi(gsmnet, imsi); + if (!subscr) { + vty_out(vty, "%% unknown subscriber%s", + VTY_NEWLINE); + return CMD_WARNING; + } + subscr_dump_full_vty(vty, subscr); + subscr_put(subscr); + + return CMD_SUCCESS; + } + + /* FIXME: iterate over all subscribers ? */ + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN(show_subscr_cache, + show_subscr_cache_cmd, + "show subscriber cache", + SHOW_STR "Display contents of subscriber cache\n") +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, &active_subscribers, entry) { + vty_out(vty, " Subscriber:%s", VTY_NEWLINE); + subscr_dump_full_vty(vty, subscr); + } + + return CMD_SUCCESS; +} + +DEFUN(sms_send_pend, + sms_send_pend_cmd, + "sms send pending", + "Send all pending SMS") +{ + struct gsm_sms *sms; + int id = 0; + + while (1) { + sms = db_sms_get_unsent_by_subscr(gsmnet, id); + if (!sms) + break; + + gsm411_send_sms_subscr(sms->receiver, sms); + + id = sms->receiver->id + 1; + } + + return CMD_SUCCESS; +} + +struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, const char *text) +{ + struct gsm_sms *sms = sms_alloc(); + + if (!sms) + return NULL; + + if (!receiver->lac) { + /* subscriber currently not attached, store in database? */ + return NULL; + } + + sms->receiver = subscr_get(receiver); + strncpy(sms->text, text, sizeof(sms->text)-1); + + /* FIXME: don't use ID 1 static */ + sms->sender = subscr_get_by_id(gsmnet, 1); + sms->reply_path_req = 0; + sms->status_rep_req = 0; + sms->ud_hdr_ind = 0; + sms->protocol_id = 0; /* implicit */ + sms->data_coding_scheme = 0; /* default 7bit */ + strncpy(sms->dest_addr, receiver->extension, sizeof(sms->dest_addr)-1); + /* Generate user_data */ + sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text); + + return sms; +} + +static int _send_sms_buffer(struct gsm_subscriber *receiver, + struct buffer *b, u_int8_t tp_pid) +{ + struct gsm_sms *sms; + + sms = sms_from_text(receiver, buffer_getstr(b)); + sms->protocol_id = tp_pid; + gsm411_send_sms_subscr(receiver, sms); + + return CMD_SUCCESS; +} + +static struct gsm_subscriber *get_subscr_by_argv(const char *type, + const char *id) +{ + if (!strcmp(type, "extension")) + return subscr_get_by_extension(gsmnet, id); + else if (!strcmp(type, "imsi")) + return subscr_get_by_imsi(gsmnet, id); + else if (!strcmp(type, "tmsi")) + return subscr_get_by_tmsi(gsmnet, atoi(id)); + else if (!strcmp(type, "id")) + return subscr_get_by_id(gsmnet, atoi(id)); + + return NULL; +} +#define SUBSCR_TYPES "(extension|imsi|tmsi|id)" + +DEFUN(subscriber_send_sms, + subscriber_send_sms_cmd, + "subscriber " SUBSCR_TYPES " EXTEN sms send .LINE", + "Select subscriber based on extension") +{ + struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]); + struct buffer *b; + int rc; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + b = argv_to_buffer(argc, argv, 2); + rc = _send_sms_buffer(subscr, b, 0); + buffer_free(b); + + subscr_put(subscr); + + return rc; +} + +DEFUN(subscriber_silent_sms, + subscriber_silent_sms_cmd, + "subscriber " SUBSCR_TYPES " EXTEN silent sms send .LINE", + "Select subscriber based on extension") +{ + struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]); + struct buffer *b; + int rc; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + b = argv_to_buffer(argc, argv, 2); + rc = _send_sms_buffer(subscr, b, 64); + buffer_free(b); + + subscr_put(subscr); + + return rc; +} + +DEFUN(subscriber_silent_call_start, + subscriber_silent_call_start_cmd, + "subscriber " SUBSCR_TYPES " EXTEN silent call start (any|tch/f|tch/any|sdcch)", + "Start a silent call to a subscriber") +{ + struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]); + int rc, type; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[2], "tch/f")) + type = RSL_CHANNEED_TCH_F; + else if (!strcmp(argv[2], "tch/any")) + type = RSL_CHANNEED_TCH_ForH; + else if (!strcmp(argv[2], "sdcch")) + type = RSL_CHANNEED_SDCCH; + else + type = RSL_CHANNEED_ANY; /* Defaults to ANY */ + + rc = gsm_silent_call_start(subscr, vty, type); + if (rc <= 0) { + vty_out(vty, "%% Subscriber not attached%s", + VTY_NEWLINE); + subscr_put(subscr); + return CMD_WARNING; + } + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(subscriber_silent_call_stop, + subscriber_silent_call_stop_cmd, + "subscriber " SUBSCR_TYPES " EXTEN silent call stop", + "Stop a silent call to a subscriber") +{ + struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]); + int rc; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + rc = gsm_silent_call_stop(subscr); + if (rc < 0) { + subscr_put(subscr); + return CMD_WARNING; + } + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_subscr_name, + cfg_subscr_name_cmd, + "name NAME", + "Set the name of the subscriber") +{ + const char *name = argv[0]; + struct gsm_subscriber *subscr = vty->index; + + strncpy(subscr->name, name, sizeof(subscr->name)); + + db_sync_subscriber(subscr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_subscr_extension, + cfg_subscr_extension_cmd, + "extension EXTENSION", + "Set the extension of the subscriber") +{ + const char *name = argv[0]; + struct gsm_subscriber *subscr = vty->index; + + strncpy(subscr->extension, name, sizeof(subscr->extension)); + + db_sync_subscriber(subscr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_subscr_authorized, + cfg_subscr_authorized_cmd, + "auth <0-1>", + "Set the authorization status of the subscriber") +{ + int auth = atoi(argv[0]); + struct gsm_subscriber *subscr = vty->index; + + if (auth) + subscr->authorized = 1; + else + subscr->authorized = 0; + + db_sync_subscriber(subscr); + + return CMD_SUCCESS; +} + +#define A3A8_ALG_TYPES "(none|comp128v1)" + +DEFUN(cfg_subscr_a3a8, + cfg_subscr_a3a8_cmd, + "a3a8 " A3A8_ALG_TYPES " [KI]", + "Set a3a8 parameters for the subscriber") +{ + struct gsm_subscriber *subscr = vty->index; + const char *alg_str = argv[0]; + const char *ki_str = argv[1]; + struct gsm_auth_info ainfo; + int rc; + + if (!strcasecmp(alg_str, "none")) { + /* Just erase */ + rc = set_authinfo_for_subscr(NULL, subscr); + } else if (!strcasecmp(alg_str, "comp128v1")) { + /* Parse hex string Ki */ + rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); + if (rc != 16) + return CMD_WARNING; + + /* Set the infos */ + ainfo.auth_algo = AUTH_ALGO_COMP128v1; + ainfo.a3a8_ki_len = rc; + rc = set_authinfo_for_subscr(&ainfo, subscr); + } else { + /* Unknown method */ + return CMD_WARNING; + } + + return rc ? CMD_WARNING : CMD_SUCCESS; +} + +static int scall_cbfn(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct scall_signal_data *sigdata = signal_data; + struct vty *vty = sigdata->data; + + switch (signal) { + case S_SCALL_SUCCESS: + vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s", + sigdata->lchan->ts->trx->arfcn, sigdata->lchan->ts->nr, + VTY_NEWLINE); + break; + case S_SCALL_EXPIRED: + vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE); + break; + } + return 0; +} + +int bsc_vty_init_extra(struct gsm_network *net) +{ + gsmnet = net; + + register_signal_handler(SS_SCALL, scall_cbfn, NULL); + + install_element(VIEW_NODE, &show_subscr_cmd); + install_element(VIEW_NODE, &show_subscr_cache_cmd); + + install_element(VIEW_NODE, &sms_send_pend_cmd); + + install_element(VIEW_NODE, &subscriber_send_sms_cmd); + install_element(VIEW_NODE, &subscriber_silent_sms_cmd); + install_element(VIEW_NODE, &subscriber_silent_call_start_cmd); + install_element(VIEW_NODE, &subscriber_silent_call_stop_cmd); + + install_element(CONFIG_NODE, &cfg_subscr_cmd); + install_node(&subscr_node, dummy_config_write); + + install_default(SUBSCR_NODE); + install_element(SUBSCR_NODE, &cfg_subscr_name_cmd); + install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd); + install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd); + install_element(SUBSCR_NODE, &cfg_subscr_a3a8_cmd); + + return 0; +} diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am new file mode 100644 index 000000000..3b1b93120 --- /dev/null +++ b/openbsc/tests/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = debug gsm0408 db channel sccp diff --git a/openbsc/tests/channel/Makefile.am b/openbsc/tests/channel/Makefile.am new file mode 100644 index 000000000..772965953 --- /dev/null +++ b/openbsc/tests/channel/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) + +noinst_PROGRAMS = channel_test + +channel_test_SOURCES = channel_test.c \ + $(top_srcdir)/src/db.c \ + $(top_srcdir)/src/gsm_subscriber_base.c \ + $(top_srcdir)/src/gsm_subscriber.c \ + $(top_srcdir)/src/debug.c \ + $(top_srcdir)/src/gsm_data.c \ + $(top_srcdir)/src/bts_ipaccess_nanobts.c \ + $(top_srcdir)/src/bts_siemens_bs11.c +channel_test_LDADD = -ldl -ldbi $(LIBOSMOCORE_LIBS) + diff --git a/openbsc/tests/channel/channel_test.c b/openbsc/tests/channel/channel_test.c new file mode 100644 index 000000000..759001c57 --- /dev/null +++ b/openbsc/tests/channel/channel_test.c @@ -0,0 +1,81 @@ +/* + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <stdio.h> +#include <stdlib.h> + +#include <assert.h> + +#include <osmocore/select.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/abis_rsl.h> + +/* our handler */ +static int subscr_cb(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) +{ + assert(hook == 101); + assert(event == 200); + assert(msg == (void*)0x1323L); + assert(data == (void*)0x4242L); + assert(param == (void*)0x2342L); + printf("Reached, didn't crash, test passed\n"); + return 0; +} + +/* mock object for testing, directly invoke the cb... maybe later through the timer */ +void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscriber, int type, gsm_cbfn *cbfn, void *data) +{ + cbfn(101, 200, (void*)0x1323L, (void*)0x4242L, data); +} + + +int main(int argc, char** argv) +{ + struct gsm_network *network; + struct gsm_bts *bts; + + printf("Testing the gsm_subscriber chan logic\n"); + + /* Create a dummy network */ + network = gsm_network_init(1, 1, NULL); + if (!network) + exit(1); + bts = gsm_bts_alloc(network, GSM_BTS_TYPE_BS11, 0, 0); + bts->location_area_code = 23; + + /* Create a dummy subscriber */ + struct gsm_subscriber *subscr = subscr_alloc(); + subscr->lac = 23; + subscr->net = network; + + /* Ask for a channel... */ + subscr_get_channel(subscr, RSL_CHANNEED_TCH_F, subscr_cb, (void*)0x2342L); + + while (1) { + bsc_select_main(0); + } +} + +void nm_state_event() {} +void input_event() {} +void sms_alloc() {} + +struct tlv_definition nm_att_tlvdef; + diff --git a/openbsc/tests/db/Makefile.am b/openbsc/tests/db/Makefile.am new file mode 100644 index 000000000..6eb9180ce --- /dev/null +++ b/openbsc/tests/db/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) + +noinst_PROGRAMS = db_test + +db_test_SOURCES = db_test.c +db_test_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a $(top_builddir)/src/libbsc.a $(LIBOSMOCORE_LIBS) -ldl -ldbi + diff --git a/openbsc/tests/db/db_test.c b/openbsc/tests/db/db_test.c new file mode 100644 index 000000000..f168acb64 --- /dev/null +++ b/openbsc/tests/db/db_test.c @@ -0,0 +1,106 @@ +/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <openbsc/db.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define COMPARE(original, copy) \ + if (original->id != copy->id) \ + fprintf(stderr, "Ids do not match in %s:%d %llu %llu\n", \ + __FUNCTION__, __LINE__, original->id, copy->id); \ + if (original->lac != copy->lac) \ + fprintf(stderr, "LAC do not match in %s:%d %d %d\n", \ + __FUNCTION__, __LINE__, original->lac, copy->lac); \ + if (original->authorized != copy->authorized) \ + fprintf(stderr, "Authorize do not match in %s:%d %d %d\n", \ + __FUNCTION__, __LINE__, original->authorized, \ + copy->authorized); \ + if (strcmp(original->imsi, copy->imsi) != 0) \ + fprintf(stderr, "IMSIs do not match in %s:%d '%s' '%s'\n", \ + __FUNCTION__, __LINE__, original->imsi, copy->imsi); \ + if (original->tmsi != copy->tmsi) \ + fprintf(stderr, "TMSIs do not match in %s:%d '%u' '%u'\n", \ + __FUNCTION__, __LINE__, original->tmsi, copy->tmsi); \ + if (strcmp(original->name, copy->name) != 0) \ + fprintf(stderr, "names do not match in %s:%d '%s' '%s'\n", \ + __FUNCTION__, __LINE__, original->name, copy->name); \ + if (strcmp(original->extension, copy->extension) != 0) \ + fprintf(stderr, "names do not match in %s:%d '%s' '%s'\n", \ + __FUNCTION__, __LINE__, original->extension, copy->extension); \ + +int main() { + + if (db_init("hlr.sqlite3")) { + printf("DB: Failed to init database. Please check the option settings.\n"); + return 1; + } + printf("DB: Database initialized.\n"); + + if (db_prepare()) { + printf("DB: Failed to prepare database.\n"); + return 1; + } + printf("DB: Database prepared.\n"); + + struct gsm_subscriber *alice = NULL; + struct gsm_subscriber *alice_db; + + char *alice_imsi = "3243245432345"; + alice = db_create_subscriber(NULL, alice_imsi); + db_sync_subscriber(alice); + alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice->imsi); + COMPARE(alice, alice_db); + subscr_put(alice_db); + subscr_put(alice); + + alice_imsi = "3693245423445"; + alice = db_create_subscriber(NULL, alice_imsi); + db_subscriber_assoc_imei(alice, "1234567890"); + db_subscriber_alloc_tmsi(alice); + alice->lac=42; + db_sync_subscriber(alice); + alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice_imsi); + COMPARE(alice, alice_db); + subscr_put(alice); + subscr_put(alice_db); + + alice_imsi = "9993245423445"; + alice = db_create_subscriber(NULL, alice_imsi); + db_subscriber_alloc_tmsi(alice); + alice->lac=42; + db_sync_subscriber(alice); + db_subscriber_assoc_imei(alice, "1234567890"); + db_subscriber_assoc_imei(alice, "6543560920"); + alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice_imsi); + COMPARE(alice, alice_db); + subscr_put(alice); + subscr_put(alice_db); + + db_fini(); + + return 0; +} + +/* stubs */ +void input_event(void) {} +void nm_state_event(void) {} diff --git a/openbsc/tests/debug/Makefile.am b/openbsc/tests/debug/Makefile.am new file mode 100644 index 000000000..8423fd178 --- /dev/null +++ b/openbsc/tests/debug/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) +noinst_PROGRAMS = debug_test + +debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c +debug_test_LDADD = $(LIBOSMOCORE_LIBS) diff --git a/openbsc/tests/debug/debug_test.c b/openbsc/tests/debug/debug_test.c new file mode 100644 index 000000000..0f0c284ab --- /dev/null +++ b/openbsc/tests/debug/debug_test.c @@ -0,0 +1,41 @@ +/* simple test for the debug interface */ +/* + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <openbsc/debug.h> + + +int main(int argc, char** argv) +{ + struct debug_target *stderr_target; + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + + debug_parse_category_mask(stderr_target, "DRLL"); + DEBUGP(DCC, "You should not see this\n"); + + debug_parse_category_mask(stderr_target, "DRLL:DCC"); + DEBUGP(DRLL, "You should see this\n"); + DEBUGP(DCC, "You should see this\n"); + DEBUGP(DMM, "You should not see this\n"); +} diff --git a/openbsc/tests/gsm0408/Makefile.am b/openbsc/tests/gsm0408/Makefile.am new file mode 100644 index 000000000..f98c673ea --- /dev/null +++ b/openbsc/tests/gsm0408/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) +noinst_PROGRAMS = gsm0408_test + +gsm0408_test_SOURCES = gsm0408_test.c +gsm0408_test_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a $(top_builddir)/src/libbsc.a $(LIBOSMOCORE_LIBS) -ldbi diff --git a/openbsc/tests/gsm0408/gsm0408_test.c b/openbsc/tests/gsm0408/gsm0408_test.c new file mode 100644 index 000000000..287d4ee99 --- /dev/null +++ b/openbsc/tests/gsm0408/gsm0408_test.c @@ -0,0 +1,112 @@ +/* simple test for the gsm0408 formatting functions */ +/* + * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.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 <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include <arpa/inet.h> + +#include <openbsc/gsm_04_08.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> + +#define COMPARE(result, op, value) \ + if (!((result) op (value))) {\ + fprintf(stderr, "Compare failed. Was %x should be %x in %s:%d\n",result, value, __FILE__, __LINE__); \ + exit(-1); \ + } + +#define COMPARE_STR(result, value) \ + if (strcmp(result, value) != 0) { \ + fprintf(stderr, "Compare failed. Was %s should be %s in %s:%d\n",result, value, __FILE__, __LINE__); \ + exit(-1); \ + } + +/* + * Test Location Area Identifier formatting. Table 10.5.3 of 04.08 + */ +static void test_location_area_identifier(void) +{ + struct gsm48_loc_area_id lai48; + + printf("Testing test location area identifier\n"); + + /* + * Test the default/test setup. Coming from + * bsc_hack.c dumps + */ + gsm48_generate_lai(&lai48, 1, 1, 1); + COMPARE(lai48.digits[0], ==, 0x00); + COMPARE(lai48.digits[1], ==, 0xF1); + COMPARE(lai48.digits[2], ==, 0x10); + COMPARE(lai48.lac, ==, htons(0x0001)); + + gsm48_generate_lai(&lai48, 602, 1, 15); + COMPARE(lai48.digits[0], ==, 0x06); + COMPARE(lai48.digits[1], ==, 0xF2); + COMPARE(lai48.digits[2], ==, 0x10); + COMPARE(lai48.lac, ==, htons(0x000f)); +} + +static void test_mi_functionality(void) +{ + const char *imsi_odd = "987654321098763"; + const char *imsi_even = "9876543210987654"; + const u_int32_t tmsi = 0xfabeacd0; + u_int8_t mi[128]; + unsigned int mi_len; + char mi_parsed[GSM48_MI_SIZE]; + + printf("Testing parsing and generating TMSI/IMSI\n"); + + /* tmsi code */ + mi_len = gsm48_generate_mid_from_tmsi(mi, tmsi); + gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len - 2); + COMPARE((u_int32_t)strtoul(mi_parsed, NULL, 10), ==, tmsi); + + /* imsi code */ + mi_len = gsm48_generate_mid_from_imsi(mi, imsi_odd); + gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); + printf("hex: %s\n", hexdump(mi, mi_len)); + COMPARE_STR(mi_parsed, imsi_odd); + + mi_len = gsm48_generate_mid_from_imsi(mi, imsi_even); + gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); + printf("hex: %s\n", hexdump(mi, mi_len)); + COMPARE_STR(mi_parsed, imsi_even); +} + +int main(int argc, char** argv) +{ + test_location_area_identifier(); + test_mi_functionality(); + + exit(0); +} + + + +/* + * Stubs to compile and link + */ +void input_event(void) {} +void nm_state_event(void) {} diff --git a/openbsc/tests/sccp/Makefile.am b/openbsc/tests/sccp/Makefile.am new file mode 100644 index 000000000..b35693e82 --- /dev/null +++ b/openbsc/tests/sccp/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) + +noinst_PROGRAMS = sccp_test + +sccp_test_SOURCES = sccp_test.c +sccp_test_LDADD = $(top_builddir)/src/libsccp.a $(top_builddir)/src/libbsc.a $(LIBOSMOCORE_LIBS) + diff --git a/openbsc/tests/sccp/sccp_test.c b/openbsc/tests/sccp/sccp_test.c new file mode 100644 index 000000000..eb41d3eaf --- /dev/null +++ b/openbsc/tests/sccp/sccp_test.c @@ -0,0 +1,828 @@ +/* + * SCCP testing code + * + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 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 <stdio.h> + +#include <arpa/inet.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <osmocore/msgb.h> + +#include <sccp/sccp.h> + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +/* BSC -> MSC */ +static const u_int8_t bssmap_reset[] = { + 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, + 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04, + 0x01, 0x20, +}; + +/* MSC -> BSC reset ack */ +static const u_int8_t bssmap_reset_ack[] = { + 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, + 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, + 0x00, 0x01, 0x31, +}; + +/* MSC -> BSC paging, connection less */ +static const u_int8_t bssmap_paging[] = { + 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, + 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10, + 0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, + 0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06, +}; + +/* MSC -> BSC paging, UDT without PC */ +static const u_int8_t bssmap_udt[] = { + 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, + 0x02, 0x42, 0xfe, 0x10, 0x00, 0x0e, 0x52, 0x08, + 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, 0x31, 0x97, + 0x61, 0x1a, 0x01, 0x06, +}; + +/* BSC -> MSC connection open */ +static const u_int8_t bssmap_cr[] = { + 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02, + 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05, + 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3, + 0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33, + 0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, + 0x31, 0x97, 0x61, 0x00 +}; + +/* MSC -> BSC connection confirm */ +static const u_int8_t bssmap_cc[] = { + 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, +}; + +/* MSC -> BSC DTAP + * + * we fake a bit and make it BSC -> MSC... so the + * payload does not make any sense.. + */ +static const u_int8_t bssmap_dtap[] = { + 0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x0f, 0x01, 0x00, 0x0c, + 0x03, 0x05, 0x5c, 0x08, 0x11, 0x81, 0x33, 0x66, 0x02, 0x13, + 0x45, 0xf4, +}; + +/* MSC -> BSC clear command */ +static const u_int8_t bssmap_clear[] = { + 0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x06, 0x00, 0x04, 0x20, + 0x04, 0x01, 0x09, +}; + +/* MSC -> BSC released */ +static const u_int8_t bssmap_released[] = { + 0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f, + 0x02, 0x23, 0x42, 0x00, +}; + +/* BSC -> MSC released */ +static const u_int8_t bssmap_release_complete[] = { + 0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03 +}; + +struct test_data { + int length; + const u_int8_t *data; + int payload_start; + int payload_length; + u_int8_t first_byte; + + /* in case it should trigger a sccp response */ + int write; + const u_int8_t *response; + int response_length; +}; + +static const struct test_data test_data[] = { + { + .length = ARRAY_SIZE(bssmap_reset), + .data = &bssmap_reset[0], + .payload_start = 12, + .payload_length = ARRAY_SIZE(bssmap_reset) - 12, + .first_byte = 0x0, + }, + { + .length = ARRAY_SIZE(bssmap_reset_ack), + .data = &bssmap_reset_ack[0], + .payload_start = 16, + .payload_length = ARRAY_SIZE(bssmap_reset_ack) - 16, + .first_byte = 0x0, + }, + { + .length = ARRAY_SIZE(bssmap_paging), + .data = &bssmap_paging[0], + .payload_start = 16, + .payload_length = ARRAY_SIZE(bssmap_paging) - 16, + .first_byte = 0x0, + }, + { + .length = ARRAY_SIZE(bssmap_cr), + .data = &bssmap_cr[0], + .payload_start = 12, + /* 0x00 is end of optional data, subtract this byte */ + .payload_length = 31, + .first_byte = 0x0, + + /* the connection request should trigger a connection confirm */ + .write = 1, + .response = &bssmap_cc[0], + .response_length= ARRAY_SIZE(bssmap_cc), + }, + { + .length = ARRAY_SIZE(bssmap_dtap), + .data = &bssmap_dtap[0], + .payload_start = 7, + .payload_length = 15, + .first_byte = 0x01, + }, + { + .length = ARRAY_SIZE(bssmap_clear), + .data = &bssmap_clear[0], + .payload_start = 7, + .payload_length = 6, + .first_byte = 0x00, + }, + { + .length = ARRAY_SIZE(bssmap_released), + .data = &bssmap_released[0], + .payload_length = 2, + .payload_start = 11, + .first_byte = 0x23, + + .write = 1, + .response = &bssmap_release_complete[0], + .response_length= ARRAY_SIZE(bssmap_release_complete), + }, +}; + +/* we will send UDTs and verify they look like this */ +static const struct test_data send_data[] = { + { + .length = ARRAY_SIZE(bssmap_udt), + .data = &bssmap_udt[0], + .payload_start = 12, + .payload_length = ARRAY_SIZE(bssmap_udt) - 12, + .first_byte = 0x0, + }, + { + .length = ARRAY_SIZE(bssmap_reset), + .data = &bssmap_reset[0], + .payload_start = 12, + .payload_length = ARRAY_SIZE(bssmap_reset) - 12, + .first_byte = 0x0, + }, +}; + +struct connection_test { + /* should the connection be refused? */ + int refuse; + + int with_data; + + /* on which side to close the connection? */ + int close_side; + int close_cause; +}; + +/* sccp connection handling we want to test */ +static const struct connection_test connection_tests[] = { + { + .refuse = 1, + }, + { + .refuse = 1, + .with_data = 1, + }, + { + .refuse = 0, + .close_side = 0, + .close_cause = 5, + }, + { + .refuse = 0, + .close_side = 0, + .close_cause = 5, + .with_data = 1, + }, + { + .refuse = 0, + .close_side = 1, + .close_cause = 5, + }, + { + .refuse = 0, + .close_side = 1, + .close_cause = 5, + .with_data = 1, + }, +}; + +struct sccp_parse_header_result { + /* results */ + int msg_type; + int wanted_len; + int src_ssn; + int dst_ssn; + + int has_src_ref, has_dst_ref; + struct sccp_source_reference src_ref; + struct sccp_source_reference dst_ref; + + /* the input */ + const u_int8_t *input; + int input_len; +}; + +static const u_int8_t it_test[] = { +0x10, 0x01, 0x07, +0x94, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00 }; + +static const struct sccp_parse_header_result parse_result[] = { + { + .msg_type = SCCP_MSG_TYPE_IT, + .wanted_len = 0, + .src_ssn = -1, + .dst_ssn = -1, + .has_src_ref = 1, + .has_dst_ref = 1, + + .src_ref = { + .octet1 = 0x01, + .octet2 = 0x04, + .octet3 = 0x00 + }, + .dst_ref = { + .octet1 = 0x01, + .octet2 = 0x07, + .octet3 = 0x94, + }, + + .input = it_test, + .input_len = sizeof(it_test), + }, +}; + + +/* testing procedure: + * - we will use sccp_write and see what will be set in the + * outgoing callback + * - we will call sccp_system_incoming and see which calls + * are made. And then compare it to the ones we expect. We + * want the payload to arrive, or callbacks to be called. + * - we will use sccp_connection_socket and sccp_connection_write + * and verify state handling of connections + */ + +static int current_test; + +/* + * test state... + */ +static int called = 0; +static int matched = 0; +static int write_called = 0; + +#define FAIL(x, args...) printf("FAILURE in %s:%d: " x, __FILE__, __LINE__, ## args) + +/* + * writing these packets and expecting a result + */ +int sccp_read_cb(struct msgb *data, unsigned len, void *context) +{ + u_int16_t payload_length = test_data[current_test].payload_length; + const u_int8_t *got, *wanted; + int i; + + called = 1; + + if (msgb_l3len(data) < len) { + /* this should never be reached */ + FAIL("Something horrible happened.. invalid packet..\n"); + exit(-1); + } + + if (len == 0 || len != payload_length) { + FAIL("length mismatch: got: %d wanted: %d\n", msgb_l3len(data), payload_length); + return -1; + } + + if (data->l3h[0] != test_data[current_test].first_byte) { + FAIL("The first bytes of l3 do not match: 0x%x 0x%x\n", + data->l3h[0], test_data[current_test].first_byte); + return -1; + } + + got = &data->l3h[0]; + wanted = test_data[current_test].data + test_data[current_test].payload_start; + + for (i = 0; i < len; ++i) { + if (got[i] != wanted[i]) { + FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n", + got[i], wanted[i], i); + return -1; + } + } + + matched = 1; + return 0; +} + +int sccp_write_cb(struct msgb *data, void *ctx) +{ + int i = 0; + const u_int8_t *got, *wanted; + + if (test_data[current_test].response == NULL) { + FAIL("Didn't expect write callback\n"); + return -1; + } else if (test_data[current_test].response_length != msgb_l2len(data)) { + FAIL("Size does not match. Got: %d Wanted: %d\n", + msgb_l2len(data), test_data[current_test].response_length); + } + + got = &data->l2h[0]; + wanted = test_data[current_test].response; + + for (i = 0; i < msgb_l2len(data); ++i) { + if (got[i] != wanted[i]) { + FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n", + got[i], wanted[i], i); + return -1; + } + } + + write_called = 1; + return 0; +} + +void sccp_c_read(struct sccp_connection *connection, struct msgb *msgb, unsigned int len) +{ + sccp_read_cb(msgb, len, connection->data_ctx); +} + +void sccp_c_state(struct sccp_connection *connection, int old_state) +{ + if (connection->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) + sccp_connection_free(connection); +} + +int sccp_accept_cb(struct sccp_connection *connection, void *user_data) +{ + called = 1; + unsigned int ref = 0; + ref |= connection->destination_local_reference.octet1 << 24; + ref |= connection->destination_local_reference.octet2 << 16; + ref |= connection->destination_local_reference.octet3 << 8; + ref = ntohl(ref); + + connection->data_cb = sccp_c_read; + connection->state_cb = sccp_c_state; + + /* accept this */ + return 0; +} + +static int sccp_udt_write_cb(struct msgb *data, void *context) +{ + const u_int8_t *got, *wanted; + int i; + + write_called = 1; + + if (send_data[current_test].length != msgb_l2len(data)) { + FAIL("Size does not match. Got: %d Wanted: %d\n", + msgb_l2len(data), send_data[current_test].length); + return -1; + } + + got = &data->l2h[0]; + wanted = send_data[current_test].data; + + for (i = 0; i < msgb_l2len(data); ++i) { + if (got[i] != wanted[i]) { + FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n", + got[i], wanted[i], i); + return -1; + } + } + + matched = 1; + return 0; +} + +static void test_sccp_system(void) +{ + sccp_system_init(sccp_write_cb, NULL); + sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL); + sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL); + + for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) { + unsigned int length = test_data[current_test].length; + struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__); + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, test_data[current_test].data, length); + + called = matched = write_called = 0; + printf("Testing packet: %d\n", current_test); + sccp_system_incoming(msg); + + if (!called || !matched || (test_data[current_test].write != write_called)) + FAIL("current test: %d called: %d matched: %d write: %d\n", + current_test, called, matched, write_called); + + msgb_free(msg); + } +} + +/* test sending of udt */ +static void test_sccp_send_udt(void) +{ + sccp_system_init(sccp_udt_write_cb, NULL); + sccp_set_read(NULL, NULL, NULL); + sccp_connection_set_incoming(NULL, NULL, NULL); + + for (current_test = 0; current_test < ARRAY_SIZE(send_data); ++current_test) { + const struct test_data *test = &send_data[current_test]; + + struct msgb *msg = msgb_alloc(test->payload_length, __func__); + msg->l3h = msgb_put(msg, test->payload_length); + memcpy(msg->l3h, test->data + test->payload_start, test->payload_length); + + matched = write_called = 0; + printf("Testing packet: %d\n", current_test); + sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0); + + if (!matched || !write_called) + FAIL("current test: %d matched: %d write: %d\n", + current_test, matched, write_called); + + msgb_free(msg); + } +} + +/* send udt from one end to another */ +static unsigned int test_value = 0x2442; +static int sccp_udt_read(struct msgb *data, unsigned int len, void *context) +{ + unsigned int *val; + + if (len != 4) { + FAIL("Wrong size: %d\n", msgb_l3len(data)); + return -1; + } + + val = (unsigned int*)data->l3h; + matched = test_value == *val; + + return 0; +} + +static int sccp_write_loop(struct msgb *data, void *context) +{ + /* send it back to us */ + sccp_system_incoming(data); + return 0; +} + +static void test_sccp_udt_communication(void) +{ + struct msgb *data; + unsigned int *val; + + sccp_system_init(sccp_write_loop, NULL); + sccp_set_read(&sccp_ssn_bssap, sccp_udt_read, NULL); + sccp_connection_set_incoming(NULL, NULL, NULL); + + + data = msgb_alloc(4, "test data"); + data->l3h = &data->data[0]; + val = (unsigned int *)msgb_put(data, 4); + *val = test_value; + + matched = 0; + sccp_write(data, &sccp_ssn_bssap, &sccp_ssn_bssap, 0); + + if (!matched) + FAIL("Talking with us didn't work\n"); + + msgb_free(data); +} + + +/* connection testing... open, send, close */ +static const struct connection_test *current_con_test; +static struct sccp_connection *outgoing_con; +static struct sccp_connection *incoming_con; +static int outgoing_data, incoming_data, incoming_state, outgoing_state; + +static struct msgb *test_data1, *test_data2, *test_data3; + +static void sccp_conn_in_state(struct sccp_connection *conn, int old_state) +{ + printf("\tincome: %d -> %d\n", old_state, conn->connection_state); + if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { + if (conn == incoming_con) { + sccp_connection_free(conn); + incoming_con = NULL; + } + } +} + +static void sccp_conn_in_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len) +{ + /* compare the data */ + ++incoming_data; + printf("\tincoming data: %d\n", len); + + /* compare the data */ + if (len != 4) { + FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len); + return; + } + + if (incoming_data == 1) { + if (memcmp(msg->l3h, test_data1->l3h, len) != 0) { + FAIL("Comparing the data failed: %d\n", incoming_data); + incoming_state = 0; + printf("Got: %s\n", hexdump(msg->l3h, len)); + printf("Wanted: %s\n", hexdump(test_data1->l3h, len)); + + } + } else if (incoming_data == 2) { + if (memcmp(msg->l3h, test_data2->l3h, len) != 0) { + FAIL("Comparing the data failed: %d\n", incoming_data); + incoming_state = 0; + printf("Got: %s\n", hexdump(msg->l3h, len)); + printf("Wanted: %s\n", hexdump(test_data2->l3h, len)); + } + } + + /* sending out data */ + if (incoming_data == 2) { + printf("\tReturning data3\n"); + sccp_connection_write(conn, test_data3); + } +} + +static int sccp_conn_accept(struct sccp_connection *conn, void *ctx) +{ + printf("\taccept: %p\n", conn); + conn->state_cb = sccp_conn_in_state; + conn->data_cb = sccp_conn_in_data; + + if (current_con_test->refuse) + return -1; + + incoming_con = conn; + return 0; +} + +/* callbacks for the outgoing side */ +static void sccp_conn_out_state(struct sccp_connection *conn, int old_state) +{ + printf("\toutgoing: %p %d -> %d\n", conn, old_state, conn->connection_state); + + if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { + if (conn == outgoing_con) { + sccp_connection_free(conn); + outgoing_con = NULL; + } + } +} + +static void sccp_conn_out_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len) +{ + ++outgoing_data; + printf("\toutgoing data: %p %d\n", conn, len); + + if (len != 4) + FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len); + + if (outgoing_data == 1) { + if (memcmp(msg->l3h, test_data3->l3h, len) != 0) { + FAIL("Comparing the data failed\n"); + outgoing_state = 0; + } + } +} + +static void do_test_sccp_connection(const struct connection_test *test) +{ + int ret; + + current_con_test = test; + outgoing_con = incoming_con = 0; + + outgoing_con = sccp_connection_socket(); + if (!outgoing_con) { + FAIL("Connection is NULL\n"); + return; + } + + outgoing_con->state_cb = sccp_conn_out_state; + outgoing_con->data_cb = sccp_conn_out_data; + outgoing_data = incoming_data = 0; + incoming_state = outgoing_state = 1; + + /* start testing */ + if (test->with_data) { + if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, test_data1) != 0) + FAIL("Binding failed\n"); + } else { + ++incoming_data; + if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, NULL) != 0) + FAIL("Binding failed\n"); + } + + if (test->refuse) { + if (outgoing_con) + FAIL("Outgoing connection should have been refused.\n"); + } else { + if (!incoming_con) + FAIL("Creating incoming didn't work.\n"); + + printf("\tWriting test data2\n"); + sccp_connection_write(outgoing_con, test_data2); + sccp_connection_send_it(outgoing_con); + + /* closing connection */ + if (test->close_side == 0) + ret = sccp_connection_close(outgoing_con, 0); + else + ret = sccp_connection_close(incoming_con, 0); + + if (ret != 0) + FAIL("Closing the connection failed\n"); + } + + /* outgoing should be gone now */ + if (outgoing_con) + FAIL("Outgoing connection was not properly closed\n"); + + if (incoming_con) + FAIL("Incoming connection was not propery closed.\n"); + + if (test->refuse == 0) { + if (outgoing_data != 1 || incoming_data != 2) { + FAIL("Data sending failed: %d/%d %d/%d\n", + outgoing_data, 1, + incoming_data, 2); + } + } + + if (!incoming_state || !outgoing_state) + FAIL("Failure with the state transition. %d %d\n", + outgoing_state, incoming_state); +} + +static void test_sccp_connection(void) +{ + sccp_system_init(sccp_write_loop, NULL); + sccp_set_read(NULL, NULL, NULL); + sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_conn_accept, NULL); + + test_data1 = msgb_alloc(4, "data1"); + test_data1->l3h = msgb_put(test_data1, 4); + *((unsigned int*)test_data1->l3h) = 0x23421122; + + test_data2 = msgb_alloc(4, "data2"); + test_data2->l3h = msgb_put(test_data2, 4); + *((unsigned int*)test_data2->l3h) = 0x42232211; + + test_data3 = msgb_alloc(4, "data3"); + test_data3->l3h = msgb_put(test_data3, 4); + *((unsigned int*)test_data3->l3h) = 0x2323ff55; + + + for (current_test = 0; current_test < ARRAY_SIZE(connection_tests); ++current_test) { + printf("Testing %d refuse: %d with_data: %d\n", + current_test, connection_tests[current_test].refuse, + connection_tests[current_test].with_data); + do_test_sccp_connection(&connection_tests[current_test]); + } + + msgb_free(test_data1); + msgb_free(test_data2); + msgb_free(test_data3); +} + +/* invalid input */ +static void test_sccp_system_crash(void) +{ + printf("trying to provoke a crash with invalid input\n"); + sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL); + sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL); + + for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) { + int original_length = test_data[current_test].length; + int length = original_length + 2; + int i; + + printf("Testing packet: %d\n", current_test); + + for (i = length; i >= 0; --i) { + unsigned int length = MIN(test_data[current_test].length, i); + struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__); + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, test_data[current_test].data, length); + sccp_system_incoming(msg); + msgb_free(msg); + } + } + + printf("survived\n"); +} + +static void test_sccp_parsing(void) +{ + for (current_test = 0; current_test < ARRAY_SIZE(parse_result); ++current_test) { + struct msgb *msg; + struct sccp_parse_result result; + + msg = msgb_alloc_headroom(1024, 128, "parse-test"); + msgb_put(msg, 1); + msg->l2h = msgb_put(msg, parse_result[current_test].input_len); + memcpy(msg->l2h, parse_result[current_test].input, msgb_l2len(msg)); + + memset(&result, 0, sizeof(result)); + if (sccp_parse_header(msg, &result) != 0) { + fprintf(stderr, "Failed to parse test: %d\n", current_test); + } else { + if (parse_result[current_test].wanted_len != result.data_len) { + fprintf(stderr, "Unexpected data length.\n"); + abort(); + } + + if (parse_result[current_test].has_src_ref) { + if (memcmp(result.source_local_reference, + &parse_result[current_test].src_ref, + sizeof(struct sccp_source_reference)) != 0) { + fprintf(stderr, "SRC REF did not match\n"); + abort(); + } + } + + if (parse_result[current_test].has_dst_ref) { + if (memcmp(result.destination_local_reference, + &parse_result[current_test].dst_ref, + sizeof(struct sccp_source_reference)) != 0) { + fprintf(stderr, "DST REF did not match\n"); + abort(); + } + } + + if (parse_result[current_test].src_ssn != -1) { + fprintf(stderr, "Not implemented.\n"); + abort(); + } + + if (parse_result[current_test].dst_ssn != -1) { + fprintf(stderr, "Not implemented.\n"); + abort(); + } + } + + msgb_free(msg); + } +} + + +int main(int argc, char **argv) +{ + test_sccp_system(); + test_sccp_send_udt(); + test_sccp_udt_communication(); + test_sccp_connection(); + test_sccp_system_crash(); + test_sccp_parsing(); + return 0; +} + +void db_store_counter() {} diff --git a/openbsc/tests/sms.txt b/openbsc/tests/sms.txt new file mode 100644 index 000000000..06c885b8b --- /dev/null +++ b/openbsc/tests/sms.txt @@ -0,0 +1,50 @@ +03 02 01 0a 02 43 0b 00 1d 39 01 1a 00 01 00 07 91 55 11 18 31 28 00 0e 31 20 04 81 21 43 00 00 ff 04 d4 f2 9c 0e + +03 02 01 0a 02 43 0b 00 1d - +39 - TransactionID 3, SMS messages :: gh->proto_discr +01 - CP-DATA :: gh->msg_type +1a - Length: 26 :: gh->data[0] +00 - MTI 0 RP-DATA (ms->n) +01 - MR 1 +00 - RP-OA +07 - RP-DA (SMSC Length) +91 - International No, Numbering Plan +55 11 18 31 28 00 - 551181138200 +0e - RP-UD len (14) + +TPDU (14 byte): +31 - MTI(01), VPF(2:relative), MMS(0), SRI(1), UDHI(0), RP(0) +20 - Message Reference +04 - DA length +81 - Numbering Plan, National number +21 43 - DA 1234 +00 - PID +00 - DCS +ff - Validity period +04 - User Data length (04) +d4 f2 9c 0e - gsm_default 7bit encoded "Test" (4 byte) + +03 02 01 0a 02 43 0b 00 9f 09 01 9c 00 da 00 07 91 88 96 13 00 00 99 90 11 7b 04 81 22 22 00 08 ff 86 6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d + +03 02 01 0a 02 43 0b 00 9f - lower levels +09 - TransactionID 0, SMS messages +01 - CP-DATA +9c - Length: (156) +00 - MTI 0 RP-DATA (ms->n) +da - MR (?) +00 - RP-OA +07 - RP-DA (SMSC Length) +91 - International No. +88 96 13 00 00 99 +90 - RP-UD len (144) +11 - +7b - Message Reference +04 - DA length +81 - Numbering Plan +22 22 - Address 2222 +00 - PID +08 - DCS (UCS2 charset) +ff - Validity period +86 - User Data length (134) +6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d + diff --git a/openbsc/tools/hlrstat.pl b/openbsc/tools/hlrstat.pl new file mode 100755 index 000000000..668fc9a4a --- /dev/null +++ b/openbsc/tools/hlrstat.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl + +use strict; +use DBI; +my $dbh = DBI->connect("dbi:SQLite:dbname=hlr.sqlite3","",""); + + +my %mcc_names; +my %mcc_mnc_names; + +sub get_mcc_mnc_name($) +{ + my $mcc_mnc = shift; + my $ret = $mcc_mnc; + + if ($mcc_mnc_names{$mcc_mnc} ne '') { + $ret = $mcc_mnc_names{$mcc_mnc}; + } + + return $ret; +} + +sub read_networks($) +{ + my $filename = shift; + my $cur_name; + + open(INFILE, $filename); + while (my $l = <INFILE>) { + chomp($l); + if ($l =~ /^#/) { + next; + } + if ($l =~ /^\t/) { + my ($mcc, $mnc, $brand, $r) = split(' ', $l, 4); + #printf("%s|%s|%s\n", $mcc, $mnc, $brand); + $mcc_mnc_names{"$mcc-$mnc"} = $brand; + $mcc_names{$mcc} = $cur_name; + } elsif ($l =~ /^(\w\w)\t(.*)/) { + #printf("%s|%s\n", $1, $2); + $cur_name = $2; + } + } + close(INFILE); +} + +read_networks("networks.tab"); + +my %oper_count; +my %country_count; + +#my $sth = $dbh->prepare("SELECT imsi FROM subscriber where authorized=1"); +my $sth = $dbh->prepare("SELECT imsi FROM subscriber"); + +$sth->execute(); + +while (my $href = $sth->fetchrow_hashref) { + my ($mcc, $mnc) = $$href{imsi} =~ /(\d{3})(\d{2}).*/; + #printf("%s %s-%s \n", $$href{imsi}, $mcc, $mnc); + $oper_count{"$mcc-$mnc"}++; + $country_count{$mcc}++; +} + + +foreach my $c (sort{$country_count{$b} <=> $country_count{$a}} keys %country_count) { + printf("%s: %d\n", $mcc_names{$c}, $country_count{$c}); + + foreach my $k (sort{$oper_count{$b} <=> $oper_count{$a}} keys %oper_count) { + if ($k =~ /^$c-/) { + printf("\t%s: %d\n", get_mcc_mnc_name($k), $oper_count{$k}); + } + } +} diff --git a/rrlp-ephemeris/.gitignore b/rrlp-ephemeris/.gitignore new file mode 100644 index 000000000..88b45e491 --- /dev/null +++ b/rrlp-ephemeris/.gitignore @@ -0,0 +1,4 @@ +asn1_gen/* +*.o +*.a +rrlp-test diff --git a/rrlp-ephemeris/COPYING b/rrlp-ephemeris/COPYING new file mode 100644 index 000000000..8d7088e89 --- /dev/null +++ b/rrlp-ephemeris/COPYING @@ -0,0 +1,2 @@ +See the included gpl-2.0.txt or gpl-3.0.txt depending on your +preferences. diff --git a/rrlp-ephemeris/Makefile b/rrlp-ephemeris/Makefile new file mode 100644 index 000000000..742cac7a6 --- /dev/null +++ b/rrlp-ephemeris/Makefile @@ -0,0 +1,44 @@ + +ASN1C=../../../tmp/rrlp/asn1c/asn1c/asn1c +ASN1_INCLUDE=/home/tnt/tmp/rrlp/asn1c/skeletons +CC=gcc +CFLAGS=-I$(ASN1_INCLUDE) -Iasn1_gen -O3 -Wall + +ASN1_FILES=$(wildcard asn1/*.asn) + + +all: rrlp-test + + +rrlp-test: libgsm-asn1.a gps.o ubx.o ubx-parse.o rrlp.o main.o + $(CC) -o $@ gps.o ubx.o ubx-parse.o rrlp.o main.o -L. -lgsm-asn1 -lm + + +# +# ASN1 file autogeneration (need recursive makefile call) +# + +ASN1_SOURCES = $(wildcard asn1_gen/*.c) +ASN1_OBJECTS = $(ASN1_SOURCES:.c=.o) + +libgsm-asn1.a: $(ASN1_FILES) + mkdir -p asn1_gen && \ + cd asn1_gen && \ + $(ASN1C) -fskeletons-copy -fnative-types -gen-PER $(addprefix ../,$^) + @rm asn1_gen/converter-sample.c asn1_gen/Makefile.am.sample + @$(MAKE) libgsm-asn1.a.submake + +libgsm-asn1.a.submake: $(ASN1_OBJECTS) + $(AR) rcs libgsm-asn1.a $^ + +.PHONY: libgsm-asn1.a.submake + + +# +# Clean +# + +clean: + rm -Rf asn1_gen + rm -f libgsm-asn1.a *.o rrlp-test + diff --git a/rrlp-ephemeris/asn1/MAP-BS-Code.asn b/rrlp-ephemeris/asn1/MAP-BS-Code.asn new file mode 100644 index 000000000..1d2536676 --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-BS-Code.asn @@ -0,0 +1,131 @@ +-- $Id: MAP-BS-Code.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.10 Bearer Service Codes + +MAP-BS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-BS-Code (20) version11 (11)} + +DEFINITIONS + +::= + +BEGIN + +BearerServiceCode ::= OCTET STRING (SIZE (1)) + -- This type is used to represent the code identifying a single + -- bearer service, a group of bearer services, or all bearer + -- services. The services are defined in TS 3GPP TS 22.002 [3]. + -- The internal structure is defined as follows: + -- + -- plmn-specific bearer services: + -- bits 87654321: defined by the HPLMN operator + + -- rest of bearer services: + -- bit 8: 0 (unused) + -- bits 7654321: group (bits 7654), and rate, if applicable + -- (bits 321) + +Ext-BearerServiceCode ::= OCTET STRING (SIZE (1..5)) + -- This type is used to represent the code identifying a single + -- bearer service, a group of bearer services, or all bearer + -- services. The services are defined in TS 3GPP TS 22.002 [3]. + -- The internal structure is defined as follows: + -- + -- OCTET 1: + -- plmn-specific bearer services: + -- bits 87654321: defined by the HPLMN operator + -- + -- rest of bearer services: + -- bit 8: 0 (unused) + -- bits 7654321: group (bits 7654), and rate, if applicable + -- (bits 321) + + -- OCTETS 2-5: reserved for future use. If received the + -- Ext-TeleserviceCode shall be + -- treated according to the exception handling defined for the + -- operation that uses this type. + + + -- Ext-BearerServiceCode includes all values defined for BearerServiceCode. + +allBearerServices BearerServiceCode ::= '00000000'B + +allDataCDA-Services BearerServiceCode ::= '00010000'B +dataCDA-300bps BearerServiceCode ::= '00010001'B +dataCDA-1200bps BearerServiceCode ::= '00010010'B +dataCDA-1200-75bps BearerServiceCode ::= '00010011'B +dataCDA-2400bps BearerServiceCode ::= '00010100'B +dataCDA-4800bps BearerServiceCode ::= '00010101'B +dataCDA-9600bps BearerServiceCode ::= '00010110'B +general-dataCDA BearerServiceCode ::= '00010111'B + +allDataCDS-Services BearerServiceCode ::= '00011000'B +dataCDS-1200bps BearerServiceCode ::= '00011010'B +dataCDS-2400bps BearerServiceCode ::= '00011100'B +dataCDS-4800bps BearerServiceCode ::= '00011101'B +dataCDS-9600bps BearerServiceCode ::= '00011110'B +general-dataCDS BearerServiceCode ::= '00011111'B + +allPadAccessCA-Services BearerServiceCode ::= '00100000'B +padAccessCA-300bps BearerServiceCode ::= '00100001'B +padAccessCA-1200bps BearerServiceCode ::= '00100010'B +padAccessCA-1200-75bps BearerServiceCode ::= '00100011'B +padAccessCA-2400bps BearerServiceCode ::= '00100100'B +padAccessCA-4800bps BearerServiceCode ::= '00100101'B +padAccessCA-9600bps BearerServiceCode ::= '00100110'B +general-padAccessCA BearerServiceCode ::= '00100111'B + +allDataPDS-Services BearerServiceCode ::= '00101000'B +dataPDS-2400bps BearerServiceCode ::= '00101100'B +dataPDS-4800bps BearerServiceCode ::= '00101101'B +dataPDS-9600bps BearerServiceCode ::= '00101110'B +general-dataPDS BearerServiceCode ::= '00101111'B + +allAlternateSpeech-DataCDA BearerServiceCode ::= '00110000'B + +allAlternateSpeech-DataCDS BearerServiceCode ::= '00111000'B + +allSpeechFollowedByDataCDA BearerServiceCode ::= '01000000'B + +allSpeechFollowedByDataCDS BearerServiceCode ::= '01001000'B + +-- The following non-hierarchical Compound Bearer Service +-- Groups are defined in TS 3GPP TS 22.030: +allDataCircuitAsynchronous BearerServiceCode ::= '01010000'B + -- covers "allDataCDA-Services", "allAlternateSpeech-DataCDA" and + -- "allSpeechFollowedByDataCDA" +allAsynchronousServices BearerServiceCode ::= '01100000'B + -- covers "allDataCDA-Services", "allAlternateSpeech-DataCDA", + -- "allSpeechFollowedByDataCDA" and "allPadAccessCDA-Services" +allDataCircuitSynchronous BearerServiceCode ::= '01011000'B + -- covers "allDataCDS-Services", "allAlternateSpeech-DataCDS" and + -- "allSpeechFollowedByDataCDS" +allSynchronousServices BearerServiceCode ::= '01101000'B + -- covers "allDataCDS-Services", "allAlternateSpeech-DataCDS", + -- "allSpeechFollowedByDataCDS" and "allDataPDS-Services" +-- +-- Compound Bearer Service Group Codes are only used in call +-- independent supplementary service operations, i.e. they +-- are not used in InsertSubscriberData or in +-- DeleteSubscriberData messages. + +allPLMN-specificBS BearerServiceCode ::= '11010000'B +plmn-specificBS-1 BearerServiceCode ::= '11010001'B +plmn-specificBS-2 BearerServiceCode ::= '11010010'B +plmn-specificBS-3 BearerServiceCode ::= '11010011'B +plmn-specificBS-4 BearerServiceCode ::= '11010100'B +plmn-specificBS-5 BearerServiceCode ::= '11010101'B +plmn-specificBS-6 BearerServiceCode ::= '11010110'B +plmn-specificBS-7 BearerServiceCode ::= '11010111'B +plmn-specificBS-8 BearerServiceCode ::= '11011000'B +plmn-specificBS-9 BearerServiceCode ::= '11011001'B +plmn-specificBS-A BearerServiceCode ::= '11011010'B +plmn-specificBS-B BearerServiceCode ::= '11011011'B +plmn-specificBS-C BearerServiceCode ::= '11011100'B +plmn-specificBS-D BearerServiceCode ::= '11011101'B +plmn-specificBS-E BearerServiceCode ::= '11011110'B +plmn-specificBS-F BearerServiceCode ::= '11011111'B + +END + diff --git a/rrlp-ephemeris/asn1/MAP-CommonDataTypes.asn b/rrlp-ephemeris/asn1/MAP-CommonDataTypes.asn new file mode 100644 index 000000000..f3d202e35 --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-CommonDataTypes.asn @@ -0,0 +1,633 @@ +-- $Id: MAP-CommonDataTypes.asn 30470 2009-10-10 12:37:56Z krj $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.8 Common data types + +MAP-CommonDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-CommonDataTypes (18) version11 (11)} + +DEFINITIONS + +IMPLICIT TAGS + +::= + +BEGIN + +EXPORTS + + -- general data types and values + AddressString, + ISDN-AddressString, + maxISDN-AddressLength, + FTN-AddressString, + ISDN-SubaddressString, + ExternalSignalInfo, + Ext-ExternalSignalInfo, +AccessNetworkSignalInfo, + SignalInfo, + maxSignalInfoLength, + AlertingPattern, + TBCD-STRING, + + -- data types for numbering and identification + IMSI, + TMSI, + Identity, + SubscriberId, + IMEI, + HLR-List, + LMSI, + GlobalCellId, + NetworkResource, + AdditionalNetworkResource, + NAEA-PreferredCI, + NAEA-CIC, + ASCI-CallReference, + SubscriberIdentity, + PLMN-Id, + + -- data types for CAMEL + CellGlobalIdOrServiceAreaIdOrLAI, + CellGlobalIdOrServiceAreaIdFixedLength, + LAIFixedLength, + + -- data types for subscriber management + BasicServiceCode, + Ext-BasicServiceCode, + EMLPP-Info, + EMLPP-Priority, + MC-SS-Info, + MaxMC-Bearers, + MC-Bearers, + Ext-SS-Status, + + -- data types for geographic location + AgeOfLocationInformation, + LCSClientExternalID, + LCSClientInternalID, + LCSServiceTypeID, +--- WS added exports needed by gsm_map.asn (extra asn1 file to handle older prot. ver.) + ProtocolId, + LCSServiceTypeID +; + +IMPORTS + TeleserviceCode, + Ext-TeleserviceCode +FROM MAP-TS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-TS-Code (19) version11 (11)} + + BearerServiceCode, + Ext-BearerServiceCode +FROM MAP-BS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-BS-Code (20) version11 (11)} + + SS-Code +FROM MAP-SS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-Code (15) version11 (11)} + + ExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} +; + + +-- general data types + +TBCD-STRING ::= OCTET STRING + -- This type (Telephony Binary Coded Decimal String) is used to + -- represent several digits from 0 through 9, *, #, a, b, c, two + -- digits per octet, each digit encoded 0000 to 1001 (0 to 9), + -- 1010 (*), 1011 (#), 1100 (a), 1101 (b) or 1110 (c); 1111 used + -- as filler when there is an odd number of digits. + + -- bits 8765 of octet n encoding digit 2n + -- bits 4321 of octet n encoding digit 2(n-1) +1 + +AddressString ::= OCTET STRING (SIZE (1..maxAddressLength)) + -- This type is used to represent a number for addressing + -- purposes. It is composed of + -- a) one octet for nature of address, and numbering plan + -- indicator. + -- b) digits of an address encoded as TBCD-String. + + -- a) The first octet includes a one bit extension indicator, a + -- 3 bits nature of address indicator and a 4 bits numbering + -- plan indicator, encoded as follows: + + -- bit 8: 1 (no extension) + + -- bits 765: nature of address indicator + -- 000 unknown + -- 001 international number + -- 010 national significant number + -- 011 network specific number + -- 100 subscriber number + -- 101 reserved + -- 110 abbreviated number + -- 111 reserved for extension + + -- bits 4321: numbering plan indicator + -- 0000 unknown + -- 0001 ISDN/Telephony Numbering Plan (Rec ITU-T E.164) + -- 0010 spare + -- 0011 data numbering plan (ITU-T Rec X.121) + -- 0100 telex numbering plan (ITU-T Rec F.69) + -- 0101 spare + -- 0110 land mobile numbering plan (ITU-T Rec E.212) + -- 0111 spare + -- 1000 national numbering plan + -- 1001 private numbering plan + -- 1111 reserved for extension + + -- all other values are reserved. + + -- b) The following octets representing digits of an address + -- encoded as a TBCD-STRING. + +maxAddressLength INTEGER ::= 20 + +ISDN-AddressString ::= + AddressString (SIZE (1..maxISDN-AddressLength)) + -- This type is used to represent ISDN numbers. + +maxISDN-AddressLength INTEGER ::= 9 + +FTN-AddressString ::= + AddressString (SIZE (1..maxFTN-AddressLength)) + -- This type is used to represent forwarded-to numbers. + -- If NAI = international the first digits represent the country code (CC) + -- and the network destination code (NDC) as for E.164. + +maxFTN-AddressLength INTEGER ::= 15 + +ISDN-SubaddressString ::= + OCTET STRING (SIZE (1..maxISDN-SubaddressLength)) + -- This type is used to represent ISDN subaddresses. + -- It is composed of + -- a) one octet for type of subaddress and odd/even indicator. + -- b) 20 octets for subaddress information. + + -- a) The first octet includes a one bit extension indicator, a + -- 3 bits type of subaddress and a one bit odd/even indicator, + -- encoded as follows: + + -- bit 8: 1 (no extension) + + -- bits 765: type of subaddress + -- 000 NSAP (X.213/ISO 8348 AD2) + -- 010 User Specified + -- All other values are reserved + + -- bit 4: odd/even indicator + -- 0 even number of address signals + -- 1 odd number of address signals + -- The odd/even indicator is used when the type of subaddress + -- is "user specified" and the coding is BCD. + + -- bits 321: 000 (unused) + + -- b) Subaddress information. + -- The NSAP X.213/ISO8348AD2 address shall be formatted as specified + -- by octet 4 which contains the Authority and Format Identifier + -- (AFI). The encoding is made according to the "preferred binary + -- encoding" as defined in X.213/ISO834AD2. For the definition + -- of this type of subaddress, see ITU-T Rec I.334. + + -- For User-specific subaddress, this field is encoded according + -- to the user specification, subject to a maximum length of 20 + -- octets. When interworking with X.25 networks BCD coding should + -- be applied. + +maxISDN-SubaddressLength INTEGER ::= 21 + +ExternalSignalInfo ::= SEQUENCE { + protocolId ProtocolId, + signalInfo SignalInfo, + -- Information about the internal structure is given in + -- clause 7.6.9. + extensionContainer ExtensionContainer OPTIONAL, + -- extensionContainer must not be used in version 2 + ...} + +SignalInfo ::= OCTET STRING (SIZE (1..maxSignalInfoLength)) + +maxSignalInfoLength INTEGER ::= 200 + -- This NamedValue represents the theoretical maximum number of octets which is + -- available to carry a single instance of the SignalInfo data type, + -- without requiring segmentation to cope with the network layer service. + -- However, the actual maximum size available for an instance of the data + -- type may be lower, especially when other information elements + -- have to be included in the same component. + +ProtocolId ::= ENUMERATED { + gsm-0408 (1), + gsm-0806 (2), + gsm-BSSMAP (3), + -- Value 3 is reserved and must not be used + ets-300102-1 (4)} + +Ext-ExternalSignalInfo ::= SEQUENCE { + ext-ProtocolId Ext-ProtocolId, + signalInfo SignalInfo, + -- Information about the internal structure is given in + -- clause 7.6.9.10 + extensionContainer ExtensionContainer OPTIONAL, + ...} + +Ext-ProtocolId ::= ENUMERATED { + ets-300356 (1), + ... + } +-- exception handling: +-- For Ext-ExternalSignalInfo sequences containing this parameter with any +-- other value than the ones listed the receiver shall ignore the whole +-- Ext-ExternalSignalInfo sequence. + +AccessNetworkSignalInfo ::= SEQUENCE { + accessNetworkProtocolId AccessNetworkProtocolId, + signalInfo LongSignalInfo, + -- Information about the internal structure is given in clause 7.6.9.1 + + extensionContainer ExtensionContainer OPTIONAL, + ...} + +LongSignalInfo ::= OCTET STRING (SIZE (1..maxLongSignalInfoLength)) + +maxLongSignalInfoLength INTEGER ::= 2560 + -- This Named Value represents the maximum number of octets which is available + -- to carry a single instance of the LongSignalInfo data type using + -- White Book SCCP with the maximum number of segments. + -- It takes account of the octets used by the lower layers of the protocol, and + -- other information elements which may be included in the same component. + +AccessNetworkProtocolId ::= ENUMERATED { + ts3G-48006 (1), + ts3G-25413 (2), + ...} + -- exception handling: + -- For AccessNetworkSignalInfo sequences containing this parameter with any + -- other value than the ones listed the receiver shall ignore the whole + -- AccessNetworkSignalInfo sequence. + +AlertingPattern ::= OCTET STRING (SIZE (1) ) + -- This type is used to represent Alerting Pattern + + -- bits 8765 : 0000 (unused) + + -- bits 43 : type of Pattern + -- 00 level + -- 01 category + -- 10 category + -- all other values are reserved. + + -- bits 21 : type of alerting + +alertingLevel-0 AlertingPattern ::= '00000000'B +alertingLevel-1 AlertingPattern ::= '00000001'B +alertingLevel-2 AlertingPattern ::= '00000010'B + -- all other values of Alerting level are reserved + -- Alerting Levels are defined in GSM 02.07 + +alertingCategory-1 AlertingPattern ::= '00000100'B +alertingCategory-2 AlertingPattern ::= '00000101'B +alertingCategory-3 AlertingPattern ::= '00000110'B +alertingCategory-4 AlertingPattern ::= '00000111'B +alertingCategory-5 AlertingPattern ::= '00001000'B + -- all other values of Alerting Category are reserved + -- Alerting categories are defined in GSM 02.07 + +-- data types for numbering and identification + +IMSI ::= TBCD-STRING (SIZE (3..8)) + -- digits of MCC, MNC, MSIN are concatenated in this order. + +Identity ::= CHOICE { + imsi IMSI, + imsi-WithLMSI IMSI-WithLMSI} + +IMSI-WithLMSI ::= SEQUENCE { + imsi IMSI, + lmsi LMSI, + -- a special value 00000000 indicates that the LMSI is not in use + ...} + +ASCI-CallReference ::= TBCD-STRING (SIZE (1..8)) + -- digits of VGCS/VBS-area,Group-ID are concatenated in this order if there is a + -- VGCS/VBS-area. + +TMSI ::= OCTET STRING (SIZE (1..4)) + +SubscriberId ::= CHOICE { + imsi [0] IMSI, + tmsi [1] TMSI} + +IMEI ::= TBCD-STRING (SIZE (8)) + -- Refers to International Mobile Station Equipment Identity + -- and Software Version Number (SVN) defined in TS 3GPP TS 23.003 [17]. + -- If the SVN is not present the last octet shall contain the + -- digit 0 and a filler. + -- If present the SVN shall be included in the last octet. + +HLR-Id ::= IMSI + -- leading digits of IMSI, i.e. (MCC, MNC, leading digits of + -- MSIN) forming HLR Id defined in TS 3GPP TS 23.003 [17]. + +HLR-List ::= SEQUENCE SIZE (1..maxNumOfHLR-Id) OF + HLR-Id + +maxNumOfHLR-Id INTEGER ::= 50 + +LMSI ::= OCTET STRING (SIZE (4)) + +GlobalCellId ::= OCTET STRING (SIZE (5..7)) + -- Refers to Cell Global Identification defined in TS 3GPP TS 23.003 [17]. + -- The internal structure is defined as follows: + -- octet 1 bits 4321 Mobile Country Code 1st digit + -- bits 8765 Mobile Country Code 2nd digit + -- octet 2 bits 4321 Mobile Country Code 3rd digit + -- bits 8765 Mobile Network Code 3rd digit + -- or filler (1111) for 2 digit MNCs + -- octet 3 bits 4321 Mobile Network Code 1st digit + -- bits 8765 Mobile Network Code 2nd digit + -- octets 4 and 5 Location Area Code according to TS 3GPP TS 24.008 [35] + -- octets 6 and 7 Cell Identity (CI) according to TS 3GPP TS 24.008 [35] + +NetworkResource ::= ENUMERATED { + plmn (0), + hlr (1), + vlr (2), + pvlr (3), + controllingMSC (4), + vmsc (5), + eir (6), + rss (7)} + +AdditionalNetworkResource ::= ENUMERATED { + sgsn (0), + ggsn (1), + gmlc (2), + gsmSCF (3), + nplr (4), + auc (5), + ... , + ue (6), + mme (7)} + -- if unknown value is received in AdditionalNetworkResource + -- it shall be ignored. + + +NAEA-PreferredCI ::= SEQUENCE { + naea-PreferredCIC [0] NAEA-CIC, + extensionContainer [1] ExtensionContainer OPTIONAL, + ...} + +NAEA-CIC ::= OCTET STRING (SIZE (3)) + -- The internal structure is defined by the Carrier Identification + -- parameter in ANSI T1.113.3. Carrier codes between "000" and "999" may + -- be encoded as 3 digits using "000" to "999" or as 4 digits using + -- "0000" to "0999". Carrier codes between "1000" and "9999" are encoded + -- using 4 digits. + +SubscriberIdentity ::= CHOICE { + imsi [0] IMSI, + msisdn [1] ISDN-AddressString + } + +LCSClientExternalID ::= SEQUENCE { + externalAddress [0] ISDN-AddressString OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ... } + +LCSClientInternalID ::= ENUMERATED { + broadcastService (0), + o-andM-HPLMN (1), + o-andM-VPLMN (2), + anonymousLocation (3), + targetMSsubscribedService (4), + ... } +-- for a CAMEL phase 3 PLMN operator client, the value targetMSsubscribedService shall be used + +LCSServiceTypeID ::= INTEGER (0..127) + -- the integer values 0-63 are reserved for Standard LCS service types + -- the integer values 64-127 are reserved for Non Standard LCS service types + +-- Standard LCS Service Types +emergencyServices LCSServiceTypeID ::= 0 +emergencyAlertServices LCSServiceTypeID ::= 1 +personTracking LCSServiceTypeID ::= 2 +fleetManagement LCSServiceTypeID ::= 3 +assetManagement LCSServiceTypeID ::= 4 +trafficCongestionReporting LCSServiceTypeID ::= 5 +roadsideAssistance LCSServiceTypeID ::= 6 +routingToNearestCommercialEnterprise LCSServiceTypeID ::= 7 +navigation LCSServiceTypeID ::= 8 + --this service type is reserved for use in previous releases +citySightseeing LCSServiceTypeID ::= 9 +localizedAdvertising LCSServiceTypeID ::= 10 +mobileYellowPages LCSServiceTypeID ::= 11 +trafficAndPublicTransportationInfo LCSServiceTypeID ::= 12 +weather LCSServiceTypeID ::= 13 +assetAndServiceFinding LCSServiceTypeID ::= 14 +gaming LCSServiceTypeID ::= 15 +findYourFriend LCSServiceTypeID ::= 16 +dating LCSServiceTypeID ::= 17 +chatting LCSServiceTypeID ::= 18 +routeFinding LCSServiceTypeID ::= 19 +whereAmI LCSServiceTypeID ::= 20 + +-- The values of LCSServiceTypeID are defined according to 3GPP TS 22.071. + +-- Non Standard LCS Service Types +serv64 LCSServiceTypeID ::= 64 +serv65 LCSServiceTypeID ::= 65 +serv66 LCSServiceTypeID ::= 66 +serv67 LCSServiceTypeID ::= 67 +serv68 LCSServiceTypeID ::= 68 +serv69 LCSServiceTypeID ::= 69 +serv70 LCSServiceTypeID ::= 70 +serv71 LCSServiceTypeID ::= 71 +serv72 LCSServiceTypeID ::= 72 +serv73 LCSServiceTypeID ::= 73 +serv74 LCSServiceTypeID ::= 74 +serv75 LCSServiceTypeID ::= 75 +serv76 LCSServiceTypeID ::= 76 +serv77 LCSServiceTypeID ::= 77 +serv78 LCSServiceTypeID ::= 78 +serv79 LCSServiceTypeID ::= 79 +serv80 LCSServiceTypeID ::= 80 +serv81 LCSServiceTypeID ::= 81 +serv82 LCSServiceTypeID ::= 82 +serv83 LCSServiceTypeID ::= 83 +serv84 LCSServiceTypeID ::= 84 +serv85 LCSServiceTypeID ::= 85 +serv86 LCSServiceTypeID ::= 86 +serv87 LCSServiceTypeID ::= 87 +serv88 LCSServiceTypeID ::= 88 +serv89 LCSServiceTypeID ::= 89 +serv90 LCSServiceTypeID ::= 90 +serv91 LCSServiceTypeID ::= 91 +serv92 LCSServiceTypeID ::= 92 +serv93 LCSServiceTypeID ::= 93 +serv94 LCSServiceTypeID ::= 94 +serv95 LCSServiceTypeID ::= 95 +serv96 LCSServiceTypeID ::= 96 +serv97 LCSServiceTypeID ::= 97 +serv98 LCSServiceTypeID ::= 98 +serv99 LCSServiceTypeID ::= 99 +serv100 LCSServiceTypeID ::= 100 +serv101 LCSServiceTypeID ::= 101 +serv102 LCSServiceTypeID ::= 102 +serv103 LCSServiceTypeID ::= 103 +serv104 LCSServiceTypeID ::= 104 +serv105 LCSServiceTypeID ::= 105 +serv106 LCSServiceTypeID ::= 106 +serv107 LCSServiceTypeID ::= 107 +serv108 LCSServiceTypeID ::= 108 +serv109 LCSServiceTypeID ::= 109 +serv110 LCSServiceTypeID ::= 110 +serv111 LCSServiceTypeID ::= 111 +serv112 LCSServiceTypeID ::= 112 +serv113 LCSServiceTypeID ::= 113 +serv114 LCSServiceTypeID ::= 114 +serv115 LCSServiceTypeID ::= 115 +serv116 LCSServiceTypeID ::= 116 +serv117 LCSServiceTypeID ::= 117 +serv118 LCSServiceTypeID ::= 118 +serv119 LCSServiceTypeID ::= 119 +serv120 LCSServiceTypeID ::= 120 +serv121 LCSServiceTypeID ::= 121 +serv122 LCSServiceTypeID ::= 122 +serv123 LCSServiceTypeID ::= 123 +serv124 LCSServiceTypeID ::= 124 +serv125 LCSServiceTypeID ::= 125 +serv126 LCSServiceTypeID ::= 126 +serv127 LCSServiceTypeID ::= 127 + +PLMN-Id ::= OCTET STRING (SIZE (3)) + -- The internal structure is defined as follows: + -- octet 1 bits 4321 Mobile Country Code 1st digit + -- bits 8765 Mobile Country Code 2nd digit + -- octet 2 bits 4321 Mobile Country Code 3rd digit + -- bits 8765 Mobile Network Code 3rd digit + -- or filler (1111) for 2 digit MNCs + -- octet 3 bits 4321 Mobile Network Code 1st digit + -- bits 8765 Mobile Network Code 2nd digit + +-- data types for CAMEL + +CellGlobalIdOrServiceAreaIdOrLAI ::= CHOICE { + cellGlobalIdOrServiceAreaIdFixedLength [0] CellGlobalIdOrServiceAreaIdFixedLength, + laiFixedLength [1] LAIFixedLength} + +CellGlobalIdOrServiceAreaIdFixedLength ::= OCTET STRING (SIZE (7)) + -- Refers to Cell Global Identification or Service Are Identification + -- defined in 3GPP TS 23.003. + -- The internal structure is defined as follows: + -- octet 1 bits 4321 Mobile Country Code 1st digit + -- bits 8765 Mobile Country Code 2nd digit + -- octet 2 bits 4321 Mobile Country Code 3rd digit + -- bits 8765 Mobile Network Code 3rd digit + -- or filler (1111) for 2 digit MNCs + -- octet 3 bits 4321 Mobile Network Code 1st digit + -- bits 8765 Mobile Network Code 2nd digit + -- octets 4 and 5 Location Area Code according to 3GPP TS 24.008 + -- octets 6 and 7 Cell Identity (CI) value or + -- Service Area Code (SAC) value + -- according to 3GPP TS 23.003 + +LAIFixedLength ::= OCTET STRING (SIZE (5)) + -- Refers to Location Area Identification defined in 3GPP TS 23.003 [17]. + -- The internal structure is defined as follows: + -- octet 1 bits 4321 Mobile Country Code 1st digit + -- bits 8765 Mobile Country Code 2nd digit + -- octet 2 bits 4321 Mobile Country Code 3rd digit + -- bits 8765 Mobile Network Code 3rd digit + -- or filler (1111) for 2 digit MNCs + -- octet 3 bits 4321 Mobile Network Code 1st digit + -- bits 8765 Mobile Network Code 2nd digit + -- octets 4 and 5 Location Area Code according to 3GPP TS 24.008 [35] + +-- data types for subscriber management + +BasicServiceCode ::= CHOICE { + bearerService [2] BearerServiceCode, + teleservice [3] TeleserviceCode} + +Ext-BasicServiceCode ::= CHOICE { + ext-BearerService [2] Ext-BearerServiceCode, + ext-Teleservice [3] Ext-TeleserviceCode} + +EMLPP-Info ::= SEQUENCE { + maximumentitledPriority EMLPP-Priority, + defaultPriority EMLPP-Priority, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +EMLPP-Priority ::= INTEGER (0..15) + -- The mapping from the values A,B,0,1,2,3,4 to the integer-value is + -- specified as follows where A is the highest and 4 is the lowest + -- priority level + -- the integer values 7-15 are spare and shall be mapped to value 4 + +priorityLevelA EMLPP-Priority ::= 6 +priorityLevelB EMLPP-Priority ::= 5 +priorityLevel0 EMLPP-Priority ::= 0 +priorityLevel1 EMLPP-Priority ::= 1 +priorityLevel2 EMLPP-Priority ::= 2 +priorityLevel3 EMLPP-Priority ::= 3 +priorityLevel4 EMLPP-Priority ::= 4 + +MC-SS-Info ::= SEQUENCE { + ss-Code [0] SS-Code, + ss-Status [1] Ext-SS-Status, + nbrSB [2] MaxMC-Bearers, + nbrUser [3] MC-Bearers, + extensionContainer [4] ExtensionContainer OPTIONAL, + ...} + +MaxMC-Bearers ::= INTEGER (2..maxNumOfMC-Bearers) + +MC-Bearers ::= INTEGER (1..maxNumOfMC-Bearers) + +maxNumOfMC-Bearers INTEGER ::= 7 + +Ext-SS-Status ::= OCTET STRING (SIZE (1..5)) + + -- OCTET 1: + -- + -- bits 8765: 0000 (unused) + -- bits 4321: Used to convey the "P bit","R bit","A bit" and "Q bit", + -- representing supplementary service state information + -- as defined in TS 3GPP TS 23.011 [22] + + -- bit 4: "Q bit" + + -- bit 3: "P bit" + + -- bit 2: "R bit" + + -- bit 1: "A bit" + + -- OCTETS 2-5: reserved for future use. They shall be discarded if + -- received and not understood. + + + -- data types for geographic location + +AgeOfLocationInformation ::= INTEGER (0..32767) +-- the value represents the elapsed time in minutes since the last +-- network contact of the mobile station (i.e. the actuality of the +-- location information). +-- value "0" indicates that the MS is currently in contact with the +-- network +-- value "32767" indicates that the location information is at least +-- 32767 minutes old + +END + diff --git a/rrlp-ephemeris/asn1/MAP-ER-DataTypes.asn b/rrlp-ephemeris/asn1/MAP-ER-DataTypes.asn new file mode 100644 index 000000000..d0b90fc78 --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-ER-DataTypes.asn @@ -0,0 +1,415 @@ +-- $Id: MAP-ER-DataTypes.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.7 Error data types + +MAP-ER-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ER-DataTypes (17) version11 (11)} + +DEFINITIONS + +IMPLICIT TAGS + +::= + +BEGIN + +EXPORTS + RoamingNotAllowedParam, + CallBarredParam, + CUG-RejectParam, + SS-IncompatibilityCause, + PW-RegistrationFailureCause, + SM-DeliveryFailureCause, + SystemFailureParam, + DataMissingParam, + UnexpectedDataParam, + FacilityNotSupParam, + OR-NotAllowedParam, + UnknownSubscriberParam, + NumberChangedParam, + UnidentifiedSubParam, + IllegalSubscriberParam, + IllegalEquipmentParam, + BearerServNotProvParam, + TeleservNotProvParam, + TracingBufferFullParam, + NoRoamingNbParam, + AbsentSubscriberParam, + BusySubscriberParam, + NoSubscriberReplyParam, + ForwardingViolationParam, + ForwardingFailedParam, + ATI-NotAllowedParam, + SubBusyForMT-SMS-Param, + MessageWaitListFullParam, + AbsentSubscriberSM-Param, + AbsentSubscriberDiagnosticSM, + ResourceLimitationParam, + NoGroupCallNbParam, + IncompatibleTerminalParam, + ShortTermDenialParam, + LongTermDenialParam, + UnauthorizedRequestingNetwork-Param, + UnauthorizedLCSClient-Param, + PositionMethodFailure-Param, +UnknownOrUnreachableLCSClient-Param, + MM-EventNotSupported-Param, +ATSI-NotAllowedParam, +ATM-NotAllowedParam, +IllegalSS-OperationParam, +SS-NotAvailableParam, +SS-SubscriptionViolationParam, +InformationNotAvailableParam, +TargetCellOutsideGCA-Param, +OngoingGroupCallParam + +; + +IMPORTS + SS-Status +FROM MAP-SS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-DataTypes (14) version11 (11)} + + SignalInfo, + BasicServiceCode, + NetworkResource, + AdditionalNetworkResource +FROM MAP-CommonDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-CommonDataTypes (18) version11 (11)} + + + SS-Code +FROM MAP-SS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-Code (15) version11 (11)} + + ExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} +; + +RoamingNotAllowedParam ::= SEQUENCE { + roamingNotAllowedCause RoamingNotAllowedCause, + extensionContainer ExtensionContainer OPTIONAL, + ..., + additionalRoamingNotAllowedCause [0] AdditionalRoamingNotAllowedCause OPTIONAL } + +-- if the additionalRoamingNotallowedCause is received by the MSC/VLR or SGSN then the +-- roamingNotAllowedCause shall be discarded. + +AdditionalRoamingNotAllowedCause ::= ENUMERATED { + supportedRAT-TypesNotAllowed (0), + ...} + +RoamingNotAllowedCause ::= ENUMERATED { + plmnRoamingNotAllowed (0), + operatorDeterminedBarring (3)} + +CallBarredParam ::= CHOICE { + callBarringCause CallBarringCause, + -- call BarringCause must not be used in version 3 and higher + extensibleCallBarredParam ExtensibleCallBarredParam + -- extensibleCallBarredParam must not be used in version <3 + } + +CallBarringCause ::= ENUMERATED { + barringServiceActive (0), + operatorBarring (1)} + +ExtensibleCallBarredParam ::= SEQUENCE { + callBarringCause CallBarringCause OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ... , + unauthorisedMessageOriginator [1] NULL OPTIONAL } + +CUG-RejectParam ::= SEQUENCE { + cug-RejectCause CUG-RejectCause OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +CUG-RejectCause ::= ENUMERATED { + incomingCallsBarredWithinCUG (0), + subscriberNotMemberOfCUG (1), + requestedBasicServiceViolatesCUG-Constraints (5), + calledPartySS-InteractionViolation (7)} + +SS-IncompatibilityCause ::= SEQUENCE { + ss-Code [1] SS-Code OPTIONAL, + basicService BasicServiceCode OPTIONAL, + ss-Status [4] SS-Status OPTIONAL, + ...} + +PW-RegistrationFailureCause ::= ENUMERATED { + undetermined (0), + invalidFormat (1), + newPasswordsMismatch (2)} + +SM-EnumeratedDeliveryFailureCause ::= ENUMERATED { + memoryCapacityExceeded (0), + equipmentProtocolError (1), + equipmentNotSM-Equipped (2), + unknownServiceCentre (3), + sc-Congestion (4), + invalidSME-Address (5), + subscriberNotSC-Subscriber (6)} + +SM-DeliveryFailureCause ::= SEQUENCE { + sm-EnumeratedDeliveryFailureCause SM-EnumeratedDeliveryFailureCause, + diagnosticInfo SignalInfo OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +AbsentSubscriberSM-Param ::= SEQUENCE { + absentSubscriberDiagnosticSM AbsentSubscriberDiagnosticSM OPTIONAL, + -- AbsentSubscriberDiagnosticSM can be either for non-GPRS + -- or for GPRS + extensionContainer ExtensionContainer OPTIONAL, + ..., + additionalAbsentSubscriberDiagnosticSM [0] AbsentSubscriberDiagnosticSM OPTIONAL } + -- if received, additionalAbsentSubscriberDiagnosticSM + -- is for GPRS and absentSubscriberDiagnosticSM is + -- for non-GPRS + +AbsentSubscriberDiagnosticSM ::= INTEGER (0..255) + -- AbsentSubscriberDiagnosticSM values are defined in 3GPP TS 23.040 + +SystemFailureParam ::= CHOICE { + networkResource NetworkResource, + -- networkResource must not be used in version 3 + extensibleSystemFailureParam ExtensibleSystemFailureParam + -- extensibleSystemFailureParam must not be used in version <3 + } + +ExtensibleSystemFailureParam ::= SEQUENCE { + networkResource NetworkResource OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + additionalNetworkResource [0] AdditionalNetworkResource OPTIONAL, + failureCauseParam [1] FailureCauseParam OPTIONAL } + +FailureCauseParam ::= ENUMERATED { + limitReachedOnNumberOfConcurrentLocationRequests (0), + ... } + -- if unknown value is received in FailureCauseParam it shall be ignored + + +DataMissingParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +UnexpectedDataParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +FacilityNotSupParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ..., + shapeOfLocationEstimateNotSupported [0] NULL OPTIONAL, + neededLcsCapabilityNotSupportedInServingNode [1] NULL OPTIONAL } + +OR-NotAllowedParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +UnknownSubscriberParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ..., + unknownSubscriberDiagnostic UnknownSubscriberDiagnostic OPTIONAL} + +UnknownSubscriberDiagnostic ::= ENUMERATED { + imsiUnknown (0), + gprs-eps-SubscriptionUnknown (1), + ..., + npdbMismatch (2)} + -- if unknown values are received in + -- UnknownSubscriberDiagnostic they shall be discarded + +NumberChangedParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +UnidentifiedSubParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +IllegalSubscriberParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +IllegalEquipmentParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +BearerServNotProvParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +TeleservNotProvParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +TracingBufferFullParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +NoRoamingNbParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +AbsentSubscriberParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ..., + absentSubscriberReason [0] AbsentSubscriberReason OPTIONAL} + +AbsentSubscriberReason ::= ENUMERATED { + imsiDetach (0), + restrictedArea (1), + noPageResponse (2), + ... , + purgedMS (3)} +-- exception handling: at reception of other values than the ones listed the +-- AbsentSubscriberReason shall be ignored. +-- The AbsentSubscriberReason: purgedMS is defined for the Super-Charger feature +-- (see TS 23.116). If this value is received in a Provide Roaming Number response +-- it shall be mapped to the AbsentSubscriberReason: imsiDetach in the Send Routeing +-- Information response + +BusySubscriberParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ..., + ccbs-Possible [0] NULL OPTIONAL, + ccbs-Busy [1] NULL OPTIONAL} + +NoSubscriberReplyParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ForwardingViolationParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ForwardingFailedParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ATI-NotAllowedParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ATSI-NotAllowedParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ATM-NotAllowedParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +IllegalSS-OperationParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +SS-NotAvailableParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +SS-SubscriptionViolationParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +InformationNotAvailableParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +SubBusyForMT-SMS-Param ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ... , + gprsConnectionSuspended NULL OPTIONAL } + -- If GprsConnectionSuspended is not understood it shall + -- be discarded + +MessageWaitListFullParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ResourceLimitationParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +NoGroupCallNbParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +IncompatibleTerminalParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ShortTermDenialParam ::= SEQUENCE { + ...} + +LongTermDenialParam ::= SEQUENCE { + ...} + +UnauthorizedRequestingNetwork-Param ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +UnauthorizedLCSClient-Param ::= SEQUENCE { + unauthorizedLCSClient-Diagnostic [0] UnauthorizedLCSClient-Diagnostic OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ... } + +UnauthorizedLCSClient-Diagnostic ::= ENUMERATED { + noAdditionalInformation (0), + clientNotInMSPrivacyExceptionList (1), + callToClientNotSetup (2), + privacyOverrideNotApplicable (3), + disallowedByLocalRegulatoryRequirements (4), + ..., + unauthorizedPrivacyClass (5), + unauthorizedCallSessionUnrelatedExternalClient (6), + unauthorizedCallSessionRelatedExternalClient (7) } +-- exception handling: +-- any unrecognized value shall be ignored + +PositionMethodFailure-Param ::= SEQUENCE { + positionMethodFailure-Diagnostic [0] PositionMethodFailure-Diagnostic OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ... } + +PositionMethodFailure-Diagnostic ::= ENUMERATED { + congestion (0), + insufficientResources (1), + insufficientMeasurementData (2), + inconsistentMeasurementData (3), + locationProcedureNotCompleted (4), + locationProcedureNotSupportedByTargetMS (5), + qoSNotAttainable (6), + positionMethodNotAvailableInNetwork (7), + positionMethodNotAvailableInLocationArea (8), + ... } +-- exception handling: +-- any unrecognized value shall be ignored + +UnknownOrUnreachableLCSClient-Param ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +MM-EventNotSupported-Param ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +TargetCellOutsideGCA-Param ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +OngoingGroupCallParam ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + + +END + diff --git a/rrlp-ephemeris/asn1/MAP-ExtensionDataTypes.asn b/rrlp-ephemeris/asn1/MAP-ExtensionDataTypes.asn new file mode 100644 index 000000000..d94c057cc --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-ExtensionDataTypes.asn @@ -0,0 +1,74 @@ +-- $Id: MAP-ExtensionDataTypes.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- MAP-ExtensionDataTypes.asn +-- +-- Taken from 3GPP TS 29.002 V8.9.0 (2009-04) +-- +-- 17.7.11 Extension data types +-- + +MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} + +DEFINITIONS + +IMPLICIT TAGS + +::= + +BEGIN + +EXPORTS + + PrivateExtension, + ExtensionContainer, + SLR-ArgExtensionContainer; + + +-- IOC for private MAP extensions + + +MAP-EXTENSION ::= CLASS { + &ExtensionType OPTIONAL, + &extensionId OBJECT IDENTIFIER } + -- The length of the Object Identifier shall not exceed 16 octets and the + -- number of components of the Object Identifier shall not exceed 16 + +-- data types + +ExtensionContainer ::= SEQUENCE { + privateExtensionList [0]PrivateExtensionList OPTIONAL, + pcs-Extensions [1]PCS-Extensions OPTIONAL, + ...} + +SLR-ArgExtensionContainer ::= SEQUENCE { + privateExtensionList [0]PrivateExtensionList OPTIONAL, + slr-Arg-PCS-Extensions [1]SLR-Arg-PCS-Extensions OPTIONAL, + ...} + +PrivateExtensionList ::= SEQUENCE SIZE (1..maxNumOfPrivateExtensions) OF + PrivateExtension + +PrivateExtension ::= SEQUENCE { + extId MAP-EXTENSION.&extensionId + ({ExtensionSet}), + extType MAP-EXTENSION.&ExtensionType + ({ExtensionSet}{@extId}) OPTIONAL} + +maxNumOfPrivateExtensions INTEGER ::= 10 + +ExtensionSet MAP-EXTENSION ::= + {... + -- ExtensionSet is the set of all defined private extensions + } + -- Unsupported private extensions shall be discarded if received. + +PCS-Extensions ::= SEQUENCE { + ...} + +SLR-Arg-PCS-Extensions ::= SEQUENCE { + ..., + na-ESRK-Request [0] NULL OPTIONAL } + +END + diff --git a/rrlp-ephemeris/asn1/MAP-LCS-DataTypes.asn b/rrlp-ephemeris/asn1/MAP-LCS-DataTypes.asn new file mode 100644 index 000000000..2434b89f9 --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-LCS-DataTypes.asn @@ -0,0 +1,657 @@ +-- $Id: MAP-LCS-DataTypes.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- MAP-LCS-DataTypes.asn +-- +-- Taken from 3GPP TS 29.002 V8.9.0 (2009-04) +-- +-- 17.7.13 Location service data types +-- + +MAP-LCS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-LCS-DataTypes (25) version11 (11)} + +DEFINITIONS +IMPLICIT TAGS +::= +BEGIN + +EXPORTS + RoutingInfoForLCS-Arg, + RoutingInfoForLCS-Res, + ProvideSubscriberLocation-Arg, + ProvideSubscriberLocation-Res, + SubscriberLocationReport-Arg, + SubscriberLocationReport-Res, +LocationType, +DeferredLocationEventType, +LCSClientName, +LCS-QoS, +Horizontal-Accuracy, +ResponseTime, +Ext-GeographicalInformation, +VelocityEstimate, +SupportedGADShapes, +Add-GeographicalInformation, +LCSRequestorID, +LCS-ReferenceNumber, +LCSCodeword, +AreaEventInfo, +ReportingPLMNList, +PeriodicLDRInfo, +SequenceNumber +; + +IMPORTS + AddressString, + ISDN-AddressString, + IMEI, + IMSI, + LMSI, + SubscriberIdentity, + AgeOfLocationInformation, + LCSClientExternalID, + LCSClientInternalID, +LCSServiceTypeID, +CellGlobalIdOrServiceAreaIdOrLAI, +PLMN-Id +FROM MAP-CommonDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-CommonDataTypes (18) version11 (11)} + + ExtensionContainer, + SLR-ArgExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} + + USSD-DataCodingScheme, +USSD-String +FROM MAP-SS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) gsm-Network (1) modules (3) + map-SS-DataTypes (14) version11 (11)} + + APN, + GSN-Address, + SupportedLCS-CapabilitySets +FROM MAP-MS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-MS-DataTypes (11) version11 (11)} + + Additional-Number +FROM MAP-SM-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SM-DataTypes (16) version11 (11)} +; + + +RoutingInfoForLCS-Arg ::= SEQUENCE { + mlcNumber [0] ISDN-AddressString, + targetMS [1] SubscriberIdentity, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +RoutingInfoForLCS-Res ::= SEQUENCE { + targetMS [0] SubscriberIdentity, + lcsLocationInfo [1] LCSLocationInfo, + extensionContainer [2] ExtensionContainer OPTIONAL, + ..., + v-gmlc-Address [3] GSN-Address OPTIONAL, + h-gmlc-Address [4] GSN-Address OPTIONAL, + ppr-Address [5] GSN-Address OPTIONAL, + additional-v-gmlc-Address [6] GSN-Address OPTIONAL } + +LCSLocationInfo ::= SEQUENCE { + networkNode-Number ISDN-AddressString, + -- NetworkNode-number can be either msc-number or sgsn-number + lmsi [0] LMSI OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ... , + gprsNodeIndicator [2] NULL OPTIONAL, + -- gprsNodeIndicator is set only if the SGSN number is sent as the Network Node Number + additional-Number [3] Additional-Number OPTIONAL, + supportedLCS-CapabilitySets [4] SupportedLCS-CapabilitySets OPTIONAL, + additional-LCS-CapabilitySets [5] SupportedLCS-CapabilitySets OPTIONAL + } + +ProvideSubscriberLocation-Arg ::= SEQUENCE { + locationType LocationType, + mlc-Number ISDN-AddressString, + lcs-ClientID [0] LCS-ClientID OPTIONAL, + privacyOverride [1] NULL OPTIONAL, + imsi [2] IMSI OPTIONAL, + msisdn [3] ISDN-AddressString OPTIONAL, + lmsi [4] LMSI OPTIONAL, + imei [5] IMEI OPTIONAL, + lcs-Priority [6] LCS-Priority OPTIONAL, + lcs-QoS [7] LCS-QoS OPTIONAL, + extensionContainer [8] ExtensionContainer OPTIONAL, + ... , + supportedGADShapes [9] SupportedGADShapes OPTIONAL, + lcs-ReferenceNumber [10] LCS-ReferenceNumber OPTIONAL, + lcsServiceTypeID [11] LCSServiceTypeID OPTIONAL, + lcsCodeword [12] LCSCodeword OPTIONAL, + lcs-PrivacyCheck [13] LCS-PrivacyCheck OPTIONAL, + areaEventInfo [14] AreaEventInfo OPTIONAL, + h-gmlc-Address [15] GSN-Address OPTIONAL, + mo-lrShortCircuitIndicator [16] NULL OPTIONAL, + periodicLDRInfo [17] PeriodicLDRInfo OPTIONAL, + reportingPLMNList [18] ReportingPLMNList OPTIONAL } + + -- one of imsi or msisdn is mandatory + -- If a location estimate type indicates activate deferred location or cancel deferred + -- location, a lcs-Reference number shall be included. + +LocationType ::= SEQUENCE { + locationEstimateType [0] LocationEstimateType, + ..., + deferredLocationEventType [1] DeferredLocationEventType OPTIONAL } + +LocationEstimateType ::= ENUMERATED { + currentLocation (0), + currentOrLastKnownLocation (1), + initialLocation (2), + ..., + activateDeferredLocation (3), + cancelDeferredLocation (4) , + notificationVerificationOnly (5) } +-- exception handling: +-- a ProvideSubscriberLocation-Arg containing an unrecognized LocationEstimateType +-- shall be rejected by the receiver with a return error cause of unexpected data value + +DeferredLocationEventType ::= BIT STRING { + msAvailable (0) , + enteringIntoArea (1), + leavingFromArea (2), + beingInsideArea (3) , + periodicLDR (4) } (SIZE (1..16)) +-- beingInsideArea is always treated as oneTimeEvent regardless of the possible value +-- of occurrenceInfo inside areaEventInfo. +-- exception handling: +-- a ProvideSubscriberLocation-Arg containing other values than listed above in +-- DeferredLocationEventType shall be rejected by the receiver with a return error cause of +-- unexpected data value. + +LCS-ClientID ::= SEQUENCE { + lcsClientType [0] LCSClientType, + lcsClientExternalID [1] LCSClientExternalID OPTIONAL, + lcsClientDialedByMS [2] AddressString OPTIONAL, + lcsClientInternalID [3] LCSClientInternalID OPTIONAL, + lcsClientName [4] LCSClientName OPTIONAL, + ..., + lcsAPN [5] APN OPTIONAL, + lcsRequestorID [6] LCSRequestorID OPTIONAL } + +LCSClientType ::= ENUMERATED { + emergencyServices (0), + valueAddedServices (1), + plmnOperatorServices (2), + lawfulInterceptServices (3), + ... } + -- exception handling: + -- unrecognized values may be ignored if the LCS client uses the privacy override + -- otherwise, an unrecognized value shall be treated as unexpected data by a receiver + -- a return error shall then be returned if received in a MAP invoke + +LCSClientName ::= SEQUENCE { + dataCodingScheme [0] USSD-DataCodingScheme, + nameString [2] NameString, + ..., + lcs-FormatIndicator [3] LCS-FormatIndicator OPTIONAL } + +-- The USSD-DataCodingScheme shall indicate use of the default alphabet through the +-- following encoding +-- bit 7 6 5 4 3 2 1 0 +-- 0 0 0 0 1 1 1 1 + +NameString ::= USSD-String (SIZE (1..maxNameStringLength)) + +maxNameStringLength INTEGER ::= 63 + +LCSRequestorID ::= SEQUENCE { + dataCodingScheme [0] USSD-DataCodingScheme, + requestorIDString [1] RequestorIDString, + ..., + lcs-FormatIndicator [2] LCS-FormatIndicator OPTIONAL } + +RequestorIDString ::= USSD-String (SIZE (1..maxRequestorIDStringLength)) + +maxRequestorIDStringLength INTEGER ::= 63 + +LCS-FormatIndicator ::= ENUMERATED { + logicalName (0), + e-mailAddress (1), + msisdn (2), + url (3), + sipUrl (4), + ... } + +LCS-Priority ::= OCTET STRING (SIZE (1)) + -- 0 = highest priority + -- 1 = normal priority + -- all other values treated as 1 + +LCS-QoS ::= SEQUENCE { + horizontal-accuracy [0] Horizontal-Accuracy OPTIONAL, + verticalCoordinateRequest [1] NULL OPTIONAL, + vertical-accuracy [2] Vertical-Accuracy OPTIONAL, responseTime [3] ResponseTime OPTIONAL, + extensionContainer [4] ExtensionContainer OPTIONAL, + ..., + velocityRequest [5] NULL OPTIONAL +} + +Horizontal-Accuracy ::= OCTET STRING (SIZE (1)) + -- bit 8 = 0 + -- bits 7-1 = 7 bit Uncertainty Code defined in 3GPP TS 23.032. The horizontal location + -- error should be less than the error indicated by the uncertainty code with 67% + -- confidence. + +Vertical-Accuracy ::= OCTET STRING (SIZE (1)) + -- bit 8 = 0 + -- bits 7-1 = 7 bit Vertical Uncertainty Code defined in 3GPP TS 23.032. + -- The vertical location error should be less than the error indicated + -- by the uncertainty code with 67% confidence. + +ResponseTime ::= SEQUENCE { + responseTimeCategory ResponseTimeCategory, + ...} +-- note: an expandable SEQUENCE simplifies later addition of a numeric response time. + +ResponseTimeCategory ::= ENUMERATED { + lowdelay (0), + delaytolerant (1), + ... } +-- exception handling: +-- an unrecognized value shall be treated the same as value 1 (delaytolerant) + +SupportedGADShapes ::= BIT STRING { + ellipsoidPoint (0), + ellipsoidPointWithUncertaintyCircle (1), + ellipsoidPointWithUncertaintyEllipse (2), + polygon (3), + ellipsoidPointWithAltitude (4), + ellipsoidPointWithAltitudeAndUncertaintyElipsoid (5), + ellipsoidArc (6) } (SIZE (7..16)) +-- A node shall mark in the BIT STRING all Shapes defined in 3GPP TS 23.032 it supports. +-- exception handling: bits 7 to 15 shall be ignored if received. + +LCS-ReferenceNumber::= OCTET STRING (SIZE(1)) + +LCSCodeword ::= SEQUENCE { + dataCodingScheme [0] USSD-DataCodingScheme, + lcsCodewordString [1] LCSCodewordString, + ...} + +LCSCodewordString ::= USSD-String (SIZE (1..maxLCSCodewordStringLength)) + +maxLCSCodewordStringLength INTEGER ::= 20 + +LCS-PrivacyCheck ::= SEQUENCE { + callSessionUnrelated [0] PrivacyCheckRelatedAction, + callSessionRelated [1] PrivacyCheckRelatedAction OPTIONAL, + ...} + +PrivacyCheckRelatedAction ::= ENUMERATED { + allowedWithoutNotification (0), + allowedWithNotification (1), + allowedIfNoResponse (2), + restrictedIfNoResponse (3), + notAllowed (4), + ...} +-- exception handling: +-- a ProvideSubscriberLocation-Arg containing an unrecognized PrivacyCheckRelatedAction +-- shall be rejected by the receiver with a return error cause of unexpected data value + +AreaEventInfo ::= SEQUENCE { + areaDefinition [0] AreaDefinition, + occurrenceInfo [1] OccurrenceInfo OPTIONAL, + intervalTime [2] IntervalTime OPTIONAL, + ...} + +AreaDefinition ::= SEQUENCE { + areaList [0] AreaList, + ...} + +AreaList ::= SEQUENCE SIZE (1..maxNumOfAreas) OF Area + +maxNumOfAreas INTEGER ::= 10 + +Area ::= SEQUENCE { + areaType [0] AreaType, + areaIdentification [1] AreaIdentification, + ...} + +AreaType ::= ENUMERATED { + countryCode (0), + plmnId (1), + locationAreaId (2), + routingAreaId (3), + cellGlobalId (4), + ..., + utranCellId (5) } + +AreaIdentification ::= OCTET STRING (SIZE (2..7)) + -- The internal structure is defined as follows: + -- octet 1 bits 4321 Mobile Country Code 1st digit + -- bits 8765 Mobile Country Code 2nd digit + -- octet 2 bits 4321 Mobile Country Code 3rd digit + -- bits 8765 Mobile Network Code 3rd digit if 3 digit MNC included + -- or filler (1111) + -- octet 3 bits 4321 Mobile Network Code 1st digit + -- bits 8765 Mobile Network Code 2nd digit + -- octets 4 and 5 Location Area Code (LAC) for Local Area Id, + -- Routing Area Id and Cell Global Id + -- octet 6 Routing Area Code (RAC) for Routing Area Id + -- octets 6 and 7 Cell Identity (CI) for Cell Global Id + -- octets 4 until 7 Utran Cell Identity (UC-Id) for Utran Cell Id + +OccurrenceInfo ::= ENUMERATED { + oneTimeEvent (0), + multipleTimeEvent (1), + ...} + +IntervalTime ::= INTEGER (1..32767) + -- minimum interval time between area reports in seconds + +PeriodicLDRInfo ::= SEQUENCE { + reportingAmount ReportingAmount, + reportingInterval ReportingInterval, + ...} +-- reportingInterval x reportingAmount shall not exceed 8639999 (99 days, 23 hours, +-- 59 minutes and 59 seconds) for compatibility with OMA MLP and RLP + +ReportingAmount ::= INTEGER (1..maxReportingAmount) + +maxReportingAmount INTEGER ::= 8639999 + +ReportingInterval ::= INTEGER (1..maxReportingInterval) +-- ReportingInterval is in seconds + +maxReportingInterval INTEGER ::= 8639999 + +ReportingPLMNList::= SEQUENCE { + plmn-ListPrioritized [0] NULL OPTIONAL, + plmn-List [1] PLMNList, + ...} + +PLMNList::= SEQUENCE SIZE (1..maxNumOfReportingPLMN) OF + ReportingPLMN + +maxNumOfReportingPLMN INTEGER ::= 20 + +ReportingPLMN::= SEQUENCE { + plmn-Id [0] PLMN-Id, + ran-Technology [1] RAN-Technology OPTIONAL, + ran-PeriodicLocationSupport [2] NULL OPTIONAL, + ...} + +RAN-Technology ::= ENUMERATED { + gsm (0), + umts (1), + ...} + +ProvideSubscriberLocation-Res ::= SEQUENCE { + locationEstimate Ext-GeographicalInformation, + ageOfLocationEstimate [0] AgeOfLocationInformation OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ... , + add-LocationEstimate [2] Add-GeographicalInformation OPTIONAL, + deferredmt-lrResponseIndicator [3] NULL OPTIONAL, + geranPositioningData [4] PositioningDataInformation OPTIONAL, + utranPositioningData [5] UtranPositioningDataInfo OPTIONAL, + cellIdOrSai [6] CellGlobalIdOrServiceAreaIdOrLAI OPTIONAL, + sai-Present [7] NULL OPTIONAL, + accuracyFulfilmentIndicator [8] AccuracyFulfilmentIndicator OPTIONAL, + velocityEstimate [9] VelocityEstimate OPTIONAL, + mo-lrShortCircuitIndicator [10] NULL OPTIONAL } + +-- if deferredmt-lrResponseIndicator is set, locationEstimate is ignored. + +-- the add-LocationEstimate parameter shall not be sent to a node that did not indicate the +-- geographic shapes supported in the ProvideSubscriberLocation-Arg +-- The locationEstimate and the add-locationEstimate parameters shall not be sent if +-- the supportedGADShapes parameter has been received in ProvideSubscriberLocation-Arg +-- and the shape encoded in locationEstimate or add-LocationEstimate is not marked +-- as supported in supportedGADShapes. In such a case ProvideSubscriberLocation +-- shall be rejected with error FacilityNotSupported with additional indication +-- shapeOfLocationEstimateNotSupported. +-- sai-Present indicates that the cellIdOrSai parameter contains a Service Area Identity. + +AccuracyFulfilmentIndicator ::= ENUMERATED { + requestedAccuracyFulfilled (0), + requestedAccuracyNotFulfilled (1), + ... } + +Ext-GeographicalInformation ::= OCTET STRING (SIZE (1..maxExt-GeographicalInformation)) + -- Refers to geographical Information defined in 3GPP TS 23.032. + -- This is composed of 1 or more octets with an internal structure according to + -- 3GPP TS 23.032 + -- Octet 1: Type of shape, only the following shapes in 3GPP TS 23.032 are allowed: + -- (a) Ellipsoid point with uncertainty circle + -- (b) Ellipsoid point with uncertainty ellipse + -- (c) Ellipsoid point with altitude and uncertainty ellipsoid + -- (d) Ellipsoid Arc + -- (e) Ellipsoid Point + -- Any other value in octet 1 shall be treated as invalid + -- Octets 2 to 8 for case (a) – Ellipsoid point with uncertainty circle + -- Degrees of Latitude 3 octets + -- Degrees of Longitude 3 octets + -- Uncertainty code 1 octet + -- Octets 2 to 11 for case (b) – Ellipsoid point with uncertainty ellipse: + -- Degrees of Latitude 3 octets + -- Degrees of Longitude 3 octets + -- Uncertainty semi-major axis 1 octet + -- Uncertainty semi-minor axis 1 octet + -- Angle of major axis 1 octet + -- Confidence 1 octet + -- Octets 2 to 14 for case (c) – Ellipsoid point with altitude and uncertainty ellipsoid + -- Degrees of Latitude 3 octets + -- Degrees of Longitude 3 octets + -- Altitude 2 octets + -- Uncertainty semi-major axis 1 octet + -- Uncertainty semi-minor axis 1 octet + -- Angle of major axis 1 octet + -- Uncertainty altitude 1 octet + -- Confidence 1 octet + -- Octets 2 to 13 for case (d) – Ellipsoid Arc + -- Degrees of Latitude 3 octets + -- Degrees of Longitude 3 octets + -- Inner radius 2 octets + -- Uncertainty radius 1 octet + -- Offset angle 1 octet + -- Included angle 1 octet + -- Confidence 1 octet + -- Octets 2 to 7 for case (e) – Ellipsoid Point + -- Degrees of Latitude 3 octets + -- Degrees of Longitude 3 octets + + -- + -- An Ext-GeographicalInformation parameter comprising more than one octet and + -- containing any other shape or an incorrect number of octets or coding according + -- to 3GPP TS 23.032 shall be treated as invalid data by a receiver. + -- + -- An Ext-GeographicalInformation parameter comprising one octet shall be discarded + -- by the receiver if an Add-GeographicalInformation parameter is received + -- in the same message. + -- + -- An Ext-GeographicalInformation parameter comprising one octet shall be treated as + -- invalid data by the receiver if an Add-GeographicalInformation parameter is not + -- received in the same message. + +maxExt-GeographicalInformation INTEGER ::= 20 + -- the maximum length allows for further shapes in 3GPP TS 23.032 to be included in later + -- versions of 3GPP TS 29.002 + +VelocityEstimate ::= OCTET STRING (SIZE (4..7)) + -- Refers to Velocity description defined in 3GPP TS 23.032. + -- This is composed of 4 or more octets with an internal structure according to + -- 3GPP TS 23.032 + -- Octet 1: Type of velocity, only the following types in 3GPP TS 23.032 are allowed: + -- (a) Horizontal Velocity + -- (b) Horizontal with Vertical Velocity + -- (c) Horizontal Velocity with Uncertainty + -- (d) Horizontal with Vertical Velocity and Uncertainty + -- For types Horizontal with Vertical Velocity and Horizontal with Vertical Velocity + -- and Uncertainty, the direction of the Vertical Speed is also included in Octet 1 + -- Any other value in octet 1 shall be treated as invalid + -- Octets 2 to 4 for case (a) Horizontal velocity: + -- Bearing 1 octet + -- Horizontal Speed 2 octets + -- Octets 2 to 5 for case (b) – Horizontal with Vertical Velocity: + -- Bearing 1 octet + -- Horizontal Speed 2 octets + -- Vertical Speed 1 octet + -- Octets 2 to 5 for case (c) – Horizontal velocity with Uncertainty: + -- Bearing 1 octet + -- Horizontal Speed 2 octets + -- Uncertainty Speed 1 octet + -- Octets 2 to 7 for case (d) – Horizontal with Vertical Velocity and Uncertainty: + -- Bearing 1 octet + -- Horizontal Speed 2 octets + -- Vertical Speed 1 octet + -- Horizontal Uncertainty Speed 1 octet + -- Vertical Uncertainty Speed 1 octet + +PositioningDataInformation ::= OCTET STRING (SIZE (2..maxPositioningDataInformation)) + -- Refers to the Positioning Data defined in 3GPP TS 49.031. + -- This is composed of 2 or more octets with an internal structure according to + -- 3GPP TS 49.031. + +maxPositioningDataInformation INTEGER ::= 10 + -- + +UtranPositioningDataInfo ::= OCTET STRING (SIZE (3..maxUtranPositioningDataInfo)) + -- Refers to the Position Data defined in 3GPP TS 25.413. + -- This is composed of the positioningDataDiscriminator and the positioningDataSet + -- included in positionData as defined in 3GPP TS 25.413. + +maxUtranPositioningDataInfo INTEGER ::= 11 + -- + +Add-GeographicalInformation ::= OCTET STRING (SIZE (1..maxAdd-GeographicalInformation)) + -- Refers to geographical Information defined in 3GPP TS 23.032. + -- This is composed of 1 or more octets with an internal structure according to + -- 3GPP TS 23.032 + -- Octet 1: Type of shape, all the shapes defined in 3GPP TS 23.032 are allowed: + -- Octets 2 to n (where n is the total number of octets necessary to encode the shape + -- according to 3GPP TS 23.032) are used to encode the shape itself in accordance with the + -- encoding defined in 3GPP TS 23.032 + -- + -- An Add-GeographicalInformation parameter, whether valid or invalid, received + -- together with a valid Ext-GeographicalInformation parameter in the same message + -- shall be discarded. + -- + -- An Add-GeographicalInformation parameter containing any shape not defined in + -- 3GPP TS 23.032 or an incorrect number of octets or coding according to + -- 3GPP TS 23.032 shall be treated as invalid data by a receiver if not received + -- together with a valid Ext-GeographicalInformation parameter in the same message. + +maxAdd-GeographicalInformation INTEGER ::= 91 + -- the maximum length allows support for all the shapes currently defined in 3GPP TS 23.032 + +SubscriberLocationReport-Arg ::= SEQUENCE { + lcs-Event LCS-Event, + lcs-ClientID LCS-ClientID, + lcsLocationInfo LCSLocationInfo, + msisdn [0] ISDN-AddressString OPTIONAL, + imsi [1] IMSI OPTIONAL, + imei [2] IMEI OPTIONAL, + na-ESRD [3] ISDN-AddressString OPTIONAL, + na-ESRK [4] ISDN-AddressString OPTIONAL, + locationEstimate [5] Ext-GeographicalInformation OPTIONAL, + ageOfLocationEstimate [6] AgeOfLocationInformation OPTIONAL, + slr-ArgExtensionContainer [7] SLR-ArgExtensionContainer OPTIONAL, + ... , + add-LocationEstimate [8] Add-GeographicalInformation OPTIONAL, + deferredmt-lrData [9] Deferredmt-lrData OPTIONAL, + lcs-ReferenceNumber [10] LCS-ReferenceNumber OPTIONAL, + geranPositioningData [11] PositioningDataInformation OPTIONAL, + utranPositioningData [12] UtranPositioningDataInfo OPTIONAL, + cellIdOrSai [13] CellGlobalIdOrServiceAreaIdOrLAI OPTIONAL, + h-gmlc-Address [14] GSN-Address OPTIONAL, + lcsServiceTypeID [15] LCSServiceTypeID OPTIONAL, + sai-Present [17] NULL OPTIONAL, + pseudonymIndicator [18] NULL OPTIONAL, + accuracyFulfilmentIndicator [19] AccuracyFulfilmentIndicator OPTIONAL, + velocityEstimate [20] VelocityEstimate OPTIONAL, + sequenceNumber [21] SequenceNumber OPTIONAL, + periodicLDRInfo [22] PeriodicLDRInfo OPTIONAL, + mo-lrShortCircuitIndicator [23] NULL OPTIONAL } + + -- one of msisdn or imsi is mandatory + -- a location estimate that is valid for the locationEstimate parameter should + -- be transferred in this parameter in preference to the add-LocationEstimate. + -- the deferredmt-lrData parameter shall be included if and only if the lcs-Event + -- indicates a deferredmt-lrResponse. + -- if the lcs-Event indicates a deferredmt-lrResponse then the locationEstimate + -- and the add-locationEstimate parameters shall not be sent if the + -- supportedGADShapes parameter had been received in ProvideSubscriberLocation-Arg + -- and the shape encoded in locationEstimate or add-LocationEstimate was not marked + -- as supported in supportedGADShapes. In such a case terminationCause + -- in deferredmt-lrData shall be present with value + -- shapeOfLocationEstimateNotSupported. + -- If a lcs event indicates deferred mt-lr response, the lcs-Reference number shall be + -- included. + -- sai-Present indicates that the cellIdOrSai parameter contains a Service Area Identity. + +Deferredmt-lrData ::= SEQUENCE { + deferredLocationEventType DeferredLocationEventType, + terminationCause [0] TerminationCause OPTIONAL, + lcsLocationInfo [1] LCSLocationInfo OPTIONAL, + ...} + -- lcsLocationInfo may be included only if a terminationCause is present + -- indicating mt-lrRestart. + +LCS-Event ::= ENUMERATED { + emergencyCallOrigination (0), + emergencyCallRelease (1), + mo-lr (2), + ..., + deferredmt-lrResponse (3) , + deferredmo-lrTTTPInitiation (4) } + -- deferredmt-lrResponse is applicable to the delivery of a location estimate + -- for an LDR initiated earlier by either the network (via an MT-LR activate deferred + -- location) or the UE (via a deferred MO-LR TTTP initiation) + -- exception handling: + -- a SubscriberLocationReport-Arg containing an unrecognized LCS-Event + -- shall be rejected by a receiver with a return error cause of unexpected data value + +TerminationCause ::= ENUMERATED { + normal (0), + errorundefined (1), + internalTimeout (2), + congestion (3), + mt-lrRestart (4), + privacyViolation (5), + ..., + shapeOfLocationEstimateNotSupported (6) , + subscriberTermination (7), + uETermination (8), + networkTermination (9) } +-- mt-lrRestart shall be used to trigger the GMLC to restart the location procedure, +-- either because the sending node knows that the terminal has moved under coverage +-- of another MSC or SGSN (e.g. Send Identification received), or because the subscriber +-- has been deregistered due to a Cancel Location received from HLR. +-- +-- exception handling +-- an unrecognized value shall be treated the same as value 1 (errorundefined) + +SequenceNumber ::= INTEGER (1..maxReportingAmount) + +SubscriberLocationReport-Res ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ..., + na-ESRK [0] ISDN-AddressString OPTIONAL, + na-ESRD [1] ISDN-AddressString OPTIONAL, + h-gmlc-Address [2] GSN-Address OPTIONAL, + mo-lrShortCircuitIndicator [3] NULL OPTIONAL, + reportingPLMNList [4] ReportingPLMNList OPTIONAL, + lcs-ReferenceNumber [5] LCS-ReferenceNumber OPTIONAL } + +-- na-ESRK and na-ESRD are mutually exclusive +-- +-- exception handling +-- receipt of both na-ESRK and na-ESRD shall be treated the same as a return error + + +END + diff --git a/rrlp-ephemeris/asn1/MAP-MS-DataTypes.asn b/rrlp-ephemeris/asn1/MAP-MS-DataTypes.asn new file mode 100644 index 000000000..9c12a028a --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-MS-DataTypes.asn @@ -0,0 +1,2780 @@ +-- $Id: MAP-MS-DataTypes.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.1 Mobile Service data types + +MAP-MS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-MS-DataTypes (11) version11 (11)} + +DEFINITIONS + +IMPLICIT TAGS + +::= + +BEGIN + +EXPORTS + + -- location registration types + UpdateLocationArg, + UpdateLocationRes, + CancelLocationArg, + CancelLocationRes, + PurgeMS-Arg, + PurgeMS-Res, + SendIdentificationArg, + SendIdentificationRes, + UpdateGprsLocationArg, + UpdateGprsLocationRes, + IST-SupportIndicator, + SupportedLCS-CapabilitySets, + + -- gprs location registration types + GSN-Address, + + -- handover types + ForwardAccessSignalling-Arg, + PrepareHO-Arg, + PrepareHO-Res, + PrepareSubsequentHO-Arg, + PrepareSubsequentHO-Res, + ProcessAccessSignalling-Arg, + SendEndSignal-Arg, + SendEndSignal-Res, + + -- authentication management types + SendAuthenticationInfoArg, + SendAuthenticationInfoRes, + AuthenticationFailureReportArg, +AuthenticationFailureReportRes, + + -- security management types + Kc, + Cksn, + + -- equipment management types + CheckIMEI-Arg, + CheckIMEI-Res, + + -- subscriber management types + InsertSubscriberDataArg, + InsertSubscriberDataRes, + LSAIdentity, + DeleteSubscriberDataArg, + DeleteSubscriberDataRes, + Ext-QoS-Subscribed, + Ext2-QoS-Subscribed, + Ext3-QoS-Subscribed, + SubscriberData, + ODB-Data, + SubscriberStatus, + ZoneCodeList, + maxNumOfZoneCodes, + O-CSI, +D-CSI, + O-BcsmCamelTDPCriteriaList, + T-BCSM-CAMEL-TDP-CriteriaList, + SS-CSI, + ServiceKey, + DefaultCallHandling, + CamelCapabilityHandling, + BasicServiceCriteria, + SupportedCamelPhases, + OfferedCamel4CSIs, + OfferedCamel4Functionalities, + maxNumOfCamelTDPData, + CUG-Index, + CUG-Info, + CUG-Interlock, + InterCUG-Restrictions, + IntraCUG-Options, + NotificationToMSUser, + QoS-Subscribed, +IST-AlertTimerValue, + T-CSI, + T-BcsmTriggerDetectionPoint, +APN, +AdditionalInfo, + + -- fault recovery types + ResetArg, + RestoreDataArg, + RestoreDataRes, + +-- provide subscriber info types +GeographicalInformation, +MS-Classmark2, +GPRSMSClass, + + -- subscriber information enquiry types + ProvideSubscriberInfoArg, + ProvideSubscriberInfoRes, + SubscriberInfo, + LocationInformation, + LocationInformationGPRS, + RAIdentity, + SubscriberState, + GPRSChargingID, +MNPInfoRes, + RouteingNumber, + + -- any time information enquiry types + AnyTimeInterrogationArg, + AnyTimeInterrogationRes, + + -- any time information handling types + AnyTimeSubscriptionInterrogationArg, + AnyTimeSubscriptionInterrogationRes, + AnyTimeModificationArg, + AnyTimeModificationRes, + + -- subscriber data modification notification types + NoteSubscriberDataModifiedArg, + NoteSubscriberDataModifiedRes, + + -- gprs location information retrieval types + SendRoutingInfoForGprsArg, + SendRoutingInfoForGprsRes, + + -- failure reporting types + FailureReportArg, + FailureReportRes, + + -- gprs notification types + NoteMsPresentForGprsArg, + NoteMsPresentForGprsRes, + + -- Mobility Management types +NoteMM-EventArg, + NoteMM-EventRes, + NumberPortabilityStatus, + PagingArea, + + -- VGCS / VBS types types +GroupId, +Long-GroupId, +AdditionalSubscriptions + +; + +IMPORTS + maxNumOfSS, + SS-SubscriptionOption, + SS-List, + SS-ForBS-Code, + Password +FROM MAP-SS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-DataTypes (14) version11 (11)} + + SS-Code +FROM MAP-SS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-Code (15) version11 (11)} + + Ext-BearerServiceCode +FROM MAP-BS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-BS-Code (20) version11 (11)} + + Ext-TeleserviceCode +FROM MAP-TS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-TS-Code (19) version11 (11)} + + AddressString, +ISDN-AddressString, + ISDN-SubaddressString, + FTN-AddressString, + AccessNetworkSignalInfo, + IMSI, + IMEI, + TMSI, + HLR-List, + LMSI, + Identity, + GlobalCellId, + CellGlobalIdOrServiceAreaIdOrLAI, + Ext-BasicServiceCode, + NAEA-PreferredCI, + EMLPP-Info, + MC-SS-Info, + SubscriberIdentity, + AgeOfLocationInformation, + LCSClientExternalID, + LCSClientInternalID, + Ext-SS-Status, + LCSServiceTypeID, + ASCI-CallReference, + TBCD-STRING, + LAIFixedLength, + PLMN-Id, +EMLPP-Priority +FROM MAP-CommonDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-CommonDataTypes (18) version11 (11)} + + ExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} + + AbsentSubscriberDiagnosticSM +FROM MAP-ER-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ER-DataTypes (17) version11 (11)} + + TracePropagationList +FROM MAP-OM-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-OM-DataTypes (12) version11 (11)} + +; + +-- location registration types + +UpdateLocationArg ::= SEQUENCE { + imsi IMSI, + msc-Number [1] ISDN-AddressString, + vlr-Number ISDN-AddressString, + lmsi [10] LMSI OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ... , + vlr-Capability [6] VLR-Capability OPTIONAL, + informPreviousNetworkEntity [11] NULL OPTIONAL, + cs-LCS-NotSupportedByUE [12] NULL OPTIONAL, + v-gmlc-Address [2] GSN-Address OPTIONAL, + add-info [13] ADD-Info OPTIONAL, + pagingArea [14] PagingArea OPTIONAL, + skipSubscriberDataUpdate [15] NULL OPTIONAL + -- The skipSubscriberDataUpdate parameter in the UpdateLocationArg and the ADD-Info + -- structures carry the same semantic. + } + +VLR-Capability ::= SEQUENCE{ + supportedCamelPhases [0] SupportedCamelPhases OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ... , + solsaSupportIndicator [2] NULL OPTIONAL, + istSupportIndicator [1] IST-SupportIndicator OPTIONAL, + superChargerSupportedInServingNetworkEntity [3] SuperChargerInfo OPTIONAL, + longFTN-Supported [4] NULL OPTIONAL, + supportedLCS-CapabilitySets [5] SupportedLCS-CapabilitySets OPTIONAL, + offeredCamel4CSIs [6] OfferedCamel4CSIs OPTIONAL, + supportedRAT-TypesIndicator [7] SupportedRAT-Types OPTIONAL, + longGroupID-Supported [8] NULL OPTIONAL } + +SupportedRAT-Types::= BIT STRING { + utran (0), + geran (1), + gan (2), + i-hspa-evolution (3), + e-utran (4)} (SIZE (2..8)) + -- exception handling: bits 5 to 7 shall be ignored if received and not understood + + + +SuperChargerInfo ::= CHOICE { + sendSubscriberData [0] NULL, + subscriberDataStored [1] AgeIndicator } + +AgeIndicator ::= OCTET STRING (SIZE (1..6)) + -- The internal structure of this parameter is implementation specific. + +IST-SupportIndicator ::= ENUMERATED { + basicISTSupported (0), + istCommandSupported (1), + ...} +-- exception handling: +-- reception of values > 1 shall be mapped to ' istCommandSupported ' + +SupportedLCS-CapabilitySets ::= BIT STRING { + lcsCapabilitySet1 (0), + lcsCapabilitySet2 (1), + lcsCapabilitySet3 (2), + lcsCapabilitySet4 (3) , + lcsCapabilitySet5 (4) } (SIZE (2..16)) +-- Core network signalling capability set1 indicates LCS Release98 or Release99 version. +-- Core network signalling capability set2 indicates LCS Release4. +-- Core network signalling capability set3 indicates LCS Release5. +-- Core network signalling capability set4 indicates LCS Release6. +-- Core network signalling capability set5 indicates LCS Release7 or later version. +-- A node shall mark in the BIT STRING all LCS capability sets it supports. +-- If no bit is set then the sending node does not support LCS. +-- If the parameter is not sent by an VLR then the VLR may support at most capability set1. +-- If the parameter is not sent by an SGSN then no support for LCS is assumed. +-- An SGSN is not allowed to indicate support of capability set1. +-- Other bits than listed above shall be discarded. + +UpdateLocationRes ::= SEQUENCE { + hlr-Number ISDN-AddressString, + extensionContainer ExtensionContainer OPTIONAL, + ..., + add-Capability NULL OPTIONAL, + pagingArea-Capability [0]NULL OPTIONAL } + +ADD-Info ::= SEQUENCE { + imeisv [0] IMEI, + skipSubscriberDataUpdate [1] NULL OPTIONAL, + -- The skipSubscriberDataUpdate parameter in the UpdateLocationArg and the ADD-Info + -- structures carry the same semantic. + ...} + + +PagingArea ::= SEQUENCE SIZE (1..5) OF LocationArea + + +LocationArea ::= CHOICE { + laiFixedLength [0] LAIFixedLength, + lac [1] LAC} + + +LAC ::= OCTET STRING (SIZE (2)) + -- Refers to Location Area Code of the Location Area Identification defined in + -- 3GPP TS 23.003 [17]. + -- Location Area Code according to 3GPP TS 24.008 [35] + +CancelLocationArg ::= [3] SEQUENCE { + identity Identity, + cancellationType CancellationType OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + typeOfUpdate [0] TypeOfUpdate OPTIONAL } + +TypeOfUpdate ::= ENUMERATED { + sgsn-change (0), + mme-change (1), + ...} + -- TypeOfUpdate shall be absent if CancellationType is different from updateProcedure + +CancellationType ::= ENUMERATED { + updateProcedure (0), + subscriptionWithdraw (1), + ...} + -- The HLR shall not send values other than listed above + +CancelLocationRes ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +PurgeMS-Arg ::= [3] SEQUENCE { + imsi IMSI, + vlr-Number [0] ISDN-AddressString OPTIONAL, + sgsn-Number [1] ISDN-AddressString OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +PurgeMS-Res ::= SEQUENCE { + freezeTMSI [0] NULL OPTIONAL, + freezeP-TMSI [1] NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + freezeM-TMSI [2] NULL OPTIONAL } + +SendIdentificationArg ::= SEQUENCE { + tmsi TMSI, + numberOfRequestedVectors NumberOfRequestedVectors OPTIONAL, + -- within a dialogue numberOfRequestedVectors shall be present in + -- the first service request and shall not be present in subsequent service requests. + -- If received in a subsequent service request it shall be discarded. + segmentationProhibited NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + msc-Number ISDN-AddressString OPTIONAL, + previous-LAI [0] LAIFixedLength OPTIONAL, + hopCounter [1] HopCounter OPTIONAL } + +HopCounter ::= INTEGER (0..3) + +SendIdentificationRes ::= [3] SEQUENCE { + imsi IMSI OPTIONAL, + -- IMSI shall be present in the first (or only) service response of a dialogue. + -- If multiple service requests are present in a dialogue then IMSI + -- shall not be present in any service response other than the first one. + authenticationSetList AuthenticationSetList OPTIONAL, + currentSecurityContext [2]CurrentSecurityContext OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +-- authentication management types + +AuthenticationSetList ::= CHOICE { + tripletList [0] TripletList, + quintupletList [1] QuintupletList } + +TripletList ::= SEQUENCE SIZE (1..5) OF + AuthenticationTriplet + +QuintupletList ::= SEQUENCE SIZE (1..5) OF + AuthenticationQuintuplet + +AuthenticationTriplet ::= SEQUENCE { + rand RAND, + sres SRES, + kc Kc, + ...} + +AuthenticationQuintuplet ::= SEQUENCE { + rand RAND, + xres XRES, + ck CK, + ik IK, + autn AUTN, + ...} + +CurrentSecurityContext ::= CHOICE { + gsm-SecurityContextData [0] GSM-SecurityContextData, + umts-SecurityContextData [1] UMTS-SecurityContextData } + +GSM-SecurityContextData ::= SEQUENCE { + kc Kc, + cksn Cksn, + ... } + +UMTS-SecurityContextData ::= SEQUENCE { + ck CK, + ik IK, + ksi KSI, + ... } + +RAND ::= OCTET STRING (SIZE (16)) + +SRES ::= OCTET STRING (SIZE (4)) + +Kc ::= OCTET STRING (SIZE (8)) + +XRES ::= OCTET STRING (SIZE (4..16)) + +CK ::= OCTET STRING (SIZE (16)) + +IK ::= OCTET STRING (SIZE (16)) + +AUTN ::= OCTET STRING (SIZE (16)) + +AUTS ::= OCTET STRING (SIZE (14)) + +Cksn ::= OCTET STRING (SIZE (1)) + -- The internal structure is defined in 3GPP TS 24.008 + +KSI ::= OCTET STRING (SIZE (1)) + -- The internal structure is defined in 3GPP TS 24.008 + +AuthenticationFailureReportArg ::= SEQUENCE { + imsi IMSI, + failureCause FailureCause, + extensionContainer ExtensionContainer OPTIONAL, + ... , + re-attempt BOOLEAN OPTIONAL, + accessType AccessType OPTIONAL, + rand RAND OPTIONAL, + vlr-Number [0] ISDN-AddressString OPTIONAL, + sgsn-Number [1] ISDN-AddressString OPTIONAL } + +AccessType ::= ENUMERATED { + call (0), + emergencyCall (1), + locationUpdating (2), + supplementaryService (3), + shortMessage (4), + gprsAttach (5), + routingAreaUpdating (6), + serviceRequest (7), + pdpContextActivation (8), + pdpContextDeactivation (9), + ..., + gprsDetach (10)} + -- exception handling: + -- received values greater than 10 shall be ignored. + +AuthenticationFailureReportRes ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +FailureCause ::= ENUMERATED { + wrongUserResponse (0), + wrongNetworkSignature (1)} + +-- gprs location registration types + +UpdateGprsLocationArg ::= SEQUENCE { + imsi IMSI, + sgsn-Number ISDN-AddressString, + sgsn-Address GSN-Address, + extensionContainer ExtensionContainer OPTIONAL, + ... , + sgsn-Capability [0] SGSN-Capability OPTIONAL, + informPreviousNetworkEntity [1] NULL OPTIONAL, + ps-LCS-NotSupportedByUE [2] NULL OPTIONAL, + v-gmlc-Address [3] GSN-Address OPTIONAL, + add-info [4] ADD-Info OPTIONAL, + eps-info [5] EPS-Info OPTIONAL, + servingNodeTypeIndicator [6] NULL OPTIONAL, + skipSubscriberDataUpdate [7] NULL OPTIONAL, + usedRAT-Type [8] Used-RAT-Type OPTIONAL + } + +Used-RAT-Type::= ENUMERATED { + utran (0), + geran (1), + gan (2), + i-hspa-evolution (3), + e-utran (4), + ...} + +EPS-Info ::= CHOICE{ + pdn-gw-update [0] PDN-GW-Update, + isr-Information [1] ISR-Information } + +PDN-GW-Update ::= SEQUENCE{ + apn [0] APN OPTIONAL, + pdn-gw-Identity [1] PDN-GW-Identity OPTIONAL, + contextId [2] ContextId OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ... } + +ISR-Information::= BIT STRING { + updateMME (0), + cancelSGSN (1)} (SIZE (2..8)) + -- exception handling: reception of unknown bit assignments in the + -- ISR-Information data type shall be discarded by the receiver + +SGSN-Capability ::= SEQUENCE{ + solsaSupportIndicator NULL OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ... , + superChargerSupportedInServingNetworkEntity [2] SuperChargerInfo OPTIONAL , + gprsEnhancementsSupportIndicator [3] NULL OPTIONAL, + supportedCamelPhases [4] SupportedCamelPhases OPTIONAL, + supportedLCS-CapabilitySets [5] SupportedLCS-CapabilitySets OPTIONAL, + offeredCamel4CSIs [6] OfferedCamel4CSIs OPTIONAL, + smsCallBarringSupportIndicator [7] NULL OPTIONAL, supportedRAT-TypesIndicator [8] SupportedRAT-Types OPTIONAL, + supportedFeatures [9] SupportedFeatures OPTIONAL } + +SupportedFeatures::= BIT STRING { + odb-all (0), + odb-HPLMN-APN (1), + odb-VPLMN-APN (2), + regSub (3)} (SIZE (4..8)) + +GSN-Address ::= OCTET STRING (SIZE (5..17)) + -- Octets are coded according to TS 3GPP TS 23.003 [17] + +UpdateGprsLocationRes ::= SEQUENCE { + hlr-Number ISDN-AddressString, + extensionContainer ExtensionContainer OPTIONAL, + ..., + add-Capability NULL OPTIONAL, + sgsn-mmeSeparationSupported [0] NULL OPTIONAL } + +-- handover types + +ForwardAccessSignalling-Arg ::= [3] SEQUENCE { + an-APDU AccessNetworkSignalInfo, + integrityProtectionInfo [0] IntegrityProtectionInformation OPTIONAL, + encryptionInfo [1] EncryptionInformation OPTIONAL, + keyStatus [2] KeyStatus OPTIONAL, + allowedGSM-Algorithms [4] AllowedGSM-Algorithms OPTIONAL, + allowedUMTS-Algorithms [5] AllowedUMTS-Algorithms OPTIONAL, + radioResourceInformation [6] RadioResourceInformation OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ..., + radioResourceList [7] RadioResourceList OPTIONAL, + bssmap-ServiceHandover [9] BSSMAP-ServiceHandover OPTIONAL, + ranap-ServiceHandover [8] RANAP-ServiceHandover OPTIONAL, + bssmap-ServiceHandoverList [10] BSSMAP-ServiceHandoverList OPTIONAL, + currentlyUsedCodec [11] Codec OPTIONAL, + iuSupportedCodecsList [12] SupportedCodecsList OPTIONAL, + rab-ConfigurationIndicator [13] NULL OPTIONAL, + iuSelectedCodec [14] Codec OPTIONAL, + alternativeChannelType [15] RadioResourceInformation OPTIONAL, + tracePropagationList [17] TracePropagationList OPTIONAL } + +AllowedGSM-Algorithms ::= OCTET STRING (SIZE (1)) + -- internal structure is coded as Algorithm identifier octet from + -- Permitted Algorithms defined in 3GPP TS 48.008 + -- A node shall mark all GSM algorithms that are allowed in MSC-B + +AllowedUMTS-Algorithms ::= SEQUENCE { + integrityProtectionAlgorithms [0] PermittedIntegrityProtectionAlgorithms OPTIONAL, + encryptionAlgorithms [1] PermittedEncryptionAlgorithms OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +PermittedIntegrityProtectionAlgorithms ::= + OCTET STRING (SIZE (1..maxPermittedIntegrityProtectionAlgorithmsLength)) + -- Octets contain a complete PermittedIntegrityProtectionAlgorithms data type + -- as defined in 3GPP TS 25.413, encoded according to the encoding scheme + -- mandated by 3GPP TS 25.413. + -- Padding bits are included, if needed, in the least significant bits of the + -- last octet of the octet string. + + +maxPermittedIntegrityProtectionAlgorithmsLength INTEGER ::= 9 + +PermittedEncryptionAlgorithms ::= + OCTET STRING (SIZE (1..maxPermittedEncryptionAlgorithmsLength)) + -- Octets contain a complete PermittedEncryptionAlgorithms data type + -- as defined in 3GPP TS 25.413, encoded according to the encoding scheme + -- mandated by 3GPP TS 25.413 + -- Padding bits are included, if needed, in the least significant bits of the + -- last octet of the octet string. + + +maxPermittedEncryptionAlgorithmsLength INTEGER ::= 9 + +KeyStatus ::= ENUMERATED { + old (0), + new (1), + ...} + -- exception handling: + -- received values in range 2-31 shall be treated as "old" + -- received values greater than 31 shall be treated as "new" + +PrepareHO-Arg ::= [3] SEQUENCE { + targetCellId [0] GlobalCellId OPTIONAL, + ho-NumberNotRequired NULL OPTIONAL, + targetRNCId [1] RNCId OPTIONAL, + an-APDU [2] AccessNetworkSignalInfo OPTIONAL, + multipleBearerRequested [3] NULL OPTIONAL, + imsi [4] IMSI OPTIONAL, + integrityProtectionInfo [5] IntegrityProtectionInformation OPTIONAL, + encryptionInfo [6] EncryptionInformation OPTIONAL, + radioResourceInformation [7] RadioResourceInformation OPTIONAL, + allowedGSM-Algorithms [9] AllowedGSM-Algorithms OPTIONAL, + allowedUMTS-Algorithms [10] AllowedUMTS-Algorithms OPTIONAL, + radioResourceList [11] RadioResourceList OPTIONAL, + extensionContainer [8] ExtensionContainer OPTIONAL, + ... , + rab-Id [12] RAB-Id OPTIONAL, + bssmap-ServiceHandover [13] BSSMAP-ServiceHandover OPTIONAL, + ranap-ServiceHandover [14] RANAP-ServiceHandover OPTIONAL, + bssmap-ServiceHandoverList [15] BSSMAP-ServiceHandoverList OPTIONAL, + asciCallReference [20] ASCI-CallReference OPTIONAL, + geran-classmark [16] GERAN-Classmark OPTIONAL, + iuCurrentlyUsedCodec [17] Codec OPTIONAL, + iuSupportedCodecsList [18] SupportedCodecsList OPTIONAL, + rab-ConfigurationIndicator [19] NULL OPTIONAL, + uesbi-Iu [21] UESBI-Iu OPTIONAL, + imeisv [22] IMEI OPTIONAL, + alternativeChannelType [23] RadioResourceInformation OPTIONAL, + tracePropagationList [25] TracePropagationList OPTIONAL } + +BSSMAP-ServiceHandoverList ::= SEQUENCE SIZE (1.. maxNumOfServiceHandovers) OF + BSSMAP-ServiceHandoverInfo + +BSSMAP-ServiceHandoverInfo ::= SEQUENCE { + bssmap-ServiceHandover BSSMAP-ServiceHandover, + rab-Id RAB-Id, + -- RAB Identity is needed to relate the service handovers with the radio access bearers. + ...} + +maxNumOfServiceHandovers INTEGER ::= 7 + +BSSMAP-ServiceHandover ::= OCTET STRING (SIZE (1)) + -- Octets are coded according the Service Handover information element in + -- 3GPP TS 48.008. + +RANAP-ServiceHandover ::= OCTET STRING (SIZE (1)) + -- Octet contains a complete Service-Handover data type + -- as defined in 3GPP TS 25.413, encoded according to the encoding scheme + -- mandated by 3GPP TS 25.413 + -- Padding bits are included in the least significant bits. + + +RadioResourceList ::= SEQUENCE SIZE (1.. maxNumOfRadioResources) OF + RadioResource + +RadioResource ::= SEQUENCE { + radioResourceInformation RadioResourceInformation, + rab-Id RAB-Id, + -- RAB Identity is needed to relate the radio resources with the radio access bearers. + ...} + +maxNumOfRadioResources INTEGER ::= 7 + +PrepareHO-Res ::= [3] SEQUENCE { + handoverNumber [0] ISDN-AddressString OPTIONAL, + relocationNumberList [1] RelocationNumberList OPTIONAL, + an-APDU [2] AccessNetworkSignalInfo OPTIONAL, + multicallBearerInfo [3] MulticallBearerInfo OPTIONAL, + multipleBearerNotSupported NULL OPTIONAL, + selectedUMTS-Algorithms [5] SelectedUMTS-Algorithms OPTIONAL, + chosenRadioResourceInformation [6] ChosenRadioResourceInformation OPTIONAL, + extensionContainer [4] ExtensionContainer OPTIONAL, + ..., + iuSelectedCodec [7] Codec OPTIONAL, + iuAvailableCodecsList [8] CodecList OPTIONAL } + +SelectedUMTS-Algorithms ::= SEQUENCE { + integrityProtectionAlgorithm [0] ChosenIntegrityProtectionAlgorithm OPTIONAL, + encryptionAlgorithm [1] ChosenEncryptionAlgorithm OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +ChosenIntegrityProtectionAlgorithm ::= OCTET STRING (SIZE (1)) + -- Octet contains a complete IntegrityProtectionAlgorithm data type + -- as defined in 3GPP TS 25.413, encoded according to the encoding scheme + -- mandated by 3GPP TS 25.413 + -- Padding bits are included in the least significant bits. + +ChosenEncryptionAlgorithm ::= OCTET STRING (SIZE (1)) + -- Octet contains a complete EncryptionAlgorithm data type + -- as defined in 3GPP TS 25.413, encoded according to the encoding scheme + -- mandated by 3GPP TS 25.413 + -- Padding bits are included in the least significant bits. + +ChosenRadioResourceInformation ::= SEQUENCE { + chosenChannelInfo [0] ChosenChannelInfo OPTIONAL, + chosenSpeechVersion [1] ChosenSpeechVersion OPTIONAL, + ...} + +ChosenChannelInfo ::= OCTET STRING (SIZE (1)) + -- Octets are coded according the Chosen Channel information element in 3GPP TS 48.008 + +ChosenSpeechVersion ::= OCTET STRING (SIZE (1)) + -- Octets are coded according the Speech Version (chosen) information element in 3GPP TS + -- 48.008 + +PrepareSubsequentHO-Arg ::= [3] SEQUENCE { + targetCellId [0] GlobalCellId OPTIONAL, + targetMSC-Number [1] ISDN-AddressString, + targetRNCId [2] RNCId OPTIONAL, + an-APDU [3] AccessNetworkSignalInfo OPTIONAL, + selectedRab-Id [4] RAB-Id OPTIONAL, + extensionContainer [5] ExtensionContainer OPTIONAL, + ..., + geran-classmark [6] GERAN-Classmark OPTIONAL, + rab-ConfigurationIndicator [7] NULL OPTIONAL } + +PrepareSubsequentHO-Res ::= [3] SEQUENCE { + an-APDU AccessNetworkSignalInfo, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +ProcessAccessSignalling-Arg ::= [3] SEQUENCE { + an-APDU AccessNetworkSignalInfo, + selectedUMTS-Algorithms [1] SelectedUMTS-Algorithms OPTIONAL, + selectedGSM-Algorithm [2] SelectedGSM-Algorithm OPTIONAL, + chosenRadioResourceInformation [3] ChosenRadioResourceInformation OPTIONAL, + selectedRab-Id [4] RAB-Id OPTIONAL, + extensionContainer [0] ExtensionContainer OPTIONAL, + ..., + iUSelectedCodec [5] Codec OPTIONAL, + iuAvailableCodecsList [6] CodecList OPTIONAL } + +SupportedCodecsList ::= SEQUENCE { + utranCodecList [0] CodecList OPTIONAL, + geranCodecList [1] CodecList OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +CodecList ::= SEQUENCE { + codec1 [1] Codec, + codec2 [2] Codec OPTIONAL, + codec3 [3] Codec OPTIONAL, + codec4 [4] Codec OPTIONAL, + codec5 [5] Codec OPTIONAL, + codec6 [6] Codec OPTIONAL, + codec7 [7] Codec OPTIONAL, + codec8 [8] Codec OPTIONAL, + extensionContainer [9] ExtensionContainer OPTIONAL, + ...} + -- Codecs are sent in priority order where codec1 has highest priority + +Codec ::= OCTET STRING (SIZE (1..4)) + + -- The internal structure is defined as follows: + -- octet 1 Coded as Codec Identification code in 3GPP TS 26.103 + -- octets 2,3,4 Parameters for the Codec as defined in 3GPP TS + -- 26.103, if available, length depending on the codec + +GERAN-Classmark ::= OCTET STRING (SIZE (2..87)) + -- Octets are coded according the GERAN Classmark information element in 3GPP TS 48.008 + +SelectedGSM-Algorithm ::= OCTET STRING (SIZE (1)) + -- internal structure is coded as Algorithm identifier octet from Chosen Encryption + -- Algorithm defined in 3GPP TS 48.008 + -- A node shall mark only the selected GSM algorithm + +SendEndSignal-Arg ::= [3] SEQUENCE { + an-APDU AccessNetworkSignalInfo, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +SendEndSignal-Res ::= SEQUENCE { + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +RNCId ::= OCTET STRING (SIZE (7)) + -- The internal structure is defined as follows: + -- octet 1 bits 4321 Mobile Country Code 1st digit + -- bits 8765 Mobile Country Code 2nd digit + -- octet 2 bits 4321 Mobile Country Code 3rd digit + -- bits 8765 Mobile Network Code 3rd digit + -- or filler (1111) for 2 digit MNCs + -- octet 3 bits 4321 Mobile Network Code 1st digit + -- bits 8765 Mobile Network Code 2nd digit + -- octets 4 and 5 Location Area Code according to 3GPP TS 24.008 + -- octets 6 and 7 RNC Id value according to 3GPP TS 25.413 + +RelocationNumberList ::= SEQUENCE SIZE (1..maxNumOfRelocationNumber) OF + RelocationNumber + +MulticallBearerInfo ::= INTEGER (1..maxNumOfRelocationNumber) + +RelocationNumber ::= SEQUENCE { + handoverNumber ISDN-AddressString, + rab-Id RAB-Id, + -- RAB Identity is needed to relate the calls with the radio access bearers. + ...} + +RAB-Id ::= INTEGER (1..maxNrOfRABs) + +maxNrOfRABs INTEGER ::= 255 + +maxNumOfRelocationNumber INTEGER ::= 7 + +RadioResourceInformation ::= OCTET STRING (SIZE (3..13)) + -- Octets are coded according the Channel Type information element in 3GPP TS 48.008 + +IntegrityProtectionInformation ::= OCTET STRING (SIZE (18..maxNumOfIntegrityInfo)) + -- Octets contain a complete IntegrityProtectionInformation data type + -- as defined in 3GPP TS 25.413, encoded according to the encoding scheme + -- mandated by 3GPP TS 25.413 + -- Padding bits are included, if needed, in the least significant bits of the + -- last octet of the octet string. + +maxNumOfIntegrityInfo INTEGER ::= 100 + +EncryptionInformation ::= OCTET STRING (SIZE (18..maxNumOfEncryptionInfo)) + -- Octets contain a complete EncryptionInformation data type + -- as defined in 3GPP TS 25.413, encoded according to the encoding scheme + -- mandated by 3GPP TS 25.413 + -- Padding bits are included, if needed, in the least significant bits of the + -- last octet of the octet string. + +maxNumOfEncryptionInfo INTEGER ::= 100 + +-- authentication management types + +SendAuthenticationInfoArg ::= SEQUENCE { + imsi [0] IMSI, + numberOfRequestedVectors NumberOfRequestedVectors, + segmentationProhibited NULL OPTIONAL, + immediateResponsePreferred [1] NULL OPTIONAL, + re-synchronisationInfo Re-synchronisationInfo OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ..., + requestingNodeType [3] RequestingNodeType OPTIONAL, + requestingPLMN-Id [4] PLMN-Id OPTIONAL, + numberOfRequestedAdditional-Vectors [5] NumberOfRequestedVectors OPTIONAL, + additionalVectorsAreForEPS [6] NULL OPTIONAL } + + +NumberOfRequestedVectors ::= INTEGER (1..5) + +Re-synchronisationInfo ::= SEQUENCE { + rand RAND, + auts AUTS, + ...} + +SendAuthenticationInfoRes ::= [3] SEQUENCE { + authenticationSetList AuthenticationSetList OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + eps-AuthenticationSetList [2] EPS-AuthenticationSetList OPTIONAL } + +EPS-AuthenticationSetList ::= SEQUENCE SIZE (1..5) OF + EPC-AV + +EPC-AV ::= SEQUENCE { + rand RAND, + xres XRES, + autn AUTN, + kasme KASME, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +KASME ::= OCTET STRING (SIZE (16)) + +RequestingNodeType ::= ENUMERATED { + vlr (0), + sgsn (1), + ..., + s-cscf (2), + bsf (3), + gan-aaa-server (4), + wlan-aaa-server (5), + mme (16), + mme-sgsn (17) + } + -- the values 2, 3, 4 and 5 shall not be used on the MAP-D or Gr interfaces + -- exception handling: + -- received values in the range (6-15) shall be treated as "vlr" + -- received values greater than 17 shall be treated as "sgsn" + +-- equipment management types + +CheckIMEI-Arg ::= SEQUENCE { + imei IMEI, + requestedEquipmentInfo RequestedEquipmentInfo, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +CheckIMEI-Res ::= SEQUENCE { + equipmentStatus EquipmentStatus OPTIONAL, + bmuef UESBI-Iu OPTIONAL, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +RequestedEquipmentInfo::= BIT STRING { + equipmentStatus (0), + bmuef (1)} (SIZE (2..8)) + -- exception handling: reception of unknown bit assignments in the + -- RequestedEquipmentInfo data type shall be discarded by the receiver + +UESBI-Iu ::= SEQUENCE { + uesbi-IuA [0] UESBI-IuA OPTIONAL, + uesbi-IuB [1] UESBI-IuB OPTIONAL, + ...} + +UESBI-IuA ::= BIT STRING (SIZE(1..128)) +-- See 3GPP TS 25.413 + +UESBI-IuB ::= BIT STRING (SIZE(1..128)) +-- See 3GPP TS 25.413 + +EquipmentStatus ::= ENUMERATED { + whiteListed (0), + blackListed (1), + greyListed (2)} + +-- subscriber management types + +InsertSubscriberDataArg ::= SEQUENCE { + imsi [0] IMSI OPTIONAL, + COMPONENTS OF SubscriberData, + extensionContainer [14] ExtensionContainer OPTIONAL, + ... , + naea-PreferredCI [15] NAEA-PreferredCI OPTIONAL, + -- naea-PreferredCI is included at the discretion of the HLR operator. + gprsSubscriptionData [16] GPRSSubscriptionData OPTIONAL, + roamingRestrictedInSgsnDueToUnsupportedFeature [23] NULL + OPTIONAL, + networkAccessMode [24] NetworkAccessMode OPTIONAL, + lsaInformation [25] LSAInformation OPTIONAL, + lmu-Indicator [21] NULL OPTIONAL, + lcsInformation [22] LCSInformation OPTIONAL, + istAlertTimer [26] IST-AlertTimerValue OPTIONAL, + superChargerSupportedInHLR [27] AgeIndicator OPTIONAL, + mc-SS-Info [28] MC-SS-Info OPTIONAL, + cs-AllocationRetentionPriority [29] CS-AllocationRetentionPriority OPTIONAL, + sgsn-CAMEL-SubscriptionInfo [17] SGSN-CAMEL-SubscriptionInfo OPTIONAL, + chargingCharacteristics [18] ChargingCharacteristics OPTIONAL, + accessRestrictionData [19] AccessRestrictionData OPTIONAL, + ics-Indicator [20] BOOLEAN OPTIONAL, + eps-SubscriptionData [31] EPS-SubscriptionData OPTIONAL, + csg-SubscriptionDataList [32] CSG-SubscriptionDataList OPTIONAL } + -- If the Network Access Mode parameter is sent, it shall be present only in + -- the first sequence if seqmentation is used + +CSG-SubscriptionDataList ::= SEQUENCE SIZE (1..50) OF + CSG-SubscriptionData + +CSG-SubscriptionData ::= SEQUENCE { + csg-Id CSG-Id, + expirationDate Time OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +CSG-Id ::= BIT STRING (SIZE (27)) + -- coded according to 3GPP TS 23.003 [17]. + +Time ::= OCTET STRING (SIZE (4)) + -- Octets are coded according to IETF RFC 3588 [139] + + +EPS-SubscriptionData ::= SEQUENCE { + apn-oi-Replacement [0] APN-OI-Replacement OPTIONAL, + rfsp-id [2] RFSP-ID OPTIONAL, + ambr [3] AMBR OPTIONAL, + apn-ConfigurationProfile [4] APN-ConfigurationProfile OPTIONAL, + stn-sr [6] ISDN-AddressString OPTIONAL, + extensionContainer [5] ExtensionContainer OPTIONAL, + ... } + +APN-OI-Replacement ::= OCTET STRING (SIZE (9..100)) + -- Octets are coded as APN Operator Identifier according to TS 3GPP TS 23.003 [17] + +RFSP-ID ::= INTEGER (1..256) + +APN-ConfigurationProfile ::= SEQUENCE { + defaultContext ContextId, + completeDataListIncluded NULL OPTIONAL, + -- If segmentation is used, completeDataListIncluded may only be present in the + -- first segment of APN-ConfigurationProfile. + epsDataList [1] EPS-DataList, + extensionContainer [2] ExtensionContainer OPTIONAL, + ... } + +EPS-DataList ::= SEQUENCE SIZE (1..maxNumOfAPN-Configurations) OF + APN-Configuration + + +maxNumOfAPN-Configurations INTEGER ::= 50 + + +APN-Configuration ::= SEQUENCE { + contextId [0] ContextId, + servedPartyIP-Address [1] PDP-Address OPTIONAL, + apn [2] APN, + eps-qos-Subscribed [3] EPS-QoS-Subscribed, + pdn-gw-Identity [4] PDN-GW-Identity OPTIONAL, + pdn-gw-AllocationType [5] PDN-GW-AllocationType OPTIONAL, + vplmnAddressAllowed [6] NULL OPTIONAL, + chargingCharacteristics [7] ChargingCharacteristics OPTIONAL, + ambr [8] AMBR OPTIONAL, + specificAPNInfoList [9] SpecificAPNInfoList OPTIONAL, extensionContainer [10] ExtensionContainer OPTIONAL, + ... } + +EPS-QoS-Subscribed ::= SEQUENCE { + qos-Class-Identifier [0] QoS-Class-Identifier, + allocation-Retention-Priority [1] Allocation-Retention-Priority, + extensionContainer [2] ExtensionContainer OPTIONAL, + ... } + +AMBR ::= SEQUENCE { + max-RequestedBandwidth-UL [0] Bandwidth, + max-RequestedBandwidth-DL [1] Bandwidth, + extensionContainer [2] ExtensionContainer OPTIONAL, + ... } + + +SpecificAPNInfoList ::= SEQUENCE SIZE (1..maxNumOfSpecificAPNInfos) OF + SpecificAPNInfo + +maxNumOfSpecificAPNInfos INTEGER ::= 50 + +SpecificAPNInfo ::= SEQUENCE { + apn [0] APN, + pdn-gw-Identity [1] PDN-GW-Identity, + extensionContainer [2] ExtensionContainer OPTIONAL, + ... } + +Bandwidth ::= INTEGER + -- bits per second + +QoS-Class-Identifier ::= INTEGER (1..9) + -- values are defined in 3GPP TS 29.212 + + + +Allocation-Retention-Priority ::= SEQUENCE { + priority-level [0] INTEGER, + pre-emption-capability [1] BOOLEAN OPTIONAL, + pre-emption-vulnerability [2] BOOLEAN OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ... } + +PDN-GW-Identity ::= SEQUENCE { + pdn-gw-ipv4-Address [0] PDP-Address OPTIONAL, + pdn-gw-ipv6-Address [1] PDP-Address OPTIONAL, + pdn-gw-name [2] FQDN OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ... } + +FQDN ::= OCTET STRING (SIZE (9..100)) + + +PDN-GW-AllocationType ::= ENUMERATED { + static (0), + dynamic (1)} + + +AccessRestrictionData ::= BIT STRING { + utranNotAllowed (0), + geranNotAllowed (1), + ganNotAllowed (2), + i-hspa-evolutionNotAllowed (3), + e-utranNotAllowed (4), + ho-toNon3GPP-AccessNotAllowed (5) } (SIZE (2..8)) + -- exception handling: + -- access restriction data related to an access type not supported by a node + -- shall be ignored + -- bits 6 to 7 shall be ignored if received and not understood + + +CS-AllocationRetentionPriority ::= OCTET STRING (SIZE (1)) + -- This data type encodes each priority level defined in TS 23.107 as the binary value + -- of the priority level. + +IST-AlertTimerValue ::= INTEGER (15..255) + +LCSInformation ::= SEQUENCE { + gmlc-List [0] GMLC-List OPTIONAL, + lcs-PrivacyExceptionList [1] LCS-PrivacyExceptionList OPTIONAL, + molr-List [2] MOLR-List OPTIONAL, + ..., + add-lcs-PrivacyExceptionList [3] LCS-PrivacyExceptionList OPTIONAL } + -- add-lcs-PrivacyExceptionList may be sent only if lcs-PrivacyExceptionList is + -- present and contains four instances of LCS-PrivacyClass. If the mentioned condition + -- is not satisfied the receiving node shall discard add-lcs-PrivacyExceptionList. + -- If an LCS-PrivacyClass is received both in lcs-PrivacyExceptionList and in + -- add-lcs-PrivacyExceptionList with the same SS-Code, then the error unexpected + -- data value shall be returned. + +GMLC-List ::= SEQUENCE SIZE (1..maxNumOfGMLC) OF + ISDN-AddressString + -- if segmentation is used, the complete GMLC-List shall be sent in one segment + +maxNumOfGMLC INTEGER ::= 5 + +NetworkAccessMode ::= ENUMERATED { + packetAndCircuit (0), + onlyCircuit (1), + onlyPacket (2), + ...} + -- if unknown values are received in NetworkAccessMode + -- they shall be discarded. + +GPRSDataList ::= SEQUENCE SIZE (1..maxNumOfPDP-Contexts) OF + PDP-Context + +maxNumOfPDP-Contexts INTEGER ::= 50 + +PDP-Context ::= SEQUENCE { + pdp-ContextId ContextId, + pdp-Type [16] PDP-Type, + pdp-Address [17] PDP-Address OPTIONAL, + qos-Subscribed [18] QoS-Subscribed, + vplmnAddressAllowed [19] NULL OPTIONAL, + apn [20] APN, + extensionContainer [21] ExtensionContainer OPTIONAL, + ... , + ext-QoS-Subscribed [0] Ext-QoS-Subscribed OPTIONAL, + pdp-ChargingCharacteristics [1] ChargingCharacteristics OPTIONAL, + ext2-QoS-Subscribed [2] Ext2-QoS-Subscribed OPTIONAL, + -- ext2-QoS-Subscribed may be present only if ext-QoS-Subscribed is present. + ext3-QoS-Subscribed [3] Ext3-QoS-Subscribed OPTIONAL + -- ext3-QoS-Subscribed may be present only if ext2-QoS-Subscribed is present. + } + +ContextId ::= INTEGER (1..maxNumOfPDP-Contexts) + +GPRSSubscriptionData ::= SEQUENCE { + completeDataListIncluded NULL OPTIONAL, + -- If segmentation is used, completeDataListIncluded may only be present in the + -- first segment of GPRSSubscriptionData. + gprsDataList [1] GPRSDataList, + extensionContainer [2] ExtensionContainer OPTIONAL, + ... } + +SGSN-CAMEL-SubscriptionInfo ::= SEQUENCE { + gprs-CSI [0] GPRS-CSI OPTIONAL, + mo-sms-CSI [1] SMS-CSI OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ..., + mt-sms-CSI [3] SMS-CSI OPTIONAL, + mt-smsCAMELTDP-CriteriaList [4] MT-smsCAMELTDP-CriteriaList OPTIONAL, + mg-csi [5] MG-CSI OPTIONAL + } + +GPRS-CSI ::= SEQUENCE { + gprs-CamelTDPDataList [0] GPRS-CamelTDPDataList OPTIONAL, + camelCapabilityHandling [1] CamelCapabilityHandling OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + notificationToCSE [3] NULL OPTIONAL, + csi-Active [4] NULL OPTIONAL, + ...} +-- notificationToCSE and csi-Active shall not be present when GPRS-CSI is sent to SGSN. +-- They may only be included in ATSI/ATM ack/NSDC message. +-- GPRS-CamelTDPData and camelCapabilityHandling shall be present in +-- the GPRS-CSI sequence. +-- If GPRS-CSI is segmented, gprs-CamelTDPDataList and camelCapabilityHandling shall be +-- present in the first segment + +GPRS-CamelTDPDataList ::= SEQUENCE SIZE (1..maxNumOfCamelTDPData) OF + GPRS-CamelTDPData +-- GPRS-CamelTDPDataList shall not contain more than one instance of +-- GPRS-CamelTDPData containing the same value for gprs-TriggerDetectionPoint. + +GPRS-CamelTDPData ::= SEQUENCE { + gprs-TriggerDetectionPoint [0] GPRS-TriggerDetectionPoint, + serviceKey [1] ServiceKey, + gsmSCF-Address [2] ISDN-AddressString, + defaultSessionHandling [3] DefaultGPRS-Handling, + extensionContainer [4] ExtensionContainer OPTIONAL, + ... + } + +DefaultGPRS-Handling ::= ENUMERATED { + continueTransaction (0) , + releaseTransaction (1) , + ...} +-- exception handling: +-- reception of values in range 2-31 shall be treated as "continueTransaction" +-- reception of values greater than 31 shall be treated as "releaseTransaction" + +GPRS-TriggerDetectionPoint ::= ENUMERATED { + attach (1), + attachChangeOfPosition (2), + pdp-ContextEstablishment (11), + pdp-ContextEstablishmentAcknowledgement (12), + pdp-ContextChangeOfPosition (14), + ... } +-- exception handling: +-- For GPRS-CamelTDPData sequences containing this parameter with any +-- other value than the ones listed the receiver shall ignore the whole +-- GPRS-CamelTDPDatasequence. + +APN ::= OCTET STRING (SIZE (2..63)) + -- Octets are coded according to TS 3GPP TS 23.003 [17] + +PDP-Type ::= OCTET STRING (SIZE (2)) + -- Octets are coded according to TS 3GPP TS 29.060 [105] + +PDP-Address ::= OCTET STRING (SIZE (1..16)) + -- Octets are coded according to TS 3GPP TS 29.060 [105] + + -- The possible size values are: + -- 1-7 octets X.25 address type + -- 4 octets IPv4 address type + -- 16 octets Ipv6 address type + +QoS-Subscribed ::= OCTET STRING (SIZE (3)) + -- Octets are coded according to TS 3GPP TS 24.008 [35] Quality of Service Octets + -- 3-5. + +Ext-QoS-Subscribed ::= OCTET STRING (SIZE (1..9)) + -- OCTET 1: + -- Allocation/Retention Priority (This octet encodes each priority level defined in + -- 23.107 as the binary value of the priority level, declaration in 29.060) + -- Octets 2-9 are coded according to 3GPP TS 24.008 [35] Quality of Service Octets + -- 6-13. + +Ext2-QoS-Subscribed ::= OCTET STRING (SIZE (1..3)) + -- Octets 1-3 are coded according to 3GPP TS 24.008 [35] Quality of Service Octets 14-16. + -- If Quality of Service information is structured with 14 octet length, then + -- Octet 1 is coded according to 3GPP TS 24.008 [35] Quality of Service Octet 14. + +Ext3-QoS-Subscribed ::= OCTET STRING (SIZE (1..2)) + -- Octets 1-2 are coded according to 3GPP TS 24.008 [35] Quality of Service Octets 17-18. + +ChargingCharacteristics ::= OCTET STRING (SIZE (2)) + -- Octets are coded according to 3GPP TS 32.215. + +LSAOnlyAccessIndicator ::= ENUMERATED { + accessOutsideLSAsAllowed (0), + accessOutsideLSAsRestricted (1)} + +LSADataList ::= SEQUENCE SIZE (1..maxNumOfLSAs) OF + LSAData + +maxNumOfLSAs INTEGER ::= 20 + +LSAData ::= SEQUENCE { + lsaIdentity [0] LSAIdentity, + lsaAttributes [1] LSAAttributes, + lsaActiveModeIndicator [2] NULL OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +LSAInformation ::= SEQUENCE { + completeDataListIncluded NULL OPTIONAL, + + -- If segmentation is used, completeDataListIncluded may only be present in the + -- first segment. + lsaOnlyAccessIndicator [1] LSAOnlyAccessIndicator OPTIONAL, + lsaDataList [2] LSADataList OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +LSAIdentity ::= OCTET STRING (SIZE (3)) + -- Octets are coded according to TS 3GPP TS 23.003 [17] + +LSAAttributes ::= OCTET STRING (SIZE (1)) + -- Octets are coded according to TS 3GPP TS 48.008 [49] + +SubscriberData ::= SEQUENCE { + msisdn [1] ISDN-AddressString OPTIONAL, + category [2] Category OPTIONAL, + subscriberStatus [3] SubscriberStatus OPTIONAL, + bearerServiceList [4] BearerServiceList OPTIONAL, + -- The exception handling for reception of unsupported / not allocated + -- bearerServiceCodes is defined in section 8.8.1 + teleserviceList [6] TeleserviceList OPTIONAL, + -- The exception handling for reception of unsupported / not allocated + -- teleserviceCodes is defined in section 8.8.1 + provisionedSS [7] Ext-SS-InfoList OPTIONAL, + odb-Data [8] ODB-Data OPTIONAL, + roamingRestrictionDueToUnsupportedFeature [9] NULL OPTIONAL, + regionalSubscriptionData [10] ZoneCodeList OPTIONAL, + vbsSubscriptionData [11] VBSDataList OPTIONAL, + vgcsSubscriptionData [12] VGCSDataList OPTIONAL, + vlrCamelSubscriptionInfo [13] VlrCamelSubscriptionInfo OPTIONAL + } + +Category ::= OCTET STRING (SIZE (1)) + -- The internal structure is defined in ITU-T Rec Q.763. + +SubscriberStatus ::= ENUMERATED { + serviceGranted (0), + operatorDeterminedBarring (1)} + +BearerServiceList ::= SEQUENCE SIZE (1..maxNumOfBearerServices) OF + Ext-BearerServiceCode + +maxNumOfBearerServices INTEGER ::= 50 + +TeleserviceList ::= SEQUENCE SIZE (1..maxNumOfTeleservices) OF + Ext-TeleserviceCode + +maxNumOfTeleservices INTEGER ::= 20 + +ODB-Data ::= SEQUENCE { + odb-GeneralData ODB-GeneralData, + odb-HPLMN-Data ODB-HPLMN-Data OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +ODB-GeneralData ::= BIT STRING { + allOG-CallsBarred (0), + internationalOGCallsBarred (1), + internationalOGCallsNotToHPLMN-CountryBarred (2), + interzonalOGCallsBarred (6), + interzonalOGCallsNotToHPLMN-CountryBarred (7), + interzonalOGCallsAndInternationalOGCallsNotToHPLMN-CountryBarred (8), + premiumRateInformationOGCallsBarred (3), + premiumRateEntertainementOGCallsBarred (4), + ss-AccessBarred (5), + allECT-Barred (9), + chargeableECT-Barred (10), + internationalECT-Barred (11), + interzonalECT-Barred (12), + doublyChargeableECT-Barred (13), + multipleECT-Barred (14), + allPacketOrientedServicesBarred (15), + roamerAccessToHPLMN-AP-Barred (16), + roamerAccessToVPLMN-AP-Barred (17), + roamingOutsidePLMNOG-CallsBarred (18), + allIC-CallsBarred (19), + roamingOutsidePLMNIC-CallsBarred (20), + roamingOutsidePLMNICountryIC-CallsBarred (21), + roamingOutsidePLMN-Barred (22), + roamingOutsidePLMN-CountryBarred (23), + registrationAllCF-Barred (24), + registrationCFNotToHPLMN-Barred (25), + registrationInterzonalCF-Barred (26), + registrationInterzonalCFNotToHPLMN-Barred (27), + registrationInternationalCF-Barred (28)} (SIZE (15..32)) + -- exception handling: reception of unknown bit assignments in the + -- ODB-GeneralData type shall be treated like unsupported ODB-GeneralData + -- When the ODB-GeneralData type is removed from the HLR for a given subscriber, + -- in NoteSubscriberDataModified operation sent toward the gsmSCF + -- all bits shall be set to "O". + +ODB-HPLMN-Data ::= BIT STRING { + plmn-SpecificBarringType1 (0), + plmn-SpecificBarringType2 (1), + plmn-SpecificBarringType3 (2), + plmn-SpecificBarringType4 (3)} (SIZE (4..32)) + -- exception handling: reception of unknown bit assignments in the + -- ODB-HPLMN-Data type shall be treated like unsupported ODB-HPLMN-Data + -- When the ODB-HPLMN-Data type is removed from the HLR for a given subscriber, + -- in NoteSubscriberDataModified operation sent toward the gsmSCF + -- all bits shall be set to "O". + +Ext-SS-InfoList ::= SEQUENCE SIZE (1..maxNumOfSS) OF + Ext-SS-Info + +Ext-SS-Info ::= CHOICE { + forwardingInfo [0] Ext-ForwInfo, + callBarringInfo [1] Ext-CallBarInfo, + cug-Info [2] CUG-Info, + ss-Data [3] Ext-SS-Data, + emlpp-Info [4] EMLPP-Info} + +Ext-ForwInfo ::= SEQUENCE { + ss-Code SS-Code, + forwardingFeatureList Ext-ForwFeatureList, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +Ext-ForwFeatureList ::= SEQUENCE SIZE (1..maxNumOfExt-BasicServiceGroups) OF + Ext-ForwFeature + +Ext-ForwFeature ::= SEQUENCE { + basicService Ext-BasicServiceCode OPTIONAL, + ss-Status [4] Ext-SS-Status, + forwardedToNumber [5] ISDN-AddressString OPTIONAL, + -- When this data type is sent from an HLR which supports CAMEL Phase 2 + -- to a VLR that supports CAMEL Phase 2 the VLR shall not check the + -- format of the number + forwardedToSubaddress [8] ISDN-SubaddressString OPTIONAL, + forwardingOptions [6] Ext-ForwOptions OPTIONAL, + noReplyConditionTime [7] Ext-NoRepCondTime OPTIONAL, + extensionContainer [9] ExtensionContainer OPTIONAL, + ..., + longForwardedToNumber [10] FTN-AddressString OPTIONAL } + +Ext-ForwOptions ::= OCTET STRING (SIZE (1..5)) + + -- OCTET 1: + + -- bit 8: notification to forwarding party + -- 0 no notification + -- 1 notification + + -- bit 7: redirecting presentation + -- 0 no presentation + -- 1 presentation + + -- bit 6: notification to calling party + -- 0 no notification + -- 1 notification + + -- bit 5: 0 (unused) + + -- bits 43: forwarding reason + -- 00 ms not reachable + -- 01 ms busy + -- 10 no reply + -- 11 unconditional + + -- bits 21: 00 (unused) + + -- OCTETS 2-5: reserved for future use. They shall be discarded if + -- received and not understood. + +Ext-NoRepCondTime ::= INTEGER (1..100) + -- Only values 5-30 are used. + -- Values in the ranges 1-4 and 31-100 are reserved for future use + -- If received: + -- values 1-4 shall be mapped on to value 5 + -- values 31-100 shall be mapped on to value 30 + +Ext-CallBarInfo ::= SEQUENCE { + ss-Code SS-Code, + callBarringFeatureList Ext-CallBarFeatureList, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +Ext-CallBarFeatureList ::= SEQUENCE SIZE (1..maxNumOfExt-BasicServiceGroups) OF + Ext-CallBarringFeature + +Ext-CallBarringFeature ::= SEQUENCE { + basicService Ext-BasicServiceCode OPTIONAL, + ss-Status [4] Ext-SS-Status, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +CUG-Info ::= SEQUENCE { + cug-SubscriptionList CUG-SubscriptionList, + cug-FeatureList CUG-FeatureList OPTIONAL, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +CUG-SubscriptionList ::= SEQUENCE SIZE (0..maxNumOfCUG) OF + CUG-Subscription + +CUG-Subscription ::= SEQUENCE { + cug-Index CUG-Index, + cug-Interlock CUG-Interlock, + intraCUG-Options IntraCUG-Options, + basicServiceGroupList Ext-BasicServiceGroupList OPTIONAL, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +CUG-Index ::= INTEGER (0..32767) + -- The internal structure is defined in ETS 300 138. + +CUG-Interlock ::= OCTET STRING (SIZE (4)) + +IntraCUG-Options ::= ENUMERATED { + noCUG-Restrictions (0), + cugIC-CallBarred (1), + cugOG-CallBarred (2)} + +maxNumOfCUG INTEGER ::= 10 + +CUG-FeatureList ::= SEQUENCE SIZE (1..maxNumOfExt-BasicServiceGroups) OF + CUG-Feature + +Ext-BasicServiceGroupList ::= SEQUENCE SIZE (1..maxNumOfExt-BasicServiceGroups) OF + Ext-BasicServiceCode + +maxNumOfExt-BasicServiceGroups INTEGER ::= 32 + +CUG-Feature ::= SEQUENCE { + basicService Ext-BasicServiceCode OPTIONAL, + preferentialCUG-Indicator CUG-Index OPTIONAL, + interCUG-Restrictions InterCUG-Restrictions, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +InterCUG-Restrictions ::= OCTET STRING (SIZE (1)) + + -- bits 876543: 000000 (unused) + -- Exception handling: + -- bits 876543 shall be ignored if received and not understood + + -- bits 21 + -- 00 CUG only facilities + -- 01 CUG with outgoing access + -- 10 CUG with incoming access + -- 11 CUG with both outgoing and incoming access + +Ext-SS-Data ::= SEQUENCE { + ss-Code SS-Code, + ss-Status [4] Ext-SS-Status, + ss-SubscriptionOption SS-SubscriptionOption OPTIONAL, + basicServiceGroupList Ext-BasicServiceGroupList OPTIONAL, + extensionContainer [5] ExtensionContainer OPTIONAL, + ...} + +LCS-PrivacyExceptionList ::= SEQUENCE SIZE (1..maxNumOfPrivacyClass) OF + LCS-PrivacyClass + +maxNumOfPrivacyClass INTEGER ::= 4 + +LCS-PrivacyClass ::= SEQUENCE { + ss-Code SS-Code, + ss-Status Ext-SS-Status, + notificationToMSUser [0] NotificationToMSUser OPTIONAL, + -- notificationToMSUser may be sent only for SS-codes callSessionRelated + -- and callSessionUnrelated. If not received for SS-codes callSessionRelated + -- and callSessionUnrelated, + -- the default values according to 3GPP TS 23.271 shall be assumed. + externalClientList [1] ExternalClientList OPTIONAL, + -- externalClientList may be sent only for SS-code callSessionUnrelated to a + -- visited node that does not support LCS Release 4 or later versions. + -- externalClientList may be sent only for SS-codes callSessionUnrelated and + -- callSessionRelated to a visited node that supports LCS Release 4 or later versions. + plmnClientList [2] PLMNClientList OPTIONAL, + -- plmnClientList may be sent only for SS-code plmnoperator. + extensionContainer [3] ExtensionContainer OPTIONAL, + ..., + ext-externalClientList [4] Ext-ExternalClientList OPTIONAL, + -- Ext-externalClientList may be sent only if the visited node supports LCS Release 4 or + -- later versions, the user did specify more than 5 clients, and White Book SCCP is used. + serviceTypeList [5] ServiceTypeList OPTIONAL + -- serviceTypeList may be sent only for SS-code serviceType and if the visited node + -- supports LCS Release 5 or later versions. + -- + -- if segmentation is used, the complete LCS-PrivacyClass shall be sent in one segment +} + +ExternalClientList ::= SEQUENCE SIZE (0..maxNumOfExternalClient) OF + ExternalClient + +maxNumOfExternalClient INTEGER ::= 5 + +PLMNClientList ::= SEQUENCE SIZE (1..maxNumOfPLMNClient) OF + LCSClientInternalID + +maxNumOfPLMNClient INTEGER ::= 5 + +Ext-ExternalClientList ::= SEQUENCE SIZE (1..maxNumOfExt-ExternalClient) OF + ExternalClient + +maxNumOfExt-ExternalClient INTEGER ::= 35 + +ExternalClient ::= SEQUENCE { + clientIdentity LCSClientExternalID, + gmlc-Restriction [0] GMLC-Restriction OPTIONAL, + notificationToMSUser [1] NotificationToMSUser OPTIONAL, + -- If notificationToMSUser is not received, the default value according to + -- 3GPP TS 23.271 shall be assumed. + extensionContainer [2] ExtensionContainer OPTIONAL, + ... } + +GMLC-Restriction ::= ENUMERATED { + gmlc-List (0), + home-Country (1) , + ... } +-- exception handling: +-- At reception of any other value than the ones listed the receiver shall ignore +-- GMLC-Restriction. + +NotificationToMSUser ::= ENUMERATED { + notifyLocationAllowed (0), + notifyAndVerify-LocationAllowedIfNoResponse (1), + notifyAndVerify-LocationNotAllowedIfNoResponse (2), + ..., + locationNotAllowed (3) } +-- exception handling: +-- At reception of any other value than the ones listed the receiver shall ignore +-- NotificationToMSUser. + +ServiceTypeList ::= SEQUENCE SIZE (1..maxNumOfServiceType) OF + ServiceType + +maxNumOfServiceType INTEGER ::= 32 + +ServiceType ::= SEQUENCE { + serviceTypeIdentity LCSServiceTypeID, + gmlc-Restriction [0] GMLC-Restriction OPTIONAL, + notificationToMSUser [1] NotificationToMSUser OPTIONAL, + -- If notificationToMSUser is not received, the default value according to + -- 3GPP TS 23.271 shall be assumed. + extensionContainer [2] ExtensionContainer OPTIONAL, + ... } + +MOLR-List ::= SEQUENCE SIZE (1..maxNumOfMOLR-Class) OF + MOLR-Class + +maxNumOfMOLR-Class INTEGER ::= 3 + +MOLR-Class ::= SEQUENCE { + ss-Code SS-Code, + ss-Status Ext-SS-Status, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +ZoneCodeList ::= SEQUENCE SIZE (1..maxNumOfZoneCodes) + OF ZoneCode + +ZoneCode ::= OCTET STRING (SIZE (2)) + -- internal structure is defined in TS 3GPP TS 23.003 [17] + +maxNumOfZoneCodes INTEGER ::= 10 + +InsertSubscriberDataRes ::= SEQUENCE { + teleserviceList [1] TeleserviceList OPTIONAL, + bearerServiceList [2] BearerServiceList OPTIONAL, + ss-List [3] SS-List OPTIONAL, + odb-GeneralData [4] ODB-GeneralData OPTIONAL, + regionalSubscriptionResponse [5] RegionalSubscriptionResponse OPTIONAL, + supportedCamelPhases [6] SupportedCamelPhases OPTIONAL, + extensionContainer [7] ExtensionContainer OPTIONAL, + ... , + offeredCamel4CSIs [8] OfferedCamel4CSIs OPTIONAL, + supportedFeatures [9] SupportedFeatures OPTIONAL } + +RegionalSubscriptionResponse ::= ENUMERATED { + networkNode-AreaRestricted (0), + tooManyZoneCodes (1), + zoneCodesConflict (2), + regionalSubscNotSupported (3)} + +DeleteSubscriberDataArg ::= SEQUENCE { + imsi [0] IMSI, + basicServiceList [1] BasicServiceList OPTIONAL, + -- The exception handling for reception of unsupported/not allocated + -- basicServiceCodes is defined in section 6.8.2 + ss-List [2] SS-List OPTIONAL, + roamingRestrictionDueToUnsupportedFeature [4] NULL OPTIONAL, + regionalSubscriptionIdentifier [5] ZoneCode OPTIONAL, + vbsGroupIndication [7] NULL OPTIONAL, + vgcsGroupIndication [8] NULL OPTIONAL, + camelSubscriptionInfoWithdraw [9] NULL OPTIONAL, + extensionContainer [6] ExtensionContainer OPTIONAL, + ..., + gprsSubscriptionDataWithdraw [10] GPRSSubscriptionDataWithdraw OPTIONAL, + roamingRestrictedInSgsnDueToUnsuppportedFeature [11] NULL OPTIONAL, + lsaInformationWithdraw [12] LSAInformationWithdraw OPTIONAL, + gmlc-ListWithdraw [13] NULL OPTIONAL, + istInformationWithdraw [14] NULL OPTIONAL, + specificCSI-Withdraw [15] SpecificCSI-Withdraw OPTIONAL, + chargingCharacteristicsWithdraw [16] NULL OPTIONAL, + stn-srWithdraw [17] NULL OPTIONAL, + epsSubscriptionDataWithdraw [18] EPS-SubscriptionDataWithdraw OPTIONAL, + apn-oi-replacementWithdraw [19] NULL OPTIONAL, + csg-SubscriptionDeleted [20] NULL OPTIONAL } + +SpecificCSI-Withdraw ::= BIT STRING { + o-csi (0), + ss-csi (1), + tif-csi (2), + d-csi (3), + vt-csi (4), + mo-sms-csi (5), + m-csi (6), + gprs-csi (7), + t-csi (8), + mt-sms-csi (9), + mg-csi (10), + o-IM-CSI (11), + d-IM-CSI (12), + vt-IM-CSI (13) } (SIZE(8..32)) +-- exception handling: +-- bits 11 to 31 shall be ignored if received by a non-IP Multimedia Core Network entity. +-- bits 0-10 and 14-31 shall be ignored if received by an IP Multimedia Core Network entity. +-- bits 11-13 are only applicable in an IP Multimedia Core Network. +-- Bit 8 and bits 11-13 are only applicable for the NoteSubscriberDataModified operation. + +GPRSSubscriptionDataWithdraw ::= CHOICE { + allGPRSData NULL, + contextIdList ContextIdList} + +EPS-SubscriptionDataWithdraw ::= CHOICE { + allEPS-Data NULL, + contextIdList ContextIdList} + +ContextIdList ::= SEQUENCE SIZE (1..maxNumOfPDP-Contexts) OF + ContextId + +LSAInformationWithdraw ::= CHOICE { + allLSAData NULL, + lsaIdentityList LSAIdentityList } + +LSAIdentityList ::= SEQUENCE SIZE (1..maxNumOfLSAs) OF + LSAIdentity + +BasicServiceList ::= SEQUENCE SIZE (1..maxNumOfBasicServices) OF + Ext-BasicServiceCode + +maxNumOfBasicServices INTEGER ::= 70 + +DeleteSubscriberDataRes ::= SEQUENCE { + regionalSubscriptionResponse [0] RegionalSubscriptionResponse OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +VlrCamelSubscriptionInfo ::= SEQUENCE { + o-CSI [0] O-CSI OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ..., + ss-CSI [2] SS-CSI OPTIONAL, + o-BcsmCamelTDP-CriteriaList [4] O-BcsmCamelTDPCriteriaList OPTIONAL, + tif-CSI [3] NULL OPTIONAL, + m-CSI [5] M-CSI OPTIONAL, + mo-sms-CSI [6] SMS-CSI OPTIONAL, + vt-CSI [7] T-CSI OPTIONAL, + t-BCSM-CAMEL-TDP-CriteriaList [8] T-BCSM-CAMEL-TDP-CriteriaList OPTIONAL, + d-CSI [9] D-CSI OPTIONAL, + mt-sms-CSI [10] SMS-CSI OPTIONAL, + mt-smsCAMELTDP-CriteriaList [11] MT-smsCAMELTDP-CriteriaList OPTIONAL + } + +MT-smsCAMELTDP-CriteriaList ::= SEQUENCE SIZE (1.. maxNumOfCamelTDPData) OF + MT-smsCAMELTDP-Criteria + +MT-smsCAMELTDP-Criteria ::= SEQUENCE { + sms-TriggerDetectionPoint SMS-TriggerDetectionPoint, + tpdu-TypeCriterion [0] TPDU-TypeCriterion OPTIONAL, + ... } + +TPDU-TypeCriterion ::= SEQUENCE SIZE (1..maxNumOfTPDUTypes) OF + MT-SMS-TPDU-Type + + +maxNumOfTPDUTypes INTEGER ::= 5 + +MT-SMS-TPDU-Type ::= ENUMERATED { + sms-DELIVER (0), + sms-SUBMIT-REPORT (1), + sms-STATUS-REPORT (2), + ... } + +-- exception handling: +-- For TPDU-TypeCriterion sequences containing this parameter with any +-- other value than the ones listed above the receiver shall ignore +-- the whole TPDU-TypeCriterion sequence. +-- In CAMEL phase 4, sms-SUBMIT-REPORT shall not be used and a received TPDU-TypeCriterion +-- sequence containing sms-SUBMIT-REPORT shall be wholly ignored. + +D-CSI ::= SEQUENCE { + dp-AnalysedInfoCriteriaList [0] DP-AnalysedInfoCriteriaList OPTIONAL, + camelCapabilityHandling [1] CamelCapabilityHandling OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + notificationToCSE [3] NULL OPTIONAL, + csi-Active [4] NULL OPTIONAL, + ...} +-- notificationToCSE and csi-Active shall not be present when D-CSI is sent to VLR/GMSC. +-- They may only be included in ATSI/ATM ack/NSDC message. +-- DP-AnalysedInfoCriteria and camelCapabilityHandling shall be present in +-- the D-CSI sequence. +-- If D-CSI is segmented, then the first segment shall contain dp-AnalysedInfoCriteriaList +-- and camelCapabilityHandling. Subsequent segments shall not contain +-- camelCapabilityHandling, but may contain dp-AnalysedInfoCriteriaList. + +DP-AnalysedInfoCriteriaList ::= SEQUENCE SIZE (1..maxNumOfDP-AnalysedInfoCriteria) OF + DP-AnalysedInfoCriterium + +maxNumOfDP-AnalysedInfoCriteria INTEGER ::= 10 + +DP-AnalysedInfoCriterium ::= SEQUENCE { + dialledNumber ISDN-AddressString, + serviceKey ServiceKey, + gsmSCF-Address ISDN-AddressString, + defaultCallHandling DefaultCallHandling, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +SS-CSI ::= SEQUENCE { + ss-CamelData SS-CamelData, + extensionContainer ExtensionContainer OPTIONAL, + ..., + notificationToCSE [0] NULL OPTIONAL, + csi-Active [1] NULL OPTIONAL +-- notificationToCSE and csi-Active shall not be present when SS-CSI is sent to VLR. +-- They may only be included in ATSI/ATM ack/NSDC message. +} + +SS-CamelData ::= SEQUENCE { + ss-EventList SS-EventList, + gsmSCF-Address ISDN-AddressString, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +SS-EventList ::= SEQUENCE SIZE (1..maxNumOfCamelSSEvents) OF SS-Code + -- Actions for the following SS-Code values are defined in CAMEL Phase 3: + -- ect SS-Code ::= '00110001'B + -- multiPTY SS-Code ::= '01010001'B + -- cd SS-Code ::= '00100100'B + -- ccbs SS-Code ::= '01000100'B + -- all other SS codes shall be ignored + -- When SS-CSI is sent to the VLR, it shall not contain a marking for ccbs. + -- If the VLR receives SS-CSI containing a marking for ccbs, the VLR shall discard the + -- ccbs marking in SS-CSI. + +maxNumOfCamelSSEvents INTEGER ::= 10 + +O-CSI ::= SEQUENCE { + o-BcsmCamelTDPDataList O-BcsmCamelTDPDataList, + extensionContainer ExtensionContainer OPTIONAL, + ..., + camelCapabilityHandling [0] CamelCapabilityHandling OPTIONAL, + notificationToCSE [1] NULL OPTIONAL, + csiActive [2] NULL OPTIONAL} +-- notificationtoCSE and csiActive shall not be present when O-CSI is sent to VLR/GMSC. +-- They may only be included in ATSI/ATM ack/NSDC message. +-- O-CSI shall not be segmented. + +O-BcsmCamelTDPDataList ::= SEQUENCE SIZE (1..maxNumOfCamelTDPData) OF + O-BcsmCamelTDPData + -- O-BcsmCamelTDPDataList shall not contain more than one instance of + -- O-BcsmCamelTDPData containing the same value for o-BcsmTriggerDetectionPoint. + -- For CAMEL Phase 2, this means that only one instance of O-BcsmCamelTDPData is allowed + -- with o-BcsmTriggerDetectionPoint being equal to DP2. + +maxNumOfCamelTDPData INTEGER ::= 10 + +O-BcsmCamelTDPData ::= SEQUENCE { + o-BcsmTriggerDetectionPoint O-BcsmTriggerDetectionPoint, + serviceKey ServiceKey, + gsmSCF-Address [0] ISDN-AddressString, + defaultCallHandling [1] DefaultCallHandling, + extensionContainer [2] ExtensionContainer OPTIONAL, + ... + } + +ServiceKey ::= INTEGER (0..2147483647) + +O-BcsmTriggerDetectionPoint ::= ENUMERATED { + collectedInfo (2), + ..., + routeSelectFailure (4) } + -- exception handling: + -- For O-BcsmCamelTDPData sequences containing this parameter with any + -- other value than the ones listed the receiver shall ignore the whole + -- O-BcsmCamelTDPDatasequence. + -- For O-BcsmCamelTDP-Criteria sequences containing this parameter with any + -- other value than the ones listed the receiver shall ignore the whole + -- O-BcsmCamelTDP-Criteria sequence. + +O-BcsmCamelTDPCriteriaList ::= SEQUENCE SIZE (1..maxNumOfCamelTDPData) OF + O-BcsmCamelTDP-Criteria + +T-BCSM-CAMEL-TDP-CriteriaList ::= SEQUENCE SIZE (1..maxNumOfCamelTDPData) OF + T-BCSM-CAMEL-TDP-Criteria + +O-BcsmCamelTDP-Criteria ::= SEQUENCE { + o-BcsmTriggerDetectionPoint O-BcsmTriggerDetectionPoint, + destinationNumberCriteria [0] DestinationNumberCriteria OPTIONAL, + basicServiceCriteria [1] BasicServiceCriteria OPTIONAL, + callTypeCriteria [2] CallTypeCriteria OPTIONAL, + ..., + o-CauseValueCriteria [3] O-CauseValueCriteria OPTIONAL, + extensionContainer [4] ExtensionContainer OPTIONAL } + +T-BCSM-CAMEL-TDP-Criteria ::= SEQUENCE { + t-BCSM-TriggerDetectionPoint T-BcsmTriggerDetectionPoint, + basicServiceCriteria [0] BasicServiceCriteria OPTIONAL, + t-CauseValueCriteria [1] T-CauseValueCriteria OPTIONAL, + ... } + +DestinationNumberCriteria ::= SEQUENCE { + matchType [0] MatchType, + destinationNumberList [1] DestinationNumberList OPTIONAL, + destinationNumberLengthList [2] DestinationNumberLengthList OPTIONAL, + -- one or both of destinationNumberList and destinationNumberLengthList + -- shall be present + ...} + +DestinationNumberList ::= SEQUENCE SIZE (1..maxNumOfCamelDestinationNumbers) OF + ISDN-AddressString + -- The receiving entity shall not check the format of a number in + -- the dialled number list + +DestinationNumberLengthList ::= SEQUENCE SIZE (1..maxNumOfCamelDestinationNumberLengths) OF + INTEGER(1..maxNumOfISDN-AddressDigits) + +BasicServiceCriteria ::= SEQUENCE SIZE(1..maxNumOfCamelBasicServiceCriteria) OF + Ext-BasicServiceCode + +maxNumOfISDN-AddressDigits INTEGER ::= 15 + +maxNumOfCamelDestinationNumbers INTEGER ::= 10 + +maxNumOfCamelDestinationNumberLengths INTEGER ::= 3 + +maxNumOfCamelBasicServiceCriteria INTEGER ::= 5 + +CallTypeCriteria ::= ENUMERATED { + forwarded (0), + notForwarded (1)} + +MatchType ::= ENUMERATED { + inhibiting (0), + enabling (1)} + +O-CauseValueCriteria ::= SEQUENCE SIZE(1..maxNumOfCAMEL-O-CauseValueCriteria) OF + CauseValue + +T-CauseValueCriteria ::= SEQUENCE SIZE(1..maxNumOfCAMEL-T-CauseValueCriteria) OF + CauseValue + +maxNumOfCAMEL-O-CauseValueCriteria INTEGER ::= 5 + +maxNumOfCAMEL-T-CauseValueCriteria INTEGER ::= 5 + +CauseValue ::= OCTET STRING (SIZE(1)) +-- Type extracted from Cause parameter in ITU-T Recommendation Q.763. +-- For the use of cause value refer to ITU-T Recommendation Q.850. + +DefaultCallHandling ::= ENUMERATED { + continueCall (0) , + releaseCall (1) , + ...} + -- exception handling: + -- reception of values in range 2-31 shall be treated as "continueCall" + -- reception of values greater than 31 shall be treated as "releaseCall" + +CamelCapabilityHandling ::= INTEGER(1..16) + -- value 1 = CAMEL phase 1, + -- value 2 = CAMEL phase 2, + -- value 3 = CAMEL Phase 3, + -- value 4 = CAMEL phase 4: + -- reception of values greater than 4 shall be treated as CAMEL phase 4. + +SupportedCamelPhases ::= BIT STRING { + phase1 (0), + phase2 (1), + phase3 (2), + phase4 (3)} (SIZE (1..16)) +-- A node shall mark in the BIT STRING all CAMEL Phases it supports. +-- Other values than listed above shall be discarded. + +OfferedCamel4CSIs ::= BIT STRING { + o-csi (0), + d-csi (1), + vt-csi (2), + t-csi (3), + mt-sms-csi (4), + mg-csi (5), + psi-enhancements (6) +} (SIZE (7..16)) +-- A node supporting Camel phase 4 shall mark in the BIT STRING all Camel4 CSIs +-- it offers. +-- Other values than listed above shall be discarded. + +OfferedCamel4Functionalities ::= BIT STRING { + initiateCallAttempt (0), + splitLeg (1), + moveLeg (2), + disconnectLeg (3), + entityReleased (4), + dfc-WithArgument (5), + playTone (6), + dtmf-MidCall (7), + chargingIndicator (8), + alertingDP (9), + locationAtAlerting (10), + changeOfPositionDP (11), + or-Interactions (12), + warningToneEnhancements (13), + cf-Enhancements (14), + subscribedEnhancedDialledServices (15), + servingNetworkEnhancedDialledServices (16), + criteriaForChangeOfPositionDP (17), + serviceChangeDP (18), + collectInformation (19) +} (SIZE (15..64)) +-- A node supporting Camel phase 4 shall mark in the BIT STRING all CAMEL4 +-- functionalities it offers. +-- Other values than listed above shall be discarded. + +SMS-CSI ::= SEQUENCE { + sms-CAMEL-TDP-DataList [0] SMS-CAMEL-TDP-DataList OPTIONAL, + camelCapabilityHandling [1] CamelCapabilityHandling OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + notificationToCSE [3] NULL OPTIONAL, + csi-Active [4] NULL OPTIONAL, + ...} +-- notificationToCSE and csi-Active shall not be present +-- when MO-SMS-CSI or MT-SMS-CSI is sent to VLR or SGSN. +-- They may only be included in ATSI/ATM ack/NSDC message. +-- SMS-CAMEL-TDP-Data and camelCapabilityHandling shall be present in +-- the SMS-CSI sequence. +-- If SMS-CSI is segmented, sms-CAMEL-TDP-DataList and camelCapabilityHandling shall be +-- present in the first segment + +SMS-CAMEL-TDP-DataList ::= SEQUENCE SIZE (1..maxNumOfCamelTDPData) OF + SMS-CAMEL-TDP-Data +-- SMS-CAMEL-TDP-DataList shall not contain more than one instance of +-- SMS-CAMEL-TDP-Data containing the same value for sms-TriggerDetectionPoint. + +SMS-CAMEL-TDP-Data ::= SEQUENCE { + sms-TriggerDetectionPoint [0] SMS-TriggerDetectionPoint, + serviceKey [1] ServiceKey, + gsmSCF-Address [2] ISDN-AddressString, + defaultSMS-Handling [3] DefaultSMS-Handling, + extensionContainer [4] ExtensionContainer OPTIONAL, + ... + } + +SMS-TriggerDetectionPoint ::= ENUMERATED { + sms-CollectedInfo (1), + ..., + sms-DeliveryRequest (2) + } +-- exception handling: +-- For SMS-CAMEL-TDP-Data and MT-smsCAMELTDP-Criteria sequences containing this +-- parameter with any other value than the ones listed the receiver shall ignore +-- the whole sequence. +-- +-- If this parameter is received with any other value than sms-CollectedInfo +-- in an SMS-CAMEL-TDP-Data sequence contained in mo-sms-CSI, then the receiver shall +-- ignore the whole SMS-CAMEL-TDP-Data sequence. +-- +-- If this parameter is received with any other value than sms-DeliveryRequest +-- in an SMS-CAMEL-TDP-Data sequence contained in mt-sms-CSI then the receiver shall +-- ignore the whole SMS-CAMEL-TDP-Data sequence. +-- +-- If this parameter is received with any other value than sms-DeliveryRequest +-- in an MT-smsCAMELTDP-Criteria sequence then the receiver shall +-- ignore the whole MT-smsCAMELTDP-Criteria sequence. + +DefaultSMS-Handling ::= ENUMERATED { + continueTransaction (0) , + releaseTransaction (1) , + ...} +-- exception handling: +-- reception of values in range 2-31 shall be treated as "continueTransaction" +-- reception of values greater than 31 shall be treated as "releaseTransaction" + +M-CSI ::= SEQUENCE { + mobilityTriggers MobilityTriggers, + serviceKey ServiceKey, + gsmSCF-Address [0] ISDN-AddressString, + extensionContainer [1] ExtensionContainer OPTIONAL, + notificationToCSE [2] NULL OPTIONAL, + csi-Active [3] NULL OPTIONAL, + ...} +-- notificationToCSE and csi-Active shall not be present when M-CSI is sent to VLR. +-- They may only be included in ATSI/ATM ack/NSDC message. + +MG-CSI ::= SEQUENCE { + mobilityTriggers MobilityTriggers, + serviceKey ServiceKey, + gsmSCF-Address [0] ISDN-AddressString, + extensionContainer [1] ExtensionContainer OPTIONAL, + notificationToCSE [2] NULL OPTIONAL, + csi-Active [3] NULL OPTIONAL, + ...} +-- notificationToCSE and csi-Active shall not be present when MG-CSI is sent to SGSN. +-- They may only be included in ATSI/ATM ack/NSDC message. + +MobilityTriggers ::= SEQUENCE SIZE (1..maxNumOfMobilityTriggers) OF + MM-Code + +maxNumOfMobilityTriggers INTEGER ::= 10 + +MM-Code ::= OCTET STRING (SIZE (1)) +-- This type is used to indicate a Mobility Management event. +-- Actions for the following MM-Code values are defined in CAMEL Phase 4: +-- +-- CS domain MM events: +-- Location-update-in-same-VLR MM-Code ::= '00000000'B +-- Location-update-to-other-VLR MM-Code ::= '00000001'B +-- IMSI-Attach MM-Code ::= '00000010'B +-- MS-initiated-IMSI-Detach MM-Code ::= '00000011'B +-- Network-initiated-IMSI-Detach MM-Code ::= '00000100'B +-- +-- PS domain MM events: +-- Routeing-Area-update-in-same-SGSN MM-Code ::= '10000000'B +-- Routeing-Area-update-to-other-SGSN-update-from-new-SGSN +-- MM-Code ::= '10000001'B +-- Routeing-Area-update-to-other-SGSN-disconnect-by-detach +-- MM-Code ::= '10000010'B +-- GPRS-Attach MM-Code ::= '10000011'B +-- MS-initiated-GPRS-Detach MM-Code ::= '10000100'B +-- Network-initiated-GPRS-Detach MM-Code ::= '10000101'B +-- Network-initiated-transfer-to-MS-not-reachable-for-paging +-- MM-Code ::= '10000110'B +-- +-- If the MSC receives any other MM-code than the ones listed above for the +-- CS domain, then the MSC shall ignore that MM-code. +-- If the SGSN receives any other MM-code than the ones listed above for the +-- PS domain, then the SGSN shall ignore that MM-code. + +T-CSI ::= SEQUENCE { + t-BcsmCamelTDPDataList T-BcsmCamelTDPDataList, + extensionContainer ExtensionContainer OPTIONAL, + ..., + camelCapabilityHandling [0] CamelCapabilityHandling OPTIONAL, + notificationToCSE [1] NULL OPTIONAL, + csi-Active [2] NULL OPTIONAL} +-- notificationToCSE and csi-Active shall not be present when VT-CSI/T-CSI is sent +-- to VLR/GMSC. +-- They may only be included in ATSI/ATM ack/NSDC message. +-- T-CSI shall not be segmented. + +T-BcsmCamelTDPDataList ::= SEQUENCE SIZE (1..maxNumOfCamelTDPData) OF + T-BcsmCamelTDPData + --- T-BcsmCamelTDPDataList shall not contain more than one instance of + --- T-BcsmCamelTDPData containing the same value for t-BcsmTriggerDetectionPoint. + --- For CAMEL Phase 2, this means that only one instance of T-BcsmCamelTDPData is allowed + --- with t-BcsmTriggerDetectionPoint being equal to DP12. + --- For CAMEL Phase 3, more TDP’s are allowed. + +T-BcsmCamelTDPData ::= SEQUENCE { + t-BcsmTriggerDetectionPoint T-BcsmTriggerDetectionPoint, + serviceKey ServiceKey, + gsmSCF-Address [0] ISDN-AddressString, + defaultCallHandling [1] DefaultCallHandling, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +T-BcsmTriggerDetectionPoint ::= ENUMERATED { + termAttemptAuthorized (12), + ... , + tBusy (13), + tNoAnswer (14)} + -- exception handling: + -- For T-BcsmCamelTDPData sequences containing this parameter with any other + -- value than the ones listed above, the receiver shall ignore the whole + -- T-BcsmCamelTDPData sequence. + +-- gprs location information retrieval types + +SendRoutingInfoForGprsArg ::= SEQUENCE { + imsi [0] IMSI, + ggsn-Address [1] GSN-Address OPTIONAL, + ggsn-Number [2] ISDN-AddressString, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +SendRoutingInfoForGprsRes ::= SEQUENCE { + sgsn-Address [0] GSN-Address, + ggsn-Address [1] GSN-Address OPTIONAL, + mobileNotReachableReason [2] AbsentSubscriberDiagnosticSM OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +-- failure report types + +FailureReportArg ::= SEQUENCE { + imsi [0] IMSI, + ggsn-Number [1] ISDN-AddressString , + ggsn-Address [2] GSN-Address OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +FailureReportRes ::= SEQUENCE { + ggsn-Address [0] GSN-Address OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ...} + +-- gprs notification types + +NoteMsPresentForGprsArg ::= SEQUENCE { + imsi [0] IMSI, + sgsn-Address [1] GSN-Address, + ggsn-Address [2] GSN-Address OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +NoteMsPresentForGprsRes ::= SEQUENCE { + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +-- fault recovery types + +ResetArg ::= SEQUENCE { + hlr-Number ISDN-AddressString, + hlr-List HLR-List OPTIONAL, + ...} + +RestoreDataArg ::= SEQUENCE { + imsi IMSI, + lmsi LMSI OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ... , + vlr-Capability [6] VLR-Capability OPTIONAL } + +RestoreDataRes ::= SEQUENCE { + hlr-Number ISDN-AddressString, + msNotReachable NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +-- VBS/VGCS types +VBSDataList ::= SEQUENCE SIZE (1..maxNumOfVBSGroupIds) OF + VoiceBroadcastData + +VGCSDataList ::= SEQUENCE SIZE (1..maxNumOfVGCSGroupIds) OF + VoiceGroupCallData + +maxNumOfVBSGroupIds INTEGER ::= 50 + +maxNumOfVGCSGroupIds INTEGER ::= 50 + +VoiceGroupCallData ::= SEQUENCE { + groupId GroupId, + -- groupId shall be filled with six TBCD fillers (1111)if the longGroupId is present + extensionContainer ExtensionContainer OPTIONAL, + ..., + additionalSubscriptions AdditionalSubscriptions OPTIONAL, + additionalInfo [0] AdditionalInfo OPTIONAL, + longGroupId [1] Long-GroupId OPTIONAL } + + -- VoiceGroupCallData containing a longGroupId shall not be sent to VLRs that did not + -- indicate support of long Group IDs within the Update Location or Restore Data + -- request message + +AdditionalInfo ::= BIT STRING (SIZE (1..136)) +-- Refers to Additional Info as specified in 3GPP TS 43.068 + +AdditionalSubscriptions ::= BIT STRING { + privilegedUplinkRequest (0), + emergencyUplinkRequest (1), + emergencyReset (2)} (SIZE (3..8)) +-- Other bits than listed above shall be discarded. + +VoiceBroadcastData ::= SEQUENCE { + groupid GroupId, + -- groupId shall be filled with six TBCD fillers (1111)if the longGroupId is present + broadcastInitEntitlement NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + longGroupId [0] Long-GroupId OPTIONAL } + +-- VoiceBroadcastData containing a longGroupId shall not be sent to VLRs that did not +-- indicate support of long Group IDs within the Update Location or Restore Data + -- request message + +GroupId ::= TBCD-STRING (SIZE (3)) + -- When Group-Id is less than six characters in length, the TBCD filler (1111) + -- is used to fill unused half octets. + -- Refers to the Group Identification as specified in 3GPP TS 23.003 + -- and 3GPP TS 43.068/ 43.069 + +Long-GroupId ::= TBCD-STRING (SIZE (4)) + -- When Long-Group-Id is less than eight characters in length, the TBCD filler (1111) + -- is used to fill unused half octets. + -- Refers to the Group Identification as specified in 3GPP TS 23.003 + -- and 3GPP TS 43.068/ 43.069 + + +-- provide subscriber info types + +ProvideSubscriberInfoArg ::= SEQUENCE { + imsi [0] IMSI, + lmsi [1] LMSI OPTIONAL, + requestedInfo [2] RequestedInfo, + extensionContainer [3] ExtensionContainer OPTIONAL, + ..., + callPriority [4] EMLPP-Priority OPTIONAL + } + +ProvideSubscriberInfoRes ::= SEQUENCE { + subscriberInfo SubscriberInfo, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +SubscriberInfo ::= SEQUENCE { + locationInformation [0] LocationInformation OPTIONAL, + subscriberState [1] SubscriberState OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ... , + locationInformationGPRS [3] LocationInformationGPRS OPTIONAL, + ps-SubscriberState [4] PS-SubscriberState OPTIONAL, + imei [5] IMEI OPTIONAL, + ms-Classmark2 [6] MS-Classmark2 OPTIONAL, + gprs-MS-Class [7] GPRSMSClass OPTIONAL, + mnpInfoRes [8] MNPInfoRes OPTIONAL } + +-- If the HLR receives locationInformation, subscriberState or ms-Classmark2 from an SGSN +-- it shall discard them. +-- If the HLR receives locationInformationGPRS, ps-SubscriberState or gprs-MS-Class from +-- a VLR it shall discard them. +-- If the HLR receives parameters which it has not requested, it shall discard them. + +MNPInfoRes ::= SEQUENCE { + routeingNumber [0] RouteingNumber OPTIONAL, + imsi [1] IMSI OPTIONAL, + msisdn [2] ISDN-AddressString OPTIONAL, + numberPortabilityStatus [3] NumberPortabilityStatus OPTIONAL, + extensionContainer [4] ExtensionContainer OPTIONAL, + ... } +-- The IMSI parameter contains a generic IMSI, i.e. it is not tied necessarily to the +-- Subscriber. MCC and MNC values in this IMSI shall point to the Subscription Network of +-- the Subscriber. See 3GPP TS 23.066 [108]. + +RouteingNumber ::= TBCD-STRING (SIZE (1..5)) + + +NumberPortabilityStatus ::= ENUMERATED { + notKnownToBePorted (0), + ownNumberPortedOut (1), + foreignNumberPortedToForeignNetwork (2), + ..., + ownNumberNotPortedOut (4), + foreignNumberPortedIn (5) + } + -- exception handling: + -- reception of other values than the ones listed the receiver shall ignore the + -- whole NumberPortabilityStatus; + -- ownNumberNotPortedOut or foreignNumberPortedIn may only be included in Any Time + -- Interrogation message. + +MS-Classmark2 ::= OCTET STRING (SIZE (3)) + -- This parameter carries the value part of the MS Classmark 2 IE defined in + -- 3GPP TS 24.008 [35]. + +GPRSMSClass ::= SEQUENCE { + mSNetworkCapability [0] MSNetworkCapability, + mSRadioAccessCapability [1] MSRadioAccessCapability OPTIONAL + } + +MSNetworkCapability ::= OCTET STRING (SIZE (1..8)) + -- This parameter carries the value part of the MS Network Capability IE defined in + -- 3GPP TS 24.008 [35]. + +MSRadioAccessCapability ::= OCTET STRING (SIZE (1..50)) + -- This parameter carries the value part of the MS Radio Access Capability IE defined in + -- 3GPP TS 24.008 [35]. + +RequestedInfo ::= SEQUENCE { + locationInformation [0] NULL OPTIONAL, + subscriberState [1] NULL OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ..., + currentLocation [3] NULL OPTIONAL, + requestedDomain [4] DomainType OPTIONAL, + imei [6] NULL OPTIONAL, + ms-classmark [5] NULL OPTIONAL, + mnpRequestedInfo [7] NULL OPTIONAL } + +-- currentLocation shall be absent if locationInformation is absent + +DomainType ::= ENUMERATED { + cs-Domain (0), + ps-Domain (1), + ...} +-- exception handling: +-- reception of values > 1 shall be mapped to 'cs-Domain' + +LocationInformation ::= SEQUENCE { + ageOfLocationInformation AgeOfLocationInformation OPTIONAL, + geographicalInformation [0] GeographicalInformation OPTIONAL, + vlr-number [1] ISDN-AddressString OPTIONAL, + locationNumber [2] LocationNumber OPTIONAL, + cellGlobalIdOrServiceAreaIdOrLAI [3] CellGlobalIdOrServiceAreaIdOrLAI OPTIONAL, + extensionContainer [4] ExtensionContainer OPTIONAL, + ... , + selectedLSA-Id [5] LSAIdentity OPTIONAL, + msc-Number [6] ISDN-AddressString OPTIONAL, + geodeticInformation [7] GeodeticInformation OPTIONAL, + currentLocationRetrieved [8] NULL OPTIONAL, + sai-Present [9] NULL OPTIONAL } +-- sai-Present indicates that the cellGlobalIdOrServiceAreaIdOrLAI parameter contains +-- a Service Area Identity. +-- currentLocationRetrieved shall be present +-- if the location information were retrieved after a successfull paging. + +LocationInformationGPRS ::= SEQUENCE { + cellGlobalIdOrServiceAreaIdOrLAI [0] CellGlobalIdOrServiceAreaIdOrLAI OPTIONAL, + routeingAreaIdentity [1] RAIdentity OPTIONAL, + geographicalInformation [2] GeographicalInformation OPTIONAL, + sgsn-Number [3] ISDN-AddressString OPTIONAL, + selectedLSAIdentity [4] LSAIdentity OPTIONAL, + extensionContainer [5] ExtensionContainer OPTIONAL, + ..., + sai-Present [6] NULL OPTIONAL, + geodeticInformation [7] GeodeticInformation OPTIONAL, + currentLocationRetrieved [8] NULL OPTIONAL, + ageOfLocationInformation [9] AgeOfLocationInformation OPTIONAL } +-- sai-Present indicates that the cellGlobalIdOrServiceAreaIdOrLAI parameter contains +-- a Service Area Identity. +-- currentLocationRetrieved shall be present if the location information +-- was retrieved after successful paging. + +RAIdentity ::= OCTET STRING (SIZE (6)) +-- Routing Area Identity is coded in accordance with 3GPP TS 29.060 [105]. +-- It shall contain the value part defined in 3GPP TS 29.060 only. I.e. the 3GPP TS 29.060 +-- type identifier octet shall not be included. + + +GeographicalInformation ::= OCTET STRING (SIZE (8)) +-- Refers to geographical Information defined in 3GPP TS 23.032. +-- Only the description of an ellipsoid point with uncertainty circle +-- as specified in 3GPP TS 23.032 is allowed to be used +-- The internal structure according to 3GPP TS 23.032 is as follows: +-- Type of shape (ellipsoid point with uncertainty circle) 1 octet +-- Degrees of Latitude 3 octets +-- Degrees of Longitude 3 octets +-- Uncertainty code 1 octet + +GeodeticInformation ::= OCTET STRING (SIZE (10)) +-- Refers to Calling Geodetic Location defined in Q.763 (1999). +-- Only the description of an ellipsoid point with uncertainty circle +-- as specified in Q.763 (1999) is allowed to be used +-- The internal structure according to Q.763 (1999) is as follows: +-- Screening and presentation indicators 1 octet +-- Type of shape (ellipsoid point with uncertainty circle) 1 octet +-- Degrees of Latitude 3 octets +-- Degrees of Longitude 3 octets +-- Uncertainty code 1 octet +-- Confidence 1 octet + +LocationNumber ::= OCTET STRING (SIZE (2..10)) + -- the internal structure is defined in ITU-T Rec Q.763 + +SubscriberState ::= CHOICE { + assumedIdle [0] NULL, + camelBusy [1] NULL, + netDetNotReachable NotReachableReason, + notProvidedFromVLR [2] NULL} + +PS-SubscriberState ::= CHOICE { + notProvidedFromSGSN [0] NULL, + ps-Detached [1] NULL, + ps-AttachedNotReachableForPaging [2] NULL, + ps-AttachedReachableForPaging [3] NULL, + ps-PDP-ActiveNotReachableForPaging [4] PDP-ContextInfoList, + ps-PDP-ActiveReachableForPaging [5] PDP-ContextInfoList, + netDetNotReachable NotReachableReason } + +PDP-ContextInfoList ::= SEQUENCE SIZE (1..maxNumOfPDP-Contexts) OF + PDP-ContextInfo + +PDP-ContextInfo ::= SEQUENCE { + pdp-ContextIdentifier [0] ContextId, + pdp-ContextActive [1] NULL OPTIONAL, + pdp-Type [2] PDP-Type, + pdp-Address [3] PDP-Address OPTIONAL, + apn-Subscribed [4] APN OPTIONAL, + apn-InUse [5] APN OPTIONAL, + nsapi [6] NSAPI OPTIONAL, + transactionId [7] TransactionId OPTIONAL, + teid-ForGnAndGp [8] TEID OPTIONAL, + teid-ForIu [9] TEID OPTIONAL, + ggsn-Address [10] GSN-Address OPTIONAL, + qos-Subscribed [11] Ext-QoS-Subscribed OPTIONAL, + qos-Requested [12] Ext-QoS-Subscribed OPTIONAL, + qos-Negotiated [13] Ext-QoS-Subscribed OPTIONAL, + chargingId [14] GPRSChargingID OPTIONAL, + chargingCharacteristics [15] ChargingCharacteristics OPTIONAL, + rnc-Address [16] GSN-Address OPTIONAL, + extensionContainer [17] ExtensionContainer OPTIONAL, + ..., + qos2-Subscribed [18] Ext2-QoS-Subscribed OPTIONAL, + -- qos2-Subscribed may be present only if qos-Subscribed is present. + qos2-Requested [19] Ext2-QoS-Subscribed OPTIONAL, + -- qos2-Requested may be present only if qos-Requested is present. + qos2-Negotiated [20] Ext2-QoS-Subscribed OPTIONAL, + -- qos2-Negotiated may be present only if qos-Negotiated is present. + qos3-Subscribed [21] Ext3-QoS-Subscribed OPTIONAL, + -- qos3-Subscribed may be present only if qos2-Subscribed is present. + qos3-Requested [22] Ext3-QoS-Subscribed OPTIONAL, + -- qos3-Requested may be present only if qos2-Requested is present. + qos3-Negotiated [23] Ext3-QoS-Subscribed OPTIONAL + -- qos3-Negotiated may be present only if qos2-Negotiated is present. +} + +NSAPI ::= INTEGER (0..15) +-- This type is used to indicate the Network layer Service Access Point + +TransactionId ::= OCTET STRING (SIZE (1..2)) +-- This type carries the value part of the transaction identifier which is used in the +-- session management messages on the access interface. The encoding is defined in +-- 3GPP TS 24.008 + +TEID ::= OCTET STRING (SIZE (4)) +-- This type carries the value part of the Tunnel Endpoint Identifier which is used to +-- distinguish between different tunnels between the same pair of entities which communicate +-- using the GPRS Tunnelling Protocol The encoding is defined in 3GPP TS 29.060. + +GPRSChargingID ::= OCTET STRING (SIZE (4)) +-- The Charging ID is a unique four octet value generated by the GGSN when +-- a PDP Context is activated. A Charging ID is generated for each activated context. +-- The encoding is defined in 3GPP TS 29.060. + +NotReachableReason ::= ENUMERATED { + msPurged (0), + imsiDetached (1), + restrictedArea (2), + notRegistered (3)} + +-- any time interrogation info types + +AnyTimeInterrogationArg ::= SEQUENCE { + subscriberIdentity [0] SubscriberIdentity, + requestedInfo [1] RequestedInfo, + gsmSCF-Address [3] ISDN-AddressString, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +AnyTimeInterrogationRes ::= SEQUENCE { + subscriberInfo SubscriberInfo, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +-- any time information handling types + +AnyTimeSubscriptionInterrogationArg ::= SEQUENCE { + subscriberIdentity [0] SubscriberIdentity, + requestedSubscriptionInfo [1] RequestedSubscriptionInfo, + gsmSCF-Address [2] ISDN-AddressString, + extensionContainer [3] ExtensionContainer OPTIONAL, + longFTN-Supported [4] NULL OPTIONAL, + ...} + +AnyTimeSubscriptionInterrogationRes ::= SEQUENCE { + callForwardingData [1] CallForwardingData OPTIONAL, + callBarringData [2] CallBarringData OPTIONAL, + odb-Info [3] ODB-Info OPTIONAL, + camel-SubscriptionInfo [4] CAMEL-SubscriptionInfo OPTIONAL, + supportedVLR-CAMEL-Phases [5] SupportedCamelPhases OPTIONAL, + supportedSGSN-CAMEL-Phases [6] SupportedCamelPhases OPTIONAL, + extensionContainer [7] ExtensionContainer OPTIONAL, + ... , + offeredCamel4CSIsInVLR [8] OfferedCamel4CSIs OPTIONAL, + offeredCamel4CSIsInSGSN [9] OfferedCamel4CSIs OPTIONAL, + msisdn-BS-List [10] MSISDN-BS-List OPTIONAL } + +RequestedSubscriptionInfo ::= SEQUENCE { + requestedSS-Info [1] SS-ForBS-Code OPTIONAL, + odb [2] NULL OPTIONAL, + requestedCAMEL-SubscriptionInfo [3] RequestedCAMEL-SubscriptionInfo OPTIONAL, + supportedVLR-CAMEL-Phases [4] NULL OPTIONAL, + supportedSGSN-CAMEL-Phases [5] NULL OPTIONAL, + extensionContainer [6] ExtensionContainer OPTIONAL, + ..., + additionalRequestedCAMEL-SubscriptionInfo + [7] AdditionalRequestedCAMEL-SubscriptionInfo + OPTIONAL, + msisdn-BS-List [8] NULL OPTIONAL } + +MSISDN-BS-List ::= SEQUENCE SIZE (1..maxNumOfMSISDN) OF + MSISDN-BS + +maxNumOfMSISDN INTEGER ::= 50 + + +MSISDN-BS ::= SEQUENCE { + msisdn ISDN-AddressString, + basicServiceList [0] BasicServiceList OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ...} + +RequestedCAMEL-SubscriptionInfo ::= ENUMERATED { + o-CSI (0), + t-CSI (1), + vt-CSI (2), + tif-CSI (3), + gprs-CSI (4), + mo-sms-CSI (5), + ss-CSI (6), + m-CSI (7), + d-csi (8)} + +AdditionalRequestedCAMEL-SubscriptionInfo ::= ENUMERATED { + mt-sms-CSI (0), + mg-csi (1), + o-IM-CSI (2), + d-IM-CSI (3), + vt-IM-CSI (4), + ...} +-- exception handling: unknown values shall be discarded by the receiver. + +CallForwardingData ::= SEQUENCE { + forwardingFeatureList Ext-ForwFeatureList, + notificationToCSE NULL OPTIONAL, + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +CallBarringData ::= SEQUENCE { + callBarringFeatureList Ext-CallBarFeatureList, + password Password OPTIONAL, + wrongPasswordAttemptsCounter WrongPasswordAttemptsCounter OPTIONAL, + notificationToCSE NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +WrongPasswordAttemptsCounter ::= INTEGER (0..4) + +ODB-Info ::= SEQUENCE { + odb-Data ODB-Data, + notificationToCSE NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +CAMEL-SubscriptionInfo ::= SEQUENCE { + o-CSI [0] O-CSI OPTIONAL, + o-BcsmCamelTDP-CriteriaList [1] O-BcsmCamelTDPCriteriaList OPTIONAL, + d-CSI [2] D-CSI OPTIONAL, + t-CSI [3] T-CSI OPTIONAL, + t-BCSM-CAMEL-TDP-CriteriaList [4] T-BCSM-CAMEL-TDP-CriteriaList OPTIONAL, + vt-CSI [5] T-CSI OPTIONAL, + vt-BCSM-CAMEL-TDP-CriteriaList [6] T-BCSM-CAMEL-TDP-CriteriaList OPTIONAL, + tif-CSI [7] NULL OPTIONAL, + tif-CSI-NotificationToCSE [8] NULL OPTIONAL, + gprs-CSI [9] GPRS-CSI OPTIONAL, + mo-sms-CSI [10] SMS-CSI OPTIONAL, + ss-CSI [11] SS-CSI OPTIONAL, + m-CSI [12] M-CSI OPTIONAL, + extensionContainer [13] ExtensionContainer OPTIONAL, + ..., + specificCSIDeletedList [14] SpecificCSI-Withdraw OPTIONAL, + mt-sms-CSI [15] SMS-CSI OPTIONAL, + mt-smsCAMELTDP-CriteriaList [16] MT-smsCAMELTDP-CriteriaList OPTIONAL, + mg-csi [17] MG-CSI OPTIONAL, + o-IM-CSI [18] O-CSI OPTIONAL, + o-IM-BcsmCamelTDP-CriteriaList [19] O-BcsmCamelTDPCriteriaList OPTIONAL, + d-IM-CSI [20] D-CSI OPTIONAL, + vt-IM-CSI [21] T-CSI OPTIONAL, + vt-IM-BCSM-CAMEL-TDP-CriteriaList [22] T-BCSM-CAMEL-TDP-CriteriaList OPTIONAL + } + +AnyTimeModificationArg ::= SEQUENCE { + subscriberIdentity [0] SubscriberIdentity, + gsmSCF-Address [1] ISDN-AddressString, + modificationRequestFor-CF-Info [2] ModificationRequestFor-CF-Info OPTIONAL, + modificationRequestFor-CB-Info [3] ModificationRequestFor-CB-Info OPTIONAL, + modificationRequestFor-CSI [4] ModificationRequestFor-CSI OPTIONAL, + extensionContainer [5] ExtensionContainer OPTIONAL, + longFTN-Supported [6] NULL OPTIONAL, + ..., + modificationRequestFor-ODB-data [7] ModificationRequestFor-ODB-data OPTIONAL, + modificationRequestFor-IP-SM-GW-Data [8] ModificationRequestFor-IP-SM-GW-Data OPTIONAL } + +AnyTimeModificationRes ::= SEQUENCE { + ss-InfoFor-CSE [0] Ext-SS-InfoFor-CSE OPTIONAL, + camel-SubscriptionInfo [1] CAMEL-SubscriptionInfo OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ..., + odb-Info [3] ODB-Info OPTIONAL } + +ModificationRequestFor-CF-Info ::= SEQUENCE { + ss-Code [0] SS-Code, + basicService [1] Ext-BasicServiceCode OPTIONAL, + ss-Status [2] Ext-SS-Status OPTIONAL, + forwardedToNumber [3] AddressString OPTIONAL, + forwardedToSubaddress [4] ISDN-SubaddressString OPTIONAL, + noReplyConditionTime [5] Ext-NoRepCondTime OPTIONAL, + modifyNotificationToCSE [6] ModificationInstruction OPTIONAL, + extensionContainer [7] ExtensionContainer OPTIONAL, + ...} + +ModificationRequestFor-CB-Info ::= SEQUENCE { + ss-Code [0] SS-Code, + basicService [1] Ext-BasicServiceCode OPTIONAL, + ss-Status [2] Ext-SS-Status OPTIONAL, + password [3] Password OPTIONAL, + wrongPasswordAttemptsCounter [4] WrongPasswordAttemptsCounter OPTIONAL, + modifyNotificationToCSE [5] ModificationInstruction OPTIONAL, + extensionContainer [6] ExtensionContainer OPTIONAL, + ...} + +ModificationRequestFor-ODB-data ::= SEQUENCE { + odb-data [0] ODB-Data OPTIONAL, + modifyNotificationToCSE [1] ModificationInstruction OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +ModificationRequestFor-CSI ::= SEQUENCE { + requestedCamel-SubscriptionInfo [0] RequestedCAMEL-SubscriptionInfo, + modifyNotificationToCSE [1] ModificationInstruction OPTIONAL, + modifyCSI-State [2] ModificationInstruction OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ..., + additionalRequestedCAMEL-SubscriptionInfo + [4] AdditionalRequestedCAMEL-SubscriptionInfo + OPTIONAL } +-- requestedCamel-SubscriptionInfo shall be discarded if +-- additionalRequestedCAMEL-SubscriptionInfo is received + +ModificationRequestFor-IP-SM-GW-Data ::= SEQUENCE { + modifyRegistrationStatus [0] ModificationInstruction OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ...} + +ModificationInstruction ::= ENUMERATED { + deactivate (0), + activate (1)} + +-- subscriber data modification notification types + +NoteSubscriberDataModifiedArg ::= SEQUENCE { + imsi IMSI, + msisdn ISDN-AddressString, + forwardingInfoFor-CSE [0] Ext-ForwardingInfoFor-CSE OPTIONAL, + callBarringInfoFor-CSE [1] Ext-CallBarringInfoFor-CSE OPTIONAL, + odb-Info [2] ODB-Info OPTIONAL, + camel-SubscriptionInfo [3] CAMEL-SubscriptionInfo OPTIONAL, + allInformationSent [4] NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +NoteSubscriberDataModifiedRes ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +-- mobility management event notificatioon info types + +NoteMM-EventArg::= SEQUENCE { + serviceKey ServiceKey, + eventMet [0] MM-Code, + imsi [1] IMSI, + msisdn [2] ISDN-AddressString, + locationInformation [3] LocationInformation OPTIONAL, + supportedCAMELPhases [5] SupportedCamelPhases OPTIONAL, + extensionContainer [6] ExtensionContainer OPTIONAL, + ..., + locationInformationGPRS [7] LocationInformationGPRS OPTIONAL, + offeredCamel4Functionalities [8] OfferedCamel4Functionalities OPTIONAL +} + +NoteMM-EventRes ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +Ext-SS-InfoFor-CSE ::= CHOICE { + forwardingInfoFor-CSE [0] Ext-ForwardingInfoFor-CSE, + callBarringInfoFor-CSE [1] Ext-CallBarringInfoFor-CSE + } + +Ext-ForwardingInfoFor-CSE ::= SEQUENCE { + ss-Code [0] SS-Code, + forwardingFeatureList [1] Ext-ForwFeatureList, + notificationToCSE [2] NULL OPTIONAL, + extensionContainer [3] ExtensionContainer OPTIONAL, + ...} + +Ext-CallBarringInfoFor-CSE ::= SEQUENCE { + ss-Code [0] SS-Code, + callBarringFeatureList [1] Ext-CallBarFeatureList, + password [2] Password OPTIONAL, + wrongPasswordAttemptsCounter [3] WrongPasswordAttemptsCounter OPTIONAL, + notificationToCSE [4] NULL OPTIONAL, + extensionContainer [5] ExtensionContainer OPTIONAL, + ...} + +END + diff --git a/rrlp-ephemeris/asn1/MAP-OM-DataTypes.asn b/rrlp-ephemeris/asn1/MAP-OM-DataTypes.asn new file mode 100644 index 000000000..024dd6f9b --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-OM-DataTypes.asn @@ -0,0 +1,216 @@ +-- $Id: MAP-OM-DataTypes.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 17.7.2 Operation and maintenance data types +-- 3GPP TS 29.002 V8.9.0 (2009-04) + +MAP-OM-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-OM-DataTypes (12) version11 (11)} + +DEFINITIONS + +IMPLICIT TAGS + +::= + +BEGIN + +EXPORTS + ActivateTraceModeArg, + ActivateTraceModeRes, + DeactivateTraceModeArg, + DeactivateTraceModeRes, + TracePropagationList +; + +IMPORTS + AddressString, + IMSI +FROM MAP-CommonDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-CommonDataTypes (18) version11 (11)} + + ExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} + +; + +ActivateTraceModeArg ::= SEQUENCE { + imsi [0] IMSI OPTIONAL, + traceReference [1] TraceReference, + traceType [2] TraceType, + omc-Id [3] AddressString OPTIONAL, + extensionContainer [4] ExtensionContainer OPTIONAL, + ..., + traceReference2 [5] TraceReference2 OPTIONAL, + traceDepthList [6] TraceDepthList OPTIONAL, + traceNE-TypeList [7] TraceNE-TypeList OPTIONAL, + traceInterfaceList [8] TraceInterfaceList OPTIONAL, + traceEventList [9] TraceEventList OPTIONAL + } + +TraceReference ::= OCTET STRING (SIZE (1..2)) + +TraceReference2 ::= OCTET STRING (SIZE (3)) + +TraceRecordingSessionReference ::= OCTET STRING (SIZE (2)) + +TraceType ::= INTEGER + (0..255) + -- Trace types are fully defined in 3GPP TS 52.008. [61] + +TraceDepthList ::= SEQUENCE { + msc-s-TraceDepth [0] TraceDepth OPTIONAL, + mgw-TraceDepth [1] TraceDepth OPTIONAL, + sgsn-TraceDepth [2] TraceDepth OPTIONAL, + ggsn-TraceDepth [3] TraceDepth OPTIONAL, + rnc-TraceDepth [4] TraceDepth OPTIONAL, + bmsc-TraceDepth [5] TraceDepth OPTIONAL, + ...} + +TraceDepth ::= ENUMERATED { + minimum (0), + medium (1), + maximum (2), + ...} +-- The value medium is applicable only for RNC. For other network elements, if value medium +-- is received, value minimum shall be applied. + +TraceNE-TypeList ::= BIT STRING { + msc-s (0), + mgw (1), + sgsn (2), + ggsn (3), + rnc (4), + bm-sc (5)} (SIZE (6..16)) +-- Other bits than listed above shall be discarded. + +TraceInterfaceList ::= SEQUENCE { + msc-s-List [0] MSC-S-InterfaceList OPTIONAL, + mgw-List [1] MGW-InterfaceList OPTIONAL, + sgsn-List [2] SGSN-InterfaceList OPTIONAL, + ggsn-List [3] GGSN-InterfaceList OPTIONAL, + rnc-List [4] RNC-InterfaceList OPTIONAL, + bmsc-List [5] BMSC-InterfaceList OPTIONAL, + ...} + +MSC-S-InterfaceList ::= BIT STRING { + a (0), + iu (1), + mc (2), + map-g (3), + map-b (4), + map-e (5), + map-f (6), + cap (7), + map-d (8), + map-c (9)} (SIZE (10..16)) +-- Other bits than listed above shall be discarded. + +MGW-InterfaceList ::= BIT STRING { + mc (0), + nb-up (1), + iu-up (2)} (SIZE (3..8)) +-- Other bits than listed above shall be discarded. + +SGSN-InterfaceList ::= BIT STRING { + gb (0), + iu (1), + gn (2), + map-gr (3), + map-gd (4), + map-gf (5), + gs (6), + ge (7)} (SIZE (8..16)) +-- Other bits than listed above shall be discarded. + +GGSN-InterfaceList ::= BIT STRING { + gn (0), + gi (1), + gmb (2)} (SIZE (3..8)) +-- Other bits than listed above shall be discarded. + +RNC-InterfaceList ::= BIT STRING { + iu (0), + iur (1), + iub (2), + uu (3)} (SIZE (4..8)) +-- Other bits than listed above shall be discarded. + +BMSC-InterfaceList ::= BIT STRING { + gmb (0)} (SIZE (1..8)) +-- Other bits than listed above shall be discarded. + +TraceEventList ::= SEQUENCE { + msc-s-List [0] MSC-S-EventList OPTIONAL, + mgw-List [1] MGW-EventList OPTIONAL, + sgsn-List [2] SGSN-EventList OPTIONAL, + ggsn-List [3] GGSN-EventList OPTIONAL, + bmsc-List [4] BMSC-EventList OPTIONAL, + ...} + +MSC-S-EventList ::= BIT STRING { + mo-mtCall (0), + mo-mt-sms (1), + lu-imsiAttach-imsiDetach (2), + handovers (3), + ss (4)} (SIZE (5..16)) +-- Other bits than listed above shall be discarded. + +MGW-EventList ::= BIT STRING { + context (0)} (SIZE (1..8)) +-- Other bits than listed above shall be discarded. + +SGSN-EventList ::= BIT STRING { + pdpContext (0), + mo-mt-sms (1), + rau-gprsAttach-gprsDetach (2), + mbmsContext (3)} (SIZE (4..16)) +-- Other bits than listed above shall be discarded. + +GGSN-EventList ::= BIT STRING { + pdpContext (0), + mbmsContext (1)} (SIZE (2..8)) +-- Other bits than listed above shall be discarded. + +BMSC-EventList ::= BIT STRING { + mbmsMulticastServiceActivation (0)} (SIZE (1..8)) +-- Other bits than listed above shall be discarded. + + +TracePropagationList ::= SEQUENCE { + traceReference [0] TraceReference OPTIONAL, + traceType [1] TraceType OPTIONAL, + traceReference2 [2] TraceReference2 OPTIONAL, + traceRecordingSessionReference [3] TraceRecordingSessionReference OPTIONAL, + rnc-TraceDepth [4] TraceDepth OPTIONAL, + rnc-InterfaceList [5] RNC-InterfaceList OPTIONAL, + msc-s-TraceDepth [6] TraceDepth OPTIONAL, + msc-s-InterfaceList [7] MSC-S-InterfaceList OPTIONAL, + msc-s-EventList [8] MSC-S-EventList OPTIONAL, + mgw-TraceDepth [9] TraceDepth OPTIONAL, + mgw-InterfaceList [10] MGW-InterfaceList OPTIONAL, + mgw-EventList [11] MGW-EventList OPTIONAL, + ...} + +ActivateTraceModeRes ::= SEQUENCE { + extensionContainer [0] ExtensionContainer OPTIONAL, + ..., + traceSupportIndicator [1] NULL OPTIONAL + } + +DeactivateTraceModeArg ::= SEQUENCE { + imsi [0] IMSI OPTIONAL, + traceReference [1] TraceReference, + extensionContainer [2] ExtensionContainer OPTIONAL, + ..., + traceReference2 [3] TraceReference2 OPTIONAL + } + +DeactivateTraceModeRes ::= SEQUENCE { + extensionContainer [0] ExtensionContainer OPTIONAL, + ...} + +END + diff --git a/rrlp-ephemeris/asn1/MAP-SM-DataTypes.asn b/rrlp-ephemeris/asn1/MAP-SM-DataTypes.asn new file mode 100644 index 000000000..0ef941f2b --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-SM-DataTypes.asn @@ -0,0 +1,270 @@ +-- $Id: MAP-SM-DataTypes.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.6 Short message data types + +MAP-SM-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SM-DataTypes (16) version11 (11)} + +DEFINITIONS + +IMPLICIT TAGS + +::= + +BEGIN + +EXPORTS + RoutingInfoForSM-Arg, + RoutingInfoForSM-Res, + MO-ForwardSM-Arg, + MO-ForwardSM-Res, + MT-ForwardSM-Arg, + MT-ForwardSM-Res, + ReportSM-DeliveryStatusArg, + ReportSM-DeliveryStatusRes, + AlertServiceCentreArg, + InformServiceCentreArg, + ReadyForSM-Arg, + ReadyForSM-Res, + SM-DeliveryOutcome, + AlertReason, + Additional-Number, + MT-ForwardSM-VGCS-Arg, + MT-ForwardSM-VGCS-Res +; + +IMPORTS + AddressString, + ISDN-AddressString, + SignalInfo, + IMSI, + LMSI, + ASCI-CallReference + +FROM MAP-CommonDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-CommonDataTypes (18) version11 (11)} + + AbsentSubscriberDiagnosticSM +FROM MAP-ER-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ER-DataTypes (17) version11 (11)} + + ExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} +; + + +RoutingInfoForSM-Arg ::= SEQUENCE { + msisdn [0] ISDN-AddressString, + sm-RP-PRI [1] BOOLEAN, + serviceCentreAddress [2] AddressString, + extensionContainer [6] ExtensionContainer OPTIONAL, + ... , + gprsSupportIndicator [7] NULL OPTIONAL, + -- gprsSupportIndicator is set only if the SMS-GMSC supports + -- receiving of two numbers from the HLR + sm-RP-MTI [8] SM-RP-MTI OPTIONAL, + sm-RP-SMEA [9] SM-RP-SMEA OPTIONAL, + sm-deliveryNotIntended [10] SM-DeliveryNotIntended OPTIONAL } + +SM-DeliveryNotIntended ::= ENUMERATED { + onlyIMSI-requested (0), + onlyMCC-MNC-requested (1), + ...} + +SM-RP-MTI ::= INTEGER (0..10) + -- 0 SMS Deliver + -- 1 SMS Status Report + -- other values are reserved for future use and shall be discarded if + -- received + +SM-RP-SMEA ::= OCTET STRING (SIZE (1..12)) + -- this parameter contains an address field which is encoded + -- as defined in 3GPP TS 23.040. An address field contains 3 elements : + -- address-length + -- type-of-address + -- address-value + +RoutingInfoForSM-Res ::= SEQUENCE { + imsi IMSI, + locationInfoWithLMSI [0] LocationInfoWithLMSI, + extensionContainer [4] ExtensionContainer OPTIONAL, + ...} + +LocationInfoWithLMSI ::= SEQUENCE { + networkNode-Number [1] ISDN-AddressString, + lmsi LMSI OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + gprsNodeIndicator [5] NULL OPTIONAL, + -- gprsNodeIndicator is set only if the SGSN number is sent as the + -- Network Node Number + additional-Number [6] Additional-Number OPTIONAL + -- NetworkNode-number can be either msc-number or sgsn-number or IP-SM-GW + -- number or SMS Router number + } + +Additional-Number ::= CHOICE { + msc-Number [0] ISDN-AddressString, + sgsn-Number [1] ISDN-AddressString} + -- additional-number can be either msc-number or sgsn-number + -- if received networkNode-number is msc-number then the + -- additional number is sgsn-number + -- if received networkNode-number is sgsn-number then the + -- additional number is msc-number + +MO-ForwardSM-Arg ::= SEQUENCE { + sm-RP-DA SM-RP-DA, + sm-RP-OA SM-RP-OA, + sm-RP-UI SignalInfo, + extensionContainer ExtensionContainer OPTIONAL, + ... , + imsi IMSI OPTIONAL } + +MO-ForwardSM-Res ::= SEQUENCE { + sm-RP-UI SignalInfo OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +MT-ForwardSM-Arg ::= SEQUENCE { + sm-RP-DA SM-RP-DA, + sm-RP-OA SM-RP-OA, + sm-RP-UI SignalInfo, + moreMessagesToSend NULL OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +MT-ForwardSM-Res ::= SEQUENCE { + sm-RP-UI SignalInfo OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +SM-RP-DA ::= CHOICE { + imsi [0] IMSI, + lmsi [1] LMSI, + serviceCentreAddressDA [4] AddressString, + noSM-RP-DA [5] NULL} + +SM-RP-OA ::= CHOICE { + msisdn [2] ISDN-AddressString, + serviceCentreAddressOA [4] AddressString, + noSM-RP-OA [5] NULL} + +ReportSM-DeliveryStatusArg ::= SEQUENCE { + msisdn ISDN-AddressString, + serviceCentreAddress AddressString, + sm-DeliveryOutcome SM-DeliveryOutcome, + absentSubscriberDiagnosticSM [0] AbsentSubscriberDiagnosticSM + OPTIONAL, + extensionContainer [1] ExtensionContainer OPTIONAL, + ..., + gprsSupportIndicator [2] NULL OPTIONAL, + -- gprsSupportIndicator is set only if the SMS-GMSC supports + -- handling of two delivery outcomes + deliveryOutcomeIndicator [3] NULL OPTIONAL, + -- DeliveryOutcomeIndicator is set when the SM-DeliveryOutcome + -- is for GPRS + additionalSM-DeliveryOutcome [4] SM-DeliveryOutcome OPTIONAL, + -- If received, additionalSM-DeliveryOutcome is for GPRS + -- If DeliveryOutcomeIndicator is set, then AdditionalSM-DeliveryOutcome shall be absent + additionalAbsentSubscriberDiagnosticSM [5] AbsentSubscriberDiagnosticSM OPTIONAL, + -- If received additionalAbsentSubscriberDiagnosticSM is for GPRS + -- If DeliveryOutcomeIndicator is set, then AdditionalAbsentSubscriberDiagnosticSM + -- shall be absent + ip-sm-gw-Indicator [6] NULL OPTIONAL, + -- the ip-sm-gw indicator indicates by its presence that sm-deliveryOutcome + -- is for delivery via IMS + -- If present, deliveryOutcomeIndicator shall be absent. + ip-sm-gw-sm-deliveryOutcome [7] SM-DeliveryOutcome OPTIONAL, + -- If received ip-sm-gw-sm-deliveryOutcome is for delivery via IMS + -- If ip-sm-gw-Indicator is set, then ip-sm-gw-sm-deliveryOutcome shall be absent + ip-sm-gw-absentSubscriberDiagnosticSM [8] AbsentSubscriberDiagnosticSM OPTIONAL + -- If received ip-sm-gw-sm-absentSubscriberDiagnosticSM is for delivery via IMS + -- If ip-sm-gw-Indicator is set, then ip-sm-gw-sm-absentSubscriberDiagnosticSM + -- shall be absent +} + +SM-DeliveryOutcome ::= ENUMERATED { + memoryCapacityExceeded (0), + absentSubscriber (1), + successfulTransfer (2)} + +ReportSM-DeliveryStatusRes ::= SEQUENCE { + storedMSISDN ISDN-AddressString OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +AlertServiceCentreArg ::= SEQUENCE { + msisdn ISDN-AddressString, + serviceCentreAddress AddressString, + ...} + +InformServiceCentreArg ::= SEQUENCE { + storedMSISDN ISDN-AddressString OPTIONAL, + mw-Status MW-Status OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ... , + absentSubscriberDiagnosticSM AbsentSubscriberDiagnosticSM OPTIONAL, + additionalAbsentSubscriberDiagnosticSM [0] AbsentSubscriberDiagnosticSM OPTIONAL } + -- additionalAbsentSubscriberDiagnosticSM may be present only if + -- absentSubscriberDiagnosticSM is present. + -- if included, additionalAbsentSubscriberDiagnosticSM is for GPRS and + -- absentSubscriberDiagnosticSM is for non-GPRS + +MW-Status ::= BIT STRING { + sc-AddressNotIncluded (0), + mnrf-Set (1), + mcef-Set (2) , + mnrg-Set (3)} (SIZE (6..16)) + -- exception handling: + -- bits 4 to 15 shall be ignored if received and not understood + +ReadyForSM-Arg ::= SEQUENCE { + imsi [0] IMSI, + alertReason AlertReason, + alertReasonIndicator NULL OPTIONAL, + -- alertReasonIndicator is set only when the alertReason + -- sent to HLR is for GPRS + extensionContainer ExtensionContainer OPTIONAL, + ..., + additionalAlertReasonIndicator [1] NULL OPTIONAL + -- additionalAlertReasonIndicator is set only when the alertReason + -- sent to HLR is for IP-SM-GW + } + +ReadyForSM-Res ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ...} + +AlertReason ::= ENUMERATED { + ms-Present (0), + memoryAvailable (1)} + +MT-ForwardSM-VGCS-Arg ::= SEQUENCE { + asciCallReference ASCI-CallReference, + sm-RP-OA SM-RP-OA, + sm-RP-UI SignalInfo, + extensionContainer ExtensionContainer OPTIONAL, + ...} + +MT-ForwardSM-VGCS-Res ::= SEQUENCE { + sm-RP-UI [0] SignalInfo OPTIONAL, + dispatcherList [1] DispatcherList OPTIONAL, + ongoingCall NULL OPTIONAL, + extensionContainer [2] ExtensionContainer OPTIONAL, + ...} + +DispatcherList ::= + SEQUENCE SIZE (1..maxNumOfDispatchers) OF + ISDN-AddressString + +maxNumOfDispatchers INTEGER ::= 5 + + + +END + diff --git a/rrlp-ephemeris/asn1/MAP-SS-Code.asn b/rrlp-ephemeris/asn1/MAP-SS-Code.asn new file mode 100644 index 000000000..163f2dc3a --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-SS-Code.asn @@ -0,0 +1,190 @@ +-- $Id: MAP-SS-Code.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.5 Supplementary service codes + +MAP-SS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-Code (15) version11 (11)} + +DEFINITIONS + +::= + +BEGIN + +SS-Code ::= OCTET STRING (SIZE (1)) + -- This type is used to represent the code identifying a single + -- supplementary service, a group of supplementary services, or + -- all supplementary services. The services and abbreviations + -- used are defined in TS 3GPP TS 22.004 [5]. The internal structure is + -- defined as follows: + -- + -- bits 87654321: group (bits 8765), and specific service + -- (bits 4321) + +allSS SS-Code ::= '00000000'B + -- reserved for possible future use + -- all SS + +allLineIdentificationSS SS-Code ::= '00010000'B + -- reserved for possible future use + -- all line identification SS +clip SS-Code ::= '00010001'B + -- calling line identification presentation +clir SS-Code ::= '00010010'B + -- calling line identification restriction +colp SS-Code ::= '00010011'B + -- connected line identification presentation +colr SS-Code ::= '00010100'B + -- connected line identification restriction +mci SS-Code ::= '00010101'B + -- reserved for possible future use + -- malicious call identification + +allNameIdentificationSS SS-Code ::= '00011000'B + -- all name identification SS +cnap SS-Code ::= '00011001'B + -- calling name presentation + + -- SS-Codes '00011010'B to '00011111'B are reserved for future + -- NameIdentification Supplementary Service use. + +allForwardingSS SS-Code ::= '00100000'B + -- all forwarding SS +cfu SS-Code ::= '00100001'B + -- call forwarding unconditional +allCondForwardingSS SS-Code ::= '00101000'B + -- all conditional forwarding SS +cfb SS-Code ::= '00101001'B + -- call forwarding on mobile subscriber busy +cfnry SS-Code ::= '00101010'B + -- call forwarding on no reply +cfnrc SS-Code ::= '00101011'B + -- call forwarding on mobile subscriber not reachable +cd SS-Code ::= '00100100'B + -- call deflection + +allCallOfferingSS SS-Code ::= '00110000'B + -- reserved for possible future use + -- all call offering SS includes also all forwarding SS +ect SS-Code ::= '00110001'B + -- explicit call transfer +mah SS-Code ::= '00110010'B + -- reserved for possible future use + -- mobile access hunting + +allCallCompletionSS SS-Code ::= '01000000'B + -- reserved for possible future use + -- all Call completion SS +cw SS-Code ::= '01000001'B + -- call waiting +hold SS-Code ::= '01000010'B + -- call hold +ccbs-A SS-Code ::= '01000011'B + -- completion of call to busy subscribers, originating side + -- this SS-Code is used only in InsertSubscriberData, DeleteSubscriberData + -- and InterrogateSS +ccbs-B SS-Code ::= '01000100'B + -- completion of call to busy subscribers, destination side + -- this SS-Code is used only in InsertSubscriberData and DeleteSubscriberData +mc SS-Code ::= '01000101'B + -- multicall + +allMultiPartySS SS-Code ::= '01010000'B + -- reserved for possible future use + -- all multiparty SS +multiPTY SS-Code ::= '01010001'B + -- multiparty + +allCommunityOfInterest-SS SS-Code ::= '01100000'B + -- reserved for possible future use + -- all community of interest SS +cug SS-Code ::= '01100001'B + -- closed user group + +allChargingSS SS-Code ::= '01110000'B + -- reserved for possible future use + -- all charging SS +aoci SS-Code ::= '01110001'B + -- advice of charge information +aocc SS-Code ::= '01110010'B + -- advice of charge charging + +allAdditionalInfoTransferSS SS-Code ::= '10000000'B + -- reserved for possible future use + -- all additional information transfer SS +uus1 SS-Code ::= '10000001'B + -- UUS1 user-to-user signalling +uus2 SS-Code ::= '10000010'B + -- UUS2 user-to-user signalling +uus3 SS-Code ::= '10000011'B + -- UUS3 user-to-user signalling + +allBarringSS SS-Code ::= '10010000'B + -- all barring SS +barringOfOutgoingCalls SS-Code ::= '10010001'B + -- barring of outgoing calls +baoc SS-Code ::= '10010010'B + -- barring of all outgoing calls +boic SS-Code ::= '10010011'B + -- barring of outgoing international calls +boicExHC SS-Code ::= '10010100'B + -- barring of outgoing international calls except those directed + -- to the home PLMN Country +barringOfIncomingCalls SS-Code ::= '10011001'B + -- barring of incoming calls +baic SS-Code ::= '10011010'B + -- barring of all incoming calls +bicRoam SS-Code ::= '10011011'B + -- barring of incoming calls when roaming outside home PLMN + -- Country + +allPLMN-specificSS SS-Code ::= '11110000'B +plmn-specificSS-1 SS-Code ::= '11110001'B +plmn-specificSS-2 SS-Code ::= '11110010'B +plmn-specificSS-3 SS-Code ::= '11110011'B +plmn-specificSS-4 SS-Code ::= '11110100'B +plmn-specificSS-5 SS-Code ::= '11110101'B +plmn-specificSS-6 SS-Code ::= '11110110'B +plmn-specificSS-7 SS-Code ::= '11110111'B +plmn-specificSS-8 SS-Code ::= '11111000'B +plmn-specificSS-9 SS-Code ::= '11111001'B +plmn-specificSS-A SS-Code ::= '11111010'B +plmn-specificSS-B SS-Code ::= '11111011'B +plmn-specificSS-C SS-Code ::= '11111100'B +plmn-specificSS-D SS-Code ::= '11111101'B +plmn-specificSS-E SS-Code ::= '11111110'B +plmn-specificSS-F SS-Code ::= '11111111'B + +allCallPrioritySS SS-Code ::= '10100000'B + -- reserved for possible future use + -- all call priority SS +emlpp SS-Code ::= '10100001'B + -- enhanced Multilevel Precedence Pre-emption (EMLPP) service + +allLCSPrivacyException SS-Code ::= '10110000'B + -- all LCS Privacy Exception Classes +universal SS-Code ::= '10110001'B + -- allow location by any LCS client +callSessionRelated SS-Code ::= '10110010'B + -- allow location by any value added LCS client to which a call/session + -- is established from the target MS +callSessionUnrelated SS-Code ::= '10110011'B + -- allow location by designated external value added LCS clients +plmnoperator SS-Code ::= '10110100'B + -- allow location by designated PLMN operator LCS clients +serviceType SS-Code ::= '10110101'B + -- allow location by LCS clients of a designated LCS service type + +allMOLR-SS SS-Code ::= '11000000'B + -- all Mobile Originating Location Request Classes +basicSelfLocation SS-Code ::= '11000001'B + -- allow an MS to request its own location +autonomousSelfLocation SS-Code ::= '11000010'B + -- allow an MS to perform self location without interaction + -- with the PLMN for a predetermined period of time +transferToThirdParty SS-Code ::= '11000011'B + -- allow an MS to request transfer of its location to another LCS client + +END + diff --git a/rrlp-ephemeris/asn1/MAP-SS-DataTypes.asn b/rrlp-ephemeris/asn1/MAP-SS-DataTypes.asn new file mode 100644 index 000000000..253f7f01a --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-SS-DataTypes.asn @@ -0,0 +1,342 @@ +-- $Id: MAP-SS-DataTypes.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.4 Supplementary service data types + +MAP-SS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-DataTypes (14) version11 (11)} + +DEFINITIONS + +IMPLICIT TAGS + +::= + +BEGIN + +EXPORTS + RegisterSS-Arg, + SS-Info, + SS-Status, + SS-SubscriptionOption, + SS-ForBS-Code, + InterrogateSS-Res, + USSD-Arg, + USSD-Res, + USSD-DataCodingScheme, + USSD-String, + Password, + GuidanceInfo, + SS-List, + SS-InfoList, + OverrideCategory, + CliRestrictionOption, + NoReplyConditionTime, + ForwardingOptions, + maxNumOfSS, + SS-Data, + SS-InvocationNotificationArg, + SS-InvocationNotificationRes, + CCBS-Feature, + RegisterCC-EntryArg, + RegisterCC-EntryRes, + EraseCC-EntryArg, + EraseCC-EntryRes +; + +IMPORTS + AddressString, + ISDN-AddressString, + ISDN-SubaddressString, + FTN-AddressString, + IMSI, + BasicServiceCode, + AlertingPattern, + EMLPP-Priority, + MaxMC-Bearers, + MC-Bearers, + ExternalSignalInfo +FROM MAP-CommonDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-CommonDataTypes (18) version11 (11)} + + ExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} + + SS-Code +FROM MAP-SS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-SS-Code (15) version11 (11)} +; + + +RegisterSS-Arg ::= SEQUENCE { + ss-Code SS-Code, + basicService BasicServiceCode OPTIONAL, + forwardedToNumber [4] AddressString OPTIONAL, + forwardedToSubaddress [6] ISDN-SubaddressString OPTIONAL, + noReplyConditionTime [5] NoReplyConditionTime OPTIONAL, + ..., + defaultPriority [7] EMLPP-Priority OPTIONAL, + nbrUser [8] MC-Bearers OPTIONAL, + longFTN-Supported [9] NULL OPTIONAL } + +NoReplyConditionTime ::= INTEGER (5..30) + +SS-Info ::= CHOICE { + forwardingInfo [0] ForwardingInfo, + callBarringInfo [1] CallBarringInfo, + ss-Data [3] SS-Data} + +ForwardingInfo ::= SEQUENCE { + ss-Code SS-Code OPTIONAL, + forwardingFeatureList ForwardingFeatureList, + ...} + +ForwardingFeatureList ::= + SEQUENCE SIZE (1..maxNumOfBasicServiceGroups) OF + ForwardingFeature + +ForwardingFeature ::= SEQUENCE { + basicService BasicServiceCode OPTIONAL, + ss-Status [4] SS-Status OPTIONAL, + forwardedToNumber [5] ISDN-AddressString OPTIONAL, + forwardedToSubaddress [8] ISDN-SubaddressString OPTIONAL, + forwardingOptions [6] ForwardingOptions OPTIONAL, + noReplyConditionTime [7] NoReplyConditionTime OPTIONAL, + ..., + longForwardedToNumber [9] FTN-AddressString OPTIONAL } + +SS-Status ::= OCTET STRING (SIZE (1)) + + -- bits 8765: 0000 (unused) + -- bits 4321: Used to convey the "P bit","R bit","A bit" and "Q bit", + -- representing supplementary service state information + -- as defined in TS 3GPP TS 23.011 [22] + + -- bit 4: "Q bit" + + -- bit 3: "P bit" + + -- bit 2: "R bit" + + -- bit 1: "A bit" + +ForwardingOptions ::= OCTET STRING (SIZE (1)) + + -- bit 8: notification to forwarding party + -- 0 no notification + -- 1 notification + + -- bit 7: redirecting presentation + -- 0 no presentation + -- 1 presentation + + -- bit 6: notification to calling party + -- 0 no notification + -- 1 notification + + -- bit 5: 0 (unused) + + -- bits 43: forwarding reason + -- 00 ms not reachable + -- 01 ms busy + -- 10 no reply + -- 11 unconditional when used in a SRI Result, + -- or call deflection when used in a RCH Argument + -- bits 21: 00 (unused) + +CallBarringInfo ::= SEQUENCE { + ss-Code SS-Code OPTIONAL, + callBarringFeatureList CallBarringFeatureList, + ...} + +CallBarringFeatureList ::= SEQUENCE SIZE (1..maxNumOfBasicServiceGroups) OF + CallBarringFeature + +CallBarringFeature ::= SEQUENCE { + basicService BasicServiceCode OPTIONAL, + ss-Status [4] SS-Status OPTIONAL, + ...} + +SS-Data ::= SEQUENCE { + ss-Code SS-Code OPTIONAL, + ss-Status [4] SS-Status OPTIONAL, + ss-SubscriptionOption SS-SubscriptionOption OPTIONAL, + basicServiceGroupList BasicServiceGroupList OPTIONAL, + ..., + defaultPriority EMLPP-Priority OPTIONAL, + nbrUser [5] MC-Bearers OPTIONAL + } + +SS-SubscriptionOption ::= CHOICE { + cliRestrictionOption [2] CliRestrictionOption, + overrideCategory [1] OverrideCategory} + +CliRestrictionOption ::= ENUMERATED { + permanent (0), + temporaryDefaultRestricted (1), + temporaryDefaultAllowed (2)} + +OverrideCategory ::= ENUMERATED { + overrideEnabled (0), + overrideDisabled (1)} + +SS-ForBS-Code ::= SEQUENCE { + ss-Code SS-Code, + basicService BasicServiceCode OPTIONAL, + ..., + longFTN-Supported [4] NULL OPTIONAL } + +GenericServiceInfo ::= SEQUENCE { + ss-Status SS-Status, + cliRestrictionOption CliRestrictionOption OPTIONAL, + ..., + maximumEntitledPriority [0] EMLPP-Priority OPTIONAL, + defaultPriority [1] EMLPP-Priority OPTIONAL, + ccbs-FeatureList [2] CCBS-FeatureList OPTIONAL, + nbrSB [3] MaxMC-Bearers OPTIONAL, + nbrUser [4] MC-Bearers OPTIONAL, + nbrSN [5] MC-Bearers OPTIONAL } + +CCBS-FeatureList ::= SEQUENCE SIZE (1..maxNumOfCCBS-Requests) OF + CCBS-Feature + +maxNumOfCCBS-Requests INTEGER ::= 5 + +CCBS-Feature ::= SEQUENCE { + ccbs-Index [0] CCBS-Index OPTIONAL, + b-subscriberNumber [1] ISDN-AddressString OPTIONAL, + b-subscriberSubaddress [2] ISDN-SubaddressString OPTIONAL, + basicServiceGroup [3] BasicServiceCode OPTIONAL, + ...} + +CCBS-Index ::= INTEGER (1..maxNumOfCCBS-Requests) + +InterrogateSS-Res ::= CHOICE { + ss-Status [0] SS-Status, + basicServiceGroupList [2] BasicServiceGroupList, + forwardingFeatureList [3] ForwardingFeatureList, + genericServiceInfo [4] GenericServiceInfo } + +USSD-Arg ::= SEQUENCE { + ussd-DataCodingScheme USSD-DataCodingScheme, + ussd-String USSD-String, + ... , + alertingPattern AlertingPattern OPTIONAL, + msisdn [0] ISDN-AddressString OPTIONAL } + +USSD-Res ::= SEQUENCE { + ussd-DataCodingScheme USSD-DataCodingScheme, + ussd-String USSD-String, + ...} + +USSD-DataCodingScheme ::= OCTET STRING (SIZE (1)) + -- The structure of the USSD-DataCodingScheme is defined by + -- the Cell Broadcast Data Coding Scheme as described in + -- TS 3GPP TS 23.038 [25] + +USSD-String ::= OCTET STRING (SIZE (1..maxUSSD-StringLength)) + -- The structure of the contents of the USSD-String is dependent + -- on the USSD-DataCodingScheme as described in TS 3GPP TS 23.038 [25]. + +maxUSSD-StringLength INTEGER ::= 160 + +Password ::= NumericString + (FROM ("0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9")) + (SIZE (4)) + +GuidanceInfo ::= ENUMERATED { + enterPW (0), + enterNewPW (1), + enterNewPW-Again (2)} + -- How this information is really delivered to the subscriber + -- (display, announcement, ...) is not part of this + -- specification. + +SS-List ::= SEQUENCE SIZE (1..maxNumOfSS) OF + SS-Code + +maxNumOfSS INTEGER ::= 30 + +SS-InfoList ::= SEQUENCE SIZE (1..maxNumOfSS) OF + SS-Info + +BasicServiceGroupList ::= SEQUENCE SIZE (1..maxNumOfBasicServiceGroups) OF + BasicServiceCode + +maxNumOfBasicServiceGroups INTEGER ::= 13 + +SS-InvocationNotificationArg ::= SEQUENCE { + imsi [0] IMSI, + msisdn [1] ISDN-AddressString, + ss-Event [2] SS-Code, + -- The following SS-Code values are allowed : + -- ect SS-Code ::= '00110001'B + -- multiPTY SS-Code ::= '01010001'B + -- cd SS-Code ::= '00100100'B + -- ccbs SS-Code ::= '01000100'B + ss-EventSpecification [3] SS-EventSpecification OPTIONAL, + extensionContainer [4] ExtensionContainer OPTIONAL, + ..., + b-subscriberNumber [5] ISDN-AddressString OPTIONAL, + ccbs-RequestState [6] CCBS-RequestState OPTIONAL + } + +CCBS-RequestState ::= ENUMERATED { + request (0), + recall (1), + active (2), + completed (3), + suspended (4), + frozen (5), + deleted (6) + } + +SS-InvocationNotificationRes ::= SEQUENCE { + extensionContainer ExtensionContainer OPTIONAL, + ... + } + +SS-EventSpecification ::= SEQUENCE SIZE (1..maxEventSpecification) OF + AddressString + +maxEventSpecification INTEGER ::= 2 + +RegisterCC-EntryArg ::= SEQUENCE { + ss-Code [0] SS-Code, + ccbs-Data [1] CCBS-Data OPTIONAL, + ...} + +CCBS-Data ::= SEQUENCE { + ccbs-Feature [0] CCBS-Feature, + translatedB-Number [1] ISDN-AddressString, + serviceIndicator [2] ServiceIndicator OPTIONAL, + callInfo [3] ExternalSignalInfo, + networkSignalInfo [4] ExternalSignalInfo, + ...} + +ServiceIndicator ::= BIT STRING { + clir-invoked (0), + camel-invoked (1)} (SIZE(2..32)) + -- exception handling: + -- bits 2 to 31 shall be ignored if received and not understood + +RegisterCC-EntryRes ::= SEQUENCE { + ccbs-Feature [0] CCBS-Feature OPTIONAL, + ...} + +EraseCC-EntryArg ::= SEQUENCE { + ss-Code [0] SS-Code, + ccbs-Index [1] CCBS-Index OPTIONAL, + ...} + +EraseCC-EntryRes ::= SEQUENCE { + ss-Code [0] SS-Code, + ss-Status [1] SS-Status OPTIONAL, + ...} + +END + diff --git a/rrlp-ephemeris/asn1/MAP-TS-Code.asn b/rrlp-ephemeris/asn1/MAP-TS-Code.asn new file mode 100644 index 000000000..5ac00bff2 --- /dev/null +++ b/rrlp-ephemeris/asn1/MAP-TS-Code.asn @@ -0,0 +1,92 @@ +-- $Id: MAP-TS-Code.asn 28149 2009-04-25 17:45:34Z etxrab $ +-- 3GPP TS 29.002 V8.9.0 (2009-04) +-- 17.7.9 Teleservice Codes + +MAP-TS-Code { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-TS-Code (19) version11 (11)} + +DEFINITIONS + +::= + +BEGIN + +TeleserviceCode ::= OCTET STRING (SIZE (1)) + -- This type is used to represent the code identifying a single + -- teleservice, a group of teleservices, or all teleservices. The + -- services are defined in TS GSM 22.003 [4]. + -- The internal structure is defined as follows: + + -- bits 87654321: group (bits 8765) and specific service + -- (bits 4321) + +Ext-TeleserviceCode ::= OCTET STRING (SIZE (1..5)) + -- This type is used to represent the code identifying a single + -- teleservice, a group of teleservices, or all teleservices. The + -- services are defined in TS GSM 22.003 [4]. + -- The internal structure is defined as follows: + + -- OCTET 1: + -- bits 87654321: group (bits 8765) and specific service + -- (bits 4321) + + -- OCTETS 2-5: reserved for future use. If received the + -- Ext-TeleserviceCode shall be + -- treated according to the exception handling defined for the + -- operation that uses this type. + + -- Ext-TeleserviceCode includes all values defined for TeleserviceCode. + +allTeleservices TeleserviceCode ::= '00000000'B + +allSpeechTransmissionServices TeleserviceCode ::= '00010000'B +telephony TeleserviceCode ::= '00010001'B +emergencyCalls TeleserviceCode ::= '00010010'B + +allShortMessageServices TeleserviceCode ::= '00100000'B +shortMessageMT-PP TeleserviceCode ::= '00100001'B +shortMessageMO-PP TeleserviceCode ::= '00100010'B + +allFacsimileTransmissionServices TeleserviceCode ::= '01100000'B +facsimileGroup3AndAlterSpeech TeleserviceCode ::= '01100001'B +automaticFacsimileGroup3 TeleserviceCode ::= '01100010'B +facsimileGroup4 TeleserviceCode ::= '01100011'B + +-- The following non-hierarchical Compound Teleservice Groups +-- are defined in TS 3GPP TS 22.030: +allDataTeleservices TeleserviceCode ::= '01110000'B + -- covers Teleservice Groups 'allFacsimileTransmissionServices' + -- and 'allShortMessageServices' +allTeleservices-ExeptSMS TeleserviceCode ::= '10000000'B + -- covers Teleservice Groups 'allSpeechTransmissionServices' and + -- 'allFacsimileTransmissionServices' +-- +-- Compound Teleservice Group Codes are only used in call +-- independent supplementary service operations, i.e. they +-- are not used in InsertSubscriberData or in +-- DeleteSubscriberData messages. + +allVoiceGroupCallServices TeleserviceCode ::= '10010000'B +voiceGroupCall TeleserviceCode ::= '10010001'B +voiceBroadcastCall TeleserviceCode ::= '10010010'B + +allPLMN-specificTS TeleserviceCode ::= '11010000'B +plmn-specificTS-1 TeleserviceCode ::= '11010001'B +plmn-specificTS-2 TeleserviceCode ::= '11010010'B +plmn-specificTS-3 TeleserviceCode ::= '11010011'B +plmn-specificTS-4 TeleserviceCode ::= '11010100'B +plmn-specificTS-5 TeleserviceCode ::= '11010101'B +plmn-specificTS-6 TeleserviceCode ::= '11010110'B +plmn-specificTS-7 TeleserviceCode ::= '11010111'B +plmn-specificTS-8 TeleserviceCode ::= '11011000'B +plmn-specificTS-9 TeleserviceCode ::= '11011001'B +plmn-specificTS-A TeleserviceCode ::= '11011010'B +plmn-specificTS-B TeleserviceCode ::= '11011011'B +plmn-specificTS-C TeleserviceCode ::= '11011100'B +plmn-specificTS-D TeleserviceCode ::= '11011101'B +plmn-specificTS-E TeleserviceCode ::= '11011110'B +plmn-specificTS-F TeleserviceCode ::= '11011111'B + +END + diff --git a/rrlp-ephemeris/asn1/RRLP-Components.asn b/rrlp-ephemeris/asn1/RRLP-Components.asn new file mode 100644 index 000000000..3bade6a6a --- /dev/null +++ b/rrlp-ephemeris/asn1/RRLP-Components.asn @@ -0,0 +1,1488 @@ +-- RRLP-Components.asn +-- $Id$ +-- Taken from 3GPP TS 44.031 V7.4.0 (2007-03) +-- http://www.3gpp.org/ftp/Specs/archive/44_series/44.031/44031-740.zip/44031-740.doc +-- +-- 4 Components +-- 5 Elements of Components +-- + +RRLP-Components +-- { RRLP-Components } + +DEFINITIONS AUTOMATIC TAGS ::= + +BEGIN + +IMPORTS + Ext-GeographicalInformation, VelocityEstimate +FROM + MAP-LCS-DataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-LCS-DataTypes (25) version11 (11)} + + ExtensionContainer +FROM MAP-ExtensionDataTypes { + itu-t identified-organization (4) etsi (0) mobileDomain (0) + gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} +; + + +-- Add here other ASN.1 definitions presented below +-- in chapters 4 and 5. + +-- Measurement Position request component +MsrPosition-Req ::= SEQUENCE { + positionInstruct PositionInstruct, + referenceAssistData ReferenceAssistData OPTIONAL, + msrAssistData MsrAssistData OPTIONAL, + systemInfoAssistData SystemInfoAssistData OPTIONAL, + gps-AssistData GPS-AssistData OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + -- Release 98 extension element +rel98-MsrPosition-Req-extension Rel98-MsrPosition-Req-Extension OPTIONAL, + -- Release 5 extension element +rel5-MsrPosition-Req-extension Rel5-MsrPosition-Req-Extension OPTIONAL, + -- Release 7 extension element +rel7-MsrPosition-Req-extension Rel7-MsrPosition-Req-Extension OPTIONAL +} + +-- Measurement Position response component +MsrPosition-Rsp ::= SEQUENCE { + multipleSets MultipleSets OPTIONAL, + referenceIdentity ReferenceIdentity OPTIONAL, + otd-MeasureInfo OTD-MeasureInfo OPTIONAL, + locationInfo LocationInfo OPTIONAL, + gps-MeasureInfo GPS-MeasureInfo OPTIONAL, + locationError LocationError OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + -- Release extension here + rel-98-MsrPosition-Rsp-Extension + Rel-98-MsrPosition-Rsp-Extension OPTIONAL, + rel-5-MsrPosition-Rsp-Extension + Rel-5-MsrPosition-Rsp-Extension OPTIONAL, + -- rel-5-MsrPosition-Rsp-Extension and other possible future extensions + -- are the only information elements that may be included in the 2nd + -- MsrPosition-Rsp component when RRLP pseudo-segmentation is used + rel-7-MsrPosition-Rsp-Extension + Rel-7-MsrPosition-Rsp-Extension OPTIONAL +} + +-- Assistance Data component +AssistanceData ::= SEQUENCE { + referenceAssistData ReferenceAssistData OPTIONAL, + msrAssistData MsrAssistData OPTIONAL, + systemInfoAssistData SystemInfoAssistData OPTIONAL, + gps-AssistData GPS-AssistData OPTIONAL, + moreAssDataToBeSent MoreAssDataToBeSent OPTIONAL, -- If not present, interpret as only + -- Assistance Data component used to + -- deliver entire set of assistance + -- data. + extensionContainer ExtensionContainer OPTIONAL, + ..., + -- Release extension here + rel98-AssistanceData-Extension Rel98-AssistanceData-Extension OPTIONAL, + rel5-AssistanceData-Extension Rel5-AssistanceData-Extension OPTIONAL, + rel7-AssistanceData-Extension Rel7-AssistanceData-Extension OPTIONAL +} + +-- Protocol Error component +ProtocolError ::= SEQUENCE { + errorCause ErrorCodes, + extensionContainer ExtensionContainer OPTIONAL, + ..., + -- Release extensions here + rel-5-ProtocolError-Extension Rel-5-ProtocolError-Extension OPTIONAL +} + +-- Position instructions +PositionInstruct ::= SEQUENCE { + -- Method type + methodType MethodType, + positionMethod PositionMethod, + measureResponseTime MeasureResponseTime, + useMultipleSets UseMultipleSets, + environmentCharacter EnvironmentCharacter OPTIONAL +} + +-- +MethodType ::= CHOICE { + msAssisted AccuracyOpt, -- accuracy is optional + msBased Accuracy, -- accuracy is mandatory + msBasedPref Accuracy, -- accuracy is mandatory + msAssistedPref Accuracy -- accuracy is mandatory +} + +-- Accuracy of the location estimation +AccuracyOpt ::= SEQUENCE { + accuracy Accuracy OPTIONAL +} + +-- The values of this field are defined in 3GPP TS 23.032 (Uncertainty code) +Accuracy ::= INTEGER (0..127) + + +-- Position Method +PositionMethod ::= ENUMERATED { + eotd (0), + gps (1), + gpsOrEOTD (2) +} + +-- Measurement request response time +MeasureResponseTime ::= INTEGER (0..7) + +-- useMultiple Sets, FFS! +UseMultipleSets ::= ENUMERATED { + multipleSets (0), -- multiple sets are allowed + oneSet (1) -- sending of multiple is not allowed +} + +-- Environment characterization +EnvironmentCharacter ::= ENUMERATED { + badArea (0), -- bad urban or suburban, heavy multipath and NLOS + notBadArea (1), -- light multipath and NLOS + mixedArea (2), -- not defined or mixed environment + ... +} + +-- E-OTD reference BTS for Assitance data IE +ReferenceAssistData ::= SEQUENCE { + bcchCarrier BCCHCarrier, -- BCCH carrier + bsic BSIC, -- BSIC + timeSlotScheme TimeSlotScheme, -- Timeslot scheme + btsPosition BTSPosition OPTIONAL +} + +-- ellipsoid point and +-- ellipsoid point with altitude and uncertainty ellipsoid shapes are supported +BTSPosition ::= Ext-GeographicalInformation + +-- RF channel number of BCCH +BCCHCarrier ::= INTEGER (0..1023) + +-- Base station Identity Code +BSIC ::= INTEGER (0..63) + +-- Timeslot scheme +TimeSlotScheme ::= ENUMERATED { + equalLength (0), + variousLength (1) +} + +-- Time slot (modulo) +ModuloTimeSlot ::= INTEGER (0..3) + +-- E-OTD measurement assistance data IE +-- The total number of neighbors in this element (MsrAssistData) +-- and in SystemInfoAssistData element (presented neighbors +-- can be at a maximum 15!) +MsrAssistData ::= SEQUENCE { + msrAssistList SeqOfMsrAssistBTS +} +SeqOfMsrAssistBTS ::= SEQUENCE (SIZE(1..15)) OF MsrAssistBTS + +MsrAssistBTS ::= SEQUENCE { + bcchCarrier BCCHCarrier, -- BCCH carrier + bsic BSIC, -- BSIC + multiFrameOffset MultiFrameOffset, -- multiframe offset + timeSlotScheme TimeSlotScheme, -- Timeslot scheme + roughRTD RoughRTD, -- rough RTD value + + -- Location Calculation Assistance data is moved here + calcAssistanceBTS CalcAssistanceBTS OPTIONAL +} + +-- Multiframe offset +MultiFrameOffset ::= INTEGER (0..51) +-- The Multiframe Offset value 51 shall not be encoded by the transmitting entity and +-- shall be treated by the receiving entity as 0. + +-- Rough RTD value between one base station and reference BTS +RoughRTD ::= INTEGER (0..1250) +-- The RoughRTD value 1250 shall not be encoded by the transmitting entity and shall +-- be treated by the receiving entity as 0. + +-- E-OTD Measurement assistance data for system information List IE +-- The total number of base stations in this element (SystemInfoAssistData +-- presented neighbors) and in MsrAssistData element can be at a maximum 15. +SystemInfoAssistData ::= SEQUENCE { + systemInfoAssistList SeqOfSystemInfoAssistBTS +} +SeqOfSystemInfoAssistBTS::= SEQUENCE (SIZE(1..32)) OF SystemInfoAssistBTS + +-- whether n.th is present or not ? +SystemInfoAssistBTS ::= CHOICE { + notPresent NULL, + present AssistBTSData +} + +-- Actual assistance data for system information base station +AssistBTSData ::= SEQUENCE { + bsic BSIC, -- BSIC + multiFrameOffset MultiFrameOffset, -- multiframe offset + timeSlotScheme TimeSlotScheme, -- Timeslot scheme + roughRTD RoughRTD, -- rough RTD value + + -- Location Calculation Assistance data + calcAssistanceBTS CalcAssistanceBTS OPTIONAL +} + +-- E-OTD Location calculation assistance data, +-- CalcAssistanceBTS element is optional not subfields +CalcAssistanceBTS ::= SEQUENCE { + fineRTD FineRTD, -- fine RTD value between base stations + referenceWGS84 ReferenceWGS84 -- reference coordinates +} + +-- Coordinates of neighbour BTS, WGS-84 ellipsoid +ReferenceWGS84 ::= SEQUENCE { + relativeNorth RelDistance, -- relative distance (south negative) + relativeEast RelDistance, -- relative distance (west negative) + -- Relative Altitude is not always known + relativeAlt RelativeAlt OPTIONAL -- relative altitude +} + +-- Fine RTD value between this BTS and the reference BTS +FineRTD ::= INTEGER (0..255) + +-- Relative north/east distance +RelDistance ::= INTEGER (-200000..200000) + +-- Relative altitude +RelativeAlt ::= INTEGER (-4000..4000) + +-- Measure position response IEs +-- Reference Identity +-- Multiple sets +MultipleSets ::= SEQUENCE { + -- number of reference sets + nbrOfSets INTEGER (2..3), + + -- This field actually tells the number of reference BTSs + nbrOfReferenceBTSs INTEGER (1..3), + + -- This field is conditional and included optionally only if + -- nbrOfSets is 3 and number of reference BTSs is 2. + referenceRelation ReferenceRelation OPTIONAL +} + +-- Relation between refence BTSs and sets +ReferenceRelation ::= ENUMERATED { + secondBTSThirdSet (0), -- 1st BTS related to 1st and 2nd sets + secondBTSSecondSet (1), -- 1st BTS related to 1st and 3rd sets + firstBTSFirstSet (2) -- 1st BTS related to 1st set +} + +-- Reference BTS Identity, this element contains number of +-- BTSs told nbrOfReferenceBTSs field in Multiple sets element) +ReferenceIdentity ::= SEQUENCE { + -- Reference BTS list + refBTSList SeqOfReferenceIdentityType +} +SeqOfReferenceIdentityType ::= SEQUENCE (SIZE(1..3)) OF ReferenceIdentityType + +-- Cell identity +ReferenceIdentityType ::= CHOICE { + bsicAndCarrier BSICAndCarrier, -- BSIC and Carrier + ci CellID, -- Cell ID, LAC not needed + requestIndex RequestIndex, -- Index to Requested Neighbor List + systemInfoIndex SystemInfoIndex, -- Index to System info list, this type of ref. identity + -- shall not be used by the MS unless it has received + -- the SystemInfoAssistData from the SMLC for this cell. + ciAndLAC CellIDAndLAC -- CI and LAC +} + +BSICAndCarrier ::= SEQUENCE { + carrier BCCHCarrier, + bsic BSIC +} + +RequestIndex ::= INTEGER (1..16) + +SystemInfoIndex ::= INTEGER (1..32) + +CellIDAndLAC ::= SEQUENCE { + referenceLAC LACID, -- Location area code + referenceCI CellID -- Cell identity +} +CellID ::= INTEGER (0..65535) +LACID ::= INTEGER (0..65535) + +-- OTD-MeasureInfo +OTD-MeasureInfo ::= SEQUENCE { + -- Measurement info elements, OTD-MsrElement is repeated number of times + -- told in nbrOfReferenceBTSs in MultipleSets, default value is 1 + otdMsrFirstSets OTD-MsrElementFirst, + + -- if more than one sets are present this element is repeated + -- NumberOfSets - 1 (-1 = first set) + otdMsrRestSets SeqOfOTD-MsrElementRest OPTIONAL +} + +SeqOfOTD-MsrElementRest ::= SEQUENCE (SIZE(1..2)) OF OTD-MsrElementRest + +-- OTD measurent information for 1 set +OTD-MsrElementFirst ::= SEQUENCE { + refFrameNumber INTEGER (0..42431), -- Frame number modulo 42432 + referenceTimeSlot ModuloTimeSlot, + toaMeasurementsOfRef TOA-MeasurementsOfRef OPTIONAL, + stdResolution StdResolution, + taCorrection INTEGER (0..960) OPTIONAL, -- TA correction + + -- measured neighbors in OTD measurements + otd-FirstSetMsrs SeqOfOTD-FirstSetMsrs OPTIONAL +} +SeqOfOTD-FirstSetMsrs ::= SEQUENCE (SIZE(1..10)) OF OTD-FirstSetMsrs + +-- OTD measurent information 2 and 3 sets if exist +OTD-MsrElementRest ::= SEQUENCE { + refFrameNumber INTEGER (0..42431), -- Frame number modulo 42432 + referenceTimeSlot ModuloTimeSlot, + toaMeasurementsOfRef TOA-MeasurementsOfRef OPTIONAL, + stdResolution StdResolution, + taCorrection INTEGER (0..960) OPTIONAL, -- TA correction + + -- measured neighbors in OTD measurements + otd-MsrsOfOtherSets SeqOfOTD-MsrsOfOtherSets OPTIONAL +} +SeqOfOTD-MsrsOfOtherSets ::= SEQUENCE (SIZE(1..10)) OF OTD-MsrsOfOtherSets + +-- Standard deviation of the TOA measurements from the reference BTS +TOA-MeasurementsOfRef ::= SEQUENCE { + refQuality RefQuality, + numOfMeasurements NumOfMeasurements +} + +RefQuality ::= INTEGER (0..31) -- St Dev of TOA of reference as defined in annex +NumOfMeasurements ::= INTEGER (0..7) -- No. of measurements for RefQuality as defined in annex +StdResolution ::= INTEGER (0..3) -- Values of resolution are defined in annex + +OTD-FirstSetMsrs ::= OTD-MeasurementWithID + +-- Neighbour info in OTD measurements 0-10 times in TD measurement info +OTD-MsrsOfOtherSets ::= CHOICE { + identityNotPresent OTD-Measurement, + identityPresent OTD-MeasurementWithID +} + +-- For this OTD measurement identity is same as the identity of BTS +-- in the first set with same sequence number +OTD-Measurement ::= SEQUENCE { + nborTimeSlot ModuloTimeSlot, + eotdQuality EOTDQuality, + otdValue OTDValue +} + +-- This measurement contains the BTS identity and measurement +OTD-MeasurementWithID ::=SEQUENCE { + neighborIdentity NeighborIdentity, + nborTimeSlot ModuloTimeSlot, + eotdQuality EOTDQuality, + otdValue OTDValue +} + +EOTDQuality ::= SEQUENCE { + nbrOfMeasurements INTEGER (0..7), + stdOfEOTD INTEGER (0..31) +} + +NeighborIdentity ::= CHOICE { + bsicAndCarrier BSICAndCarrier, -- BSIC and Carrier + ci CellID, -- Cell ID, LAC not needed + multiFrameCarrier MultiFrameCarrier, -- MultiFrameOffest and BSIC + requestIndex RequestIndex, -- Index to Requested Neighbor List + systemInfoIndex SystemInfoIndex, -- Index to System info list, this type of neighbour + -- identity shall not be used by the MS unless it has + -- received the SystemInfoAssistData from the SMLC for + -- this cell. + ciAndLAC CellIDAndLAC -- CI and LAC +} + +-- Multiframe and carrier +MultiFrameCarrier ::= SEQUENCE { + bcchCarrier BCCHCarrier, + multiFrameOffset MultiFrameOffset +} + +-- OTD measurement value for neighbour +OTDValue ::= INTEGER (0..39999) + +-- Location information IE +LocationInfo ::= SEQUENCE { + refFrame INTEGER (0..65535), -- Reference Frame number + -- If refFrame is within (42432..65535), it shall be ignored by the receiver + -- in that case the MS should provide GPS TOW if available + gpsTOW INTEGER (0..14399999) OPTIONAL, -- GPS TOW + fixType FixType, + -- Note that applicable range for refFrame is 0 - 42431 + -- Possible shapes carried in posEstimate are + -- ellipsoid point, + -- ellipsoid point with uncertainty circle + -- ellipsoid point with uncertainty ellipse + -- ellipsoid point with altitude and uncertainty ellipsoid + posEstimate Ext-GeographicalInformation +} + +FixType ::= INTEGER { + twoDFix (0), + threeDFix (1) +} (0..1) + +-- GPS-Measurement information +GPS-MeasureInfo ::= SEQUENCE { + -- Measurement info elements + -- user has to make sure that in this element is number of elements + -- defined in reference BTS identity + gpsMsrSetList SeqOfGPS-MsrSetElement +} +SeqOfGPS-MsrSetElement ::= SEQUENCE (SIZE(1..3)) OF GPS-MsrSetElement + +-- OTD measurent information 1-3 times in message +GPS-MsrSetElement ::= SEQUENCE { + refFrame INTEGER (0..65535) OPTIONAL, -- Reference Frame number + gpsTOW GPSTOW24b, -- GPS TOW + -- Note that applicable range for refFrame is 0 - 42431 + +--N_SAT can be read from number of elements of gps-msrList + + gps-msrList SeqOfGPS-MsrElement +} + +-- 24 bit presentation for GPSTOW +GPSTOW24b ::= INTEGER (0..14399999) + +-- measured elements in measurement parameters field +SeqOfGPS-MsrElement ::= SEQUENCE (SIZE(1..16)) OF GPS-MsrElement + +GPS-MsrElement ::= SEQUENCE { + satelliteID SatelliteID, -- Satellite identifier + cNo INTEGER (0..63), -- carrier noise ratio + doppler INTEGER (-32768..32767), -- doppler, mulltiply by 0.2 + wholeChips INTEGER (0..1022), -- whole value of the code phase measurement + fracChips INTEGER (0..1024), -- fractional value of the code phase measurement + -- a value of 1024 shall not be encoded by the sender + -- the receiver shall consider a value of 1024 to be + -- invalid data + mpathIndic MpathIndic, -- multipath indicator + pseuRangeRMSErr INTEGER (0..63) -- index +} + +-- Multipath indicator +MpathIndic ::= ENUMERATED { + notMeasured (0), + low (1), + medium (2), + high (3) +} + +-- Location error IE +LocationError ::= SEQUENCE { + locErrorReason LocErrorReason, + additionalAssistanceData AdditionalAssistanceData OPTIONAL, + ... +} + +LocErrorReason ::= ENUMERATED { + unDefined (0), + notEnoughBTSs (1), + notEnoughSats (2), + eotdLocCalAssDataMissing (3), + eotdAssDataMissing (4), + gpsLocCalAssDataMissing (5), + gpsAssDataMissing (6), + methodNotSupported (7), + notProcessed (8), + refBTSForGPSNotServingBTS (9), + refBTSForEOTDNotServingBTS (10), + ..., + notEnoughGANSSSats (11), + ganssAssDataMissing (12), + refBTSForGANSSNotServingBTS (13) +} + +-- exception handling: +-- an unrecognized value shall be treated the same as value 0 + + +-- defines additional assistance data needed for any new location attempt +-- MS shall retain any assistance data already received +AdditionalAssistanceData ::= SEQUENCE { + gpsAssistanceData GPSAssistanceData OPTIONAL, + extensionContainer ExtensionContainer OPTIONAL, + ..., + ganssAssistanceData GANSSAssistanceData OPTIONAL +} + +GPSAssistanceData ::= OCTET STRING (SIZE (1..maxGPSAssistanceData)) +-- GPSAssistanceData has identical structure and encoding to octets 3 to n of the +-- GPS Assistance Data IE in 3GPP TS 49.031 + +maxGPSAssistanceData INTEGER ::= 40 + +GANSSAssistanceData ::= OCTET STRING (SIZE (1..maxGANSSAssistanceData)) +-- GANSSAssistanceData has identical structure and encoding to octets 3 to n of the +-- GANSS Assistance Data IE in 3GPP TS 49.031 + +maxGANSSAssistanceData INTEGER ::= 40 + + +-- Protocol Error Causes +ErrorCodes ::= ENUMERATED { + unDefined (0), +missingComponet (1), +incorrectData (2), +missingIEorComponentElement (3), +messageTooShort (4), +unknowReferenceNumber (5), +... +} + +-- exception handling: +-- an unrecognized value shall be treated the same as value 0 + +-- GPS assistance data IE +GPS-AssistData ::= SEQUENCE { + controlHeader ControlHeader +} + +-- More Assistance Data To Be Sent IE +-- More Assistance Data Components On the Way indication for delivery of an entire set of assistance +-- data in multiple Assistance Data components. + +MoreAssDataToBeSent ::= ENUMERATED { + noMoreMessages (0), -- This is the only or last Assistance Data message used to deliver + -- the entire set of assistance data. + moreMessagesOnTheWay (1) -- The SMLC will send more Assistance Data messages or a final RRLP + -- Measure Position Request message to deliver the + -- the entire set of assistance data. +} + +-- Control header of the GPS assistance data +ControlHeader ::= SEQUENCE { + + -- Field type Present information + referenceTime ReferenceTime OPTIONAL, + refLocation RefLocation OPTIONAL, + dgpsCorrections DGPSCorrections OPTIONAL, + navigationModel NavigationModel OPTIONAL, + ionosphericModel IonosphericModel OPTIONAL, + utcModel UTCModel OPTIONAL, + almanac Almanac OPTIONAL, + acquisAssist AcquisAssist OPTIONAL, + realTimeIntegrity SeqOf-BadSatelliteSet OPTIONAL +} + +ReferenceTime ::= SEQUENCE { + gpsTime GPSTime, + gsmTime GSMTime OPTIONAL, + gpsTowAssist GPSTOWAssist OPTIONAL +} + +-- GPS Time includes week number and time-of-week (TOW) +GPSTime ::= SEQUENCE { + gpsTOW23b GPSTOW23b, + gpsWeek GPSWeek +} + +-- GPSTOW, range 0-604799.92, resolution 0.08 sec, 23-bit presentation +GPSTOW23b ::= INTEGER (0..7559999) + +-- GPS week number +GPSWeek ::= INTEGER (0..1023) + +-- GPSTOWAssist consists of TLM message, Anti-spoof flag, Alert flag, and 2 reserved bits in TLM Word +-- for each visible satellite. +-- N_SAT can be read from number of elements in GPSTOWAssist +GPSTOWAssist ::= SEQUENCE (SIZE(1..12)) OF GPSTOWAssistElement + +GPSTOWAssistElement ::= SEQUENCE { + satelliteID SatelliteID, + tlmWord TLMWord, + antiSpoof AntiSpoofFlag, + alert AlertFlag, + tlmRsvdBits TLMReservedBits +} + +-- TLM Word, 14 bits +TLMWord ::= INTEGER (0..16383) + +-- Anti-Spoof flag +AntiSpoofFlag ::= INTEGER (0..1) + +-- Alert flag +AlertFlag ::= INTEGER (0..1) + +-- Reserved bits in TLM word, MSB occurs earlier in TLM Word transmitted by satellite +TLMReservedBits ::= INTEGER (0..3) + +GSMTime ::= SEQUENCE { + bcchCarrier BCCHCarrier, -- BCCH carrier + bsic BSIC, -- BSIC + frameNumber FrameNumber, + timeSlot TimeSlot, + bitNumber BitNumber +} + +-- Frame number +FrameNumber ::= INTEGER (0..2097151) + +-- Time slot number +TimeSlot ::= INTEGER (0..7) + +-- Bit number +BitNumber ::= INTEGER (0..156) + + +-- Reference Location IE +RefLocation ::= SEQUENCE { + threeDLocation Ext-GeographicalInformation +} + +-- DGPS Corrections IE +DGPSCorrections ::= SEQUENCE { + + gpsTOW INTEGER (0..604799), -- DGPS reference time + status INTEGER (0..7), + -- N_SAT can be read from number of elements of satList + satList SeqOfSatElement +} +SeqOfSatElement ::= SEQUENCE (SIZE (1..16)) OF SatElement + +-- number of correction for satellites +SatElement ::= SEQUENCE { + satelliteID SatelliteID, + + +--- Sequence number for ephemeris + iode INTEGER (0..239), + -- User Differential Range Error + udre INTEGER (0..3), + + -- Pseudo Range Correction, range is + -- -655.04 - +655.04, + pseudoRangeCor INTEGER (-2047..2047), + + -- Pseudo Range Rate Correction, range is + -- -4.064 - +4.064, + rangeRateCor INTEGER (-127..127), + +-- Delta Pseudo Range Correction 2 + deltaPseudoRangeCor2 INTEGER (-127..127), -- This IE shall be ignored by the receiver and + -- set to zero by the sender + -- Delta Pseudo Range Correction 2 + deltaRangeRateCor2 INTEGER (-7..7), -- This IE shall be ignored by the receiver and + -- set to zero by the sender + -- Delta Pseudo Range Correction 3 + deltaPseudoRangeCor3 INTEGER (-127..127), -- This IE shall be ignored by the receiver and + -- set to zero by the sender + -- Delta Pseudo Range Correction 3 + deltaRangeRateCor3 INTEGER (-7..7) -- This IE shall be ignored by the receiver and + -- set to zero by the sender +} + +SatelliteID ::= INTEGER (0..63) -- identifies satellite + +-- Navigation Model IE +NavigationModel ::= SEQUENCE { + navModelList SeqOfNavModelElement +} + +-- navigation model satellite list +SeqOfNavModelElement ::= SEQUENCE (SIZE(1..16)) OF NavModelElement + +NavModelElement ::= SEQUENCE { + satelliteID SatelliteID, + satStatus SatStatus -- satellite status +} + +-- the Status of the navigation model +SatStatus ::= CHOICE { + -- New satellite, new Navigation Model + newSatelliteAndModelUC UncompressedEphemeris, + + -- Existing satellite, Existing Navigation Model + oldSatelliteAndModel NULL, + + -- Existing satellite, new Navigation Model + newNaviModelUC UncompressedEphemeris, + ... +} + +-- Uncompressed satellite emhemeris and clock corrections +UncompressedEphemeris ::= SEQUENCE { + ephemCodeOnL2 INTEGER (0..3), + ephemURA INTEGER (0..15), + ephemSVhealth INTEGER (0..63), + ephemIODC INTEGER (0..1023), + ephemL2Pflag INTEGER (0..1), + ephemSF1Rsvd EphemerisSubframe1Reserved, + ephemTgd INTEGER (-128..127), + ephemToc INTEGER (0..37799), + ephemAF2 INTEGER (-128..127), + ephemAF1 INTEGER (-32768..32767), + ephemAF0 INTEGER (-2097152..2097151), + ephemCrs INTEGER (-32768..32767), + ephemDeltaN INTEGER (-32768..32767), + ephemM0 INTEGER (-2147483648..2147483647), + ephemCuc INTEGER (-32768..32767), + ephemE INTEGER (0..4294967295), + ephemCus INTEGER (-32768..32767), + ephemAPowerHalf INTEGER (0..4294967295), + ephemToe INTEGER (0..37799), + ephemFitFlag INTEGER (0..1), + ephemAODA INTEGER (0..31), + ephemCic INTEGER (-32768..32767), + ephemOmegaA0 INTEGER (-2147483648..2147483647), + ephemCis INTEGER (-32768..32767), + ephemI0 INTEGER (-2147483648..2147483647), + ephemCrc INTEGER (-32768..32767), + ephemW INTEGER (-2147483648..2147483647), + ephemOmegaADot INTEGER (-8388608..8388607), + ephemIDot INTEGER (-8192..8191) +} + +-- Reserved bits in subframe 1 of navigation message +EphemerisSubframe1Reserved ::= SEQUENCE { + reserved1 INTEGER (0..8388607), -- 23-bit field + reserved2 INTEGER (0..16777215), -- 24-bit field + reserved3 INTEGER (0..16777215), -- 24-bit field + reserved4 INTEGER (0..65535) -- 16-bit field +} + +-- Ionospheric Model IE +IonosphericModel ::= SEQUENCE { + alfa0 INTEGER (-128..127), + alfa1 INTEGER (-128..127), + alfa2 INTEGER (-128..127), + alfa3 INTEGER (-128..127), + beta0 INTEGER (-128..127), + beta1 INTEGER (-128..127), + beta2 INTEGER (-128..127), + beta3 INTEGER (-128..127) +} + +-- Universal Time Coordinate Model +UTCModel ::= SEQUENCE { + utcA1 INTEGER (-8388608..8388607), + utcA0 INTEGER (-2147483648..2147483647), + utcTot INTEGER (0..255), + utcWNt INTEGER (0..255), + utcDeltaTls INTEGER (-128..127), + utcWNlsf INTEGER (0..255), + utcDN INTEGER (-128..127), + utcDeltaTlsf INTEGER (-128..127) +} + +-- Almanac, Long term model +-- NOTE: These are parameters are subset of the ephemeris +-- NOTE: But with reduced resolution and accuracy +Almanac ::= SEQUENCE { + alamanacWNa INTEGER (0..255), -- Once per message + + -- navigation model satellite list. + -- The size of almanacList is actually Nums_Sats_Total field + almanacList SeqOfAlmanacElement +} +SeqOfAlmanacElement ::= SEQUENCE (SIZE(1..64)) OF AlmanacElement + +-- Almanac info once per satellite +AlmanacElement ::= SEQUENCE { + satelliteID SatelliteID, + almanacE INTEGER (0..65535), + alamanacToa INTEGER (0..255), + almanacKsii INTEGER (-32768..32767), + almanacOmegaDot INTEGER (-32768..32767), + almanacSVhealth INTEGER (0..255), + almanacAPowerHalf INTEGER (0..16777215), + almanacOmega0 INTEGER (-8388608..8388607), + almanacW INTEGER (-8388608..8388607), + almanacM0 INTEGER (-8388608..8388607), + almanacAF0 INTEGER (-1024..1023), + almanacAF1 INTEGER (-1024..1023) +} + +-- Acquisition Assistance +AcquisAssist ::= SEQUENCE { + + -- Number of Satellites can be read from acquistList + timeRelation TimeRelation, + + -- Acquisition assistance list + -- The size of Number of Satellites is actually Number of Satellites field + acquisList SeqOfAcquisElement +} +SeqOfAcquisElement ::= SEQUENCE (SIZE(1..16)) OF AcquisElement + +-- the relationship between GPS time and air-interface timing +TimeRelation ::= SEQUENCE { + -- + gpsTOW GPSTOW23b, -- 23b presentation + gsmTime GSMTime OPTIONAL +} + +-- data occuring per number of satellites +AcquisElement ::= SEQUENCE { + svid SatelliteID, + + -- Doppler 0th order term, + -- -5120.0 - 5117.5 Hz (= -2048 - 2047 with 2.5 Hz resolution) + doppler0 INTEGER (-2048..2047), + addionalDoppler AddionalDopplerFields OPTIONAL, + codePhase INTEGER (0..1022), -- Code Phase + intCodePhase INTEGER (0..19), -- Integer Code Phase + gpsBitNumber INTEGER (0..3), -- GPS bit number + codePhaseSearchWindow INTEGER (0..15), -- Code Phase Search Window + addionalAngle AddionalAngleFields OPTIONAL +} + +AddionalDopplerFields ::= SEQUENCE { + -- Doppler 1st order term, -1.0 - +0.5 Hz/sec + -- (= -42 + (0 to 63) with 1/42 Hz/sec. resolution) + doppler1 INTEGER (0..63), + dopplerUncertainty INTEGER (0..7) + -- a sender shall not encode any DopplerUncertainty value in the range 5 to 7 + -- a receiver shall ignore any value between 5 and 7. +} + +AddionalAngleFields ::= SEQUENCE { + -- azimuth angle, 0 - 348.75 deg (= 0 - 31 with 11.25 deg resolution) + azimuth INTEGER (0..31), + -- elevation angle, 0 - 78.75 deg (= 0 - 7 with 11.25 deg resolution) + elevation INTEGER (0..7) +} + +-- Real-Time Integrity +-- number of bad satellites can be read from this element +SeqOf-BadSatelliteSet ::= SEQUENCE (SIZE(1..16)) OF SatelliteID + +-- Extension Elements + +-- Release 98 Extensions here +Rel98-MsrPosition-Req-Extension ::= SEQUENCE { + rel98-Ext-ExpOTD Rel98-Ext-ExpOTD OPTIONAL, -- ExpectedOTD extension + ..., + gpsTimeAssistanceMeasurementRequest NULL OPTIONAL, + gpsReferenceTimeUncertainty GPSReferenceTimeUncertainty OPTIONAL + +-- Further R98 extensions here +} +Rel98-AssistanceData-Extension ::= SEQUENCE { + rel98-Ext-ExpOTD Rel98-Ext-ExpOTD OPTIONAL, -- ExpectedOTD extension + ..., + gpsTimeAssistanceMeasurementRequest NULL OPTIONAL, + gpsReferenceTimeUncertainty GPSReferenceTimeUncertainty OPTIONAL + +-- Further R98 extensions here +} + +-- Release 98 ExpOTD extension +Rel98-Ext-ExpOTD ::= SEQUENCE { +-- If MsrAssistBTS is included in message, msrAssistData-R98-ExpOTD shall be included. + msrAssistData-R98-ExpOTD MsrAssistData-R98-ExpOTD OPTIONAL, + +-- If SystemInfoAssistaData is included in message, systemInfoAssistData-R98-ExpOTD shall be +-- included. + systemInfoAssistData-R98-ExpOTD SystemInfoAssistData-R98-ExpOTD OPTIONAL +} + +-- MsrAssistData R98 extension +MsrAssistData-R98-ExpOTD ::= SEQUENCE { + msrAssistList-R98-ExpOTD SeqOfMsrAssistBTS-R98-ExpOTD +} + +-- Indexes in SeqOfMsrAssistBTS-R98-ExpOTD refer to SeqOfMsrAssistBTS +-- If the index exceeds the SegOfMsrAssistBTS range or if there is other +-- inconsistencies between the BTS indices, the MS shall apply protocol +-- error cause incorrectData +SeqOfMsrAssistBTS-R98-ExpOTD ::= SEQUENCE (SIZE(1..15)) OF MsrAssistBTS-R98-ExpOTD + +-- This element completes MsrAssistBTS IE +MsrAssistBTS-R98-ExpOTD ::= SEQUENCE { + expectedOTD ExpectedOTD, + expOTDUncertainty ExpOTDUncertainty +} + +-- SystemInfoAssistData R98 extension +SystemInfoAssistData-R98-ExpOTD ::= SEQUENCE { + systemInfoAssistListR98-ExpOTD SeqOfSystemInfoAssistBTS-R98-ExpOTD +} + +-- SeqOfSystemInfoAssistBTS-R98-ExpOTD index refer to SeqOfSystemInfoAssistBTS +-- If the index exceeds the SegOfSystemInfoAssistBTS range or if there is other +-- inconsistencies between the BTS indices, the MS shall apply protocol +-- error cause incorrectData +SeqOfSystemInfoAssistBTS-R98-ExpOTD ::= SEQUENCE (SIZE(1..32)) OF SystemInfoAssistBTS-R98-ExpOTD + +-- whether n.th is present or not ? +SystemInfoAssistBTS-R98-ExpOTD ::= CHOICE { + notPresent NULL, + present AssistBTSData-R98-ExpOTD +} + +-- This element completes AssistBTSData IE +AssistBTSData-R98-ExpOTD ::= SEQUENCE { + expectedOTD ExpectedOTD, + expOTDuncertainty ExpOTDUncertainty -- Uncertainty of expected OTD +} + +-- Expected OTD value between nbor base station and reference BTS +-- at MS's current estimated location. +ExpectedOTD ::= INTEGER (0..1250) +-- The ExpectedOTD value 1250 shall not be encoded by the transmitting entity and +-- shall be treated by the receiving entity as 0. +-- Uncertainty of Exptected OTD in bits +ExpOTDUncertainty ::= INTEGER(0..7) + +-- Release 98 extensions + +GPSReferenceTimeUncertainty ::= INTEGER (0 .. 127) -- Coding according to Annex + +GPSTimeAssistanceMeasurements ::= SEQUENCE { + referenceFrameMSB INTEGER (0 .. 63), -- MSB of frame number + gpsTowSubms INTEGER (0 .. 9999) OPTIONAL, -- in units of 100ns, for MS based AGPS + deltaTow INTEGER (0 .. 127) OPTIONAL, -- for MS assisted AGPS + gpsReferenceTimeUncertainty GPSReferenceTimeUncertainty OPTIONAL +} + +Rel-98-MsrPosition-Rsp-Extension ::= SEQUENCE { + + -- First extension to Release 98 + rel-98-Ext-MeasureInfo SEQUENCE { + otd-MeasureInfo-R98-Ext OTD-MeasureInfo-R98-Ext OPTIONAL + }, + ..., + timeAssistanceMeasurements GPSTimeAssistanceMeasurements OPTIONAL + -- Further R98 extensions here +} + +-- This is an addition to OTD-MeasureInfo element defined in original message, +-- If OTD-MeasureInfo is absent, or if one or more OTD-MsrElementRest are present +-- OTD-MeasureInfo-R98-Ext shall be absent. +-- OTD-MeasureInfo-R98-Ext +OTD-MeasureInfo-R98-Ext ::= SEQUENCE { + -- Measurement info elements + otdMsrFirstSets-R98-Ext OTD-MsrElementFirst-R98-Ext +} + +-- OTD measurement information Ext for the first set only +OTD-MsrElementFirst-R98-Ext ::= SEQUENCE { + -- additional measured neighbors in OTD measurements + otd-FirstSetMsrs-R98-Ext SeqOfOTD-FirstSetMsrs-R98-Ext OPTIONAL +} +SeqOfOTD-FirstSetMsrs-R98-Ext ::= SEQUENCE (SIZE(1..5)) OF OTD-FirstSetMsrs + +Rel-5-MsrPosition-Rsp-Extension ::= SEQUENCE { + + extended-reference Extended-reference OPTIONAL, + -- The extended-reference shall be included by the MS if and only if previously + -- received from the SMLC in a Measure Position Request. When included, the value sent + -- by the MS shall equal the value received from the SMLC. + + -- extension to Release 5, for RRLP pseudo-segmentation here + otd-MeasureInfo-5-Ext OTD-MeasureInfo-5-Ext OPTIONAL, + ulPseudoSegInd UlPseudoSegInd OPTIONAL, -- Included when uplink RRLP + -- Pseudo-segmentation is used, not included when no uplink pseudo-segmentation is used + ... + -- Possibly more extensions for Release 5 here later +} + +Extended-reference ::= SEQUENCE { + smlc-code INTEGER (0..63), + transaction-ID INTEGER (0..262143) +} + +OTD-MeasureInfo-5-Ext ::= SeqOfOTD-MsrElementRest + -- if more than one measurement sets are present this element is repeated + -- NumberOfSets - 1 (-1 = first set) combined in OTD-MeasureInfo-5-Ext and + -- OTD-MeasureInfo (e.g. if NumberOfSets is 3, then one otdMsrRestSets may + -- be sent in OTD-MeasureInfo-5-Ext and one in OTD-MeasureInfo) + +-- First part of Uplink RRLP Pseudo-segmentation indication, possibly more may be defined +-- in the future for segmentation with more than two segments. +UlPseudoSegInd ::= ENUMERATED { + firstOfMany (0), + secondOfMany(1) +} + +Rel5-MsrPosition-Req-Extension ::= SEQUENCE { + extended-reference Extended-reference, + ... + -- Possibly more extensions for Release 5 here later +} + +Rel5-AssistanceData-Extension ::= SEQUENCE { + extended-reference Extended-reference, + ... + +-- Possibly more extensions for Release 5 here later +} + +Rel-5-ProtocolError-Extension::= SEQUENCE { + extended-reference Extended-reference OPTIONAL, + -- The extended-reference shall be included by the MS if and only if previously + -- received from the SMLC. + -- When included, the value sent by the MS shall equal the value received from the SMLC. + ... + + -- Possibly more extensions for Release 5 here later +} + +-- Release 7 Extensions here + +Rel7-MsrPosition-Req-Extension ::= SEQUENCE { +velocityRequested NULL OPTIONAL, + ganssPositionMethod GANSSPositioningMethod OPTIONAL, + ganss-AssistData GANSS-AssistData OPTIONAL, + ganssCarrierPhaseMeasurementRequest NULL OPTIONAL, + ganssTODGSMTimeAssociationMeasurementRequest NULL OPTIONAL, +requiredResponseTime RequiredResponseTime OPTIONAL, + ... + -- Further Release 7 extentions here +} + +-- additional satellite systems may be added in future versions of the protocol +GANSSPositioningMethod ::= BIT STRING { + gps (0), + galileo (1)} (SIZE (2..16)) + +GANSS-AssistData ::= SEQUENCE { + ganss-controlHeader GANSS-ControlHeader +} + +GANSS-ControlHeader ::= SEQUENCE { + ganssCommonAssistData GANSSCommonAssistData OPTIONAL, + ganssGenericAssistDataList SeqOfGANSSGenericAssistDataElement OPTIONAL +} + +-- GANSS Common Assistance Data Elements +GANSSCommonAssistData ::= SEQUENCE { + ganssReferenceTime GANSSReferenceTime OPTIONAL, + ganssRefLocation GANSSRefLocation OPTIONAL, + ganssIonosphericModel GANSSIonosphericModel OPTIONAL, + ... +} + +-- List of GANSS Generic Assistance Data Elements, up to 8 GANSS +SeqOfGANSSGenericAssistDataElement ::= SEQUENCE (SIZE (1..8)) OF GANSSGenericAssistDataElement + +-- GANSS Generic Assistance Data Elements +GANSSGenericAssistDataElement ::= SEQUENCE { + ganssID INTEGER (0..7) OPTIONAL, -- Coding according to Annex + ganssTimeModel SeqOfGANSSTimeModel OPTIONAL, + ganssDiffCorrections GANSSDiffCorrections OPTIONAL, + ganssNavigationModel GANSSNavModel OPTIONAL, + ganssRealTimeIntegrity GANSSRealTimeIntegrity OPTIONAL, + ganssDataBitAssist GANSSDataBitAssist OPTIONAL, + ganssRefMeasurementAssist GANSSRefMeasurementAssist OPTIONAL, + ganssAlmanacModel GANSSAlmanacModel OPTIONAL, + ganssUTCModel GANSSUTCModel OPTIONAL, + ... +} + +-- GANSS COMMON ASSISTANCE DATA ELEMENTS + +-- GANSS Reference Time IE +GANSSReferenceTime ::= SEQUENCE { + ganssRefTimeInfo GANSSRefTimeInfo, + ganssTOD-GSMTimeAssociation GANSSTOD-GSMTimeAssociation OPTIONAL +} + +-- GANSS Reference Time includes GANSS TOD, GANSS Day, uncertainty +GANSSRefTimeInfo ::= SEQUENCE { + ganssDay INTEGER(0 .. 8191) OPTIONAL, + ganssTOD GANSSTOD, + ganssTODUncertainty GANSSTODUncertainty OPTIONAL, + ganssTimeID INTEGER (0 .. 7) OPTIONAL +} + +-- GANSS TOD integer seconds +GANSSTOD ::= INTEGER (0 .. 86399) + +-- GANSS TOD uncertainty +GANSSTODUncertainty ::= INTEGER (0 .. 127) -- Coding according to Annex + +-- GANSS TOD-GSM Time association +GANSSTOD-GSMTimeAssociation ::= SEQUENCE { + bcchCarrier BCCHCarrier, -- BCCH carrier + bsic BSIC, -- BSIC + frameNumber FrameNumber, + timeSlot TimeSlot, + bitNumber BitNumber, + frameDrift FrameDrift OPTIONAL +} + +-- Frame drift +FrameDrift ::= INTEGER(-64 .. 63) + +-- GANSS Reference Location IE +GANSSRefLocation ::= SEQUENCE { + threeDLocation Ext-GeographicalInformation +} + +-- GANSS Ionospheric Model IE +-- GANSS Ionospheric Model consists of NeQuick model parameters and storm flags + +GANSSIonosphericModel ::= SEQUENCE { + ganssIonoModel GANSSIonosphereModel, + ganssIonoStormFlags GANSSIonoStormFlags OPTIONAL, + ... +} + +-- GANSS ionosphere model. Coding according to Annex +GANSSIonosphereModel ::= SEQUENCE { + ai0 INTEGER (0 .. 4095), + ai1 INTEGER (0 .. 4095), + ai2 INTEGER (0 .. 4095) +} + +-- GANSS ionosphere storm flags +GANSSIonoStormFlags ::= SEQUENCE { + ionoStormFlag1 INTEGER (0 .. 1), + ionoStormFlag2 INTEGER (0 .. 1), + ionoStormFlag3 INTEGER (0 .. 1), + ionoStormFlag4 INTEGER (0 .. 1), + ionoStormFlag5 INTEGER (0 .. 1) +} + +-- GANSS GENERIC ASSISTANCE DATA ELEMENTS + +-- GANSS Time Model IE consists of time offset and first and second order parameters to relate GNSS +-- specific system time to selected time reference +SeqOfGANSSTimeModel ::= SEQUENCE (SIZE(1..7)) OF GANSSTimeModelElement + +GANSSTimeModelElement ::= SEQUENCE { + ganssTimeModelRefTime INTEGER(0 .. 65535), + tA0 TA0, + tA1 TA1 OPTIONAL, + tA2 TA2 OPTIONAL, + gnssTOID INTEGER (0 .. 7), + weekNumber INTEGER (0 .. 8191) OPTIONAL +} + +-- GANSS time model parameter A0 +TA0 ::= INTEGER (-2147483648 .. 2147483647) + +-- GANSS time model parameter A1 +TA1 ::= INTEGER (-8388608 .. 8388607) + +-- GANSS time model parameter A2 +TA2 ::= INTEGER (-64 .. 63) + +-- DGANSS Corrections IE +GANSSDiffCorrections ::= SEQUENCE { + dganssRefTime INTEGER (0 .. 119), -- DGANSS reference time + + -- N_SGN_TYPE can be read from number of elements of sgnTypeList + sgnTypeList SeqOfSgnTypeElement +} + +SeqOfSgnTypeElement ::= SEQUENCE (SIZE (1..3)) OF SgnTypeElement -- max three signals per GNSS + +-- DGANSS signal type element, once per GNSS signal type included in DGANSS +SgnTypeElement ::= SEQUENCE { + ganssSignalID GANSSSignalID OPTIONAL, -- signal type identity + ganssStatusHealth INTEGER (0 .. 7), + -- N_SGN can be read from number of elements of dganssSgnList + dganssSgnList SeqOfDGANSSSgnElement +} + +GANSSSignalID ::= INTEGER (0 .. 3) -- Coding according to Annex +SeqOfDGANSSSgnElement ::= SEQUENCE (SIZE (1..16)) OF DGANSSSgnElement + +-- number of correction for signals +DGANSSSgnElement ::= SEQUENCE { + svID SVID, -- Satellite identity + +--- Sequence number for GANSS Navigation Model that matches the DGANSS correction set + iod INTEGER (0 .. 1023), + + -- User Differential Range Error + udre INTEGER (0..3), + + -- Pseudo Range Correction, range is + -- -655.04 - +655.04, + pseudoRangeCor INTEGER (-2047..2047), + + -- Pseudo Range Rate Correction, range is + -- -4.064 - +4.064, + rangeRateCor INTEGER (-127..127) +} + +SVID ::= INTEGER (0 .. 63) -- Coding according to Annex + +-- GANSS Navigation Model IE +GANSSNavModel ::= SEQUENCE { + nonBroadcastIndFlag INTEGER (0 .. 1), + toeMSB INTEGER (0 .. 31) OPTIONAL, -- 5 MSB of toe and toc + eMSB INTEGER (0 .. 127) OPTIONAL, + sqrtAMBS INTEGER (0 .. 63) OPTIONAL, + ganssSatelliteList SeqOfGANSSSatelliteElement +} + +SeqOfGANSSSatelliteElement ::= SEQUENCE (SIZE(1..32)) OF GANSSSatelliteElement + +GANSSSatelliteElement ::= SEQUENCE { + svID SVID, + svHealth INTEGER (-7 .. 13), -- Coding according to Annex + iod INTEGER (0 .. 1023), -- Coding according to Annex + ganssClockModel GANSSClockModel, + ganssOrbitModel GANSSOrbitModel, + ... +} + +-- GANSS orbit model for the GNSS satellite according to the choice +GANSSOrbitModel ::= CHOICE { + keplerianSet NavModel-KeplerianSet, + ... +} + +-- Navigation model in Keplerian parameters +NavModel-KeplerianSet ::= SEQUENCE { + keplerToeLSB INTEGER (0 .. 511), -- 9LSB are given in GANSSNavigationModel + keplerW INTEGER (-2147483648..2147483647), + keplerDeltaN INTEGER (-32768..32767), + keplerM0 INTEGER (-2147483648..2147483647), + keplerOmegaDot INTEGER (-8388608..8388607), + keplerELSB INTEGER (0..33554431), + keplerIDot INTEGER (-8192..8191), + keplerAPowerHalfLSB INTEGER (0.. 67108863), + keplerI0 INTEGER (-2147483648..2147483647), + keplerOmega0 INTEGER (-2147483648..2147483647), + keplerCrs INTEGER (-32768..32767), + keplerCis INTEGER (-32768..32767), + keplerCus INTEGER (-32768..32767), + keplerCrc INTEGER (-32768..32767), + keplerCic INTEGER (-32768..32767), + keplerCuc INTEGER (-32768..32767) +} + +-- GANSS clock model for the GNSS satellite according to the choice +GANSSClockModel ::= CHOICE { + standardClockModelList SeqOfStandardClockModelElement, + ... +} + +SeqOfStandardClockModelElement ::= SEQUENCE (SIZE(1..2)) OF StandardClockModelElement + +StandardClockModelElement ::= SEQUENCE { + stanClockTocLSB INTEGER (0 .. 511), -- 9LSB of time of clock + stanClockAF2 INTEGER (-2048 .. 2047), + stanClockAF1 INTEGER (-131072 .. 131071), + stanClockAF0 INTEGER (-134217728 .. 134217727), + stanClockTgd INTEGER (-512 .. 511) OPTIONAL, + stanModelID INTEGER (0 .. 1) OPTIONAL, + ... +} + +-- GANSS Real-Time Integrity IE +GANSSRealTimeIntegrity ::= SEQUENCE { + -- list of bad signals + -- NBS can be read from number of elements in SeqOf-BadSignalSet + ganssBadSignalList SeqOfBadSignalElement +} + +SeqOfBadSignalElement ::= SEQUENCE (SIZE(1..16)) OF BadSignalElement + +BadSignalElement ::= SEQUENCE { + badSVID SVID, -- Coding according to Annex + badSignalID INTEGER (0 .. 3) OPTIONAL -- Coding according to Annex +} + + +-- GANSS Data Bit Assistance IE +GANSSDataBitAssist ::= SEQUENCE { + ganssTOD INTEGER (0 .. 59), + svID SVID, + ganssDataTypeID INTEGER (0 .. 2), -- Coding according to Annex + -- list of navigation data bits + -- N_BIT can be read from number of elements in SeqOf-DataBits + ganssDataBits SeqOf-GANSSDataBits +} + +SeqOf-GANSSDataBits ::= SEQUENCE (SIZE(1 .. 1024)) OF GANSSDataBit +GANSSDataBit ::= INTEGER(0 .. 1) + +-- GANSS Reference Measurement Assistance IE +-- Code and Doppler assistance from the network. +GANSSRefMeasurementAssist ::= SEQUENCE { + ganssSignalID INTEGER (0 .. 3) OPTIONAL, -- Coding according to Annex + ganssRefMeasAssitList SeqOfGANSSRefMeasurementElement +} + +SeqOfGANSSRefMeasurementElement ::= SEQUENCE (SIZE(1 .. 16)) OF GANSSRefMeasurementElement + +GANSSRefMeasurementElement ::= SEQUENCE { + svID SVID, + -- Doppler 0th order term, + -- -1024 m/s to 1023.5 m/s with 0.5 m/s resolution) + doppler0 INTEGER (-2048 .. 2047), -- Coding according to Annex + additionalDoppler AdditionalDopplerFields OPTIONAL, + codePhase INTEGER (0 .. 1022), -- Code Phase in ms + intCodePhase INTEGER (0 .. 127), -- Integer Code Phase in ms + codePhaseSearchWindow INTEGER (0 .. 31), -- Code Phase Search Window, see Annex + additionalAngle AddionalAngleFields OPTIONAL, + ... +} + +AdditionalDopplerFields ::= SEQUENCE { + -- Doppler 1st order term, -0.2 - +0.1 m/s2 + doppler1 INTEGER (0..63), + dopplerUncertainty INTEGER (0..4) +} + +-- GANSS Almanac Model IE +GANSSAlmanacModel ::= SEQUENCE { + weekNumber INTEGER (0 .. 255), + svIDMask SVIDMASK, + toa INTEGER (0 .. 255) OPTIONAL, + ioda INTEGER (0 .. 3) OPTIONAL, + ganssAlmanacList SeqOfGANSSAlmanacElement +} + +-- SV ID Mask, LSB for ID 1 and MSB for ID 36 +SVIDMASK ::= BIT STRING (SIZE (1..36)) + +SeqOfGANSSAlmanacElement ::= SEQUENCE (SIZE(1 .. 36)) OF GANSSAlmanacElement + +-- GANSS Almanac Model +GANSSAlmanacElement ::= CHOICE { + keplerianAlmanacSet Almanac-KeplerianSet, + ... +} + +-- Almanac parameters according to Keplerian parameters +Almanac-KeplerianSet ::= SEQUENCE { + kepAlmanacE INTEGER (0 .. 2047), + kepAlmanacDeltaI INTEGER (-1024 .. 1023), + kepAlmanacOmegaDot INTEGER (-1024 .. 1023), + kepSVHealth INTEGER (0 .. 15), -- Coding according to Annex + kepAlmanacAPowerHalf INTEGER (-65536 .. 65535), + kepAlmanacOmega0 INTEGER (-32768 .. 32767), + kepAlmanacW INTEGER (-32768 .. 32767), + kepAlmanacM0 INTEGER (-32768 .. 32767), + kepAlmanacAF0 INTEGER (-8192 .. 8191), + kepAlmanacAF1 INTEGER (-1024..1023) +} + +-- GANSS Universal Time Coordinate Model +GANSSUTCModel ::= SEQUENCE { + ganssUtcA1 INTEGER (-8388608..8388607), + ganssUtcA0 INTEGER (-2147483648..2147483647), + ganssUtcTot INTEGER (0..255), + ganssUtcWNt INTEGER (0..255), + ganssUtcDeltaTls INTEGER (-128..127), + ganssUtcWNlsf INTEGER (0..255), + ganssUtcDN INTEGER (-128..127), + ganssUtcDeltaTlsf INTEGER (-128..127) +} + +--Required Measurement Request Response Time, range is 1 to 128 seconds. +RequiredResponseTime ::= INTEGER (1..128) + +Rel-7-MsrPosition-Rsp-Extension ::= SEQUENCE { + + velEstimate VelocityEstimate OPTIONAL, + -- Horizontal Velocity + -- Horizontal with Vertical Velocity + -- Horizontal Velocity with Uncertainty + -- Horizontal with Vertical Velocity and Uncertainty + ganssLocationInfo GANSSLocationInfo OPTIONAL, + ganssMeasureInfo GANSSMeasureInfo OPTIONAL, + ... +-- Further Release 7 extensions here +} + +-- GANSS Location Information contains location estimate, time stamp with uncertainty +-- and optionally Reference Frame field +GANSSLocationInfo ::= SEQUENCE { + referenceFrame ReferenceFrame OPTIONAL, -- Reference Frame Number + ganssTODm GANSSTODm OPTIONAL, -- GNSS TOD modulo + ganssTODFrac INTEGER (0 .. 16384) OPTIONAL, -- Coding according to Annex + ganssTODUncertainty GANSSTODUncertainty OPTIONAL, -- Coding according to Annex + ganssTimeID INTEGER (0 .. 3) OPTIONAL, -- Coding according to Annex + fixType FixType, + posData PositionData, + stationaryIndication INTEGER(0 .. 1) OPTIONAL, -- '0' if moving or motion not known + -- Possible shapes carried in posEstimate are + -- ellipsoid point, + -- ellipsoid point with uncertainty circle + -- ellipsoid point with uncertainty ellipse + -- ellipsoid point with altitude and uncertainty ellipsoid + posEstimate Ext-GeographicalInformation, + ... +} + +PositionData ::= BIT STRING { + e-otd(0), + gps (1), + galileo (2) } (SIZE (3..16)) + + +-- GANSS TOD modulo 1 hour +GANSSTODm ::= INTEGER (0 .. 3599999) + +ReferenceFrame ::= SEQUENCE { + referenceFN INTEGER (0 .. 65535), + -- Note that applicable range for referenceFN is 0 - 42431 + referenceFNMSB INTEGER (0 .. 63) OPTIONAL -- MSB of Reference Frame Number +} + + + +-- GANSS Measurement Information +GANSSMeasureInfo ::= SEQUENCE { + -- Measurement info elements + -- user has to make sure that in this element is number of elements + -- defined in reference BTS identity + ganssMsrSetList SeqOfGANSS-MsrSetElement +} +SeqOfGANSS-MsrSetElement ::= SEQUENCE (SIZE(1..3)) OF GANSS-MsrSetElement + +-- GANSS measurement information 1-3 times in a message +GANSS-MsrSetElement ::= SEQUENCE { + referenceFrame ReferenceFrame OPTIONAL, -- Reference Frame Number + ganssTODm GANSSTODm OPTIONAL, -- GANSS TOD modulo + deltaGNASSTOD INTEGER (0 .. 127) OPTIONAL, + ganssTODUncertainty GANSSTODUncertainty OPTIONAL, -- Coding accoring to Annex + + --N_SGN_TYPE can be read from number of elements of SeqOfGANSS-SgnTypeElement + ganss-SgnTypeList SeqOfGANSS-SgnTypeElement +} + +-- Measurements can be returned up to 6 different signal types +SeqOfGANSS-SgnTypeElement ::= SEQUENCE (SIZE(1..6)) OF GANSS-SgnTypeElement + +GANSS-SgnTypeElement ::= SEQUENCE { + ganssSignalID INTEGER (0 .. 15), -- Coding accroding to Annex + --N_SGN can be read from number of elements of SeqOfGANSS-SgnElement + ganss-SgnList SeqOfGANSS-SgnElement +} + +-- Measurements can be returned up to 16 per signal types +SeqOfGANSS-SgnElement ::= SEQUENCE (SIZE(1..16)) OF GANSS-SgnElement + + +GANSS-SgnElement ::= SEQUENCE { + svID SVID, + cNo INTEGER (0 .. 63), + mpathDet MpathIndic, -- Coding according to Annex + carrierQualityInd INTEGER (0 .. 3) OPTIONAL, -- Coding according to Annex + codePhase INTEGER (0 .. 2097151), + integerCodePhase INTEGER (0 .. 63) OPTIONAL, + codePhaseRMSError INTEGER (0..63), -- Coding accoring to Annex + doppler INTEGER (-32768 .. 32767) OPTIONAL, + adr INTEGER (0 .. 33554431) OPTIONAL +} + +Rel7-AssistanceData-Extension ::= SEQUENCE { + ganss-AssistData GANSS-AssistData OPTIONAL, + ganssCarrierPhaseMeasurementRequest NULL OPTIONAL, + ganssTODGSMTimeAssociationMeasurementRequest NULL OPTIONAL, + ... +-- Possibly more extensions for Release 7 here +} + +END diff --git a/rrlp-ephemeris/asn1/RRLP-Messages.asn b/rrlp-ephemeris/asn1/RRLP-Messages.asn new file mode 100644 index 000000000..79140e2a2 --- /dev/null +++ b/rrlp-ephemeris/asn1/RRLP-Messages.asn @@ -0,0 +1,38 @@ +-- RRLP-Messages.asn +-- $Id$ +-- Taken from 3GPP TS 44.031 V7.4.0 (2007-03) +-- http://www.3gpp.org/ftp/Specs/archive/44_series/44.031/44031-740.zip/44031-740.doc +-- +-- 3.1 General Format of RRLP Message +-- + +RRLP-Messages +-- { RRLP-messages } + +DEFINITIONS AUTOMATIC TAGS ::= + +BEGIN + +IMPORTS + MsrPosition-Req, MsrPosition-Rsp, AssistanceData, + ProtocolError +FROM + RRLP-Components -- { RRLP-Components } +; + +PDU ::= SEQUENCE { + referenceNumber INTEGER (0..7), + component RRLP-Component +} + +RRLP-Component ::= CHOICE { + msrPositionReq MsrPosition-Req, + msrPositionRsp MsrPosition-Rsp, + assistanceData AssistanceData, + assistanceDataAck NULL, + protocolError ProtocolError, + ... + +} + +END diff --git a/rrlp-ephemeris/asn1/patch-rrlp-components.diff b/rrlp-ephemeris/asn1/patch-rrlp-components.diff new file mode 100644 index 000000000..a5e55ae06 --- /dev/null +++ b/rrlp-ephemeris/asn1/patch-rrlp-components.diff @@ -0,0 +1,36 @@ +--- RRLP-Components.asn 2009-10-26 22:10:44.000000000 +0100 ++++ RRLP-Components.asn 2009-10-26 22:10:44.000000000 +0100 +@@ -18,16 +18,16 @@ + Ext-GeographicalInformation, VelocityEstimate + FROM + MAP-LCS-DataTypes { +- ccitt identified-organization (4) etsi (0) mobileDomain (0) +- gsm-Network (1) modules (3) map-LCS-DataTypes (25) version5 (5)} ++ itu-t identified-organization (4) etsi (0) mobileDomain (0) ++ gsm-Network (1) modules (3) map-LCS-DataTypes (25) version11 (11)} + + ExtensionContainer + FROM MAP-ExtensionDataTypes { +- ccitt identified-organization (4) etsi (0) mobileDomain (0) +- gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version4 (4)} ++ itu-t identified-organization (4) etsi (0) mobileDomain (0) ++ gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version11 (11)} + ; + + -- Add here other ASN.1 definitions presented below + -- in chapters 4 and 5. + +@@ -305,11 +305,11 @@ + SystemInfoIndex ::= INTEGER (1..32) + + CellIDAndLAC ::= SEQUENCE { +- referenceLAC LAC, -- Location area code ++ referenceLAC LACID, -- Location area code + referenceCI CellID -- Cell identity + } + CellID ::= INTEGER (0..65535) +-LAC ::= INTEGER (0..65535) ++LACID ::= INTEGER (0..65535) + + -- OTD-MeasureInfo + OTD-MeasureInfo ::= SEQUENCE { diff --git a/rrlp-ephemeris/asn1c_patches/00_add_enumerated_verbose.diff b/rrlp-ephemeris/asn1c_patches/00_add_enumerated_verbose.diff new file mode 100644 index 000000000..64c22a3cf --- /dev/null +++ b/rrlp-ephemeris/asn1c_patches/00_add_enumerated_verbose.diff @@ -0,0 +1,56 @@ +Index: skeletons/NativeEnumerated.c +=================================================================== +--- skeletons/NativeEnumerated.c (revision 1407) ++++ skeletons/NativeEnumerated.c (working copy) +@@ -22,7 +22,7 @@ + "ENUMERATED", /* The ASN.1 type is still ENUMERATED */ + "ENUMERATED", + NativeInteger_free, +- NativeInteger_print, ++ NativeEnumerated_print, + asn_generic_no_constraint, + NativeInteger_decode_ber, + NativeInteger_encode_der, +@@ -205,3 +205,30 @@ + _ASN_ENCODED_OK(er); + } + ++int ++NativeEnumerated_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, ++ asn_app_consume_bytes_f *cb, void *app_key) { ++ asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; ++ const long *native = (const long *)sptr; ++ char scratch[256]; ++ int ret; ++ ++ (void)td; /* Unused argument */ ++ (void)ilevel; /* Unused argument */ ++ ++ if(native) { ++ const asn_INTEGER_enum_map_t *map = INTEGER_map_value2enum(specs, *native); ++ if (map && map->enum_len && map->enum_name) { ++ ret = snprintf(scratch, sizeof(scratch), ++ "%s", map->enum_name); ++ } else { ++ ret = snprintf(scratch, sizeof(scratch), ++ (specs && specs->field_unsigned) ++ ? "%lu" : "%ld", *native); ++ } ++ assert(ret > 0 && (size_t)ret < sizeof(scratch)); ++ return (cb(scratch, ret, app_key) < 0) ? -1 : 0; ++ } else { ++ return (cb("<absent>", 8, app_key) < 0) ? -1 : 0; ++ } ++} +Index: skeletons/NativeEnumerated.h +=================================================================== +--- skeletons/NativeEnumerated.h (revision 1407) ++++ skeletons/NativeEnumerated.h (working copy) +@@ -24,6 +24,7 @@ + xer_type_encoder_f NativeEnumerated_encode_xer; + per_type_decoder_f NativeEnumerated_decode_uper; + per_type_encoder_f NativeEnumerated_encode_uper; ++asn_struct_print_f NativeEnumerated_print; + + #ifdef __cplusplus + } diff --git a/rrlp-ephemeris/asn1c_patches/01_fix_per_encoding_dieter.diff b/rrlp-ephemeris/asn1c_patches/01_fix_per_encoding_dieter.diff new file mode 100644 index 000000000..a09c20159 --- /dev/null +++ b/rrlp-ephemeris/asn1c_patches/01_fix_per_encoding_dieter.diff @@ -0,0 +1,17 @@ +Index: skeletons/per_support.c +=================================================================== +--- skeletons/per_support.c (revision 1407) ++++ skeletons/per_support.c (working copy) +@@ -336,7 +336,12 @@ + buf[3] = bits; + else { + ASN_DEBUG("->[PER out split %d]", obits); ++#if 1 // Dieter ++ po->nboff -= obits; // undo incrementation from a few lines above ++ per_put_few_bits(po, bits >> (obits - 24), 24); // shift according to the rest of the bits ++#else + per_put_few_bits(po, bits >> 8, 24); ++#endif + per_put_few_bits(po, bits, obits - 24); + ASN_DEBUG("<-[PER out split %d]", obits); + } diff --git a/rrlp-ephemeris/data.ubx b/rrlp-ephemeris/data.ubx Binary files differnew file mode 100644 index 000000000..07cdddcd9 --- /dev/null +++ b/rrlp-ephemeris/data.ubx diff --git a/rrlp-ephemeris/get-test-data.sh b/rrlp-ephemeris/get-test-data.sh new file mode 100755 index 000000000..169f43c8f --- /dev/null +++ b/rrlp-ephemeris/get-test-data.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +DEV=$1 +OUTF=$2 + +# Change mode +echo -en "\$PUBX,41,1,0001,0001,9600,0*14\r\n" > ${DEV} + +# Wait a little +sleep 2 + +# Start dump +echo -en "\xb5\x62\x01\x02\x00\x00\x03\x0a" | \ + socat -t5 ${DEV},b9600,raw,clocal=1,echo=0 - > ${OUTF} +echo -en "\xb5\x62\x0b\x10\x00\x00\x1b\x5c" | \ + socat -t10 ${DEV},b9600,raw,clocal=1,echo=0 - >> ${OUTF} + diff --git a/rrlp-ephemeris/gpl-2.0.txt b/rrlp-ephemeris/gpl-2.0.txt new file mode 100644 index 000000000..d511905c1 --- /dev/null +++ b/rrlp-ephemeris/gpl-2.0.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/rrlp-ephemeris/gpl-3.0.txt b/rrlp-ephemeris/gpl-3.0.txt new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/rrlp-ephemeris/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/rrlp-ephemeris/gps.c b/rrlp-ephemeris/gps.c new file mode 100644 index 000000000..c23574840 --- /dev/null +++ b/rrlp-ephemeris/gps.c @@ -0,0 +1,126 @@ +/* + * gps.c + * + * A few utility functions to deal with low level GPS data + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "gps.h" + + +#define GET_FIELD_U(w, nb, pos) (((w) >> (pos)) & ((1<<(nb))-1)) +#define GET_FIELD_S(w, nb, pos) (((int)((w) << (32-(nb)-(pos)))) >> (32-(nb))) + +/* + * Unpacks GPS Subframe 1,2,3 payloads (3 * 8 words) + * + * Note: eph->sv_id is not filled here since not present in those subframes + * + * (no parity bit checking is done, only the lower 24 bits of each word + * are used) + */ +int +gps_unpack_sf123(uint32_t *sf, struct gps_ephemeris_sv *eph) +{ + uint32_t *sf1 = &sf[0]; + uint32_t *sf2 = &sf[8]; + uint32_t *sf3 = &sf[16]; + + int iode1, iode2; + + eph->week_no = GET_FIELD_U(sf1[0], 10, 14); + eph->code_on_l2 = GET_FIELD_U(sf1[0], 2, 12); + eph->sv_ura = GET_FIELD_U(sf1[0], 4, 8); + eph->sv_health = GET_FIELD_U(sf1[0], 6, 2); + eph->l2_p_flag = GET_FIELD_U(sf1[1], 1, 23); + eph->t_gd = GET_FIELD_S(sf1[4], 8, 0); + eph->iodc = (GET_FIELD_U(sf1[0], 2, 0) << 8) | \ + GET_FIELD_U(sf1[5], 8, 16); + eph->t_oc = GET_FIELD_U(sf1[5], 16, 0); + eph->a_f2 = GET_FIELD_S(sf1[6], 8, 16); + eph->a_f1 = GET_FIELD_S(sf1[6], 16, 0); + eph->a_f0 = GET_FIELD_S(sf1[7], 22, 2); + + iode1 = GET_FIELD_U(sf2[0], 8, 16); + eph->c_rs = GET_FIELD_S(sf2[0], 16, 0); + eph->delta_n = GET_FIELD_S(sf2[1], 16, 8); + eph->m_0 = (GET_FIELD_S(sf2[1], 8, 0) << 24) | \ + GET_FIELD_U(sf2[2], 24, 0); + eph->c_uc = GET_FIELD_S(sf2[3], 16, 8); + eph->e = (GET_FIELD_U(sf2[3], 8, 0) << 24) | \ + GET_FIELD_U(sf2[4], 24, 0); + eph->c_us = GET_FIELD_S(sf2[5], 16, 8); + eph->a_powhalf = (GET_FIELD_U(sf2[5], 8, 0) << 24) | \ + GET_FIELD_U(sf2[6], 24, 0); + eph->t_oe = GET_FIELD_U(sf2[7], 16, 8); + eph->fit_flag = GET_FIELD_U(sf2[7], 1, 7); + + eph->c_ic = GET_FIELD_S(sf3[0], 16, 8); + eph->omega_0 = (GET_FIELD_S(sf3[0], 8, 0) << 24) | \ + GET_FIELD_U(sf3[1], 24, 0); + eph->c_is = GET_FIELD_S(sf3[2], 16, 8); + eph->i_0 = (GET_FIELD_S(sf3[2], 8, 0) << 24) | \ + GET_FIELD_U(sf3[3], 24, 0); + eph->c_rc = GET_FIELD_S(sf3[4], 16, 8); + eph->w = (GET_FIELD_S(sf3[4], 8, 0) << 24) | \ + GET_FIELD_U(sf3[5], 24, 0); + eph->omega_dot = GET_FIELD_S(sf3[6], 24, 0); + iode2 = GET_FIELD_U(sf3[7], 8, 16); + eph->idot = GET_FIELD_S(sf3[7], 14, 2); + + eph->_rsvd1 = GET_FIELD_U(sf1[1], 23, 0); + eph->_rsvd2 = GET_FIELD_U(sf1[2], 24, 0); + eph->_rsvd3 = GET_FIELD_U(sf1[3], 24, 0); + eph->_rsvd4 = GET_FIELD_U(sf1[4], 16, 8); + eph->aodo = GET_FIELD_U(sf2[7], 5, 2); + + /* Check & cross-validate iodc[7:0], iode1, iode2 */ + if ((iode1 != iode2) || (iode1 != (eph->iodc & 0xff))) + return -1; + + return 0; +} + + +/* + * Unpacks GPS Subframe 4 or 5 Almanac pages payload (8 words) + * + * (no parity bit checking is done, only the lower 24 bits of each word + * are used) + */ +int +gps_unpack_sf45_almanac(uint32_t *sf, struct gps_almanac_sv *alm) +{ + alm->sv_id = GET_FIELD_U(sf[0], 6, 16); + + alm->e = GET_FIELD_U(sf[0], 16, 0); + alm->t_oa = GET_FIELD_U(sf[1], 8, 16); + alm->ksii = GET_FIELD_S(sf[1], 16, 0); + alm->omega_dot = GET_FIELD_S(sf[2], 16, 8); + alm->sv_health = GET_FIELD_U(sf[2], 8, 0); + alm->a_powhalf = GET_FIELD_U(sf[3], 24, 0); + alm->omega_0 = GET_FIELD_S(sf[4], 24, 0); + alm->w = GET_FIELD_S(sf[5], 24, 0); + alm->m_0 = GET_FIELD_S(sf[6], 24, 0); + alm->a_f0 = (GET_FIELD_S(sf[7], 8, 16) << 3) | \ + GET_FIELD_U(sf[7], 3, 2); + alm->a_f1 = GET_FIELD_S(sf[7], 11, 5); + + return 0; +} + diff --git a/rrlp-ephemeris/gps.h b/rrlp-ephemeris/gps.h new file mode 100644 index 000000000..241b9d743 --- /dev/null +++ b/rrlp-ephemeris/gps.h @@ -0,0 +1,190 @@ +/* + * gps.h + * + * Header to deal with low level GPS data + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GPS_H__ +#define __GPS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + + +#define MAX_SV 64 + + +/* Ionosperic model data */ +struct gps_ionosphere_model { + /* #bits Scale factor Effective Units */ + /* (LSB) range */ + + int alpha_0; /* s 8 2^-30 seconds */ + int alpha_1; /* s 8 2^-27 s / semi-circles */ + int alpha_2; /* s 8 2^-24 s / (semi-circles)^2 */ + int alpha_3; /* s 8 2^-24 s / (semi-circles)^3 */ + int beta_0; /* s 8 2^11 seconds */ + int beta_1; /* s 8 2^14 s / semi-circles */ + int beta_2; /* s 8 2^16 s / (semi-circles)^2 */ + int beta_3; /* s 8 2^16 s / (semi-circles)^3 */ +}; + + +/* UTC model data */ +struct gps_utc_model { + /* #bits Scale factor Effective Units */ + /* (LSB) range */ + + int a0; /* s 32 2^-30 seconds */ + int a1; /* s 24 2^-50 seconds / seconds */ + int delta_t_ls; /* s 8 1 seconds */ + int t_ot; /* u 8 2^12 602,112 seconds */ + int wn_t; /* u 8 1 weeks */ + int wn_lsf; /* u 8 1 weeks */ + int dn; /* u 8 1 7 days */ + int delta_t_lsf;/* s 8 1 seconds */ +}; + + +/* Almanach data */ +struct gps_almanac_sv { + int sv_id; + int sv_health; + + /* #bits Scale factor Effective Units */ + /* (LSB) range */ + + int e; /* u 16 2^-21 */ + int t_oa; /* u 8 2^12 602,112 seconds */ + int ksii; /* s 16 2^-19 semi-circles */ + int omega_dot; /* s 16 2^-38 semi-circles / s */ + int a_powhalf; /* u 24 2^-11 meters */ + int omega_0; /* s 24 2^-23 semi-circles */ + int w; /* s 24 2^-23 semi-circles */ + int m_0; /* s 24 2^-23 semi-circles */ + int a_f0; /* s 11 2^-20 seconds */ + int a_f1; /* s 11 2^-38 seconds / seconds */ +}; + +struct gps_almanac { + int wna; + int n_sv; + struct gps_almanac_sv svs[MAX_SV]; +}; + + +/* Ephemeris data */ +struct gps_ephemeris_sv { + int sv_id; + + /* #bits Scale factor Effective Units */ + /* (LSB) range */ + + int code_on_l2; /* u 2 1 / */ + int week_no; /* u 10 1 week */ + int l2_p_flag; /* u 1 1 / */ + int sv_ura; /* u 4 / / */ + int sv_health; /* u 6 / / */ + int t_gd; /* s 8 2^-31 seconds */ + int iodc; /* u 10 / / */ + int t_oc; /* u 16 2^4 604,784 seconds */ + int a_f2; /* s 8 2^-55 sec / sec^2 */ + int a_f1; /* s 16 2^-43 sec / sec */ + int a_f0; /* s 22 2^-31 seconds */ + + int c_rs; /* s 16 2^-5 meters */ + int delta_n; /* s 16 2^-43 semi-circles / s */ + int m_0; /* s 32 2^-31 semi-circles */ + int c_uc; /* s 16 2^-29 radians */ + unsigned int e; /* u 32 2^-33 0.03 / */ + int c_us; /* s 16 2^-29 radians */ + unsigned int a_powhalf; /* u 32 2^-19 meters^(1/2) */ + int t_oe; /* u 16 2^4 604,784 seconds */ + int fit_flag; /* u 1 / / */ + + int c_ic; /* s 16 2^-29 radians */ + int omega_0; /* s 32 2^-31 semi-circles */ + int c_is; /* s 16 2^-29 radians */ + int i_0; /* s 32 2^-31 semi-circles */ + int c_rc; /* s 16 2^-5 meters */ + int w; /* s 32 2^-31 semi-circles */ + int omega_dot; /* s 24 2^-43 semi-circles / s */ + int idot; /* s 14 2^-43 semi-circles / s */ + + int _rsvd1; /* 23 bits */ + int _rsvd2; /* 24 bits */ + int _rsvd3; /* 24 bits */ + int _rsvd4; /* 16 bits */ + int aodo; /* 8 bits Not sure it needs to be here ... */ +}; + +struct gps_ephemeris { + int n_sv; + struct gps_ephemeris_sv svs[MAX_SV]; +}; + + +/* Reference position */ +struct gps_ref_pos { /* WSG84 ellipsoid */ + double latitude; /* deg */ + double longitude; /* deg */ + double altitude; /* m above ellipsoid */ +}; + + +/* Reference time */ +struct gps_ref_time { + int wn; /* GPS week number */ + double tow; /* in seconds */ +}; + + +/* All assist data */ +#define GPS_FIELD_IONOSPHERE (1<<0) +#define GPS_FIELD_UTC (1<<1) +#define GPS_FIELD_ALMANAC (1<<2) +#define GPS_FIELD_EPHEMERIS (1<<3) +#define GPS_FIELD_REFPOS (1<<4) +#define GPS_FIELD_REFTIME (1<<5) + +struct gps_assist_data { + int fields; + struct gps_ionosphere_model ionosphere; + struct gps_utc_model utc; + struct gps_almanac almanac; + struct gps_ephemeris ephemeris; + struct gps_ref_pos ref_pos; + struct gps_ref_time ref_time; +}; + + +/* GPS Subframe utility methods (see gps.c for details) */ +int gps_unpack_sf123(uint32_t *sf, struct gps_ephemeris_sv *eph); +int gps_unpack_sf45_almanac(uint32_t *sf, struct gps_almanac_sv *alm); + + +#ifdef __cplusplus +} +#endif + +#endif /* __GPS_H__ */ + diff --git a/rrlp-ephemeris/main.c b/rrlp-ephemeris/main.c new file mode 100644 index 000000000..bb025a2ba --- /dev/null +++ b/rrlp-ephemeris/main.c @@ -0,0 +1,99 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "gps.h" +#include "ubx.h" +#include "ubx-parse.h" +#include "rrlp.h" + +static int +do_ubx_read(struct gps_assist_data *gps, const char *filename) +{ + int rv, fd, i; + struct stat st; + void *buf; + + /* Load file */ + fd = open(filename, O_RDONLY); + if (fd < 0) + return -1; + + rv = fstat(fd, &st); + if (rv < 0) { + close(fd); + return -1; + } + + buf = malloc(st.st_size); + if (!buf) { + close(fd); + return -1; + } + + rv = read(fd, buf, st.st_size); + if (rv != st.st_size) { + free(buf); + close(fd); + return -1; + } + + /* Parse each message */ + for (i=0; i<st.st_size;) { + int rv; + rv = ubx_msg_dispatch(ubx_parse_dt, buf + i, st.st_size - i, gps); + if (rv < 0) + i++; /* Invalid message: try one byte later */ + else + i += rv; + } + + /* Done */ + free(buf); + close(fd); + + return 0; +} + +static int +do_rrlp(struct gps_assist_data *gps) +{ + struct rrlp_assist_req ar; + void *pdus[64]; + int len[64]; + int i, rv; + + char *test = "\x28\x00\x80\x10\x01\x32\x00\x19\x4F\x07\x15\x04"; + + rrlp_decode_assistance_request(&ar, test, 12); + printf("%08x %016llx\n", ar.req_elems, (long long unsigned) ar.eph_svs); + + ar.req_elems = -1; + ar.eph_svs = -1LL; + rv = rrlp_gps_assist_pdus(gps, &ar, pdus, len, 64); + printf("%d\n", rv); + for (i=0; i<rv; i++) { + printf("%p %d\n", pdus[i], len[i]); + } + return 0; +} + +int main(int argc, char *argv[]) +{ + struct gps_assist_data gps; + int rv; + + memset(&gps, 0x00, sizeof(gps)); + + rv = do_ubx_read(&gps, "data.ubx"); + + rv = do_rrlp(&gps); + + return 0; +} + diff --git a/rrlp-ephemeris/rrlp.c b/rrlp-ephemeris/rrlp.c new file mode 100644 index 000000000..e60c3ab01 --- /dev/null +++ b/rrlp-ephemeris/rrlp.c @@ -0,0 +1,648 @@ +/* + * rrlp.c + * + * RRLP implementation + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + +#include <errno.h> +#include <math.h> + +#include "gps.h" +#include "rrlp.h" + +#include <PDU.h> +#include <GPS-AssistData.h> +#include <NavigationModel.h> +#include <IonosphericModel.h> +#include <UTCModel.h> +#include <Almanac.h> +#include <RefLocation.h> +#include <ReferenceTime.h> + + +/* ------------------------------------------------------------------------ */ +/* RRLP Assistance request decoding */ +/* ---------------------------------------------------------------------{{{ */ +/* Decode and validate the assistance data request messages. + * See section 10.10 of + * . ETSI TS 149 031 V8.1.0 (2009-01) + * . 3GPP TS 49.031 version 8.1.0 Release 8 + */ + +/* Packed structure from 49.031 spec (RGA = Request GPS Assistance) */ + +#define RRLP_RGA0_ALMANAC (1<<0) +#define RRLP_RGA0_UTC_MODEL (1<<1) +#define RRLP_RGA0_IONO_MODEL (1<<2) +#define RRLP_RGA0_NAV_MODEL (1<<3) +#define RRLP_RGA0_DGPS (1<<4) +#define RRLP_RGA0_REF_LOC (1<<5) +#define RRLP_RGA0_REF_TIME (1<<6) +#define RRLP_RGA0_ACQ_ASSIST (1<<7) + +#define RRLP_RGA1_REALTIME_INT (1<<0) +#define RRLP_RGA1_EPH_EXT (1<<1) +#define RRLP_RGA1_EPH_EXT_CHECK (1<<2) + +struct rrlp_rga_hdr { + uint8_t items0; + uint8_t items1; +} __attribute__((packed)); + +struct rrlp_rga_eph_sv { + uint8_t sv_id; /* [7:6] reserved, [5:0] sv_id */ + uint8_t iode; /* latest eph in the MS memory in hours */ +} __attribute__((packed)); + +struct rrlp_rga_eph { + uint8_t wn_hi; /* [7:6] = wn[9:8] */ + uint8_t wn_lo; /* wn[7:0] */ + uint8_t toe; /* latest eph in the MS memory in hours */ + uint8_t nsat_tmtoe; /* [7:4] nstat, [3:0] T-Toe limit */ + struct rrlp_rga_eph_sv svs[0]; +} __attribute__((packed)); + +struct rrlp_rga_eph_ext { + uint8_t validity; /* in 4 hours units */ +} __attribute__((packed)); + +struct rrlp_rga_eph_ext_check { + /* weeks are in gps week modulo 4 */ + uint8_t wn_begin_end; /* [7:4] begin, [3:0] end */ + uint8_t tow_begin; + uint8_t tow_end; +} __attribute__((packed)); + + +/* Parsing function */ + +int +rrlp_decode_assistance_request( + struct rrlp_assist_req *ar, + void *req, int req_len) +{ + struct rrlp_rga_hdr *hdr = NULL; + struct rrlp_rga_eph *eph = NULL; + struct rrlp_rga_eph_ext *eph_ext = NULL; + struct rrlp_rga_eph_ext_check *eph_ext_check = NULL; + int p = 0; + + /* Reset */ + ar->req_elems = 0; + ar->eph_svs = 0; + + /* Parse message */ + hdr = req; + p += sizeof(struct rrlp_rga_hdr); + if (p > req_len) + return -1; + + if (hdr->items0 & RRLP_RGA0_NAV_MODEL) { + eph = req + p; + p += sizeof(struct rrlp_rga_eph); + if (p > req_len) + return -1; + p += (eph->nsat_tmtoe >> 4) * sizeof(struct rrlp_rga_eph_sv); + if (p > req_len) + return -1; + } + + if (hdr->items1 & RRLP_RGA1_EPH_EXT) { + eph_ext = req + p; + p += sizeof(struct rrlp_rga_eph_ext); + if (p > req_len) + return -1; + } + + if (hdr->items1 & RRLP_RGA1_EPH_EXT_CHECK) { + eph_ext_check = req + p; + p += sizeof(struct rrlp_rga_eph_ext_check); + if (p > req_len) + return -1; + } + + if (p != req_len) + return -2; /* not all bytes consumed ??? */ + + /* Print a warning for unsupported requests */ + if ((eph_ext != NULL) || + (eph_ext_check != NULL) || + (hdr->items0 & (RRLP_RGA0_DGPS | RRLP_RGA0_ACQ_ASSIST)) || + (hdr->items1 & RRLP_RGA1_REALTIME_INT)) { + fprintf(stderr, "[w] Unsupported assistance data requested, ignored ...\n"); + } + + /* Copy the request */ + if (hdr->items0 & RRLP_RGA0_ALMANAC) + ar->req_elems |= RRLP_AR_ALMANAC; + + if (hdr->items0 & RRLP_RGA0_UTC_MODEL) + ar->req_elems |= RRLP_AR_UTC_MODEL; + + if (hdr->items0 & RRLP_RGA0_IONO_MODEL) + ar->req_elems |= RRLP_AR_IONO_MODEL; + + if (hdr->items0 & RRLP_RGA0_REF_LOC) + ar->req_elems |= RRLP_AR_REF_LOC; + + if (hdr->items0 & RRLP_RGA0_REF_TIME) + ar->req_elems |= RRLP_AR_REF_TIME; + + if (hdr->items0 & RRLP_RGA0_NAV_MODEL) { + int i, n_svs = eph->nsat_tmtoe >> 4; + ar->req_elems |= RRLP_AR_EPHEMERIS; + for (i=0; i<n_svs; i++) + ar->eph_svs |= (1ULL << (eph->svs[i].sv_id - 1)); + } + + return 0; +} + +/* }}} */ + + +/* ------------------------------------------------------------------------ */ +/* RRLP elements fill */ +/* ---------------------------------------------------------------------{{{ */ + + /* Helpers */ + +static void +_ts_23_032_store_latitude(double lat, uint8_t *b) +{ + uint32_t x; + x = (uint32_t) floor(fabs(lat/90.0) * ((double)(1<<23))); + if (x >= (1<<23)) + x = (1<<23) - 1; + if (lat < 0.0) + x |= (1<<23); + b[0] = (x >> 16) & 0xff; + b[1] = (x >> 8) & 0xff; + b[2] = x & 0xff; +} + +static void +_ts_23_032_store_longitude(double lon, uint8_t *b) +{ + int32_t x; + x = floor((lon/360.0) * ((double)(1<<24))); + if (x >= (1<<23)) + x = 0x007fffff; + else if (x < -(1<<23)) + x = 0x00800000; + b[0] = (x >> 16) & 0xff; + b[1] = (x >> 8) & 0xff; + b[2] = x & 0xff; +} + +static void +_ts_23_032_store_altitude(double alt, uint8_t *b) +{ + int alt_i = (int)fabs(alt); + b[0] = ((alt_i >> 8) & 0x7f) | (alt<0.0 ? 0x80 : 0x00); + b[1] = alt_i & 0xff; +} + + + /* Fill methods */ + +static void +_rrlp_fill_navigation_model_element( + struct NavModelElement *rrlp_nme, + struct gps_ephemeris_sv *gps_eph_sv) +{ + struct UncompressedEphemeris *rrlp_eph; + + rrlp_nme->satStatus.present = SatStatus_PR_newSatelliteAndModelUC; + rrlp_nme->satelliteID = gps_eph_sv->sv_id; + + rrlp_eph = &rrlp_nme->satStatus.choice.newSatelliteAndModelUC; + + rrlp_eph->ephemCodeOnL2 = gps_eph_sv->code_on_l2; + rrlp_eph->ephemURA = gps_eph_sv->sv_ura; + rrlp_eph->ephemSVhealth = gps_eph_sv->sv_health; + rrlp_eph->ephemIODC = gps_eph_sv->iodc; + rrlp_eph->ephemL2Pflag = gps_eph_sv->l2_p_flag; + rrlp_eph->ephemTgd = gps_eph_sv->t_gd; + rrlp_eph->ephemToc = gps_eph_sv->t_oc; + rrlp_eph->ephemAF2 = gps_eph_sv->a_f2; + rrlp_eph->ephemAF1 = gps_eph_sv->a_f1; + rrlp_eph->ephemAF0 = gps_eph_sv->a_f0; + rrlp_eph->ephemCrs = gps_eph_sv->c_rs; + rrlp_eph->ephemDeltaN = gps_eph_sv->delta_n; + rrlp_eph->ephemM0 = gps_eph_sv->m_0; + rrlp_eph->ephemCuc = gps_eph_sv->c_uc; + rrlp_eph->ephemE = gps_eph_sv->e; + rrlp_eph->ephemCus = gps_eph_sv->c_us; + rrlp_eph->ephemAPowerHalf = gps_eph_sv->a_powhalf; + rrlp_eph->ephemToe = gps_eph_sv->t_oe; + rrlp_eph->ephemFitFlag = gps_eph_sv->fit_flag; + rrlp_eph->ephemAODA = gps_eph_sv->aodo; + rrlp_eph->ephemCic = gps_eph_sv->c_ic; + rrlp_eph->ephemOmegaA0 = gps_eph_sv->omega_0; + rrlp_eph->ephemCis = gps_eph_sv->c_is; + rrlp_eph->ephemI0 = gps_eph_sv->i_0; + rrlp_eph->ephemCrc = gps_eph_sv->c_rc; + rrlp_eph->ephemW = gps_eph_sv->w; + rrlp_eph->ephemOmegaADot = gps_eph_sv->omega_dot; + rrlp_eph->ephemIDot = gps_eph_sv->idot; + + rrlp_eph->ephemSF1Rsvd.reserved1 = gps_eph_sv->_rsvd1; + rrlp_eph->ephemSF1Rsvd.reserved2 = gps_eph_sv->_rsvd2; + rrlp_eph->ephemSF1Rsvd.reserved3 = gps_eph_sv->_rsvd3; + rrlp_eph->ephemSF1Rsvd.reserved4 = gps_eph_sv->_rsvd4; +} + +static void +_rrlp_fill_almanac_element( + struct AlmanacElement *rrlp_ae, + struct gps_almanac_sv *gps_alm_sv) +{ + rrlp_ae->satelliteID = gps_alm_sv->sv_id; + + rrlp_ae->almanacE = gps_alm_sv->e; + rrlp_ae->alamanacToa = gps_alm_sv->t_oa; + rrlp_ae->almanacKsii = gps_alm_sv->ksii; + rrlp_ae->almanacOmegaDot = gps_alm_sv->omega_dot; + rrlp_ae->almanacSVhealth = gps_alm_sv->sv_health; + rrlp_ae->almanacAPowerHalf = gps_alm_sv->a_powhalf; + rrlp_ae->almanacOmega0 = gps_alm_sv->omega_0; + rrlp_ae->almanacW = gps_alm_sv->w; + rrlp_ae->almanacM0 = gps_alm_sv->m_0; + rrlp_ae->almanacAF0 = gps_alm_sv->a_f0; + rrlp_ae->almanacAF1 = gps_alm_sv->a_f1; + +} + +static void +_rrlp_fill_ionospheric_model( + struct IonosphericModel *rrlp_iono, + struct gps_ionosphere_model *gps_iono) +{ + rrlp_iono->alfa0 = gps_iono->alpha_0; + rrlp_iono->alfa1 = gps_iono->alpha_1; + rrlp_iono->alfa2 = gps_iono->alpha_2; + rrlp_iono->alfa3 = gps_iono->alpha_3; + rrlp_iono->beta0 = gps_iono->beta_0; + rrlp_iono->beta1 = gps_iono->beta_1; + rrlp_iono->beta2 = gps_iono->beta_2; + rrlp_iono->beta3 = gps_iono->beta_3; +} + +static void +_rrlp_fill_utc_model( + struct UTCModel *rrlp_utc, + struct gps_utc_model *gps_utc) +{ + rrlp_utc->utcA1 = gps_utc->a1; + rrlp_utc->utcA0 = gps_utc->a0; + rrlp_utc->utcTot = gps_utc->t_ot; + rrlp_utc->utcWNt = gps_utc->wn_t & 0xff; + rrlp_utc->utcDeltaTls = gps_utc->delta_t_ls; + rrlp_utc->utcWNlsf = gps_utc->wn_lsf & 0xff; + rrlp_utc->utcDN = gps_utc->dn; + rrlp_utc->utcDeltaTlsf = gps_utc->delta_t_lsf; +} + +/* }}} */ + + +/* ------------------------------------------------------------------------ */ +/* RRLP Assistance PDU Generation */ +/* ---------------------------------------------------------------------{{{ */ + +struct PDU * +_rrlp_create_gps_assist_pdu(int refnum, struct GPS_AssistData **o_gps_ad) +{ + struct PDU *pdu; + struct GPS_AssistData *gps_ad; + + pdu = calloc(1, sizeof(*pdu)); + if (!pdu) + return NULL; + + gps_ad = calloc(1, sizeof(*gps_ad)); + if (!gps_ad) { + free(pdu); + return NULL; + } + + if (o_gps_ad) + *o_gps_ad = gps_ad; + + pdu->referenceNumber = refnum; + pdu->component.present = RRLP_Component_PR_assistanceData; + pdu->component.choice.assistanceData.gps_AssistData = gps_ad; + + return pdu; +} + +static int +_rrlp_add_ionospheric_model( + struct GPS_AssistData *rrlp_gps_ad, + struct gps_assist_data *gps_ad) +{ + struct IonosphericModel *rrlp_iono; + + if (!(gps_ad->fields & GPS_FIELD_IONOSPHERE)) + return -EINVAL; + + rrlp_iono = calloc(1, sizeof(*rrlp_iono)); + if (!rrlp_iono) + return -ENOMEM; + rrlp_gps_ad->controlHeader.ionosphericModel = rrlp_iono; + + _rrlp_fill_ionospheric_model(rrlp_iono, &gps_ad->ionosphere); + + return 0; +} + +static int +_rrlp_add_utc_model( + struct GPS_AssistData *rrlp_gps_ad, + struct gps_assist_data *gps_ad) +{ + struct UTCModel *rrlp_utc; + + if (!(gps_ad->fields & GPS_FIELD_UTC)) + return -EINVAL; + + rrlp_utc = calloc(1, sizeof(*rrlp_utc)); + if (!rrlp_utc) + return -ENOMEM; + rrlp_gps_ad->controlHeader.utcModel = rrlp_utc; + + _rrlp_fill_utc_model(rrlp_utc, &gps_ad->utc); + + return 0; +} + +static int +_rrlp_add_reference_location( + struct GPS_AssistData *rrlp_gps_ad, + struct gps_assist_data *gps_ad) +{ + struct RefLocation *rrlp_refloc; + uint8_t *b; + + if (!(gps_ad->fields & GPS_FIELD_REFPOS)) + return -EINVAL; + + rrlp_refloc = calloc(1, sizeof(*rrlp_refloc)); + if (!rrlp_refloc) + return -ENOMEM; + rrlp_gps_ad->controlHeader.refLocation = rrlp_refloc; + + b = malloc(9); + + b[0] = 0x80; /* Ellipsoid Point with altitude */ + _ts_23_032_store_latitude(gps_ad->ref_pos.latitude, &b[1]); + _ts_23_032_store_longitude(gps_ad->ref_pos.longitude, &b[4]); + _ts_23_032_store_altitude(gps_ad->ref_pos.altitude, &b[7]); + + rrlp_refloc->threeDLocation.buf = b; + rrlp_refloc->threeDLocation.size = 9; + + return 0; +} + +static int +_rrlp_add_reference_time( + struct GPS_AssistData *rrlp_gps_ad, + struct gps_assist_data *gps_ad) +{ + struct ReferenceTime *rrlp_reftime; + + if (!(gps_ad->fields & GPS_FIELD_REFTIME)) + return -EINVAL; + + rrlp_reftime = calloc(1, sizeof(*rrlp_reftime)); + if (!rrlp_reftime) + return -ENOMEM; + rrlp_gps_ad->controlHeader.referenceTime = rrlp_reftime; + + rrlp_reftime->gpsTime.gpsWeek = gps_ad->ref_time.wn & 0x3ff; /* 10b */ + rrlp_reftime->gpsTime.gpsTOW23b = + ((int)floor(gps_ad->ref_time.tow / 0.08)) & 0x7fffff; /* 23b */ + + return 0; +} + +static int +_rrlp_add_almanac( + struct GPS_AssistData *rrlp_gps_ad, + struct gps_assist_data *gps_ad, int *start, int count) +{ + int i; + struct Almanac *rrlp_alm; + struct gps_almanac *gps_alm = &gps_ad->almanac; + + if (!(gps_ad->fields & GPS_FIELD_ALMANAC)) + return -EINVAL; + + rrlp_alm = calloc(1, sizeof(*rrlp_alm)); + if (!rrlp_alm) + return -ENOMEM; + rrlp_gps_ad->controlHeader.almanac = rrlp_alm; + + rrlp_alm->alamanacWNa = gps_alm->wna; + if (count == -1) + count = gps_alm->n_sv - *start; + for (i=*start; (i<*start+count) && (i<gps_alm->n_sv); i++) { + struct AlmanacElement *ae; + ae = calloc(1, sizeof(*ae)); + if (!ae) + return -ENOMEM; + _rrlp_fill_almanac_element(ae, &gps_alm->svs[i]); + ASN_SEQUENCE_ADD(&rrlp_alm->almanacList.list, ae); + } + + *start = i; + + return i < gps_alm->n_sv; +} + +static int +_rrlp_add_ephemeris( + struct GPS_AssistData *rrlp_gps_ad, + struct gps_assist_data *gps_ad, int *start, int count, uint64_t mask) +{ + int i, j; + struct NavigationModel *rrlp_nav; + struct gps_ephemeris *gps_eph = &gps_ad->ephemeris; + + if (!(gps_ad->fields & GPS_FIELD_EPHEMERIS)) + return -EINVAL; + + rrlp_nav = calloc(1, sizeof(*rrlp_nav)); + if (!rrlp_nav) + return -ENOMEM; + rrlp_gps_ad->controlHeader.navigationModel = rrlp_nav; + + if (count == -1) + count = gps_eph->n_sv - *start; + for (i=*start,j=0; (j<count) && (i<gps_eph->n_sv); i++) { + if (!(mask & (1ULL<<(gps_eph->svs[i].sv_id-1)))) + continue; + struct NavModelElement *nme; + nme = calloc(1, sizeof(*nme)); + if (!nme) + return -ENOMEM; + _rrlp_fill_navigation_model_element(nme, &gps_eph->svs[i]); + ASN_SEQUENCE_ADD(&rrlp_nav->navModelList.list, nme); + j++; + } + + *start = i; + + return i < gps_eph->n_sv; +} + + +#define MAX_PDUS 64 + +int +rrlp_gps_assist_pdus( + struct gps_assist_data *gps_ad, struct rrlp_assist_req *req, + void **o_pdu, int *o_len, int o_max_pdus) +{ + struct PDU *lst_pdu[MAX_PDUS]; + int lst_cnt = 0; + + struct PDU *rrlp_pdu = NULL; + struct GPS_AssistData *rrlp_gps_ad = NULL; + uint32_t re = req->req_elems; + int i, rv = 0; + + /* IonosphericModel, UTCModel, RefLocation, ReferenceTime */ + if (re & (RRLP_AR_IONO_MODEL | + RRLP_AR_UTC_MODEL | + RRLP_AR_REF_TIME | + RRLP_AR_REF_LOC)) + { + int pdu_has_data = 0; + + rrlp_pdu = _rrlp_create_gps_assist_pdu(1, &rrlp_gps_ad); + if (!rrlp_pdu) { + rv = -ENOMEM; + goto error; + } + + if (re & RRLP_AR_IONO_MODEL) + if (!_rrlp_add_ionospheric_model(rrlp_gps_ad, gps_ad)) + pdu_has_data = 1; + + if (re & RRLP_AR_UTC_MODEL) + if (!_rrlp_add_utc_model(rrlp_gps_ad, gps_ad)) + pdu_has_data = 1; + + if (re & RRLP_AR_REF_TIME) + if (!_rrlp_add_reference_time(rrlp_gps_ad, gps_ad)) + pdu_has_data = 1; + + if (re & RRLP_AR_REF_LOC) + if (!_rrlp_add_reference_location(rrlp_gps_ad, gps_ad)) + pdu_has_data = 1; + + if (pdu_has_data) { + lst_pdu[lst_cnt++] = rrlp_pdu; + rrlp_pdu = NULL; + } + } + + /* Almanac */ + if (re & RRLP_AR_ALMANAC) { + i = 0; + do { + if (!(gps_ad->fields & GPS_FIELD_ALMANAC)) + break; + + if (!rrlp_pdu) { + rrlp_pdu = _rrlp_create_gps_assist_pdu(1, &rrlp_gps_ad); + if (!rrlp_pdu) { + rv = -ENOMEM; + goto error; + } + } + + rv = _rrlp_add_almanac(rrlp_gps_ad, gps_ad, &i, 10); + if (rv < 0) + goto error; + + lst_pdu[lst_cnt++] = rrlp_pdu; + rrlp_pdu = NULL; + } while (rv); + } + + /* Ephemeris */ + if (re & RRLP_AR_EPHEMERIS) { + i = 0; + do { + if (!(gps_ad->fields & GPS_FIELD_EPHEMERIS)) + break; + + if (!rrlp_pdu) { + rrlp_pdu = _rrlp_create_gps_assist_pdu(1, &rrlp_gps_ad); + if (!rrlp_pdu) { + rv = -ENOMEM; + goto error; + } + } + + rv = _rrlp_add_ephemeris(rrlp_gps_ad, gps_ad, &i, 2, req->eph_svs); + + lst_pdu[lst_cnt++] = rrlp_pdu; + rrlp_pdu = NULL; + + } while (rv); + } + + /* Serialize & Release all PDUs */ + for (i=0; i<lst_cnt && i<o_max_pdus; i++) { + /* Pseudo segmentation flags */ + MoreAssDataToBeSent_t *mad = calloc(1, sizeof(*mad)); + *mad = (i == (lst_cnt-1)) ? + MoreAssDataToBeSent_noMoreMessages : + MoreAssDataToBeSent_moreMessagesOnTheWay; + lst_pdu[i]->component.choice.assistanceData.moreAssDataToBeSent = mad; + + /* Serialization */ + // asn_fprint(stdout, &asn_DEF_PDU, lst_pdu[i]); + rv = uper_encode_to_new_buffer(&asn_DEF_PDU, NULL, lst_pdu[i], &o_pdu[i]); + if (rv < 0) + goto error; + o_len[i] = rv; + } + + rv = lst_cnt; + + /* Release ASN.1 objects */ +error: + if (rrlp_pdu) + asn_DEF_PDU.free_struct(&asn_DEF_PDU, (void*)rrlp_pdu, 0); + + for (i=0; i<lst_cnt; i++) + asn_DEF_PDU.free_struct(&asn_DEF_PDU, lst_pdu[i], 0); + + return rv; +} + +/* }}} */ + diff --git a/rrlp-ephemeris/rrlp.h b/rrlp-ephemeris/rrlp.h new file mode 100644 index 000000000..a5e4344e5 --- /dev/null +++ b/rrlp-ephemeris/rrlp.h @@ -0,0 +1,64 @@ +/* + * rrlp.h + * + * RRLP Header + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __RRLP_H__ +#define __RRLP_H__ + +#include <stdint.h> + +#include "gps.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Our internal simplified structure for requests */ + +#define RRLP_AR_REF_LOC (1<<0) +#define RRLP_AR_REF_TIME (1<<1) +#define RRLP_AR_UTC_MODEL (1<<2) +#define RRLP_AR_IONO_MODEL (1<<3) +#define RRLP_AR_ALMANAC (1<<4) +#define RRLP_AR_EPHEMERIS (1<<5) + +struct rrlp_assist_req { + uint32_t req_elems; + uint64_t eph_svs; +}; + + +/* Methods */ +int rrlp_decode_assistance_request(struct rrlp_assist_req *ar, + void *req, int req_len); + +int rrlp_gps_assist_pdus( + struct gps_assist_data *gps_ad, struct rrlp_assist_req *req, + void **o_pdu, int *o_len, int o_max_pdus); + + +#ifdef __cplusplus +} +#endif + +#endif /* __RRLP_H__ */ + diff --git a/rrlp-ephemeris/ubx-parse.c b/rrlp-ephemeris/ubx-parse.c new file mode 100644 index 000000000..c3d0f70d7 --- /dev/null +++ b/rrlp-ephemeris/ubx-parse.c @@ -0,0 +1,177 @@ +/* + * ubx-parse.c + * + * Implementation of parsing code converting UBX messages to GPS assist + * data + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#include "gps.h" +#include "ubx.h" +#include "ubx-parse.h" + + +/* Helpers */ + +static int +float_to_fixedpoint(float f, int sf) +{ + if (sf < 0) { + while (sf++ < 0) + f *= 2.0f; + } else { + while (sf-- > 0) + f *= 0.5f; + } + + return (int)f; +} + +static inline int +double_to_fixedpoint(double d, int sf) +{ + if (sf < 0) { + while (sf++ < 0) + d *= 2.0; + } else { + while (sf-- > 0) + d *= 0.5; + } + + return (int)d; +} + + +/* UBX message parsing to fill gps assist data */ + +static void +_ubx_msg_parse_nav_posllh(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud) +{ + struct ubx_nav_posllh *nav_posllh = pl; + struct gps_assist_data *gps = ud; + + //printf("[.] NAV_POSLLH\n"); + + gps->fields |= GPS_FIELD_REFPOS; + + gps->ref_pos.latitude = (double)(nav_posllh->lat) * 1e-7; + gps->ref_pos.longitude = (double)(nav_posllh->lon) * 1e-7; + gps->ref_pos.altitude = (double)(nav_posllh->height) * 1e-3; +} + +static void +_ubx_msg_parse_aid_ini(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud) +{ + struct ubx_aid_ini *aid_ini = pl; + struct gps_assist_data *gps = ud; + + //printf("[.] AID_INI\n"); + + /* Extract info for "Reference Time" */ + gps->fields |= GPS_FIELD_REFTIME; + + gps->ref_time.wn = aid_ini->wn; + gps->ref_time.tow = (double)aid_ini->tow * 1e-3; + + // FIXME: We could extract ref position as well but we need it in + // WGS84 geodetic coordinates and it's provided as ecef, so + // we need a lot of math ... +} + +static void +_ubx_msg_parse_aid_hui(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud) +{ + struct ubx_aid_hui *aid_hui = pl; + struct gps_assist_data *gps = ud; + + //printf("[.] AID_HUI\n"); + + if (aid_hui->flags & 0x2) { /* UTC parameters valid */ + struct gps_utc_model *utc = &gps->utc; + + gps->fields |= GPS_FIELD_UTC; + + utc->a0 = double_to_fixedpoint(aid_hui->utc_a0, -30); + utc->a1 = double_to_fixedpoint(aid_hui->utc_a1, -50); + utc->delta_t_ls = aid_hui->utc_ls; + utc->t_ot = aid_hui->utc_tot >> 12; + utc->wn_t = aid_hui->utc_wnt; + utc->wn_lsf = aid_hui->utc_wnf; + utc->dn = aid_hui->utc_dn; + utc->delta_t_lsf = aid_hui->utc_lsf; + } + + if (aid_hui->flags & 0x04) { /* Klobuchar parameters valid */ + struct gps_ionosphere_model *iono = &gps->ionosphere; + + gps->fields |= GPS_FIELD_IONOSPHERE; + + iono->alpha_0 = float_to_fixedpoint(aid_hui->klob_a0, -30); + iono->alpha_1 = float_to_fixedpoint(aid_hui->klob_a1, -27); + iono->alpha_2 = float_to_fixedpoint(aid_hui->klob_a2, -24); + iono->alpha_3 = float_to_fixedpoint(aid_hui->klob_a3, -24); + iono->beta_0 = float_to_fixedpoint(aid_hui->klob_b0, 11); + iono->beta_1 = float_to_fixedpoint(aid_hui->klob_b1, 14); + iono->beta_2 = float_to_fixedpoint(aid_hui->klob_b2, 16); + iono->beta_3 = float_to_fixedpoint(aid_hui->klob_b3, 16); + } +} + +static void +_ubx_msg_parse_aid_alm(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud) +{ + struct ubx_aid_alm *aid_alm = pl; + struct gps_assist_data *gps = ud; + + //printf("[.] AID_ALM %d - %d\n", aid_alm->sv_id, aid_alm->gps_week); + + if (aid_alm->gps_week) { + gps->fields |= GPS_FIELD_ALMANAC; + gps->almanac.wna = aid_alm->gps_week & 0xff; + gps_unpack_sf45_almanac(aid_alm->alm_words, &gps->almanac.svs[gps->almanac.n_sv++]); + } +} + +static void +_ubx_msg_parse_aid_eph(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud) +{ + struct ubx_aid_eph *aid_eph = pl; + struct gps_assist_data *gps = ud; + + //printf("[.] AID_EPH %d - %s\n", aid_eph->sv_id, aid_eph->present ? "present" : "not present"); + + if (aid_eph->present) { + int i = gps->ephemeris.n_sv++; + gps->fields |= GPS_FIELD_EPHEMERIS; + gps->ephemeris.svs[i].sv_id = aid_eph->sv_id; + gps_unpack_sf123(aid_eph->eph_words, &gps->ephemeris.svs[i]); + } +} + + +/* Dispatch table */ +struct ubx_dispatch_entry ubx_parse_dt[] = { + UBX_DISPATCH(NAV, POSLLH, _ubx_msg_parse_nav_posllh), + UBX_DISPATCH(AID, INI, _ubx_msg_parse_aid_ini), + UBX_DISPATCH(AID, HUI, _ubx_msg_parse_aid_hui), + UBX_DISPATCH(AID, ALM, _ubx_msg_parse_aid_alm), + UBX_DISPATCH(AID, EPH, _ubx_msg_parse_aid_eph), +}; + diff --git a/rrlp-ephemeris/ubx-parse.h b/rrlp-ephemeris/ubx-parse.h new file mode 100644 index 000000000..621475d65 --- /dev/null +++ b/rrlp-ephemeris/ubx-parse.h @@ -0,0 +1,45 @@ +/* + * ubx-parse.h + * + * Header for parsing code converting UBX messages to GPS assist data + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __UBX_PARSE_H__ +#define __UBX_PARSE_H__ + + +#include "gps.h" +#include "ubx.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Dispatch table */ +extern struct ubx_dispatch_entry ubx_parse_dt[]; + + +#ifdef __cplusplus +} +#endif + +#endif /* __UBX_PARSE_H__ */ + diff --git a/rrlp-ephemeris/ubx.c b/rrlp-ephemeris/ubx.c new file mode 100644 index 000000000..83dd1f055 --- /dev/null +++ b/rrlp-ephemeris/ubx.c @@ -0,0 +1,81 @@ +/* + * ubx.c + * + * Implementation of generic UBX helpers + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdint.h> + +#include "ubx.h" + + +static void +ubx_checksum(uint8_t *data, int len, uint8_t *cksum) +{ + int i; + uint8_t ck0 = 0, ck1 = 0; + for (i=0; i<len; i++) { + ck0 += data[i]; + ck1 += ck0; + } + cksum[0] = ck0; + cksum[1] = ck1; +} + + +static ubx_msg_handler_t +ubx_find_handler(struct ubx_dispatch_entry *dt, uint8_t msg_class, uint8_t msg_id) +{ + while (dt->handler) { + if ((dt->msg_class == msg_class) && (dt->msg_id == msg_id)) + return dt->handler; + dt++; + } + return NULL; +} + + +int +ubx_msg_dispatch(struct ubx_dispatch_entry *dt, + void *msg, int len, void *userdata) +{ + struct ubx_hdr *hdr = msg; + uint8_t cksum[2], *cksum_ptr; + ubx_msg_handler_t h; + + if ((hdr->sync[0] != UBX_SYNC0) || (hdr->sync[1] != UBX_SYNC1)) { + fprintf(stderr, "[!] Invalid sync bytes\n"); + return -1; + } + + ubx_checksum(msg + 2, sizeof(struct ubx_hdr) + hdr->payload_len - 2, cksum); + cksum_ptr = msg + (sizeof(struct ubx_hdr) + hdr->payload_len); + if ((cksum_ptr[0] != cksum[0]) || (cksum_ptr[1] != cksum[1])) { + fprintf(stderr, "[!] Invalid checksum\n"); + return -1; + } + + h = ubx_find_handler(dt, hdr->msg_class, hdr->msg_id); + if (h) + h(hdr, msg + sizeof(struct ubx_hdr), hdr->payload_len, userdata); + + return sizeof(struct ubx_hdr) + hdr->payload_len + 2; +} + diff --git a/rrlp-ephemeris/ubx.h b/rrlp-ephemeris/ubx.h new file mode 100644 index 000000000..826438658 --- /dev/null +++ b/rrlp-ephemeris/ubx.h @@ -0,0 +1,232 @@ +/* + * ubx.h + * + * Header for UBX related stuff + * + * + * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __UBX_H__ +#define __UBX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/* Constants used in UBX */ + + /* Sync bytes (two first bytes of each message) */ +#define UBX_SYNC0 0xb5 +#define UBX_SYNC1 0x62 + + /* UBX messages classes */ +#define UBX_CLASS_NAV 0x01 +#define UBX_CLASS_RXM 0x02 +#define UBX_CLASS_INF 0x04 +#define UBX_CLASS_ACK 0x05 +#define UBX_CLASS_CFG 0x06 +#define UBX_CLASS_UPD 0x09 +#define UBX_CLASS_MON 0x0a +#define UBX_CLASS_AID 0x0b +#define UBX_CLASS_TIM 0x0d + + /* UBX messages type ID (by class) */ +#define UBX_NAV_POSECEF 0x01 +#define UBX_NAV_POSLLH 0x02 +#define UBX_NAV_STATUS 0x03 +#define UBX_NAV_DOP 0x04 +#define UBX_NAV_SOL 0x06 +#define UBX_NAV_POSUTM 0x08 +#define UBX_NAV_VELECEF 0x11 +#define UBX_NAV_VELNED 0x12 +#define UBX_NAV_TIMEGPS 0x20 +#define UBX_NAV_TIMEUTC 0x21 +#define UBX_NAV_CLOCK 0x22 +#define UBX_NAV_SVINFO 0x30 +#define UBX_NAV_DGPS 0x31 +#define UBX_NAV_SBAS 0x32 +#define UBX_NAV_EKFSTATUS 0x40 + +#define UBX_RXM_RAW 0x10 +#define UBX_RXM_SFRB 0x11 +#define UBX_RXM_SVSI 0x20 +#define UBX_RXM_SVSI_GPS 0x20 +#define UBX_RXM_ALM 0x30 +#define UBX_RXM_EPH 0x31 +#define UBX_RXM_POSREQ 0x40 + +#define UBX_INF_ERROR 0x00 +#define UBX_INF_WARNING 0x01 +#define UBX_INF_NOTICE 0x02 +#define UBX_INF_TEST 0x03 +#define UBX_INF_DEBUG 0x04 +#define UBX_INF_USER 0x07 + +#define UBX_ACK_NAK 0x00 +#define UBX_ACK_ACK 0x01 + +#define UBX_CFG_PRT 0x00 +#define UBX_CFG_USB 0x1b +#define UBX_CFG_MSG 0x01 +#define UBX_CFG_NMEA 0x17 +#define UBX_CFG_RATE 0x08 +#define UBX_CFG_CFG 0x09 +#define UBX_CFG_TP 0x07 +#define UBX_CFG_NAV2 0x1a +#define UBX_CFG_DAT 0x06 +#define UBX_CFG_INF 0x02 +#define UBX_CFG_RST 0x04 +#define UBX_CFG_RXM 0x11 +#define UBX_CFG_ANT 0x13 +#define UBX_CFG_FXN 0x0e +#define UBX_CFG_SBAS 0x16 +#define UBX_CFG_LIC 0x80 +#define UBX_CFG_TM 0x10 +#define UBX_CFG_TM2 0x19 +#define UBX_CFG_TMODE 0x1d +#define UBX_CFG_EKF 0x12 + +#define UBX_UPD_DOWNL 0x01 +#define UBX_UPD_UPLOAD 0x02 +#define UBX_UPD_EXEC 0x03 +#define UBX_UPD_MEMCPY 0x04 + +#define UBX_MON_SCHD 0x01 +#define UBX_MON_IO 0x02 +#define UBX_MON_IPC 0x03 +#define UBX_MON_VER 0x04 +#define UBX_MON_EXCEPT 0x05 +#define UBX_MON_MSGPP 0x06 +#define UBX_MON_RXBUF 0x07 +#define UBX_MON_TXBUF 0x08 +#define UBX_MON_HW 0x09 +#define UBX_MON_USB 0x0a + +#define UBX_AID_REQ 0x00 +#define UBX_AID_INI 0x01 +#define UBX_AID_HUI 0x02 +#define UBX_AID_DATA 0x10 +#define UBX_AID_ALM 0x30 +#define UBX_AID_EPH 0x31 + +#define UBX_TIM_TP 0x01 +#define UBX_TIM_TM 0x02 +#define UBX_TIM_TM2 0x03 +#define UBX_TIM_SVIN 0x04 + + +/* Header */ +struct ubx_hdr { + uint8_t sync[2]; + uint8_t msg_class; + uint8_t msg_id; + uint16_t payload_len; +} __attribute__((packed)); + + +/* Payload formats (some of them) */ +struct ubx_nav_posllh { + uint32_t itow; + int32_t lon; /* scaling 1e-7 */ + int32_t lat; /* scaling 1e-7 */ + int32_t height;/* mm */ + int32_t hsl; /* mm */ + uint32_t hacc; /* mm */ + uint32_t vacc; /* mm */ +} __attribute__((packed)); + +struct ubx_aid_ini { + int32_t x; + int32_t y; + int32_t z; + uint32_t posacc; + uint16_t tm_cfg; + uint16_t wn; + uint32_t tow; + int32_t tow_ns; + uint32_t tacc_ms; + uint32_t tacc_ns; + int32_t clkd; + uint32_t clkdacc; + uint32_t flags; +} __attribute__((packed)); + +struct ubx_aid_hui { + uint32_t health; + double utc_a1; + double utc_a0; + int32_t utc_tot; + int16_t utc_wnt; + int16_t utc_ls; + int16_t utc_wnf; + int16_t utc_dn; + int16_t utc_lsf; + int16_t utc_spare; + float klob_a0; + float klob_a1; + float klob_a2; + float klob_a3; + float klob_b0; + float klob_b1; + float klob_b2; + float klob_b3; + uint32_t flags; +} __attribute__((packed)); + +struct ubx_aid_alm { + uint32_t sv_id; + uint32_t gps_week; + uint32_t alm_words[8]; /* Present only if 'gps_week' != 0 */ +} __attribute__((packed)); + +struct ubx_aid_eph { + uint32_t sv_id; + uint32_t present; + uint32_t eph_words[24]; /* Present only if 'present' != 0 */ +} __attribute__((packed)); + + +/* Message handler */ +typedef void (*ubx_msg_handler_t)( + struct ubx_hdr *hdr, void *payload, int payload_len, void *userdata); + +struct ubx_dispatch_entry { + uint8_t msg_class; + uint8_t msg_id; + ubx_msg_handler_t handler; +}; + +#define UBX_DISPATCH(kls,id,hdl) { \ + .msg_class = UBX_CLASS_ ## kls , \ + .msg_id = UBX_ ## kls ## _ ## id, \ + .handler = (hdl), \ +} + + +/* Methods */ +int ubx_msg_dispatch(struct ubx_dispatch_entry *dt, + void *msg, int len, void *userdata); + + +#ifdef __cplusplus +} +#endif + +#endif /* __UBX_H__ */ + diff --git a/wireshark/abis_oml.patch b/wireshark/abis_oml.patch new file mode 100644 index 000000000..9f06b4d82 --- /dev/null +++ b/wireshark/abis_oml.patch @@ -0,0 +1,2192 @@ +Index: wireshark/epan/dissectors/Makefile.common +=================================================================== +--- wireshark.orig/epan/dissectors/Makefile.common ++++ wireshark/epan/dissectors/Makefile.common +@@ -474,6 +474,7 @@ + packet-gsm_a_gm.c \ + packet-gsm_a_rp.c \ + packet-gsm_a_rr.c \ ++ packet-gsm_abis_oml.c \ + packet-gsm_ipa.c \ + packet-gsm_bsslap.c \ + packet-gsm_bssmap_le.c \ +diff --git a/epan/dissectors/packet-gsm_abis_oml.c b/epan/dissectors/packet-gsm_abis_oml.c +new file mode 100644 +index 0000000..2de9dca +--- /dev/null ++++ b/epan/dissectors/packet-gsm_abis_oml.c +@@ -0,0 +1,1382 @@ ++/* packet-abis_oml.c ++ * Routines for packet dissection of GSM A-bis over IP (3GPP TS 12.21) ++ * Copyright 2009 by Harald Welte <laforge@gnumonks.org> ++ * Copyright 2009 by Holger Hans Peter Freyther <zecke@selfish.org> ++ * based on A-bis OML code in OpenBSC ++ * ++ * $Id$ ++ * ++ * Wireshark - Network traffic analyzer ++ * By Gerald Combs <gerald@wireshark.org> ++ * Copyright 1998 Gerald Combs ++ * ++ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib.h> ++ ++#include <epan/packet.h> ++#include <epan/emem.h> ++#include <epan/lapd_sapi.h> ++#include <epan/prefs.h> ++ ++#include "packet-gsm_abis_oml.h" ++#include "packet-gsm_a_common.h" ++ ++/* initialize the protocol and registered fields */ ++static int proto_abis_oml = -1; ++ ++/* OML header */ ++static int hf_oml_msg_disc = -1; ++static int hf_oml_placement = -1; ++static int hf_oml_sequence = -1; ++static int hf_oml_length = -1; ++/* FOM header */ ++static int hf_oml_fom_msgtype = -1; ++static int hf_oml_fom_objclass = -1; ++static int hf_oml_fom_inst_bts = -1; ++static int hf_oml_fom_inst_trx = -1; ++static int hf_oml_fom_inst_ts = -1; ++static int hf_oml_fom_attr_tag = -1; ++static int hf_oml_fom_attr_len = -1; ++static int hf_oml_fom_attr_val = -1; ++/* FOM attributes */ ++static int hf_attr_adm_state = -1; ++static int hf_attr_arfcn = -1; ++static int hf_attr_oper_state = -1; ++static int hf_attr_avail_state = -1; ++static int hf_attr_event_type = -1; ++static int hf_attr_severity = -1; ++static int hf_attr_bcch_arfcn = -1; ++static int hf_attr_bsic = -1; ++static int hf_attr_test_no = -1; ++static int hf_attr_tsc = -1; ++static int hf_attr_tei = -1; ++static int hf_attr_ach_btsp = -1; ++static int hf_attr_ach_tslot = -1; ++static int hf_attr_ach_sslot = -1; ++static int hf_attr_gsm_time = -1; ++static int hf_attr_chan_comb = -1; ++/* Ipaccess */ ++static int hf_oml_ipa_tres_attr_tag = -1; ++static int hf_oml_ipa_tres_attr_len = -1; ++static int hf_attr_ipa_test_res = -1; ++static int hf_attr_ipa_tr_rxlev = -1; ++static int hf_attr_ipa_tr_b_rxlev = -1; ++static int hf_attr_ipa_tr_arfcn = -1; ++static int hf_attr_ipa_tr_f_qual = -1; ++static int hf_attr_ipa_tr_f_err = -1; ++static int hf_attr_ipa_tr_rxqual = -1; ++static int hf_attr_ipa_tr_frame_offs = -1; ++static int hf_attr_ipa_tr_framenr_offs = -1; ++static int hf_attr_ipa_tr_bsic = -1; ++static int hf_attr_ipa_tr_cell_id = -1; ++static int hf_attr_ipa_tr_si2 = -1; ++static int hf_attr_ipa_tr_si2bis = -1; ++static int hf_attr_ipa_tr_si2ter = -1; ++static int hf_attr_ipa_tr_chan_desc = -1; ++static int hf_attr_ipa_rsl_ip = -1; ++static int hf_attr_ipa_rsl_port = -1; ++static int hf_attr_ipa_prim_oml_ip = -1; ++static int hf_attr_ipa_prim_oml_port = -1; ++static int hf_attr_ipa_location_name = -1; ++static int hf_attr_ipa_unit_id = -1; ++static int hf_attr_ipa_unit_name = -1; ++static int hf_attr_ipa_nv_flags = -1; ++static int hf_attr_ipa_nv_mask = -1; ++static int hf_attr_ipa_nsl_sport = -1; ++static int hf_attr_ipa_nsl_daddr = -1; ++static int hf_attr_ipa_nsl_dport = -1; ++static int hf_attr_ipa_nsei = -1; ++static int hf_attr_ipa_nsvci = -1; ++static int hf_attr_ipa_bvci = -1; ++static int hf_attr_ipa_rac = -1; ++ ++/* initialize the subtree pointers */ ++static int ett_oml = -1; ++static int ett_oml_fom = -1; ++static int ett_oml_fom_att = -1; ++ ++/* Decode things as nanoBTS traces */ ++static gboolean global_oml_use_nano_bts = FALSE; ++ ++static proto_tree *top_tree; ++ ++/* TS 12.21 Chapter 8.1 / TS 08.59 */ ++static const value_string oml_msg_disc_vals[] = { ++ { ABIS_OM_MDISC_FOM, "Formatted O&M" }, ++ { ABIS_OM_MDISC_MMI, "MMI Transfer" }, ++ { ABIS_OM_MDISC_TRAU, "TRAU O&M" }, ++ { ABIS_OM_MDISC_MANUF, "Manufacturer specific" }, ++}; ++ ++/* TS 12.21 Chapter 8.1.1 */ ++static const value_string oml_placement_vals[] = { ++ { ABIS_OM_PLACEMENT_ONLY, "Only" }, ++ { ABIS_OM_PLACEMENT_FIRST, "First" }, ++ { ABIS_OM_PLACEMENT_MIDDLE, "Middle" }, ++ { ABIS_OM_PLACEMENT_LAST, "Last" }, ++}; ++ ++/* TS 12.21 Chapter 9.2 */ ++static const value_string oml_fom_msgtype_vals[] = { ++ { NM_MT_LOAD_INIT, "Software Load Init" }, ++ { NM_MT_LOAD_INIT_ACK, "Software Load Init ACK" }, ++ { NM_MT_LOAD_INIT_NACK, "Software Load Init NACK" }, ++ { NM_MT_LOAD_SEG, "Software Load Segment" }, ++ { NM_MT_LOAD_SEG_ACK, "Software Load Segment ACK" }, ++ { NM_MT_LOAD_END, "Software Load End" }, ++ { NM_MT_LOAD_END_ACK, "Software Load End ACK" }, ++ { NM_MT_LOAD_END_NACK, "Software Load End NACK" }, ++ { NM_MT_SW_ACT_REQ, "Software Activate Request" }, ++ { NM_MT_SW_ACT_REQ_ACK, "Software Activate Request ACK" }, ++ { NM_MT_SW_ACT_REQ_NACK, "Software Activate Request NACK" }, ++ { NM_MT_ACTIVATE_SW, "Activate Software" }, ++ { NM_MT_ACTIVATE_SW_ACK, "Activate Software ACK" }, ++ { NM_MT_ACTIVATE_SW_NACK, "Activate Software NACK" }, ++ { NM_MT_SW_ACTIVATED_REP, "Software Activated Report" }, ++ { NM_MT_ESTABLISH_TEI, "Establish TEI" }, ++ { NM_MT_ESTABLISH_TEI_ACK, "Establish TEI ACK" }, ++ { NM_MT_ESTABLISH_TEI_NACK, "Establish TEI NACK" }, ++ { NM_MT_CONN_TERR_SIGN, "Connect Terrestrial Signalling" }, ++ { NM_MT_CONN_TERR_SIGN_ACK, "Connect Terrestrial Signalling ACK" }, ++ { NM_MT_CONN_TERR_SIGN_NACK, "Connect Terrestrial Signalling NACK" }, ++ { NM_MT_DISC_TERR_SIGN, "Disconnect Terrestrial Signalling" }, ++ { NM_MT_DISC_TERR_SIGN_ACK, "Disconnect Terrestrial Signalling ACK" }, ++ { NM_MT_DISC_TERR_SIGN_NACK, "Disconnect Terrestrial Signalling NACK" }, ++ { NM_MT_CONN_TERR_TRAF, "Connect Terrestrial Traffic" }, ++ { NM_MT_CONN_TERR_TRAF_ACK, "Connect Terrestrial Traffic ACK" }, ++ { NM_MT_CONN_TERR_TRAF_NACK, "Connect Terrestrial Traffic NACK" }, ++ { NM_MT_DISC_TERR_TRAF, "Disconnect Terrestrial Traffic" }, ++ { NM_MT_DISC_TERR_TRAF_ACK, "Disconnect Terrestrial Traffic ACK" }, ++ { NM_MT_DISC_TERR_TRAF_NACK, "Disconnect Terrestrial Traffic NACK" }, ++ { NM_MT_CONN_MDROP_LINK, "Connect Multi-Drop Link" }, ++ { NM_MT_CONN_MDROP_LINK_ACK, "Connect Multi-Drop Link ACK" }, ++ { NM_MT_CONN_MDROP_LINK_NACK, "Connect Multi-Drop Link NACK" }, ++ { NM_MT_DISC_MDROP_LINK, "Disconnect Multi-Drop Link" }, ++ { NM_MT_DISC_MDROP_LINK_ACK, "Disconnect Multi-Drop Link ACK" }, ++ { NM_MT_DISC_MDROP_LINK_NACK, "Disconnect Multi-Drop Link NACK" }, ++ { NM_MT_SET_BTS_ATTR, "Set BTS Attributes" }, ++ { NM_MT_SET_BTS_ATTR_ACK, "Set BTS Attributes ACK" }, ++ { NM_MT_SET_BTS_ATTR_NACK, "Set BTS Attributes NACK" }, ++ { NM_MT_SET_RADIO_ATTR, "Set Radio Carrier Attributes" }, ++ { NM_MT_SET_RADIO_ATTR_ACK, "Set Radio Carrier Attributes ACK" }, ++ { NM_MT_SET_RADIO_ATTR_NACK, "Set Radio Carrier Attributes NACK" }, ++ { NM_MT_SET_CHAN_ATTR, "Set Channel Attributes" }, ++ { NM_MT_SET_CHAN_ATTR_ACK, "Set Channel Attributes ACK" }, ++ { NM_MT_SET_CHAN_ATTR_NACK, "Set Channel Attributes NACK" }, ++ { NM_MT_PERF_TEST, "Perform Test" }, ++ { NM_MT_PERF_TEST_ACK, "Perform Test ACK" }, ++ { NM_MT_PERF_TEST_NACK, "Perform Test NACK" }, ++ { NM_MT_TEST_REP, "Test Report" }, ++ { NM_MT_SEND_TEST_REP, "Send Test Report" }, ++ { NM_MT_SEND_TEST_REP_ACK, "Send Test Report ACK" }, ++ { NM_MT_SEND_TEST_REP_NACK, "Send Test Report NACK" }, ++ { NM_MT_STOP_TEST, "Stop Test" }, ++ { NM_MT_STOP_TEST_ACK, "Stop Test ACK" }, ++ { NM_MT_STOP_TEST_NACK, "Stop Test NACK" }, ++ { NM_MT_STATECHG_EVENT_REP, "State Changed Event Report" }, ++ { NM_MT_FAILURE_EVENT_REP, "Failure Event Report" }, ++ { NM_MT_STOP_EVENT_REP, "Stop Sending Event Reports" }, ++ { NM_MT_STOP_EVENT_REP_ACK, "Stop Sending Event Reports ACK" }, ++ { NM_MT_STOP_EVENT_REP_NACK, "Stop Sending Event Reports NACK" }, ++ { NM_MT_REST_EVENT_REP, "Restart Sending Event Reports" }, ++ { NM_MT_REST_EVENT_REP_ACK, "Restart Sending Event Reports ACK" }, ++ { NM_MT_REST_EVENT_REP_NACK, "Restart Sending Event Reports NACK" }, ++ { NM_MT_CHG_ADM_STATE, "Change Administrative State" }, ++ { NM_MT_CHG_ADM_STATE_ACK, "Change Administrative State ACK" }, ++ { NM_MT_CHG_ADM_STATE_NACK, "Change Administrative State NACK" }, ++ { NM_MT_CHG_ADM_STATE_REQ, "Change Administrative State Request" }, ++ { NM_MT_CHG_ADM_STATE_REQ_ACK, "Change Administrative State Request ACK" }, ++ { NM_MT_CHG_ADM_STATE_REQ_NACK, "Change Administrative State Request NACK" }, ++ { NM_MT_REP_OUTST_ALARMS, "Report Outstanding Alarms" }, ++ { NM_MT_REP_OUTST_ALARMS_ACK, "Report Outstanding Alarms ACK" }, ++ { NM_MT_REP_OUTST_ALARMS_NACK, "Report Outstanding Alarms NACK" }, ++ { NM_MT_CHANGEOVER, "Changeover" }, ++ { NM_MT_CHANGEOVER_ACK, "Changeover ACK" }, ++ { NM_MT_CHANGEOVER_NACK, "Changeover NACK" }, ++ { NM_MT_OPSTART, "Opstart" }, ++ { NM_MT_OPSTART_ACK, "Opstart ACK" }, ++ { NM_MT_OPSTART_NACK, "Opstart NACK" }, ++ { NM_MT_REINIT, "Reinitialize" }, ++ { NM_MT_REINIT_ACK, "Reinitialize ACK" }, ++ { NM_MT_REINIT_NACK, "Reinitialize NACK" }, ++ { NM_MT_SET_SITE_OUT, "Set Site Outputs" }, ++ { NM_MT_SET_SITE_OUT_ACK, "Set Site Outputs ACK" }, ++ { NM_MT_SET_SITE_OUT_NACK, "Set Site Outputs NACK" }, ++ { NM_MT_CHG_HW_CONF, "Change HW Configuration" }, ++ { NM_MT_CHG_HW_CONF_ACK, "Change HW Configuration ACK" }, ++ { NM_MT_CHG_HW_CONF_NACK, "Change HW Configuration NACK" }, ++ { NM_MT_MEAS_RES_REQ, "Measurement Result Request" }, ++ { NM_MT_MEAS_RES_RESP, "Measurement Result Response" }, ++ { NM_MT_STOP_MEAS, "Stop Measurement" }, ++ { NM_MT_START_MEAS, "Start Measurement" }, ++ { NM_MT_GET_ATTR, "Get Attributes" }, ++ { NM_MT_GET_ATTR_RESP, "Get Attributes Response" }, ++ { NM_MT_GET_ATTR_NACK, "Get Attributes NACK" }, ++ { NM_MT_SET_ALARM_THRES, "Set Alarm Threshold" }, ++ { NM_MT_SET_ALARM_THRES_ACK, "Set Alarm Threshold ACK" }, ++ { NM_MT_SET_ALARM_THRES_NACK, "Set Alarm Threshold NACK" }, ++ /* proprietary, not in the standard */ ++ { NM_MT_IPACC_RESTART, "IPA Restart" }, ++ { NM_MT_IPACC_RESTART_ACK, "IPA Restart ACK" }, ++ { NM_MT_IPACC_RSL_CONNECT, "IPA RSL Connect" }, ++ { NM_MT_IPACC_RSL_CONNECT_ACK, "IPA RSL Connect ACK" }, ++ { NM_MT_IPACC_RSL_CONNECT_NACK, "IPA RSL Connect NACK" }, ++ { NM_MT_IPACC_RSL_DISCONNECT, "IPA RSL Disconnect" }, ++ { NM_MT_IPACC_RSL_DISCONNECT_ACK, "IPA RSL Disconnect ACK" }, ++ { NM_MT_IPACC_RSL_DISCONNECT_NACK, "IPA RSL Disconnect NACK" }, ++ { NM_MT_IPACC_CONN_TRAF, "IPA Connect Traffic" }, ++ { NM_MT_IPACC_CONN_TRAF_ACK, "IPA Connect Traffic ACK" }, ++ { NM_MT_IPACC_CONN_TRAF_NACK, "IPA Connect Traffic NACK" }, ++ { NM_MT_IPACC_DISC_TRAF, "IPA Disconnect Traffic" }, ++ { NM_MT_IPACC_DISC_TRAF_ACK, "IPA Disconnect Traffic ACK" }, ++ { NM_MT_IPACC_DISC_TRAF_NACK, "IPA Disconnect Traffic NACK" }, ++ { NM_MT_IPACC_DEF_BOOT_SW, "IPA Default Boot Software" }, ++ { NM_MT_IPACC_DEF_BOOT_SW_ACK, "IPA Default Boot Software ACK" }, ++ { NM_MT_IPACC_DEF_BOOT_SW_NACK, "IPA Default Boot Software NACK" }, ++ { NM_MT_IPACC_SET_NVATTR, "IPA Set NVRAM Attributes" }, ++ { NM_MT_IPACC_SET_NVATTR_ACK, "IPA Set NVRAM Attributes ACK" }, ++ { NM_MT_IPACC_SET_NVATTR_NACK, "IPA Set NVRAM Attributes NACK" }, ++ { NM_MT_IPACC_GET_NVATTR, "IPA Get NVRAM Attributes" }, ++ { NM_MT_IPACC_GET_NVATTR_ACK, "IPA Get NVRAM Attributes ACK" }, ++ { NM_MT_IPACC_GET_NVATTR_NACK, "IPA Get NVRAM Attributes NACK" }, ++ { NM_MT_IPACC_SET_ATTR, "IPA Set Attributes" }, ++ { NM_MT_IPACC_SET_ATTR_ACK, "IPA Set Attributes ACK" }, ++ { NM_MT_IPACC_SET_ATTR_NACK, "IPA Set Attributes NACK" }, ++ { NM_MT_IPACC_ATTR_CHG_EVT, "IPA Attribute Change Event" }, ++ { NM_MT_IPACC_SW_DEACT, "IPA Software Deactivate" }, ++ { NM_MT_IPACC_SW_DEACT_ACK, "IPA Software Deactivate ACK" }, ++ { NM_MT_IPACC_SW_DEACT_NACK, "IPA Software Deactivate NACK" }, ++ { NM_MT_IPACC_MEAS_RES_REQ_NACK,"IPA Measurement Result Request NACK" }, ++ { NM_MT_IPACC_START_MEAS_NACK, "IPA Start Measurement NACK" }, ++ { NM_MT_IPACC_STOP_MEAS_NACK, "IPA Stop Measurement NACK" }, ++ { NM_MT_BS11_RESET_RESOURCE, "SIE Reset Resource" }, ++ { NM_MT_BS11_BEGIN_DB_TX, "SIE Begin Database Transmission" }, ++ { NM_MT_BS11_BEGIN_DB_TX_ACK, "SIE Begin Database Transmission ACK" }, ++ { NM_MT_BS11_BEGIN_DB_TX_NACK, "SIE Begin Database Transmission NACK" }, ++ { NM_MT_BS11_END_DB_TX, "SIE End Database Transmission" }, ++ { NM_MT_BS11_END_DB_TX_ACK, "SIE End Database Transmission ACK" }, ++ { NM_MT_BS11_END_DB_TX_NACK, "SIE End Database Transmission NACK" }, ++ { NM_MT_BS11_CREATE_OBJ, "SIE Create Object" }, ++ { NM_MT_BS11_CREATE_OBJ_ACK, "SIE Create Object ACK" }, ++ { NM_MT_BS11_CREATE_OBJ_NACK, "SIE Create Object NACK" }, ++ { NM_MT_BS11_DELETE_OBJ, "SIE Delete Object" }, ++ { NM_MT_BS11_DELETE_OBJ_ACK, "SIE Delete Object ACK" }, ++ { NM_MT_BS11_DELETE_OBJ_NACK, "SIE Delete Object NACK" }, ++ { NM_MT_BS11_GET_STATE, "SIE Get State" }, ++ { NM_MT_BS11_GET_STATE_ACK, "SIE Get State ACK" }, ++ { NM_MT_BS11_LMT_LOGON, "SIE LMT Logon" }, ++ { NM_MT_BS11_LMT_LOGON_ACK, "SIE LMT Logon ACK" }, ++ { NM_MT_BS11_RESTART, "SIE Restart" }, ++ { NM_MT_BS11_RESTART_ACK, "SIE Restart ACK" }, ++ { NM_MT_BS11_DISCONNECT, "SIE Disconnect BTS" }, ++ { NM_MT_BS11_DISCONNECT_ACK, "SIE Disconnect BTS ACK" }, ++ { NM_MT_BS11_LMT_LOGOFF, "SIE LMT Logoff" }, ++ { NM_MT_BS11_LMT_LOGOFF_ACK, "SIE LMT Logoff ACK" }, ++ { NM_MT_BS11_RECONNECT, "SIE Reconnect BTS" }, ++ { NM_MT_BS11_RECONNECT_ACK, "SIE Reconnect BTS ACK" }, ++}; ++ ++/* TS 12.21 Section 9.2: Object Class */ ++static const value_string oml_fom_objclass_vals[] = { ++ { NM_OC_SITE_MANAGER, "BTS Site Manager" }, ++ { NM_OC_BTS, "BTS" }, ++ { NM_OC_RADIO_CARRIER, "Radio Carrier" }, ++ { NM_OC_CHANNEL, "Radio Channel" }, ++ { NM_OC_BASEB_TRANSC, "Baseband Transceiver" }, ++ /* proprietary, vendor specific */ ++ { NM_OC_BS11_ADJC, "SIE Adjacend Channel" }, ++ { NM_OC_BS11_HANDOVER, "SIE Handover" }, ++ { NM_OC_BS11_PWR_CTRL, "SIE Power Control" }, ++ { NM_OC_BS11_BTSE, "SIE BTSE" }, ++ { NM_OC_BS11_RACK, "SIE Rack" }, ++ { NM_OC_BS11, "SIE SiemensHW" }, ++ { NM_OC_BS11_TEST, "SIE Test" }, ++ { NM_OC_BS11_ENVABTSE, "SIE EnvaBTSE" }, ++ { NM_OC_BS11_BPORT, "SIE BPort" }, ++ { NM_OC_GPRS_NSE, "GPRS NSE" }, ++ { NM_OC_GPRS_CELL, "GPRS Cell" }, ++ { NM_OC_GPRS_NSVC0, "GPRS NSVC0" }, ++ { NM_OC_GPRS_NSVC1, "GPRS NSVC1" }, ++ { NM_OC_NULL, "NULL" }, ++}; ++ ++/* TS 12.21 Section 9.4: Attributes */ ++static const value_string oml_fom_attr_vals[] = { ++ { NM_ATT_ABIS_CHANNEL, "A-bis Channel" }, ++ { NM_ATT_ADD_INFO, "Additional Information" }, ++ { NM_ATT_ADD_TEXT, "Additional Text" }, ++ { NM_ATT_ADM_STATE, "Administrative State" }, ++ { NM_ATT_ARFCN_LIST, "ARFCN List" }, ++ { NM_ATT_AUTON_REPORT, "Autonomously Report" }, ++ { NM_ATT_AVAIL_STATUS, "Availability Status" }, ++ { NM_ATT_BCCH_ARFCN, "BCCH ARFCN" }, ++ { NM_ATT_BSIC, "BSIC" }, ++ { NM_ATT_BTS_AIR_TIMER, "BTS Air Timer" }, ++ { NM_ATT_CCCH_L_I_P, "CCCH Load Indication Period" }, ++ { NM_ATT_CCCH_L_T, "CCCH Load Threshold" }, ++ { NM_ATT_CHAN_COMB, "Channel Combination" }, ++ { NM_ATT_CONN_FAIL_CRIT, "Connection Fail Criterion" }, ++ { NM_ATT_DEST, "Destination" }, ++ { NM_ATT_EVENT_TYPE, "Event Type" }, ++ { NM_ATT_FILE_ID, "File ID" }, ++ { NM_ATT_FILE_VERSION, "File Version" }, ++ { NM_ATT_GSM_TIME, "GSM Time" }, ++ { NM_ATT_HSN, "HSN" }, ++ { NM_ATT_HW_CONFIG, "HW Configuration" }, ++ { NM_ATT_HW_DESC, "HW Description" }, ++ { NM_ATT_INTAVE_PARAM, "Intave Parameter" }, ++ { NM_ATT_INTERF_BOUND, "Interference Boundaries" }, ++ { NM_ATT_LIST_REQ_ATTR, "List of required Attributes" }, ++ { NM_ATT_MAIO, "MAIO" }, ++ { NM_ATT_MANUF_STATE, "Manufacturer Dependent State" }, ++ { NM_ATT_MANUF_THRESH, "Manufacturer Dependent Thresholds" }, ++ { NM_ATT_MANUF_ID, "Manufacturer Id" }, ++ { NM_ATT_MAX_TA, "Maximum Timing Advance" }, ++ { NM_ATT_MDROP_LINK, "Multi-drop BSC Link" }, ++ { NM_ATT_MDROP_NEXT, "Multi-drop next BTS Link" }, ++ { NM_ATT_NACK_CAUSES, "NACK Causes" }, ++ { NM_ATT_NY1, "Ny1" }, ++ { NM_ATT_OPER_STATE, "Operational State" }, ++ { NM_ATT_OVERL_PERIOD, "Overload Period" }, ++ { NM_ATT_PHYS_CONF, "Physical Config" }, ++ { NM_ATT_POWER_CLASS, "Power Class" }, ++ { NM_ATT_POWER_THRESH, "Power Output Thresholds" }, ++ { NM_ATT_PROB_CAUSE, "Probable Cause" }, ++ { NM_ATT_RACH_B_THRESH, "RACH Busy Threshold" }, ++ { NM_ATT_LDAVG_SLOTS, "RACH Load Averaging Slots" }, ++ { NM_ATT_RAD_SUBC, "Radio Sub Channel" }, ++ { NM_ATT_RF_MAXPOWR_R, "RF Max Power Reduction" }, ++ { NM_ATT_SITE_INPUTS, "Site Inputs" }, ++ { NM_ATT_SITE_OUTPUTS, "Site Outputs" }, ++ { NM_ATT_SOURCE, "Source" }, ++ { NM_ATT_SPEC_PROB, "Specific Problems" }, ++ { NM_ATT_START_TIME, "Starting Time" }, ++ { NM_ATT_T200, "T200" }, ++ { NM_ATT_TEI, "TEI" }, ++ { NM_ATT_TEST_DUR, "Test Duration" }, ++ { NM_ATT_TEST_NO, "Test No" }, ++ { NM_ATT_TEST_REPORT, "Test Report Info" }, ++ { NM_ATT_VSWR_THRESH, "VSWR Thresholds " }, ++ { NM_ATT_WINDOW_SIZE, "Window Size" }, ++ { NM_ATT_BS11_RSSI_OFFS, "SIE RSSI Offset" }, ++ { NM_ATT_BS11_TXPWR, "SIE TX Power" }, ++ { NM_ATT_BS11_DIVERSITY, "SIE Diversity" }, ++ { NM_ATT_TSC, "Training Sequence Code" }, ++ { NM_ATT_SW_CONFIG, "SW Configuration" }, ++ { NM_ATT_SW_DESCR, "SW Description" }, ++ { NM_ATT_SEVERITY, "Perceived Severity" }, ++ { NM_ATT_GET_ARI, "Get ARI" }, ++ { NM_ATT_HW_CONF_CHG, "HW Configuration Change" }, ++ { NM_ATT_OUTST_ALARM, "Outstanding Alarm" }, ++ { NM_ATT_FILE_DATA, "File Data" }, ++ { NM_ATT_MEAS_RES, "Measurement Result" }, ++ { NM_ATT_MEAS_TYPE, "Measurement Type" }, ++ { NM_ATT_BS11_ESN_FW_CODE_NO, "SIE ESN FW Code Number" }, ++ { NM_ATT_BS11_ESN_HW_CODE_NO, "SIE ESN HW Code Number" }, ++ { NM_ATT_BS11_ESN_PCB_SERIAL, "SIE ESN PCB Serial Number" }, ++ { NM_ATT_BS11_EXCESSIVE_DISTANCE, "SIE Excessive Distance" }, ++ { NM_ATT_BS11_ALL_TEST_CATG, "SIE All Test Categories" }, ++ { NM_ATT_BS11_BTSLS_HOPPING, "SIE BTS LS Hopping" }, ++ { NM_ATT_BS11_CELL_ALLOC_NR, "SIE Cell Allocation Number" }, ++ { NM_ATT_BS11_CELL_GLOBAL_ID, "SIE Cell Global ID" }, ++ { NM_ATT_BS11_ENA_INTERF_CLASS, "SIE Enable Interference Class" }, ++ /* FIXME */ ++ { NM_ATT_BS11_ENA_MS_PWR_CTRL, "SIE Enable MS Power Control" }, ++ { NM_ATT_BS11_ENA_PWR_BDGT_HO, "SIE Enable Power Budget HO" }, ++ { NM_ATT_BS11_ENA_RXLEV_HO, "SIE Enable RxLevel HO" }, ++ { NM_ATT_BS11_ENA_RXQUAL_HO, "SIE Enable RxQual HO" }, ++ { NM_ATT_BS11_FACCH_QUAL, "SIE FACCH Quality" }, ++ { NM_ATT_IPACC_DST_IP, "IPA Destination IP Address" }, ++ { NM_ATT_IPACC_DST_IP_PORT, "IPA Destionation IP Port" }, ++ { NM_ATT_IPACC_SSRC, "IPA RTP SSRC" }, ++ { NM_ATT_IPACC_RTP_PAYLD_TYPE, "IPA RTP Payload Type" }, ++ { NM_ATT_IPACC_BASEB_ID, "IPA Baseband Identifier" }, ++ { NM_ATT_IPACC_STREAM_ID, "IPA Stream Identifier" }, ++ { NM_ATT_IPACC_NV_FLAGS, "IPA NVRAM Flags" }, ++ { NM_ATT_IPACC_FREQ_CTRL, "IPA Frequency Control" }, ++ { NM_ATT_IPACC_PRIM_OML_CFG, "IPA Primary OML Config" }, ++ { NM_ATT_IPACC_SEC_OML_CFG, "IPA Secondary OML Config" }, ++ { NM_ATT_IPACC_IP_IF_CFG, "IPA IP Interface Config" }, ++ { NM_ATT_IPACC_IP_GW_CFG, "IPA IP Gateway Config" }, ++ { NM_ATT_IPACC_IN_SERV_TIME, "IPA In Service Time" }, ++ { NM_ATT_IPACC_TRX_BTS_ASS, "IPA TRX BTS Assignment" }, ++ { NM_ATT_IPACC_LOCATION, "IPA BTS Location Name" }, ++ { NM_ATT_IPACC_PAGING_CFG, "IPA Paging Configuration" }, ++ { NM_ATT_IPACC_FILE_DATA, "IPA File Data" }, ++ { NM_ATT_IPACC_UNIT_ID, "IPA Unit ID" }, ++ { NM_ATT_IPACC_PARENT_UNIT_ID, "IPA Parent Unit ID" }, ++ { NM_ATT_IPACC_UNIT_NAME, "IPA Unit Name" }, ++ { NM_ATT_IPACC_SNMP_CFG, "IPA SNMP Config" }, ++ { NM_ATT_IPACC_PRIM_OML_CFG_LIST, "IPA Primary OML Config List" }, ++ { NM_ATT_IPACC_PRIM_OML_FB_TOUT,"IPA Primary OML Fallback Timeout" }, ++ { NM_ATT_IPACC_CUR_SW_CFG, "IPA Current Software Config" }, ++ { NM_ATT_IPACC_TIMING_BUS, "IPA Timing Bus" }, ++ { NM_ATT_IPACC_CGI, "IPA CGI" }, ++ { NM_ATT_IPACC_RAC, "IPA RAC" }, ++ { NM_ATT_IPACC_OBJ_VERSION, "IPA Object Version" }, ++ { NM_ATT_IPACC_GPRS_PAGING_CFG, "IPA GPRS Paging Configuration" }, ++ { NM_ATT_IPACC_NSEI, "IPA NSEI" }, ++ { NM_ATT_IPACC_BVCI, "IPA BVCI" }, ++ { NM_ATT_IPACC_NSVCI, "IPA NSVCI" }, ++ { NM_ATT_IPACC_NS_CFG, "IPA NS Configuration" }, ++ { NM_ATT_IPACC_BSSGP_CFG, "IPA BSSGP Configuration" }, ++ { NM_ATT_IPACC_NS_LINK_CFG, "IPA NS Link Configuration" }, ++ { NM_ATT_IPACC_RLC_CFG, "IPA RLC Configuration" }, ++ { NM_ATT_IPACC_ALM_THRESH_LIST, "IPA Alarm Threshold List" }, ++ { NM_ATT_IPACC_MONIT_VAL_LIST, "IPA Monitored Value List" }, ++ { NM_ATT_IPACC_TIB_CONTROL, "IPA Timing Interface Bus Control" }, ++ { NM_ATT_IPACC_SUPP_FEATURES, "IPA Supported Features" }, ++ { NM_ATT_IPACC_CODING_SCHEMES, "IPA Coding Schemes" }, ++ { NM_ATT_IPACC_RLC_CFG_2, "IPA RLC Configuration 2" }, ++ { NM_ATT_IPACC_HEARTB_TOUT, "IPA Heartbeat Timeout" }, ++ { NM_ATT_IPACC_UPTIME, "IPA Uptime" }, ++ { NM_ATT_IPACC_RLC_CFG_3, "IPA RLC Configuration 3" }, ++ { NM_ATT_IPACC_SSL_CFG, "IPA SSL Configuration" }, ++ { NM_ATT_IPACC_SEC_POSSIBLE, "IPA Security Possible" }, ++ { NM_ATT_IPACC_IML_SSL_STATE, "IPA IML SSL State" }, ++ { NM_ATT_IPACC_REVOC_DATE, "IPA Revocation Date" }, ++ /* FIXME: More SIE */ ++}; ++ ++/* Section 9.4.4: Administrative State */ ++static const value_string oml_adm_state_vals[] = { ++ { NM_STATE_LOCKED, "Locked" }, ++ { NM_STATE_UNLOCKED, "Unlocked" }, ++ { NM_STATE_SHUTDOWN, "Shutdown" }, ++ { NM_STATE_NULL, "Null" }, ++}; ++ ++static const value_string oml_oper_state_vals[] = { ++ { 1, "Disabled" }, ++ { 2, "Enabled" }, ++ { 0xff, "NULL" }, ++}; ++ ++/* Section 9.4.7 Availability Status */ ++static const value_string oml_avail_state_vals[] = { ++ { 0, "In test" }, ++ { 1, "Failed" }, ++ { 2, "Power off" }, ++ { 3, "Off line" }, ++ { 5, "Dependency" }, ++ { 6, "Degraded" }, ++ { 7, "Not installed" }, ++ { 0xff, "OK" }, ++}; ++ ++/* Section 9.4.13: Channel Combination */ ++static const value_string oml_chan_comb_vals[] = { ++ { NM_CHANC_TCHFull, "TCH/F" }, ++ { NM_CHANC_TCHHalf, "TCH/H" }, ++ { NM_CHANC_TCHHalf2, "TCH/H 2" }, ++ { NM_CHANC_SDCCH, "SDCCH" }, ++ { NM_CHANC_mainBCCH, "Main BCCH" }, ++ { NM_CHANC_BCCHComb, "Combined BCCH" }, ++ { NM_CHANC_BCCH, "BCCH" }, ++ { NM_CHANC_BCCH_CBCH, "BCCH+CBCH" }, ++ { NM_CHANC_SDCCH_CBCH, "SDCCH+CBCH" }, ++}; ++ ++/* Section 9.4.16: Event Type */ ++static const value_string oml_event_type_vals[] = { ++ { NM_EVT_COMM_FAIL, "Communication Failure" }, ++ { NM_EVT_QOS_FAIL, "QoS Failure" }, ++ { NM_EVT_PROC_FAIL, "Processor Failure" }, ++ { NM_EVT_EQUIP_FAIL, "Equipment Failure" }, ++ { NM_EVT_ENV_FAIL, "Environment Failure" }, ++}; ++ ++/* Section 9.4.63: Perceived Severity */ ++static const value_string oml_severity_vals[] = { ++ { NM_SEVER_CEASED, "Ceased" }, ++ { NM_SEVER_CRITICAL, "Critical" }, ++ { NM_SEVER_MAJOR, "Major" }, ++ { NM_SEVER_MINOR, "Minor" }, ++ { NM_SEVER_WARNING, "Warning" }, ++ { NM_SEVER_INDETERMINATE, "Indeterminate" }, ++}; ++ ++/* Section 9.4.36: NACK Causes */ ++static const value_string oml_nack_cause[] = { ++ { NM_NACK_INCORR_STRUCT, "Incorrect message structure" }, ++ { NM_NACK_MSGTYPE_INVAL, "Invalid message type value" }, ++ { NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" }, ++ { NM_NACK_OBJCLASS_NOTSUPP, "Object Class not supported" }, ++ { NM_NACK_BTSNR_UNKN, "BTS Number unknown" }, ++ { NM_NACK_TRXNR_UNKN, "TRX Number unknown" }, ++ { NM_NACK_OBJINST_UNKN, "Object Instance unknown" }, ++ { NM_NACK_ATTRID_INVAL, "Invalid Attribute ID value" }, ++ { NM_NACK_ATTRID_NOTSUPP, "Attribute ID not supported" }, ++ { NM_NACK_PARAM_RANGE, "Parameter value out of range" }, ++ { NM_NACK_ATTRLIST_INCONSISTENT, "Inconsistency in Attribute list" }, ++ { NM_NACK_SPEC_IMPL_NOTSUPP, "Specified Implementation not supported" }, ++ { NM_NACK_CANT_PERFORM, "Message cannot be performed" }, ++ { NM_NACK_RES_NOTIMPL, "Resource not implemented" }, ++ { NM_NACK_RES_NOTAVAIL, "Resource not available" }, ++ { NM_NACK_FREQ_NOTAVAIL, "Frequency not available" }, ++ { NM_NACK_TEST_NOTSUPP, "Test not supported" }, ++ { NM_NACK_CAPACITY_RESTR, "Capacity restrictions" }, ++ { NM_NACK_PHYSCFG_NOTPERFORM, "Phys config cannot be performed" }, ++ { NM_NACK_TEST_NOTINIT, "Test not initiated" }, ++ { NM_NACK_PHYSCFG_NOTRESTORE, "Phys config cannot be restored" }, ++ { NM_NACK_TEST_NOSUCH, "No such Test" }, ++ { NM_NACK_TEST_NOSTOP, "Test cannot be stopped" }, ++ { NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsisten with physical config" }, ++ { NM_NACK_FILE_INCOMPLETE, "Complete file not received" }, ++ { NM_NACK_FILE_NOTAVAIL, "File not available at destination" }, ++ { NM_NACK_FILE_NOTACTIVATE, "File cannot be activated" }, ++ { NM_NACK_REQ_NOT_GRANT, "Request not granted" }, ++ { NM_NACK_WAIT, "Wait" }, ++ { NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" }, ++ { NM_NACK_MEAS_NOTSUPP, "Measurement not supported" }, ++ { NM_NACK_MEAS_NOTSTART, "Measurement not started" }, ++ { 0xff, "NULL" }, ++}; ++ ++static const value_string oml_test_no_vals[] = { ++ { NM_IPACC_TESTNO_RLOOP_ANT, "Radio Loop test via antenna" }, ++ { NM_IPACC_TESTNO_RLOOP_XCVR, "Radio Loop test via transceiver" }, ++ { NM_IPACC_TESTNO_FUNC_OBJ, "BTS Functional object self test" }, ++ { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" }, ++ { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" }, ++ { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" }, ++ { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Information" }, ++ { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" }, ++ { NM_IPACC_TESTNO_SYSINFO_MONITOR, "SysInfo Monitor" }, ++ { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH & CCCH Monitor" }, ++}; ++ ++static const value_string ipacc_test_res_vals[] = { ++ { NM_IPACC_TESTRES_SUCCESS, "Success" }, ++ { NM_IPACC_TESTRES_TIMEOUT, "Timeout" }, ++ { NM_IPACC_TESTRES_NO_CHANS, "No suitable channels available" }, ++ { NM_IPACC_TESTRES_PARTIAL, "Partial" }, ++ { NM_IPACC_TESTRES_STOPPED, "Stopped" }, ++}; ++ ++static const value_string ipacc_testres_ie_vals[] = { ++ { NM_IPACC_TR_IE_FREQ_ERR_LIST, "Frequency Error List" }, ++ { NM_IPACC_TR_IE_CHAN_USAGE, "Channel Usage" }, ++ { NM_IPACC_TR_IE_BCCH_INFO, "BCCH Information" }, ++ { NM_IPACC_TR_IE_RESULT_DETAILS,"Result Details" }, ++ { NM_IPACC_TR_IE_FREQ_ERR, "Frequency Error" }, ++}; ++ ++/* Parse the ip.access specific BCCH Information IE embedded into the Test ++ * Report IE */ ++static gint ++ipacc_tr_ie_bcch(tvbuff_t *tvb, proto_tree *att_tree, int offset) ++{ ++ guint16 binfo_type, tmp; ++ ++ binfo_type = tvb_get_ntohs(tvb, offset); ++ offset += 2; ++ ++ tmp = tvb_get_ntohs(tvb, offset); ++ ++ /* FIXME: there are still some bugs remaining here */ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_arfcn, ++ tvb, offset, 2, TRUE); ++ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_f_qual, ++ tvb, offset, 2, TRUE); ++ offset += 2; ++ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_b_rxlev, ++ tvb, offset++, 1, TRUE); ++ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_rxqual, ++ tvb, offset++, 1, TRUE); ++ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_f_err, ++ tvb, offset, 2, TRUE); ++ offset += 2; ++ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_frame_offs, ++ tvb, offset, 2, TRUE); ++ offset += 2; ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_framenr_offs, ++ tvb, offset, 4, TRUE); ++ offset += 4; ++ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_bsic, ++ tvb, offset++, 1, TRUE); ++ ++ de_lai(tvb, att_tree, offset, 5, NULL, 0); ++ offset += 5; ++ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_cell_id, ++ tvb, offset, 2, TRUE); ++ offset += 2; ++ ++ if (binfo_type & 0x8000) { ++ /* System Information 2 */ ++ /* FIXME: Parse 04.18 Neighbour Cell Description */ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_si2, ++ tvb, offset, 16, TRUE); ++ offset += 16; ++ } ++ if (binfo_type & 0x0001) { ++ /* System Information 2bis */ ++ /* FIXME: Parse 04.18 Neighbour Cell Description */ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_si2bis, ++ tvb, offset, 16, TRUE); ++ offset += 16; ++ } ++ if (binfo_type & 0x0002) { ++ /* System Information 2ter */ ++ /* FIXME: Parse 04.18 Neighbour Cell Description */ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_si2ter, ++ tvb, offset, 16, TRUE); ++ offset += 16; ++ } ++ if (binfo_type & 0x0004) { ++ /* FIXME: Parse 04.18 Cell Channel Description */ ++ proto_tree_add_item(att_tree, hf_attr_ipa_tr_chan_desc, ++ tvb, offset, 16, TRUE); ++ offset += 16; ++ } ++ ++ return offset; ++} ++ ++/* Parse the ip.access specific Channel Usage IE embedded into the Test ++ * Report IE */ ++static gint ++ipacc_tr_ie_chan_usage(tvbuff_t *tvb, proto_tree *att_tree, int offset) ++{ ++ while (tvb_reported_length_remaining(tvb, offset) != 0) { ++ guint16 result = tvb_get_ntohs(tvb, offset); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_tr_arfcn, ++ tvb, offset, 2, result); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_tr_rxlev, ++ tvb, offset, 2, result); ++ offset += 2; ++ } ++ return offset; ++} ++ ++/* Parse the ip.access specific format of the standard test report IE */ ++static gint ++dissect_ipacc_test_rep(proto_tree *tree, tvbuff_t *tvb) ++{ ++ gint offset = 0; ++ ++ proto_tree_add_item(tree, hf_attr_ipa_test_res, tvb, offset++, ++ 1, FALSE); ++ ++ while (tvb_reported_length_remaining(tvb, offset) != 0) { ++ guint8 ie = tvb_get_guint8(tvb, offset); ++ guint16 len = tvb_get_ntohs(tvb, offset+1); ++ proto_item *ti; ++ proto_tree *att_tree; ++ ++ ti = proto_tree_add_item(tree, hf_oml_ipa_tres_attr_tag, tvb, ++ offset++, 1, FALSE); ++ att_tree = proto_item_add_subtree(ti, ett_oml_fom_att); ++ proto_tree_add_uint(att_tree, hf_oml_ipa_tres_attr_len, tvb, ++ offset, 2, len); ++ offset += 2; ++ ++ switch (ie) { ++ case NM_IPACC_TR_IE_CHAN_USAGE: ++ offset = ipacc_tr_ie_chan_usage(tvb, ++ att_tree, offset); ++ break; ++ case NM_IPACC_TR_IE_BCCH_INFO: ++ offset = ipacc_tr_ie_bcch(tvb, ++ att_tree, offset); ++ break; ++ default: ++ break; ++ } ++ } ++ return offset; ++} ++ ++/* Dissect OML FOM Attributes after OML + FOM header */ ++static gint ++dissect_oml_attrs(tvbuff_t *tvb, int base_offs, packet_info *pinfo, ++ proto_tree *tree) ++{ ++ int offset = base_offs; ++ ++ while (tvb_reported_length_remaining(tvb, offset) != 0) { ++ guint i; ++ guint8 tag, val8; ++ guint16 val16; ++ guint32 val32; ++ unsigned int len, len_len, hlen; ++ const struct tlv_def *tdef; ++ proto_item *ti; ++ proto_tree *att_tree; ++ tvbuff_t *sub_tvb; ++ ++ tag = tvb_get_guint8(tvb, offset); ++ tdef = &nm_att_tlvdef.def[tag]; ++ ++ switch (tdef->type) { ++ case TLV_TYPE_FIXED: ++ hlen = 1; ++ len_len = 0; ++ len = tdef->fixed_len; ++ break; ++ case TLV_TYPE_T: ++ hlen = 1; ++ len_len = 0; ++ len = 0; ++ break; ++ case TLV_TYPE_TV: ++ hlen = 1; ++ len_len = 0; ++ len = 1; ++ break; ++ case TLV_TYPE_TLV: ++ hlen = 2; ++ len_len = 1; ++ len = tvb_get_guint8(tvb, offset+1); ++ break; ++ case TLV_TYPE_TL16V: ++ hlen = 3; ++ len_len = 2; ++ len = tvb_get_guint8(tvb, offset+1) << 8 | ++ tvb_get_guint8(tvb, offset+2); ++ break; ++ default: ++ hlen = len_len = len = 0; ++ DISSECTOR_ASSERT_NOT_REACHED(); ++ break; ++ } ++ ++ ti = proto_tree_add_item(tree, hf_oml_fom_attr_tag, tvb, ++ offset, 1, FALSE); ++ att_tree = proto_item_add_subtree(ti, ett_oml_fom_att); ++ proto_tree_add_uint(att_tree, hf_oml_fom_attr_len, tvb, ++ offset+1, len_len, len); ++ offset += hlen; ++ ++ sub_tvb = tvb_new_subset(tvb, offset, len, len); ++ ++ switch (tag) { ++ /* parse only the most common IE for now */ ++ case NM_ATT_ABIS_CHANNEL: ++ proto_tree_add_item(att_tree, hf_attr_ach_btsp, tvb, ++ offset, 1, TRUE); ++ proto_tree_add_item(att_tree, hf_attr_ach_tslot, tvb, ++ offset+1, 1, TRUE); ++ proto_tree_add_item(att_tree, hf_attr_ach_sslot, tvb, ++ offset+2, 1, TRUE); ++ break; ++ case NM_ATT_ADM_STATE: ++ proto_tree_add_item(att_tree, hf_attr_adm_state, tvb, ++ offset, len, FALSE); ++ val8 = tvb_get_guint8(tvb, offset); ++ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", ++ val_to_str(val8, oml_adm_state_vals, ++ "%02x")); ++ break; ++ case NM_ATT_ARFCN_LIST: ++ for (i = 0; i < len; i += 2) { ++ val16 = tvb_get_ntohs(tvb, offset + i); ++ proto_tree_add_uint(att_tree, hf_attr_arfcn, ++ tvb, offset + i, 2, val16); ++ } ++ break; ++ case NM_ATT_AVAIL_STATUS: ++ /* Availability status can have length 0 */ ++ if (len) { ++ val8 = tvb_get_guint8(tvb, offset); ++ proto_tree_add_item(att_tree, ++ hf_attr_avail_state, tvb, ++ offset, len, FALSE); ++ } else ++ val8 = 0xff; ++ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", ++ val_to_str(val8, oml_avail_state_vals, ++ "%02x")); ++ break; ++ case NM_ATT_BCCH_ARFCN: ++ proto_tree_add_item(att_tree, hf_attr_bcch_arfcn, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_BSIC: ++ proto_tree_add_item(att_tree, hf_attr_bsic, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_CHAN_COMB: ++ proto_tree_add_item(att_tree, hf_attr_chan_comb, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_EVENT_TYPE: ++ proto_tree_add_item(att_tree, hf_attr_event_type, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_GSM_TIME: ++ proto_tree_add_item(att_tree, hf_attr_gsm_time, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_OPER_STATE: ++ proto_tree_add_item(att_tree, hf_attr_oper_state, tvb, ++ offset, len, FALSE); ++ val8 = tvb_get_guint8(tvb, offset); ++ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", ++ val_to_str(val8, oml_oper_state_vals, ++ "%02x")); ++ break; ++ case NM_ATT_TEI: ++ proto_tree_add_item(att_tree, hf_attr_tei, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_TSC: ++ proto_tree_add_item(att_tree, hf_attr_tsc, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_SEVERITY: ++ proto_tree_add_item(att_tree, hf_attr_severity, tvb, ++ offset, len, TRUE); ++ break; ++ case NM_ATT_TEST_REPORT: ++ dissect_ipacc_test_rep(att_tree, sub_tvb); ++ break; ++ case NM_ATT_TEST_NO: ++ proto_tree_add_item(att_tree, hf_attr_test_no, tvb, ++ offset, len, TRUE); ++ val8 = tvb_get_guint8(tvb, offset); ++ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", ++ val_to_str(val8, oml_test_no_vals, ++ "%02x")); ++ break; ++ ++ /* proprietary ip.access extensions */ ++ case NM_ATT_IPACC_DST_IP: ++ val32 = tvb_get_ntohl(tvb, offset); ++ proto_tree_add_ipv4(att_tree, hf_attr_ipa_rsl_ip, tvb, ++ offset, len, val32); ++ break; ++ case NM_ATT_IPACC_DST_IP_PORT: ++ val16 = tvb_get_ntohs(tvb, offset); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_rsl_port, tvb, ++ offset, len, val16); ++ break; ++ case NM_ATT_IPACC_LOCATION: ++ proto_tree_add_item(att_tree, hf_attr_ipa_location_name, ++ tvb, offset, len, TRUE); ++ break; ++ case NM_ATT_IPACC_UNIT_ID: ++ proto_tree_add_item(att_tree, hf_attr_ipa_unit_id, ++ tvb, offset, len, TRUE); ++ break; ++ case NM_ATT_IPACC_UNIT_NAME: ++ proto_tree_add_item(att_tree, hf_attr_ipa_unit_name, ++ tvb, offset, len, TRUE); ++ break; ++ case NM_ATT_IPACC_PRIM_OML_CFG_LIST: ++ proto_tree_add_item(att_tree, hf_attr_ipa_prim_oml_ip, ++ tvb, offset+1, 4, TRUE); ++ proto_tree_add_item(att_tree, hf_attr_ipa_prim_oml_port, ++ tvb, offset+1+4, 2, TRUE); ++ break; ++ case NM_ATT_IPACC_NV_FLAGS: ++ { ++ guint flags, mask; ++ flags = tvb_get_guint8(tvb, offset); ++ mask = tvb_get_guint8(tvb, offset+1); ++ flags |= tvb_get_guint8(tvb, offset+2) << 8; ++ mask |= tvb_get_guint8(tvb, offset+3) << 8; ++ proto_tree_add_uint(att_tree, hf_attr_ipa_nv_flags, ++ tvb, offset, 3, flags); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_nv_mask, ++ tvb, offset+1, 3, mask); ++ } ++ break; ++ case NM_ATT_IPACC_RAC: ++ proto_tree_add_item(att_tree, hf_attr_ipa_rac, ++ tvb, offset, 1, TRUE); ++ break; ++ case NM_ATT_IPACC_NSEI: ++ val16 = tvb_get_ntohs(tvb, offset); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_nsei, ++ tvb, offset, 2, val16); ++ break; ++ case NM_ATT_IPACC_NSVCI: ++ val16 = tvb_get_ntohs(tvb, offset); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_nsvci, ++ tvb, offset, 2, val16); ++ break; ++ case NM_ATT_IPACC_BVCI: ++ val16 = tvb_get_ntohs(tvb, offset); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_bvci, ++ tvb, offset, 2, val16); ++ break; ++ case NM_ATT_IPACC_NS_LINK_CFG: ++ val16 = tvb_get_ntohs(tvb, offset); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_nsl_sport, ++ tvb, offset, 2, val16); ++ val32 = tvb_get_ipv4(tvb, offset+2); ++ proto_tree_add_ipv4(att_tree, hf_attr_ipa_nsl_daddr, ++ tvb, offset+2, 4, val32); ++ val16 = tvb_get_ntohs(tvb, offset+6); ++ proto_tree_add_uint(att_tree, hf_attr_ipa_nsl_dport, ++ tvb, offset+6, 2, val16); ++ break; ++ default: ++ proto_tree_add_item(att_tree, hf_oml_fom_attr_val, tvb, ++ offset, len, FALSE); ++ } ++ offset += len; ++ } ++ return offset; ++} ++ ++static int ++dissect_oml_fom(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ++ int offset, proto_item *top_ti) ++{ ++ guint8 msg_type, obj_class, bts_nr, trx_nr, ts_nr; ++ proto_item *ti; ++ proto_tree *fom_tree; ++ ++ msg_type = tvb_get_guint8(tvb, offset); ++ obj_class = tvb_get_guint8(tvb, offset+1); ++ bts_nr = tvb_get_guint8(tvb, offset+2); ++ trx_nr = tvb_get_guint8(tvb, offset+3); ++ ts_nr = tvb_get_guint8(tvb, offset+4); ++ proto_item_append_text(top_ti, ", %s(%02x,%02x,%02x) %s ", ++ val_to_str(obj_class, oml_fom_objclass_vals, "%02x"), ++ bts_nr, trx_nr, ts_nr, ++ val_to_str(msg_type, oml_fom_msgtype_vals, ++ "unknown 0x%x")); ++ col_append_fstr(pinfo->cinfo, COL_INFO, "%s(%02x,%02x,%02x) %s ", ++ val_to_str(obj_class, oml_fom_objclass_vals, "%02x"), ++ bts_nr, trx_nr, ts_nr, ++ val_to_str(msg_type, oml_fom_msgtype_vals, ++ "unknown 0x%x")); ++ ti = proto_tree_add_item(tree, hf_oml_fom_msgtype, tvb, offset++, 1, FALSE); ++ fom_tree = proto_item_add_subtree(ti, ett_oml_fom); ++ proto_tree_add_item(fom_tree, hf_oml_fom_objclass, tvb, offset++, 1, FALSE); ++ proto_tree_add_item(fom_tree, hf_oml_fom_inst_bts, tvb, offset++, 1, FALSE); ++ proto_tree_add_item(fom_tree, hf_oml_fom_inst_trx, tvb, offset++, 1, FALSE); ++ proto_tree_add_item(fom_tree, hf_oml_fom_inst_ts, tvb, offset++, 1, FALSE); ++ ++ ++ /* dissect the TLV objects in the message body */ ++ offset = dissect_oml_attrs(tvb, offset, pinfo, fom_tree); ++ ++ return offset; ++} ++ ++static const guint8 ipaccess_magic[] = "com.ipaccess"; ++ ++static int ++dissect_oml_manuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ++ int offset, proto_item *top_ti) ++{ ++ if (tvb_get_guint8(tvb, offset) != 0x0d || ++ tvb_memeql(tvb, offset+1, ipaccess_magic, sizeof(ipaccess_magic))) ++ return offset; ++ ++ offset += sizeof(ipaccess_magic) + 1; ++ ++ return dissect_oml_fom(tvb, pinfo, tree, offset, top_ti); ++} ++ ++static void ++dissect_abis_oml(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) ++{ ++ proto_item *ti; ++ proto_tree *oml_tree; ++ ++ int offset = 0; ++ ++ col_set_str(pinfo->cinfo, COL_PROTOCOL, "OML"); ++ ++ top_tree = tree; ++ if (tree) { ++ u_int8_t msg_disc = tvb_get_guint8(tvb, offset); ++ ++ ti = proto_tree_add_item(tree, proto_abis_oml, tvb, 0, -1, FALSE); ++ oml_tree = proto_item_add_subtree(ti, ett_oml); ++ ++ proto_tree_add_item(oml_tree, hf_oml_msg_disc, tvb, offset++, ++ 1, TRUE); ++ proto_tree_add_item(oml_tree, hf_oml_placement, tvb, offset++, ++ 1, TRUE); ++ proto_tree_add_item(oml_tree, hf_oml_sequence, tvb, offset++, ++ 1, TRUE); ++ proto_tree_add_item(oml_tree, hf_oml_length, tvb, offset++, ++ 1, TRUE); ++ ++ switch (msg_disc) { ++ case ABIS_OM_MDISC_FOM: ++ offset = dissect_oml_fom(tvb, pinfo, oml_tree, ++ offset, ti); ++ break; ++ case ABIS_OM_MDISC_MANUF: ++ offset = dissect_oml_manuf(tvb, pinfo, oml_tree, offset, ti); ++ break; ++ case ABIS_OM_MDISC_MMI: ++ case ABIS_OM_MDISC_TRAU: ++ default: ++ break; ++ } ++ } ++} ++ ++void ++proto_reg_handoff_abis_oml(void); ++ ++void ++proto_register_abis_oml(void) ++{ ++ static hf_register_info hf[] = { ++ { &hf_oml_msg_disc, ++ { "Message Discriminator", "oml.msg_dsc", ++ FT_UINT8, BASE_HEX, VALS(oml_msg_disc_vals), 0, ++ "GSM 12.21 Message Discriminator", HFILL } ++ }, ++ { &hf_oml_placement, ++ { "Placement Indicator", "oml.placement", ++ FT_UINT8, BASE_HEX, VALS(oml_placement_vals), 0, ++ "GSM 12.21 Placement Indicator", HFILL } ++ }, ++ { &hf_oml_sequence, ++ { "Sequence Number", "oml.sequence", ++ FT_UINT8, BASE_HEX, NULL, 0, ++ "Sequence Number (if multi-part msg)", HFILL } ++ }, ++ { &hf_oml_length, ++ { "Length Indicator", "oml.length", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ "Total length of payload", HFILL } ++ }, ++ { &hf_oml_fom_msgtype, ++ { "FOM Message Type", "oml.fom.msg_type", ++ FT_UINT8, BASE_HEX, VALS(oml_fom_msgtype_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_oml_fom_objclass, ++ { "FOM Object Class", "oml.fom.obj_class", ++ FT_UINT8, BASE_HEX, VALS(oml_fom_objclass_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_oml_fom_inst_bts, ++ { "FOM Object Instance BTS", "oml.fom.obj_inst.bts", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_oml_fom_inst_trx, ++ { "FOM Object Instance TRX", "oml.fom.obj_inst.trx", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_oml_fom_inst_ts, ++ { "FOM Object Instance TS", "oml.fom.obj_inst.ts", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_oml_fom_attr_tag, ++ { "FOM Attribute ID", "oml.fom.attr_id", ++ FT_UINT8, BASE_HEX, VALS(oml_fom_attr_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_oml_fom_attr_len, ++ { "FOM Attribute Length", "oml.fom.attr_len", ++ FT_UINT16, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_oml_fom_attr_val, ++ { "FOM Attribute Value", "oml.fom.attr_val", ++ FT_BYTES, BASE_NONE, NULL, 0, ++ NULL, HFILL } ++ }, ++ ++ ++ ++ /* OML Attributes */ ++ { &hf_attr_adm_state, ++ { "Administrative State", "oml.fom.attr.adm_state", ++ FT_UINT8, BASE_HEX, VALS(oml_adm_state_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_arfcn, ++ { "ARFCN", "oml.fom.attr.arfcn", ++ FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_oper_state, ++ { "Operational State", "oml.fom.attr.oper_state", ++ FT_UINT8, BASE_HEX, VALS(oml_oper_state_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_avail_state, ++ { "Availability Status", "oml.fom.attr.avail_state", ++ FT_UINT8, BASE_HEX, VALS(oml_avail_state_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_event_type, ++ { "Event Type", "oml.fom.attr.event_type", ++ FT_UINT8, BASE_HEX, VALS(oml_event_type_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_severity, ++ { "Severity", "oml.fom.attr.severity", ++ FT_UINT8, BASE_HEX, VALS(oml_severity_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_bcch_arfcn, ++ { "BCCH ARFCN", "oml.fom.attr.bcch_arfcn", ++ FT_UINT16, BASE_DEC, NULL, 0, ++ "ARFCN of the BCCH", HFILL } ++ }, ++ { &hf_attr_bsic, ++ { "BSIC", "oml.fom.attr.bsic", ++ FT_UINT16, BASE_HEX, NULL, 0, ++ "Base Station Identity Cdoe", HFILL } ++ }, ++ { &hf_attr_test_no, ++ { "Test Number", "oml.fom.attr.test_no", ++ FT_UINT8, BASE_HEX, VALS(oml_test_no_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_tsc, ++ { "TSC", "oml.fom.attr.tsc", ++ FT_UINT8, BASE_HEX, NULL, 0, ++ "Training Sequence Code", HFILL } ++ }, ++ { &hf_attr_tei, ++ { "TEI", "oml.fom.attr.tei", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_ach_btsp, ++ { "BTS E1 Port", "oml.fom.attr.abis_ch.bts_port", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_ach_tslot, ++ { "E1 Timeslot", "oml.fom.attr.abis_ch.timeslot", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_ach_sslot, ++ { "E1 Subslot", "oml.fom.attr.abis_ch.subslot", ++ FT_UINT8, BASE_DEC, NULL, 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_gsm_time, ++ { "GSM Time", "oml.fom.attr.gsm_time", ++ FT_UINT16, BASE_DEC, NULL, 0, ++ "GSM Time", HFILL } ++ }, ++ { &hf_attr_chan_comb, ++ { "Channel Combination", "oml.fom.attr.chan_comb", ++ FT_UINT8, BASE_HEX, VALS(oml_chan_comb_vals), 0, ++ NULL, HFILL } ++ }, ++ /* IP Access */ ++ { &hf_oml_ipa_tres_attr_tag, ++ { "IPA Test Result Embedded IE", ++ "oml.fom.testrep.ipa_tag", ++ FT_UINT8, BASE_HEX, VALS(ipacc_testres_ie_vals), 0, ++ "Information Element embedded into the Test Result " ++ "of ip.access BTS", HFILL }, ++ }, ++ { &hf_oml_ipa_tres_attr_len, ++ { "IPA Test Result Embedded IE Length", ++ "oml.fom.testrep.ipa_len", ++ FT_UINT16, BASE_DEC, NULL, 0, ++ "Length of ip.access Test Result Embedded IE", HFILL } ++ }, ++ { &hf_attr_ipa_test_res, ++ { "IPA Test Result", "oml.fom.testrep.result", ++ FT_UINT8, BASE_DEC, VALS(ipacc_test_res_vals), 0, ++ NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_rxlev, ++ { "Rx Level", "oml.fom.testrep.ipa_rxlev", ++ FT_UINT16, BASE_DEC, NULL, 0xfc00, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_b_rxlev, ++ { "Rx Level", "oml.fom.testrep.ipa_rxlev_b", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_arfcn, ++ { "ARFCN", "oml.fom.testrep.ipa_arfcn", ++ FT_UINT16, BASE_DEC, NULL, 0x03ff, "ARFCN", HFILL } ++ }, ++ { &hf_attr_ipa_tr_f_qual, ++ { "Frequency Quality", "oml.fom.testrep.ipa.freq_qual", ++ FT_UINT8, BASE_DEC, NULL, 0xfc, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_f_err, ++ { "Frequency Error", "oml.fom.testrep.ipa.freq_err", ++ FT_INT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_rxqual, ++ { "Rx Quality", "oml.fom.testrep.ipa.rx_qual", ++ FT_UINT8, BASE_DEC, NULL, 0x7, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_frame_offs, ++ { "Frame Offset", "oml.fom.testrep.ipa.frame_offset", ++ FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_framenr_offs, ++ { "Frame Number Offset", ++ "oml.fom.testrep.ipa.framenr_offset", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_bsic, ++ { "BSIC", "oml.fom.testrep.ipa.bsic", ++ FT_UINT8, BASE_DEC, NULL, 0x3f, ++ "Base Station Identity Code", HFILL } ++ }, ++ { &hf_attr_ipa_tr_cell_id, ++ { "Cell ID", "oml.fom.testrep.ipa.cell_id", ++ FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_rsl_ip, ++ { "BSC RSL IP Address", "oml.fom.attr.ipa.rsl_ip", ++ FT_IPv4, BASE_NONE, NULL, 0, ++ "IP Address to which the BTS establishes " ++ "the RSL link", HFILL } ++ }, ++ { &hf_attr_ipa_rsl_port, ++ { "BSC RSL TCP Port", "oml.fom.attr.ipa.rsl_port", ++ FT_UINT16, BASE_DEC, NULL, 0, ++ "Port number to which the BST establishes " ++ "the RSL link", HFILL } ++ }, ++ { &hf_attr_ipa_prim_oml_ip, ++ { "Primary OML IP Address", ++ "oml.fom.attr.ipa.prim_oml_ip", ++ FT_IPv4, BASE_NONE, NULL, 0, ++ "IP Address of the BSC for the primary OML link", ++ HFILL } ++ }, ++ { &hf_attr_ipa_prim_oml_port, ++ { "Primary OML TCP Port", ++ "oml.fom.attr.ipa.prim_oml_port", ++ FT_UINT16, BASE_DEC, NULL, 0, ++ "TCP Port of the BSC for the primarly OML link", ++ HFILL } ++ }, ++ { &hf_attr_ipa_location_name, ++ { "Location Name", "oml.fom.attr.ipa.loc_name", ++ FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_unit_name, ++ { "Unit Name", "oml.fom.attr.ipa.unit_name", ++ FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_unit_id, ++ { "Unit ID", "oml.fom.attr.ipa.unit_id", ++ FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_nv_flags, ++ { "NVRAM Config Flags", "oml.fom.attr.ipa.nv_flags", ++ FT_UINT16, BASE_HEX, NULL, 0xffff, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_nv_mask, ++ { "NVRAM Config Mask", "oml.fom.attr.ipa.nv_mask", ++ FT_UINT16, BASE_HEX, NULL, 0xffff, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_si2, ++ { "System Information 2", "oml.fom.attr.ipa.si2", ++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_si2bis, ++ { "System Information 2bis", "oml.fom.attr.ipa.si2bis", ++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_si2ter, ++ { "System Information 2ter", "oml.fom.attr.ipa.si2ter", ++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_tr_chan_desc, ++ { "Cell Channel Description", ++ "oml.fom.attr.ipa.chan_desc", ++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_nsl_sport, ++ { "NS Link IP Source Port", ++ "oml.fom.attr.ipa.nsl_sport", ++ FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_nsl_daddr, ++ { "NS Link IP Destination Addr", ++ "oml.fom.attr.ipa.nsl_daddr", ++ FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_nsl_dport, ++ { "NS Link IP Destination Port", ++ "oml.fom.attr.ipa.nsl_dport", ++ FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_nsei, ++ { "NSEI", "oml.fom.attr.ipa.nsei", ++ FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_nsvci, ++ { "NSVCI", "oml.fom.attr.ipa.nsvci", ++ FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_bvci, ++ { "BVCI", "oml.fom.attr.ipa.bvci", ++ FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_attr_ipa_rac, ++ { "RAC", "oml.fom.attr.ipa.rac", ++ FT_UINT8, BASE_HEX, NULL, 0, ++ "Routing Area Code", HFILL } ++ }, ++ }; ++ static gint *ett[] = { ++ &ett_oml, ++ &ett_oml_fom, ++ &ett_oml_fom_att, ++ }; ++ ++ module_t *oml_module; ++ ++ proto_abis_oml = proto_register_protocol("GSM A-bis OML", "OML", ++ "gsm_abis_oml"); ++ ++ proto_register_field_array(proto_abis_oml, hf, array_length(hf)); ++ ++ proto_register_subtree_array(ett, array_length(ett)); ++ ++ register_dissector("gsm_abis_oml", dissect_abis_oml, proto_abis_oml); ++ ++ ++ oml_module = prefs_register_protocol(proto_abis_oml, proto_reg_handoff_abis_oml); ++ prefs_register_bool_preference(oml_module, "use_ipaccess_oml", ++ "Use nanoBTS definitions", ++ "Use ipaccess nanoBTS specific definitions for OML", ++ &global_oml_use_nano_bts); ++} ++ ++void ++proto_reg_handoff_abis_oml(void) ++{ ++ dissector_handle_t abis_oml_handle; ++ ++ abis_oml_handle = create_dissector_handle(dissect_abis_oml, proto_abis_oml); ++ dissector_add("lapd.gsm.sapi", LAPD_GSM_SAPI_OM_PROC, abis_oml_handle); ++} +Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h +=================================================================== +--- /dev/null ++++ wireshark/epan/dissectors/packet-gsm_abis_oml.h +@@ -0,0 +1,786 @@ ++/* GSM Network Management messages on the A-bis interface ++ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ ++ ++/* (C) 2008-2009 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. ++ * ++ */ ++ ++#ifndef __PACKET_ABIS_OML_H__ ++#define __PACKET_ABIS_OML_H__ ++ ++#include <sys/types.h> ++ ++/* From openbsc/include/openbsc/abis_nm.h */ ++ ++/* generic header in front of every OML message according to TS 08.59 */ ++struct abis_om_hdr { ++ guint8 mdisc; ++ guint8 placement; ++ guint8 sequence; ++ guint8 length; ++ guint8 data[0]; ++} __attribute__ ((packed)); ++ ++#define ABIS_OM_MDISC_FOM 0x80 ++#define ABIS_OM_MDISC_MMI 0x40 ++#define ABIS_OM_MDISC_TRAU 0x20 ++#define ABIS_OM_MDISC_MANUF 0x10 ++#define ABIS_OM_PLACEMENT_ONLY 0x80 ++#define ABIS_OM_PLACEMENT_FIRST 0x40 ++#define ABIS_OM_PLACEMENT_MIDDLE 0x20 ++#define ABIS_OM_PLACEMENT_LAST 0x10 ++ ++struct abis_om_obj_inst { ++ guint8 bts_nr; ++ guint8 trx_nr; ++ guint8 ts_nr; ++} __attribute__ ((packed)); ++ ++struct abis_om_fom_hdr { ++ guint8 msg_type; ++ guint8 obj_class; ++ struct abis_om_obj_inst obj_inst; ++ guint8 data[0]; ++} __attribute__ ((packed)); ++ ++#define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr)) ++ ++/* Section 9.1: Message Types */ ++enum abis_nm_msgtype { ++ /* SW Download Management Messages */ ++ NM_MT_LOAD_INIT = 0x01, ++ NM_MT_LOAD_INIT_ACK, ++ NM_MT_LOAD_INIT_NACK, ++ NM_MT_LOAD_SEG, ++ NM_MT_LOAD_SEG_ACK, ++ NM_MT_LOAD_ABORT, ++ NM_MT_LOAD_END, ++ NM_MT_LOAD_END_ACK, ++ NM_MT_LOAD_END_NACK, ++ NM_MT_SW_ACT_REQ, /* BTS->BSC */ ++ NM_MT_SW_ACT_REQ_ACK, ++ NM_MT_SW_ACT_REQ_NACK, ++ NM_MT_ACTIVATE_SW, /* BSC->BTS */ ++ NM_MT_ACTIVATE_SW_ACK, ++ NM_MT_ACTIVATE_SW_NACK, ++ NM_MT_SW_ACTIVATED_REP, /* 0x10 */ ++ /* A-bis Interface Management Messages */ ++ NM_MT_ESTABLISH_TEI = 0x21, ++ NM_MT_ESTABLISH_TEI_ACK, ++ NM_MT_ESTABLISH_TEI_NACK, ++ NM_MT_CONN_TERR_SIGN, ++ NM_MT_CONN_TERR_SIGN_ACK, ++ NM_MT_CONN_TERR_SIGN_NACK, ++ NM_MT_DISC_TERR_SIGN, ++ NM_MT_DISC_TERR_SIGN_ACK, ++ NM_MT_DISC_TERR_SIGN_NACK, ++ NM_MT_CONN_TERR_TRAF, ++ NM_MT_CONN_TERR_TRAF_ACK, ++ NM_MT_CONN_TERR_TRAF_NACK, ++ NM_MT_DISC_TERR_TRAF, ++ NM_MT_DISC_TERR_TRAF_ACK, ++ NM_MT_DISC_TERR_TRAF_NACK, ++ /* Transmission Management Messages */ ++ NM_MT_CONN_MDROP_LINK = 0x31, ++ NM_MT_CONN_MDROP_LINK_ACK, ++ NM_MT_CONN_MDROP_LINK_NACK, ++ NM_MT_DISC_MDROP_LINK, ++ NM_MT_DISC_MDROP_LINK_ACK, ++ NM_MT_DISC_MDROP_LINK_NACK, ++ /* Air Interface Management Messages */ ++ NM_MT_SET_BTS_ATTR = 0x41, ++ NM_MT_SET_BTS_ATTR_ACK, ++ NM_MT_SET_BTS_ATTR_NACK, ++ NM_MT_SET_RADIO_ATTR, ++ NM_MT_SET_RADIO_ATTR_ACK, ++ NM_MT_SET_RADIO_ATTR_NACK, ++ NM_MT_SET_CHAN_ATTR, ++ NM_MT_SET_CHAN_ATTR_ACK, ++ NM_MT_SET_CHAN_ATTR_NACK, ++ /* Test Management Messages */ ++ NM_MT_PERF_TEST = 0x51, ++ NM_MT_PERF_TEST_ACK, ++ NM_MT_PERF_TEST_NACK, ++ NM_MT_TEST_REP, ++ NM_MT_SEND_TEST_REP, ++ NM_MT_SEND_TEST_REP_ACK, ++ NM_MT_SEND_TEST_REP_NACK, ++ NM_MT_STOP_TEST, ++ NM_MT_STOP_TEST_ACK, ++ NM_MT_STOP_TEST_NACK, ++ /* State Management and Event Report Messages */ ++ NM_MT_STATECHG_EVENT_REP = 0x61, ++ NM_MT_FAILURE_EVENT_REP, ++ NM_MT_STOP_EVENT_REP, ++ NM_MT_STOP_EVENT_REP_ACK, ++ NM_MT_STOP_EVENT_REP_NACK, ++ NM_MT_REST_EVENT_REP, ++ NM_MT_REST_EVENT_REP_ACK, ++ NM_MT_REST_EVENT_REP_NACK, ++ NM_MT_CHG_ADM_STATE, ++ NM_MT_CHG_ADM_STATE_ACK, ++ NM_MT_CHG_ADM_STATE_NACK, ++ NM_MT_CHG_ADM_STATE_REQ, ++ NM_MT_CHG_ADM_STATE_REQ_ACK, ++ NM_MT_CHG_ADM_STATE_REQ_NACK, ++ NM_MT_REP_OUTST_ALARMS = 0x93, ++ NM_MT_REP_OUTST_ALARMS_ACK, ++ NM_MT_REP_OUTST_ALARMS_NACK, ++ /* Equipment Management Messages */ ++ NM_MT_CHANGEOVER = 0x71, ++ NM_MT_CHANGEOVER_ACK, ++ NM_MT_CHANGEOVER_NACK, ++ NM_MT_OPSTART, ++ NM_MT_OPSTART_ACK, ++ NM_MT_OPSTART_NACK, ++ NM_MT_REINIT, ++ NM_MT_REINIT_ACK, ++ NM_MT_REINIT_NACK, ++ NM_MT_SET_SITE_OUT, /* BS11: get alarm ?!? */ ++ NM_MT_SET_SITE_OUT_ACK, ++ NM_MT_SET_SITE_OUT_NACK, ++ NM_MT_CHG_HW_CONF = 0x90, ++ NM_MT_CHG_HW_CONF_ACK, ++ NM_MT_CHG_HW_CONF_NACK, ++ /* Measurement Management Messages */ ++ NM_MT_MEAS_RES_REQ = 0x8a, ++ NM_MT_MEAS_RES_RESP, ++ NM_MT_STOP_MEAS, ++ NM_MT_START_MEAS, ++ /* Other Messages */ ++ NM_MT_GET_ATTR = 0x81, ++ NM_MT_GET_ATTR_RESP, ++ NM_MT_GET_ATTR_NACK, ++ NM_MT_SET_ALARM_THRES, ++ NM_MT_SET_ALARM_THRES_ACK, ++ NM_MT_SET_ALARM_THRES_NACK, ++ ++ NM_MT_IPACC_RESTART = 0x87, ++ NM_MT_IPACC_RESTART_ACK, ++}; ++ ++enum abis_nm_msgtype_bs11 { ++ NM_MT_BS11_RESET_RESOURCE = 0x74, ++ ++ NM_MT_BS11_BEGIN_DB_TX = 0xa3, ++ NM_MT_BS11_BEGIN_DB_TX_ACK, ++ NM_MT_BS11_BEGIN_DB_TX_NACK, ++ NM_MT_BS11_END_DB_TX = 0xa6, ++ NM_MT_BS11_END_DB_TX_ACK, ++ NM_MT_BS11_END_DB_TX_NACK, ++ NM_MT_BS11_CREATE_OBJ = 0xa9, ++ NM_MT_BS11_CREATE_OBJ_ACK, ++ NM_MT_BS11_CREATE_OBJ_NACK, ++ NM_MT_BS11_DELETE_OBJ = 0xac, ++ NM_MT_BS11_DELETE_OBJ_ACK, ++ NM_MT_BS11_DELETE_OBJ_NACK, ++ ++ NM_MT_BS11_SET_ATTR = 0xd0, ++ NM_MT_BS11_SET_ATTR_ACK, ++ NM_MT_BS11_SET_ATTR_NACK, ++ NM_MT_BS11_LMT_SESSION = 0xdc, ++ ++ NM_MT_BS11_GET_STATE = 0xe3, ++ NM_MT_BS11_GET_STATE_ACK, ++ NM_MT_BS11_LMT_LOGON = 0xe5, ++ NM_MT_BS11_LMT_LOGON_ACK, ++ NM_MT_BS11_RESTART = 0xe7, ++ NM_MT_BS11_RESTART_ACK, ++ NM_MT_BS11_DISCONNECT = 0xe9, ++ NM_MT_BS11_DISCONNECT_ACK, ++ NM_MT_BS11_LMT_LOGOFF = 0xec, ++ NM_MT_BS11_LMT_LOGOFF_ACK, ++ NM_MT_BS11_RECONNECT = 0xf1, ++ NM_MT_BS11_RECONNECT_ACK, ++}; ++ ++enum abis_nm_msgtype_ipacc { ++ NM_MT_IPACC_RSL_CONNECT = 0xe0, ++ NM_MT_IPACC_RSL_CONNECT_ACK, ++ NM_MT_IPACC_RSL_CONNECT_NACK, ++ NM_MT_IPACC_RSL_DISCONNECT = 0xe3, ++ NM_MT_IPACC_RSL_DISCONNECT_ACK, ++ NM_MT_IPACC_RSL_DISCONNECT_NACK, ++ NM_MT_IPACC_CONN_TRAF = 0xe6, ++ NM_MT_IPACC_CONN_TRAF_ACK, ++ NM_MT_IPACC_CONN_TRAF_NACK, ++ NM_MT_IPACC_DISC_TRAF = 0xe9, ++ NM_MT_IPACC_DISC_TRAF_ACK, ++ NM_MT_IPACC_DISC_TRAF_NACK, ++ NM_MT_IPACC_DEF_BOOT_SW = 0xec, ++ NM_MT_IPACC_DEF_BOOT_SW_ACK, ++ NM_MT_IPACC_DEF_BOOT_SW_NACK, ++ NM_MT_IPACC_SET_NVATTR = 0xef, ++ NM_MT_IPACC_SET_NVATTR_ACK, ++ NM_MT_IPACC_SET_NVATTR_NACK, ++ NM_MT_IPACC_GET_NVATTR = 0xf2, ++ NM_MT_IPACC_GET_NVATTR_ACK, ++ NM_MT_IPACC_GET_NVATTR_NACK, ++ NM_MT_IPACC_SET_ATTR = 0xf5, ++ NM_MT_IPACC_SET_ATTR_ACK, ++ NM_MT_IPACC_SET_ATTR_NACK, ++ NM_MT_IPACC_ATTR_CHG_EVT = 0xf8, ++ NM_MT_IPACC_SW_DEACT = 0xf9, ++ NM_MT_IPACC_SW_DEACT_ACK, ++ NM_MT_IPACC_SW_DEACT_NACK, ++ NM_MT_IPACC_MEAS_RES_REQ_NACK = 0xfc, ++ NM_MT_IPACC_START_MEAS_NACK, ++ NM_MT_IPACC_STOP_MEAS_NACK, ++}; ++ ++enum abis_nm_bs11_cell_alloc { ++ NM_BS11_CANR_GSM = 0x00, ++ NM_BS11_CANR_DCS1800 = 0x01, ++}; ++ ++/* Section 9.2: Object Class */ ++enum abis_nm_obj_class { ++ NM_OC_SITE_MANAGER = 0x00, ++ NM_OC_BTS, ++ NM_OC_RADIO_CARRIER, ++ NM_OC_CHANNEL, ++ NM_OC_BASEB_TRANSC, ++ /* RFU: 05-FE */ ++ NM_OC_BS11_ADJC = 0xa0, ++ NM_OC_BS11_HANDOVER = 0xa1, ++ NM_OC_BS11_PWR_CTRL = 0xa2, ++ NM_OC_BS11_BTSE = 0xa3, /* LMT? */ ++ NM_OC_BS11_RACK = 0xa4, ++ NM_OC_BS11 = 0xa5, /* 01: ALCO */ ++ NM_OC_BS11_TEST = 0xa6, ++ NM_OC_BS11_ENVABTSE = 0xa8, ++ NM_OC_BS11_BPORT = 0xa9, ++ ++ NM_OC_GPRS_NSE = 0xf0, ++ NM_OC_GPRS_CELL = 0xf1, ++ NM_OC_GPRS_NSVC0 = 0xf2, ++ NM_OC_GPRS_NSVC1 = 0xf3, ++ ++ NM_OC_NULL = 0xff, ++}; ++ ++/* Section 9.4: Attributes */ ++enum abis_nm_attr { ++ NM_ATT_ABIS_CHANNEL = 0x01, ++ NM_ATT_ADD_INFO, ++ NM_ATT_ADD_TEXT, ++ NM_ATT_ADM_STATE, ++ NM_ATT_ARFCN_LIST, ++ NM_ATT_AUTON_REPORT, ++ NM_ATT_AVAIL_STATUS, ++ NM_ATT_BCCH_ARFCN, ++ NM_ATT_BSIC, ++ NM_ATT_BTS_AIR_TIMER, ++ NM_ATT_CCCH_L_I_P, ++ NM_ATT_CCCH_L_T, ++ NM_ATT_CHAN_COMB, ++ NM_ATT_CONN_FAIL_CRIT, ++ NM_ATT_DEST, ++ /* res */ ++ NM_ATT_EVENT_TYPE = 0x11, /* BS11: file data ?!? */ ++ NM_ATT_FILE_ID, ++ NM_ATT_FILE_VERSION, ++ NM_ATT_GSM_TIME, ++ NM_ATT_HSN, ++ NM_ATT_HW_CONFIG, ++ NM_ATT_HW_DESC, ++ NM_ATT_INTAVE_PARAM, ++ NM_ATT_INTERF_BOUND, ++ NM_ATT_LIST_REQ_ATTR, ++ NM_ATT_MAIO, ++ NM_ATT_MANUF_STATE, ++ NM_ATT_MANUF_THRESH, ++ NM_ATT_MANUF_ID, ++ NM_ATT_MAX_TA, ++ NM_ATT_MDROP_LINK, /* 0x20 */ ++ NM_ATT_MDROP_NEXT, ++ NM_ATT_NACK_CAUSES, ++ NM_ATT_NY1, ++ NM_ATT_OPER_STATE, ++ NM_ATT_OVERL_PERIOD, ++ NM_ATT_PHYS_CONF, ++ NM_ATT_POWER_CLASS, ++ NM_ATT_POWER_THRESH, ++ NM_ATT_PROB_CAUSE, ++ NM_ATT_RACH_B_THRESH, ++ NM_ATT_LDAVG_SLOTS, ++ NM_ATT_RAD_SUBC, ++ NM_ATT_RF_MAXPOWR_R, ++ NM_ATT_SITE_INPUTS, ++ NM_ATT_SITE_OUTPUTS, ++ NM_ATT_SOURCE, /* 0x30 */ ++ NM_ATT_SPEC_PROB, ++ NM_ATT_START_TIME, ++ NM_ATT_T200, ++ NM_ATT_TEI, ++ NM_ATT_TEST_DUR, ++ NM_ATT_TEST_NO, ++ NM_ATT_TEST_REPORT, ++ NM_ATT_VSWR_THRESH, ++ NM_ATT_WINDOW_SIZE, ++ /* Res */ ++ NM_ATT_BS11_RSSI_OFFS = 0x3d, ++ NM_ATT_BS11_TXPWR = 0x3e, ++ NM_ATT_BS11_DIVERSITY = 0x3f, ++ /* Res */ ++ NM_ATT_TSC = 0x40, ++ NM_ATT_SW_CONFIG, ++ NM_ATT_SW_DESCR, ++ NM_ATT_SEVERITY, ++ NM_ATT_GET_ARI, ++ NM_ATT_HW_CONF_CHG, ++ NM_ATT_OUTST_ALARM, ++ NM_ATT_FILE_DATA, ++ NM_ATT_MEAS_RES, ++ NM_ATT_MEAS_TYPE, ++ ++ NM_ATT_BS11_ESN_FW_CODE_NO = 0x4c, ++ NM_ATT_BS11_ESN_HW_CODE_NO = 0x4f, ++ ++ NM_ATT_BS11_ESN_PCB_SERIAL = 0x55, ++ NM_ATT_BS11_EXCESSIVE_DISTANCE = 0x58, ++ ++ NM_ATT_BS11_ALL_TEST_CATG = 0x60, ++ NM_ATT_BS11_BTSLS_HOPPING, ++ NM_ATT_BS11_CELL_ALLOC_NR, ++ NM_ATT_BS11_CELL_GLOBAL_ID, ++ NM_ATT_BS11_ENA_INTERF_CLASS = 0x66, ++ NM_ATT_BS11_ENA_INT_INTEC_HANDO = 0x67, ++ NM_ATT_BS11_ENA_INT_INTRC_HANDO = 0x68, ++ NM_ATT_BS11_ENA_MS_PWR_CTRL = 0x69, ++ NM_ATT_BS11_ENA_PWR_BDGT_HO = 0x6a, ++ NM_ATT_BS11_ENA_PWR_CTRL_RLFW = 0x6b, ++ NM_ATT_BS11_ENA_RXLEV_HO = 0x6c, ++ NM_ATT_BS11_ENA_RXQUAL_HO = 0x6d, ++ NM_ATT_BS11_FACCH_QUAL = 0x6e, ++ ++ NM_ATT_IPACC_DST_IP = 0x80, ++ NM_ATT_IPACC_DST_IP_PORT = 0x81, ++ NM_ATT_IPACC_SSRC = 0x82, /* RTP Sync Source */ ++ NM_ATT_IPACC_RTP_PAYLD_TYPE = 0x83, ++ NM_ATT_IPACC_BASEB_ID = 0x84, ++ NM_ATT_IPACC_STREAM_ID = 0x85, ++ NM_ATT_IPACC_NV_FLAGS = 0x86, ++ NM_ATT_IPACC_FREQ_CTRL = 0x87, ++ NM_ATT_IPACC_PRIM_OML_CFG = 0x88, ++ NM_ATT_IPACC_SEC_OML_CFG = 0x89, ++ NM_ATT_IPACC_IP_IF_CFG = 0x8a, /* IP interface */ ++ NM_ATT_IPACC_IP_GW_CFG = 0x8b, /* IP gateway */ ++ NM_ATT_IPACC_IN_SERV_TIME = 0x8c, ++ NM_ATT_IPACC_TRX_BTS_ASS = 0x8d, ++ NM_ATT_IPACC_LOCATION = 0x8e, /* string describing location */ ++ NM_ATT_IPACC_PAGING_CFG = 0x8f, ++ NM_ATT_IPACC_FILE_DATA = 0x90, ++ NM_ATT_IPACC_UNIT_ID = 0x91, /* Site/BTS/TRX */ ++ NM_ATT_IPACC_PARENT_UNIT_ID = 0x92, ++ NM_ATT_IPACC_UNIT_NAME = 0x93, /* default: nbts-<mac-as-string> */ ++ NM_ATT_IPACC_SNMP_CFG = 0x94, ++ NM_ATT_IPACC_PRIM_OML_CFG_LIST = 0x95, ++ NM_ATT_IPACC_PRIM_OML_FB_TOUT = 0x96, /* fallback timeout */ ++ NM_ATT_IPACC_CUR_SW_CFG = 0x97, ++ NM_ATT_IPACC_TIMING_BUS = 0x98, ++ NM_ATT_IPACC_CGI = 0x99, /* Cell Global ID */ ++ NM_ATT_IPACC_RAC = 0x9a, ++ NM_ATT_IPACC_OBJ_VERSION = 0x9b, ++ NM_ATT_IPACC_GPRS_PAGING_CFG = 0x9c, ++ NM_ATT_IPACC_NSEI = 0x9d, ++ NM_ATT_IPACC_BVCI = 0x9e, ++ NM_ATT_IPACC_NSVCI = 0x9f, ++ NM_ATT_IPACC_NS_CFG = 0xa0, ++ NM_ATT_IPACC_BSSGP_CFG = 0xa1, ++ NM_ATT_IPACC_NS_LINK_CFG = 0xa2, ++ NM_ATT_IPACC_RLC_CFG = 0xa3, ++ NM_ATT_IPACC_ALM_THRESH_LIST = 0xa4, ++ NM_ATT_IPACC_MONIT_VAL_LIST = 0xa5, ++ NM_ATT_IPACC_TIB_CONTROL = 0xa6, ++ NM_ATT_IPACC_SUPP_FEATURES = 0xa7, ++ NM_ATT_IPACC_CODING_SCHEMES = 0xa8, ++ NM_ATT_IPACC_RLC_CFG_2 = 0xa9, ++ NM_ATT_IPACC_HEARTB_TOUT = 0xaa, ++ NM_ATT_IPACC_UPTIME = 0xab, ++ NM_ATT_IPACC_RLC_CFG_3 = 0xac, ++ NM_ATT_IPACC_SSL_CFG = 0xad, ++ NM_ATT_IPACC_SEC_POSSIBLE = 0xae, ++ NM_ATT_IPACC_IML_SSL_STATE = 0xaf, ++ NM_ATT_IPACC_REVOC_DATE = 0xb0, ++ ++ ++ NM_ATT_BS11_RF_RES_IND_PER = 0x8f, ++ ++ NM_ATT_BS11_RX_LEV_MIN_CELL = 0x90, ++ NM_ATT_BS11_ABIS_EXT_TIME = 0x91, ++ NM_ATT_BS11_TIMER_HO_REQUEST = 0x92, ++ NM_ATT_BS11_TIMER_NCELL = 0x93, ++ NM_ATT_BS11_TSYNC = 0x94, ++ NM_ATT_BS11_TTRAU = 0x95, ++ NM_ATT_BS11_EMRG_CFG_MEMBER = 0x9b, ++ NM_ATT_BS11_TRX_AREA = 0x9f, ++ ++ NM_ATT_BS11_BCCH_RECONF = 0xd7, ++ NM_ATT_BS11_BIT_ERR_THESH = 0xa0, ++ NM_ATT_BS11_BOOT_SW_VERS = 0xa1, ++ NM_ATT_BS11_CCLK_ACCURACY = 0xa3, ++ NM_ATT_BS11_CCLK_TYPE = 0xa4, ++ NM_ATT_BS11_INP_IMPEDANCE = 0xaa, ++ NM_ATT_BS11_L1_PROT_TYPE = 0xab, ++ NM_ATT_BS11_LINE_CFG = 0xac, ++ NM_ATT_BS11_LI_PORT_1 = 0xad, ++ NM_ATT_BS11_LI_PORT_2 = 0xae, ++ ++ NM_ATT_BS11_L1_REM_ALM_TYPE = 0xb0, ++ NM_ATT_BS11_SW_LOAD_INTENDED = 0xbb, ++ NM_ATT_BS11_SW_LOAD_SAFETY = 0xbc, ++ NM_ATT_BS11_SW_LOAD_STORED = 0xbd, ++ ++ NM_ATT_BS11_VENDOR_NAME = 0xc1, ++ NM_ATT_BS11_HOPPING_MODE = 0xc5, ++ NM_ATT_BS11_LMT_LOGON_SESSION = 0xc6, ++ NM_ATT_BS11_LMT_LOGIN_TIME = 0xc7, ++ NM_ATT_BS11_LMT_USER_ACC_LEV = 0xc8, ++ NM_ATT_BS11_LMT_USER_NAME = 0xc9, ++ ++ NM_ATT_BS11_L1_CONTROL_TS = 0xd8, ++ NM_ATT_BS11_RADIO_MEAS_GRAN = 0xdc, /* in SACCH multiframes */ ++ NM_ATT_BS11_RADIO_MEAS_REP = 0xdd, ++ ++ NM_ATT_BS11_SH_LAPD_INT_TIMER = 0xe8, ++ ++ NM_ATT_BS11_BTS_STATE = 0xf0, ++ NM_ATT_BS11_E1_STATE = 0xf1, ++ NM_ATT_BS11_PLL = 0xf2, ++ NM_ATT_BS11_RX_OFFSET = 0xf3, ++ NM_ATT_BS11_ANT_TYPE = 0xf4, ++ NM_ATT_BS11_PLL_MODE = 0xfc, ++ NM_ATT_BS11_PASSWORD = 0xfd, ++}; ++#define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE ++ ++/* Section 9.4.4: Administrative State */ ++enum abis_nm_adm_state { ++ NM_STATE_LOCKED = 0x01, ++ NM_STATE_UNLOCKED = 0x02, ++ NM_STATE_SHUTDOWN = 0x03, ++ NM_STATE_NULL = 0xff, ++}; ++ ++/* Section 9.4.13: Channel Combination */ ++enum abis_nm_chan_comb { ++ NM_CHANC_TCHFull = 0x00, ++ NM_CHANC_TCHHalf = 0x01, ++ NM_CHANC_TCHHalf2 = 0x02, ++ NM_CHANC_SDCCH = 0x03, ++ NM_CHANC_mainBCCH = 0x04, ++ NM_CHANC_BCCHComb = 0x05, ++ NM_CHANC_BCCH = 0x06, ++ NM_CHANC_BCCH_CBCH = 0x07, ++ NM_CHANC_SDCCH_CBCH = 0x08, ++}; ++ ++/* Section 9.4.16: Event Type */ ++enum abis_nm_event_type { ++ NM_EVT_COMM_FAIL = 0x00, ++ NM_EVT_QOS_FAIL = 0x01, ++ NM_EVT_PROC_FAIL = 0x02, ++ NM_EVT_EQUIP_FAIL = 0x03, ++ NM_EVT_ENV_FAIL = 0x04, ++}; ++ ++/* Section: 9.4.63: Perceived Severity */ ++enum abis_nm_severity { ++ NM_SEVER_CEASED = 0x00, ++ NM_SEVER_CRITICAL = 0x01, ++ NM_SEVER_MAJOR = 0x02, ++ NM_SEVER_MINOR = 0x03, ++ NM_SEVER_WARNING = 0x04, ++ NM_SEVER_INDETERMINATE = 0x05, ++}; ++ ++/* Section 9.4.43: Probable Cause Type */ ++enum abis_nm_pcause_type { ++ NM_PCAUSE_T_X721 = 0x01, ++ NM_PCAUSE_T_GSM = 0x02, ++ NM_PCAUSE_T_MANUF = 0x03, ++}; ++ ++/* Section 9.4.36: NACK Causes */ ++enum abis_nm_nack_cause { ++ /* General Nack Causes */ ++ NM_NACK_INCORR_STRUCT = 0x01, ++ NM_NACK_MSGTYPE_INVAL = 0x02, ++ NM_NACK_OBJCLASS_INVAL = 0x05, ++ NM_NACK_OBJCLASS_NOTSUPP = 0x06, ++ NM_NACK_BTSNR_UNKN = 0x07, ++ NM_NACK_TRXNR_UNKN = 0x08, ++ NM_NACK_OBJINST_UNKN = 0x09, ++ NM_NACK_ATTRID_INVAL = 0x0c, ++ NM_NACK_ATTRID_NOTSUPP = 0x0d, ++ NM_NACK_PARAM_RANGE = 0x0e, ++ NM_NACK_ATTRLIST_INCONSISTENT = 0x0f, ++ NM_NACK_SPEC_IMPL_NOTSUPP = 0x10, ++ NM_NACK_CANT_PERFORM = 0x11, ++ /* Specific Nack Causes */ ++ NM_NACK_RES_NOTIMPL = 0x19, ++ NM_NACK_RES_NOTAVAIL = 0x1a, ++ NM_NACK_FREQ_NOTAVAIL = 0x1b, ++ NM_NACK_TEST_NOTSUPP = 0x1c, ++ NM_NACK_CAPACITY_RESTR = 0x1d, ++ NM_NACK_PHYSCFG_NOTPERFORM = 0x1e, ++ NM_NACK_TEST_NOTINIT = 0x1f, ++ NM_NACK_PHYSCFG_NOTRESTORE = 0x20, ++ NM_NACK_TEST_NOSUCH = 0x21, ++ NM_NACK_TEST_NOSTOP = 0x22, ++ NM_NACK_MSGINCONSIST_PHYSCFG = 0x23, ++ NM_NACK_FILE_INCOMPLETE = 0x25, ++ NM_NACK_FILE_NOTAVAIL = 0x26, ++ NM_NACK_FILE_NOTACTIVATE = 0x27, ++ NM_NACK_REQ_NOT_GRANT = 0x28, ++ NM_NACK_WAIT = 0x29, ++ NM_NACK_NOTH_REPORT_EXIST = 0x2a, ++ NM_NACK_MEAS_NOTSUPP = 0x2b, ++ NM_NACK_MEAS_NOTSTART = 0x2c, ++}; ++ ++/* Section 9.4.1 */ ++struct abis_nm_channel { ++ guint8 attrib; ++ guint8 bts_port; ++ guint8 timeslot; ++ guint8 subslot; ++} __attribute__ ((packed)); ++ ++/* Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */ ++enum abis_bs11_objtype { ++ BS11_OBJ_ALCO = 0x01, ++ BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */ ++ BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */ ++ BS11_OBJ_CCLK = 0x04, ++ BS11_OBJ_GPSU = 0x06, ++ BS11_OBJ_LI = 0x07, ++ BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/ ++}; ++ ++enum abis_bs11_trx_power { ++ BS11_TRX_POWER_GSM_2W = 0x06, ++ BS11_TRX_POWER_GSM_250mW= 0x07, ++ BS11_TRX_POWER_GSM_80mW = 0x08, ++ BS11_TRX_POWER_GSM_30mW = 0x09, ++ BS11_TRX_POWER_DCS_3W = 0x0a, ++ BS11_TRX_POWER_DCS_1W6 = 0x0b, ++ BS11_TRX_POWER_DCS_500mW= 0x0c, ++ BS11_TRX_POWER_DCS_160mW= 0x0d, ++}; ++ ++enum abis_bs11_li_pll_mode { ++ BS11_LI_PLL_LOCKED = 2, ++ BS11_LI_PLL_STANDALONE = 3, ++}; ++ ++enum abis_bs11_phase { ++ BS11_STATE_SOFTWARE_RQD = 0x01, ++ BS11_STATE_LOAD_SMU_INTENDED = 0x11, ++ BS11_STATE_LOAD_SMU_SAFETY = 0x21, ++ BS11_STATE_LOAD_FAILED = 0x31, ++ BS11_STATE_LOAD_DIAGNOSTIC = 0x41, ++ BS11_STATE_WARM_UP = 0x51, ++ BS11_STATE_WARM_UP_2 = 0x52, ++ BS11_STATE_WAIT_MIN_CFG = 0x62, ++ BS11_STATE_MAINTENANCE = 0x72, ++ BS11_STATE_LOAD_MBCCU = 0x92, ++ BS11_STATE_WAIT_MIN_CFG_2 = 0xA2, ++ BS11_STATE_NORMAL = 0x03, ++ BS11_STATE_ABIS_LOAD = 0x13, ++}; ++ ++/* From openbsc/include/openbsc/tlv.h */ ++enum tlv_type { ++ TLV_TYPE_FIXED, ++ TLV_TYPE_T, ++ TLV_TYPE_TV, ++ TLV_TYPE_TLV, ++ TLV_TYPE_TL16V, ++}; ++ ++struct tlv_def { ++ enum tlv_type type; ++ u_int8_t fixed_len; ++}; ++ ++struct tlv_definition { ++ struct tlv_def def[0xff]; ++}; ++ ++enum abis_nm_ipacc_test_no { ++ NM_IPACC_TESTNO_RLOOP_ANT = 0x01, ++ NM_IPACC_TESTNO_RLOOP_XCVR = 0x02, ++ NM_IPACC_TESTNO_FUNC_OBJ = 0x03, ++ NM_IPACC_TESTNO_CHAN_USAGE = 0x40, ++ NM_IPACC_TESTNO_BCCH_CHAN_USAGE = 0x41, ++ NM_IPACC_TESTNO_FREQ_SYNC = 0x42, ++ NM_IPACC_TESTNO_BCCH_INFO = 0x43, ++ NM_IPACC_TESTNO_TX_BEACON = 0x44, ++ NM_IPACC_TESTNO_SYSINFO_MONITOR = 0x45, ++ NM_IPACC_TESTNO_BCCCH_MONITOR = 0x46, ++}; ++ ++/* first byte after length inside NM_ATT_TEST_REPORT */ ++enum abis_nm_ipacc_test_res { ++ NM_IPACC_TESTRES_SUCCESS = 0, ++ NM_IPACC_TESTRES_TIMEOUT = 1, ++ NM_IPACC_TESTRES_NO_CHANS = 2, ++ NM_IPACC_TESTRES_PARTIAL = 3, ++ NM_IPACC_TESTRES_STOPPED = 4, ++}; ++ ++/* internal IE inside NM_ATT_TEST_REPORT */ ++enum abis_nm_ipacc_testres_ie { ++ NM_IPACC_TR_IE_FREQ_ERR_LIST = 3, ++ NM_IPACC_TR_IE_CHAN_USAGE = 4, ++ NM_IPACC_TR_IE_BCCH_INFO = 6, ++ NM_IPACC_TR_IE_RESULT_DETAILS = 8, ++ NM_IPACC_TR_IE_FREQ_ERR = 18, ++}; ++ ++/* From openbsc/src/abis_nm.c */ ++static const struct tlv_definition nm_att_tlvdef = { ++ .def = { ++ [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, ++ [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_ADM_STATE] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_BSIC] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_DEST] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_HSN] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V, 0 }, ++ //BS11 [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_HW_DESC] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 }, ++ [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_MAIO] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_MAX_TA] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_NY1] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_OPER_STATE] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 }, ++ [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 }, ++ [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_SOURCE] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 }, ++ [NM_ATT_TEI] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_TEST_NO] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_TSC] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_SEVERITY] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V, 0 }, ++#if 0 ++ /* BS11 specifics */ ++ [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV, 0 }, ++ [0xd5] = { TLV_TYPE_TLV, 0 }, ++ [0xa8] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV, 0 }, ++ [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV, 0 }, ++ [0x95] = { TLV_TYPE_FIXED, 2 }, ++#endif ++ /* ip.access specifics */ ++ [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, ++ [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_IPACC_PRIM_OML_CFG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, ++ [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, ++ [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, ++ [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, ++ [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, 0 }, ++ [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_GPRS_PAGING_CFG] ={ TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V, 0 }, ++ [NM_ATT_IPACC_FILE_DATA] = { TLV_TYPE_TL16V, 0 }, ++ }, ++}; ++ ++#endif /* _NM_H */ diff --git a/wireshark/lucent-hnb.patch b/wireshark/lucent-hnb.patch new file mode 100644 index 000000000..4c85195ea --- /dev/null +++ b/wireshark/lucent-hnb.patch @@ -0,0 +1,120 @@ +Index: wireshark/epan/dissectors/packet-lucent_hnb.c +=================================================================== +--- /dev/null ++++ wireshark/epan/dissectors/packet-lucent_hnb.c +@@ -0,0 +1,103 @@ ++/* packet-lucent_hnb.c ++ * Routines for packet dissection of Alcatel/Lucent HomeNodeB ++ * Copyright 2009 by Harald Welte <laforge@gnumonks.org> ++ * ++ * This protocol decoder is based entirely on reverse engineering, i.e. ++ * on educated guesses. ++ * ++ * $Id: packet-lucent_hnb.c 29254 2009-07-31 19:19:25Z gerald $ ++ * ++ * Wireshark - Network traffic analyzer ++ * By Gerald Combs <gerald@wireshark.org> ++ * Copyright 1998 Gerald Combs ++ * ++ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#define LHNB_SCTP_PPI_MM 1 ++#define LHNB_SCTP_PPI_GMM 6 ++ ++#define LHNB_SCTP_PORT 6005 ++ ++#include <glib.h> ++ ++#include <epan/packet.h> ++ ++/* Initialize the protocol and registered fields */ ++static int proto_lhnb = -1; ++ ++static int hf_lhnb_length = -1; ++ ++/* Initialize the subtree pointers */ ++static gint ett_lhnb = -1; ++ ++static dissector_handle_t ranap_handle; ++ ++/* Code to actually dissect the packets */ ++static void ++dissect_lhnb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) ++{ ++ ++ int offset = 0; ++ u_int16_t len; ++ tvbuff_t *next_tvb; ++ ++ col_set_str(pinfo->cinfo, COL_PROTOCOL, "LHNB"); ++ col_clear(pinfo->cinfo, COL_INFO); ++ ++ proto_tree_add_item(tree, hf_lhnb_length, tvb, offset+2, 2, FALSE); ++ len = tvb_get_ntohs(tvb, offset+2); ++ next_tvb = tvb_new_subset(tvb, offset+2+6, len-4, -1); ++ ++ call_dissector(ranap_handle, next_tvb, pinfo, tree); ++} ++ ++void proto_register_lucent_hnb(void) ++{ ++ static hf_register_info hf[] = { ++ {&hf_lhnb_length, ++ {"Length", "lhnb.len", ++ FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL} ++ }, ++ }; ++ ++ static gint *ett[] = { ++ &ett_lhnb, ++ }; ++ ++ proto_lhnb = ++ proto_register_protocol("Alcatel/Lucent HomeNodeB", ++ "Lucent HNB", "lhnb"); ++ ++ proto_register_field_array(proto_lhnb, hf, array_length(hf)); ++ proto_register_subtree_array(ett, array_length(ett)); ++} ++ ++void proto_reg_handoff_lucent_hnb(void) ++{ ++ dissector_handle_t lhnb_handle; ++ ++ ranap_handle = find_dissector("ranap"); ++ ++ lhnb_handle = create_dissector_handle(dissect_lhnb, proto_lhnb); ++ ++ dissector_add("sctp.ppi", LHNB_SCTP_PPI_MM, lhnb_handle); ++ dissector_add("sctp.ppi", LHNB_SCTP_PPI_GMM, lhnb_handle); ++ dissector_add("sctp.port", LHNB_SCTP_PORT, lhnb_handle); ++} +Index: wireshark/epan/dissectors/Makefile.common +=================================================================== +--- wireshark.orig/epan/dissectors/Makefile.common ++++ wireshark/epan/dissectors/Makefile.common +@@ -605,6 +605,7 @@ + packet-loop.c \ + packet-lpd.c \ + packet-lsc.c \ ++ packet-lucent_hnb.c \ + packet-lwapp.c \ + packet-lwres.c \ + packet-m2pa.c \ diff --git a/wireshark/rsl-ipaccess.patch b/wireshark/rsl-ipaccess.patch new file mode 100644 index 000000000..36c09c57b --- /dev/null +++ b/wireshark/rsl-ipaccess.patch @@ -0,0 +1,652 @@ +Index: wireshark/epan/dissectors/packet-rsl.c +=================================================================== +--- wireshark.orig/epan/dissectors/packet-rsl.c 2009-10-21 23:03:41.000000000 +0200 ++++ wireshark/epan/dissectors/packet-rsl.c 2009-10-22 10:02:51.000000000 +0200 +@@ -2,6 +2,7 @@ + * Routines for Radio Signalling Link (RSL) dissection. + * + * Copyright 2007, Anders Broman <anders.broman@ericsson.com> ++ * Copyright 2009, Harald Welte <laforge@gnumonks.org> + * + * $Id: packet-rsl.c 29944 2009-09-16 13:39:37Z morriss $ + * +@@ -44,6 +45,8 @@ + #include <epan/lapd_sapi.h> + + #include "packet-gsm_a_common.h" ++#include "packet-rtp.h" ++#include "packet-rtcp.h" + + /* Initialize the protocol and registered fields */ + static int proto_rsl = -1; +@@ -117,6 +120,24 @@ + static int hf_rsl_rtd = -1; + static int hf_rsl_delay_ind = -1; + static int hf_rsl_tfo = -1; ++static int hf_rsl_speech_mode_s = -1; ++static int hf_rsl_speech_mode_m = -1; ++static int hf_rsl_conn_stat = -1; ++static int hf_rsl_conn_id = -1; ++static int hf_rsl_rtp_payload = -1; ++static int hf_rsl_rtp_csd_fmt_d = -1; ++static int hf_rsl_rtp_csd_fmt_ir = -1; ++static int hf_rsl_local_port = -1; ++static int hf_rsl_remote_port = -1; ++static int hf_rsl_local_ip = -1; ++static int hf_rsl_remote_ip = -1; ++static int hf_rsl_cstat_tx_pkts = -1; ++static int hf_rsl_cstat_tx_octs = -1; ++static int hf_rsl_cstat_rx_pkts = -1; ++static int hf_rsl_cstat_rx_octs = -1; ++static int hf_rsl_cstat_lost_pkts = -1; ++static int hf_rsl_cstat_ia_jitter = -1; ++static int hf_rsl_cstat_avg_tx_dly = -1; + + /* Initialize the subtree pointers */ + static int ett_rsl = -1; +@@ -174,6 +195,15 @@ + static int ett_ie_meas_res_no = -1; + static int ett_ie_message_id = -1; + static int ett_ie_sys_info_type = -1; ++static int ett_ie_speech_mode = -1; ++static int ett_ie_conn_stat = -1; ++static int ett_ie_conn_id = -1; ++static int ett_ie_remote_ip = -1; ++static int ett_ie_remote_port = -1; ++static int ett_ie_local_port = -1; ++static int ett_ie_local_ip = -1; ++static int ett_ie_rtp_payload = -1; ++ + + proto_tree *top_tree; + dissector_handle_t gsm_a_ccch_handle; +@@ -209,8 +239,11 @@ + { 0x06, "Common Channel Management messages" }, + { 0x08, "TRX Management messages" }, + { 0x16, "Location Services messages" }, ++ { 0x3f, "ip.access Vendor Specific messages" }, + { 0, NULL } + }; ++#define RSL_MSGDISC_IPACCESS 0x3f ++ + /* + * 9.2 MESSAGE TYPE + */ +@@ -277,6 +310,49 @@ + /* 0 1 - - - - - - Location Services messages: */ + #define RSL_MSG_LOC_INF 65 /* 8.7.1 */ + ++/* Vendor-Specific messages of ip.access nanoBTS. There is no public documentation ++ * about those extensions, all information in this dissector is based on lawful ++ * protocol reverse enginering by Harald Welte <laforge@gnumonks.org> */ ++#define RSL_MSG_TYPE_IPAC_MEAS_PP_DEF 0x60 ++#define RSL_MSG_TYPE_IPAC_HO_CAND_INQ 0x61 ++#define RSL_MSG_TYPE_IPAC_HO_CAND_RESP 0x62 ++ ++#define RSL_MSG_TYPE_IPAC_PDCH_ACT 0x48 ++#define RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK 0x49 ++#define RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK 0x4a ++#define RSL_MSG_TYPE_IPAC_PDCH_DEACT 0x4b ++#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK 0x4c ++#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK 0x4d ++ ++#define RSL_MSG_TYPE_IPAC_BIND 0x70 ++#define RSL_MSG_TYPE_IPAC_BIND_ACK 0x71 ++#define RSL_MSG_TYPE_IPAC_BIND_NACK 0x72 ++#define RSL_MSG_TYPE_IPAC_CONNECT 0x73 ++#define RSL_MSG_TYPE_IPAC_CONNECT_ACK 0x74 ++#define RSL_MSG_TYPE_IPAC_CONNECT_NACK 0x75 ++#define RSL_MSG_TYPE_IPAC_DISC_IND 0x76 ++#define RSL_MSG_TYPE_IPAC_DISC 0x77 ++#define RSL_MSG_TYPE_IPAC_DISC_ACK 0x78 ++#define RSL_MSG_TYPE_IPAC_DISC_NACK 0x79 ++ ++#define RSL_IE_IPAC_SRTP_CONFIG 0xe0 ++#define RSL_IE_IPAC_PROXY_UDP 0xe1 ++#define RSL_IE_IPAC_BSCMPL_TOUT 0xe2 ++#define RSL_IE_IPAC_REMOTE_IP 0xf0 ++#define RSL_IE_IPAC_REMOTE_PORT 0xf1 ++#define RSL_IE_IPAC_RTP_PAYLOAD 0xf2 ++#define RSL_IE_IPAC_LOCAL_PORT 0xf3 ++#define RSL_IE_IPAC_SPEECH_MODE 0xf4 ++#define RSL_IE_IPAC_LOCAL_IP 0xf5 ++#define RSL_IE_IPAC_CONN_STAT 0xf6 ++#define RSL_IE_IPAC_HO_C_PARMS 0xf7 ++#define RSL_IE_IPAC_CONN_ID 0xf8 ++#define RSL_IE_IPAC_RTP_CSD_FMT 0xf9 ++#define RSL_IE_IPAC_RTP_JIT_BUF 0xfa ++#define RSL_IE_IPAC_RTP_COMPR 0xfb ++#define RSL_IE_IPAC_RTP_PAYLOAD2 0xfc ++#define RSL_IE_IPAC_RTP_MPLEX 0xfd ++#define RSL_IE_IPAC_RTP_MPLEX_ID 0xfe + + static const value_string rsl_msg_type_vals[] = { + /* 0 0 0 0 - - - - Radio Link Layer Management messages: */ +@@ -339,6 +415,26 @@ + { 0x3f, "TFO MODification REQuest" }, /* 8.4.31 */ + /* 0 1 - - - - - - Location Services messages: */ + { 0x41, "Location Information" }, /* 8.7.1 */ ++ /* ip.access */ ++ { 0x48, "ip.access PDCH ACTIVATION" }, ++ { 0x49, "ip.access PDCH ACTIVATION ACK" }, ++ { 0x4a, "ip.access PDCH ACTIVATION NACK" }, ++ { 0x4b, "ip.access PDCH DEACTIVATION" }, ++ { 0x4c, "ip.access PDCH DEACTIVATION ACK" }, ++ { 0x4d, "ip.access PDCH DEACTIVATION NACK" }, ++ { 0x60, "ip.access MEASurement PREPROCessing DeFauLT" }, ++ { 0x61, "ip.access HANDOover CANDidate ENQuiry" }, ++ { 0x62, "ip.access HANDOover CANDidate RESPonse" }, ++ { 0x70, "ip.access CRCX" }, ++ { 0x71, "ip.access CRCX ACK" }, ++ { 0x72, "ip.access CRCX NACK" }, ++ { 0x73, "ip.access MDCX" }, ++ { 0x74, "ip.access MDCX ACK" }, ++ { 0x75, "ip.access MDCX NACK" }, ++ { 0x76, "ip.access DLCX INDication" }, ++ { 0x77, "ip.access DLCX" }, ++ { 0x78, "ip.access DLCX ACK" }, ++ { 0x79, "ip.access DLCX NACK" }, + { 0, NULL } + }; + +@@ -372,10 +468,10 @@ static const value_string rsl_msg_type_vals[] = { + #define RSL_IE_MESSAGE_ID 28 + + #define RSL_IE_SYS_INFO_TYPE 30 +- +- +- +- ++#define RSL_IE_MS_POWER_PARAM 31 ++#define RSL_IE_BS_POWER_PARAM 32 ++#define RSL_IE_PREPROC_PARAM 33 ++#define RSL_IE_PREPROC_MEAS 34 + #define RSL_IE_FULL_IMM_ASS_INF 35 + #define RSL_IE_SMSCB_INF 36 + #define RSL_IE_FULL_MS_TIMING_OFFSET 37 +@@ -478,6 +574,24 @@ + Not used + + */ ++ { 0xe0, "SRTP Configuration" }, ++ { 0xe1, "BSC Proxy UDP Port" }, ++ { 0xe2, "BSC Multiplex Timeout" }, ++ { 0xf0, "Remote IP Address" }, ++ { 0xf1, "Remote RTP Port" }, ++ { 0xf2, "RTP Payload Type" }, ++ { 0xf3, "Local RTP Port" }, ++ { 0xf4, "Speech Mode" }, ++ { 0xf5, "Local IP Address" }, ++ { 0xf6, "Connection Statistics" }, ++ { 0xf7, "Handover C Parameters" }, ++ { 0xf8, "Connection Identifier" }, ++ { 0xf9, "RTP CSD Format" }, ++ { 0xfa, "RTP Jitter Buffer" }, ++ { 0xfb, "RTP Compression" }, ++ { 0xfc, "RTP Payload Type 2" }, ++ { 0xfd, "RTP Multiplex" }, ++ { 0xfe, "RTP Multiplex Identifier" }, + { 0, NULL } + }; + +@@ -514,6 +628,96 @@ + { 0, NULL } + }; + ++/* From openbsc/include/openbsc/tlv.h */ ++enum tlv_type { ++ TLV_TYPE_FIXED, ++ TLV_TYPE_T, ++ TLV_TYPE_TV, ++ TLV_TYPE_TLV, ++ TLV_TYPE_TL16V, ++}; ++ ++struct tlv_def { ++ enum tlv_type type; ++ u_int8_t fixed_len; ++}; ++ ++struct tlv_definition { ++ struct tlv_def def[0xff]; ++}; ++ ++static const struct tlv_definition rsl_att_tlvdef = { ++ .def = { ++ [RSL_IE_CH_NO] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_LINK_ID] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_ACT_TYPE] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_BS_POW] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_CH_ID] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_CH_MODE] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_ENC_INF] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_FRAME_NO] = { TLV_TYPE_FIXED, 2 }, ++ [RSL_IE_HO_REF] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_L1_INF] = { TLV_TYPE_FIXED, 2 }, ++ [RSL_IE_L3_INF] = { TLV_TYPE_TL16V, 0 }, ++ [RSL_IE_MS_ID] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_MS_POW] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_PAGING_GRP] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_PAGING_LOAD] = { TLV_TYPE_FIXED, 2 }, ++ [RSL_IE_PHY_CTX] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_ACCESS_DELAY] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_RACH_LOAD] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_REQ_REF] = { TLV_TYPE_FIXED, 3 }, ++ [RSL_IE_REL_MODE] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_RESOURCE_INF] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_RLM_CAUSE] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_STARTING_TIME] = { TLV_TYPE_FIXED, 2 }, ++ [RSL_IE_TIMING_ADV] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_UPLINK_MEAS] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_CAUSE] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_MEAS_RES_NO] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_MESSAGE_ID] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_SYS_INFO_TYPE] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_MS_POWER_PARAM] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_BS_POWER_PARAM] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_PREPROC_PARAM] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_PREPROC_MEAS] = { TLV_TYPE_TLV, 0 }, ++ //[RSL_IE_IMM_ASS_INFO] = { TLV_TYPE_TLV, 0 }, ++ //[RSL_IE_SMSCB_INFO] = { TLV_TYPE_FIXED, 23 }, ++ //[RSL_IE_MS_TIMING_OFFSET] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_ERR_MSG] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_FULL_BCCH_INF] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_CH_NEEDED] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_CB_CMD_TYPE] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_SMSCB_MESS] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_FULL_IMM_ASS_INF] = { TLV_TYPE_TLV, 0 }, ++ //[RSL_IE_SACCH_INFO] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_CBCH_LOAD_INF] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_SMSCB_CH_IND] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_GRP_CALL_REF] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_CH_DESC] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_NCH_DRX_INF] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_CMD_IND] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_EMLPP_PRIO] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_UIC] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_MAIN_CH_REF] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_MULTIRATE_CONF] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_MULTIRATE_CNTRL] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_SUP_CODEC_TYPES] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_CODEC_CONF] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_RTD] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_TFO_STATUS] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_LLP_APDU] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 }, ++ [RSL_IE_IPAC_REMOTE_PORT] = { TLV_TYPE_FIXED, 2 }, ++ [RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 }, ++ [RSL_IE_IPAC_CONN_STAT] = { TLV_TYPE_TLV, 0 }, ++ [RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 }, ++ [RSL_IE_IPAC_SPEECH_MODE] = { TLV_TYPE_TV, 0 }, ++ [RSL_IE_IPAC_CONN_ID] = { TLV_TYPE_FIXED, 2 }, ++ [RSL_IE_IPAC_RTP_PAYLOAD2] = { TLV_TYPE_TV, 0 }, ++ }, ++}; ++ + /* 9.3.1 Channel number 9.3.1 M TV 2 */ + static int + dissect_rsl_ie_ch_no(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory) +@@ -2044,7 +2248,6 @@ + proto_item_set_len(ti, length+2); + + proto_tree_add_item(ie_tree, hf_rsl_ie_length, tvb, offset, 1, FALSE); +- offset++; + + /* Received Message */ + offset = dissct_rsl_msg(tvb, pinfo, ie_tree, offset); +@@ -2909,12 +3112,183 @@ + } + + static int ++dissct_rsl_ipaccess_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) ++{ ++ guint8 msg_type; ++ guint32 local_addr = 0; ++ guint16 local_port = 0; ++ address src_addr; ++ ++ msg_type = tvb_get_guint8(tvb, offset)&0x7f; ++ offset++; ++ ++#if 0 ++ switch (msg_type) { ++ case RSL_MSG_TYPE_IPAC_BIND: ++ case RSL_MSG_TYPE_IPAC_BIND_ACK: ++ case RSL_MSG_TYPE_IPAC_BIND_NACK: ++ case RSL_MSG_TYPE_IPAC_CONNECT: ++ case RSL_MSG_TYPE_IPAC_CONNECT_ACK: ++ case RSL_MSG_TYPE_IPAC_CONNECT_NACK: ++ case RSL_MSG_TYPE_IPAC_DISC_IND: ++ case RSL_MSG_TYPE_IPAC_DISC: ++ case RSL_MSG_TYPE_IPAC_DISC_ACK: ++ case RSL_MSG_TYPE_IPAC_DISC_NACK: ++ case RSL_MSG_TYPE_IPAC_PDCH_ACT: ++ case RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK: ++ case RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK: ++ case RSL_MSG_TYPE_IPAC_PDCH_DEACT: ++ case RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK: ++ case RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK: ++ /* Channel number 9.3.1 M TV 2 */ ++ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE); ++ break; ++ } ++#endif ++ /* parse TLV attributes */ ++ while (tvb_reported_length_remaining(tvb, offset) != 0) { ++ guint8 tag; ++ unsigned int len, hlen, len_len; ++ const struct tlv_def *tdef; ++ proto_item *ti; ++ proto_tree *ie_tree; ++ ++ tag = tvb_get_guint8(tvb, offset); ++ tdef = &rsl_att_tlvdef.def[tag]; ++ ++ switch (tdef->type) { ++ case TLV_TYPE_FIXED: ++ hlen = 1; ++ len_len = 0; ++ len = tdef->fixed_len; ++ break; ++ case TLV_TYPE_T: ++ hlen = 1; ++ len_len = 0; ++ len = 0; ++ break; ++ case TLV_TYPE_TV: ++ hlen = 1; ++ len_len = 0; ++ len = 1; ++ break; ++ case TLV_TYPE_TLV: ++ hlen = 2; ++ len_len = 1; ++ len = tvb_get_guint8(tvb, offset+1); ++ break; ++ case TLV_TYPE_TL16V: ++ hlen = 3; ++ len_len = 2; ++ len = tvb_get_guint8(tvb, offset+1) << 8 | ++ tvb_get_guint8(tvb, offset+2); ++ break; ++ default: ++ hlen = len_len = len = 0; ++ DISSECTOR_ASSERT_NOT_REACHED(); ++ break; ++ } ++ ++ ti = proto_tree_add_item(tree, hf_rsl_ie_id, tvb, offset, 1, FALSE); ++ ie_tree = proto_item_add_subtree(ti, ett_ie_local_port); ++ offset += hlen; ++ ++ switch (tag) { ++ case RSL_IE_CH_NO: ++ dissect_rsl_ie_ch_no(tvb, pinfo, ie_tree, offset, FALSE); ++ break; ++ case RSL_IE_FRAME_NO: ++ dissect_rsl_ie_frame_no(tvb, pinfo, ie_tree, offset, FALSE); ++ break; ++ case RSL_IE_MS_POW: ++ dissect_rsl_ie_ms_pow(tvb, pinfo, ie_tree, offset, FALSE); ++ break; ++ case RSL_IE_IPAC_REMOTE_IP: ++ proto_tree_add_item(ie_tree, hf_rsl_remote_ip, tvb, ++ offset, len, FALSE); ++ break; ++ case RSL_IE_IPAC_REMOTE_PORT: ++ proto_tree_add_item(ie_tree, hf_rsl_remote_port, tvb, ++ offset, len, FALSE); ++ break; ++ case RSL_IE_IPAC_LOCAL_IP: ++ proto_tree_add_item(ie_tree, hf_rsl_local_ip, tvb, ++ offset, len, FALSE); ++ local_addr = tvb_get_ipv4(tvb, offset); ++ break; ++ case RSL_IE_IPAC_LOCAL_PORT: ++ proto_tree_add_item(ie_tree, hf_rsl_local_port, tvb, ++ offset, len, FALSE); ++ local_port = tvb_get_ntohs(tvb, offset); ++ break; ++ case RSL_IE_IPAC_SPEECH_MODE: ++ proto_tree_add_item(ie_tree, hf_rsl_speech_mode_s, tvb, ++ offset, len, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_speech_mode_m, tvb, ++ offset, len, FALSE); ++ break; ++ case RSL_IE_IPAC_RTP_PAYLOAD: ++ case RSL_IE_IPAC_RTP_PAYLOAD2: ++ proto_tree_add_item(ie_tree, hf_rsl_rtp_payload, tvb, ++ offset, len, FALSE); ++ break; ++ case RSL_IE_IPAC_RTP_CSD_FMT: ++ proto_tree_add_item(ie_tree, hf_rsl_rtp_csd_fmt_d, tvb, ++ offset, len, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_rtp_csd_fmt_ir, tvb, ++ offset, len, FALSE); ++ break; ++ case RSL_IE_IPAC_CONN_ID: ++ proto_tree_add_item(ie_tree, hf_rsl_conn_id, tvb, ++ offset, len, FALSE); ++ break; ++ case RSL_IE_IPAC_CONN_STAT: ++ proto_tree_add_item(ie_tree, hf_rsl_cstat_tx_pkts, tvb, ++ offset, 4, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_cstat_tx_octs, tvb, ++ offset+4, 4, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_cstat_rx_pkts, tvb, ++ offset+8, 4, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_cstat_rx_octs, tvb, ++ offset+12, 4, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_cstat_lost_pkts, tvb, ++ offset+16, 4, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_cstat_ia_jitter, tvb, ++ offset+20, 4, FALSE); ++ proto_tree_add_item(ie_tree, hf_rsl_cstat_avg_tx_dly, tvb, ++ offset+24, 4, FALSE); ++ break; ++ } ++ offset += len; ++ } ++ ++ switch (msg_type) { ++ case RSL_MSG_TYPE_IPAC_BIND_ACK: ++ /* Notify the RTP and RTCP dissectors about a new RTP stream */ ++ src_addr.type = AT_IPv4; ++ src_addr.len = 4; ++ src_addr.data = (guint8 *)&local_addr; ++ rtp_add_address(pinfo, &src_addr, local_port, 0, ++ "GSM A-bis/IP", pinfo->fd->num, 0, NULL); ++ rtcp_add_address(pinfo, &src_addr, local_port+1, 0, ++ "GSM A-bis/IP", pinfo->fd->num); ++ break; ++ } ++ return offset; ++} ++ ++static int + dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) + { +- guint8 msg_type; ++ guint8 msg_disc, msg_type; + ++ msg_disc = tvb_get_guint8(tvb, offset++) >> 1; + msg_type = tvb_get_guint8(tvb,offset)&0x7f; + proto_tree_add_item(tree, hf_rsl_msg_type, tvb, offset, 1, FALSE); ++ ++ if (msg_disc == RSL_MSGDISC_IPACCESS) { ++ offset = dissct_rsl_ipaccess_msg(tvb, pinfo, tree, offset); ++ return offset; ++ } + offset++; + + switch (msg_type){ +@@ -3482,6 +3856,18 @@ + /* LLP APDU 9.3.58 M LV 2-N */ + offset = dissect_rsl_ie_llp_apdu(tvb, pinfo, tree, offset, TRUE); + break; ++ /* the following messages are ip.access specific but sent without ++ * ip.access memssage discriminator */ ++ case RSL_MSG_TYPE_IPAC_MEAS_PP_DEF: ++ case RSL_MSG_TYPE_IPAC_HO_CAND_INQ: ++ case RSL_MSG_TYPE_IPAC_HO_CAND_RESP: ++ case RSL_MSG_TYPE_IPAC_PDCH_ACT: ++ case RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK: ++ case RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK: ++ case RSL_MSG_TYPE_IPAC_PDCH_DEACT: ++ case RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK: ++ case RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK: ++ offset = dissct_rsl_ipaccess_msg(tvb, pinfo, tree, offset-1); + default: + break; + } +@@ -3489,6 +3875,40 @@ + return offset; + + } ++ ++static const value_string rsl_ipacc_spm_s_vals[] = { ++ { 0, "GSM FR codec (GSM type 1, FS)" }, ++ { 1, "GSM EFR codec (GSM type 2, FS)" }, ++ { 2, "GSM AMR/FR codec (GSM type 3, FS)" }, ++ { 3, "GSM HR codec (GSM type 1, HS)" }, ++ { 5, "GSM AMR/HR codec (GSM type 3, HS)" }, ++ { 0xf, "As specified by RTP Payload Type IE" }, ++ { 0, NULL } ++}; ++ ++static const value_string rsl_ipacc_spm_m_vals[] = { ++ { 0, "Send and Receive" }, ++ { 1, "Receive Only" }, ++ { 2, "Send Only" }, ++ { 0, NULL } ++}; ++ ++static const value_string rsl_ipacc_rtp_csd_fmt_d_vals[] = { ++ { 0, "External TRAU format" }, ++ { 1, "Non-TRAU Packed format" }, ++ { 2, "TRAU within the BTS" }, ++ { 3, "IWF-Free BTS-BTS Data" }, ++ { 0, NULL } ++}; ++ ++static const value_string rsl_ipacc_rtp_csd_fmt_ir_vals[] = { ++ { 0, "8kb/s" }, ++ { 1, "16kb/s" }, ++ { 2, "32kb/s" }, ++ { 3, "64kb/s" }, ++ { 0, NULL } ++}; ++ + static void + dissect_rsl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) + { +@@ -3516,7 +3936,6 @@ + /* 9.1 Message discriminator */ + proto_tree_add_item(rsl_tree, hf_rsl_msg_dsc, tvb, offset, 1, FALSE); + proto_tree_add_item(rsl_tree, hf_rsl_T_bit, tvb, offset, 1, FALSE); +- offset++; + + offset = dissct_rsl_msg(tvb, pinfo, rsl_tree, offset); + +@@ -3886,6 +4305,86 @@ + FT_UINT8, BASE_DEC, VALS(rsl_emlpp_prio_vals), 0x03, + NULL, HFILL } + }, ++ { &hf_rsl_speech_mode_s, ++ { "ip.access Speech Mode S", "rsl.ipacc.speech_mode_s", ++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_spm_s_vals), ++ 0xf, NULL, HFILL } ++ }, ++ { &hf_rsl_speech_mode_m, ++ { "ip.access Speech Mode M", "rsl.ipacc.speech_mode_m", ++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_spm_m_vals), ++ 0xf0, NULL, HFILL } ++ }, ++ { &hf_rsl_conn_stat, ++ { "ip.access Connection Statistics","rsl.ipacc.conn_stat", ++ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } ++ }, ++ { &hf_rsl_conn_id, ++ { "ip.access Connection ID", "rsl.ipacc.conn_id", ++ FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } ++ }, ++ { &hf_rsl_rtp_payload, ++ { "ip.access RTP Payload Type", "rsl.ipacc.rtp_payload", ++ FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } ++ }, ++ { &hf_rsl_rtp_csd_fmt_d, ++ { "ip.access RTP CSD Format D", "rsl.ipacc.rtp_csd_fmt_d", ++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_rtp_csd_fmt_d_vals), ++ 0x0f, NULL, HFILL }, ++ }, ++ { &hf_rsl_rtp_csd_fmt_ir, ++ { "ip.access RTP CSD Format IR", "rsl.ipacc.rtp_csd_fmt_ir", ++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_rtp_csd_fmt_ir_vals), ++ 0xf0, NULL, HFILL }, ++ }, ++ { &hf_rsl_local_port, ++ { "ip.access Local RTP Port", "rsl.ipacc.local_port", ++ FT_UINT16, BASE_DEC, NULL, 0x0, ++ "ip.access Local RTP Port", HFILL }, ++ }, ++ { &hf_rsl_remote_port, ++ { "ip.access Remote RTP Port", "rsl.ipacc.remote_port", ++ FT_UINT16, BASE_DEC, NULL, 0x0, ++ "ip.access Remote RTP Port", HFILL }, ++ }, ++ { &hf_rsl_local_ip, ++ { "ip.access Local IP Address", "rsl.ipacc.local_ip", ++ FT_IPv4, BASE_NONE, NULL, 0x0, ++ "ip.access Local IP Address", HFILL }, ++ }, ++ { &hf_rsl_remote_ip, ++ { "ip.access Remote IP Address", "rsl.ipacc.remote_ip", ++ FT_IPv4, BASE_NONE, NULL, 0x0, ++ "ip.access Remote IP Address", HFILL }, ++ }, ++ { &hf_rsl_cstat_tx_pkts, ++ { "Packets Sent", "rsl.ipacc.cstat.tx_pkts", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_rsl_cstat_tx_octs, ++ { "Octets Sent", "rsl.ipacc.cstat.tx_octets", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_rsl_cstat_rx_pkts, ++ { "Packets Received", "rsl.ipacc.cstat.rx_pkts", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_rsl_cstat_rx_octs, ++ { "Octets Received", "rsl.ipacc.cstat.rx_octets", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_rsl_cstat_lost_pkts, ++ { "Packets Lost", "rsl.ipacc.cstat.lost_pkts", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_rsl_cstat_ia_jitter, ++ { "Inter-arrival Jitter", "rsl.ipacc.cstat.ia_jitter", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, ++ { &hf_rsl_cstat_avg_tx_dly, ++ { "Average Tx Delay", "rsl.ipacc.cstat.avg_tx_delay", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } ++ }, + }; + static gint *ett[] = { + &ett_rsl, +@@ -3943,6 +4442,14 @@ + &ett_ie_meas_res_no, + &ett_ie_message_id, + &ett_ie_sys_info_type, ++ &ett_ie_speech_mode, ++ &ett_ie_conn_stat, ++ &ett_ie_conn_id, ++ &ett_ie_remote_ip, ++ &ett_ie_remote_port, ++ &ett_ie_local_port, ++ &ett_ie_local_ip, ++ &ett_ie_rtp_payload, + }; + + /* Register the protocol name and description */ diff --git a/wireshark/rsl-system_info.patch b/wireshark/rsl-system_info.patch new file mode 100644 index 000000000..2945c6540 --- /dev/null +++ b/wireshark/rsl-system_info.patch @@ -0,0 +1,13 @@ +Index: wireshark/epan/dissectors/packet-rsl.c +=================================================================== +--- wireshark.orig/epan/dissectors/packet-rsl.c ++++ wireshark/epan/dissectors/packet-rsl.c +@@ -2291,7 +2291,7 @@ + + proto_tree_add_text(ie_tree, tvb,offset,length,"Layer 3 message"); + next_tvb = tvb_new_subset(tvb, offset, length, length); +- /* call_dissector(gsm_a_dtap_handle, next_tvb, pinfo, top_tree);*/ ++ call_dissector(gsm_a_ccch_handle, next_tvb, pinfo, top_tree); + + offset = offset + length; + |