Merge branch 'master' into on-waves/sccp

This commit is contained in:
Holger Hans Peter Freyther 2010-02-03 18:15:05 +01:00
commit 67f5003caa
137 changed files with 21007 additions and 1585 deletions

6
openbsc/.gitignore vendored
View File

@ -3,8 +3,12 @@
.deps
Makefile
Makefile.in
bscconfig.h
bscconfig.h.in
openbsc.pc
bsc_hack
bsc_msc_ip
bsc_mgcp
*.*~
*.sw?
@ -26,6 +30,8 @@ hlr.sqlite3
bs11_config
ipaccess-config
ipaccess-find
ipaccess-firmware
ipaccess-proxy
isdnsync
#tests

View File

@ -3,3 +3,5 @@ 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>

View File

@ -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]

54
openbsc/contrib/mgcp_server.py Executable file
View File

@ -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)

89
openbsc/doc/handover.txt Normal file
View File

@ -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

View File

@ -3,4 +3,6 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
timer.h misdn.h chan_alloc.h telnet_interface.h paging.h \
subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h
bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h \
silent_call.h mgcp.h meas_rep.h bitvec.h rest_octets.h \
system_information.h handover.h statistics.h

View File

@ -211,6 +211,7 @@ enum abis_nm_msgtype_bs11 {
enum abis_nm_msgtype_ipacc {
NM_MT_IPACC_RESTART = 0x87,
NM_MT_IPACC_RESTART_ACK,
NM_MT_IPACC_RESTART_NACK,
NM_MT_IPACC_RSL_CONNECT = 0xe0,
NM_MT_IPACC_RSL_CONNECT_ACK,
NM_MT_IPACC_RSL_CONNECT_NACK,
@ -485,6 +486,12 @@ enum abis_nm_avail_state {
NM_AVSTATE_OK = 0xff,
};
enum abis_nm_op_state {
NM_OPSTATE_DISABLED = 1,
NM_OPSTATE_ENABLED = 2,
NM_OPSTATE_NULL = 0xff,
};
/* Section 9.4.13: Channel Combination */
enum abis_nm_chan_comb {
NM_CHANC_TCHFull = 0x00, /* TCH/F + TCH/H + SACCH/TF */
@ -712,6 +719,8 @@ struct ipac_bcch_info {
u_int8_t ca_list_si1[16];
};
extern const struct tlv_definition nm_att_tlvdef;
/* PUBLIC */
struct msgb;
@ -726,7 +735,7 @@ struct abis_nm_cfg {
extern int abis_nm_rcvmsg(struct msgb *msg);
int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len);
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,
@ -780,10 +789,13 @@ 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,
@ -798,7 +810,7 @@ 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 *bts, u_int8_t *attr,
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,
@ -819,4 +831,5 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
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 */

View File

@ -497,7 +497,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
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 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,
@ -537,8 +537,8 @@ 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_int16_t conn_id,
u_int8_t rtp_payload2);
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);
@ -547,7 +547,7 @@ 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(struct gsm_lchan *lchan);
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 */

View File

@ -0,0 +1,65 @@
#ifndef _BITVEC_H
#define _BITVEC_H
/* bit vector utility routines */
/* (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.
*
*/
/* In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are
* defined relative to the 0x2b padding pattern */
enum bit_value {
ZERO = 0,
ONE = 1,
L = 2,
H = 3,
};
struct bitvec {
unsigned int cur_bit; /* curser to the next unused bit */
unsigned int data_len; /* length of data array in bytes */
u_int8_t *data; /* pointer to data array */
};
/* check if the bit is 0 or 1 for a given position inside a bitvec */
enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr);
/* get the Nth set bit inside the bit vector */
unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n);
/* Set a bit at given position */
int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
enum bit_value bit);
/* Set the next bit in the vector */
int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
/* Set multiple bits at the current position */
int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
/* Add an unsigned integer (of length count bits) to current position */
int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
/* Pad the bit vector up to a certain bit position */
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
#endif /* _BITVEC_H */

View File

@ -1,64 +0,0 @@
/*
* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2008 by Stefan Schmidt <stefan@datenfreihafen.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 _CALL_HANDLING_H
#define _CALL_HANDLING_H
#include "linuxlist.h"
#include "gsm_subscriber.h"
#include "timer.h"
/*
* State transitions to be seen from the outside
*/
#define CALL_STATE_NULL 0
#define CALL_STATE_SETUP 1
#define CALL_STATE_PROCEED 2
#define CALL_STATE_ALERT 3
#define CALL_STATE_CONNECT 4
#define CALL_STATE_ACTIVE 5
#define CALL_STATE_RELEASE 6
struct call_data {
struct llist_head entry;
void (*state_change_cb)(int oldstate, int newstate, int event, void *data);
void *data;
char *destination_number;
/* Internal */
int state;
char tmsi[GSM_TMSI_LENGTH];
struct timer_list t30x; /* to be added for... */
};
int call_initiate(struct call_data *call, char *tmsi);
void call_abort(struct call_data *call);
/**
* Get notified about new incoming calls. The call_data is owned
* and managed by the internal call handling.
*/
void call_set_callback(void (*cb)(struct call_data *call, void *data), void* data);
void call_proceed(struct call_data *call_data);
void call_connect(struct call_data *call_data);
#endif /* _CALL_HANDLING_H */

View File

@ -49,4 +49,18 @@ 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 */

View File

@ -0,0 +1,22 @@
/*
* COMP128 header
*
* See comp128.c for details
*/
#ifndef __COMP128_H__
#define __COMP128_H__
#include <sys/types.h>
/*
* Performs the COMP128 algorithm (used as A3/A8)
* ki : u_int8_t [16]
* srand : u_int8_t [16]
* sres : u_int8_t [4]
* kc : u_int8_t [8]
*/
void comp128(u_int8_t *ki, u_int8_t *srand, u_int8_t *sres, u_int8_t *kc);
#endif /* __COMP128_H__ */

View File

@ -43,9 +43,20 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
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);
@ -53,4 +64,8 @@ int db_sms_mark_sent(struct gsm_sms *sms);
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 */

View File

@ -1,45 +1,131 @@
#ifndef _DEBUG_H
#define _DEBUG_H
#include <stdio.h>
#include "linuxlist.h"
#define DEBUG
#define DRLL 0x0001
#define DCC 0x0002
#define DMM 0x0004
#define DRR 0x0008
#define DRSL 0x0010
#define DNM 0x0020
#define DMNCC 0x0080
#define DSMS 0x0100
#define DPAG 0x0200
#define DMEAS 0x0400
#define DMI 0x1000
#define DMIB 0x2000
#define DMUX 0x4000
#define DINP 0x8000
#define DSCCP 0x10000
#define DMSC 0x20000
#define DMGCP 0x40000
/* 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 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)));
void debug_parse_category_mask(const char* mask);
void debug_use_color(int use_color);
void debug_timestamp(int enable);
extern unsigned int debug_mask;
/* 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 */

View File

@ -1,6 +1,8 @@
#ifndef _GSM_04_08_H
#define _GSM_04_08_H
#include <openbsc/meas_rep.h>
/* GSM TS 04.08 definitions */
struct gsm_lchan;
@ -88,6 +90,22 @@ struct gsm48_ass_cmd {
u_int8_t data[0];
} __attribute__((packed));
/* Chapter 10.5.2.2 */
struct gsm48_cell_desc {
u_int8_t bcc:3,
ncc:3,
arfcn_hi:2;
u_int8_t arfcn_lo;
} __attribute__((packed));
/* Chapter 9.1.15 */
struct gsm48_ho_cmd {
struct gsm48_cell_desc cell_desc;
struct gsm48_chan_desc chan_desc;
u_int8_t ho_ref;
u_int8_t power_command;
u_int8_t data[0];
} __attribute__((packed));
/* Chapter 9.1.18 */
struct gsm48_imm_ass {
@ -169,6 +187,13 @@ struct gsm48_control_channel_descr {
u_int8_t t3212;
} __attribute__ ((packed));
struct gsm48_cell_options {
u_int8_t radio_link_timeout:4,
dtx:2,
pwrc:1,
spare:1;
} __attribute__ ((packed));
/* Section 9.2.9 CM service request */
struct gsm48_service_request {
u_int8_t cm_service_type : 4,
@ -185,7 +210,7 @@ struct gsm48_system_information_type_1 {
struct gsm48_system_information_type_header header;
u_int8_t cell_channel_description[16];
struct gsm48_rach_control rach_control;
u_int8_t s1_reset;
u_int8_t rest_octets[0]; /* NCH position on the CCCH */
} __attribute__ ((packed));
/* Section 9.1.32 System information Type 2 */
@ -202,10 +227,10 @@ struct gsm48_system_information_type_3 {
u_int16_t cell_identity;
struct gsm48_loc_area_id lai;
struct gsm48_control_channel_descr control_channel_desc;
u_int8_t cell_options;
struct gsm48_cell_options cell_options;
struct gsm48_cell_sel_par cell_sel_par;
struct gsm48_rach_control rach_control;
u_int8_t s3_reset_octets[4];
u_int8_t rest_octets[0];
} __attribute__ ((packed));
/* Section 9.1.36 System information Type 4 */
@ -235,9 +260,15 @@ struct gsm48_system_information_type_6 {
u_int8_t system_information;
u_int16_t cell_identity;
struct gsm48_loc_area_id lai;
u_int8_t cell_options;
struct gsm48_cell_options cell_options;
u_int8_t ncc_permitted;
u_int8_t si_6_reset[0];
u_int8_t rest_octets[0];
} __attribute__ ((packed));
/* Section 9.1.43a System Information type 13 */
struct gsm48_system_information_type_13 {
struct gsm48_system_information_type_header header;
u_int8_t rest_octets[0];
} __attribute__ ((packed));
/* Section 9.2.12 IMSI Detach Indication */
@ -618,30 +649,6 @@ enum gsm48_reject_value {
GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16,
};
/* extracted from a L3 measurement report IE */
struct gsm_meas_rep_cell {
u_int8_t rxlev;
u_int8_t bcch_freq; /* fixme: translate to ARFCN */
u_int8_t bsic;
};
struct gsm_meas_rep {
unsigned int flags;
u_int8_t rxlev_full;
u_int8_t rxqual_full;
u_int8_t rxlev_sub;
u_int8_t rxqual_sub;
int num_cell;
struct gsm_meas_rep_cell cell[6];
};
#define MEAS_REP_F_DTX 0x01
#define MEAS_REP_F_VALID 0x02
#define MEAS_REP_F_BA1 0x04
void gsm48_parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
int len);
enum chreq_type {
CHREQ_T_EMERG_CALL,
CHREQ_T_CALL_REEST_TCH_F,
@ -656,6 +663,9 @@ enum chreq_type {
CHREQ_T_PAG_R_ANY_NECI1,
CHREQ_T_PAG_R_TCH_F,
CHREQ_T_PAG_R_TCH_FH,
CHREQ_T_LMU,
CHREQ_T_RESERVED_SDCCH,
CHREQ_T_RESERVED_IGNORE,
};
/* Chapter 11.3 */
@ -738,7 +748,6 @@ struct gsm_trans;
/* config options controlling the behaviour of the lower leves */
void gsm0408_allow_everyone(int allow);
void gsm0408_set_reject_cause(int cause);
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id);
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
@ -747,7 +756,7 @@ 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 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);
@ -759,7 +768,9 @@ 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 *lchan, u_int8_t power_class);
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);
@ -779,5 +790,7 @@ 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

View File

