Compare commits

..

62 Commits

Author SHA1 Message Date
Holger Hans Peter Freyther
71e90b8eea on-waves: Increase the version number... 2010-08-04 00:46:59 +08:00
Holger Hans Peter Freyther
72bd2c247c mgcp: Only patch the header if we had a change in SSRC 2010-08-04 00:26:20 +08:00
Holger Hans Peter Freyther
f45ee6371f mgcp: Style issue... add a space. 2010-08-04 00:26:13 +08:00
Holger Hans Peter Freyther
d585586d7b mgcp: Allow switching the audio streams, patch the header
Patch the sequence number, the SSRC and the timestamp to
allow to mix various voice streams, e.g. toggling the loop
during the call.
2010-08-04 00:26:00 +08:00
Holger Hans Peter Freyther
0b3ffb1513 mgcp: Move the rtp state into a struct
Use a struct to group the rtp state for the up and the down
link of the bts.
2010-08-04 00:25:52 +08:00
Holger Hans Peter Freyther
0577dc1372 mgcp: Fix the documentation entry for the parameters 2010-08-04 00:25:39 +08:00
Holger Hans Peter Freyther
dab98fb15a bsc: Fix the vty writing... it is dtx-used... 2010-08-03 23:52:56 +08:00
Holger Hans Peter Freyther
d8ba591c49 grace: Do not crash if there is no rf ctl
Accept the new connection if there is no rf ctl. Fixes a segfault.
2010-08-03 18:11:27 +08:00
Holger Hans Peter Freyther
87c8f182cc mgcp: Allow to change the receive (the loopback part) via the VTY
Conflicts:

	openbsc/src/mgcp/mgcp_vty.c
2010-08-03 17:37:59 +08:00
Holger Hans Peter Freyther
c4fe5ac43a mgcp: Implement the "loopback" mode for a connection endpoint. 2010-08-03 17:37:30 +08:00
Holger Hans Peter Freyther
00d1f0d7a9 osmocore: Build a against the latest version.. 2010-08-03 03:13:06 +08:00
Holger Hans Peter Freyther
caecc9ad80 osmo-grace: Send USSD messages on the TCH to inform the user..
Send a USSD notification to the user to inform him that the
service will go away in a second..
2010-07-29 20:29:28 +08:00
Holger Hans Peter Freyther
c00bf8f930 osmo-grace: Introduce a global trace text to be send to subscribers 2010-07-29 19:32:12 +08:00
Holger Hans Peter Freyther
d657c67c9c osmo-grace: Handle the grace signal to execute a grace action 2010-07-29 19:19:09 +08:00
Holger Hans Peter Freyther
9ad1f2404f osmo_grace: Allow new connections when the network policy is S_RF_ON.
In case of S_RF_OFF and S_RF_GRACE we will allow new connections.
2010-07-29 19:11:51 +08:00
Holger Hans Peter Freyther
0dcacda194 osmo_rf: Keep the current policy inside the RF struct..
Keep a back pointer to the rf struct inside the connection,
resolve the network through the back pointer. Also assume
that the RF is on. In case we start with RF locked, the policy
is on but we will not see any MS talking to us.
2010-07-29 19:11:06 +08:00
Holger Hans Peter Freyther
1a9ffe85eb bsc_rf: Embed the rf status inside the GSM Network.
Right now we have a network-wide RF lock, in the future
one BSC might have multiple BTSs at different positions
and a global state will not make sense anymore and need
to be moved over to the BTS struct..
2010-07-29 18:57:10 +08:00
Holger Hans Peter Freyther
c94b9c4eb4 bsc_grace: Add a new per network check to decide if new connections are allowed
In case of an ordered RF shutdown we can enter a grace period
where no new RF connections are allowed but active connections
will stay alive until the RF is switched off.
2010-07-29 18:44:39 +08:00
Holger Hans Peter Freyther
b5fa05ff41 gsm_04_80: Allow to specify the alert pattern for the notification
Allow to specify the level (not the category) of the notification
this provides an easy way to test it on the phones.

Conflicts:

	openbsc/src/vty_interface_layer3.c
2010-07-29 04:33:42 +08:00
Holger Hans Peter Freyther
83f94497ce gsm_04_80: Embed a ss_Code inside the NotifySS-ARG...
Indicate that this is about the Call Name Presentation (cnap)
but the a1200 still ignores the call completelty...
2010-07-29 04:33:42 +08:00
Holger Hans Peter Freyther
1230b3c02e gsm_04_80: Send a Release Complete otherwise the USSD unit stays BUSY
We need to release the USSD unit, otherwise it is staying blocked
and will stop to function (even across LUs on my a1200). This code
should encode the transaction and the direction depending on the
network state but this is omitted right now.

Conflicts:

	openbsc/src/vty_interface_layer3.c
2010-07-29 04:33:35 +08:00
Holger Hans Peter Freyther
6d2d523e77 gsm_04_80: Add untested code for USSD notification...
One should be able to send a USSD Notification to a given
subscriber if we has an active link...

Conflicts:

	openbsc/src/vty_interface_layer3.c
2010-07-29 04:29:11 +08:00
Holger Hans Peter Freyther
dbd957c872 gsm_04_80: Fix the size calculation of the 04.80 message..
Subtract the two bytes we were adding to the length of the message.
2010-07-29 04:23:25 +08:00
Holger Hans Peter Freyther
bfc3688024 gsm_04_80: Use msgb_push to get the verification code of msgb
msgb started to verify that we do have enough tail/headroom
and this code was not using this check.
2010-07-29 04:23:25 +08:00
Holger Hans Peter Freyther
c50510b67e gsm_04_80: Add code to wrap a facility IE around. 2010-07-29 04:23:25 +08:00
Holger Hans Peter Freyther
980891cdf4 gsm_04_80: Create a unstructuredSS-Notify message
Create a unstructuredSS-Notify for a given type.
2010-07-29 04:23:25 +08:00
Holger Hans Peter Freyther
c43a0ab856 gsm_04_80: Fix the style and move the '*' to the function 2010-07-29 04:23:25 +08:00
Holger Hans Peter Freyther
c882a0560d gsm0480: Implement a generic "invoke" wrapping for messages.
Implement a GSM 04.80 invoke wrapper for a component and an
invoke id.

Conflicts:

	openbsc/src/gsm_04_80.c
2010-07-29 04:23:18 +08:00
Holger Hans Peter Freyther
1c5d12009e gsm0480: Attempt to encode a NotifySS-Arg with a username.. 2010-07-29 04:13:11 +08:00
Holger Hans Peter Freyther
b6dd348df2 bsc_msc_rf: Add a grace command, send a signal 2010-07-29 03:08:47 +08:00
Holger Hans Peter Freyther
b43e2afb3e mgcp: Attempt to count lost packets better... 2010-07-29 03:05:50 +08:00
Holger Hans Peter Freyther
d506107d1c mgcp: Provide the RTP packet loss information in the mgcp overview 2010-07-29 02:55:12 +08:00
Holger Hans Peter Freyther
7947b6be88 mgcp: Attempt to count missing RTP packets with a basic calculation
This code compares the UDP sequence numbers of two RTP messages
and guesses if packets are missing. It is guessing in two ways:

	1.) by default the sequence number is 0, so on the first
	    value we ignore the jump... we might ignore a real issue
	    in case of a wrap around which is easily possible as the
	    sequence should be a random number.
	2.) the UDP stream might have been reordered on the network
	    and we would see the jump...

In any case these two shortcomings are acceptable for the feature
that is meant to provide some basic analysis..
2010-07-29 02:55:05 +08:00
Holger Hans Peter Freyther
175a7b42af bsc_msc_ip: Use ip-dscp and provide the old value as alias. 2010-07-27 21:06:29 +08:00
Holger Hans Peter Freyther
38a2653801 mgcp: Rename TOS to DSCP
DSCP is the more modern information for TOS and the kernel
will set parts of TOS by itself (e.g. for ECN).
2010-07-27 21:01:19 +08:00
Holger Hans Peter Freyther
f1bb05fbef nat: Remove the nat code from the On-Waves branch... new code is in master 2010-07-27 21:01:18 +08:00
Holger Hans Peter Freyther
0c4f7ecabc bssap: Use libosmocore for the assignment complete msg generation 2010-07-23 18:34:38 +08:00
Holger Hans Peter Freyther
5822be4690 bssap: Use gsm0808 method to create complete layer3 message. 2010-07-23 18:23:29 +08:00
Holger Hans Peter Freyther
acda6908ad bssap: Move cipher mode complete to libosmocore 2010-07-23 17:32:52 +08:00
Holger Hans Peter Freyther
83f46278dd bssap: Use libosmocore GSM0808 generation routines. 2010-07-23 17:29:44 +08:00
Holger Hans Peter Freyther
e1c37bc4fe abis_nm: Band aid on OML initialisation by queuing messages
Instead of sending many messages we will queue the OML
messages and wait for the ACK/NACK before sending the
next message from the queue. We tag the msgb to remember
if we need to wait for an ack or not.

We keep the order of all messages, on ACKs and similiar
occassions we will drown the queue until we reach a message
that needs to be acked and then wait for that ack again.

Possible breakage can appear when we send an OML (e.g.
BS11 specific message) msg which does not need to be acked
through the abis_nm_sendmsg call. The fix will be to use
the _direct version of this method.

Re-Enable as it might have fixed something... who knows.

Conflicts:

	openbsc/include/openbsc/abis_nm.h
	openbsc/include/openbsc/gsm_data.h
	openbsc/src/gsm_data.c
	openbsc/src/input/ipaccess.c
2010-07-23 17:24:23 +08:00
Holger Hans Peter Freyther
0f87be341d Revert "abis_nm: Band aid on OML initialisation by queuing messages"
This has not fixed the init issue and we will need to figure out
an alternative to this.

This reverts commit a68f139820.
2010-07-09 17:48:06 +08:00
Holger Hans Peter Freyther
f15e647cba bsc_init: Allow DTXu and enable DTXd on RSL
Allow the MS to use uplink discontinous transmission by
setting the right bit in the SystemInformation and set
DTXd/DTXu on the RSL channel commands.

This is configurable via dtx-used (0|1) on the network
level..
2010-06-28 10:52:22 +08:00
Holger Hans Peter Freyther
e2c1a6a33d abis_nm.c: Use LOGPC with a LOGL_ERROR for the NACK messages
Make NACK messages print as errors.
2010-06-24 22:06:01 +08:00
Holger Hans Peter Freyther
9626783494 Bump the version for init and alignment fixes. 2010-06-23 11:07:59 +08:00
Holger Hans Peter Freyther
a68f139820 abis_nm: Band aid on OML initialisation by queuing messages
Instead of sending many messages we will queue the OML
messages and wait for the ACK/NACK before sending the
next message from the queue. We tag the msgb to remember
if we need to wait for an ack or not.

We keep the order of all messages, on ACKs and similiar
occassions we will drown the queue until we reach a message
that needs to be acked and then wait for that ack again.

Possible breakage can appear when we send an OML (e.g.
BS11 specific message) msg which does not need to be acked
through the abis_nm_sendmsg call. The fix will be to use
the _direct version of this method.

Conflicts:

	openbsc/include/openbsc/abis_nm.h
	openbsc/include/openbsc/gsm_data.h
	openbsc/src/gsm_data.c
	openbsc/src/input/ipaccess.c
2010-06-23 10:53:28 +08:00
Holger Hans Peter Freyther
539b8ed99f bssap.c: handle CHAN Activate NACK...
This was previously handled by the unexpected release
lchan handling for the secondary channel, we will now
just set the secondary_lchan pointer back to NULL and
let the framework free the resources.
2010-06-22 12:55:57 +08:00
Holger Hans Peter Freyther
d7cb8aa275 abis_rsl: Send the IMMEDIATE Assignment after the Channel Ack.
The Channel Activate might be sent to a different TRX than the
Immediate Assignment. So we need to make sure that the channel
is activated before we send the immediate assignment for the RACH.

Another reason for that is according to GSM 08.58 we should take
the frame number from the activate and use it for the starting
time inside the immediate assignment message. We obviously do not
do this yet.

The code assumes that the BTS will either respond with a CHAN ACK
or a CHAN NACK if not the lchan will remain in the request state.

Conflicts:

	openbsc/include/openbsc/gsm_data.h
	openbsc/src/abis_rsl.c
	openbsc/src/chan_alloc.c
2010-06-22 12:40:30 +08:00
Holger Hans Peter Freyther
38454904cb bssap.c: Fix possible unaligned memory access.
Use a memcpy to read and access the data to gurantee that
the data is properly aligned. The performance hit should
be smaller than the abort handled by the kernel.
2010-06-21 16:57:14 +08:00
Holger Hans Peter Freyther
c60465359b abis_nm.c: Reading the in_addr can lead to unaligned memory access
The value of the in_addr might not be 32 bit aligned and reading
it can generate an alignment error on ARM. Fix it by using memcpy
to copy the data into a local variable.

There are many more potential alignment issues that we will fix
when we hit them.
2010-06-21 16:57:14 +08:00
Holger Hans Peter Freyther
e9eb4d1ab8 bsc_init: Avoid unaligned access to nanobts_attr_nsvc0
nanobts_attr_nsvc0 + 10 is unlikely to be 32 bit aligned
and will trigger an alignment error on ARM..
2010-06-21 16:57:14 +08:00
Holger Hans Peter Freyther
c2b9bd202e Tag a release without the DTX change... 2010-06-16 18:30:37 +08:00
Holger Hans Peter Freyther
0d147d47b9 Revert "bsc_init: Enable DTX in Cell Options..."
This is incomplete and needs more thinking... We need to change
RSL chan modify as well..

This reverts commit 5bfb9102af.
2010-06-16 17:17:57 +08:00
Holger Hans Peter Freyther
ce701d7c5f new release with possible fix for the blackberry... 2010-06-16 12:55:14 +08:00
Holger Hans Peter Freyther
f3759a4934 si13: Use the correct pseudo length for the SI13 message
The GSM04.08 Section 10.5.2.19 specifies the L2 Pseudo Length
and the length does not include rest octets, so we will need
to use a zero for the length.

The patch is coming from Dieter Spaar.
2010-06-16 12:05:56 +08:00
Holger Hans Peter Freyther
5bfb9102af bsc_init: Enable DTX in Cell Options...
Allow the MS to use uplink discontinous transmission has
it can save some power for the MS and we can save some
bandwidth on the uplink as well.
2010-06-16 12:02:13 +08:00
Holger Hans Peter Freyther
2300d69df6 bsc: Increase the hand_off variable when we are done with the lchan.
Every time the highlevel code is done with the channel, increase
the value. This way we will be able to see if we are leaking a
channel that was never used or should have returned to the system.
2010-06-14 17:27:49 +08:00
Holger Hans Peter Freyther
c67cd2e11a vty: Dump the state of the SAPIs of a lchan as well. 2010-06-14 17:20:56 +08:00
Holger Hans Peter Freyther
2afb915758 lchan: Introduce a handoff variable...
This variable can be used by higher levels to declare they
were done with lchan...
2010-06-14 17:20:04 +08:00
Holger Hans Peter Freyther
dbac9295e7 chan_alloc: Swicth to LOGP from DEBUGP 2010-06-14 17:15:54 +08:00
Holger Hans Peter Freyther
9f972a5fb0 vty: Add show lchan-status to show the status...
Sometimes we see channels being allocated and never be freed.
This command should help to understand why this is happening
and in which state this channel is.
2010-06-14 17:00:35 +08:00
Holger Hans Peter Freyther
33f3dcbbca vty: Rename show lchan summary to show lchan-summary. 2010-06-14 17:00:19 +08:00
40 changed files with 805 additions and 4438 deletions

View File

@@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script
AC_INIT(openbsc, 0.3.99.15onwaves)
AC_INIT(openbsc, 0.3.99.19onwaves)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
dnl kernel style compile messages
@@ -15,7 +15,7 @@ dnl checks for libraries
AC_SEARCH_LIBS(crypt, crypt,
[LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.7)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.15)
dnl checks for header files
AC_HEADER_STDC
@@ -51,5 +51,4 @@ AC_OUTPUT(
tests/db/Makefile
tests/channel/Makefile
tests/sccp/Makefile
tests/bsc-nat/Makefile
Makefile)

View File

@@ -6,7 +6,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \
bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \
silent_call.h mgcp.h meas_rep.h rest_octets.h \
system_information.h handover.h mgcp_internal.h \
vty.h bssap.h bsc_msc.h bsc_nat.h bsc_msc_rf.h
vty.h bssap.h bsc_msc.h bsc_nat.h bsc_msc_rf.h \
bsc_msc_grace.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
openbscdir = $(includedir)/openbsc

View File

@@ -170,4 +170,6 @@ 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);
void abis_nm_clear_queue(struct gsm_bts *bts);
#endif /* _NM_H */

View File

@@ -0,0 +1,29 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef BSC_MSC_GRACE_H
#define BSC_MSC_GRACE_H
#include "gsm_data.h"
int bsc_grace_allow_new_connection(struct gsm_network *network);
#endif

View File

@@ -6,13 +6,15 @@
struct gsm_network;
struct bsc_msc_rf {
/* the value of signal.h */
int policy;
struct bsc_fd listen;
struct gsm_network *gsm_network;
};
struct bsc_msc_rf_conn {
struct write_queue queue;
struct gsm_network *gsm_network;
struct bsc_msc_rf *rf;
};
struct bsc_msc_rf *bsc_msc_rf_create(const char *path, struct gsm_network *net);

View File

@@ -19,4 +19,13 @@ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char* response_t
int gsm0480_send_ussd_reject(const struct msgb *msg,
const struct ussd_request *request);
struct msgb *gsm0480_create_notifySS(const char *text);
struct msgb *gsm0480_create_unstructuredSS_Notify(int alertLevel, const char *text);
int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id);
int gsm0480_wrap_facility(struct msgb *msg);
int gsm0480_send_ussdNotify(struct gsm_lchan *lchan, int level, const char *text);
int gsm0480_send_releaseComplete(struct gsm_lchan *lchan);
#endif

View File

