2011-01-16 10:16:01 +00:00
|
|
|
/* CCCH passive sniffer */
|
|
|
|
/* (C) 2010-2011 by Holger Hans Peter Freyther
|
|
|
|
* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-03-02 22:37:12 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <osmocore/msgb.h>
|
|
|
|
#include <osmocore/rsl.h>
|
|
|
|
#include <osmocore/tlv.h>
|
2010-09-12 13:19:59 +00:00
|
|
|
#include <osmocore/gsm48_ie.h>
|
2011-01-16 10:20:09 +00:00
|
|
|
#include <osmocore/signal.h>
|
2010-03-02 22:37:12 +00:00
|
|
|
#include <osmocore/protocol/gsm_04_08.h>
|
|
|
|
|
2010-07-27 18:44:46 +00:00
|
|
|
#include <osmocom/bb/common/logging.h>
|
|
|
|
#include <osmocom/bb/common/lapdm.h>
|
|
|
|
#include <osmocom/bb/misc/rslms.h>
|
|
|
|
#include <osmocom/bb/misc/layer3.h>
|
|
|
|
#include <osmocom/bb/common/osmocom_data.h>
|
|
|
|
#include <osmocom/bb/common/l1ctl.h>
|
2011-01-16 10:20:09 +00:00
|
|
|
#include <osmocom/bb/common/l23_app.h>
|
2010-03-02 22:37:12 +00:00
|
|
|
|
2010-08-01 09:56:17 +00:00
|
|
|
static struct {
|
2010-09-12 13:19:59 +00:00
|
|
|
int has_si1;
|
2010-08-01 09:56:17 +00:00
|
|
|
int ccch_mode;
|
|
|
|
int ccch_enabled;
|
|
|
|
int rach_count;
|
2010-09-12 13:19:59 +00:00
|
|
|
struct gsm_sysinfo_freq cell_arfcns[1024];
|
2010-08-01 09:56:17 +00:00
|
|
|
} app_state;
|
|
|
|
|
2010-06-20 21:02:40 +00:00
|
|
|
|
|
|
|
static void dump_bcch(struct osmocom_ms *ms, uint8_t tc, const uint8_t *data)
|
2010-03-03 13:11:46 +00:00
|
|
|
{
|
|
|
|
struct gsm48_system_information_type_header *si_hdr;
|
2010-06-20 21:02:40 +00:00
|
|
|
si_hdr = (struct gsm48_system_information_type_header *) data;
|
2010-03-03 13:11:46 +00:00
|
|
|
|
|
|
|
/* GSM 05.02 §6.3.1.3 Mapping of BCCH data */
|
|
|
|
switch (si_hdr->system_information) {
|
|
|
|
case GSM48_MT_RR_SYSINFO_1:
|
|
|
|
fprintf(stderr, "\tSI1");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 0)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
2010-09-12 13:19:59 +00:00
|
|
|
if (!app_state.has_si1) {
|
|
|
|
struct gsm48_system_information_type_1 *si1 =
|
|
|
|
(struct gsm48_system_information_type_1 *)data;
|
|
|
|
|
2011-01-16 10:22:58 +00:00
|
|
|
gsm48_decode_freq_list(app_state.cell_arfcns,
|
2010-09-12 13:19:59 +00:00
|
|
|
si1->cell_channel_description,
|
|
|
|
sizeof(si1->cell_channel_description),
|
|
|
|
0xff, 0x01);
|
|
|
|
|
|
|
|
app_state.has_si1 = 1;
|
|
|
|
}
|
2010-03-03 13:11:46 +00:00
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_2:
|
|
|
|
fprintf(stderr, "\tSI2");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 1)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_3:
|
|
|
|
fprintf(stderr, "\tSI3");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 2 && tc != 6)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
2010-08-01 09:56:17 +00:00
|
|
|
if (app_state.ccch_mode == CCCH_MODE_NONE) {
|
2010-06-20 21:02:40 +00:00
|
|
|
struct gsm48_system_information_type_3 *si3 =
|
|
|
|
(struct gsm48_system_information_type_3 *)data;
|
|
|
|
|
|
|
|
if (si3->control_channel_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
|
2010-08-01 09:56:17 +00:00
|
|
|
app_state.ccch_mode = CCCH_MODE_COMBINED;
|
2010-06-20 21:02:40 +00:00
|
|
|
else
|
2010-08-01 09:56:17 +00:00
|
|
|
app_state.ccch_mode = CCCH_MODE_NON_COMBINED;
|
2010-06-20 21:02:40 +00:00
|
|
|
|
2010-08-01 09:56:17 +00:00
|
|
|
l1ctl_tx_ccch_mode_req(ms, app_state.ccch_mode);
|
2010-06-20 21:02:40 +00:00
|
|
|
}
|
2010-03-03 13:11:46 +00:00
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_4:
|
|
|
|
fprintf(stderr, "\tSI4");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 3 && tc != 7)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_5:
|
|
|
|
fprintf(stderr, "\tSI5");
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_6:
|
|
|
|
fprintf(stderr, "\tSI6");
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_7:
|
|
|
|
fprintf(stderr, "\tSI7");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 7)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_8:
|
|
|
|
fprintf(stderr, "\tSI8");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 3)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_9:
|
|
|
|
fprintf(stderr, "\tSI9");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 4)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_13:
|
|
|
|
fprintf(stderr, "\tSI13");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 4 && tc != 0)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_16:
|
|
|
|
fprintf(stderr, "\tSI16");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 6)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_17:
|
|
|
|
fprintf(stderr, "\tSI17");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 2)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_2bis:
|
|
|
|
fprintf(stderr, "\tSI2bis");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 5)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_2ter:
|
|
|
|
fprintf(stderr, "\tSI2ter");
|
|
|
|
#ifdef BCCH_TC_CHECK
|
|
|
|
if (tc != 5 && tc != 4)
|
|
|
|
fprintf(stderr, " on wrong TC");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_5bis:
|
|
|
|
fprintf(stderr, "\tSI5bis");
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_SYSINFO_5ter:
|
|
|
|
fprintf(stderr, "\tSI5ter");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "\tUnknown SI");
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-02 22:37:12 +00:00
|
|
|
/* send location updating request * (as part of RSLms EST IND /
|
|
|
|
LAPDm SABME) */
|
|
|
|
static int gsm48_tx_loc_upd_req(struct osmocom_ms *ms, uint8_t chan_nr)
|
|
|
|
{
|
|
|
|
struct msgb *msg = msgb_alloc_headroom(256, 16, "loc_upd_req");
|
|
|
|
struct gsm48_hdr *gh;
|
|
|
|
struct gsm48_loc_upd_req *lu_r;
|
|
|
|
|
2010-03-27 00:56:05 +00:00
|
|
|
DEBUGP(DMM, "chan_nr=%u\n", chan_nr);
|
2010-03-03 12:57:54 +00:00
|
|
|
|
|
|
|
msg->l3h = msgb_put(msg, sizeof(*gh));
|
|
|
|
gh = (struct gsm48_hdr *) msg->l3h;
|
2010-03-02 22:37:12 +00:00
|
|
|
gh->proto_discr = GSM48_PDISC_MM;
|
|
|
|
gh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST;
|
|
|
|
lu_r = (struct gsm48_loc_upd_req *) msgb_put(msg, sizeof(*lu_r));
|
|
|
|
lu_r->type = GSM48_LUPD_IMSI_ATT;
|
|
|
|
lu_r->key_seq = 0;
|
|
|
|
/* FIXME: set LAI and CM1 */
|
|
|
|
/* FIXME: set MI */
|
|
|
|
lu_r->mi_len = 0;
|
|
|
|
|
|
|
|
return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, chan_nr, 0, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms)
|
|
|
|
{
|
|
|
|
struct gsm48_imm_ass *ia = msgb_l3(msg);
|
|
|
|
uint8_t ch_type, ch_subch, ch_ts;
|
2010-09-12 13:19:59 +00:00
|
|
|
int rv;
|
|
|
|
|
|
|
|
/* Discard packet TBF assignement */
|
|
|
|
if (ia->page_mode & 0xf0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* FIXME: compare RA and GSM time with when we sent RACH req */
|
2010-03-02 22:37:12 +00:00
|
|
|
|
|
|
|
rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts);
|
|
|
|
|
2010-09-12 13:19:59 +00:00
|
|
|
if (!ia->chan_desc.h0.h) {
|
|
|
|
/* Non-hopping */
|
|
|
|
uint16_t arfcn;
|
2010-03-02 22:37:12 +00:00
|
|
|
|
2010-09-12 13:19:59 +00:00
|
|
|
arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8);
|
2010-03-02 22:37:12 +00:00
|
|
|
|
2010-09-12 13:19:59 +00:00
|
|
|
DEBUGP(DRR, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
|
|
|
|
"ARFCN=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra,
|
|
|
|
ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch,
|
|
|
|
ia->chan_desc.h0.tsc);
|
|
|
|
|
|
|
|
/* request L1 to go to dedicated mode on assigned channel */
|
|
|
|
rv = l1ctl_tx_dm_est_req_h0(ms,
|
2010-09-26 20:55:02 +00:00
|
|
|
arfcn, ia->chan_desc.chan_nr, ia->chan_desc.h0.tsc,
|
|
|
|
GSM48_CMODE_SIGN);
|
2010-09-12 13:19:59 +00:00
|
|
|
} else {
|
|
|
|
/* Hopping */
|
|
|
|
uint8_t maio, hsn, ma_len;
|
|
|
|
uint16_t ma[64], arfcn;
|
|
|
|
int i, j, k;
|
|
|
|
|
|
|
|
hsn = ia->chan_desc.h1.hsn;
|
|
|
|
maio = ia->chan_desc.h1.maio_low | (ia->chan_desc.h1.maio_high << 2);
|
2010-03-02 22:37:12 +00:00
|
|
|
|
2010-09-12 13:19:59 +00:00
|
|
|
DEBUGP(DRR, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
|
|
|
|
"HSN=%u, MAIO=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra,
|
|
|
|
ia->chan_desc.chan_nr, hsn, maio, ch_ts, ch_subch,
|
|
|
|
ia->chan_desc.h1.tsc);
|
2010-03-02 22:37:12 +00:00
|
|
|
|
2010-09-12 13:19:59 +00:00
|
|
|
/* decode mobile allocation */
|
|
|
|
ma_len = 0;
|
|
|
|
for (i=1, j=0; i<=1024; i++) {
|
|
|
|
arfcn = i & 1023;
|
|
|
|
if (app_state.cell_arfcns[arfcn].mask & 0x01) {
|
|
|
|
k = ia->mob_alloc_len - (j>>3) - 1;
|
|
|
|
if (ia->mob_alloc[k] & (1 << (j&7))) {
|
|
|
|
ma[ma_len++] = arfcn;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* request L1 to go to dedicated mode on assigned channel */
|
|
|
|
rv = l1ctl_tx_dm_est_req_h1(ms,
|
|
|
|
maio, hsn, ma, ma_len,
|
2010-09-26 20:55:02 +00:00
|
|
|
ia->chan_desc.chan_nr, ia->chan_desc.h1.tsc,
|
|
|
|
GSM48_CMODE_SIGN);
|
2010-09-12 13:19:59 +00:00
|
|
|
}
|
2010-03-02 22:37:12 +00:00
|
|
|
|
2010-03-27 00:56:05 +00:00
|
|
|
DEBUGPC(DRR, "\n");
|
|
|
|
|
2010-09-12 13:19:59 +00:00
|
|
|
rv = gsm48_tx_loc_upd_req(ms, ia->chan_desc.chan_nr);
|
|
|
|
|
|
|
|
return rv;
|
2010-03-02 22:37:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms)
|
|
|
|
{
|
|
|
|
struct gsm48_system_information_type_header *sih = msgb_l3(msg);
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (sih->rr_protocol_discriminator != GSM48_PDISC_RR)
|
2010-03-27 00:56:05 +00:00
|
|
|
LOGP(DRR, LOGL_ERROR, "PCH pdisc != RR\n");
|
2011-01-16 10:14:37 +00:00
|
|
|
|
2010-03-02 22:37:12 +00:00
|
|
|
switch (sih->system_information) {
|
|
|
|
case GSM48_MT_RR_PAG_REQ_1:
|
|
|
|
case GSM48_MT_RR_PAG_REQ_2:
|
|
|
|
case GSM48_MT_RR_PAG_REQ_3:
|
|
|
|
/* FIXME: implement decoding of paging request */
|
|
|
|
break;
|
|
|
|
case GSM48_MT_RR_IMM_ASS:
|
|
|
|
rc = gsm48_rx_imm_ass(msg, ms);
|
|
|
|
break;
|
|
|
|
default:
|
2010-03-27 00:56:05 +00:00
|
|
|
LOGP(DRR, LOGL_NOTICE, "unknown PCH/AGCH type 0x%02x\n",
|
|
|
|
sih->system_information);
|
2010-03-02 22:37:12 +00:00
|
|
|
rc = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-03-03 13:11:46 +00:00
|
|
|
int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms)
|
2010-03-02 22:37:12 +00:00
|
|
|
{
|
2010-03-03 13:11:46 +00:00
|
|
|
/* FIXME: we have lost the gsm frame time until here, need to store it
|
|
|
|
* in some msgb context */
|
|
|
|
//dump_bcch(dl->time.tc, ccch->data);
|
2010-06-20 21:02:40 +00:00
|
|
|
dump_bcch(ms, 0, msg->l3h);
|
2010-03-03 13:11:46 +00:00
|
|
|
|
2010-08-01 09:56:17 +00:00
|
|
|
/* Req channel logic */
|
|
|
|
if (app_state.ccch_enabled && (app_state.rach_count < 2)) {
|
2010-10-30 15:30:59 +00:00
|
|
|
l1ctl_tx_rach_req(ms, app_state.rach_count, 0,
|
|
|
|
app_state.ccch_mode == CCCH_MODE_COMBINED);
|
2010-08-01 09:56:17 +00:00
|
|
|
app_state.rach_count++;
|
|
|
|
}
|
|
|
|
|
2010-03-03 13:11:46 +00:00
|
|
|
return 0;
|
2010-03-02 22:37:12 +00:00
|
|
|
}
|
2010-08-01 09:56:17 +00:00
|
|
|
|
|
|
|
void layer3_app_reset(void)
|
|
|
|
{
|
|
|
|
/* Reset state */
|
2010-09-12 13:19:59 +00:00
|
|
|
app_state.has_si1 = 0;
|
2010-08-01 09:56:17 +00:00
|
|
|
app_state.ccch_mode = CCCH_MODE_NONE;
|
|
|
|
app_state.ccch_enabled = 0;
|
|
|
|
app_state.rach_count = 0;
|
2010-09-12 13:19:59 +00:00
|
|
|
|
|
|
|
memset(&app_state.cell_arfcns, 0x00, sizeof(app_state.cell_arfcns));
|
2010-08-01 09:56:17 +00:00
|
|
|
}
|
2011-01-16 10:20:09 +00:00
|
|
|
|
|
|
|
static int signal_cb(unsigned int subsys, unsigned int signal,
|
|
|
|
void *handler_data, void *signal_data)
|
|
|
|
{
|
|
|
|
struct osmocom_ms *ms;
|
|
|
|
|
|
|
|
if (subsys != SS_L1CTL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (signal) {
|
|
|
|
case S_L1CTL_RESET:
|
|
|
|
ms = signal_data;
|
|
|
|
layer3_app_reset();
|
|
|
|
return l1ctl_tx_fbsb_req(ms, ms->test_arfcn,
|
|
|
|
L1CTL_FBSB_F_FB01SB, 100, 0,
|
|
|
|
CCCH_MODE_NONE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int l23_app_init(struct osmocom_ms *ms)
|
|
|
|
{
|
|
|
|
register_signal_handler(SS_L1CTL, &signal_cb, NULL);
|
|
|
|
l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
|
|
|
|
return layer3_init(ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct l23_app_info info = {
|
|
|
|
.copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n",
|
|
|
|
.contribution = "Contributions by Holger Hans Peter Freyther\n",
|
|
|
|
};
|
|
|
|
|
|
|
|
struct l23_app_info *l23_app_info()
|
|
|
|
{
|
|
|
|
return &info;
|
|
|
|
}
|