@ -9,6 +9,7 @@ struct value_string {
};
const char *get_value_string(const struct value_string *vs, u_int32_t val);
int get_string_value(const struct value_string *vs, const char *str);
enum gsm_band {
GSM_BAND_400,
@ -38,6 +39,13 @@ enum gsm_chan_t {
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 {
@ -53,6 +61,8 @@ enum gsm_chreq_reason_t {
#include <openbsc/abis_rsl.h>
#include <openbsc/mncc.h>
#include <openbsc/tlv.h>
#include <openbsc/bitvec.h>
#include <openbsc/statistics.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@ -92,14 +102,14 @@ typedef int gsm_cbfn(unsigned int hooknum,
#define LCHAN_RELEASE_TIMEOUT 20, 0
#define use_lchan(lchan) \
do { lchan->use_count++; \
DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
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(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
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);
@ -110,6 +120,28 @@ 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;
@ -135,6 +167,20 @@ struct gsm_loc_updating_operation {
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)
@ -143,6 +189,15 @@ struct gsm_loc_updating_operation {
#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;
@ -154,6 +209,8 @@ struct gsm_lchan {
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;
@ -163,6 +220,8 @@ struct gsm_lchan {
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;
@ -185,6 +244,24 @@ struct gsm_lchan {
/* 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 {
@ -212,13 +289,6 @@ struct gsm_bts_trx_ts {
/* To which E1 subslot are we connected */
struct gsm_e1_subslot e1_link;
struct {
u_int32_t bound_ip;
u_int16_t bound_port;
u_int8_t rtp_payload2;
u_int16_t conn_id;
struct rtp_socket *rtp_socket;
} abis_ip;
struct gsm_lchan lchan[TS_MAX_LCHAN];
};
@ -265,6 +335,15 @@ enum gsm_bts_type {
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
*/
@ -286,7 +365,6 @@ struct gsm_paging_request {
gsm_cbfn *cbfn;
void *cbfn_param;
};
#define T3113_VALUE 60, 0
/*
* This keeps track of the paging status of one BTS. It
@ -333,11 +411,11 @@ struct gsm_bts {
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;
int cell_barred;
/* maximum Tx power that the MS is permitted to use in this cell */
int ms_max_power;
@ -354,8 +432,6 @@ struct gsm_bts {
/* number of this BTS on given E1 link */
u_int8_t bts_nr;
struct gsm48_control_channel_descr chan_desc;
/* paging state and control */
struct gsm_bts_paging_state paging;
@ -366,11 +442,28 @@ struct gsm_bts {
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 {
@ -399,12 +492,58 @@ struct gsm_bts {
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;
@ -412,8 +551,28 @@ struct gsm_network {
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);
@ -422,6 +581,24 @@ struct gsm_network {
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
@ -446,20 +623,30 @@ struct gsm_sms {
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_lchan_name(enum gsm_chan_t c);
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,
@ -471,6 +658,7 @@ 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);
@ -478,6 +666,7 @@ char *gsm_band_name(enum gsm_band band);
enum gsm_band gsm_band_parse(const char *mhz);
extern void *tall_bsc_ctx;
extern int ipacc_rtp_direct;
static inline int is_ipaccess_bts(struct gsm_bts *bts)
{
@ -506,4 +695,11 @@ static inline int is_siemens_bts(struct gsm_bts *bts)
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);
struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan);
#endif

View File

@ -86,6 +86,8 @@ 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;

View File

@ -33,5 +33,16 @@ int gsm_7bit_encode(u_int8_t *result, const char *data);
int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl);
/* According to TS 08.05 Chapter 8.1.4 */
int rxlev2dbm(u_int8_t rxlev);
u_int8_t dbm2rxlev(int dbm);
/* According to GSM 04.08 Chapter 10.5.2.29 */
static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
static inline int rach_max_trans_raw2val(int raw) {
const int tbl[4] = { 1, 2, 4, 7 };
return tbl[raw & 3];
}
void generate_backtrace();
#endif

View File

@ -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 */

View File

@ -2,6 +2,10 @@
#define _IPACCESS_H
#include "e1_input.h"
#include "linuxlist.h"
#define IPA_TCP_PORT_OML 3002
#define IPA_TCP_PORT_RSL 3003
struct ipaccess_head {
u_int16_t len; /* network byte order */
@ -45,4 +49,54 @@ 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);
/*
* 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_int8_t dummy[2];
u_int16_t part_length;
/* 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;
};
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 */

View File

@ -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 */

View File

@ -0,0 +1,70 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/*
* (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.
*
*/
#ifndef OPENBSC_MGCP_H
#define OPENBSC_MGCP_H
#include "msgb.h"
#define RTP_PORT_DEFAULT 4000
extern unsigned int rtp_base_port;
/**
* 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);
}
int mgcp_parse_config(const char *config_file, struct gsm_network *dummy_network);
struct msgb *mgcp_handle_message(struct msgb *msg);
struct msgb *mgcp_create_rsip(void);
int mgcp_vty_init(void);
/* endpoint managed */
#define MGCP_ENDP_CRCX 1
#define MGCP_ENDP_DLCX 2
#define MGCP_ENDP_MDCX 3
typedef int (*mgcp_change)(int endpoint, int state, int local_rtp, void *);
void mgcp_set_change_cb(mgcp_change cb, void *data);
#endif

View File

@ -87,7 +87,8 @@ struct gsm_call {
#define MNCC_FRAME_DROP 0x0202
#define MNCC_LCHAN_MODIFY 0x0203
#define GSM_TRAU_FRAME 0x0300
#define GSM_TCHF_FRAME 0x0300
#define GSM_TCHF_FRAME_EFR 0x0301
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@ -162,6 +163,14 @@ struct gsm_mncc_cccap {
int pcp;
};
enum {
GSM_MNCC_BCAP_SPEECH = 0,
GSM_MNCC_BCAP_UNR_DIG = 1,
GSM_MNCC_BCAP_AUDIO = 2,
GSM_MNCC_BCAP_FAX_G3 = 3,
GSM_MNCC_BCAP_OTHER_ITC = 5,
GSM_MNCC_BCAP_RESERVED = 7,
};
struct gsm_mncc {
/* context based information */
@ -199,7 +208,7 @@ struct gsm_mncc {
unsigned char lchan_mode;
};
struct gsm_trau_frame {
struct gsm_data_frame {
u_int32_t msg_type;
u_int32_t callref;
unsigned char data[0];

View File

@ -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 */

View File

@ -34,6 +34,11 @@ enum rtp_rx_action {
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;
@ -56,15 +61,25 @@ struct rtp_socket {
struct rtp_socket *other_sock;
} proxy;
struct {
void (*recv_cb)(struct msgb *msg);
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 */

View File

@ -40,11 +40,13 @@ enum signal_subsystems {
SS_LCHAN,
SS_SUBSCR,
SS_SCALL,
SS_GLOBAL,
};
/* SS_PAGING signals */
enum signal_paging {
S_PAGING_COMPLETED,
S_PAGING_SUCCEEDED,
S_PAGING_EXPIRED,
};
/* SS_SMS signals */
@ -58,6 +60,7 @@ enum signal_sms {
/* SS_ABISIP signals */
enum signal_abisip {
S_ABISIP_CRCX_ACK,
S_ABISIP_MDCX_ACK,
S_ABISIP_DLCX_IND,
};
@ -67,6 +70,9 @@ enum signal_nm {
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 */
};
@ -78,12 +84,19 @@ enum signal_lchan {
* 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 */
@ -93,6 +106,10 @@ enum signal_scall {
S_SCALL_DETACHED,
};
enum signal_global {
S_GLOBAL_SHUTDOWN,
};
typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data);
@ -110,6 +127,11 @@ struct scall_signal_data {
void *data;
};
struct ipacc_ack_signal_data {
struct gsm_bts *bts;
u_int8_t msg_type;
};
/* Management */
int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);

View File

@ -1,7 +1,10 @@
#ifndef _SILENT_CALL_H
#define _SILENT_CALL_H
extern int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data);
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 */

View File

@ -0,0 +1,31 @@
#ifndef _STATISTICS_H
#define _STATISTICS_H
struct counter {
struct llist_head list;
const char *name;
const char *description;
unsigned long value;
};
static inline void counter_inc(struct counter *ctr)
{
ctr->value++;
}
static inline unsigned long counter_get(struct counter *ctr)
{
return ctr->value;
}
static inline void counter_reset(struct counter *ctr)
{
ctr->value = 0;
}
struct counter *counter_alloc(const char *name);
void counter_free(struct counter *ctr);
int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data);
#endif /* _STATISTICS_H */

View File

@ -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

View File

@ -22,7 +22,7 @@
#define TELNET_INTERFACE_H
#include "gsm_data.h"
#include "linuxlist.h"
#include "debug.h"
#include "select.h"
#include <vty/vty.h>
@ -35,6 +35,7 @@ struct telnet_connection {
struct gsm_network *network;
struct bsc_fd fd;
struct vty *vty;
struct debug_target *dbg;
};

View File

@ -119,6 +119,7 @@ static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag,
return buf;
}
/* 'val' is still in host byte order! */
static inline u_int8_t *tv16_put(u_int8_t *buf, u_int8_t tag,
u_int16_t val)
{
@ -184,6 +185,7 @@ struct tlv_p_entry {
};
enum tlv_type {
TLV_TYPE_NONE,
TLV_TYPE_FIXED,
TLV_TYPE_T,
TLV_TYPE_TV,
@ -212,6 +214,8 @@ int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
const u_int8_t *buf, int buf_len);
int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
const u_int8_t *buf, int buf_len, u_int8_t lv_tag, u_int8_t lv_tag2);
/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
#define TLVP_LEN(x, y) (x)->lv[y].len

View File

@ -26,6 +26,9 @@ struct gsm_trans {
/* reference from MNCC or other application */
u_int32_t callref;
/* if traffic channel receive was requested */
int tch_recv;
union {
struct {
@ -65,4 +68,9 @@ 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

View File

@ -46,4 +46,4 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref);
/* send trau from application */
int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf);
int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame);

View File

@ -106,6 +106,7 @@ enum node_type {
TRX_NODE,
TS_NODE,
SUBSCR_NODE,
MGCP_NODE,
};
/* Node which has some commands and prompt string and configuration
@ -355,4 +356,6 @@ void host_config_set(const char *);
void print_version(const char *);
extern void *tall_vty_cmd_ctx;
#endif /* _ZEBRA_COMMAND_H */

View File

@ -59,4 +59,6 @@ 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 */

View File

@ -1,7 +1,8 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
isdnsync bsc_mgcp ipaccess-proxy
noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
noinst_HEADERS = vty/cardshell.h
@ -10,11 +11,14 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.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 tlv_parser.c \
input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
talloc_ctx.c
talloc_ctx.c system_information.c bitvec.c rest_octets.c \
rtp_proxy.c statistics.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 rtp_proxy.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
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 comp128.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
@ -24,11 +28,17 @@ 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 msgb.c debug.c \
select.c timer.c rs232.c tlv_parser.c signal.c talloc.c
select.c timer.c rs232.c tlv_parser.c signal.c talloc.c \
bts_siemens_bs11.c
ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c
ipaccess_find_SOURCES = ipaccess/ipaccess-find.c select.c timer.c
ipaccess_config_SOURCES = ipaccess-config.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 msgb.c talloc.c debug.c select.c timer.c telnet_interface.c
bsc_mgcp_LDADD = libvty.a
ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c msgb.c select.c talloc.c debug.c timer.c

475
openbsc/src/abis_nm.c Executable file → Normal file
View File

@ -47,6 +47,7 @@
#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[] = {
@ -263,7 +264,7 @@ static const enum abis_nm_attr nm_att_settable[] = {
NM_ATT_MEAS_TYPE,
};
static const struct tlv_definition nm_att_tlvdef = {
const struct tlv_definition nm_att_tlvdef = {
.def = {
[NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 },
[NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V },
@ -329,77 +330,6 @@ static const struct tlv_definition nm_att_tlvdef = {
[NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
[NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
[NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
/* 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 },
/* 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 },
//[0x95] = { TLV_TYPE_FIXED, 2 },
[0x85] = { TLV_TYPE_TV },
},
};
@ -422,9 +352,11 @@ int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan)
return -EINVAL;
}
int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len)
int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_t *buf, int len)
{
return tlv_parse(tp, &nm_att_tlvdef, buf, len, 0, 0);
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)
@ -540,11 +472,11 @@ static const char *obj_class_name(u_int8_t oc)
const char *nm_opstate_name(u_int8_t os)
{
switch (os) {
case 1:
case NM_OPSTATE_DISABLED:
return "Disabled";
case 2:
case NM_OPSTATE_ENABLED:
return "Enabled";
case 0xff:
case NM_OPSTATE_NULL:
return "NULL";
default:
return "RFU";
@ -598,6 +530,13 @@ const char *nm_adm_name(u_int8_t adm)
}
}
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) ",
@ -635,7 +574,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &trx->bb_transc.nm_state;
break;
case NM_OC_CHANNEL:
if (obj_inst->trx_nr > bts->num_trx) {
if (obj_inst->trx_nr >= bts->num_trx) {
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
}
@ -671,7 +610,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->bs11.rack.nm_state;
break;
case NM_OC_BS11_ENVABTSE:
if (obj_inst->trx_nr > ARRAY_SIZE(bts->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;
@ -682,7 +621,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->gprs.cell.nm_state;
break;
case NM_OC_GPRS_NSVC:
if (obj_inst->trx_nr > ARRAY_SIZE(bts->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;
@ -719,7 +658,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = &trx->bb_transc;
break;
case NM_OC_CHANNEL:
if (obj_inst->trx_nr > bts->num_trx) {
if (obj_inst->trx_nr >= bts->num_trx) {
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
}
@ -738,7 +677,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = &bts->gprs.cell;
break;
case NM_OC_GPRS_NSVC:
if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
return NULL;
obj = &bts->gprs.nsvc[obj_inst->trx_nr];
break;
@ -793,7 +732,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
new_state = *nm_state;
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
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));
@ -805,19 +744,25 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
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 (memcmp(&new_state, nm_state, sizeof(new_state))) {
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 = 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) {
@ -839,7 +784,7 @@ static int rx_fail_evt_rep(struct msgb *mb)
DEBUGPC(DNM, "Failure Event Report ");
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
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)));
@ -933,7 +878,7 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
if (nack)
return ret;
abis_nm_tlv_parse(&tp, 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)) {
@ -967,7 +912,7 @@ static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb)
struct tlv_parsed tp;
u_int8_t adm_state;
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE))
return -EINVAL;
@ -983,7 +928,7 @@ static int abis_nm_rx_lmt_event(struct msgb *mb)
struct tlv_parsed tp;
DEBUGP(DNM, "LMT Event ");
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
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);
@ -1029,7 +974,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
else
DEBUGPC(DNM, "NACK 0x%02x ", mt);
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
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)));
@ -1043,13 +988,11 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
/* check if last message is to be acked */
if (is_ack_nack(nmh->last_msgtype)) {
if (mt == MT_ACK(nmh->last_msgtype)) {
fprintf(stderr, "received ACK (0x%x)\n",
foh->msg_type);
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 */
fprintf(stderr, "received NACK (0x%x)\n",
foh->msg_type);
DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type);
/* FIXME: somehow signal this to the caller */
} else {
/* really strange things happen */
@ -1071,6 +1014,12 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
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;
@ -1088,8 +1037,8 @@ static int abis_nm_rcvmsg_manuf(struct msgb *mb)
rc = abis_nm_rx_ipacc(mb);
break;
default:
fprintf(stderr, "don't know how to parse OML for this "
"BTS type (%u)\n", bts_type);
LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
"BTS type (%u)\n", bts_type);
rc = 0;
break;
}
@ -1106,12 +1055,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
/* Various consistency checks */
if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
fprintf(stderr, "ABIS OML placement 0x%x not supported\n",
LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
oh->placement);
return -EINVAL;
}
if (oh->sequence != 0) {
fprintf(stderr, "ABIS OML sequence 0x%x != 0x00\n",
LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
oh->sequence);
return -EINVAL;
}
@ -1119,12 +1068,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
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) {
fprintf(stderr, "ABIS OML truncated message (%u > %u)\n",
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)
fprintf(stderr, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), 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);
@ -1137,11 +1086,11 @@ int abis_nm_rcvmsg(struct msgb *msg)
break;
case ABIS_OM_MDISC_MMI:
case ABIS_OM_MDISC_TRAU:
fprintf(stderr, "unimplemented ABIS OML message discriminator 0x%x\n",
LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n",
oh->mdisc);
break;
default:
fprintf(stderr, "unknown ABIS OML message discriminator 0x%x\n",
LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n",
oh->mdisc);
return -EINVAL;
}
@ -1216,6 +1165,22 @@ struct abis_nm_sw {
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)
{
@ -1227,11 +1192,8 @@ static int sw_load_init(struct abis_nm_sw *sw)
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]);
/* 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);
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);
@ -1289,7 +1251,24 @@ static int sw_load_segment(struct abis_nm_sw *sw)
/* 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;
}
@ -1313,11 +1292,7 @@ static int sw_load_end(struct abis_nm_sw *sw)
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);
sw_add_file_id_and_ver(sw, msg);
return abis_nm_sendmsg(sw->bts, msg);
}
@ -1341,6 +1316,59 @@ static int sw_activate(struct abis_nm_sw *sw)
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];
@ -1372,6 +1400,19 @@ static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
/* 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);
@ -1470,6 +1511,12 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
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:
@ -1483,6 +1530,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
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) {
@ -1559,10 +1607,26 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
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;
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;
@ -1590,7 +1654,10 @@ int abis_nm_software_load_status(struct gsm_bts *bts)
return rc;
}
percent = (ftell(sw->stream) * 100) / st.st_size;
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;
}
@ -1755,7 +1822,8 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
/* As it turns out, the BS-11 has some very peculiar restrictions
* on the channel combinations it allows */
if (ts->trx->bts->type == GSM_BTS_TYPE_BS11) {
switch (ts->trx->bts->type) {
case GSM_BTS_TYPE_BS11:
switch (chan_comb) {
case NM_CHANC_TCHHalf:
case NM_CHANC_TCHHalf2:
@ -1801,6 +1869,83 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
/* 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;
}
@ -2203,10 +2348,18 @@ int abis_nm_bs11_get_cclk(struct gsm_bts *bts)
}
//static const u_int8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 };
static const u_int8_t bs11_logon_c8[] = { 0x02 };
static const u_int8_t bs11_logon_c9[] = "FACTORY";
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();
@ -2217,15 +2370,15 @@ int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on)
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
if (on) {
u_int8_t len = 3*2 + sizeof(bdt)
+ sizeof(bs11_logon_c8) + sizeof(bs11_logon_c9);
+ 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,
sizeof(bs11_logon_c8), bs11_logon_c8);
1, &level);
msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME,
sizeof(bs11_logon_c9), bs11_logon_c9);
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);
@ -2273,6 +2426,27 @@ int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked)
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);
@ -2519,14 +2693,15 @@ static int abis_nm_rx_ipacc(struct msgb *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)) {
DEBUGP(DNM, "id string is not com.ipaccess !?!\n");
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, foh->data, oh->length-sizeof(*foh));
abis_nm_tlv_parse(&tp, msg->trx->bts, foh->data, oh->length-sizeof(*foh));
debugp_foh(foh);
@ -2549,7 +2724,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
DEBUGPC(DNM, "\n");
break;
case NM_MT_IPACC_RSL_CONNECT_NACK:
DEBUGPC(DNM, "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)));
@ -2561,35 +2736,35 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
/* FIXME: decode and show the actual attributes */
break;
case NM_MT_IPACC_SET_NVATTR_NACK:
DEBUGPC(DNM, "SET NVATTR NACK ");
LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK ");
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
DEBUGPC(DNM, " CAUSE=%s\n",
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
else
DEBUGPC(DNM, "\n");
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:
DEBUGPC(DNM, "GET NVATTR NACK ");
LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK ");
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
DEBUGPC(DNM, " CAUSE=%s\n",
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
else
DEBUGPC(DNM, "\n");
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:
DEBUGPC(DNM, "SET ATTR NACK ");
LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK ");
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
DEBUGPC(DNM, " CAUSE=%s\n",
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
else
DEBUGPC(DNM, "\n");
LOGPC(DNM, LOGL_ERROR, "\n");
break;
default:
DEBUGPC(DNM, "unknown\n");
@ -2601,7 +2776,14 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
case NM_MT_IPACC_RSL_CONNECT_NACK:
case NM_MT_IPACC_SET_NVATTR_NACK:
case NM_MT_IPACC_GET_NVATTR_NACK:
dispatch_signal(SS_NM, S_NM_IPACC_NACK, &foh->msg_type);
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;
@ -2648,11 +2830,11 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
}
/* set some attributes in NVRAM */
int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr,
int attr_len)
{
return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_NVATTR,
NM_OC_BASEB_TRANSC, 0, 0, 0xff, attr,
return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR,
NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr,
attr_len);
}
@ -2699,6 +2881,19 @@ int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
attr, attr_len);
}
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",

File diff suppressed because it is too large Load Diff

170
openbsc/src/bitvec.c Normal file
View File

@ -0,0 +1,170 @@
/* bit vector utility routines */
/* (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 <sys/types.h>
#include <openbsc/bitvec.h>
#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit)
static inline unsigned int bytenum_from_bitnum(unsigned int bitnum)
{
unsigned int bytenum = bitnum / 8;
return bytenum;
}
/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */
static u_int8_t bitval2mask(enum bit_value bit, u_int8_t bitnum)
{
int bitval;
switch (bit) {
case ZERO:
bitval = (0 << bitnum);
break;
case ONE:
bitval = (1 << bitnum);
break;
case L:
bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum));
break;
case H:
bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum));
break;
default:
return 0;
}
return bitval;
}
/* check if the bit is 0 or 1 for a given position inside a bitvec */
enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr)
{
unsigned int bytenum = bytenum_from_bitnum(bitnr);
unsigned int bitnum = 7 - (bitnr % 8);
u_int8_t bitval;
if (bytenum >= bv->data_len)
return -EINVAL;
bitval = bitval2mask(ONE, bitnum);
if (bv->data[bytenum] & bitval)
return ONE;
return ZERO;
}
/* get the Nth set bit inside the bit vector */
unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n)
{
unsigned int i, k = 0;
for (i = 0; i < bv->data_len*8; i++) {
if (bitvec_get_bit_pos(bv, i) == ONE) {
k++;
if (k == n)
return i;
}
}
return 0;
}
/* set the bit at a given position inside a bitvec */
int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
enum bit_value bit)
{
unsigned int bytenum = bytenum_from_bitnum(bitnr);
unsigned int bitnum = 7 - (bitnr % 8);
u_int8_t bitval;
if (bytenum >= bv->data_len)
return -EINVAL;
/* first clear the bit */
bitval = bitval2mask(ONE, bitnum);
bv->data[bytenum] &= ~bitval;
/* then set it to desired value */
bitval = bitval2mask(bit, bitnum);
bv->data[bytenum] |= bitval;
return 0;
}
/* set the next bit inside a bitvec */
int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
{
int rc;
rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit);
if (!rc)
bv->cur_bit++;
return rc;
}
/* set multiple bits (based on array of bitvals) at current pos */
int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
{
int i, rc;
for (i = 0; i < count; i++) {
rc = bitvec_set_bit(bv, bits[i]);
if (rc)
return rc;
}
return 0;
}
/* set multiple bits (based on numeric value) at current pos */
int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits)
{
int i, rc;
for (i = 0; i < num_bits; i++) {
int bit = 0;
if (ui & (1 << (num_bits - i - 1)))
bit = 1;
rc = bitvec_set_bit(bv, bit);
if (rc)
return rc;
}
return 0;
}
/* pad all remaining bits up to num_bits */
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
{
unsigned int i;
for (i = bv->cur_bit; i <= up_to_bit; i++)
bitvec_set_bit(bv, L);
return 0;
}

View File

@ -51,7 +51,7 @@ enum bs11cfg_state {
STATE_QUERY,
};
static enum bs11cfg_state bs11cfg_state = STATE_NONE;
static char *command;
static char *command, *value;
struct timer_list status_timer;
static const u_int8_t obj_li_attr[] = {
@ -71,6 +71,13 @@ static const char *trx1_password = "1111111111";
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);
@ -533,6 +540,21 @@ static int handle_state_resp(enum abis_bs11_phase state)
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;
@ -627,7 +649,7 @@ int handle_serial_msg(struct msgb *rx_msg)
exit(0);
break;
case NM_MT_BS11_GET_STATE_ACK:
rc = abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
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)
@ -635,7 +657,7 @@ int handle_serial_msg(struct msgb *rx_msg)
break;
case NM_MT_GET_ATTR_RESP:
printf("\n%sATTRIBUTES:\n", obj_name(foh));
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*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;
@ -699,25 +721,27 @@ static void print_help(void)
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\tSpecify delay in milliseconds\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\tQuery the BS-11 about serial number and configuration\n");
printf("\tdisconnect\tDisconnect A-bis link (go into administrative state)\n");
printf("\tresconnect\tReconnect A-bis link (go into normal state)\n");
printf("\trestart\t\tRestart the BTS\n");
printf("\tsoftware\tDownload Software (only in administrative state)\n");
printf("\tcreate-trx1\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
printf("\tdelete-trx1\tDelete objects for TRX1\n");
printf("\tpll-e1-locked\tSet the PLL to be locked to E1 clock\n");
printf("\tpll-standalone\tSet the PLL to be in standalone mode\n");
printf("\toml-tei\tSet OML E1 TS and TEI\n");
printf("\tbport0-star\tSet BPORT0 line config to star\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\tCreate BPORT1 object\n");
printf("\tdelete-bport1\tDelete BPORT1 object\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)
@ -754,7 +778,7 @@ static void handle_options(int argc, char **argv)
serial_port = optarg;
break;
case 'b':
debug_parse_category_mask(optarg);
debug_parse_category_mask(stderr_target, optarg);
break;
case 's':
fname_software = optarg;
@ -784,6 +808,9 @@ static void handle_options(int argc, char **argv)
}
if (optind < argc)
command = argv[optind];
if (optind+1 < argc)
value = argv[optind+1];
}
static int num_sigint;
@ -807,7 +834,12 @@ 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) {

View File

@ -1,6 +1,6 @@
/* A hackish minimal BSC (+MSC +HLR) implementation */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved
*
@ -35,12 +35,18 @@
#include <openbsc/debug.h>
#include <openbsc/e1_input.h>
#include <openbsc/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";
extern int ipacc_rtp_direct;
/* 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);
@ -72,9 +78,9 @@ static void print_help()
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(" -r --reject-cause number The reject cause for LOCATION UPDATING REJECT.\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)
@ -88,7 +94,6 @@ static void handle_options(int argc, char** argv)
{"disable-color", 0, 0, 's'},
{"database", 1, 0, 'l'},
{"authorize-everyone", 0, 0, 'a'},
{"reject-cause", 1, 0, 'r'},
{"pcap", 1, 0, 'p'},
{"timestamp", 0, 0, 'T'},
{"rtp-proxy", 0, 0, 'P'},
@ -106,10 +111,10 @@ static void handle_options(int argc, char** argv)
print_help();
exit(0);
case 's':
debug_use_color(0);
debug_set_use_color(stderr_target, 0);
break;
case 'd':
debug_parse_category_mask(optarg);
debug_parse_category_mask(stderr_target, optarg);
break;
case 'l':
database_name = strdup(optarg);
@ -117,14 +122,11 @@ static void handle_options(int argc, char** argv)
case 'c':
config_file = strdup(optarg);
break;
case 'r':
gsm0408_set_reject_cause(atoi(optarg));
break;
case 'p':
create_pcap_file(optarg);
break;
case 'T':
debug_timestamp(1);
debug_set_print_timestamp(stderr_target, 1);
break;
case 'P':
ipacc_rtp_direct = 0;
@ -136,6 +138,7 @@ static void handle_options(int argc, char** argv)
}
}
extern void *tall_vty_ctx;
static void signal_handler(int signal)
{
fprintf(stdout, "signal %u received\n", signal);
@ -143,6 +146,7 @@ static void signal_handler(int signal)
switch (signal) {
case SIGINT:
bsc_shutdown_net(bsc_gsmnet);
dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
sleep(3);
exit(0);
break;
@ -150,21 +154,53 @@ static void signal_handler(int signal)
/* 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);
@ -184,6 +220,11 @@ int main(int argc, char **argv)
}
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);
@ -191,9 +232,12 @@ int main(int argc, char **argv)
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);
}
}

View File

@ -28,16 +28,15 @@
#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 <openbsc/talloc.h>
/* global pointer to the gsm network data structure */
extern struct gsm_network *bsc_gsmnet;
extern int ipacc_rtp_direct;
static void patch_nm_tables(struct gsm_bts *bts);
static void patch_si_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 */
@ -356,8 +355,10 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
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)
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:
@ -391,38 +392,11 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
break;
case NM_OC_RADIO_CARRIER:
trx = obj;
if (new_state->operational == 1 &&
new_state->availability == NM_AVSTATE_OFF_LINE) {
/* 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, obj_class,
trx->bts->bts_nr, trx->nr, 0xff,
NM_STATE_UNLOCKED);
abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
trx->nr, 0xff);
}
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_BASEB_TRANSC:
trx = container_of(obj, struct gsm_bts_trx, bb_transc);
if (new_state->operational == 1 &&
new_state->availability == NM_AVSTATE_DEPENDENCY) {
abis_nm_chg_adm_state(trx->bts, obj_class,
trx->bts->bts_nr, trx->nr, 0xff,
NM_STATE_UNLOCKED);
abis_nm_opstart(trx->bts, 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;
default:
break;
}
@ -436,8 +410,43 @@ static int sw_activ_rep(struct msgb *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;
}
@ -446,7 +455,7 @@ static int sw_activ_rep(struct msgb *mb)
static int oml_msg_nack(u_int8_t mt)
{
if (mt == NM_MT_SET_BTS_ATTR_NACK) {
fprintf(stderr, "Failed to set BTS attributes. That is fatal. "
LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. "
"Was the bts type and frequency properly specified?\n");
exit(-1);
}
@ -549,7 +558,7 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
trx->nominal_power = 23;
break;
default:
fprintf(stderr, "Unsupported nanoBTS GSM band %s\n",
LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n",
gsm_band_name(trx->bts->band));
break;
}
@ -568,6 +577,7 @@ static void nm_reconfig_bts(struct gsm_bts *bts)
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 */
@ -613,7 +623,7 @@ static void bootstrap_om_bs11(struct gsm_bts *bts)
static void bootstrap_om(struct gsm_bts *bts)
{
fprintf(stdout, "bootstrapping OML for BTS %u\n", bts->nr);
LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
switch (bts->type) {
case GSM_BTS_TYPE_BS11:
@ -623,13 +633,13 @@ static void bootstrap_om(struct gsm_bts *bts)
bootstrap_om_nanobts(bts);
break;
default:
fprintf(stderr, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
LOGP(DNM, LOGL_ERROR, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
}
}
static int shutdown_om(struct gsm_bts *bts)
{
fprintf(stdout, "shutting down OML for BTS %u\n", bts->nr);
LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr);
/* stop sending event reports */
abis_nm_event_reports(bts, 0);
@ -660,219 +670,55 @@ int bsc_shutdown_net(struct gsm_network *net)
return 0;
}
struct bcch_info {
u_int8_t type;
u_int8_t len;
const u_int8_t *data;
};
/*
SYSTEM INFORMATION TYPE 1
Cell channel description
Format-ID bit map 0
CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
RACH Control Parameters
maximum 7 retransmissions
8 slots used to spread transmission
cell not barred for access
call reestablishment not allowed
Access Control Class = 0000
*/
static u_int8_t si1[] = {
/* header */0x55, 0x06, 0x19,
/* ccdesc */0x04 /*0x00*/, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*0x01*/,
/* rach */0xD5, 0x04, 0x00,
/* s1 reset*/0x2B
};
/*
SYSTEM INFORMATION TYPE 2
Neighbour Cells Description
EXT-IND: Carries the complete BA
BA-IND = 0
Format-ID bit map 0
CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
NCC permitted (NCC) = FF
RACH Control Parameters
maximum 7 retransmissions
8 slots used to spread transmission
cell not barred for access
call reestablishment not allowed
Access Control Class = 0000
*/
static u_int8_t si2[] = {
/* header */0x59, 0x06, 0x1A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* ncc */0xFF,
/* rach*/0xD5, 0x04, 0x00
};
/*
SYSTEM INFORMATION TYPE 3
Cell identity = 00001 (1h)
Location area identification
Mobile Country Code (MCC): 001
Mobile Network Code (MNC): 01
Location Area Code (LAC): 00001 (1h)
Control Channel Description
Attach-detach: MSs in the cell are not allowed to apply IMSI attach /detach
0 blocks reserved for access grant
1 channel used for CCCH, with SDCCH
5 multiframes period for PAGING REQUEST
Time-out T3212 = 0
Cell Options BCCH
Power control indicator: not set
MSs shall not use uplink DTX
Radio link timeout = 36
Cell Selection Parameters
Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
max.TX power level MS may use for CCH = 2 <- according to GSM05.05 39dBm (max)
Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
Half rate support (NECI): New establishment causes are not supported
min.RX signal level for MS = 0
RACH Control Parameters
maximum 7 retransmissions
8 slots used to spread transmission
cell not barred for access
call reestablishment not allowed
Access Control Class = 0000
SI 3 Rest Octets (not present)
*/
static u_int8_t si3[] = {
/* header */0x49, 0x06, 0x1B,
/* cell */0x00, 0x01,
/* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
/* desc */0x01, 0x03, 0x00,
/* option*/0x28,
/* selection*/0x62, 0x00,
/* rach */0xD5, 0x04, 0x00,
/* rest */ 0x2B, 0x2B, 0x2B, 0x2B
};
/*
SYSTEM INFORMATION TYPE 4
Location area identification
Mobile Country Code (MCC): 001
Mobile Network Code (MNC): 01
Location Area Code (LAC): 00001 (1h)
Cell Selection Parameters
Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
max.TX power level MS may use for CCH = 2
Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
Half rate support (NECI): New establishment causes are not supported
min.RX signal level for MS = 0
RACH Control Parameters
maximum 7 retransmissions
8 slots used to spread transmission
cell not barred for access
call reestablishment not allowed
Access Control Class = 0000
CBCH Channel Description
Type = SDCCH/4[2]
Timeslot Number: 0
Training Sequence Code: 7h
ARFCN: 1
SI Rest Octets (not present)
*/
static u_int8_t si4[] = {
/* header */0x41, 0x06, 0x1C,
/* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
/* sel */0x62, 0x00,
/* rach*/0xD5, 0x04, 0x00,
/* cbch chan desc */ 0x64, 0x30, 0xE0, HARDCODED_ARFCN/*0x01*/,
/* rest octets */ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B
};
/*
SYSTEM INFORMATION TYPE 5
Neighbour Cells Description
EXT-IND: Carries the complete BA
BA-IND = 0
Format-ID bit map 0
CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*/
static u_int8_t si5[] = {
/* header without l2 len*/0x06, 0x1D,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// SYSTEM INFORMATION TYPE 6
/*
SACCH FILLING
System Info Type: SYSTEM INFORMATION 6
L3 Information (Hex): 06 1E 00 01 xx xx 10 00 01 28 FF
SYSTEM INFORMATION TYPE 6
Cell identity = 00001 (1h)
Location area identification
Mobile Country Code (MCC): 001
Mobile Network Code (MNC): 01
Location Area Code (LAC): 00001 (1h)
Cell Options SACCH
Power control indicator: not set
MSs shall not use uplink DTX on a TCH-F. MS shall not use uplink DTX on TCH-H.
Radio link timeout = 36
NCC permitted (NCC) = FF
*/
static u_int8_t si6[] = {
/* header */0x06, 0x1E,
/* cell id*/ 0x00, 0x01,
/* lai */ 0x00, 0xF1, 0x10, 0x00, 0x01,
/* options */ 0x28,
/* ncc */ 0xFF,
};
static const struct bcch_info bcch_infos[] = {
{
.type = RSL_SYSTEM_INFO_1,
.len = sizeof(si1),
.data = si1,
}, {
.type = RSL_SYSTEM_INFO_2,
.len = sizeof(si2),
.data = si2,
}, {
.type = RSL_SYSTEM_INFO_3,
.len = sizeof(si3),
.data = si3,
}, {
.type = RSL_SYSTEM_INFO_4,
.len = sizeof(si4),
.data = si4,
},
};
static_assert(sizeof(si1) == sizeof(struct gsm48_system_information_type_1), type1)
static_assert(sizeof(si2) == sizeof(struct gsm48_system_information_type_2), type2)
static_assert(sizeof(si3) == sizeof(struct gsm48_system_information_type_3), type3)
static_assert(sizeof(si4) >= sizeof(struct gsm48_system_information_type_4), type4)
static_assert(sizeof(si5) == sizeof(struct gsm48_system_information_type_5), type5)
static_assert(sizeof(si6) >= sizeof(struct gsm48_system_information_type_6), type6)
/* set all system information types */
static int set_system_infos(struct gsm_bts_trx *trx)
{
int i;
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 = 0; i < ARRAY_SIZE(bcch_infos); i++) {
rsl_bcch_info(trx, bcch_infos[i].type,
bcch_infos[i].data,
bcch_infos[i].len);
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));
}
#ifdef GPRS
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);
#endif
}
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si5, sizeof(si5));
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si6, sizeof(si6));
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;
}
/*
@ -906,81 +752,13 @@ static void patch_nm_tables(struct gsm_bts *bts)
nanobts_attr_radio[1] = bts->c0->max_power_red / 2;
}
/*
* Patch the various SYSTEM INFORMATION tables to update
* the LAI
*/
static void patch_si_tables(struct gsm_bts *bts)
{
u_int8_t arfcn_low = bts->c0->arfcn & 0xff;
u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
/* covert the raw packet to the struct */
struct gsm48_system_information_type_1 *type_1 =
(struct gsm48_system_information_type_1*)&si1;
struct gsm48_system_information_type_2 *type_2 =
(struct gsm48_system_information_type_2*)&si2;
struct gsm48_system_information_type_3 *type_3 =
(struct gsm48_system_information_type_3*)&si3;
struct gsm48_system_information_type_4 *type_4 =
(struct gsm48_system_information_type_4*)&si4;
struct gsm48_system_information_type_6 *type_6 =
(struct gsm48_system_information_type_6*)&si6;
struct gsm48_loc_area_id lai;
gsm0408_generate_lai(&lai, bts->network->country_code,
bts->network->network_code,
bts->location_area_code);
/* assign the MCC and MNC */
type_3->lai = lai;
type_4->lai = lai;
type_6->lai = lai;
/* set the CI */
type_3->cell_identity = htons(bts->cell_identity);
type_6->cell_identity = htons(bts->cell_identity);
type_4->data[2] &= 0xf0;
type_4->data[2] |= arfcn_high;
type_4->data[3] = arfcn_low;
/* patch Control Channel Description 10.5.2.11 */
type_3->control_channel_desc = bts->chan_desc;
/* patch TSC */
si4[15] &= ~0xe0;
si4[15] |= (bts->tsc & 7) << 5;
/* patch MS max power for CCH */
type_4->cell_sel_par.ms_txpwr_max_ccch =
ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
/* Set NECI to influence channel request */
type_3->cell_sel_par.neci = bts->network->neci;
type_4->cell_sel_par.neci = bts->network->neci;
if (bts->cell_barred) {
type_1->rach_control.cell_bar = 1;
type_2->rach_control.cell_bar = 1;
type_3->rach_control.cell_bar = 1;
type_4->rach_control.cell_bar = 1;
} else {
type_1->rach_control.cell_bar = 0;
type_2->rach_control.cell_bar = 0;
type_3->rach_control.cell_bar = 0;
type_4->rach_control.cell_bar = 0;
}
}
static void bootstrap_rsl(struct gsm_bts_trx *trx)
{
fprintf(stdout, "bootstrapping RSL for BTS/TRX (%u/%u) "
"using MCC=%u MNC=%u BSIC=%u TSC=%u\n",
trx->bts->nr, trx->nr, bsc_gsmnet->country_code,
bsc_gsmnet->network_code, trx->bts->bsic, trx->bts->tsc);
patch_si_tables(trx->bts);
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);
}
@ -1000,7 +778,7 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
}
break;
case EVT_E1_TEI_DN:
fprintf(stderr, "Lost some E1 TEI link\n");
LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n");
/* FIXME: deal with TEI or L1 link loss */
break;
default:
@ -1013,33 +791,50 @@ static int bootstrap_bts(struct gsm_bts *bts)
switch (bts->band) {
case GSM_BAND_1800:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
fprintf(stderr, "GSM1800 channel must be between 512-885.\n");
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) {
fprintf(stderr, "GSM1900 channel must be between 512-810.\n");
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) {
fprintf(stderr, "GSM900 channel must be between 1-124.\n");
LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-124.\n");
return -EINVAL;
}
break;
default:
fprintf(stderr, "Unsupported frequency band.\n");
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->chan_desc.att = 1;
bts->chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
bts->chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
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;
@ -1062,7 +857,7 @@ int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, int, void *),
telnet_init(bsc_gsmnet, 4242);
rc = vty_read_config_file(config_file);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}