@@ -81,12 +81,14 @@ enum bts_gprs_mode {
BTS_GPRS_EGPRS = 2,
};
#define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3]
struct msgb;
typedef int gsm_cbfn(unsigned int hooknum,
unsigned int event,
struct msgb *msg,
void *data, void *param);
struct bsc_msc_rf;
struct sccp_connection;
/* Real authentication information containing Ki */
@@ -218,6 +220,7 @@ struct gsm_subscriber_connection {
/* use count. how many users use this channel */
unsigned int use_count;
int hand_off;
/* Are we part of a special "silent" call */
int silent_call;
@@ -265,6 +268,9 @@ struct gsm_lchan {
*/
struct bss_sccp_connection_data *msc_data;
/* GSM Random Access data */
struct gsm48_req_ref *rqd_ref;
uint8_t rqd_ta;
/* cache of last measurement reports on this lchan */
struct gsm_meas_rep meas_rep[6];
@@ -536,6 +542,10 @@ struct gsm_bts {
/* transceivers */
int num_trx;
struct llist_head trx_list;
/* Abis NM queue */
struct llist_head abis_queue;
int abis_nm_pend;
};
/* Some statistics of our network */
@@ -669,6 +679,9 @@ struct gsm_network {
enum gsm_chan_t ctype_by_chreq[16];
/* enable the DTXu and DTXd for this network */
int dtx_enabled;
/* Use a TCH for handling requests of type paging any */
int pag_any_tch;
@@ -680,10 +693,12 @@ struct gsm_network {
char *bsc_token;
char *msc_ip;
int msc_port;
int msc_prio;
int msc_ip_dscp;
struct bsc_msc_connection *msc_con;
int ping_timeout;
int pong_timeout;
struct bsc_msc_rf *rf;
char *ussd_grace_txt;
};
#define SMS_HDR_SIZE 128

View File

@@ -93,7 +93,7 @@ struct mgcp_config {
int audio_loop;
int early_bind;
int rtp_base_port;
int endp_tos;
int endp_dscp;
/* only used in forward mode */
char *forward_ip;

View File

@@ -28,11 +28,33 @@
#define CI_UNUSED 0
enum mgcp_connection_mode {
MGCP_CONN_NONE = 0,
MGCP_CONN_RECV_ONLY = 1,
MGCP_CONN_SEND_ONLY = 2,
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
MGCP_CONN_LOOPBACK = 4,
};
struct mgcp_rtp_state {
int initialized;
int patch;
uint32_t orig_ssrc;
uint32_t ssrc;
uint16_t seq_no;
int lost_no;
int seq_offset;
uint32_t last_timestamp;
int32_t timestamp_offset;
};
struct mgcp_endpoint {
int ci;
char *callid;
char *local_options;
int conn_mode;
int orig_mode;
int bts_payload_type;
int net_payload_type;
@@ -61,6 +83,10 @@ struct mgcp_endpoint {
/* statistics */
unsigned int in_bts;
unsigned int in_remote;
/* sequence bits */
struct mgcp_rtp_state net_state;
struct mgcp_rtp_state bts_state;
};
#define ENDPOINT_NUMBER(endp) abs(endp - endp->cfg->endpoints)

View File

@@ -43,6 +43,7 @@ enum signal_subsystems {
SS_SCALL,
SS_GLOBAL,
SS_CHALLOC,
SS_RF,
};
/* SS_PAGING signals */
@@ -118,6 +119,13 @@ enum signal_global {
S_GLOBAL_SHUTDOWN,
};
/* SS_RF signals */
enum signal_rf {
S_RF_OFF,
S_RF_ON,
S_RF_GRACE,
};
struct paging_signal_data {
struct gsm_subscriber *subscr;
struct gsm_bts *bts;
@@ -143,4 +151,8 @@ struct challoc_signal_data {
enum gsm_chan_t type;
};
struct rf_signal_data {
struct gsm_network *net;
};
#endif

View File

@@ -107,8 +107,6 @@ enum node_type {
TS_NODE,
SUBSCR_NODE,
MGCP_NODE,
NAT_NODE,
BSC_NODE,
};
/* Node which has some commands and prompt string and configuration

View File

@@ -4,7 +4,7 @@ AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
isdnsync bsc_mgcp ipaccess-proxy \
bsc_msc_ip bsc_nat
bsc_msc_ip
noinst_LIBRARIES = libbsc.a libmsc.a libvty.a
noinst_HEADERS = vty/cardshell.h
@@ -35,7 +35,7 @@ bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c debug.c \
rs232.c bts_siemens_bs11.c
bsc_msc_ip_SOURCES = bssap.c bsc_msc_ip.c bsc_init.c vty_interface.c vty_interface_bsc.c \
bsc_msc.c bsc_msc_rf.c
bsc_msc.c bsc_msc_rf.c bsc_msc_grace.c gsm_04_80.c
bsc_msc_ip_LDADD = libbsc.a libvty.a libsccp.a
@@ -52,8 +52,3 @@ bsc_mgcp_LDADD = libvty.a
ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c
bsc_nat_SOURCES = nat/bsc_nat.c nat/bsc_filter.c nat/bsc_sccp.c \
nat/bsc_nat_utils.c nat/bsc_nat_vty.c nat/bsc_mgcp_utils.c \
mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \
bsc_msc.c bssap.c
bsc_nat_LDADD = libvty.a libbsc.a libsccp.a -lrt

View File

@@ -410,11 +410,31 @@ static struct msgb *nm_msgb_alloc(void)
}
/* Send a OML NM Message from BSC to BTS */
int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg)
{
msg->trx = bts->c0;
return _abis_nm_sendmsg(msg);
/* queue OML messages */
if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) {
bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg);
return _abis_nm_sendmsg(msg);
} else {
msgb_enqueue(&bts->abis_queue, msg);
return 0;
}
}
int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
{
OBSC_NM_W_ACK_CB(msg) = 1;
return abis_nm_queue_msg(bts, msg);
}
static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg)
{
OBSC_NM_W_ACK_CB(msg) = 0;
return abis_nm_queue_msg(bts, msg);
}
static int abis_nm_rcvmsg_sw(struct msgb *mb);
@@ -952,12 +972,30 @@ static int abis_nm_rx_lmt_event(struct msgb *mb)
return 0;
}
static void abis_nm_queue_send_next(struct gsm_bts *bts)
{
int wait = 0;
struct msgb *msg;
/* the queue is empty */
while (!llist_empty(&bts->abis_queue)) {
msg = msgb_dequeue(&bts->abis_queue);
wait = OBSC_NM_W_ACK_CB(msg);
_abis_nm_sendmsg(msg);
if (wait)
break;
}
bts->abis_nm_pend = wait;
}
/* Receive a OML NM Message from BTS */
static int abis_nm_rcvmsg_fom(struct msgb *mb)
{
struct abis_om_hdr *oh = msgb_l2(mb);
struct abis_om_fom_hdr *foh = msgb_l3(mb);
u_int8_t mt = foh->msg_type;
int ret = 0;
/* check for unsolicited message */
if (is_report(mt))
@@ -971,16 +1009,17 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
debugp_foh(foh);
DEBUGPC(DNM, "%s NACK ", get_value_string(nack_names, mt));
LOGPC(DNM, LOGL_ERROR, "%s NACK ", get_value_string(nack_names, mt));
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
DEBUGPC(DNM, "CAUSE=%s\n",
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");
dispatch_signal(SS_NM, S_NM_NACK, (void*) &mt);
abis_nm_queue_send_next(mb->trx->bts);
return 0;
}
#if 0
@@ -1002,13 +1041,13 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
switch (mt) {
case NM_MT_CHG_ADM_STATE_ACK:
return abis_nm_rx_chg_adm_state_ack(mb);
ret = abis_nm_rx_chg_adm_state_ack(mb);
break;
case NM_MT_SW_ACT_REQ:
return abis_nm_rx_sw_act_req(mb);
ret = abis_nm_rx_sw_act_req(mb);
break;
case NM_MT_BS11_LMT_SESSION:
return abis_nm_rx_lmt_event(mb);
ret = abis_nm_rx_lmt_event(mb);
break;
case NM_MT_CONN_MDROP_LINK_ACK:
DEBUGP(DNM, "CONN MDROP LINK ACK\n");
@@ -1021,7 +1060,8 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
break;
}
return 0;
abis_nm_queue_send_next(mb->trx->bts);
return ret;
}
static int abis_nm_rx_ipacc(struct msgb *mb);
@@ -1034,6 +1074,7 @@ static int abis_nm_rcvmsg_manuf(struct msgb *mb)
switch (bts_type) {
case GSM_BTS_TYPE_NANOBTS:
rc = abis_nm_rx_ipacc(mb);
abis_nm_queue_send_next(mb->trx->bts);
break;
default:
LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
@@ -1277,7 +1318,7 @@ static int sw_load_segment(struct abis_nm_sw *sw)
sw->obj_instance[0], sw->obj_instance[1],
sw->obj_instance[2]);
return abis_nm_sendmsg(sw->bts, msg);
return abis_nm_sendmsg_direct(sw->bts, msg);
}
/* 6.2.4 / 8.3.4 Load Data End */
@@ -1470,6 +1511,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
sw->cb_data, NULL);
rc = sw_fill_window(sw);
sw->state = SW_STATE_WAIT_SEGACK;
abis_nm_queue_send_next(mb->trx->bts);
break;
case NM_MT_LOAD_INIT_NACK:
if (sw->forced) {
@@ -1490,6 +1532,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
sw->cb_data, NULL);
sw->state = SW_STATE_ERROR;
}
abis_nm_queue_send_next(mb->trx->bts);
break;
}
break;
@@ -1510,6 +1553,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
sw->state = SW_STATE_WAIT_ENDACK;
rc = sw_load_end(sw);
}
abis_nm_queue_send_next(mb->trx->bts);
break;
case NM_MT_LOAD_ABORT:
if (sw->cbfn)
@@ -1531,6 +1575,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
NM_MT_LOAD_END_ACK, mb,
sw->cb_data, NULL);
rc = 0;
abis_nm_queue_send_next(mb->trx->bts);
break;
case NM_MT_LOAD_END_NACK:
if (sw->forced) {
@@ -1550,6 +1595,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
NM_MT_LOAD_END_NACK, mb,
sw->cb_data, NULL);
}
abis_nm_queue_send_next(mb->trx->bts);
break;
}
case SW_STATE_WAIT_ACTACK:
@@ -1563,6 +1609,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
sw->cbfn(GSM_HOOK_NM_SWLOAD,
NM_MT_ACTIVATE_SW_ACK, mb,
sw->cb_data, NULL);
abis_nm_queue_send_next(mb->trx->bts);
break;
case NM_MT_ACTIVATE_SW_NACK:
DEBUGP(DNM, "Activate Software NACK\n");
@@ -1572,6 +1619,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
sw->cbfn(GSM_HOOK_NM_SWLOAD,
NM_MT_ACTIVATE_SW_NACK, mb,
sw->cb_data, NULL);
abis_nm_queue_send_next(mb->trx->bts);
break;
}
case SW_STATE_NONE:
@@ -2014,7 +2062,7 @@ int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1,
if (nack)
msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP);
return abis_nm_sendmsg(bts, msg);
return abis_nm_sendmsg_direct(bts, msg);
}
int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *rawmsg)
@@ -2690,6 +2738,7 @@ static const char ipaccess_magic[] = "com.ipaccess";
static int abis_nm_rx_ipacc(struct msgb *msg)
{
struct in_addr addr;
struct abis_om_hdr *oh = msgb_l2(msg);
struct abis_om_fom_hdr *foh;
u_int8_t idstrlen = oh->data[0];
@@ -2711,10 +2760,12 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
switch (foh->msg_type) {
case NM_MT_IPACC_RSL_CONNECT_ACK:
DEBUGPC(DNM, "RSL CONNECT ACK ");
if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP))
DEBUGPC(DNM, "IP=%s ",
inet_ntoa(*((struct in_addr *)
TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP))));
if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) {
memcpy(&addr,
TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr));
DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr));
}
if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT))
DEBUGPC(DNM, "PORT=%u ",
ntohs(*((u_int16_t *)
@@ -3013,3 +3064,15 @@ int ipac_parse_bcch_info(struct ipac_bcch_info *binf, u_int8_t *buf)
return 0;
}
void abis_nm_clear_queue(struct gsm_bts *bts)
{
struct msgb *msg;
while (!llist_empty(&bts->abis_queue)) {
msg = msgb_dequeue(&bts->abis_queue);
msgb_free(msg);
}
bts->abis_nm_pend = 0;
}

View File

@@ -42,11 +42,15 @@
#include <openbsc/rtp_proxy.h>
#include <osmocore/rsl.h>
#include <osmocore/talloc.h>
#define RSL_ALLOC_SIZE 1024
#define RSL_ALLOC_HEADROOM 128
#define MAX(a, b) (a) >= (b) ? (a) : (b)
static int rsl_send_imm_assignment(struct gsm_lchan *lchan);
static u_int8_t mdisc_by_msgtype(u_int8_t msg_type)
{
/* mask off the transparent bit ? */
@@ -325,7 +329,10 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
memset(cm, 0, sizeof(cm));
/* FIXME: what to do with data calls ? */
cm->dtx_dtu = 0x00;
if (lchan->ts->trx->bts->network->dtx_enabled)
cm->dtx_dtu = 0x03;
else
cm->dtx_dtu = 0x00;
/* set TCH Speech/Data */
cm->spd_ind = lchan->rsl_cmode;
@@ -791,6 +798,13 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
gsm_lchans_name(msg->lchan->state));
rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE);
if (msg->lchan->rqd_ref) {
rsl_send_imm_assignment(msg->lchan);
talloc_free(msg->lchan->rqd_ref);
msg->lchan->rqd_ref = NULL;
msg->lchan->rqd_ta = 0;
}
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan);
return 0;
@@ -1134,12 +1148,10 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
struct gsm_bts *bts = msg->trx->bts;
struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);
struct gsm48_req_ref *rqd_ref;
struct gsm48_imm_ass ia;
enum gsm_chan_t lctype;
enum gsm_chreq_reason_t chreq_reason;
struct gsm_lchan *lchan;
u_int8_t rqd_ta;
int ret;
int is_lu;
u_int16_t arfcn;
@@ -1185,6 +1197,17 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
gsm_lchans_name(lchan->state));
rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
/* save the RACH data as we need it after the CHAN ACT ACK */
lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref);
if (!lchan->rqd_ref) {
LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n");
lchan_free(lchan);
return -ENOMEM;
}
memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref));
lchan->rqd_ta = rqd_ta;
ts_number = lchan->ts->nr;
arfcn = lchan->ts->trx->arfcn;
subch = lchan->nr;
@@ -1194,8 +1217,25 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
lchan->bs_power = 0; /* 0dB reduction, output power = Pn */
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
lchan->tch_mode = GSM48_CMODE_SIGN;
/* FIXME: Start another timer or assume the BTS sends a ACK/NACK? */
rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0);
DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s "
"r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch,
gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason),
rqd_ref->ra);
return 0;
}
static int rsl_send_imm_assignment(struct gsm_lchan *lchan)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm48_imm_ass ia;
u_int16_t arfcn;
arfcn = lchan->ts->trx->arfcn;
/* create IMMEDIATE ASSIGN 04.08 messge */
memset(&ia, 0, sizeof(ia));
ia.l2_plen = 0x2d;
@@ -1208,24 +1248,17 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
ia.chan_desc.h0.arfcn_low = arfcn & 0xff;
ia.chan_desc.h0.tsc = bts->tsc;
/* use request reference extracted from CHAN_RQD */
memcpy(&ia.req_ref, rqd_ref, sizeof(ia.req_ref));
ia.timing_advance = rqd_ta;
memcpy(&ia.req_ref, lchan->rqd_ref, sizeof(ia.req_ref));
ia.timing_advance = lchan->rqd_ta;
ia.mob_alloc_len = 0;
DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s "
"r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch,
gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason),
rqd_ref->ra);
/* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
lchan->T3101.cb = t3101_expired;
lchan->T3101.data = lchan;
bsc_schedule_timer(&lchan->T3101, bts->network->T3101, 0);
/* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
ret = rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);
return ret;
return rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);
}
/* MS has requested a channel on the RACH */

View File

@@ -830,6 +830,16 @@ err_out:
return rc;
}
static void patch_16(uint8_t *data, const uint16_t val)
{
memcpy(data, &val, sizeof(val));
}
static void patch_32(uint8_t *data, const uint32_t val)
{
memcpy(data, &val, sizeof(val));
}
/*
* Patch the various SYSTEM INFORMATION tables to update
* the LAI
@@ -892,12 +902,12 @@ static void patch_nm_tables(struct gsm_bts *bts)
nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff;
/* patch IP address as SGSN IP */
*(u_int16_t *)(nanobts_attr_nsvc0+8) =
htons(bts->gprs.nsvc[0].remote_port);
*(u_int32_t *)(nanobts_attr_nsvc0+10) =
htonl(bts->gprs.nsvc[0].remote_ip);
*(u_int16_t *)(nanobts_attr_nsvc0+14) =
htons(bts->gprs.nsvc[0].local_port);
patch_16(nanobts_attr_nsvc0 + 8,
htons(bts->gprs.nsvc[0].remote_port));
patch_32(nanobts_attr_nsvc0 + 10,
htonl(bts->gprs.nsvc[0].remote_ip));
patch_16(nanobts_attr_nsvc0 + 14,
htons(bts->gprs.nsvc[0].local_port));
/* patch BVCI */
nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8;
@@ -969,6 +979,8 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
trx->nm_state.availability = 0;
trx->bb_transc.nm_state.operational = 0;
trx->bb_transc.nm_state.availability = 0;
abis_nm_clear_queue(trx->bts);
break;
default:
break;
@@ -1044,8 +1056,14 @@ static int bootstrap_bts(struct gsm_bts *bts)
}
/* 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.radio_link_timeout = 7; /* 12 */
/* allow/disallow DTXu */
if (bts->network->dtx_enabled)
bts->si_common.cell_options.dtx = 0;
else
bts->si_common.cell_options.dtx = 2;
bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
bts->si_common.cell_sel_par.acs = 0;

View File

@@ -0,0 +1,97 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc/bsc_msc_grace.h>
#include <openbsc/bsc_msc_rf.h>
#include <openbsc/gsm_04_80.h>
#include <openbsc/signal.h>
int bsc_grace_allow_new_connection(struct gsm_network *network)
{
if (!network->rf)
return 1;
return network->rf->policy == S_RF_ON;
}
static int handle_sub(struct gsm_lchan *lchan, const char *text)
{
/* only send it to TCH */
if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F)
return -1;
/* only when active */
if (lchan->state != LCHAN_S_ACTIVE)
return -1;
gsm0480_send_ussdNotify(lchan, 0, text);
gsm0480_send_releaseComplete(lchan);
return 0;
}
/*
* The place to handle the grace mode. Right now we will send
* USSD messages to the subscriber, in the future we might start
* a timer to have different modes for the grace period.
*/
static int handle_grace(struct gsm_network *network)
{
int ts_nr, lchan_nr;
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
if (!network->ussd_grace_txt)
return 0;
llist_for_each_entry(bts, &network->bts_list, list) {
llist_for_each_entry(trx, &bts->trx_list, list) {
for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) {
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) {
handle_sub(&ts->lchan[lchan_nr],
network->ussd_grace_txt);
}
}
}
}
return 0;
}
static int handle_rf_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct rf_signal_data *sig;
if (subsys != SS_RF)
return -1;
sig = signal_data;
if (signal == S_RF_GRACE)
handle_grace(sig->net);
return 0;
}
static __attribute__((constructor)) void on_dso_load_grace(void)
{
register_signal_handler(SS_RF, handle_rf_signal, NULL);
}

View File