View File

@ -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 <openbsc/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);
}

View File

@ -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 <openbsc/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);
}

40
openbsc/src/bts_unknown.c Normal file
View File

@ -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 <openbsc/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);
}

View File

@ -35,6 +35,29 @@
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)
{
@ -63,6 +86,9 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
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) {
@ -97,6 +123,10 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
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 */
@ -121,6 +151,7 @@ static const u_int8_t subslots_per_pchan[] = {
[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 *
@ -129,14 +160,20 @@ _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)
if (lc->type == GSM_LCHAN_NONE &&
lc->state == LCHAN_S_NONE)
return lc;
}
}
@ -203,9 +240,14 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
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:
fprintf(stderr, "Unknown gsm_chan_t %u\n", type);
LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);
}
if (lchan) {
@ -230,6 +272,8 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
/* 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);
@ -244,6 +288,18 @@ void lchan_free(struct gsm_lchan *lchan)
/* 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 */
@ -262,11 +318,11 @@ int lchan_auto_release(struct gsm_lchan *lchan)
}
/* spoofed? message */
if (lchan->use_count < 0) {
DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count);
}
if (lchan->use_count < 0)
LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
lchan->use_count);
DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
rsl_release_request(lchan, 0);
return 1;
}
@ -312,3 +368,51 @@ struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr)
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);
}

230
openbsc/src/comp128.c Normal file
View File

@ -0,0 +1,230 @@
/*
* COMP128 implementation
*
*
* This code is inspired by original code from :
* Marc Briceno <marc@scard.org>, Ian Goldberg <iang@cs.berkeley.edu>,
* and David Wagner <daw@cs.berkeley.edu>
*
* But it has been fully rewritten from various PDFs found online describing
* the algorithm because the licence of the code referenced above was unclear.
* A comment snippet from the original code is included below, it describes
* where the doc came from and how the algorithm was reverse engineered.
*
*
* (C) 2009 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
* --- SNIP ---
*
* This code derived from a leaked document from the GSM standards.
* Some missing pieces were filled in by reverse-engineering a working SIM.
* We have verified that this is the correct COMP128 algorithm.
*
* The first page of the document identifies it as
* _Technical Information: GSM System Security Study_.
* 10-1617-01, 10th June 1988.
* The bottom of the title page is marked
* Racal Research Ltd.
* Worton Drive, Worton Grange Industrial Estate,
* Reading, Berks. RG2 0SB, England.
* Telephone: Reading (0734) 868601 Telex: 847152
* The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy!
*
* Note: There are three typos in the spec (discovered by
* reverse-engineering).
* First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read
* "z = (2 * x[m] + x[n]) mod 2^(9-j)".
* Second, the "k" loop in the "Form bits from bytes" section is severely
* botched: the k index should run only from 0 to 3, and clearly the range
* on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8,
* to be consistent with the subsequent section).
* Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as
* claimed in the document. (And the document doesn't specify how Kc is
* derived, but that was also easily discovered with reverse engineering.)
* All of these typos have been corrected in the following code.
*
* --- /SNIP ---
*/
#include <string.h>
#include <sys/types.h>
/* The compression tables (just copied ...) */
static const u_int8_t table_0[512] = {
102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188,
109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161,
252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70,
67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116,
247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225,
182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48,
149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176,
250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121,
61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196,
56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231,
174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255,
239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82,
104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5,
57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226,
184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23,
80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119,
177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246,
213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108,
37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59,
26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207,
194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215,
243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245,
90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137,
27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32,
103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172,
197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210,
165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125,
54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192,
170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198,
254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147,
218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154,
159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253,
}, table_1[256] = {
19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43,
27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5,
35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6,
53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20,
90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78,
76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51,
57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67,
12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75,
95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29,
82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114,
82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74,
113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73,
118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83,
104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126,
31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52,
101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35,
}, table_2[128] = {
52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43,
37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18,
55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59,
62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56,
48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61,
29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0,
20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27,
31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7,
}, table_3[64] = {
1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31,
28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9,
20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10,
17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19,
}, table_4[32] = {
15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8,
10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12,
};
static const u_int8_t *_comp128_table[5] = { table_0, table_1, table_2, table_3, table_4 };
static inline void
_comp128_compression_round(u_int8_t *x, int n, const u_int8_t *tbl)
{
int i, j, m, a, b, y, z;
m = 4 - n;
for (i=0; i<(1<<n); i++)
for (j=0; j<(1<<m); j++) {
a = j + i * (2<<m);
b = a + (1<<m);
y = (x[a] + (x[b]<<1)) & ((32<<m)-1);
z = ((x[a]<<1) + x[b]) & ((32<<m)-1);
x[a] = tbl[y];
x[b] = tbl[z];
}
}
static inline void
_comp128_compression(u_int8_t *x)
{
int n;
for (n=0; n<5; n++)
_comp128_compression_round(x, n, _comp128_table[n]);
}
static inline void
_comp128_bitsfrombytes(u_int8_t *x, u_int8_t *bits)
{
int i;
memset(bits, 0x00, 128);
for (i=0; i<128; i++)
if (x[i>>2] & (1<<(3-(i&3))))
bits[i] = 1;
}
static inline void
_comp128_permutation(u_int8_t *x, u_int8_t *bits)
{
int i;
memset(&x[16], 0x00, 16);
for (i=0; i<128; i++)
x[(i>>3)+16] |= bits[(i*17) & 127] << (7-(i&7));
}
void
comp128(u_int8_t *ki, u_int8_t *rand, u_int8_t *sres, u_int8_t *kc)
{
int i;
u_int8_t x[32], bits[128];
/* x[16-31] = RAND */
memcpy(&x[16], rand, 16);
/* Round 1-7 */
for (i=0; i<7; i++) {
/* x[0-15] = Ki */
memcpy(x, ki, 16);
/* Compression */
_comp128_compression(x);
/* FormBitFromBytes */
_comp128_bitsfrombytes(x, bits);
/* Permutation */
_comp128_permutation(x, bits);
}
/* Round 8 (final) */
/* x[0-15] = Ki */
memcpy(x, ki, 16);
/* Compression */
_comp128_compression(x);
/* Output stage */
for (i=0; i<8; i+=2)
sres[i>>1] = x[i]<<4 | x[i+1];
for (i=0; i<12; i+=2)
kc[i>>1] = (x[i + 18] << 6) |
(x[i + 19] << 2) |
(x[i + 20] >> 2);
kc[6] = (x[30]<<6) | (x[31]<<2);
kc[7] = 0;
}

View File

@ -25,6 +25,7 @@
#include <openbsc/db.h>
#include <openbsc/talloc.h>
#include <openbsc/debug.h>
#include <openbsc/statistics.h>
#include <libgen.h>
#include <stdio.h>
@ -117,12 +118,35 @@ static char *create_stmts[] = {
"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;
void db_error_func(dbi_conn conn, void *data)
{
const char *msg;
dbi_conn_error(conn, &msg);
printf("DBI: %s\n", msg);
LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
}
static int check_db_revision(void)
@ -149,11 +173,13 @@ static int check_db_revision(void)
return 0;
}
int db_init(const char *name) {
int db_init(const char *name)
{
dbi_initialize(NULL);
conn = dbi_conn_new("sqlite3");
if (conn==NULL) {
printf("DB: Failed to create connection.\n");
if (conn == NULL) {
LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n");
return 1;
}
@ -186,21 +212,23 @@ out_err:
}
int db_prepare() {
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==NULL) {
printf("DB: Failed to create some table.\n");
if (!result) {
LOGP(DDB, LOGL_ERROR,
"Failed to create some table.\n");
return 1;
}
dbi_result_free(result);
}
if (check_db_revision() < 0) {
fprintf(stderr, "Database schema revision invalid, "
LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
"please update your database schema\n");
return -1;
}
@ -208,7 +236,8 @@ int db_prepare() {
return 0;
}
int db_fini() {
int db_fini()
{
dbi_conn_close(conn);
dbi_shutdown();
@ -219,10 +248,10 @@ int db_fini() {
return 0;
}
struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi)
{
dbi_result result;
struct gsm_subscriber* subscr;
struct gsm_subscriber *subscr;
/* Is this subscriber known in the db? */
subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi);
@ -230,11 +259,10 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
result = dbi_conn_queryf(conn,
"UPDATE Subscriber set updated = datetime('now') "
"WHERE imsi = %s " , imsi);
if (result==NULL) {
printf("DB: failed to update timestamp\n");
} else {
if (!result)
LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n");
else
dbi_result_free(result);
}
return subscr;
}
@ -249,14 +277,13 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
"(%s, datetime('now'), datetime('now')) ",
imsi
);
if (result==NULL) {
printf("DB: Failed to create Subscriber by IMSI.\n");
}
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);
printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
db_subscriber_alloc_exten(subscr);
return subscr;
}
@ -265,7 +292,7 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
{
dbi_result result;
const char *string;
unsigned int cm1;
unsigned char cm1;
const unsigned char *cm2, *cm3;
struct gsm_equipment *equip = &subscr->equipment;
@ -288,7 +315,9 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
if (string)
strncpy(equip->imei, string, sizeof(equip->imei));
cm1 = dbi_result_get_uint(result, "classmark1") & 0xff;
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");
@ -307,6 +336,214 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
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,
@ -353,15 +590,15 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
free(quoted);
break;
default:
printf("DB: Unknown query selector for Subscriber.\n");
LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n");
return NULL;
}
if (result==NULL) {
printf("DB: Failed to query Subscriber.\n");
if (!result) {
LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n");
return NULL;
}
if (!dbi_result_next_row(result)) {
printf("DB: Failed to find the Subscriber. '%u' '%s'\n",
DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n",
field, id);
dbi_result_free(result);
return NULL;
@ -388,7 +625,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
subscr->lac = dbi_result_get_uint(result, "lac");
subscr->authorized = dbi_result_get_uint(result, "authorized");
printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n",
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);
@ -398,7 +635,8 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
return subscr;
}
int db_sync_subscriber(struct gsm_subscriber* subscriber) {
int db_sync_subscriber(struct gsm_subscriber *subscriber)
{
dbi_result result;
char tmsi[14];
char *q_tmsi;
@ -410,6 +648,7 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) {
&q_tmsi);
} else
q_tmsi = strdup("NULL");
result = dbi_conn_queryf(conn,
"UPDATE Subscriber "
"SET updated = datetime('now'), "
@ -424,14 +663,17 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) {
subscriber->authorized,
q_tmsi,
subscriber->lac,
subscriber->imsi
);
subscriber->imsi);
free(q_tmsi);
if (result==NULL) {
printf("DB: Failed to update Subscriber (by IMSI).\n");
if (!result) {
LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n");
return 1;
}
dbi_result_free(result);
return 0;
}
@ -442,15 +684,15 @@ int db_sync_equipment(struct gsm_equipment *equip)
u_int8_t classmark1;
memcpy(&classmark1, &equip->classmark1, sizeof(classmark1));
printf("DB: Sync Equipment IMEI=%s, classmark1=%02x",
DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x",
equip->imei, classmark1);
if (equip->classmark2_len)
printf(", classmark2=%s",
DEBUGPC(DDB, ", classmark2=%s",
hexdump(equip->classmark2, equip->classmark2_len));
if (equip->classmark3_len)
printf(", classmark3=%s",
DEBUGPC(DDB, ", classmark3=%s",
hexdump(equip->classmark3, equip->classmark3_len));
printf("\n");
DEBUGPC(DDB, "\n");
dbi_conn_quote_binary_copy(conn, equip->classmark2,
equip->classmark2_len, &cm2);
@ -470,7 +712,7 @@ int db_sync_equipment(struct gsm_equipment *equip)
free(cm3);
if (!result) {
printf("DB: Failed to update Equipment\n");
LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n");
return -EIO;
}
@ -478,10 +720,12 @@ int db_sync_equipment(struct gsm_equipment *equip)
return 0;
}
int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
dbi_result result=NULL;
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)
@ -492,20 +736,23 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
result = dbi_conn_queryf(conn,
"SELECT * FROM Subscriber "
"WHERE tmsi = %s ",
tmsi_quoted
);
tmsi_quoted);
free(tmsi_quoted);
if (result==NULL) {
printf("DB: Failed to query Subscriber while allocating new TMSI.\n");
if (!result) {
LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
"while allocating new TMSI.\n");
return 1;
}
if (dbi_result_get_numrows(result)){
if (dbi_result_get_numrows(result)) {
dbi_result_free(result);
continue;
}
if (!dbi_result_next_row(result)) {
dbi_result_free(result);
printf("DB: Allocated TMSI %u for IMSI %s.\n", subscriber->tmsi, subscriber->imsi);
DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n",
subscriber->tmsi, subscriber->imsi);
return db_sync_subscriber(subscriber);
}
dbi_result_free(result);
@ -513,9 +760,11 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
return 0;
}
int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
dbi_result result=NULL;
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,
@ -523,8 +772,9 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
"WHERE extension = %i",
try
);
if (result==NULL) {
printf("DB: Failed to query Subscriber while allocating new extension.\n");
if (!result) {
LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
"while allocating new extension.\n");
return 1;
}
if (dbi_result_get_numrows(result)){
@ -538,7 +788,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
dbi_result_free(result);
}
sprintf(subscriber->extension, "%i", try);
printf("DB: Allocated extension %i for IMSI %s.\n", try, subscriber->imsi);
DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi);
return db_sync_subscriber(subscriber);
}
/*
@ -547,7 +797,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
* an error.
*/
int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* token)
int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, u_int32_t *token)
{
dbi_result result;
u_int32_t try;
@ -561,7 +811,8 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
"WHERE subscriber_id = %llu OR token = \"%08X\" ",
subscriber->id, try);
if (!result) {
printf("DB: Failed to query AuthToken while allocating new token.\n");
LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken "
"while allocating new token.\n");
return 1;
}
if (dbi_result_get_numrows(result)) {
@ -581,17 +832,19 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
"(%llu, datetime('now'), \"%08X\") ",
subscriber->id, try);
if (!result) {
printf("DB: Failed to create token %08X for IMSI %s.\n", try, subscriber->imsi);
LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for "
"IMSI %s.\n", try, subscriber->imsi);
return 1;
}
dbi_result_free(result);
*token = try;
printf("DB: Allocated token %08X for IMSI %s.\n", try, subscriber->imsi);
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]) {
int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH])
{
unsigned long long equipment_id, watch_id;
dbi_result result;
@ -603,32 +856,32 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
"(imei, created, updated) "
"VALUES "
"(%s, datetime('now'), datetime('now')) ",
imei
);
if (result==NULL) {
printf("DB: Failed to create Equipment by IMEI.\n");
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) {
printf("DB: New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
}
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==NULL) {
printf("DB: Failed to query Equipment by IMEI.\n");
if (!result) {
LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n");
return 1;
}
if (!dbi_result_next_row(result)) {
printf("DB: Failed to find the Equipment.\n");
LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n");
dbi_result_free(result);
return 1;
}
@ -641,33 +894,33 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
"(subscriber_id, equipment_id, created, updated) "
"VALUES "
"(%llu, %llu, datetime('now'), datetime('now')) ",
subscriber->id, equipment_id
);
if (result==NULL) {
printf("DB: Failed to create EquipmentWatch.\n");
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)) {
if (dbi_result_get_numrows_affected(result))
watch_id = dbi_conn_sequence_last(conn, NULL);
}
dbi_result_free(result);
if (watch_id) {
printf("DB: New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
}
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==NULL) {
printf("DB: Failed to update EquipmentWatch.\n");
subscriber->id, equipment_id);
if (!result) {
LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n");
return 1;
}
dbi_result_free(result);
printf("DB: Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
equipment_id, subscriber->imsi, imei);
}
return 0;
@ -769,8 +1022,9 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
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 id",
"ORDER BY sms.id LIMIT 1",
min_id);
if (!result)
return NULL;
@ -787,7 +1041,34 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
return sms;
}
/* retrieve the next unsent SMS with ID >= min_id */
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;
@ -796,8 +1077,9 @@ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
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 id",
"ORDER BY sms.id LIMIT 1",
subscr->id);
if (!result)
return NULL;
@ -824,7 +1106,7 @@ int db_sms_mark_sent(struct gsm_sms *sms)
"SET sent = datetime('now') "
"WHERE id = %llu", sms->id);
if (!result) {
printf("DB: Failed to mark SMS %llu as sent.\n", sms->id);
LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
return 1;
}
@ -842,7 +1124,8 @@ int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
"SET deliver_attempts = deliver_attempts + 1 "
"WHERE id = %llu", sms->id);
if (!result) {
printf("DB: Failed to inc deliver attempts for SMS %llu.\n", sms->id);
LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
"SMS %llu.\n", sms->id);
return 1;
}
@ -873,3 +1156,24 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr,
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;
}

View File