@@ -1,8 +1,8 @@
/* The BSC Process to handle GSM08.08 (A-Interface) */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009 by On-Waves
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -46,6 +46,7 @@
#include <openbsc/bsc_msc.h>
#include <openbsc/bsc_nat.h>
#include <openbsc/bsc_msc_rf.h>
#include <openbsc/bsc_msc_grace.h>
#include <osmocore/select.h>
#include <osmocore/talloc.h>
@@ -136,12 +137,14 @@ static void bss_close_lchans(struct bss_sccp_connection_data *bss)
{
if (bss->lchan) {
bss->lchan->msc_data = NULL;
bss->lchan->conn.hand_off += 1;
put_subscr_con(&bss->lchan->conn, 0);
bss->lchan = NULL;
}
if (bss->secondary_lchan) {
bss->secondary_lchan->msc_data = NULL;
bss->secondary_lchan->conn.hand_off += 1;
put_subscr_con(&bss->secondary_lchan->conn, 0);
bss->secondary_lchan = NULL;
}
@@ -292,6 +295,11 @@ static int open_sccp_connection(struct msgb *layer3)
return -1;
}
if (!bsc_grace_allow_new_connection(bsc_gsmnet)) {
LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
return -1;
}
LOGP(DMSC, LOGL_DEBUG, "Opening new layer3 connection\n");
sccp_connection = sccp_connection_socket();
if (!sccp_connection) {
@@ -399,7 +407,7 @@ static int handle_cipher_m_complete(struct msgb *msg)
}
LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n");
resp = bssmap_create_cipher_complete(msg);
resp = gsm0808_create_cipher_complete(msg, msg->lchan->encr.alg_id);
if (!resp) {
LOGP(DMSC, LOGL_ERROR, "Creating MSC response failed.\n");
return -1;
@@ -422,12 +430,14 @@ static int handle_ass_compl(struct msgb *msg)
if (!msg->lchan->msc_data) {
LOGP(DMSC, LOGL_ERROR, "No MSC data\n");
msg->lchan->conn.hand_off += 1;
put_subscr_con(&msg->lchan->conn, 0);
return -1;
}
if (msg->lchan->msc_data->secondary_lchan != msg->lchan) {
LOGP(DMSC, LOGL_ERROR, "Wrong assignment complete.\n");
msg->lchan->conn.hand_off += 1;
put_subscr_con(&msg->lchan->conn, 0);
return -1;
}
@@ -435,6 +445,7 @@ static int handle_ass_compl(struct msgb *msg)
if (msgb_l3len(msg) - sizeof(*gh) != 1) {
LOGP(DMSC, LOGL_ERROR, "assignment compl invalid: %d\n",
msgb_l3len(msg) - sizeof(*gh));
msg->lchan->conn.hand_off += 1;
put_subscr_con(&msg->lchan->conn, 0);
return -1;
}
@@ -453,6 +464,7 @@ static int handle_ass_compl(struct msgb *msg)
if (old_chan->conn.subscr)
subscr_put(old_chan->conn.subscr);
old_chan->conn.subscr = NULL;
old_chan->conn.hand_off += 1;
put_subscr_con(&old_chan->conn, 1);
}
@@ -476,6 +488,7 @@ static int handle_ass_fail(struct msgb *msg)
LOGP(DMSC, LOGL_ERROR, "ASSIGNMENT FAILURE from MS, forwarding to MSC\n");
if (!msg->lchan->msc_data) {
LOGP(DMSC, LOGL_ERROR, "No MSC data\n");
msg->lchan->conn.hand_off += 1;
put_subscr_con(&msg->lchan->conn, 0);
return -1;
}
@@ -484,6 +497,7 @@ static int handle_ass_fail(struct msgb *msg)
if (msg->lchan->msc_data->lchan != msg->lchan) {
LOGP(DMSC, LOGL_NOTICE, "Failure should come on the old link.\n");
msg->lchan->msc_data = NULL;
msg->lchan->conn.hand_off += 1;
put_subscr_con(&msg->lchan->conn, 0);
return -1;
}
@@ -612,6 +626,7 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
} else if (rc <= 0 && !msg->lchan->msc_data && msg->lchan->conn.use_count == 0) {
if (msg->lchan->state == LCHAN_S_ACTIVE) {
LOGP(DMSC, LOGL_NOTICE, "Closing unowned channel.\n");
msg->lchan->conn.hand_off += 1;
use_subscr_con(&msg->lchan->conn);
put_subscr_con(&msg->lchan->conn, 0);
}
@@ -1230,9 +1245,8 @@ int main(int argc, char **argv)
}
if (rf_ctl) {
struct bsc_msc_rf *rf;
rf = bsc_msc_rf_create(rf_ctl, bsc_gsmnet);
if (!rf) {
bsc_gsmnet->rf = bsc_msc_rf_create(rf_ctl, bsc_gsmnet);
if (!bsc_gsmnet->rf) {
fprintf(stderr, "Failed to create the RF service.\n");
exit(1);
}
@@ -1245,7 +1259,7 @@ int main(int argc, char **argv)
bsc_gsmnet->msc_con = bsc_msc_create(msc,
bsc_gsmnet->msc_port,
bsc_gsmnet->msc_prio);
bsc_gsmnet->msc_ip_dscp);
if (!bsc_gsmnet->msc_con) {
fprintf(stderr, "Creating a bsc_msc_connection failed.\n");
exit(1);

View File

@@ -24,6 +24,7 @@
#include <openbsc/bsc_msc_rf.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/signal.h>
#include <osmocore/talloc.h>
#include <osmocore/protocol/gsm_12_21.h>
@@ -37,6 +38,7 @@
#define RF_CMD_QUERY '?'
#define RF_CMD_OFF '0'
#define RF_CMD_ON '1'
#define RF_CMD_GRACE 'g'
static int lock_each_trx(struct gsm_network *net, int lock)
{
@@ -61,7 +63,7 @@ static void handle_query(struct bsc_msc_rf_conn *conn)
struct gsm_bts *bts;
char send = RF_CMD_OFF;
llist_for_each_entry(bts, &conn->gsm_network->bts_list, list) {
llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) {
struct gsm_bts_trx *trx;
llist_for_each_entry(trx, &bts->trx_list, list) {
if (trx->nm_state.availability == NM_AVSTATE_OK &&
@@ -90,6 +92,15 @@ static void handle_query(struct bsc_msc_rf_conn *conn)
return;
}
static void send_signal(struct bsc_msc_rf_conn *conn, int val)
{
struct rf_signal_data sig;
sig.net = conn->rf->gsm_network;
conn->rf->policy = val;
dispatch_signal(SS_RF, val, &sig);
}
static int rf_read_cmd(struct bsc_fd *fd)
{
struct bsc_msc_rf_conn *conn = fd->data;
@@ -111,10 +122,15 @@ static int rf_read_cmd(struct bsc_fd *fd)
handle_query(conn);
break;
case RF_CMD_OFF:
lock_each_trx(conn->gsm_network, 1);
lock_each_trx(conn->rf->gsm_network, 1);
send_signal(conn, S_RF_OFF);
break;
case RF_CMD_ON:
lock_each_trx(conn->gsm_network, 0);
lock_each_trx(conn->rf->gsm_network, 0);
send_signal(conn, S_RF_ON);
break;
case RF_CMD_GRACE:
send_signal(conn, S_RF_GRACE);
break;
default:
LOGP(DINP, LOGL_ERROR, "Unknown command %d\n", buf[0]);
@@ -165,7 +181,7 @@ static int rf_ctl_accept(struct bsc_fd *bfd, unsigned int what)
conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE;
conn->queue.read_cb = rf_read_cmd;
conn->queue.write_cb = rf_write_cmd;
conn->gsm_network = rf->gsm_network;
conn->rf = rf;
if (bsc_register_fd(&conn->queue.bfd) != 0) {
close(fd);
@@ -243,6 +259,7 @@ struct bsc_msc_rf *bsc_msc_rf_create(const char *path, struct gsm_network *net)
}
rf->gsm_network = net;
rf->policy = S_RF_ON;
return rf;
}

View File

@@ -45,6 +45,13 @@
static void bts_queue_send(struct msgb *msg, int link_id);
static void bssmap_free_secondary(struct bss_sccp_connection_data *data);
static uint32_t read_data32(const uint8_t *data)
{
uint32_t res;
memcpy(&res, data, sizeof(res));
return res;
}
static u_int16_t get_network_code_for_msc(struct gsm_network *net)
{
@@ -124,8 +131,7 @@ static int bssmap_handle_paging(struct gsm_network *net, struct msgb *msg, unsig
* Support paging to all network or one BTS at one LAC
*/
if (data_length == 3 && data[0] == CELL_IDENT_LAC) {
unsigned int *_lac = (unsigned int *)&data[1];
lac = ntohs(*_lac);
lac = ntohs(read_data32(&data[1]));
} else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) {
LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", hexdump(data, data_length));
return -1;
@@ -170,6 +176,7 @@ static int bssmap_handle_clear_command(struct sccp_connection *conn,
bssmap_free_secondary(msg->lchan->msc_data);
msg->lchan->msc_data = NULL;
msg->lchan->conn.hand_off += 1;
put_subscr_con(&msg->lchan->conn, 0);
}
@@ -258,7 +265,7 @@ reject:
if (msg->lchan && msg->lchan->msc_data)
msg->lchan->msc_data->block_gsm = 0;
resp = bssmap_create_cipher_reject(reject_cause);
resp = gsm0808_create_cipher_reject(reject_cause);
if (!resp) {
LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n");
return -1;
@@ -294,6 +301,7 @@ static void bssmap_free_secondary(struct bss_sccp_connection_data *data)
if (lchan->conn.subscr)
subscr_put(lchan->conn.subscr);
lchan->conn.subscr = NULL;
lchan->conn.hand_off += 1;
put_subscr_con(&lchan->conn, 1);
}
@@ -451,6 +459,7 @@ static void continue_new_assignment(struct gsm_lchan *new_lchan)
{
if (!new_lchan->msc_data) {
LOGP(DMSC, LOGL_ERROR, "No BSS data found.\n");
new_lchan->conn.hand_off += 1;
put_subscr_con(&new_lchan->conn, 0);
return;
}
@@ -458,6 +467,7 @@ static void continue_new_assignment(struct gsm_lchan *new_lchan)
if (new_lchan->msc_data->secondary_lchan != new_lchan) {
LOGP(DMSC, LOGL_ERROR, "This is not the secondary channel?\n");
new_lchan->msc_data = NULL;
new_lchan->conn.hand_off += 1;
put_subscr_con(&new_lchan->conn, 0);
return;
}
@@ -612,7 +622,8 @@ int bssmap_rcvmsg_udt(struct gsm_network *net, struct msgb *msg, unsigned int le
ret = bssmap_handle_reset_ack(net, msg, length);
break;
case BSS_MAP_MSG_PAGING:
ret = bssmap_handle_paging(net, msg, length);
if (bsc_grace_allow_new_connection(net))
ret = bssmap_handle_paging(net, msg, length);
break;
}
@@ -719,132 +730,12 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length)
/* Create messages */
struct msgb *bssmap_create_layer3(struct msgb *msg_l3)
{
u_int8_t *data;
u_int16_t *ci;
struct msgb* msg;
struct gsm48_loc_area_id *lai;
struct gsm_bts *bts = msg_l3->lchan->ts->trx->bts;
u_int16_t network_code = get_network_code_for_msc(bts->network);
u_int16_t country_code = get_country_code_for_msc(bts->network);
msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"bssmap cmpl l3");
if (!msg)
return NULL;
/* create the bssmap header */
msg->l3h = msgb_put(msg, 2);
msg->l3h[0] = 0x0;
/* create layer 3 header */
data = msgb_put(msg, 1);
data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3;
/* create the cell header */
data = msgb_put(msg, 3);
data[0] = GSM0808_IE_CELL_IDENTIFIER;
data[1] = 1 + sizeof(*lai) + 2;
data[2] = CELL_IDENT_WHOLE_GLOBAL;
lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
gsm48_generate_lai(lai, country_code,
network_code, bts->location_area_code);
ci = (u_int16_t *) msgb_put(msg, 2);
*ci = htons(bts->cell_identity);
/* copy the layer3 data */
data = msgb_put(msg, msgb_l3len(msg_l3) + 2);
data[0] = GSM0808_IE_LAYER_3_INFORMATION;
data[1] = msgb_l3len(msg_l3);
memcpy(&data[2], msg_l3->l3h, data[1]);
/* update the size */
msg->l3h[1] = msgb_l3len(msg) - 2;
return msg;
}
struct msgb *bssmap_create_cipher_complete(struct msgb *layer3)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"cipher-complete");
if (!msg)
return NULL;
/* send response with BSS override for A5/1... cheating */
msg->l3h = msgb_put(msg, 3);
msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[1] = 0xff;
msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE;
/* include layer3 in case we have at least two octets */
if (layer3 && msgb_l3len(layer3) > 2) {
msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2);
msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS;
msg->l4h[1] = msgb_l3len(layer3);
memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3));
}
/* and the optional BSS message */
msg->l4h = msgb_put(msg, 2);
msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
msg->l4h[1] = layer3->lchan->encr.alg_id;
/* update the size */
msg->l3h[1] = msgb_l3len(msg) - 2;
return msg;
}
struct msgb *bssmap_create_cipher_reject(u_int8_t cause)
{
struct msgb *msg = msgb_alloc(30, "bssmap: clear complete");
if (!msg)
return NULL;
msg->l3h = msgb_put(msg, 3);
msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[1] = 2;
msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT;
msg->l3h[3] = cause;
return msg;
}
struct msgb *bssmap_create_classmark_update(const u_int8_t *classmark_data, u_int8_t length)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"classmark-update");
if (!msg)
return NULL;
msg->l3h = msgb_put(msg, 3);
msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[1] = 0xff;
msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE;
msg->l4h = msgb_put(msg, length);
memcpy(msg->l4h, classmark_data, length);
/* update the size */
msg->l3h[1] = msgb_l3len(msg) - 2;
return msg;
}
struct msgb *bssmap_create_sapi_reject(u_int8_t link_id)
{
struct msgb *msg = msgb_alloc(30, "bssmap: sapi 'n' reject");
if (!msg)
return NULL;
msg->l3h = msgb_put(msg, 5);
msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[1] = 3;
msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT;
msg->l3h[3] = link_id;
msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED;
return msg;
return gsm0808_create_layer3(msg_l3, network_code, country_code,
bts->location_area_code, bts->cell_identity);
}
static u_int8_t chan_mode_to_speech(struct gsm_lchan *lchan)
@@ -931,50 +822,10 @@ static u_int8_t lchan_to_chosen_channel(struct gsm_lchan *lchan)
struct msgb *bssmap_create_assignment_completed(struct gsm_lchan *lchan, u_int8_t rr_cause)
{
u_int8_t *data;
u_int8_t speech_mode;
struct msgb *msg = msgb_alloc(35, "bssmap: ass compl");
if (!msg)
return NULL;
msg->l3h = msgb_put(msg, 3);
msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[1] = 0xff;
msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE;
/* write 3.2.2.22 */
data = msgb_put(msg, 2);
data[0] = GSM0808_IE_RR_CAUSE;
data[1] = rr_cause;
/* write cirtcuit identity code 3.2.2.2 */
/* write cell identifier 3.2.2.17 */
/* write chosen channel 3.2.2.33 when BTS picked it */
data = msgb_put(msg, 2);
data[0] = GSM0808_IE_CHOSEN_CHANNEL;
data[1] = lchan_to_chosen_channel(lchan);
/* write chosen encryption algorithm 3.2.2.44 */
data = msgb_put(msg, 2);
data[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
data[1] = lchan->encr.alg_id;
/* write circuit pool 3.2.2.45 */
/* write speech version chosen: 3.2.2.51 when BTS picked it */
speech_mode = chan_mode_to_speech(lchan);
if (speech_mode != 0) {
data = msgb_put(msg, 2);
data[0] = GSM0808_IE_SPEECH_VERSION;
data[1] = speech_mode;
}
/* write LSA identifier 3.2.2.15 */
/* update the size */
msg->l3h[1] = msgb_l3len(msg) - 2;
return msg;
return gsm0808_create_assignment_completed(rr_cause,
lchan_to_chosen_channel(lchan),
lchan->encr.alg_id,
chan_mode_to_speech(lchan));
}
struct msgb *dtap_create_msg(struct msgb *msg_l3, u_int8_t link_id)
@@ -1055,6 +906,20 @@ static int bssap_handle_lchan_signal(unsigned int subsys, unsigned int signal,
case S_LCHAN_ACTIVATE_ACK:
continue_new_assignment(lchan);
break;
case S_LCHAN_ACTIVATE_NACK:
if (lchan->msc_data && lchan->msc_data->secondary_lchan == lchan) {
LOGP(DMSC, LOGL_ERROR, "Activating a secondary lchan failed.\n");
/*
* The channel will be freed, so let us forget about it, T10 will
* fire and we will send the assignment failure to the network. We
* do not give up the refcount so we will get another unexpected
* release... but that will be handled just fine.
*/
lchan->msc_data->secondary_lchan = NULL;
lchan->msc_data = NULL;
}
break;
}
break;
}
@@ -1151,7 +1016,7 @@ static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id,
struct msgb *sapi_reject;
bts_free_queued(data);
sapi_reject = bssmap_create_sapi_reject(link_id);
sapi_reject = gsm0808_create_sapi_reject(link_id);
if (!sapi_reject){
LOGP(DMSC, LOGL_ERROR, "Failed to create SAPI reject\n");
return;

View File

@@ -34,6 +34,8 @@
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <osmocore/talloc.h>
static int ts_is_usable(struct gsm_bts_trx_ts *ts)
{
/* FIXME: How does this behave for BS-11 ? */
@@ -330,6 +332,12 @@ void lchan_free(struct gsm_lchan *lchan)
}
for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
lchan->neigh_meas[i].arfcn = 0;
if (lchan->rqd_ref) {
talloc_free(lchan->rqd_ref);
lchan->rqd_ref = NULL;
lchan->rqd_ta = 0;
}
lchan->conn.silent_call = 0;
sig.lchan = lchan;
@@ -426,11 +434,11 @@ int rsl_lchan_rll_release(struct gsm_lchan *lchan, u_int8_t link_id)
int _lchan_release(struct gsm_lchan *lchan, u_int8_t release_reason)
{
if (lchan->conn.use_count > 0) {
DEBUGP(DRLL, "BUG: _lchan_release called without zero use_count.\n");
LOGP(DRLL, LOGL_ERROR, "BUG: _lchan_release called without zero use_count.\n");
return 0;
}
DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
LOGP(DRLL, LOGL_NOTICE, "%s Recycling Channel.\n", gsm_lchan_name(lchan));
rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
lchan->release_reason = release_reason;
_lchan_handle_release(lchan);

View File

@@ -2,7 +2,7 @@
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2008, 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009 by Mike Haben <michael.haben@btinternet.com>
*
* All Rights Reserved
@@ -50,22 +50,22 @@ static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_t tag)
{
msgb->data -= 2;
msgb->data[0] = tag;
msgb->data[1] = msgb->len;
msgb->len += 2;
return msgb->data;
uint8_t *data = msgb_push(msgb, 2);
data[0] = tag;
data[1] = msgb->len - 2;
return data;
}
static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, u_int8_t tag,
u_int8_t value)
{
msgb->data -= 3;
msgb->len += 3;
msgb->data[0] = tag;
msgb->data[1] = 1;
msgb->data[2] = value;
return msgb->data;
uint8_t *data = msgb_push(msgb, 3);
data[0] = tag;
data[1] = 1;
data[2] = value;
return data;
}
@@ -244,6 +244,116 @@ static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
return rc;
}
struct msgb *gsm0480_create_notifySS(const char *text)
{
struct msgb *msg;
uint8_t *data, *tmp_len;
uint8_t *seq_len_ptr, *cal_len_ptr, *opt_len_ptr, *nam_len_ptr;
int len;
len = strlen(text);
if (len < 1 || len > 160)
return NULL;
msg = gsm48_msgb_alloc();
if (!msg)
return NULL;
msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG);
seq_len_ptr = msgb_put(msg, 1);
/* ss_code for CNAP { */
msgb_put_u8(msg, 0x81);
msgb_put_u8(msg, 1);
msgb_put_u8(msg, 0x19);
/* } ss_code */
/* nameIndicator { */
msgb_put_u8(msg, 0xB4);
nam_len_ptr = msgb_put(msg, 1);
/* callingName { */
msgb_put_u8(msg, 0xA0);
opt_len_ptr = msgb_put(msg, 1);
msgb_put_u8(msg, 0xA0);
cal_len_ptr = msgb_put(msg, 1);
/* namePresentationAllowed { */
/* add the DCS value */
msgb_put_u8(msg, 0x80);
msgb_put_u8(msg, 1);
msgb_put_u8(msg, 0x0F);
/* add the lengthInCharacters */
msgb_put_u8(msg, 0x81);
msgb_put_u8(msg, 1);
msgb_put_u8(msg, strlen(text));
/* add the actual string */
msgb_put_u8(msg, 0x82);
tmp_len = msgb_put(msg, 1);
data = msgb_put(msg, 0);
len = gsm_7bit_encode(data, text);
tmp_len[0] = len;
msgb_put(msg, len);
/* }; namePresentationAllowed */
cal_len_ptr[0] = 3 + 3 + 2 + len;
opt_len_ptr[0] = cal_len_ptr[0] + 2;
/* }; callingName */
nam_len_ptr[0] = opt_len_ptr[0] + 2;
/* ); nameIndicator */
/* write the lengths... */
seq_len_ptr[0] = 3 + nam_len_ptr[0] + 2;
return msg;
}
struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text)
{
struct msgb *msg;
uint8_t *seq_len_ptr, *ussd_len_ptr, *data;
int len;
msg = gsm48_msgb_alloc();
if (!msg)
return NULL;
/* SEQUENCE { */
msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG);
seq_len_ptr = msgb_put(msg, 1);
/* DCS { */
msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
msgb_put_u8(msg, 1);
msgb_put_u8(msg, 0x0F);
/* } DCS */
/* USSD-String { */
msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
ussd_len_ptr = msgb_put(msg, 1);
data = msgb_put(msg, 0);
len = gsm_7bit_encode(data, text);
msgb_put(msg, len);
ussd_len_ptr[0] = len;
/* USSD-String } */
/* alertingPattern { */
msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
msgb_put_u8(msg, 1);
msgb_put_u8(msg, alertPattern);
/* } alertingPattern */
seq_len_ptr[0] = 3 + 2 + ussd_len_ptr[0] + 3;
/* } SEQUENCE */
return msg;
}
/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_text,
const struct ussd_request *req)
@@ -297,8 +407,37 @@ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_t
return gsm48_sendmsg(msg, NULL);
}
/* wrap an invoke around it... the other way around
*
* 1.) Invoke Component tag
* 2.) Invoke ID Tag
* 3.) Operation
* 4.) Data
*/
int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id)
{
/* 3. operation */
msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, op);
/* 2. invoke id tag */
msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id);
/* 1. component tag */
msgb_wrap_with_TL(msg, GSM0480_CTYPE_INVOKE);
return 0;
}
/* wrap the GSM 04.08 Facility IE around it */
int gsm0480_wrap_facility(struct msgb *msg)
{
msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
return 0;
}
int gsm0480_send_ussd_reject(const struct msgb *in_msg,
const struct ussd_request *req)
const struct ussd_request *req)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
@@ -326,3 +465,43 @@ int gsm0480_send_ussd_reject(const struct msgb *in_msg,
return gsm48_sendmsg(msg, NULL);
}
int gsm0480_send_ussdNotify(struct gsm_lchan *lchan, int level, const char *text)
{
struct gsm48_hdr *gh;
struct msgb *msg;
msg = gsm0480_create_unstructuredSS_Notify(level, text);
if (!msg)
return -1;
gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0);
gsm0480_wrap_facility(msg);
msg->lchan = lchan;
/* And finally pre-pend the L3 header */
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS;
gh->msg_type = GSM0480_MTYPE_REGISTER;
return gsm48_sendmsg(msg, NULL);
}
int gsm0480_send_releaseComplete(struct gsm_lchan *lchan)
{
struct gsm48_hdr *gh;
struct msgb *msg;
msg = gsm48_msgb_alloc();
if (!msg)
return -1;
msg->lchan = lchan;
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS;
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
return gsm48_sendmsg(msg, NULL);
}