@ -25,23 +25,84 @@
#include <string.h>
#include <strings.h>
#include <time.h>
#include <errno.h>
#include <openbsc/debug.h>
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS);
/* 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 },
};
const char *get_value_string(const struct value_string *vs, u_int32_t val)
{
int i;
for (i = 0;; i++) {
if (vs[i].value == 0 && vs[i].str == NULL)
break;
if (vs[i].value == val)
return vs[i].str;
}
return "unknown";
}
int get_string_value(const struct value_string *vs, const char *str)
{
int i;
for (i = 0;; i++) {
if (vs[i].value == 0 && vs[i].str == NULL)
break;
if (!strcasecmp(vs[i].str, str))
return vs[i].value;
}
return -EINVAL;
}
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 },
#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
@ -60,52 +121,83 @@ static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
DEBUG_CATEGORY(DHO, "DHO", "", "")
DEBUG_CATEGORY(DDB, "DDB", "", "")
DEBUG_CATEGORY(DDB, "DREF", "", "")
};
static int use_color = 1;
static const struct value_string loglevel_strs[] = {
{ 0, "EVERYTHING" },
{ 1, "DEBUG" },
{ 3, "INFO" },
{ 5, "NOTICE" },
{ 7, "ERROR" },
{ 8, "FATAL" },
{ 0, NULL },
};
void debug_use_color(int color)
int debug_parse_level(const char *lvl)
{
use_color = color;
return get_string_value(loglevel_strs, lvl);
}
static int print_timestamp = 0;
void debug_timestamp(int enable)
int debug_parse_category(const char *category)
{
print_timestamp = enable;
}
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.
* category1:category2:category3
* The format can be this: category1:category2:category3
* or category1,2:category2,3:...
*/
void debug_parse_category_mask(const char *_mask)
void debug_parse_category_mask(struct debug_target* target, const char *_mask)
{
unsigned int new_mask = 0;
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) {
if (strcasecmp(debug_info[i].name, category_token) == 0)
new_mask |= debug_info[i].number;
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);
debug_mask = new_mask;
}
const char* color(int subsys)
static const char* color(int subsys)
{
int i = 0;
for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) {
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
if (debug_info[i].number == subsys)
return debug_info[i].color;
}
@ -113,35 +205,111 @@ const char* color(int subsys)
return "";
}
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
static void _output(struct debug_target *target, unsigned int subsys, char *file, int line,
int cont, const char *format, va_list ap)
{
va_list ap;
FILE *outfd = stderr;
char col[30];
char sub[30];
char tim[30];
char buf[4096];
char final[4096];
if (!(debug_mask & subsys))
return;
/* prepare the data */
col[0] = '\0';
sub[0] = '\0';
tim[0] = '\0';
buf[0] = '\0';
va_start(ap, format);
fprintf(outfd, "%s", color(subsys));
/* 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 (print_timestamp) {
if (target->print_timestamp) {
char *timestr;
time_t tm;
tm = time(NULL);
timestr = ctime(&tm);
timestr[strlen(timestr)-1] = '\0';
fprintf(outfd, "%s ", timestr);
snprintf(tim, sizeof(tim), "%s ", timestr);
tim[sizeof(tim)-1] = '\0';
}
fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line);
snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
sub[sizeof(sub)-1] = '\0';
}
vfprintf(outfd, format, ap);
fprintf(outfd, "\033[0;m");
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);
}
fflush(outfd);
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];
@ -163,3 +331,122 @@ char *hexdump(const unsigned char *buf, int len)
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");
}

View File

@ -10,6 +10,7 @@
#include <openbsc/misdn.h>
#include <openbsc/ipaccess.h>
#include <openbsc/talloc.h>
#include <openbsc/debug.h>
#define SAPI_L2ML 0
#define SAPI_OML 62
@ -25,7 +26,7 @@ int e1_reconfig_ts(struct gsm_bts_trx_ts *ts)
struct e1inp_line *line;
struct e1inp_ts *e1_ts;
printf("e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
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;
@ -87,7 +88,7 @@ int e1_reconfig_bts(struct gsm_bts *bts)
struct e1inp_sign_link *oml_link;
struct gsm_bts_trx *trx;
printf("e1_reconfig_bts(%u)\n", bts->nr);
DEBUGP(DMI, "e1_reconfig_bts(%u)\n", bts->nr);
if (!e1_link->e1_ts)
return -EINVAL;

View File

@ -52,6 +52,7 @@
#include <openbsc/trau_frame.h>
#include <openbsc/trau_mux.h>
#include <openbsc/talloc.h>
#include <openbsc/signal.h>
#include <openbsc/misdn.h>
#define NUM_E1_TS 32
@ -233,10 +234,16 @@ int abis_rsl_sendmsg(struct msgb *msg)
msg->l2h = msg->data;
if (!msg->trx || !msg->trx->rsl_link) {
fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
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;
@ -263,7 +270,7 @@ int _abis_nm_sendmsg(struct msgb *msg)
msg->l2h = msg->data;
if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
LOGP(DRSL, LOGL_ERROR, "nm_sendmsg: msg->trx == NULL\n");
return -EINVAL;
}
@ -305,7 +312,7 @@ int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
subch_demux_init(&ts->trau.demux);
break;
default:
fprintf(stderr, "unsupported E1 timeslot type %u\n",
LOGP(DMI, LOGL_ERROR, "unsupported E1 timeslot type %u\n",
ts->type);
return -EINVAL;
}
@ -430,10 +437,12 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
link = e1inp_lookup_sign_link(ts, tei, sapi);
if (!link) {
fprintf(stderr, "didn't find signalling link for "
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;
@ -445,7 +454,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
break;
default:
ret = -EINVAL;
fprintf(stderr, "unknown link type %u\n", link->type);
LOGP(DMI, LOGL_ERROR, "unknown link type %u\n", link->type);
break;
}
break;
@ -454,7 +463,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
break;
default:
ret = -EINVAL;
fprintf(stderr, "unknown TS type %u\n", ts->type);
LOGP(DMI, LOGL_ERROR, "unknown TS type %u\n", ts->type);
break;
}
@ -491,7 +500,7 @@ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
msgb_put(msg, 40);
break;
default:
fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
LOGP(DMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type);
return NULL;
}
return msg;
@ -523,8 +532,24 @@ 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);
}

View File

@ -32,6 +32,7 @@
#include <openbsc/db.h>
#include <openbsc/msgb.h>
#include <openbsc/bitvec.h>
#include <openbsc/tlv.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
@ -49,6 +50,7 @@
#include <openbsc/talloc.h>
#include <openbsc/transaction.h>
#include <openbsc/ussd.h>
#include <openbsc/silent_call.h>
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@ -56,8 +58,6 @@
void *tall_locop_ctx;
extern int ipacc_rtp_direct;
static const struct tlv_definition rsl_att_tlvdef = {
.def = {
[GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV },
@ -166,63 +166,6 @@ static const char *rr_cause_name(u_int8_t cause)
return strbuf;
}
static void parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
int len)
{
memset(rep, 0, sizeof(*rep));
if (data[0] & 0x80)
rep->flags |= MEAS_REP_F_BA1;
if (data[0] & 0x40)
rep->flags |= MEAS_REP_F_DTX;
if ((data[1] & 0x40) == 0x00)
rep->flags |= MEAS_REP_F_VALID;
rep->rxlev_full = data[0] & 0x3f;
rep->rxlev_sub = data[1] & 0x3f;
rep->rxqual_full = (data[3] >> 4) & 0x7;
rep->rxqual_sub = (data[3] >> 1) & 0x7;
rep->num_cell = data[4] >> 6 | ((data[3] & 0x01) << 2);
if (rep->num_cell < 1)
return;
/* an encoding nightmare in perfection */
rep->cell[0].rxlev = data[4] & 0x3f;
rep->cell[0].bcch_freq = data[5] >> 2;
rep->cell[0].bsic = ((data[5] & 0x03) << 3) | (data[6] >> 5);
if (rep->num_cell < 2)
return;
rep->cell[1].rxlev = ((data[6] & 0x1f) << 1) | (data[7] >> 7);
rep->cell[1].bcch_freq = (data[7] >> 2) & 0x1f;
rep->cell[1].bsic = ((data[7] & 0x03) << 4) | (data[8] >> 4);
if (rep->num_cell < 3)
return;
rep->cell[2].rxlev = ((data[8] & 0x0f) << 2) | (data[9] >> 6);
rep->cell[2].bcch_freq = (data[9] >> 1) & 0x1f;
rep->cell[2].bsic = ((data[9] & 0x01) << 6) | (data[10] >> 3);
if (rep->num_cell < 4)
return;
rep->cell[3].rxlev = ((data[10] & 0x07) << 3) | (data[11] >> 5);
rep->cell[3].bcch_freq = data[11] & 0x1f;
rep->cell[3].bsic = data[12] >> 2;
if (rep->num_cell < 5)
return;
rep->cell[4].rxlev = ((data[12] & 0x03) << 4) | (data[13] >> 4);
rep->cell[4].bcch_freq = ((data[13] & 0xf) << 1) | (data[14] >> 7);
rep->cell[4].bsic = (data[14] >> 1) & 0x3f;
if (rep->num_cell < 6)
return;
rep->cell[5].rxlev = ((data[14] & 0x01) << 5) | (data[15] >> 3);
rep->cell[5].bcch_freq = ((data[15] & 0x07) << 2) | (data[16] >> 6);
rep->cell[5].bsic = data[16] & 0x3f;
}
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);
@ -234,12 +177,6 @@ struct gsm_lai {
u_int16_t lac;
};
static int reject_cause = 0;
void gsm0408_set_reject_cause(int cause)
{
reject_cause = cause;
}
static u_int32_t new_callref = 0x80000001;
static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
@ -298,11 +235,18 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
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;
}
@ -433,18 +377,29 @@ static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
bcap->coding = (lv[1] & 0x10) >> 4;
bcap->radio = (lv[1] & 0x60) >> 5;
i = 1;
s = 0;
while(!(lv[i] & 0x80)) {
i++; /* octet 3a etc */
if (in_len < i)
return 0;
bcap->speech_ver[s++] = lv[i] & 0x0f;
bcap->speech_ver[s] = -1; /* end of list */
if (i == 2) /* octet 3a */
bcap->speech_ctm = (lv[i] & 0x20) >> 5;
if (s == 7) /* maximum speech versions + end of list */
return 0;
if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
i = 1;
s = 0;
while(!(lv[i] & 0x80)) {
i++; /* octet 3a etc */
if (in_len < i)
return 0;
bcap->speech_ver[s++] = lv[i] & 0x0f;
bcap->speech_ver[s] = -1; /* end of list */
if (i == 2) /* octet 3a */
bcap->speech_ctm = (lv[i] & 0x20) >> 5;
if (s == 7) /* maximum speech versions + end of list */
return 0;
}
} else {
i = 1;
while (!(lv[i] & 0x80)) {
i++; /* octet 3a etc */
if (in_len < i)
return 0;
/* ignore them */
}
/* FIXME: implement OCTET 4+ parsing */
}
return 0;
@ -455,21 +410,24 @@ static int encode_bearer_cap(struct msgb *msg, int lv_only,
const struct gsm_mncc_bearer_cap *bcap)
{
u_int8_t lv[32 + 1];
int i, s;
int i = 1, s;
lv[1] = bcap->transfer;
lv[1] |= bcap->mode << 3;
lv[1] |= bcap->coding << 4;
lv[1] |= bcap->radio << 5;
i = 1;
for (s = 0; bcap->speech_ver[s] >= 0; s++) {
i++; /* octet 3a etc */
lv[i] = bcap->speech_ver[s];
if (i == 2) /* octet 3a */
lv[i] |= bcap->speech_ctm << 5;
if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
for (s = 0; bcap->speech_ver[s] >= 0; s++) {
i++; /* octet 3a etc */
lv[i] = bcap->speech_ver[s];
if (i == 2) /* octet 3a */
lv[i] |= bcap->speech_ctm << 5;
}
lv[i] |= 0x80; /* last IE of octet 3 etc */
} else {
/* FIXME: implement OCTET 4+ encoding */
}
lv[i] |= 0x80; /* last IE of octet 3 etc */
lv[0] = i;
if (lv_only)
@ -861,6 +819,7 @@ static int encode_more(struct msgb *msg)
/* 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;
@ -871,7 +830,12 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
gh->data[0] = cause;
DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
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);
}
@ -884,7 +848,6 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
struct gsm48_hdr *gh;
struct gsm48_loc_area_id *lai;
u_int8_t *mid;
int ret;
msg->lchan = lchan;
@ -901,12 +864,9 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
ret = gsm48_sendmsg(msg, NULL);
counter_inc(bts->network->stats.loc_upd_resp.accept);
/* send MM INFO with network name */
ret = gsm48_tx_mm_info(lchan);
return ret;
return gsm48_sendmsg(msg, NULL);
}
/* Transmit Chapter 9.2.10 Identity Request */
@ -940,6 +900,8 @@ static int mm_rx_id_resp(struct msgb *msg)
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 */
@ -971,9 +933,10 @@ static int mm_rx_id_resp(struct msgb *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, reject_cause);
gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
lchan_auto_release(lchan);
}
@ -1019,6 +982,20 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
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.
@ -1198,20 +1175,19 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
}
/* Section 9.2.2 */
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand)
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\n");
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;
/* Key Sequence: FIXME fixed to 0 */
ar->key_seq = 0;
ar->key_seq = key_seq;
/* 16 bytes RAND parameters */
if (rand)
@ -1302,6 +1278,8 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
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);
@ -1315,7 +1293,9 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
if (!msg->lchan->subscr)
msg->lchan->subscr = subscr;
else if (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);
}
@ -1341,6 +1321,8 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
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,
@ -1362,8 +1344,7 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
if (subscr) {
subscr_update(subscr, msg->trx->bts,
GSM_SUBSCRIBER_UPDATE_DETACHED);
DEBUGP(DMM, "Subscriber: %s\n",
subscr->name ? subscr->name : subscr->imsi);
DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr));
subscr->equipment.classmark1 = idi->classmark1;
db_sync_equipment(&subscr->equipment);
@ -1372,6 +1353,12 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
} 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;
}
@ -1407,7 +1394,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
case GSM48_MT_MM_TMSI_REALL_COMPL:
DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
msg->lchan->subscr ?
msg->lchan->subscr->imsi :
subscr_name(msg->lchan->subscr) :
"unknown subscriber");
break;
case GSM48_MT_MM_IMSI_DETACH_IND:
@ -1420,7 +1407,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n");
break;
default:
fprintf(stderr, "Unknown GSM 04.08 MM msg type 0x%02x\n",
LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n",
gh->msg_type);
break;
}
@ -1528,26 +1515,13 @@ static int gsm48_rx_rr_status(struct msgb *msg)
static int gsm48_rx_rr_meas_rep(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
static struct gsm_meas_rep meas_rep;
struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan);
DEBUGP(DMEAS, "MEASUREMENT REPORT ");
parse_meas_rep(&meas_rep, gh->data, payload_len);
if (meas_rep.flags & MEAS_REP_F_DTX)
DEBUGPC(DMEAS, "DTX ");
if (meas_rep.flags & MEAS_REP_F_BA1)
DEBUGPC(DMEAS, "BA1 ");
if (!(meas_rep.flags & MEAS_REP_F_VALID))
DEBUGPC(DMEAS, "NOT VALID ");
else
DEBUGPC(DMEAS, "FULL(lev=%u, qual=%u) SUB(lev=%u, qual=%u) ",
meas_rep.rxlev_full, meas_rep.rxqual_full, meas_rep.rxlev_sub,
meas_rep.rxqual_sub);
DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", meas_rep.num_cell);
/* FIXME: put the results somwhere */
/* 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;
}
@ -1569,6 +1543,33 @@ static int gsm48_rx_rr_app_info(struct msgb *msg)
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)
@ -1602,9 +1603,15 @@ static int gsm0408_rcv_rr(struct msgb *msg)
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:
fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
gh->msg_type);
LOGP(DRR, LOGL_NOTICE, "Unimplemented "
"GSM 04.08 RR msg type 0x%02x\n", gh->msg_type);
break;
}
@ -1812,13 +1819,16 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
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;
struct gsm_bts_trx_ts *ts;
int rc;
struct gsm_network *net;
struct gsm_trans *trans;
if (subsys != SS_ABISIP)
return 0;
@ -1827,56 +1837,21 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
if (ipacc_rtp_direct)
return 0;
ts = lchan->ts;
switch (signal) {
case S_ABISIP_CRCX_ACK:
/* the BTS has successfully bound a TCH to a local ip/port,
* which means we can connect our UDP socket to it */
if (ts->abis_ip.rtp_socket) {
rtp_socket_free(ts->abis_ip.rtp_socket);
ts->abis_ip.rtp_socket = NULL;
}
ts->abis_ip.rtp_socket = rtp_socket_create();
if (!ts->abis_ip.rtp_socket)
goto out_err;
rc = rtp_socket_connect(ts->abis_ip.rtp_socket,
ts->abis_ip.bound_ip,
ts->abis_ip.bound_port);
if (rc < 0)
goto out_err;
break;
case S_ABISIP_DLCX_IND:
/* the BTS tells us a RTP stream has been disconnected */
if (ts->abis_ip.rtp_socket) {
rtp_socket_free(ts->abis_ip.rtp_socket);
ts->abis_ip.rtp_socket = NULL;
/* 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;
out_err:
/* FIXME: do something */
return 0;
}
/* bind rtp proxy to local IP/port and tell BTS to connect to it */
static int ipacc_connect_proxy_bind(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
struct rtp_socket *rs = ts->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),
ts->abis_ip.conn_id,
/* FIXME: use RTP payload of bound socket, not BTS*/
ts->abis_ip.rtp_payload2);
return rc;
}
/* map two ipaccess RTP streams onto each other */
@ -1884,7 +1859,6 @@ 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;
struct gsm_bts_trx_ts *ts;
int rc;
DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n",
@ -1895,33 +1869,31 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
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 = ipacc_connect_proxy_bind(lchan);
rc = rsl_ipacc_mdcx_to_rtpsock(lchan);
if (rc < 0)
return rc;
rc = ipacc_connect_proxy_bind(remote_lchan);
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->ts->abis_ip.rtp_socket,
remote_lchan->ts->abis_ip.rtp_socket);
rtp_socket_proxy(lchan->abis_ip.rtp_socket,
remote_lchan->abis_ip.rtp_socket);
} else {
/* directly connect TCH RTP streams to each other */
ts = remote_lchan->ts;
rc = rsl_ipacc_mdcx(lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
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;
ts = lchan->ts;
rc = rsl_ipacc_mdcx(remote_lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
remote_lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
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:
@ -1929,8 +1901,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
break;
default:
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
rc = -EINVAL;
break;
return -EINVAL;
}
return 0;
@ -1952,45 +1923,61 @@ static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
return tch_map(trans1->lchan, trans2->lchan);
}
/* enable receive of channels to upqueue */
static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
/* 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, data->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;
// todo IPACCESS
if (enable)
return trau_recv_lchan(trans->lchan, data->callref);
return trau_mux_unmap(NULL, data->callref);
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;
}
/* send a frame to channel */
static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
{
struct gsm_trans *trans;
/* Find callref */
trans = trans_find_by_callref(net, frame->callref);
if (!trans)
return -EIO;
if (!trans->lchan)
return 0;
if (trans->lchan->type != GSM_LCHAN_TCH_F &&
trans->lchan->type != GSM_LCHAN_TCH_H)
return 0;
// todo IPACCESS
return trau_send_lchan(trans->lchan,
(struct decoded_trau_frame *)frame->data);
}
static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
{
DEBUGP(DCC, "-> STATUS ENQ\n");
@ -2163,6 +2150,10 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
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);
@ -2711,6 +2702,9 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
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,
@ -3217,11 +3211,30 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
case MNCC_BRIDGE:
return tch_bridge(net, arg);
case MNCC_FRAME_DROP:
return tch_recv(net, arg, 0);
return tch_recv_mncc(net, data->callref, 0);
case MNCC_FRAME_RECV:
return tch_recv(net, arg, 1);
case GSM_TRAU_FRAME:
return tch_frame(net, arg);
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));
@ -3487,6 +3500,9 @@ 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:
@ -3503,15 +3519,15 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
break;
case GSM48_PDISC_MM_GPRS:
case GSM48_PDISC_SM_GPRS:
fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02x\n",
pdisc);
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:
fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02x\n",
pdisc);
LOGP(DRLL, LOGL_NOTICE, "Unknown "
"GSM 04.08 discriminator 0x%02x\n", pdisc);
break;
}