View File

@@ -234,6 +234,8 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
bts->rach_b_thresh = -1;
bts->rach_ldavg_slots = -1;
INIT_LLIST_HEAD(&bts->abis_queue);
llist_add_tail(&bts->list, &net->bts_list);
return bts;

View File

@@ -573,7 +573,7 @@ static int handle_ts1_write(struct bsc_fd *bfd)
e1i_ts->sign.tx_timer.data = e1i_ts;
/* Reducing this might break the nanoBTS 900 init. */
bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 0);
return ret;
}

View File

@@ -94,17 +94,53 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp)
endp->net_rtp, buf, 1);
}
static void patch_payload(int payload, char *data, int len)
static void patch_and_count(struct mgcp_rtp_state *state, int payload, char *data, int len)
{
uint16_t seq;
uint32_t timestamp;
struct rtp_hdr *rtp_hdr;
if (len < sizeof(*rtp_hdr))
return;
rtp_hdr = (struct rtp_hdr *) data;
seq = ntohs(rtp_hdr->sequence);
timestamp = ntohl(rtp_hdr->timestamp);
if (!state->initialized) {
state->seq_no = seq - 1;
state->ssrc = state->orig_ssrc = rtp_hdr->ssrc;
state->initialized = 1;
state->last_timestamp = timestamp;
} else if (state->ssrc != rtp_hdr->ssrc) {
state->ssrc = rtp_hdr->ssrc;
state->seq_offset = (state->seq_no + 1) - seq;
state->timestamp_offset = state->last_timestamp - timestamp;
state->patch = 1;
LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed... SSRC: %u offset: %d\n",
state->ssrc, state->seq_offset);
}
/* apply the offset and store it back to the packet */
if (state->patch) {
seq += state->seq_offset;
rtp_hdr->sequence = htons(seq);
rtp_hdr->ssrc = state->orig_ssrc;
timestamp += state->timestamp_offset;
rtp_hdr->timestamp = htonl(timestamp);
}
/* seq changed, now compare if we have lost something */
if (state->seq_no + 1u != seq)
state->lost_no = abs(seq - (state->seq_no + 1));
state->seq_no = seq;
state->last_timestamp = timestamp;
if (payload < 0)
return;
rtp_hdr = (struct rtp_hdr *) data;
rtp_hdr->payload_type = payload;
}
@@ -194,15 +230,21 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
if (cfg->audio_loop)
dest = !dest;
/* Loop based on the conn_mode, maybe undoing the above */
if (endp->conn_mode == MGCP_CONN_LOOPBACK)
dest = !dest;
if (dest == DEST_NETWORK) {
if (proto == PROTO_RTP)
patch_payload(endp->net_payload_type, buf, rc);
patch_and_count(&endp->bts_state,
endp->net_payload_type, buf, rc);
return udp_send(fd->fd, &endp->remote,
proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp,
buf, rc);
} else {
if (proto == PROTO_RTP)
patch_payload(endp->bts_payload_type, buf, rc);
patch_and_count(&endp->net_state,
endp->bts_payload_type, buf, rc);
return udp_send(fd->fd, &endp->bts,
proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp,
buf, rc);
@@ -257,8 +299,8 @@ static int bind_rtp(struct mgcp_endpoint *endp)
goto cleanup1;
}
set_ip_tos(endp->local_rtp.fd, cfg->endp_tos);
set_ip_tos(endp->local_rtcp.fd, cfg->endp_tos);
set_ip_tos(endp->local_rtp.fd, cfg->endp_dscp);
set_ip_tos(endp->local_rtcp.fd, cfg->endp_dscp);
endp->local_rtp.cb = rtp_data_cb;
endp->local_rtp.data = endp;

View File

@@ -38,13 +38,6 @@
#include <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
enum mgcp_connection_mode {
MGCP_CONN_NONE = 0,
MGCP_CONN_RECV_ONLY = 1,
MGCP_CONN_SEND_ONLY = 2,
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
};
/**
* Macro for tokenizing MGCP messages and SDP in one go.
*
@@ -364,6 +357,8 @@ static int parse_conn_mode(const char* msg, int *conn_mode)
*conn_mode = MGCP_CONN_RECV_ONLY;
else if (strcmp(msg, "sendrecv") == 0)
*conn_mode = MGCP_CONN_RECV_SEND;
else if (strcmp(msg, "loopback") == 0)
*conn_mode = MGCP_CONN_LOOPBACK;
else {
LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
ret = -1;
@@ -414,6 +409,8 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
error_code = 517;
goto error2;
}
endp->orig_mode = endp->conn_mode;
break;
default:
LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
@@ -518,6 +515,7 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
error_code = 517;
goto error3;
}
endp->orig_mode = endp->conn_mode;
break;
case 'Z':
silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0;
@@ -742,7 +740,7 @@ int mgcp_endpoints_allocate(struct mgcp_config *cfg)
void mgcp_free_endp(struct mgcp_endpoint *endp)
{
LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
endp->ci= CI_UNUSED;
endp->ci = CI_UNUSED;
if (endp->callid) {
talloc_free(endp->callid);
@@ -764,4 +762,9 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
endp->in_bts = endp->in_remote = 0;
memset(&endp->remote, 0, sizeof(endp->remote));
memset(&endp->bts, 0, sizeof(endp->bts));
memset(&endp->net_state, 0, sizeof(endp->net_state));
memset(&endp->bts_state, 0, sizeof(endp->bts_state));
endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
}

View File

@@ -57,7 +57,7 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
vty_out(vty, " bind early %u%s", !!g_cfg->early_bind, VTY_NEWLINE);
vty_out(vty, " rtp base %u%s", g_cfg->rtp_base_port, VTY_NEWLINE);
vty_out(vty, " rtp ip-tos %d%s", g_cfg->endp_tos, VTY_NEWLINE);
vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE);
if (g_cfg->audio_payload != -1)
vty_out(vty, " sdp audio payload number %d%s", g_cfg->audio_payload, VTY_NEWLINE);
if (g_cfg->audio_name)
@@ -82,11 +82,12 @@ DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
vty_out(vty, "MGCP is up and running with %u endpoints:%s", g_cfg->number_endpoints - 1, VTY_NEWLINE);
for (i = 1; i < g_cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &g_cfg->endpoints[i];
vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s traffic received bts: %u remote: %u%s",
vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s traffic received bts: %u/%u remote: %u/%u%s",
i, endp->ci,
ntohs(endp->net_rtp), ntohs(endp->net_rtcp),
ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
inet_ntoa(endp->bts), endp->in_bts, endp->in_remote,
inet_ntoa(endp->bts), endp->in_bts, endp->bts_state.lost_no,
endp->in_remote, endp->net_state.lost_no,
VTY_NEWLINE);
}
@@ -166,16 +167,21 @@ DEFUN(cfg_mgcp_rtp_base_port,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_ip_tos,
cfg_mgcp_rtp_ip_tos_cmd,
"rtp ip-tos <0-255>",
"Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The TOS value.")
DEFUN(cfg_mgcp_rtp_ip_dscp,
cfg_mgcp_rtp_ip_dscp_cmd,
"rtp ip-dscp <0-255>",
"Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The DSCP value.")
{
int tos = atoi(argv[0]);
g_cfg->endp_tos = tos;
int dscp = atoi(argv[0]);
g_cfg->endp_dscp = dscp;
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd,
"rtp ip-tos <0-255>",
"Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The DSCP value.")
DEFUN(cfg_mgcp_sdp_payload_number,
cfg_mgcp_sdp_payload_number_cmd,
"sdp audio payload number <1-255>",
@@ -247,12 +253,41 @@ DEFUN(cfg_mgcp_agent_addr,
return CMD_SUCCESS;
}
DEFUN(loop_endp,
loop_endp_cmd,
"loop-endpoint NAME (0|1)",
"Loop a given endpoint\n"
"The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n")
{
struct mgcp_endpoint *endp;
int endp_no = strtoul(argv[0], NULL, 16);
if (endp_no < 1 || endp_no >= g_cfg->number_endpoints) {
vty_out(vty, "Loopback number %s/%d is invalid.%s",
argv[0], endp_no, VTY_NEWLINE);
return CMD_WARNING;
}
endp = &g_cfg->endpoints[endp_no];
int loop = atoi(argv[1]);
if (loop)
endp->conn_mode = MGCP_CONN_LOOPBACK;
else
endp->conn_mode = endp->orig_mode;
return CMD_SUCCESS;
}
int mgcp_vty_init(void)
{
install_element(VIEW_NODE, &show_mgcp_cmd);
install_element(ENABLE_NODE, &loop_endp_cmd);
install_element(CONFIG_NODE, &cfg_mgcp_cmd);
install_node(&mgcp_node, config_write_mgcp);
install_default(MGCP_NODE);
install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
@@ -260,6 +295,7 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);

View File

@@ -1,216 +0,0 @@
/* BSC Multiplexer/NAT */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc/bsc_nat.h>
#include <openbsc/bssap.h>
#include <openbsc/ipaccess.h>
#include <openbsc/debug.h>
#include <osmocore/talloc.h>
#include <sccp/sccp.h>
/*
* The idea is to have a simple struct describing a IPA packet with
* SCCP SSN and the GSM 08.08 payload and decide. We will both have
* a white and a blacklist of packets we want to handle.
*
* TODO: Implement a "NOT" in the filter language.
*/
#define ALLOW_ANY -1
#define FILTER_TO_BSC 1
#define FILTER_TO_MSC 2
#define FILTER_TO_BOTH 3
struct bsc_pkt_filter {
int ipa_proto;
int dest_ssn;
int bssap;
int gsm;
int filter_dir;
};
static struct bsc_pkt_filter black_list[] = {
/* filter reset messages to the MSC */
{ IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC },
/* filter reset ack messages to the BSC */
{ IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC },
/* filter ip access */
{ IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC },
};
static struct bsc_pkt_filter white_list[] = {
/* allow IPAC_PROTO_SCCP messages to both sides */
{ IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
/* allow MGCP messages to both sides */
{ NAT_IPAC_PROTO_MGCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
};
struct bsc_nat_parsed* bsc_nat_parse(struct msgb *msg)
{
struct sccp_parse_result result;
struct bsc_nat_parsed *parsed;
struct ipaccess_head *hh;
/* quick fail */
if (msg->len < 4)
return NULL;
parsed = talloc_zero(msg, struct bsc_nat_parsed);
if (!parsed)
return NULL;
/* more init */
parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1;
parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1;
/* start parsing */
hh = (struct ipaccess_head *) msg->data;
parsed->ipa_proto = hh->proto;
msg->l2h = &hh->data[0];
/* do a size check on the input */
if (ntohs(hh->len) != msgb_l2len(msg)) {
LOGP(DINP, LOGL_ERROR, "Wrong input length?\n");
talloc_free(parsed);
return NULL;
}
/* analyze sccp down here */
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
memset(&result, 0, sizeof(result));
if (sccp_parse_header(msg, &result) != 0) {
talloc_free(parsed);
return 0;
}
if (msg->l3h && msgb_l3len(msg) < 3) {
LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n");
talloc_free(parsed);
return 0;
}
parsed->sccp_type = sccp_determine_msg_type(msg);
parsed->src_local_ref = result.source_local_reference;
parsed->dest_local_ref = result.destination_local_reference;
parsed->called_ssn = result.called.ssn;
parsed->calling_ssn = result.calling.ssn;
/* in case of connection confirm we have no payload */
if (msg->l3h) {
parsed->bssap = msg->l3h[0];
parsed->gsm_type = msg->l3h[2];
}
}
return parsed;
}
int bsc_nat_filter_ipa(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed)
{
int i;
/* go through the blacklist now */
for (i = 0; i < ARRAY_SIZE(black_list); ++i) {
/* ignore the rule? */
if (black_list[i].filter_dir != FILTER_TO_BOTH
&& black_list[i].filter_dir != dir)
continue;
/* the proto is not blacklisted */
if (black_list[i].ipa_proto != ALLOW_ANY
&& black_list[i].ipa_proto != parsed->ipa_proto)
continue;
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
/* the SSN is not blacklisted */
if (black_list[i].dest_ssn != ALLOW_ANY
&& black_list[i].dest_ssn != parsed->called_ssn)
continue;
/* bssap */
if (black_list[i].bssap != ALLOW_ANY
&& black_list[i].bssap != parsed->bssap)
continue;
/* gsm */
if (black_list[i].gsm != ALLOW_ANY
&& black_list[i].gsm != parsed->gsm_type)
continue;
/* blacklisted */
LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
return 1;
} else {
/* blacklisted, we have no content sniffing yet */
LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
return 1;
}
}
/* go through the whitelust now */
for (i = 0; i < ARRAY_SIZE(white_list); ++i) {
/* ignore the rule? */
if (white_list[i].filter_dir != FILTER_TO_BOTH
&& white_list[i].filter_dir != dir)
continue;
/* the proto is not whitelisted */
if (white_list[i].ipa_proto != ALLOW_ANY
&& white_list[i].ipa_proto != parsed->ipa_proto)
continue;
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
/* the SSN is not whitelisted */
if (white_list[i].dest_ssn != ALLOW_ANY
&& white_list[i].dest_ssn != parsed->called_ssn)
continue;
/* bssap */
if (white_list[i].bssap != ALLOW_ANY
&& white_list[i].bssap != parsed->bssap)
continue;
/* gsm */
if (white_list[i].gsm != ALLOW_ANY
&& white_list[i].gsm != parsed->gsm_type)
continue;
/* whitelisted */
LOGP(DNAT, LOGL_INFO, "Whitelisted with rule %d\n", i);
return 0;
} else {
/* whitelisted */
return 0;
}
}
return 1;
}

View File