View File

@ -258,6 +258,11 @@ static const struct chreq chreq_type_neci1[] = {
{ 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 */
@ -270,6 +275,11 @@ static const struct chreq chreq_type_neci0[] = {
{ 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[] = {
@ -286,6 +296,9 @@ static const enum gsm_chan_t ctype_by_chreq[] = {
[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[] = {
@ -295,13 +308,16 @@ static const enum gsm_chreq_reason_t reason_by_chreq[] = {
[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_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)
@ -324,7 +340,7 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
if ((ra & chr->mask) == chr->val)
return ctype_by_chreq[chr->type];
}
fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
return GSM_LCHAN_SDCCH;
}
@ -347,7 +363,7 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, in
if ((ra & chr->mask) == chr->val)
return reason_by_chreq[chr->type];
}
fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
return GSM_CHREQ_REASON_OTHER;
}
@ -459,7 +475,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
if (!msg->lchan->subscr) {
msg->lchan->subscr = subscr;
} else if (msg->lchan->subscr != subscr) {
DEBUGP(DRR, "<- Channel already owned by someone else?\n");
LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n");
subscr_put(subscr);
return -EINVAL;
} else {
@ -472,7 +488,9 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
sig_data.bts = msg->lchan->ts->trx->bts;
sig_data.lchan = msg->lchan;
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
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);
@ -503,18 +521,62 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
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 *lchan, u_int8_t power_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));
u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
msg->lchan = lchan;
msg->lchan = dest_lchan;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_ASS_CMD;
@ -526,17 +588,16 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
* the chan_desc. But as long as multi-slot configurations
* are not used we seem to be fine.
*/
ass->chan_desc.chan_nr = lchan2chan_nr(lchan);
ass->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
ass->chan_desc.h0.h = 0;
ass->chan_desc.h0.arfcn_high = arfcn >> 8;
ass->chan_desc.h0.arfcn_low = arfcn & 0xff;
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) {
DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
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;
@ -576,7 +637,8 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t 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) {
DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
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;
@ -609,7 +671,7 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
if (mod->mode != msg->lchan->tch_mode) {
DEBUGP(DRR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
msg->lchan->tch_mode, mod->mode);
return -1;
}
@ -642,3 +704,82 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
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;
}

View File

@ -173,7 +173,7 @@ static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
DEBUGP(DSMS, "TX: CP-ACK ");
break;
case GSM411_MT_CP_ERROR:
DEBUGP(DSMS, "TX: CP-ACK ");
DEBUGP(DSMS, "TX: CP-ERROR ");
break;
}
@ -198,115 +198,6 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
}
static time_t gsm340_scts(u_int8_t *scts);
static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
{
u_int8_t vp;
unsigned long minutes;
time_t expires;
time_t now;
switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
/* Chapter 9.2.3.12.1 */
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;
break;
case GSM340_TP_VPF_ABSOLUTE:
/* Chapter 9.2.3.12.2 */
expires = gsm340_scts(sms_vp);
now = mktime(gmtime(NULL));
if (expires <= now)
minutes = 0;
else
minutes = (expires-now)/60;
break;
case GSM340_TP_VPF_ENHANCED:
/* Chapter 9.2.3.12.3 */
/* FIXME: implementation */
DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
break;
case GSM340_TP_VPF_NONE:
/* Default validity: two days */
minutes = 24 * 60 * 2;
break;
}
return minutes;
}
/* 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)
DEBUGP(DSMS, "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) {
DEBUGP(DSMS, "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 = 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;
}
/* Turn int into semi-octet representation: 98 => 0x89 */
static u_int8_t bcdify(u_int8_t value)
{
@ -324,11 +215,11 @@ static u_int8_t unbcdify(u_int8_t value)
u_int8_t ret;
if ((value & 0x0F) > 9 || (value >> 4) > 9)
DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
LOGP(DSMS, LOGL_ERROR,
"unbcdify got too big nibble: 0x%02X\n", value);
ret = (value&0x0F)*10;
if (ret > 90)
ret += value>>4;
ret += value>>4;
return ret;
}
@ -370,6 +261,179 @@ static time_t gsm340_scts(u_int8_t *scts)
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 = 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)
@ -437,7 +501,8 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
memcpy(smsp, sms->user_data, sms->user_data_len);
break;
default:
DEBUGP(DSMS, "Unhandled Data Coding Scheme: 0x%02X\n", sms->data_coding_scheme);
LOGP(DSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n",
sms->data_coding_scheme);
break;
}
@ -457,6 +522,8 @@ static int gsm340_rx_tpdu(struct msgb *msg)
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;
@ -475,7 +542,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
/* length in bytes of the destination address */
da_len_bytes = 2 + *smsp/2 + *smsp%2;
if (da_len_bytes > 12) {
DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
LOGP(DSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n");
rc = GSM411_RP_CAUSE_SEMANT_INC_MSG;
goto out;
}
@ -499,14 +566,17 @@ static int gsm340_rx_tpdu(struct msgb *msg)
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:
DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
sms_vpf);
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++;
@ -524,16 +594,17 @@ static int gsm340_rx_tpdu(struct msgb *msg)
}
}
DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
"PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
"UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref,
gsms->protocol_id, gsms->data_coding_scheme,
gsms->dest_addr, gsms->user_data_len,
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->sender = subscr_get(msg->lchan->subscr);
gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
dispatch_signal(SS_SMS, 0, gsms);
@ -542,6 +613,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
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;
}
@ -552,11 +624,11 @@ static int gsm340_rx_tpdu(struct msgb *msg)
break;
case GSM340_SMS_COMMAND_MS2SC:
case GSM340_SMS_DELIVER_REP_MS2SC:
DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms_mti);
LOGP(DSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti);
rc = GSM411_RP_CAUSE_IE_NOTEXIST;
break;
default:
DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti);
LOGP(DSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti);
rc = GSM411_RP_CAUSE_IE_NOTEXIST;
break;
}
@ -586,7 +658,7 @@ static int gsm411_send_rp_error(struct gsm_trans *trans,
msgb_tv_put(msg, 1, cause);
DEBUGP(DSMS, "TX: SMS RP ERROR, cause %d (%s)\n", 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);
@ -602,10 +674,11 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
int rc = 0;
if (src_len && src)
DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n");
LOGP(DSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n");
if (!dst_len || !dst || !tpdu_len || !tpdu) {
DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
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;
@ -660,13 +733,13 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
* transmitted */
if (!trans->sms.is_mt) {
DEBUGP(DSMS, "RX RP-ACK on a MO transfer ?\n");
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) {
DEBUGP(DSMS, "RX RP-ACK but no sms in transaction?!?\n");
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);
}
@ -679,14 +752,16 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
sms_free(sms);
trans->sms.sms = NULL;
/* free the transaction here */
trans_free(trans);
/* 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
/* free the transaction here */
trans_free(trans);
/* release channel if done */
if (!sms)
rsl_release_request(msg->lchan, trans->sms.link_id);
return 0;
@ -695,6 +770,7 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
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];
@ -703,11 +779,12 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
* successfully receive the SMS. We need to investigate
* the cause and take action depending on it */
DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d:%d (%s)\n", cause_len, cause,
get_value_string(rp_cause_strs, cause));
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) {
DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
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);
@ -715,7 +792,8 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
}
if (!sms) {
DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
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,
@ -728,7 +806,9 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
* 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;
@ -790,7 +870,7 @@ static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh,
rc = gsm411_rx_rp_error(msg, trans, rp_data);
break;
default:
DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
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;
@ -821,7 +901,7 @@ static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
struct msgb *msg = gsm411_msgb_alloc();
u_int8_t *causep;
DEBUGP(DSMS, "TX CP-ERROR, cause %d (%s)\n", cause,
LOGP(DSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause,
get_value_string(cp_cause_strs, cause));
causep = msgb_put(msg, 1);
@ -844,11 +924,11 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
return -EIO;
/* FIXME: send some error message */
DEBUGP(DSMS, "trans_id=%x ", gh->proto_discr >> 4);
DEBUGP(DSMS, "trans_id=%x ", transaction_id);
trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
transaction_id);
if (!trans) {
DEBUGPC(DSMS, "(unknown) ");
DEBUGPC(DSMS, "(new) ");
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
@ -868,6 +948,33 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
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
@ -940,10 +1047,14 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
struct gsm_trans *trans;
u_int8_t *data, *rp_ud_len;
u_int8_t msg_ref = 42;
u_int8_t transaction_id;
int transaction_id;
int rc;
transaction_id = 4; /* FIXME: we always use 4 for now */
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;
@ -953,7 +1064,7 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
DEBUGP(DSMS, "No memory for trans\n");
LOGP(DSMS, LOGL_ERROR, "No memory for trans\n");
/* FIXME: send some error message */
return -ENOMEM;
}
@ -1001,6 +1112,8 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
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 */
}

View File

@ -27,21 +27,12 @@
#include <openbsc/gsm_data.h>
#include <openbsc/talloc.h>
#include <openbsc/abis_nm.h>
#include <openbsc/statistics.h>
void *tall_bsc_ctx;
const char *get_value_string(const struct value_string *vs, u_int32_t val)
{
int i;
for (i = 0;; i++) {
if (vs[i].value == 0 && vs[i].str == NULL)
break;
if (vs[i].value == val)
return vs[i].str;
}
return "unknown";
}
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)
@ -91,7 +82,7 @@ static const char *lchan_names[] = {
[GSM_LCHAN_UNKNOWN] = "UNKNOWN",
};
const char *gsm_lchan_name(enum gsm_chan_t c)
const char *gsm_lchant_name(enum gsm_chan_t c)
{
if (c >= ARRAY_SIZE(lchan_names))
return "INVALID";
@ -99,6 +90,20 @@ const char *gsm_lchan_name(enum gsm_chan_t c)
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",
@ -115,6 +120,29 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
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);
@ -125,6 +153,7 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
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];
@ -144,6 +173,9 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
}
}
if (trx->nr != 0)
trx->nominal_power = bts->c0->nominal_power;
llist_add_tail(&trx->list, &bts->trx_list);
return trx;
@ -153,19 +185,38 @@ 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;
@ -197,11 +248,49 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
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;
@ -222,6 +311,25 @@ struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num)
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;
@ -239,6 +347,15 @@ struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num)
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)",
@ -247,6 +364,16 @@ char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
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",
@ -270,6 +397,17 @@ const char *btstype2str(enum gsm_bts_type type)
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,
@ -361,3 +499,63 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
return gsm_auth_policy_names[policy];
}
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;
}

View File

@ -34,6 +34,14 @@
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)
{
@ -100,12 +108,15 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
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:

View File

@ -137,7 +137,7 @@ static void subscr_free(struct gsm_subscriber *subscr)
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
{
subscr->use_count++;
DEBUGP(DCC, "subscr %s usage increases usage to: %d\n",
DEBUGP(DREF, "subscr %s usage increases usage to: %d\n",
subscr->extension, subscr->use_count);
return subscr;
}
@ -145,7 +145,7 @@ struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
{
subscr->use_count--;
DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n",
DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n",
subscr->extension, subscr->use_count);
if (subscr->use_count <= 0)
subscr_free(subscr);

View File

@ -90,8 +90,10 @@ int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
return 0;
else if (dbm < 5)
return 19;
else
else {
/* we are guaranteed to have (5 <= dbm < 39) */
return 2 + ((39 - dbm) / 2);
}
break;
case GSM_BAND_1800:
if (dbm >= 36)
@ -100,16 +102,24 @@ int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
return 30;
else if (dbm >= 32)
return 31;
else
else if (dbm == 31)
return 0;
else {
/* we are guaranteed to have (0 <= dbm < 31) */
return (30 - dbm) / 2;
}
break;
case GSM_BAND_1900:
if (dbm >= 33)
return 30;
else if (dbm >= 32)
return 31;
else
else if (dbm == 31)
return 0;
else {
/* we are guaranteed to have (0 <= dbm < 31) */
return (30 - dbm) / 2;
}
break;
}
return -EINVAL;
@ -150,6 +160,28 @@ int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
return -EINVAL;
}
/* According to TS 08.05 Chapter 8.1.4 */
int rxlev2dbm(u_int8_t rxlev)
{
if (rxlev > 63)
rxlev = 63;
return -110 + rxlev;
}
/* According to TS 08.05 Chapter 8.1.4 */
u_int8_t dbm2rxlev(int dbm)
{
int rxlev = dbm + 110;
if (rxlev > 63)
rxlev = 63;
else if (rxlev < 0)
rxlev = 0;
return rxlev;
}
void generate_backtrace()
{
int i, nptrs;

View File

@ -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 <openbsc/msgb.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/meas_rep.h>
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
#include <openbsc/handover.h>
#include <openbsc/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);
}

View File

@ -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 <openbsc/msgb.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/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 <openbsc/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);
}

View File

@ -213,7 +213,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
&site_id, &bts_id, &trx_id);
bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id);
if (!bts) {
DEBUGP(DINP, "Unable to find BTS configuration for "
LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for "
" %u/%u/%u, disconnecting\n", site_id, bts_id,
trx_id);
return -EIO;
@ -238,6 +238,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
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);
@ -247,9 +248,8 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
return 0;
}
/* FIXME: this is per BTS */
static int oml_up = 0;
static int rsl_up = 0;
#define OML_UP 0x0001
#define RSL_UP 0x0002
/*
* read one ipa message from the socket
@ -270,7 +270,8 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
hh = (struct ipaccess_head *) msg->data;
ret = recv(bfd->fd, msg->data, 3, 0);
if (ret < 0) {
fprintf(stderr, "recv error %s\n", strerror(errno));
if (errno != EAGAIN)
LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
msgb_free(msg);
*error = ret;
return NULL;
@ -287,7 +288,7 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
len = ntohs(hh->len);
ret = recv(bfd->fd, msg->l2h, len, 0);
if (ret < len) {
fprintf(stderr, "short read!\n");
LOGP(DINP, LOGL_ERROR, "short read!\n");
msgb_free(msg);
*error = -EIO;
return NULL;
@ -310,7 +311,13 @@ static int handle_ts1_read(struct bsc_fd *bfd)
msg = ipaccess_read_msg(bfd, &error);
if (!msg) {
if (error == 0) {
fprintf(stderr, "BTS disappeared, dead socket\n");
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);
@ -340,7 +347,8 @@ static int handle_ts1_read(struct bsc_fd *bfd)
link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
if (!link) {
printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto);
LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
"hh->proto=0x%02x\n", hh->proto);
msgb_free(msg);
return -EIO;
}
@ -348,21 +356,21 @@ static int handle_ts1_read(struct bsc_fd *bfd)
switch (link->type) {
case E1INP_SIGN_RSL:
if (!rsl_up) {
if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) {
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
rsl_up = 1;
msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr);
}
ret = abis_rsl_rcvmsg(msg);
break;
case E1INP_SIGN_OML:
if (!oml_up) {
if (!(msg->trx->bts->ip_access.flags & OML_UP)) {
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
oml_up = 1;
msg->trx->bts->ip_access.flags |= OML_UP;
}
ret = abis_nm_rcvmsg(msg);
break;
default:
DEBUGP(DMI, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
msgb_free(msg);
break;
}
@ -462,7 +470,7 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
if (what & BSC_FD_WRITE)
rc = handle_ts1_write(bfd);
} else
fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type);
return rc;
}
@ -492,7 +500,8 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
perror("accept");
return ret;
}
DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr));
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) {
@ -514,7 +523,7 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
bfd->when = BSC_FD_READ;
ret = bsc_register_fd(bfd);
if (ret < 0) {
fprintf(stderr, "could not register FD\n");
LOGP(DINP, LOGL_ERROR, "could not register FD\n");
close(bfd->fd);
talloc_free(line);
return ret;
@ -550,13 +559,13 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
perror("accept");
return bfd->fd;
}
DEBUGP(DINP, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
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) {
fprintf(stderr, "could not register FD\n");
LOGP(DINP, LOGL_ERROR, "could not register FD\n");
close(bfd->fd);
talloc_free(bfd);
return ret;
@ -587,7 +596,7 @@ static int make_sock(struct bsc_fd *bfd, u_int16_t port,
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
if (ret < 0) {
fprintf(stderr, "could not bind l2 socket %s\n",
LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n",
strerror(errno));
return -EIO;
}
@ -623,7 +632,7 @@ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
if (ret < 0) {
fprintf(stderr, "could not connect socket\n");
LOGP(DINP, LOGL_ERROR, "could not connect socket\n");
close(bfd->fd);
return ret;
}
@ -657,12 +666,12 @@ int ipaccess_setup(struct gsm_network *gsmnet)
e1h->gsmnet = gsmnet;
/* Listen for OML connections */
ret = make_sock(&e1h->listen_fd, 3002, listen_fd_cb);
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, 3003, rsl_listen_fd_cb);
ret = make_sock(&e1h->rsl_listen_fd, IPA_TCP_PORT_RSL, rsl_listen_fd_cb);
return ret;
}