@@ -1,559 +0,0 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc/bsc_nat.h>
#include <openbsc/gsm_data.h>
#include <openbsc/bssap.h>
#include <openbsc/debug.h>
#include <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
#include <sccp/sccp.h>
#include <osmocore/talloc.h>
#include <osmocore/gsm0808.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
int bsc_mgcp_assign(struct sccp_connections *con, struct msgb *msg)
{
struct sccp_connections *mcon;
struct tlv_parsed tp;
u_int16_t cic;
u_int8_t timeslot;
u_int8_t multiplex;
int combined;
if (!msg->l3h) {
LOGP(DNAT, LOGL_ERROR, "Assignment message should have l3h pointer.\n");
return -1;
}
if (msgb_l3len(msg) < 3) {
LOGP(DNAT, LOGL_ERROR, "Assignment message has not enough space for GSM0808.\n");
return -1;
}
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
LOGP(DNAT, LOGL_ERROR, "Circuit identity code not found in assignment message.\n");
return -1;
}
cic = ntohs(*(u_int16_t *)TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
timeslot = cic & 0x1f;
multiplex = (cic & ~0x1f) >> 5;
combined = (32 * multiplex) + timeslot;
/* find stale connections using that endpoint */
llist_for_each_entry(mcon, &con->bsc->nat->sccp_connections, list_entry) {
if (mcon->msc_timeslot == combined) {
LOGP(DNAT, LOGL_ERROR,
"Timeslot %d was assigned to 0x%x and now 0x%x\n",
combined,
sccp_src_ref_to_int(&mcon->patched_ref),
sccp_src_ref_to_int(&con->patched_ref));
bsc_mgcp_dlcx(mcon);
}
}
con->msc_timeslot = combined;
con->bsc_timeslot = con->msc_timeslot;
return 0;
}
static void bsc_mgcp_free_endpoint(struct bsc_nat *nat, int i)
{
if (nat->bsc_endpoints[i].transaction_id) {
talloc_free(nat->bsc_endpoints[i].transaction_id);
nat->bsc_endpoints[i].transaction_id = NULL;
}
nat->bsc_endpoints[i].bsc = NULL;
}
void bsc_mgcp_free_endpoints(struct bsc_nat *nat)
{
int i;
for (i = 1; i < nat->mgcp_cfg->number_endpoints; ++i){
bsc_mgcp_free_endpoint(nat, i);
mgcp_free_endp(&nat->mgcp_cfg->endpoints[i]);
}
}
/* send a MDCX where we do not want a response */
static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, struct mgcp_endpoint *endp)
{
char buf[2096];
int len;
len = snprintf(buf, sizeof(buf),
"MDCX 23 %x@mgw MGCP 1.0\r\n"
"Z: noanswer\r\n"
"\r\n"
"c=IN IP4 %s\r\n"
"m=audio %d RTP/AVP 255\r\n",
ENDPOINT_NUMBER(endp),
bsc->nat->mgcp_cfg->source_addr,
endp->rtp_port);
if (len < 0) {
LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n");
return;
}
}
static void bsc_mgcp_send_dlcx(struct bsc_connection *bsc, int endpoint)
{
char buf[2096];
int len;
len = snprintf(buf, sizeof(buf),
"DLCX 23 %x@mgw MGCP 1.0\r\n"
"Z: noanswer\r\n", endpoint);
if (len < 0) {
LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n");
return;
}
bsc_write_mgcp(bsc, (u_int8_t *) buf, len);
}
void bsc_mgcp_init(struct sccp_connections *con)
{
con->msc_timeslot = -1;
con->bsc_timeslot = -1;
con->crcx = 0;
}
void bsc_mgcp_dlcx(struct sccp_connections *con)
{
/* send a DLCX down the stream */
if (con->bsc_timeslot != -1 && con->crcx) {
int endp = mgcp_timeslot_to_endpoint(0, con->msc_timeslot);
bsc_mgcp_send_dlcx(con->bsc, endp);
bsc_mgcp_free_endpoint(con->bsc->nat, endp);
}
bsc_mgcp_init(con);
}
struct sccp_connections *bsc_mgcp_find_con(struct bsc_nat *nat, int endpoint)
{
struct sccp_connections *con = NULL;
struct sccp_connections *sccp;
llist_for_each_entry(sccp, &nat->sccp_connections, list_entry) {
if (sccp->msc_timeslot == -1)
continue;
if (mgcp_timeslot_to_endpoint(0, sccp->msc_timeslot) != endpoint)
continue;
con = sccp;
}
if (con)
return con;
LOGP(DMGCP, LOGL_ERROR, "Failed to find the connection.\n");
return NULL;
}
int bsc_mgcp_policy_cb(struct mgcp_config *cfg, int endpoint, int state, const char *transaction_id)
{
struct bsc_nat *nat;
struct bsc_endpoint *bsc_endp;
struct sccp_connections *sccp;
struct mgcp_endpoint *mgcp_endp;
struct msgb *bsc_msg;
nat = cfg->data;
bsc_endp = &nat->bsc_endpoints[endpoint];
mgcp_endp = &nat->mgcp_cfg->endpoints[endpoint];
if (bsc_endp->transaction_id) {
LOGP(DMGCP, LOGL_ERROR, "Endpoint 0x%x had pending transaction: '%s'\n",
endpoint, bsc_endp->transaction_id);
talloc_free(bsc_endp->transaction_id);
bsc_endp->transaction_id = NULL;
}
bsc_endp->bsc = NULL;
sccp = bsc_mgcp_find_con(nat, endpoint);
if (!sccp) {
LOGP(DMGCP, LOGL_ERROR, "Did not find BSC for change on endpoint: 0x%x state: %d\n", endpoint, state);
switch (state) {
case MGCP_ENDP_CRCX:
return MGCP_POLICY_REJECT;
break;
case MGCP_ENDP_DLCX:
return MGCP_POLICY_CONT;
break;
case MGCP_ENDP_MDCX:
return MGCP_POLICY_CONT;
break;
default:
LOGP(DMGCP, LOGL_FATAL, "Unhandled state: %d\n", state);
return MGCP_POLICY_CONT;
break;
}
}
/* we need to generate a new and patched message */
bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length,
nat->mgcp_cfg->source_addr, mgcp_endp->rtp_port);
if (!bsc_msg) {
LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n");
return MGCP_POLICY_CONT;
}
bsc_endp->transaction_id = talloc_strdup(nat, transaction_id);
bsc_endp->bsc = sccp->bsc;
/* we need to update some bits */
if (state == MGCP_ENDP_CRCX) {
struct sockaddr_in sock;
socklen_t len = sizeof(sock);
if (getpeername(sccp->bsc->write_queue.bfd.fd, (struct sockaddr *) &sock, &len) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Can not get the peername...%d/%s\n",
errno, strerror(errno));
} else {
mgcp_endp->bts = sock.sin_addr;
}
/* send the message and a fake MDCX for force sending of a dummy packet */
sccp->crcx = 1;
bsc_write(sccp->bsc, bsc_msg, NAT_IPAC_PROTO_MGCP);
bsc_mgcp_send_mdcx(sccp->bsc, mgcp_endp);
return MGCP_POLICY_DEFER;
} else if (state == MGCP_ENDP_DLCX) {
/* we will free the endpoint now and send a DLCX to the BSC */
msgb_free(bsc_msg);
bsc_mgcp_dlcx(sccp);
return MGCP_POLICY_CONT;
} else {
bsc_write(sccp->bsc, bsc_msg, NAT_IPAC_PROTO_MGCP);
return MGCP_POLICY_DEFER;
}
}
/*
* We have received a msg from the BSC. We will see if we know
* this transaction and if it belongs to the BSC. Then we will
* need to patch the content to point to the local network and we
* need to update the I: that was assigned by the BSS.
*/
void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg)
{
struct msgb *output;
struct bsc_endpoint *bsc_endp = NULL;
struct mgcp_endpoint *endp = NULL;
int i, code;
char transaction_id[60];
/* Some assumption that our buffer is big enough.. and null terminate */
if (msgb_l2len(msg) > 2000) {
LOGP(DMGCP, LOGL_ERROR, "MGCP message too long.\n");
return;
}
msg->l2h[msgb_l2len(msg)] = '\0';
if (bsc_mgcp_parse_response((const char *) msg->l2h, &code, transaction_id) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to parse response code.\n");
return;
}
for (i = 1; i < bsc->nat->mgcp_cfg->number_endpoints; ++i) {
if (bsc->nat->bsc_endpoints[i].bsc != bsc)
continue;
/* no one listening? a bug? */
if (!bsc->nat->bsc_endpoints[i].transaction_id)
continue;
if (strcmp(transaction_id, bsc->nat->bsc_endpoints[i].transaction_id) != 0)
continue;
endp = &bsc->nat->mgcp_cfg->endpoints[i];
bsc_endp = &bsc->nat->bsc_endpoints[i];
break;
}
if (!bsc_endp) {
LOGP(DMGCP, LOGL_ERROR, "Could not find active endpoint: %s for msg: '%s'\n",
transaction_id, (const char *) msg->l2h);
return;
}
endp->ci = bsc_mgcp_extract_ci((const char *) msg->l2h);
/* free some stuff */
talloc_free(bsc_endp->transaction_id);
bsc_endp->transaction_id = NULL;
/*
* rewrite the information. In case the endpoint was deleted
* there should be nothing for us to rewrite so putting endp->rtp_port
* with the value of 0 should be no problem.
*/
output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg),
bsc->nat->mgcp_cfg->source_addr, endp->rtp_port);
if (!output) {
LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n");
return;
}
if (write_queue_enqueue(&bsc->nat->mgcp_queue, output) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to queue MGCP msg.\n");
msgb_free(output);
}
}
int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60])
{
/* we want to parse two strings */
return sscanf(str, "%3d %59s\n", code, transaction) != 2;
}
int bsc_mgcp_extract_ci(const char *str)
{
int ci;
char *res = strstr(str, "I: ");
if (!res)
return CI_UNUSED;
if (sscanf(res, "I: %d", &ci) != 1)
return CI_UNUSED;
return ci;
}
/* we need to replace some strings... */
struct msgb *bsc_mgcp_rewrite(char *input, int length, const char *ip, int port)
{
static const char *ip_str = "c=IN IP4 ";
static const char *aud_str = "m=audio ";
char buf[128];
char *running, *token;
struct msgb *output;
if (length > 4096 - 128) {
LOGP(DMGCP, LOGL_ERROR, "Input is too long.\n");
return NULL;
}
output = msgb_alloc_headroom(4096, 128, "MGCP rewritten");
if (!output) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate new MGCP msg.\n");
return NULL;
}
running = input;
output->l2h = output->data;
for (token = strsep(&running, "\n"); running; token = strsep(&running, "\n")) {
int len = strlen(token);
int cr = len > 0 && token[len - 1] == '\r';
if (strncmp(ip_str, token, (sizeof ip_str) - 1) == 0) {
output->l3h = msgb_put(output, strlen(ip_str));
memcpy(output->l3h, ip_str, strlen(ip_str));
output->l3h = msgb_put(output, strlen(ip));
memcpy(output->l3h, ip, strlen(ip));
if (cr) {
output->l3h = msgb_put(output, 2);
output->l3h[0] = '\r';
output->l3h[1] = '\n';
} else {
output->l3h = msgb_put(output, 1);
output->l3h[0] = '\n';
}
} else if (strncmp(aud_str, token, (sizeof aud_str) - 1) == 0) {
int payload;
if (sscanf(token, "m=audio %*d RTP/AVP %d", &payload) != 1) {
LOGP(DMGCP, LOGL_ERROR, "Could not parsed audio line.\n");
msgb_free(output);
return NULL;
}
snprintf(buf, sizeof(buf)-1, "m=audio %d RTP/AVP %d%s",
port, payload, cr ? "\r\n" : "\n");
buf[sizeof(buf)-1] = '\0';
output->l3h = msgb_put(output, strlen(buf));
memcpy(output->l3h, buf, strlen(buf));
} else {
output->l3h = msgb_put(output, len + 1);
memcpy(output->l3h, token, len);
output->l3h[len] = '\n';
}
}
return output;
}
static int mgcp_do_read(struct bsc_fd *fd)
{
struct bsc_nat *nat;
struct msgb *msg, *resp;
int rc;
nat = fd->data;
rc = read(fd->fd, nat->mgcp_msg, sizeof(nat->mgcp_msg) - 1);
if (rc <= 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to read errno: %d\n", errno);
return -1;
}
nat->mgcp_msg[rc] = '\0';
nat->mgcp_length = rc;
msg = msgb_alloc(sizeof(nat->mgcp_msg), "MGCP GW Read");
if (!msg) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create buffer.\n");
return -1;
}
msg->l2h = msgb_put(msg, rc);
memcpy(msg->l2h, nat->mgcp_msg, msgb_l2len(msg));
resp = mgcp_handle_message(nat->mgcp_cfg, msg);
msgb_free(msg);
/* we do have a direct answer... e.g. AUEP */
if (resp) {
if (write_queue_enqueue(&nat->mgcp_queue, resp) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to enqueue msg.\n");
msgb_free(resp);
}
}
return 0;
}
static int mgcp_do_write(struct bsc_fd *bfd, struct msgb *msg)
{
int rc;
rc = write(bfd->fd, msg->data, msg->len);
if (rc != msg->len) {
LOGP(DMGCP, LOGL_ERROR, "Failed to write msg to MGCP CallAgent.\n");
return -1;
}
return rc;
}
int bsc_mgcp_nat_init(struct bsc_nat *nat)
{
int on;
struct sockaddr_in addr;
if (!nat->mgcp_cfg->call_agent_addr) {
LOGP(DMGCP, LOGL_ERROR, "The BSC nat requires the call agent ip to be set.\n");
return -1;
}
if (nat->mgcp_cfg->bts_ip) {
LOGP(DMGCP, LOGL_ERROR, "Do not set the BTS ip for the nat.\n");
return -1;
}
nat->mgcp_queue.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
if (nat->mgcp_queue.bfd.fd < 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create MGCP socket. errno: %d\n", errno);
return -1;
}
on = 1;
setsockopt(nat->mgcp_queue.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
addr.sin_family = AF_INET;
addr.sin_port = htons(nat->mgcp_cfg->source_port);
inet_aton(nat->mgcp_cfg->source_addr, &addr.sin_addr);
if (bind(nat->mgcp_queue.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to bind. errno: %d\n", errno);
close(nat->mgcp_queue.bfd.fd);
nat->mgcp_queue.bfd.fd = -1;
return -1;
}
addr.sin_port = htons(2727);
inet_aton(nat->mgcp_cfg->call_agent_addr, &addr.sin_addr);
if (connect(nat->mgcp_queue.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
nat->mgcp_cfg->call_agent_addr, errno);
close(nat->mgcp_queue.bfd.fd);
nat->mgcp_queue.bfd.fd = -1;
return -1;
}
write_queue_init(&nat->mgcp_queue, 10);
nat->mgcp_queue.bfd.when = BSC_FD_READ;
nat->mgcp_queue.bfd.data = nat;
nat->mgcp_queue.read_cb = mgcp_do_read;
nat->mgcp_queue.write_cb = mgcp_do_write;
if (bsc_register_fd(&nat->mgcp_queue.bfd) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to register MGCP fd.\n");
close(nat->mgcp_queue.bfd.fd);
nat->mgcp_queue.bfd.fd = -1;
return -1;
}
/* some more MGCP config handling */
nat->mgcp_cfg->audio_payload = -1;
nat->mgcp_cfg->data = nat;
nat->mgcp_cfg->policy_cb = bsc_mgcp_policy_cb;
nat->mgcp_cfg->force_realloc = 1;
nat->mgcp_cfg->bts_ip = "";
nat->bsc_endpoints = talloc_zero_array(nat,
struct bsc_endpoint,
nat->mgcp_cfg->number_endpoints + 1);
return 0;
}
void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc)
{
int i;
for (i = 1; i < bsc->nat->mgcp_cfg->number_endpoints; ++i) {
struct bsc_endpoint *bsc_endp = &bsc->nat->bsc_endpoints[i];
if (bsc_endp->bsc != bsc)
continue;
bsc_mgcp_free_endpoint(bsc->nat, i);
mgcp_free_endp(&bsc->nat->mgcp_cfg->endpoints[i]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,491 +0,0 @@
/* BSC Multiplexer/NAT Utilities */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc/bsc_nat.h>
#include <openbsc/gsm_data.h>
#include <openbsc/bssap.h>
#include <openbsc/debug.h>
#include <openbsc/ipaccess.h>
#include <osmocore/linuxlist.h>
#include <osmocore/talloc.h>
#include <osmocore/gsm0808.h>
#include <sccp/sccp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
struct bsc_nat *bsc_nat_alloc(void)
{
struct bsc_nat *nat = talloc_zero(tall_bsc_ctx, struct bsc_nat);
if (!nat)
return NULL;
INIT_LLIST_HEAD(&nat->sccp_connections);
INIT_LLIST_HEAD(&nat->bsc_connections);
INIT_LLIST_HEAD(&nat->bsc_configs);
INIT_LLIST_HEAD(&nat->access_lists);
nat->stats.sccp.conn = counter_alloc("nat.sccp.conn");
nat->stats.sccp.calls = counter_alloc("nat.sccp.calls");
nat->stats.bsc.reconn = counter_alloc("nat.bsc.conn");
nat->stats.bsc.auth_fail = counter_alloc("nat.bsc.auth_fail");
nat->stats.msc.reconn = counter_alloc("nat.msc.conn");
nat->msc_ip = talloc_strdup(nat, "127.0.0.1");
nat->msc_port = 5000;
nat->auth_timeout = 2;
nat->ping_timeout = 20;
nat->pong_timeout = 5;
return nat;
}
void bsc_nat_set_msc_ip(struct bsc_nat *nat, const char *ip)
{
if (nat->msc_ip)
talloc_free(nat->msc_ip);
nat->msc_ip = talloc_strdup(nat, ip);
}
struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat)
{
struct bsc_connection *con = talloc_zero(nat, struct bsc_connection);
if (!con)
return NULL;
con->nat = nat;
write_queue_init(&con->write_queue, 100);
return con;
}
struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token, unsigned int lac)
{
struct bsc_config *conf = talloc_zero(nat, struct bsc_config);
if (!conf)
return NULL;
conf->token = talloc_strdup(conf, token);
conf->lac = lac;
conf->nr = nat->num_bsc;
conf->nat = nat;
llist_add_tail(&conf->entry, &nat->bsc_configs);
++nat->num_bsc;
conf->stats.sccp.conn = counter_alloc("nat.bsc.sccp.conn");
conf->stats.sccp.calls = counter_alloc("nat.bsc.sccp.calls");
conf->stats.net.reconn = counter_alloc("nat.bsc.net.reconnects");
return conf;
}
void sccp_connection_destroy(struct sccp_connections *conn)
{
LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n",
sccp_src_ref_to_int(&conn->real_ref),
sccp_src_ref_to_int(&conn->patched_ref), conn->bsc);
bsc_mgcp_dlcx(conn);
llist_del(&conn->list_entry);
talloc_free(conn);
}
struct bsc_connection *bsc_nat_find_bsc(struct bsc_nat *nat, struct msgb *msg, int *lac_out)
{
struct bsc_connection *bsc;
int data_length;
const u_int8_t *data;
struct tlv_parsed tp;
int i = 0;
*lac_out = -1;
if (!msg->l3h || msgb_l3len(msg) < 3) {
LOGP(DNAT, LOGL_ERROR, "Paging message is too short.\n");
return NULL;
}
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n");
return NULL;
}
data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
/* No need to try a different BSS */
if (data[0] == CELL_IDENT_BSS) {
return NULL;
} else if (data[0] != CELL_IDENT_LAC) {
LOGP(DNAT, LOGL_ERROR, "Unhandled cell ident discrminator: %d\n", data[0]);
return NULL;
}
/* Currently we only handle one BSC */
for (i = 1; i < data_length - 1; i += 2) {
unsigned int _lac = ntohs(*(unsigned int *) &data[i]);
*lac_out = _lac;
llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) {
if (!bsc->cfg)
continue;
if (!bsc->authenticated || _lac != bsc->cfg->lac)
continue;
return bsc;
}
}
return NULL;
}
int bsc_write_mgcp(struct bsc_connection *bsc, const u_int8_t *data, unsigned int length)
{
struct msgb *msg;
if (length > 4096 - 128) {
LOGP(DINP, LOGL_ERROR, "Can not send message of that size.\n");
return -1;
}
msg = msgb_alloc_headroom(4096, 128, "to-bsc");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n");
return -1;
}
/* copy the data */
msg->l3h = msgb_put(msg, length);
memcpy(msg->l3h, data, length);
return bsc_write(bsc, msg, NAT_IPAC_PROTO_MGCP);
}
int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int proto)
{
/* prepend the header */
ipaccess_prepend_header(msg, proto);
if (write_queue_enqueue(&bsc->write_queue, msg) != 0) {
LOGP(DINP, LOGL_ERROR, "Failed to enqueue the write.\n");
msgb_free(msg);
return -1;
}
return 0;
}
static int lst_check_allow(struct bsc_nat_acc_lst *lst, const char *mi_string)
{
struct bsc_nat_acc_lst_entry *entry;
llist_for_each_entry(entry, &lst->fltr_list, list) {
if (!entry->imsi_allow)
continue;
if (regexec(&entry->imsi_allow_re, mi_string, 0, NULL, 0) == 0)
return 0;
}
return 1;
}
static int lst_check_deny(struct bsc_nat_acc_lst *lst, const char *mi_string)
{
struct bsc_nat_acc_lst_entry *entry;
llist_for_each_entry(entry, &lst->fltr_list, list) {
if (!entry->imsi_deny)
continue;
if (regexec(&entry->imsi_deny_re, mi_string, 0, NULL, 0) == 0)
return 0;
}
return 1;
}
/* apply white/black list */
static int auth_imsi(struct bsc_connection *bsc, const char *mi_string)
{
/*
* Now apply blacklist/whitelist of the BSC and the NAT.
* 1.) Reject if the IMSI is not allowed at the BSC
* 2.) Allow directly if the IMSI is allowed at the BSC
* 3.) Reject if the IMSI not allowed at the global level.
* 4.) Allow directly if the IMSI is allowed at the global level
*/
struct bsc_nat_acc_lst *nat_lst = NULL;
struct bsc_nat_acc_lst *bsc_lst = NULL;
bsc_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->cfg->acc_lst_name);
nat_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->nat->acc_lst_name);
if (bsc_lst) {
/* 1. BSC deny */
if (lst_check_deny(bsc_lst, mi_string) == 0) {
LOGP(DNAT, LOGL_ERROR,
"Filtering %s by imsi_deny on bsc nr: %d.\n", mi_string, bsc->cfg->nr);
return -2;
}
/* 2. BSC allow */
if (lst_check_allow(bsc_lst, mi_string) == 0)
return 0;
}
/* 3. NAT deny */
if (nat_lst) {
if (lst_check_deny(nat_lst, mi_string) == 0) {
LOGP(DNAT, LOGL_ERROR,
"Filtering %s by nat imsi_deny on bsc nr: %d.\n", mi_string, bsc->cfg->nr);
return -3;
}
}
return 0;
}
static int _cr_check_loc_upd(struct bsc_connection *bsc, uint8_t *data, unsigned int length)
{
u_int8_t mi_type;
struct gsm48_loc_upd_req *lu;
char mi_string[GSM48_MI_SIZE];
if (length < sizeof(*lu)) {
LOGP(DNAT, LOGL_ERROR,
"LU does not fit. Length is %d \n", length);
return -1;
}
lu = (struct gsm48_loc_upd_req *) data;
mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
/*
* We can only deal with the IMSI. This will fail for a phone that
* will send the TMSI of a previous network to us.
*/
if (mi_type != GSM_MI_TYPE_IMSI)
return 0;
gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len);
return auth_imsi(bsc, mi_string);
}
static int _cr_check_cm_serv_req(struct bsc_connection *bsc, uint8_t *data, unsigned int length)
{
static const uint32_t classmark_offset =
offsetof(struct gsm48_service_request, classmark);
char mi_string[GSM48_MI_SIZE];
uint8_t mi_type;
int rc;
struct gsm48_service_request *req;
/* unfortunately in Phase1 the classmark2 length is variable */
if (length < sizeof(*req)) {
LOGP(DNAT, LOGL_ERROR,
"CM Serv Req does not fit. Length is %d\n", length);
return -1;
}
req = (struct gsm48_service_request *) data;
rc = gsm48_extract_mi((uint8_t *) &req->classmark,
length - classmark_offset, mi_string, &mi_type);
if (rc < 0) {
LOGP(DNAT, LOGL_ERROR, "Failed to parse the classmark2/mi. error: %d\n", rc);
return -1;
}
/* we have to let the TMSI or such pass */
if (mi_type != GSM_MI_TYPE_IMSI)
return 0;
return auth_imsi(bsc, mi_string);
}
static int _cr_check_pag_resp(struct bsc_connection *bsc, uint8_t *data, unsigned int length)
{
struct gsm48_pag_resp *resp;
char mi_string[GSM48_MI_SIZE];
u_int8_t mi_type;
if (length < sizeof(*resp)) {
LOGP(DNAT, LOGL_ERROR, "PAG RESP does not fit. Length was %d.\n", length);
return -1;
}
resp = (struct gsm48_pag_resp *) data;
if (gsm48_paging_extract_mi(resp, length, mi_string, &mi_type) < 0) {
LOGP(DNAT, LOGL_ERROR, "Failed to extract the MI.\n");
return -1;
}
/* we need to let it pass for now */
if (mi_type != GSM_MI_TYPE_IMSI)
return 0;
return auth_imsi(bsc, mi_string);
}
/* Filter out CR data... */
int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed, int *con_type)
{
struct tlv_parsed tp;
struct gsm48_hdr *hdr48;
int hdr48_len;
int len;
*con_type = NAT_CON_TYPE_NONE;
if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) {
LOGP(DNAT, LOGL_ERROR,
"Rejecting CR message due wrong GSM Type %d\n", parsed->gsm_type);
return -1;
}
/* the parsed has had some basic l3 length check */
len = msg->l3h[1];
if (msgb_l3len(msg) - 3 < len) {
LOGP(DNAT, LOGL_ERROR,
"The CR Data has not enough space...\n");
return -1;
}
msg->l4h = &msg->l3h[3];
len -= 1;
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h, len, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) {
LOGP(DNAT, LOGL_ERROR, "CR Data does not contain layer3 information.\n");
return -1;
}
hdr48_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION);
if (hdr48_len < sizeof(*hdr48)) {
LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n");
return -1;
}
hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION);
if (hdr48->proto_discr == GSM48_PDISC_MM &&
hdr48->msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) {
*con_type = NAT_CON_TYPE_LU;
return _cr_check_loc_upd(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48));
} else if (hdr48->proto_discr == GSM48_PDISC_MM &&
hdr48->msg_type == GSM48_MT_MM_CM_SERV_REQ) {
*con_type = NAT_CON_TYPE_CM_SERV_REQ;
return _cr_check_cm_serv_req(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48));
} else if (hdr48->proto_discr == GSM48_PDISC_RR &&
hdr48->msg_type == GSM48_MT_RR_PAG_RESP) {
*con_type = NAT_CON_TYPE_PAG_RESP;
return _cr_check_pag_resp(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48));
} else {
/* We only want to filter the above, let other things pass */
*con_type = NAT_CON_TYPE_OTHER;
return 0;
}
}
void bsc_parse_reg(void *ctx, regex_t *reg, char **imsi, int argc, const char **argv)
{
if (*imsi) {
talloc_free(*imsi);
*imsi = NULL;
}
regfree(reg);
if (argc > 0) {
*imsi = talloc_strdup(ctx, argv[0]);
regcomp(reg, argv[0], 0);
}
}
static const char *con_types [] = {
[NAT_CON_TYPE_NONE] = "n/a",
[NAT_CON_TYPE_LU] = "Location Update",
[NAT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req",
[NAT_CON_TYPE_PAG_RESP] = "Paging Response",
[NAT_CON_TYPE_LOCAL_REJECT] = "Local Reject",
[NAT_CON_TYPE_OTHER] = "Other",
};
const char *bsc_con_type_to_string(int type)
{
return con_types[type];
}
struct bsc_nat_acc_lst *bsc_nat_acc_lst_find(struct bsc_nat *nat, const char *name)
{
struct bsc_nat_acc_lst *lst;
if (!name)
return NULL;
llist_for_each_entry(lst, &nat->access_lists, list)
if (strcmp(lst->name, name) == 0)
return lst;
return NULL;
}
struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *name)
{
struct bsc_nat_acc_lst *lst;
lst = bsc_nat_acc_lst_find(nat, name);
if (lst)
return lst;
lst = talloc_zero(nat, struct bsc_nat_acc_lst);
if (!lst) {
LOGP(DNAT, LOGL_ERROR, "Failed to allocate access list");
return NULL;
}
INIT_LLIST_HEAD(&lst->fltr_list);
lst->name = talloc_strdup(lst, name);
llist_add_tail(&lst->list, &nat->access_lists);
return lst;
}
void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst)
{
llist_del(&lst->list);
talloc_free(lst);
}
struct bsc_nat_acc_lst_entry *bsc_nat_acc_lst_entry_create(struct bsc_nat_acc_lst *lst)
{
struct bsc_nat_acc_lst_entry *entry;
entry = talloc_zero(lst, struct bsc_nat_acc_lst_entry);
if (!entry)
return NULL;
llist_add_tail(&entry->list, &lst->fltr_list);
return entry;
}