View File

@ -1,6 +1,8 @@
/* 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
@ -24,6 +26,8 @@
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
@ -39,6 +43,7 @@
#include <openbsc/abis_nm.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/talloc.h>
static struct gsm_network *gsmnet;
@ -48,6 +53,21 @@ 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;
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 };
@ -68,6 +88,28 @@ static int ipacc_msg_nack(u_int8_t mt)
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;
@ -149,14 +191,25 @@ static int test_rep(void *_msg)
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
u_int8_t *msg_type;
struct ipacc_ack_signal_data *ipacc_data;
switch (signal) {
case S_NM_IPACC_NACK:
msg_type = signal_data;
return ipacc_msg_nack(*msg_type);
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;
}
@ -164,6 +217,84 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
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;
@ -182,7 +313,7 @@ static void bootstrap_om(struct gsm_bts *bts)
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, buf, 3+len+1);
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1);
}
if (prim_oml_ip) {
struct in_addr ia;
@ -205,7 +336,8 @@ static void bootstrap_om(struct gsm_bts *bts)
*cur++ = 0;
*cur++ = 0;
printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
abis_nm_ipaccess_set_nvattr(bts, buf, 3+len);
oml_state = 1;
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
}
if (nv_mask) {
len = 4;
@ -219,13 +351,14 @@ static void bootstrap_om(struct gsm_bts *bts)
*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, buf, 3+len);
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
}
if (restart) {
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)
@ -257,17 +390,146 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
{
if (evt == EVT_STATECHG_OPER &&
obj_class == NM_OC_RADIO_CARRIER &&
new_state->availability == 3 &&
net_listen_testnr) {
new_state->availability == 3) {
struct gsm_bts_trx *trx = obj;
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));
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 void 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;
}
/* verify the file */
if (fstat(fd, &stat) == -1) {
perror("Can not stat the file");
return;
}
ipaccess_analyze_file(fd, stat.st_size, 0, entry);
if (close(fd) != 0) {
perror("Close failed.\n");
return;
}
/* 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);
}
}
talloc_free(tall_firm_ctx);
}
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;
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);
if (close(fd) != 0) {
perror("Close failed.\n");
return;
}
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("\n\n");
}
printf("\n\n");
}
talloc_free(tall_firm_ctx);
}
static void print_usage(void)
{
printf("Usage: ipaccess-config\n");
@ -282,6 +544,8 @@ static void print_help(void)
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");
}
int main(int argc, char **argv)
@ -289,6 +553,15 @@ 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");
@ -304,9 +577,11 @@ int main(int argc, char **argv)
{ "help", 0, 0, 'h' },
{ "listen", 1, 0, 'l' },
{ "stream-id", 1, 0, 's' },
{ "software", 1, 0, 'd' },
{ "firmware", 1, 0, 'f' },
};
c = getopt_long(argc, argv, "u:o:rn:l:hs:", long_options,
c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:", long_options,
&option_index);
if (c == -1)
@ -336,8 +611,14 @@ int main(int argc, char **argv)
break;
case 's':
stream_id = atoi(optarg);
printf("foo: %d\n", stream_id);
break;
case 'd':
software = strdup(optarg);
find_sw_load_params(optarg);
break;
case 'f':
analyze_firmware(optarg);
exit(0);
case 'h':
print_usage();
print_help();
@ -356,6 +637,10 @@ int main(int argc, char **argv)
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);

View File

@ -0,0 +1,123 @@
/* 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 <openbsc/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) == 160, _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;
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 (!firmware_header)
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);
/* this semantic appears to be only the case for 0x0000 */
if (firmware_header->more_more_magic != 0)
return -1;
if (ntohs(firmware_header->part_length) % PART_LENGTH != 0) {
fprintf(stderr, "The part length seems to be wrong.\n");
return -1;
}
/* look into each firmware now */
for (i = 0; i < ntohs(firmware_header->part_length) / PART_LENGTH; ++i) {
struct sdp_header_entry entry;
struct sdp_header_item *header_entry;
unsigned int offset = base_offset + sizeof(struct sdp_firmware);
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;
}
/* 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;
}
header_entry = talloc_zero(header, struct sdp_header_item);
header_entry->header_entry = entry;
llist_add(&header_entry->entry, &header->header_list);
ipaccess_analyze_file(fd, ntohl(entry.length), offset, list);
}
return 0;
}

File diff suppressed because it is too large Load Diff

114
openbsc/src/meas_rep.c Normal file
View File

@ -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;
}

19
openbsc/src/mgcp.cfg Normal file
View File

@ -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

View File

@ -0,0 +1,228 @@
/* 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 <arpa/inet.h>
#include <openbsc/debug.h>
#include <openbsc/msgb.h>
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
#include <openbsc/select.h>
#include <openbsc/mgcp.h>
#include <openbsc/telnet_interface.h>
#include <vty/command.h>
#include <vty/vty.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 int source_port = 2427;
static const char *source_addr = "0.0.0.0";
static struct bsc_fd bfd;
static int first_request = 1;
static char *config_file = "mgcp.cfg";
/* used by msgb and mgcp */
void *tall_bsc_ctx = NULL;
unsigned int rtp_base_port = RTP_PORT_DEFAULT;
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(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 bsc_vty_init(struct gsm_network *dummy)
{
cmd_init(1);
vty_init();
mgcp_vty_init();
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);
handle_options(argc, argv);
telnet_init(&dummy_network, 4243);
rc = mgcp_parse_config(config_file, &dummy_network);
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(source_port);
inet_aton(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;
}

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <openbsc/gsm_04_08.h>
@ -29,6 +30,8 @@
#include <openbsc/mncc.h>
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
#include <openbsc/transaction.h>
#include <openbsc/rtp_proxy.h>
void *tall_call_ctx;
@ -82,10 +85,9 @@ static struct mncc_names {
{"MNCC_FRAME_DROP", 0x0202},
{"MNCC_LCHAN_MODIFY", 0x0203},
{"GSM_TRAU_FRAME", 0x0300},
{"GSM_TCH_FRAME", 0x0300},
{NULL, 0}
};
{NULL, 0} };
static LLIST_HEAD(call_list);
@ -136,19 +138,36 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type,
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))) {
memset(&mncc, 0, sizeof(struct gsm_mncc));
mncc.callref = call->callref;
mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
mncc_send(call->net, MNCC_REJ_REQ, &mncc);
free_call(call);
return 0;
goto out_reject;
}
llist_add_tail(&remote->entry, &call_list);
remote->net = call->net;
@ -179,6 +198,11 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type,
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,
@ -210,7 +234,8 @@ static int mncc_notify_ind(struct gsm_call *call, int msg_type,
static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
struct gsm_mncc *connect)
{
struct gsm_mncc connect_ack;
struct gsm_mncc connect_ack, frame_recv;
struct gsm_network *net = call->net;
struct gsm_call *remote;
u_int32_t refs[2];
@ -231,7 +256,26 @@ static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
refs[0] = call->callref;
refs[1] = call->remote_ref;
DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
return mncc_send(call->net, MNCC_BRIDGE, refs);
/* 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,
@ -279,6 +323,28 @@ static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *re
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;
@ -320,8 +386,15 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
}
DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
get_mncc_name(msg_type));
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:
@ -382,8 +455,12 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
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:
DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref);
break;
}

View File

@ -26,8 +26,9 @@
#include <openbsc/msgb.h>
#include <openbsc/gsm_data.h>
#include <openbsc/talloc.h>
#include <openbsc/debug.h>
static void *tall_msgb_ctx;
void *tall_msgb_ctx;
struct msgb *msgb_alloc(u_int16_t size, const char *name)
{
@ -35,8 +36,10 @@ struct msgb *msgb_alloc(u_int16_t size, const char *name)
msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name);
if (!msg)
if (!msg) {
LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n");
return NULL;
}
msg->data_len = size;
msg->len = 0;
@ -93,8 +96,3 @@ void msgb_reset(struct msgb *msg)
msg->l3h = NULL;
msg->smsh = NULL;
}
static __attribute__((constructor)) void on_dso_load_trau_msgb(void)
{
tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 1, "msgb");
}

View File

@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
timer t3101 10
timer t3113 60
bts 0
type bs11
band GSM900
@ -29,7 +31,7 @@ network
phys_chan_config CCCH+SDCCH4
e1 line 0 timeslot 1 sub-slot full
timeslot 1
phys_chan_config SDCCH8
phys_chan_config TCH/F
e1 line 0 timeslot 2 sub-slot 1
timeslot 2
phys_chan_config TCH/F

View File

@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
timer t3101 10
timer t3113 60
bts 0
type bs11
band GSM900

View File

@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
timer t3101 10
timer t3113 60
bts 0
type bs11
band GSM900
@ -29,7 +31,7 @@ network
phys_chan_config CCCH+SDCCH4
e1 line 0 timeslot 1 sub-slot full
timeslot 1
phys_chan_config SDCCH8
phys_chan_config TCH/F
e1 line 0 timeslot 2 sub-slot 1
timeslot 2
phys_chan_config TCH/F

View File

@ -11,6 +11,8 @@ network
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

View File

@ -55,7 +55,7 @@ static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *
int blocks;
unsigned int group;
ccch_conf = bts->chan_desc.ccch_conf;
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);
@ -212,7 +212,9 @@ static void paging_T3113_expired(void *data)
cbfn = req->cbfn;
paging_remove_request(&req->bts->paging, req);
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
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);
@ -239,7 +241,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
req->cbfn_param = data;
req->T3113.cb = paging_T3113_expired;
req->T3113.data = req;
bsc_schedule_timer(&req->T3113, T3113_VALUE);
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))
@ -254,6 +256,8 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
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;
@ -261,6 +265,11 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
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 */
@ -269,6 +278,9 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
return rc;
} while (1);
if (num_pages == 0)
counter_inc(network->stats.paging.detached);
return num_pages;
}

396
openbsc/src/rest_octets.c Normal file
View File

@ -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 <openbsc/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 (0) {
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;
}

View File

@ -1,4 +1,4 @@
/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
*
@ -28,9 +28,42 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/chan_alloc.h>
/* RRLP MS based position request */
/* 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)
{
@ -44,8 +77,7 @@ static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
lchan = lchan_for_subscr(subscr);
if (!lchan)
break;
gsm48_send_rr_app_info(lchan, 0x00, sizeof(ms_based_pos_req),
ms_based_pos_req);
send_rrlp_req(lchan);
break;
}
return 0;
@ -57,11 +89,11 @@ static int paging_sig_cb(unsigned int subsys, unsigned int signal,
struct paging_signal_data *psig_data = signal_data;
switch (signal) {
case S_PAGING_COMPLETED:
case S_PAGING_SUCCEEDED:
/* A subscriber has attached. */
gsm48_send_rr_app_info(psig_data->lchan, 0x00,
sizeof(ms_based_pos_req),
ms_based_pos_req);
send_rrlp_req(psig_data->lchan);
break;
case S_PAGING_EXPIRED:
break;
}
return 0;

View File

@ -24,6 +24,10 @@
#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 <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
@ -57,6 +61,214 @@ struct rtcp_hdr {
#define RTCP_IE_CNAME 1
/* according to RFC 3550 */
struct rtp_hdr {
u_int8_t csrc_count:4,
extension:1,
padding:1,
version:2;
u_int8_t payload_type:7,
marker:1;
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,
@ -123,10 +335,16 @@ static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
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) {
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),
@ -148,6 +366,7 @@ 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)
@ -184,13 +403,40 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
break;
case RTP_RECV_UPSTREAM:
case RTP_NONE:
/* FIXME: other cases */
DEBUGP(DMUX, "unhandled action: %d\n", rs->rx_action);
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 rc;
return 0;
out_free:
msgb_free(msg);
@ -211,7 +457,7 @@ static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
written = write(rss->bfd.fd, msg->data, msg->len);
if (written < msg->len) {
perror("short write");
LOGP(DMIB, LOGL_ERROR, "short write");
msgb_free(msg);
return -EIO;
}
@ -420,6 +666,23 @@ int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
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;

View File

@ -95,14 +95,20 @@ restart:
llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
int flags = 0;
if (FD_ISSET(ufd->fd, &readset))
if (FD_ISSET(ufd->fd, &readset)) {
flags |= BSC_FD_READ;
FD_CLR(ufd->fd, &readset);
}
if (FD_ISSET(ufd->fd, &writeset))
if (FD_ISSET(ufd->fd, &writeset)) {
flags |= BSC_FD_WRITE;
FD_CLR(ufd->fd, &writeset);
}
if (FD_ISSET(ufd->fd, &exceptset))
if (FD_ISSET(ufd->fd, &exceptset)) {
flags |= BSC_FD_EXCEPT;
FD_CLR(ufd->fd, &exceptset);
}
if (flags) {
work = 1;

View File

@ -34,6 +34,7 @@
#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)
{
@ -53,6 +54,7 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int 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);
@ -69,15 +71,58 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
return rc;
}
int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data)
/* 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, RSL_CHANNEED_TCH_F,
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;
@ -86,7 +131,10 @@ int gsm_silent_call_stop(struct gsm_subscriber *subscr)
if (!lchan)
return -EINVAL;
/* FIXME: did we actually establish a silent call for this guy? */
/* did we actually establish a silent call for this guy? */
if (!lchan->silent_call)
return -EINVAL;
put_lchan(lchan);
return 0;

70
openbsc/src/statistics.c Normal file
View File

@ -0,0 +1,70 @@
/* utility routines for keeping some statistics */
/* (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/signal.h>
#include <openbsc/linuxlist.h>
#include <openbsc/talloc.h>
#include <openbsc/statistics.h>
#include <openbsc/db.h>
#include <openbsc/timer.h>
static LLIST_HEAD(counters);
void *tall_ctr_ctx;
struct counter *counter_alloc(const char *name)
{
struct counter *ctr = talloc_zero(tall_ctr_ctx, struct counter);
if (!ctr)
return NULL;
ctr->name = name;
llist_add_tail(&ctr->list, &counters);
return ctr;
}
void counter_free(struct counter *ctr)
{
llist_del(&ctr->list);
talloc_free(ctr);
}
int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data)
{
struct counter *ctr;
int rc = 0;
llist_for_each_entry(ctr, &counters, list) {
rc = handle_counter(ctr, data);
if (rc < 0)
return rc;
}
return rc;
}

View File

@ -0,0 +1,469 @@
/* 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 <openbsc/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);
}
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 = 0,
},
.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);
gsm0408_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;
gsm0408_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);
gsm0408_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 = 1000,
.t3192 = 1000,
.drx_timer_max = 1,
.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 = 0,
.si_change_field = 0,
.pbcch_present = 0,
{
.no_pbcch = {
.rac = 0,
.spgc_ccch_sup = 0,
.net_ctrl_ord = 0,
.prio_acc_thr = 0,
},
},
};
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;
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)
{
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;
}

View File

@ -105,6 +105,15 @@
#endif
#endif
#ifdef __APPLE__
/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */
size_t strnlen(const char *s, size_t n)
{
const char *p = (const char *)memchr(s, 0, n);
return(p ? p-s : n);
}
#endif
/* this null_context is only used if talloc_enable_leak_report() or
talloc_enable_leak_report_full() is called, otherwise it remains
NULL

View File

@ -1,6 +1,7 @@
#include <openbsc/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;
@ -13,9 +14,11 @@ 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");
@ -29,4 +32,5 @@ void talloc_ctx_init(void)
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");
}

View File

@ -35,6 +35,7 @@
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
#include <openbsc/debug.h>
#include <vty/buffer.h>
@ -71,7 +72,7 @@ void telnet_init(struct gsm_network *network, int port) {
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
perror("Telnet interface socket creation failed");
LOGP(DNM, LOGL_ERROR, "Telnet interface socket creation failed\n");
return;
}
@ -83,12 +84,12 @@ void telnet_init(struct gsm_network *network, int port) {
sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
perror("Telnet interface failed to bind");
LOGP(DNM, LOGL_ERROR, "Telnet interface failed to bind\n");
return;
}
if (listen(fd, 0) < 0) {
perror("Telnet interface failed to listen");
LOGP(DNM, LOGL_ERROR, "Telnet interface failed to listen\n");
return;
}
@ -101,9 +102,9 @@ static void print_welcome(int fd) {
int ret;
static char *msg =
"Welcome to the OpenBSC Control interface\n"
"Copyright (C) 2008, 2009 Harald Welte\n"
"Copyright (C) 2008-2010 Harald Welte\n"
"Contributions by Daniel Willmann, Jan Lübbe, "
"Stefan Schmidt, Holger Freyther\n\n"
"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 "
@ -119,6 +120,12 @@ int telnet_close_client(struct bsc_fd *fd) {
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;
@ -154,7 +161,7 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
if (new_connection < 0) {
perror("telnet accept failed");
LOGP(DNM, LOGL_ERROR, "telnet accept failed\n");
return -1;
}
@ -171,8 +178,10 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
print_welcome(new_connection);
connection->vty = vty_create(new_connection, connection);
if (!connection->vty)
if (!connection->vty) {
LOGP(DNM, LOGL_ERROR, "couldn't create VTY\n");
return -1;
}
return 0;
}

View File

@ -149,6 +149,19 @@ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
return num_parsed;
}
/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
{
int i;
for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
if (src->def[i].type == TLV_TYPE_NONE)
continue;
if (dst->def[i].type == TLV_TYPE_NONE)
dst->def[i] = src->def[i];
}
}
static __attribute__((constructor)) void on_dso_load_tlv(void)
{
int i;

View File

@ -60,10 +60,10 @@ static int token_subscr_cb(unsigned int subsys, unsigned int signal,
struct gsm_sms *sms;
int rc = 0;
if (subscr->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
if (signal != S_SUBSCR_ATTACHED)
return 0;
if (signal != S_SUBSCR_ATTACHED)
if (subscr->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
return 0;
if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) {
@ -103,7 +103,7 @@ unauth:
if (lchan) {
u_int8_t auth_rand[16];
/* kick the subscriber off the network */
gsm48_tx_mm_auth_req(lchan, auth_rand);
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);
@ -139,7 +139,7 @@ static int token_sms_cb(unsigned int subsys, unsigned int signal,
lchan = lchan_for_subscr(sms->receiver);
if (lchan) {
/* kick the subscriber off the network */
gsm48_tx_mm_auth_req(lchan, auth_rand);
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);

View File

@ -119,7 +119,7 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
struct gsm_network *net = subscr->net;
struct gsm_trans *trans;
unsigned int used_tid_bitmask = 0;
int i;
int i, j, h;
if (ti_flag)
ti_flag = 0x8;
@ -133,10 +133,39 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
used_tid_bitmask |= (1 << trans->transaction_id);
}
for (i = 0; i <= 7; i++) {
if ((used_tid_bitmask & (1 << (i | ti_flag))) == 0)
return i | ti_flag;
/* 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;
}

View File

@ -107,11 +107,13 @@ int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
case TRAU_FT_DATA_DOWN:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
DEBUGP(DMUX, "can't decode unimplemented TRAU Frame Type 0x%02x\n", cbits5);
LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU "
"Frame Type 0x%02x\n", cbits5);
return -1;
break;
default:
DEBUGP(DMUX, "can't decode unknown TRAU Frame Type 0x%02x\n", cbits5);
LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU "
"Frame Type 0x%02x\n", cbits5);
return -1;
break;
}
@ -162,11 +164,13 @@ int trau_frame_up2down(struct decoded_trau_frame *fr)
case TRAU_FT_DATA_UP:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
"0x%02x\n", cbits5);
return -1;
break;
default:
DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
"0x%02x\n", cbits5);
return -1;
break;
}
@ -224,11 +228,13 @@ int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
case TRAU_FT_DATA_DOWN:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
"0x%02x\n", cbits5);
return -1;
break;
default:
DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
"0x%02x\n", cbits5);
return -1;
break;
}

View File

@ -32,6 +32,19 @@
#include <openbsc/debug.h>
#include <openbsc/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;
@ -56,8 +69,10 @@ int trau_mux_map(const struct gsm_e1_subslot *src,
struct map_entry *me;
me = talloc(tall_map_ctx, struct map_entry);
if (!me)
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",
@ -142,6 +157,8 @@ lookup_trau_upqueue(const struct gsm_e1_subslot *src)
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)
@ -151,8 +168,6 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
struct subch_mux *mx;
struct upqueue_entry *ue;
struct msgb *msg;
struct gsm_trau_frame *frame;
int rc;
/* decode TRAU, change it to downlink, re-encode */
@ -161,19 +176,44 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
return rc;
if (!dst_e1_ss) {
struct msgb *msg;
struct gsm_data_frame *frame;
unsigned char *data;
int i, j, k, l, o;
/* frame shall be sent to upqueue */
if (!(ue = lookup_trau_upqueue(src_e1_ss)))
return -EINVAL;
if (!ue->callref)
return -EINVAL;
msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf),
"TRAU");
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_trau_frame *)msg->data;
frame->msg_type = GSM_TRAU_FRAME;
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;
memcpy(frame->data, &tf, sizeof(tf));
msgb_enqueue(&ue->net->upqueue, msg);
return 0;
@ -219,17 +259,53 @@ int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
return 0;
}
int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
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;
encode_trau_frame(trau_bits_out, tf);
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,