View File

@@ -1,565 +0,0 @@
/* OpenBSC NAT interface to quagga VTY */
/* (C) 2010 by Holger Hans Peter Freyther
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <vty/command.h>
#include <vty/buffer.h>
#include <vty/vty.h>
#include <openbsc/bsc_nat.h>
#include <openbsc/bsc_msc.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/mgcp.h>
#include <openbsc/vty.h>
#include <osmocore/talloc.h>
#include <sccp/sccp.h>
#include <stdlib.h>
static struct bsc_nat *_nat;
static struct cmd_node nat_node = {
NAT_NODE,
"%s(nat)#",
1,
};
static struct cmd_node bsc_node = {
BSC_NODE,
"%s(bsc)#",
1,
};
static void write_acc_lst(struct vty *vty, struct bsc_nat_acc_lst *lst)
{
struct bsc_nat_acc_lst_entry *entry;
llist_for_each_entry(entry, &lst->fltr_list, list) {
if (entry->imsi_allow)
vty_out(vty, " access-list %s imsi-allow %s%s",
lst->name, entry->imsi_allow, VTY_NEWLINE);
if (entry->imsi_deny)
vty_out(vty, " access-list %s imsi-deny %s%s",
lst->name, entry->imsi_deny, VTY_NEWLINE);
}
}
static int config_write_nat(struct vty *vty)
{
struct bsc_nat_acc_lst *lst;
vty_out(vty, "nat%s", VTY_NEWLINE);
vty_out(vty, " msc ip %s%s", _nat->msc_ip, VTY_NEWLINE);
vty_out(vty, " msc port %d%s", _nat->msc_port, VTY_NEWLINE);
vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE);
vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE);
vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE);
if (_nat->token)
vty_out(vty, " token %s%s", _nat->token, VTY_NEWLINE);
vty_out(vty, " ip-tos %d%s", _nat->bsc_ip_tos, VTY_NEWLINE);
if (_nat->acc_lst_name)
vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE);
llist_for_each_entry(lst, &_nat->access_lists, list) {
write_acc_lst(vty, lst);
}
return CMD_SUCCESS;
}
static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc)
{
vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE);
vty_out(vty, " token %s%s", bsc->token, VTY_NEWLINE);
vty_out(vty, " location_area_code %u%s", bsc->lac, VTY_NEWLINE);
vty_out(vty, " paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE);
if (bsc->description)
vty_out(vty, " description %s%s", bsc->description, VTY_NEWLINE);
if (bsc->acc_lst_name)
vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE);
}
static int config_write_bsc(struct vty *vty)
{
struct bsc_config *bsc;
llist_for_each_entry(bsc, &_nat->bsc_configs, entry)
config_write_bsc_single(vty, bsc);
return CMD_SUCCESS;
}
DEFUN(show_sccp, show_sccp_cmd, "show sccp connections",
SHOW_STR "Display information about current SCCP connections")
{
struct sccp_connections *con;
vty_out(vty, "Listing all opening SCCP connections%s", VTY_NEWLINE);
llist_for_each_entry(con, &_nat->sccp_connections, list_entry) {
vty_out(vty, "For BSC Nr: %d lac: %d; BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s",
con->bsc->cfg ? con->bsc->cfg->nr : -1,
con->bsc->cfg ? con->bsc->cfg->lac : -1,
sccp_src_ref_to_int(&con->real_ref),
sccp_src_ref_to_int(&con->patched_ref),
con->has_remote_ref,
sccp_src_ref_to_int(&con->remote_ref),
con->msc_timeslot, con->bsc_timeslot,
bsc_con_type_to_string(con->con_type),
VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(show_bsc, show_bsc_cmd, "show bsc connections",
SHOW_STR "Display information about current BSCs")
{
struct bsc_connection *con;
struct sockaddr_in sock;
socklen_t len = sizeof(sock);
llist_for_each_entry(con, &_nat->bsc_connections, list_entry) {
getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
vty_out(vty, "BSC nr: %d lac: %d auth: %d fd: %d peername: %s%s",
con->cfg ? con->cfg->nr : -1,
con->cfg ? con->cfg->lac : -1,
con->authenticated, con->write_queue.bfd.fd,
inet_ntoa(sock.sin_addr), VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(show_bsc_cfg, show_bsc_cfg_cmd, "show bsc config",
SHOW_STR "Display information about known BSC configs")
{
struct bsc_config *conf;
llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
vty_out(vty, "BSC token: '%s' lac: %u nr: %u%s",
conf->token, conf->lac, conf->nr, VTY_NEWLINE);
if (conf->acc_lst_name)
vty_out(vty, " access-list: %s%s",
conf->acc_lst_name, VTY_NEWLINE);
vty_out(vty, " paging forbidden: %d%s",
conf->forbid_paging, VTY_NEWLINE);
if (conf->description)
vty_out(vty, " description: %s%s", conf->description, VTY_NEWLINE);
else
vty_out(vty, " No description.%s", VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(show_stats,
show_stats_cmd,
"show statistics [NR]",
SHOW_STR "Display network statistics")
{
struct bsc_config *conf;
int nr = -1;
if (argc == 1)
nr = atoi(argv[0]);
vty_out(vty, "NAT statistics%s", VTY_NEWLINE);
vty_out(vty, " SCCP Connections %lu total, %lu calls%s",
counter_get(_nat->stats.sccp.conn),
counter_get(_nat->stats.sccp.calls), VTY_NEWLINE);
vty_out(vty, " MSC Connections %lu%s",
counter_get(_nat->stats.msc.reconn), VTY_NEWLINE);
vty_out(vty, " BSC Connections %lu total, %lu auth failed.%s",
counter_get(_nat->stats.bsc.reconn),
counter_get(_nat->stats.bsc.auth_fail), VTY_NEWLINE);
llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
if (argc == 1 && nr != conf->nr)
continue;
vty_out(vty, " BSC lac: %d nr: %d%s",
conf->lac, conf->nr, VTY_NEWLINE);
vty_out(vty, " SCCP Connnections %lu total, %lu calls%s",
counter_get(conf->stats.sccp.conn),
counter_get(conf->stats.sccp.calls), VTY_NEWLINE);
vty_out(vty, " BSC Connections %lu total%s",
counter_get(conf->stats.net.reconn), VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(show_msc,
show_msc_cmd,
"show msc connection",
SHOW_STR "Show the status of the MSC connection.")
{
if (!_nat->msc_con) {
vty_out(vty, "The MSC is not yet configured.\n");
return CMD_WARNING;
}
vty_out(vty, "MSC on %s:%d is connected: %d%s\n",
_nat->msc_con->ip, _nat->msc_con->port,
_nat->msc_con->is_connected, VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(close_bsc,
close_bsc_cmd,
"close bsc connection BSC_NR",
"Close the connection with the BSC identified by the config number.")
{
struct bsc_connection *bsc;
int bsc_nr = atoi(argv[0]);
llist_for_each_entry(bsc, &_nat->bsc_connections, list_entry) {
if (!bsc->cfg || bsc->cfg->nr != bsc_nr)
continue;
bsc_close_connection(bsc);
break;
}
return CMD_SUCCESS;
}
DEFUN(cfg_nat, cfg_nat_cmd, "nat", "Configute the NAT")
{
vty->index = _nat;
vty->node = NAT_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_nat_msc_ip,
cfg_nat_msc_ip_cmd,
"msc ip A.B.C.D",
"Set the IP address of the MSC.")
{
bsc_nat_set_msc_ip(_nat, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_msc_port,
cfg_nat_msc_port_cmd,
"msc port <1-65500>",
"Set the port of the MSC.")
{
_nat->msc_port = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_auth_time,
cfg_nat_auth_time_cmd,
"timeout auth <1-256>",
"The time to wait for an auth response.")
{
_nat->auth_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_ping_time,
cfg_nat_ping_time_cmd,
"timeout ping NR",
"Send a ping every NR seconds. Negative to disable.")
{
_nat->ping_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_pong_time,
cfg_nat_pong_time_cmd,
"timeout pong NR",
"Wait NR seconds for the PONG response. Should be smaller than ping.")
{
_nat->pong_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_token, cfg_nat_token_cmd,
"token TOKEN",
"Set a token for the NAT")
{
if (_nat->token)
talloc_free(_nat->token);
_nat->token = talloc_strdup(_nat, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_bsc_ip_tos, cfg_nat_bsc_ip_tos_cmd,
"ip-tos <0-255>",
"Set the IP_TOS for the BSCs to use\n" "Set the IP_TOS attribute")
{
_nat->bsc_ip_tos = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_acc_lst_name,
cfg_nat_acc_lst_name_cmd,
"access-list-name NAME",
"Set the name of the access list to use.\n"
"The name of the to be used access list.")
{
if (_nat->acc_lst_name)
talloc_free(_nat->acc_lst_name);
_nat->acc_lst_name = talloc_strdup(_nat, argv[0]);
return CMD_SUCCESS;
}
/* per BSC configuration */
DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", "Select a BSC to configure")
{
int bsc_nr = atoi(argv[0]);
struct bsc_config *bsc;
if (bsc_nr > _nat->num_bsc) {
vty_out(vty, "%% The next unused BSC number is %u%s",
_nat->num_bsc, VTY_NEWLINE);
return CMD_WARNING;
} else if (bsc_nr == _nat->num_bsc) {
/* allocate a new one */
bsc = bsc_config_alloc(_nat, "unknown", 0);
} else
bsc = bsc_config_num(_nat, bsc_nr);
if (!bsc)
return CMD_WARNING;
vty->index = bsc;
vty->node = BSC_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", "Set the token")
{
struct bsc_config *conf = vty->index;
if (conf->token)
talloc_free(conf->token);
conf->token = talloc_strdup(conf, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>",
"Set the Location Area Code (LAC) of this BSC")
{
struct bsc_config *tmp;
struct bsc_config *conf = vty->index;
int lac = atoi(argv[0]);
if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
lac, VTY_NEWLINE);
return CMD_WARNING;
}
/* verify that the LACs are unique */
llist_for_each_entry(tmp, &_nat->bsc_configs, entry) {
if (tmp->lac == lac) {
vty_out(vty, "%% LAC %d is already used.%s", lac, VTY_NEWLINE);
return CMD_ERR_INCOMPLETE;
}
}
conf->lac = lac;
return CMD_SUCCESS;
}
DEFUN(cfg_lst_imsi_allow,
cfg_lst_imsi_allow_cmd,
"access-list NAME imsi-allow [REGEXP]",
"Allow IMSIs matching the REGEXP\n"
"The name of the access-list\n"
"The regexp of allowed IMSIs\n")
{
struct bsc_nat_acc_lst *acc;
struct bsc_nat_acc_lst_entry *entry;
acc = bsc_nat_acc_lst_get(_nat, argv[0]);
if (!acc)
return CMD_WARNING;
entry = bsc_nat_acc_lst_entry_create(acc);
if (!entry)
return CMD_WARNING;
bsc_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, argc - 1, &argv[1]);
return CMD_SUCCESS;
}
DEFUN(cfg_lst_imsi_deny,
cfg_lst_imsi_deny_cmd,
"access-list NAME imsi-deny [REGEXP]",
"Allow IMSIs matching the REGEXP\n"
"The name of the access-list\n"
"The regexp of to be denied IMSIs\n")
{
struct bsc_nat_acc_lst *acc;
struct bsc_nat_acc_lst_entry *entry;
acc = bsc_nat_acc_lst_get(_nat, argv[0]);
if (!acc)
return CMD_WARNING;
entry = bsc_nat_acc_lst_entry_create(acc);
if (!entry)
return CMD_WARNING;
bsc_parse_reg(acc, &entry->imsi_deny_re, &entry->imsi_deny, argc - 1, &argv[1]);
return CMD_SUCCESS;
}
/* naming to follow Zebra... */
DEFUN(cfg_lst_no,
cfg_lst_no_cmd,
"no access-list NAME",
NO_STR "Remove an access-list by name\n"
"The access-list to remove\n")
{
struct bsc_nat_acc_lst *acc;
acc = bsc_nat_acc_lst_find(_nat, argv[0]);
if (!acc)
return CMD_WARNING;
bsc_nat_acc_lst_delete(acc);
return CMD_SUCCESS;
}
DEFUN(cfg_bsc_acc_lst_name,
cfg_bsc_acc_lst_name_cmd,
"access-list-name NAME",
"Set the name of the access list to use.\n"
"The name of the to be used access list.")
{
struct bsc_config *conf = vty->index;
if (conf->acc_lst_name)
talloc_free(conf->acc_lst_name);
conf->acc_lst_name = talloc_strdup(conf, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_bsc_paging,
cfg_bsc_paging_cmd,
"paging forbidden (0|1)",
"Forbid sending PAGING REQUESTS to the BSC.")
{
struct bsc_config *conf = vty->index;
if (strcmp("1", argv[0]) == 0)
conf->forbid_paging = 1;
else
conf->forbid_paging = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_bsc_desc,
cfg_bsc_desc_cmd,
"description DESC",
"Provide a description for the given BSC.")
{
struct bsc_config *conf = vty->index;
if (conf->description)
talloc_free(conf->description);
conf->description = talloc_strdup(conf, argv[0]);
return CMD_SUCCESS;
}
DEFUN(test_regex, test_regex_cmd,
"test regex PATTERN STRING",
"Check if the string is matching the current pattern.")
{
regex_t reg;
char *str = NULL;
memset(&reg, 0, sizeof(reg));
bsc_parse_reg(_nat, &reg, &str, 1, argv);
vty_out(vty, "String matches allow pattern: %d%s",
regexec(&reg, argv[1], 0, NULL, 0) == 0, VTY_NEWLINE);
talloc_free(str);
regfree(&reg);
return CMD_SUCCESS;
}
int bsc_nat_vty_init(struct bsc_nat *nat)
{
_nat = nat;
cmd_init(1);
vty_init();
/* show commands */
install_element(VIEW_NODE, &show_sccp_cmd);
install_element(VIEW_NODE, &show_bsc_cmd);
install_element(VIEW_NODE, &show_bsc_cfg_cmd);
install_element(VIEW_NODE, &show_stats_cmd);
install_element(VIEW_NODE, &close_bsc_cmd);
install_element(VIEW_NODE, &show_msc_cmd);
install_element(VIEW_NODE, &test_regex_cmd);
openbsc_vty_add_cmds();
/* nat group */
install_element(CONFIG_NODE, &cfg_nat_cmd);
install_node(&nat_node, config_write_nat);
install_default(NAT_NODE);
install_element(NAT_NODE, &cfg_nat_msc_ip_cmd);
install_element(NAT_NODE, &cfg_nat_msc_port_cmd);
install_element(NAT_NODE, &cfg_nat_auth_time_cmd);
install_element(NAT_NODE, &cfg_nat_ping_time_cmd);
install_element(NAT_NODE, &cfg_nat_pong_time_cmd);
install_element(NAT_NODE, &cfg_nat_token_cmd);
install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd);
install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd);
/* access-list */
install_element(NAT_NODE, &cfg_lst_imsi_allow_cmd);
install_element(NAT_NODE, &cfg_lst_imsi_deny_cmd);
install_element(NAT_NODE, &cfg_lst_no_cmd);
/* BSC subgroups */
install_element(NAT_NODE, &cfg_bsc_cmd);
install_node(&bsc_node, config_write_bsc);
install_default(BSC_NODE);
install_element(BSC_NODE, &cfg_bsc_token_cmd);
install_element(BSC_NODE, &cfg_bsc_lac_cmd);
install_element(BSC_NODE, &cfg_bsc_paging_cmd);
install_element(BSC_NODE, &cfg_bsc_desc_cmd);
install_element(BSC_NODE, &cfg_bsc_acc_lst_name_cmd);
mgcp_vty_init();
return 0;
}
/* called by the telnet interface... we have our own init above */
void bsc_vty_init()
{}

View File

@@ -1,237 +0,0 @@
/* SCCP patching and handling routines */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc/debug.h>
#include <openbsc/bsc_nat.h>
#include <sccp/sccp.h>
#include <osmocore/talloc.h>
#include <string.h>
#include <time.h>
static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2)
{
return memcmp(ref1, ref2, sizeof(*ref1)) == 0;
}
/*
* SCCP patching below
*/
/* check if we are using this ref for patched already */
static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat)
{
struct sccp_connections *conn;
llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
if (memcmp(ref, &conn->patched_ref, sizeof(*ref)) == 0)
return -1;
}
return 0;
}
/* copied from sccp.c */
static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat)
{
static u_int32_t last_ref = 0x50000;
int wrapped = 0;
do {
struct sccp_source_reference reference;
reference.octet1 = (last_ref >> 0) & 0xff;
reference.octet2 = (last_ref >> 8) & 0xff;
reference.octet3 = (last_ref >> 16) & 0xff;
++last_ref;
/* do not use the reversed word and wrap around */
if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n");
last_ref = 0;
++wrapped;
}
if (sccp_ref_is_free(&reference, nat) == 0) {
*ref = reference;
return 0;
}
} while (wrapped != 2);
LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n");
return -1;
}
struct sccp_connections *create_sccp_src_ref(struct bsc_connection *bsc,
struct bsc_nat_parsed *parsed)
{
struct sccp_connections *conn;
/* Some commercial BSCs like to reassign there SRC ref */
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
if (conn->bsc != bsc)
continue;
if (memcmp(&conn->real_ref, parsed->src_local_ref, sizeof(conn->real_ref)) != 0)
continue;
/* the BSC has reassigned the SRC ref and we failed to keep track */
memset(&conn->remote_ref, 0, sizeof(conn->remote_ref));
if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n",
bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref));
bsc_mgcp_dlcx(conn);
llist_del(&conn->list_entry);
talloc_free(conn);
return NULL;
} else {
clock_gettime(CLOCK_MONOTONIC, &conn->creation_time);
bsc_mgcp_dlcx(conn);
return conn;
}
}
conn = talloc_zero(bsc->nat, struct sccp_connections);
if (!conn) {
LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n");
return NULL;
}
conn->bsc = bsc;
clock_gettime(CLOCK_MONOTONIC, &conn->creation_time);
conn->real_ref = *parsed->src_local_ref;
if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n");
talloc_free(conn);
return NULL;
}
bsc_mgcp_init(conn);
llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections);
counter_inc(bsc->cfg->stats.sccp.conn);
counter_inc(bsc->cfg->nat->stats.sccp.conn);
LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n",
sccp_src_ref_to_int(&conn->real_ref),
sccp_src_ref_to_int(&conn->patched_ref), bsc);
return conn;
}
int update_sccp_src_ref(struct sccp_connections *sccp, struct bsc_nat_parsed *parsed)
{
if (!parsed->dest_local_ref || !parsed->src_local_ref) {
LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n");
return -1;
}
sccp->remote_ref = *parsed->src_local_ref;
sccp->has_remote_ref = 1;
LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n",
sccp_src_ref_to_int(&sccp->patched_ref),
sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc);
return 0;
}
void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed)
{
struct sccp_connections *conn;
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
if (memcmp(parsed->src_local_ref,
&conn->patched_ref, sizeof(conn->patched_ref)) == 0) {
sccp_connection_destroy(conn);
return;
}
}
LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n",
sccp_src_ref_to_int(parsed->src_local_ref));
}
/*
* We have a message from the MSC to the BSC. The MSC is using
* an address that was assigned by the MUX, we need to update the
* dest reference to the real network.
*/
struct sccp_connections *patch_sccp_src_ref_to_bsc(struct msgb *msg,
struct bsc_nat_parsed *parsed,
struct bsc_nat *nat)
{
struct sccp_connections *conn;
if (!parsed->dest_local_ref) {
LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n");
return NULL;
}
llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
if (!equal(parsed->dest_local_ref, &conn->patched_ref))
continue;
/* Change the dest address to the real one */
*parsed->dest_local_ref = conn->real_ref;
return conn;
}
return NULL;
}
/*
* These are message to the MSC. We will need to find the BSC
* Connection by either the SRC or the DST local reference.
*
* In case of a CR we need to work by the SRC local reference
* in all other cases we need to work by the destination local
* reference..
*/
struct sccp_connections *patch_sccp_src_ref_to_msc(struct msgb *msg,
struct bsc_nat_parsed *parsed,
struct bsc_connection *bsc)
{
struct sccp_connections *conn;
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
if (conn->bsc != bsc)
continue;
if (parsed->src_local_ref) {
if (equal(parsed->src_local_ref, &conn->real_ref)) {
*parsed->src_local_ref = conn->patched_ref;
return conn;
}
} else if (parsed->dest_local_ref) {
if (equal(parsed->dest_local_ref, &conn->remote_ref))
return conn;
} else {
LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n");
return NULL;
}
}
return NULL;
}

View File

@@ -451,7 +451,8 @@ static int generate_si13(u_int8_t *output, struct gsm_bts *bts)
if (ret < 0)
return ret;
si13->header.l2_plen = ret & 0xff;
/* length is coded in bit 2 an up */
si13->header.l2_plen = 0x01;
return sizeof (*si13) + ret;
}

View File