View File

@ -49,6 +49,8 @@ Boston, MA 02111-1307, USA. */
#include <openbsc/gsm_subscriber.h>
#include <openbsc/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;
@ -173,7 +175,7 @@ char *argv_concat(const char **argv, int argc, int shift)
len += strlen(argv[i]) + 1;
if (!len)
return NULL;
p = str = _talloc_zero(tall_vty_ctx, len, "arvg_concat");
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])));
@ -275,7 +277,7 @@ vector cmd_make_strvec(const char *string)
*cp != '\0')
cp++;
strlen = cp - start;
token = _talloc_zero(tall_vty_ctx, strlen + 1, "make_strvec");
token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
memcpy(token, start, strlen);
*(token + strlen) = '\0';
vector_set(strvec, token);
@ -331,7 +333,7 @@ static char *cmd_desc_str(const char **string)
cp++;
strlen = cp - start;
token = _talloc_zero(tall_vty_ctx, strlen + 1, "cmd_desc_str");
token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
memcpy(token, start, strlen);
*(token + strlen) = '\0';
@ -402,11 +404,11 @@ static vector cmd_make_descvec(const char *string, const char *descstr)
len = cp - sp;
token = _talloc_zero(tall_vty_ctx, len + 1, "cmd_make_descvec");
token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
memcpy(token, sp, len);
*(token + len) = '\0';
desc = talloc_zero(tall_vty_ctx, struct desc);
desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
desc->cmd = token;
desc->str = cmd_desc_str(&dp);
@ -1804,7 +1806,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
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_ctx, string));
vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
}
}
}
@ -1845,7 +1847,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
if (len < lcd) {
char *lcdstr;
lcdstr = _talloc_zero(tall_vty_ctx, lcd + 1,
lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
"complete-lcdstr");
memcpy(lcdstr, matchvec->index[0], lcd);
lcdstr[lcd] = '\0';
@ -2463,13 +2465,13 @@ DEFUN(config_write_file,
config_file = host.config;
config_file_sav =
_talloc_zero(tall_vty_ctx,
_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_ctx, strlen(config_file) + 8,
config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
"config_file_tmp");
sprintf(config_file_tmp, "%s.XXXXXX", config_file);
@ -2650,7 +2652,7 @@ DEFUN(config_hostname,
if (host.name)
talloc_free(host.name);
host.name = talloc_strdup(tall_vty_ctx, argv[0]);
host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@ -2685,7 +2687,7 @@ DEFUN(config_password, password_cmd,
host.password = NULL;
if (host.password_encrypt)
talloc_free(host.password_encrypt);
host.password_encrypt = talloc_strdup(tall_vty_ctx, argv[1]);
host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
return CMD_SUCCESS;
} else {
vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
@ -2708,10 +2710,10 @@ DEFUN(config_password, password_cmd,
if (host.encrypt) {
if (host.password_encrypt)
talloc_free(host.password_encrypt);
host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0]));
host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
} else
#endif
host.password = talloc_strdup(tall_vty_ctx, argv[0]);
host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@ -2744,7 +2746,7 @@ ALIAS(config_password, password_text_cmd,
if (host.enable_encrypt)
talloc_free(host.enable_encrypt);
host.enable_encrypt = talloc_strdup(tall_vty_ctx, argv[1]);
host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
return CMD_SUCCESS;
} else {
@ -2769,10 +2771,10 @@ ALIAS(config_password, password_text_cmd,
if (host.encrypt) {
if (host.enable_encrypt)
talloc_free(host.enable_encrypt);
host.enable_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0]));
host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
} else
#endif
host.enable = talloc_strdup(tall_vty_ctx, argv[0]);
host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@ -2816,12 +2818,12 @@ DEFUN(service_password_encrypt,
if (host.password) {
if (host.password_encrypt)
talloc_free(host.password_encrypt);
host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(host.password));
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_ctx, zencrypt(host.enable));
host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
}
return CMD_SUCCESS;
@ -3095,7 +3097,7 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel)
if (host.logfile)
talloc_free(host.logfile);
host.logfile = talloc_strdup(tall_vty_ctx, fname);
host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
return CMD_SUCCESS;
}
@ -3285,7 +3287,7 @@ DEFUN(banner_motd_file,
{
if (host.motdfile)
talloc_free(host.motdfile);
host.motdfile = talloc_strdup(tall_vty_ctx, argv[0]);
host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@ -3313,7 +3315,7 @@ DEFUN(no_banner_motd,
/* Set config filename. Called from vty.c */
void host_config_set(const char *filename)
{
host.config = talloc_strdup(tall_vty_ctx, filename);
host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
}
void install_default(enum node_type node)

View File

@ -27,10 +27,12 @@
#include <openbsc/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_ctx, struct _vector);
vector v = talloc_zero(tall_vty_vec_ctx, struct _vector);
if (!v)
return NULL;
@ -40,7 +42,7 @@ vector vector_init(unsigned int size)
v->alloced = size;
v->active = 0;
v->index = _talloc_zero(tall_vty_ctx, sizeof(void *) * size,
v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size,
"vector_init:index");
if (!v->index) {
talloc_free(v);
@ -68,7 +70,7 @@ void vector_free(vector v)
vector vector_copy(vector v)
{
unsigned int size;
vector new = talloc_zero(tall_vty_ctx, struct _vector);
vector new = talloc_zero(tall_vty_vec_ctx, struct _vector);
if (!new)
return NULL;
@ -76,7 +78,7 @@ vector vector_copy(vector v)
new->alloced = v->alloced;
size = sizeof(void *) * (v->alloced);
new->index = _talloc_zero(tall_vty_ctx, size, "vector_copy:index");
new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index");
if (!new->index) {
talloc_free(new);
return NULL;
@ -92,7 +94,7 @@ void vector_ensure(vector v, unsigned int num)
if (v->alloced > num)
return;
v->index = talloc_realloc_size(tall_vty_ctx, v->index,
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;

View File

@ -236,6 +236,8 @@ int vty_out(struct vty *vty, const char *format, ...)
talloc_free(p);
}
vty_event(VTY_WRITE, vty->fd, vty);
return len;
}
@ -1631,6 +1633,8 @@ extern void *tall_bsc_ctx;
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();

View File

@ -33,8 +33,11 @@
#include <openbsc/e1_input.h>
#include <openbsc/abis_nm.h>
#include <openbsc/gsm_utils.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/meas_rep.h>
#include <openbsc/db.h>
#include <openbsc/talloc.h>
#include <openbsc/telnet_interface.h>
static struct gsm_network *gsmnet;
@ -74,8 +77,30 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
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);
@ -85,10 +110,21 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
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",
@ -120,13 +156,26 @@ static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l)
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);
if (bts->cell_barred)
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",
@ -143,6 +192,10 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
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]",
@ -213,6 +266,7 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
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);
@ -234,13 +288,22 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
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);
if (bts->chan_desc.t3212)
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->chan_desc.t3212 * 10, VTY_NEWLINE);
bts->si_common.chan_desc.t3212 * 10, VTY_NEWLINE);
vty_out(vty, " channel allocator %s%s",
bts->chan_alloc_reverse ? "descending" : "ascending",
VTY_NEWLINE);
if (bts->cell_barred)
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",
@ -273,8 +336,37 @@ static int config_write_net(struct vty *vty)
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;
}
@ -354,24 +446,15 @@ DEFUN(show_trx,
static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
{
struct in_addr ia;
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)) {
ia.s_addr = ts->abis_ip.bound_ip;
vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
inet_ntoa(ia), ts->abis_ip.bound_port,
ts->abis_ip.rtp_payload2, ts->abis_ip.conn_id,
VTY_NEWLINE);
} else {
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,
@ -429,8 +512,12 @@ DEFUN(show_ts,
return CMD_SUCCESS;
}
void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
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)
@ -443,23 +530,77 @@ void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
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_lchan_name(lchan->type),
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);
vty_out(vty, " Use Count: %u%s", lchan->use_count, VTY_NEWLINE);
vty_out(vty, " BS Power %u, MS Power %u%s", lchan->bs_power,
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
@ -712,6 +853,244 @@ DEFUN(show_paging,
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",
@ -782,6 +1161,16 @@ DEFUN(cfg_net_auth_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)",
@ -801,6 +1190,120 @@ DEFUN(cfg_net_neci,
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,
@ -821,8 +1324,11 @@ DEFUN(cfg_bts,
} else
bts = gsm_bts_num(gsmnet, bts_nr);
if (!bts)
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;
@ -836,13 +1342,11 @@ DEFUN(cfg_bts_type,
"Set the BTS type\n")
{
struct gsm_bts *bts = vty->index;
int rc;
bts->type = parse_btstype(argv[0]);
if (is_ipaccess_bts(bts)) {
/* Set the default OML Stream ID to 0xff */
bts->oml_tei = 0xff;
}
rc = gsm_set_bts_type(bts, parse_btstype(argv[0]));
if (rc < 0)
return CMD_WARNING;
return CMD_SUCCESS;
}
@ -909,6 +1413,7 @@ DEFUN(cfg_bts_lac,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_tsc,
cfg_bts_tsc_cmd,
"training_sequence_code <0-255>",
@ -1024,13 +1529,33 @@ DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd,
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->cell_barred = atoi(argv[0]);
bts->si_common.rach_control.cell_bar = atoi(argv[0]);
return CMD_SUCCESS;
}
@ -1046,13 +1571,35 @@ DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd,
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->chan_desc.t3212 = atoi(argv[0]) / 10;
bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 10;
return CMD_SUCCESS;
}
@ -1106,6 +1653,18 @@ DEFUN(cfg_trx_arfcn,
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>",
@ -1158,6 +1717,17 @@ DEFUN(cfg_trx_rsl_e1_tei,
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,
@ -1230,6 +1800,17 @@ int bsc_vty_init(struct gsm_network *net)
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);
@ -1239,8 +1820,29 @@ int bsc_vty_init(struct gsm_network *net)
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);
@ -1256,18 +1858,24 @@ int bsc_vty_init(struct gsm_network *net)
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_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);

View File

@ -40,9 +40,7 @@
#include <openbsc/db.h>
#include <openbsc/talloc.h>
#include <openbsc/signal.h>
/* forward declarations */
void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr);
#include <openbsc/debug.h>
static struct gsm_network *gsmnet;
@ -74,6 +72,33 @@ static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
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,
@ -97,6 +122,55 @@ DEFUN(cfg_subscr,
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,
@ -114,7 +188,7 @@ DEFUN(show_subscr,
VTY_NEWLINE);
return CMD_WARNING;
}
subscr_dump_vty(vty, subscr);
subscr_dump_full_vty(vty, subscr);
subscr_put(subscr);
return CMD_SUCCESS;
@ -135,7 +209,7 @@ DEFUN(show_subscr_cache,
llist_for_each_entry(subscr, &active_subscribers, entry) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
subscr_dump_vty(vty, subscr);
subscr_dump_full_vty(vty, subscr);
}
return CMD_SUCCESS;
@ -143,23 +217,20 @@ DEFUN(show_subscr_cache,
DEFUN(sms_send_pend,
sms_send_pend_cmd,
"sms send pending MIN_ID",
"Send all pending SMS starting from MIN_ID")
"sms send pending",
"Send all pending SMS")
{
struct gsm_sms *sms;
int id = atoi(argv[0]);
int id = 0;
while (1) {
sms = db_sms_get_unsent(gsmnet, id++);
sms = db_sms_get_unsent_by_subscr(gsmnet, id);
if (!sms)
return CMD_WARNING;
if (!sms->receiver) {
sms_free(sms);
continue;
}
break;
gsm411_send_sms_subscr(sms->receiver, sms);
id = sms->receiver->id + 1;
}
return CMD_SUCCESS;
@ -269,10 +340,46 @@ DEFUN(subscriber_silent_sms,
return rc;
}
DEFUN(subscriber_silent_call,
subscriber_silent_call_cmd,
"subscriber " SUBSCR_TYPES " EXTEN silent call (start|stop)",
"Send a silent call to a subscriber")
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;
@ -283,17 +390,10 @@ DEFUN(subscriber_silent_call,
return CMD_WARNING;
}
if (!strcmp(argv[2], "start")) {
rc = gsm_silent_call_start(subscr, vty);
if (rc <= 0) {
vty_out(vty, "%% Subscriber not attached%s",
VTY_NEWLINE);
return CMD_WARNING;
}
} else {
rc = gsm_silent_call_stop(subscr);
if (rc < 0)
return CMD_WARNING;
rc = gsm_silent_call_stop(subscr);
if (rc < 0) {
subscr_put(subscr);
return CMD_WARNING;
}
subscr_put(subscr);
@ -349,6 +449,40 @@ DEFUN(cfg_subscr_authorized,
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)
{
@ -381,7 +515,8 @@ int bsc_vty_init_extra(struct gsm_network *net)
install_element(VIEW_NODE, &subscriber_send_sms_cmd);
install_element(VIEW_NODE, &subscriber_silent_sms_cmd);
install_element(VIEW_NODE, &subscriber_silent_call_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);
@ -390,6 +525,7 @@ int bsc_vty_init_extra(struct gsm_network *net)
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;
}

View File

@ -12,6 +12,10 @@ channel_test_SOURCES = channel_test.c \
$(top_srcdir)/src/select.c \
$(top_srcdir)/src/talloc.c \
$(top_srcdir)/src/gsm_data.c \
$(top_srcdir)/src/signal.c
$(top_srcdir)/src/signal.c \
$(top_srcdir)/src/statistics.c \
$(top_srcdir)/src/bts_ipaccess_nanobts.c \
$(top_srcdir)/src/bts_siemens_bs11.c \
$(top_srcdir)/src/tlv_parser.c
channel_test_LDADD = -ldl -ldbi

View File

@ -77,3 +77,5 @@ void nm_state_event() {}
void input_event() {}
void sms_alloc() {}
struct tlv_definition nm_att_tlvdef;

View File

@ -1,4 +1,4 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
noinst_PROGRAMS = debug_test
debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c
debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c $(top_srcdir)/src/talloc.c

View File

@ -1,6 +1,6 @@
/* simple test for the debug interface */
/*
* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.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
@ -24,11 +24,18 @@
int main(int argc, char** argv)
{
debug_parse_category_mask("DRLL");
DEBUGP(DCC, "You should not see this\n");
struct debug_target *stderr_target;
debug_parse_category_mask("DRLL:DCC");
DEBUGP(DRLL, "You should see this\n");
DEBUGP(DCC, "You should see this\n");
DEBUGP(DMM, "You should not see this\n");
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");
}

Some files were not shown because too many files have changed in this diff Show More