@@ -47,7 +47,6 @@ Boston, MA 02111-1307, USA. */
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/bsc_nat.h>
#include <osmocore/talloc.h>
void *tall_vty_cmd_ctx;
@@ -1950,13 +1949,6 @@ enum node_type vty_go_parent(struct vty *vty)
subscr_put(vty->index);
vty->index = NULL;
break;
case BSC_NODE:
vty->node = NAT_NODE;
{
struct bsc_config *bsc = vty->index;
vty->index = bsc->nat;
}
break;
default:
vty->node = CONFIG_NODE;
}
@@ -2373,14 +2365,6 @@ DEFUN(config_exit,
case MGCP_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
case NAT_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
case BSC_NODE:
vty->node = NAT_NODE;
vty->index = NULL;
break;
default:
break;

View File

@@ -1,4 +1,4 @@
/* OpenBSC interface to quagga VTY */
/* ipenBSC interface to quagga VTY */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
@@ -500,6 +500,7 @@ static int config_write_net(struct vty *vty)
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);
vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE);
vty_out(vty, " ipacc rtp_payload %u%s", gsmnet->rtp_payload, VTY_NEWLINE);
vty_out(vty, " rtp base %u%s", gsmnet->rtp_base_port, VTY_NEWLINE);
@@ -524,9 +525,12 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " bsc_token %s%s", gsmnet->bsc_token, VTY_NEWLINE);
vty_out(vty, " msc ip %s%s", gsmnet->msc_ip, VTY_NEWLINE);
vty_out(vty, " msc port %d%s", gsmnet->msc_port, VTY_NEWLINE);
vty_out(vty, " msc ip-tos %d%s", gsmnet->msc_prio, VTY_NEWLINE);
vty_out(vty, " msc ip-dscp %d%s", gsmnet->msc_ip_dscp, VTY_NEWLINE);
vty_out(vty, " timeout ping %d%s", gsmnet->ping_timeout, VTY_NEWLINE);
vty_out(vty, " timeout pong %d%s", gsmnet->pong_timeout, VTY_NEWLINE);
if (gsmnet->ussd_grace_txt)
vty_out(vty, " bsc-grace-text %s%s", gsmnet->ussd_grace_txt, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -779,6 +783,19 @@ static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan)
VTY_NEWLINE);
}
static void lchan_dump_status_vty(struct vty *vty, struct gsm_lchan *lchan)
{
vty_out(vty, "Lchan: %u/%u/%u/%u Type: %s State: %s ref: %u HO: %d Subscriber: %d "
"Time: %lu SAPI: %d/%d%s",
lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type),
gsm_lchans_name(lchan->state), lchan->conn.use_count,
lchan->conn.hand_off,
lchan->conn.subscr != NULL, (unsigned long) lchan->alloc_time.tv_sec,
lchan->sapis[0], lchan->sapis[3],
VTY_NEWLINE);
}
static int lchan_summary(struct vty *vty, int argc, const char **argv,
void (*dump_cb)(struct vty *, struct gsm_lchan *))
{
@@ -859,12 +876,20 @@ DEFUN(show_lchan,
DEFUN(show_lchan_summary,
show_lchan_summary_cmd,
"show lchan summary [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
"show lchan-summary [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
SHOW_STR "Display a short summary about a logical channel\n")
{
return lchan_summary(vty, argc, argv, lchan_dump_short_vty);
}
DEFUN(show_lchan_status,
show_lchan_status_cmd,
"show lchan-status [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
SHOW_STR "Display a short stat about a logical channel\n")
{
return lchan_summary(vty, argc, argv, lchan_dump_status_vty);
}
static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv)
{
vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE);
@@ -1392,13 +1417,17 @@ DEFUN(cfg_net_msc_port,
DEFUN(cfg_net_msc_prio,
cfg_net_msc_prio_cmd,
"msc ip-tos <0-255>",
"msc ip-dscp <0-255>",
"Set the IP_TOS socket attribite")
{
gsmnet->msc_prio = atoi(argv[0]);
gsmnet->msc_ip_dscp = atoi(argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_net_msc_prio, cfg_net_msc_ip_tos_cmd,
"msc ip-tos <0-255>",
"Set the IP_TOS socket attribite\n" "The DSCP to use.\n")
DEFUN(cfg_net_ping_time,
cfg_net_ping_time_cmd,
"timeout ping NR",
@@ -1417,6 +1446,22 @@ DEFUN(cfg_net_pong_time,
return CMD_SUCCESS;
}
DEFUN(cfg_net_grace_ussd,
cfg_net_grace_ussd_cmd,
"bsc-grace-text .TEXT",
"Set the USSD notifcation to be send.\n" "Text to be sent\n")
{
char *data = argv_concat(argv, argc, 1);
if (!data)
return CMD_WARNING;
if (gsmnet->ussd_grace_txt)
talloc_free(gsmnet->ussd_grace_txt);
gsmnet->ussd_grace_txt = talloc_strdup(gsmnet, data);
talloc_free(data);
return CMD_SUCCESS;
}
#define DECLARE_TIMER(number, doc) \
DEFUN(cfg_net_T##number, \
cfg_net_T##number##_cmd, \
@@ -1447,6 +1492,16 @@ DECLARE_TIMER(3117, "Currently not used.")
DECLARE_TIMER(3119, "Currently not used.")
DECLARE_TIMER(3141, "Currently not used.")
DEFUN(cfg_net_dtx,
cfg_net_dtx_cmd,
"dtx-used (0|1)",
"Enable the usage of DTX.\n"
"DTX is enabled/disabled")
{
gsmnet->dtx_enabled = atoi(argv[0]);
return CMD_SUCCESS;
}
/* per-BTS configuration */
DEFUN(cfg_bts,
cfg_bts_cmd,
@@ -2171,6 +2226,7 @@ int bsc_vty_init(struct gsm_network *net)
install_element(VIEW_NODE, &show_ts_cmd);
install_element(VIEW_NODE, &show_lchan_cmd);
install_element(VIEW_NODE, &show_lchan_summary_cmd);
install_element(VIEW_NODE, &show_lchan_status_cmd);
install_element(VIEW_NODE, &show_e1drv_cmd);
install_element(VIEW_NODE, &show_e1line_cmd);
@@ -2219,13 +2275,16 @@ int bsc_vty_init(struct gsm_network *net)
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_net_dtx_cmd);
install_element(GSMNET_NODE, &cfg_net_bsc_token_cmd);
install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd);
install_element(GSMNET_NODE, &cfg_net_msc_ip_cmd);
install_element(GSMNET_NODE, &cfg_net_msc_port_cmd);
install_element(GSMNET_NODE, &cfg_net_msc_ip_tos_cmd);
install_element(GSMNET_NODE, &cfg_net_msc_prio_cmd);
install_element(GSMNET_NODE, &cfg_net_ping_time_cmd);
install_element(GSMNET_NODE, &cfg_net_pong_time_cmd);
install_element(GSMNET_NODE, &cfg_net_grace_ussd_cmd);
install_element(GSMNET_NODE, &cfg_bts_cmd);
install_node(&bts_node, config_write_bts);

View File

@@ -73,33 +73,6 @@ 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,

View File

@@ -1 +1 @@
SUBDIRS = debug gsm0408 db channel sccp bsc-nat
SUBDIRS = debug gsm0408 db channel sccp

View File

@@ -1,17 +0,0 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS)
EXTRA_DIST = bsc_data.c
noinst_PROGRAMS = bsc_nat_test
bsc_nat_test_SOURCES = bsc_nat_test.c \
$(top_srcdir)/src/nat/bsc_filter.c \
$(top_srcdir)/src/nat/bsc_sccp.c \
$(top_srcdir)/src/nat/bsc_nat_utils.c \
$(top_srcdir)/src/nat/bsc_mgcp_utils.c \
$(top_srcdir)/src/mgcp/mgcp_protocol.c \
$(top_srcdir)/src/mgcp/mgcp_network.c \
$(top_srcdir)/src/bssap.c
bsc_nat_test_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libsccp.a $(LIBOSMOCORE_LIBS) -lrt

View File

@@ -1,154 +0,0 @@
/* test data */
/* BSC -> MSC, CR */
static const u_int8_t bsc_cr[] = {
0x00, 0x2e, 0xfd,
0x01, 0x00, 0x00, 0x15, 0x02, 0x02, 0x04, 0x02,
0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05,
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3,
0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4,
0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00 };
static const u_int8_t bsc_cr_patched[] = {
0x00, 0x2e, 0xfd,
0x01, 0x00, 0x00, 0x05, 0x02, 0x02, 0x04, 0x02,
0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05,
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3,
0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4,
0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00 };
/* CC, MSC -> BSC */
static const u_int8_t msc_cc[] = {
0x00, 0x0a, 0xfd,
0x02, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x02,
0x01, 0x00 };
static const u_int8_t msc_cc_patched[] = {
0x00, 0x0a, 0xfd,
0x02, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x02,
0x01, 0x00 };
/* Classmark, BSC -> MSC */
static const u_int8_t bsc_dtap[] = {
0x00, 0x17, 0xfd,
0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00,
0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13,
0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 };
static const u_int8_t bsc_dtap_patched[] = {
0x00, 0x17, 0xfd,
0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00,
0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13,
0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 };
/* Clear command, MSC -> BSC */
static const u_int8_t msc_dtap[] = {
0x00, 0x0d, 0xfd,
0x06, 0x00, 0x00, 0x05, 0x00, 0x01, 0x06, 0x00,
0x04, 0x20, 0x04, 0x01, 0x09 };
static const u_int8_t msc_dtap_patched[] = {
0x00, 0x0d, 0xfd,
0x06, 0x00, 0x00, 0x15, 0x00, 0x01, 0x06, 0x00,
0x04, 0x20, 0x04, 0x01, 0x09 };
/*RLSD, MSC -> BSC */
static const u_int8_t msc_rlsd[] = {
0x00, 0x0a, 0xfd,
0x04, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x00,
0x01, 0x00 };
static const u_int8_t msc_rlsd_patched[] = {
0x00, 0x0a, 0xfd,
0x04, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x00,
0x01, 0x00 };
/* RLC, BSC -> MSC */
static const u_int8_t bsc_rlc[] = {
0x00, 0x07, 0xfd,
0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x15 };
static const u_int8_t bsc_rlc_patched[] = {
0x00, 0x07, 0xfd,
0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x05 };
/* a paging command */
static const u_int8_t paging_by_lac_cmd[] = {
0x00, 0x22, 0xfd, 0x09,
0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x02, 0x00,
0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x12, 0x00,
0x10, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, 0x02,
0x01, 0x50, 0x02, 0x30, 0x1a, 0x03, 0x05, 0x20,
0x15 };
/* an assignment command */
static const u_int8_t ass_cmd[] = {
0x00, 0x12, 0xfd, 0x06,
0x00, 0x00, 0x49, 0x00, 0x01, 0x0b, 0x00, 0x09,
0x01, 0x0b, 0x03, 0x01, 0x0a, 0x11, 0x01, 0x00,
0x15 };
/*
* MGCP messages
*/
/* nothing to patch */
static const char crcx[] = "CRCX 23265295 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n";
static const char crcx_patched[] = "CRCX 23265295 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n";
/* patch the ip and port */
static const char crcx_resp[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n";
static const char crcx_resp_patched[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 10.0.0.1\r\nm=audio 999 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n";
/* patch the ip and port */
static const char mdcx[] = " MDCX 23330829 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 172.16.18.2\r\nt=0 0\r\nm=audio 4410 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n";
static const char mdcx_patched[] = " MDCX 23330829 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 10.0.0.23\r\nt=0 0\r\nm=audio 6666 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n";
static const char mdcx_resp[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n";
static const char mdcx_resp_patched[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 10.0.0.23\r\nm=audio 5555 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n";
/* different line ending */
static const char mdcx_resp2[] = "200 33330829\n\nv=0\nc=IN IP4 172.16.18.2\nm=audio 4002 RTP/AVP 98\na=rtpmap:98 AMR/8000\n";
static const char mdcx_resp_patched2[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\n";
struct mgcp_patch_test {
const char *orig;
const char *patch;
const char *ip;
const int port;
};
static const struct mgcp_patch_test mgcp_messages[] = {
{
.orig = crcx,
.patch = crcx_patched,
.ip = "0.0.0.0",
.port = 2323,
},
{
.orig = crcx_resp,
.patch = crcx_resp_patched,
.ip = "10.0.0.1",
.port = 999,
},
{
.orig = mdcx,
.patch = mdcx_patched,
.ip = "10.0.0.23",
.port = 6666,
},
{
.orig = mdcx_resp,
.patch = mdcx_resp_patched,
.ip = "10.0.0.23",
.port = 5555,
},
{
.orig = mdcx_resp2,
.patch = mdcx_resp_patched2,
.ip = "10.0.0.23",
.port = 5555,
},
};

View File

@@ -1,728 +0,0 @@
/*
* BSC NAT Message filtering
*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/bsc_nat.h>
#include <osmocore/talloc.h>
#include <stdio.h>
/* test messages for ipa */
static u_int8_t ipa_id[] = {
0x00, 0x01, 0xfe, 0x06,
};
/* SCCP messages are below */
static u_int8_t gsm_reset[] = {
0x00, 0x12, 0xfd,
0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
0x01, 0x20,
};
static const u_int8_t gsm_reset_ack[] = {
0x00, 0x13, 0xfd,
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
0x00, 0x01, 0x31,
};
static const u_int8_t gsm_paging[] = {
0x00, 0x20, 0xfd,
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10,
0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10,
0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06,
};
/* BSC -> MSC connection open */
static const u_int8_t bssmap_cr[] = {
0x00, 0x2c, 0xfd,
0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02,
0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05,
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3,
0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33,
0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01,
0x31, 0x97, 0x61, 0x00
};
/* MSC -> BSC connection confirm */
static const u_int8_t bssmap_cc[] = {
0x00, 0x0a, 0xfd,
0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00,
};
/* MSC -> BSC released */
static const u_int8_t bssmap_released[] = {
0x00, 0x0e, 0xfd,
0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f,
0x02, 0x23, 0x42, 0x00,
};
/* BSC -> MSC released */
static const u_int8_t bssmap_release_complete[] = {
0x00, 0x07, 0xfd,
0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03
};
/* both directions IT timer */
static const u_int8_t connnection_it[] = {
0x00, 0x0b, 0xfd,
0x10, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03,
0x00, 0x00, 0x00, 0x00,
};
/* error in both directions */
static const u_int8_t proto_error[] = {
0x00, 0x05, 0xfd,
0x0f, 0x22, 0x33, 0x44, 0x00,
};
/* MGCP wrap... */
static const u_int8_t mgcp_msg[] = {
0x00, 0x03, 0xfc,
0x20, 0x20, 0x20,
};
/* location updating request */
static const u_int8_t bss_lu[] = {
0x00, 0x2e, 0xfd,
0x01, 0x91, 0x45, 0x14, 0x02, 0x02, 0x04, 0x02,
0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05,
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x14, 0xc3,
0x50, 0x17, 0x12, 0x05, 0x08, 0x70, 0x72, 0xf4,
0x80, 0xff, 0xfe, 0x30, 0x08, 0x29, 0x44, 0x50,
0x12, 0x03, 0x24, 0x01, 0x95, 0x00
};
/* paging response */
static const uint8_t pag_resp[] = {
0x00, 0x2c, 0xfd, 0x01, 0xe5, 0x68,
0x14, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f,
0x1f, 0x00, 0x1d, 0x57, 0x05, 0x08, 0x00, 0x72,
0xf4, 0x80, 0x20, 0x16, 0xc3, 0x50, 0x17, 0x10,
0x06, 0x27, 0x01, 0x03, 0x30, 0x18, 0x96, 0x08,
0x29, 0x26, 0x30, 0x32, 0x11, 0x42, 0x01, 0x19,
0x00
};
struct filter_result {
const u_int8_t *data;
const u_int16_t length;
const int dir;
const int result;
};
static const struct filter_result results[] = {
{
.data = ipa_id,
.length = ARRAY_SIZE(ipa_id),
.dir = DIR_MSC,
.result = 1,
},
{
.data = gsm_reset,
.length = ARRAY_SIZE(gsm_reset),
.dir = DIR_MSC,
.result = 1,
},
{
.data = gsm_reset_ack,
.length = ARRAY_SIZE(gsm_reset_ack),
.dir = DIR_BSC,
.result = 1,
},
{
.data = gsm_paging,
.length = ARRAY_SIZE(gsm_paging),
.dir = DIR_BSC,
.result = 0,
},
{
.data = bssmap_cr,
.length = ARRAY_SIZE(bssmap_cr),
.dir = DIR_MSC,
.result = 0,
},
{
.data = bssmap_cc,
.length = ARRAY_SIZE(bssmap_cc),
.dir = DIR_BSC,
.result = 0,
},
{
.data = bssmap_released,
.length = ARRAY_SIZE(bssmap_released),
.dir = DIR_MSC,
.result = 0,
},
{
.data = bssmap_release_complete,
.length = ARRAY_SIZE(bssmap_release_complete),
.dir = DIR_BSC,
.result = 0,
},
{
.data = mgcp_msg,
.length = ARRAY_SIZE(mgcp_msg),
.dir = DIR_MSC,
.result = 0,
},
{
.data = connnection_it,
.length = ARRAY_SIZE(connnection_it),
.dir = DIR_BSC,
.result = 0,
},
{
.data = connnection_it,
.length = ARRAY_SIZE(connnection_it),
.dir = DIR_MSC,
.result = 0,
},
{
.data = proto_error,
.length = ARRAY_SIZE(proto_error),
.dir = DIR_BSC,
.result = 0,
},
{
.data = proto_error,
.length = ARRAY_SIZE(proto_error),
.dir = DIR_MSC,
.result = 0,
},
};
static void test_filter(void)
{
int i;
/* start testinh with proper messages */
fprintf(stderr, "Testing BSS Filtering.\n");
for (i = 0; i < ARRAY_SIZE(results); ++i) {
int result;
struct bsc_nat_parsed *parsed;
struct msgb *msg = msgb_alloc(4096, "test-message");
fprintf(stderr, "Going to test item: %d\n", i);
memcpy(msg->data, results[i].data, results[i].length);
msg->l2h = msgb_put(msg, results[i].length);
parsed = bsc_nat_parse(msg);
if (!parsed) {
fprintf(stderr, "FAIL: Failed to parse the message\n");
continue;
}
result = bsc_nat_filter_ipa(results[i].dir, msg, parsed);
if (result != results[i].result) {
fprintf(stderr, "FAIL: Not the expected result got: %d wanted: %d\n",
result, results[i].result);
}
msgb_free(msg);
}
}
#include "bsc_data.c"
static void copy_to_msg(struct msgb *msg, const u_int8_t *data, unsigned int length)
{
msgb_reset(msg);
msg->l2h = msgb_put(msg, length);
memcpy(msg->l2h, data, msgb_l2len(msg));
}
#define VERIFY(con_found, con, msg, ver, str) \
if (!con_found || con_found->bsc != con) { \
fprintf(stderr, "Failed to find the con: %p\n", con_found); \
abort(); \
} \
if (memcmp(msg->data, ver, sizeof(ver)) != 0) { \
fprintf(stderr, "Failed to patch the %s msg.\n", str); \
abort(); \
}
/* test conn tracking once */
static void test_contrack()
{
struct bsc_nat *nat;
struct bsc_connection *con;
struct sccp_connections *con_found;
struct sccp_connections *rc_con;
struct bsc_nat_parsed *parsed;
struct msgb *msg;
fprintf(stderr, "Testing connection tracking.\n");
nat = bsc_nat_alloc();
con = bsc_connection_alloc(nat);
con->cfg = bsc_config_alloc(nat, "foo", 23);
msg = msgb_alloc(4096, "test");
/* 1.) create a connection */
copy_to_msg(msg, bsc_cr, sizeof(bsc_cr));
parsed = bsc_nat_parse(msg);
con_found = patch_sccp_src_ref_to_msc(msg, parsed, con);
if (con_found != NULL) {
fprintf(stderr, "Con should not exist %p\n", con_found);
abort();
}
rc_con = create_sccp_src_ref(con, parsed);
if (!rc_con) {
fprintf(stderr, "Failed to create a ref\n");
abort();
}
con_found = patch_sccp_src_ref_to_msc(msg, parsed, con);
if (!con_found || con_found->bsc != con) {
fprintf(stderr, "Failed to find the con: %p\n", con_found);
abort();
}
if (con_found != rc_con) {
fprintf(stderr, "Failed to find the right connection.\n");
abort();
}
if (memcmp(msg->data, bsc_cr_patched, sizeof(bsc_cr_patched)) != 0) {
fprintf(stderr, "Failed to patch the BSC CR msg.\n");
abort();
}
talloc_free(parsed);
/* 2.) get the cc */
copy_to_msg(msg, msc_cc, sizeof(msc_cc));
parsed = bsc_nat_parse(msg);
con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
VERIFY(con_found, con, msg, msc_cc_patched, "MSC CC");
if (update_sccp_src_ref(con_found, parsed) != 0) {
fprintf(stderr, "Failed to update the SCCP con.\n");
abort();
}
/* 3.) send some data */
copy_to_msg(msg, bsc_dtap, sizeof(bsc_dtap));
parsed = bsc_nat_parse(msg);
con_found = patch_sccp_src_ref_to_msc(msg, parsed, con);
VERIFY(con_found, con, msg, bsc_dtap_patched, "BSC DTAP");
/* 4.) receive some data */
copy_to_msg(msg, msc_dtap, sizeof(msc_dtap));
parsed = bsc_nat_parse(msg);
con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
VERIFY(con_found, con, msg, msc_dtap_patched, "MSC DTAP");
/* 5.) close the connection */
copy_to_msg(msg, msc_rlsd, sizeof(msc_rlsd));
parsed = bsc_nat_parse(msg);
con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
VERIFY(con_found, con, msg, msc_rlsd_patched, "MSC RLSD");
/* 6.) confirm the connection close */
copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc));
parsed = bsc_nat_parse(msg);
con_found = patch_sccp_src_ref_to_msc(msg, parsed, con);
if (!con_found || con_found->bsc != con) {
fprintf(stderr, "Failed to find the con: %p\n", con_found);
abort();
}
if (memcmp(msg->data, bsc_rlc_patched, sizeof(bsc_rlc_patched)) != 0) {
fprintf(stderr, "Failed to patch the BSC CR msg.\n");
abort();
}
remove_sccp_src_ref(con, msg, parsed);
talloc_free(parsed);
copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc));
parsed = bsc_nat_parse(msg);
con_found = patch_sccp_src_ref_to_msc(msg, parsed, con);
/* verify that it is gone */
if (con_found != NULL) {
fprintf(stderr, "Con should be gone. %p\n", con_found);
abort();
}
talloc_free(parsed);
talloc_free(nat);
msgb_free(msg);
}
static void test_paging(void)
{
int lac;
struct bsc_nat *nat;
struct bsc_connection *con;
struct bsc_nat_parsed *parsed;
struct bsc_config cfg;
struct msgb *msg;
fprintf(stderr, "Testing paging by lac.\n");
nat = bsc_nat_alloc();
con = bsc_connection_alloc(nat);
con->cfg = &cfg;
cfg.lac = 23;
con->authenticated = 1;
llist_add(&con->list_entry, &nat->bsc_connections);
msg = msgb_alloc(4096, "test");
/* Test completely bad input */
copy_to_msg(msg, paging_by_lac_cmd, sizeof(paging_by_lac_cmd));
if (bsc_nat_find_bsc(nat, msg, &lac) != 0) {
fprintf(stderr, "Should have not found anything.\n");
abort();
}
/* Test it by not finding it */
copy_to_msg(msg, paging_by_lac_cmd, sizeof(paging_by_lac_cmd));
parsed = bsc_nat_parse(msg);
if (bsc_nat_find_bsc(nat, msg, &lac) != 0) {
fprintf(stderr, "Should have not found aynthing.\n");
abort();
}
talloc_free(parsed);
/* Test by finding it */
cfg.lac = 8213;
copy_to_msg(msg, paging_by_lac_cmd, sizeof(paging_by_lac_cmd));
parsed = bsc_nat_parse(msg);
if (bsc_nat_find_bsc(nat, msg, &lac) != con) {
fprintf(stderr, "Should have found it.\n");
abort();
}
talloc_free(parsed);
}
static void test_mgcp_ass_tracking(void)
{
struct bsc_connection *bsc;
struct bsc_nat *nat;
struct sccp_connections con;
struct bsc_nat_parsed *parsed;
struct msgb *msg;
fprintf(stderr, "Testing MGCP.\n");
memset(&con, 0, sizeof(con));
nat = bsc_nat_alloc();
nat->bsc_endpoints = talloc_zero_array(nat,
struct bsc_endpoint,
33);
bsc = bsc_connection_alloc(nat);
bsc->cfg = bsc_config_alloc(nat, "foo", 2323);
con.bsc = bsc;
msg = msgb_alloc(4096, "foo");
copy_to_msg(msg, ass_cmd, sizeof(ass_cmd));
parsed = bsc_nat_parse(msg);
if (bsc_mgcp_assign(&con, msg) != 0) {
fprintf(stderr, "Failed to handle assignment.\n");
abort();
}
if (con.msc_timeslot != 21) {
fprintf(stderr, "Timeslot should be 21.\n");
abort();
}
if (con.bsc_timeslot != 21) {
fprintf(stderr, "Assigned timeslot should have been 21.\n");
abort();
}
talloc_free(parsed);
bsc_mgcp_dlcx(&con);
if (con.bsc_timeslot != -1 || con.msc_timeslot != -1) {
fprintf(stderr, "Clearing should remove the mapping.\n");
abort();
}
talloc_free(nat);
}
/* test the code to find a given connection */
static void test_mgcp_find(void)
{
struct bsc_nat *nat;
struct bsc_connection *con;
struct sccp_connections *sccp_con;
fprintf(stderr, "Testing finding of a BSC Connection\n");
nat = bsc_nat_alloc();
con = bsc_connection_alloc(nat);
llist_add(&con->list_entry, &nat->bsc_connections);
sccp_con = talloc_zero(con, struct sccp_connections);
sccp_con->msc_timeslot = 12;
sccp_con->bsc_timeslot = 12;
sccp_con->bsc = con;
llist_add(&sccp_con->list_entry, &nat->sccp_connections);
if (bsc_mgcp_find_con(nat, 11) != NULL) {
fprintf(stderr, "Found the wrong connection.\n");
abort();
}
if (bsc_mgcp_find_con(nat, 12) != sccp_con) {
fprintf(stderr, "Didn't find the connection\n");
abort();
}
sccp_con->msc_timeslot = 0;
sccp_con->bsc_timeslot = 0;
if (bsc_mgcp_find_con(nat, 1) != sccp_con) {
fprintf(stderr, "Didn't find the connection\n");
abort();
}
/* free everything */
talloc_free(nat);
}
static void test_mgcp_rewrite(void)
{
int i;
struct msgb *output;
fprintf(stderr, "Test rewriting MGCP messages.\n");
for (i = 0; i < ARRAY_SIZE(mgcp_messages); ++i) {
const char *orig = mgcp_messages[i].orig;
const char *patc = mgcp_messages[i].patch;
const char *ip = mgcp_messages[i].ip;
const int port = mgcp_messages[i].port;
char *input = strdup(orig);
output = bsc_mgcp_rewrite(input, strlen(input), ip, port);
if (msgb_l2len(output) != strlen(patc)) {
fprintf(stderr, "Wrong sizes for test: %d %d != %d != %d\n", i, msgb_l2len(output), strlen(patc), strlen(orig));
fprintf(stderr, "String '%s' vs '%s'\n", (const char *) output->l2h, patc);
abort();
}
if (memcmp(output->l2h, patc, msgb_l2len(output)) != 0) {
fprintf(stderr, "Broken on %d msg: '%s'\n", i, (const char *) output->l2h);
abort();
}
msgb_free(output);
free(input);
}
}
static void test_mgcp_parse(void)
{
int code, ci;
char transaction[60];
fprintf(stderr, "Test MGCP response parsing.\n");
if (bsc_mgcp_parse_response(crcx_resp, &code, transaction) != 0) {
fprintf(stderr, "Failed to parse CRCX resp.\n");
abort();
}
if (code != 200) {
fprintf(stderr, "Failed to parse the CODE properly. Got: %d\n", code);
abort();
}
if (strcmp(transaction, "23265295") != 0) {
fprintf(stderr, "Failed to parse transaction id: '%s'\n", transaction);
abort();
}
ci = bsc_mgcp_extract_ci(crcx_resp);
if (ci != 1) {
fprintf(stderr, "Failed to parse the CI. Got: %d\n", ci);
abort();
}
}
struct cr_filter {
const u_int8_t *data;
int length;
int result;
int contype;
const char *bsc_imsi_allow;
const char *bsc_imsi_deny;
const char *nat_imsi_deny;
};
static struct cr_filter cr_filter[] = {
{
.data = bssmap_cr,
.length = sizeof(bssmap_cr),
.result = 0,
.contype = NAT_CON_TYPE_CM_SERV_REQ,
},
{
.data = bss_lu,
.length = sizeof(bss_lu),
.result = 0,
.contype = NAT_CON_TYPE_LU,
},
{
.data = pag_resp,
.length = sizeof(pag_resp),
.result = 0,
.contype = NAT_CON_TYPE_PAG_RESP,
},
{
/* nat deny is before blank/null BSC */
.data = bss_lu,
.length = sizeof(bss_lu),
.result = -3,
.nat_imsi_deny = "[0-9]*",
.contype = NAT_CON_TYPE_LU,
},
{
/* BSC allow is before NAT deny */
.data = bss_lu,
.length = sizeof(bss_lu),
.result = 0,
.nat_imsi_deny = "[0-9]*",
.bsc_imsi_allow = "2440[0-9]*",
.contype = NAT_CON_TYPE_LU,
},
{
/* BSC allow is before NAT deny */
.data = bss_lu,
.length = sizeof(bss_lu),
.result = 0,
.bsc_imsi_allow = "[0-9]*",
.nat_imsi_deny = "[0-9]*",
.contype = NAT_CON_TYPE_LU,
},
{
/* filter as deny is first */
.data = bss_lu,
.length = sizeof(bss_lu),
.result = -2,
.bsc_imsi_deny = "[0-9]*",
.bsc_imsi_allow = "[0-9]*",
.nat_imsi_deny = "[0-9]*",
.contype = NAT_CON_TYPE_LU,
},
};
static void test_cr_filter()
{
int i, res, contype;
struct msgb *msg = msgb_alloc(4096, "test_cr_filter");
struct bsc_nat_parsed *parsed;
struct bsc_nat_acc_lst *nat_lst, *bsc_lst;
struct bsc_nat_acc_lst_entry *nat_entry, *bsc_entry;
struct bsc_nat *nat = bsc_nat_alloc();
struct bsc_connection *bsc = bsc_connection_alloc(nat);
bsc->cfg = bsc_config_alloc(nat, "foo", 1234);
bsc->cfg->acc_lst_name = "bsc";
nat->acc_lst_name = "nat";
nat_lst = bsc_nat_acc_lst_get(nat, "nat");
bsc_lst = bsc_nat_acc_lst_get(nat, "bsc");
bsc_entry = bsc_nat_acc_lst_entry_create(bsc_lst);
nat_entry = bsc_nat_acc_lst_entry_create(nat_lst);
for (i = 0; i < ARRAY_SIZE(cr_filter); ++i) {
msgb_reset(msg);
copy_to_msg(msg, cr_filter[i].data, cr_filter[i].length);
nat_lst = bsc_nat_acc_lst_get(nat, "nat");
bsc_lst = bsc_nat_acc_lst_get(nat, "bsc");
bsc_parse_reg(nat_entry, &nat_entry->imsi_deny_re, &nat_entry->imsi_deny,
cr_filter[i].nat_imsi_deny ? 1 : 0,
&cr_filter[i].nat_imsi_deny);
bsc_parse_reg(bsc_entry, &bsc_entry->imsi_allow_re, &bsc_entry->imsi_allow,
cr_filter[i].bsc_imsi_allow ? 1 : 0,
&cr_filter[i].bsc_imsi_allow);
bsc_parse_reg(bsc_entry, &bsc_entry->imsi_deny_re, &bsc_entry->imsi_deny,
cr_filter[i].bsc_imsi_deny ? 1 : 0,
&cr_filter[i].bsc_imsi_deny);
parsed = bsc_nat_parse(msg);
if (!parsed) {
fprintf(stderr, "FAIL: Failed to parse the message\n");
abort();
}
res = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &contype);
if (res != cr_filter[i].result) {
fprintf(stderr, "FAIL: Wrong result %d for test %d.\n", res, i);
abort();
}
if (contype != cr_filter[i].contype) {
fprintf(stderr, "FAIL: Wrong contype %d for test %d.\n", res, contype);
abort();
}
talloc_free(parsed);
}
msgb_free(msg);
}
int main(int argc, char **argv)
{
struct log_target *stderr_target;
log_init(&log_info);
stderr_target = log_target_create_stderr();
log_add_target(stderr_target);
log_set_all_filter(stderr_target, 1);
test_filter();
test_contrack();
test_paging();
test_mgcp_ass_tracking();
test_mgcp_find();
test_mgcp_rewrite();
test_mgcp_parse();
test_cr_filter();
return 0;
}
void input_event()
{}
int nm_state_event()
{
return -1;
}
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
{
return -1;
}