Compare commits

...

256 Commits

Author SHA1 Message Date
Holger Hans Peter Freyther
2537b2f836 This is 0.3.99.20 with bring-up hacks, possible crash fixes 2010-10-07 05:56:42 +08:00
Holger Hans Peter Freyther
1168d13e0c msc: Only unregister the fd if the fd value is valid.
This makes sure that someone can call bsc_msc_lost multiple times
even if there is no MSC connection. This makes sense as bsc_msc_lost
is public and be called from client code.
2010-10-07 04:55:42 +08:00
Holger Hans Peter Freyther
588c423f12 msc: Stop the connection timeout when we unregister the bfd
When we dropped the connection... stop the timer as we might
call bsc_unregister_fd twice...
2010-10-07 04:42:03 +08:00
Holger Hans Peter Freyther
088601004c gsm_04_80: Require libosmocore for creating USSD messages
Use the libosmocore to create USSD messages, increase the
minimum version of libosmocore, add header files, remove
the code.

Conflicts:

	openbsc/configure.in
	openbsc/include/openbsc/gsm_04_80.h
	openbsc/src/gsm_04_08.c
	openbsc/src/gsm_04_80.c
2010-10-06 23:36:22 +08:00
Holger Hans Peter Freyther
55982ca9a7 oml: In case we get a NACK, drop the OML connection and hope for the best
Assume that a NACK is a onetime failure and that on the next
attempt it will work better. If that is not the case we might
even send a reboot to the BTS.
2010-10-03 01:48:45 +08:00
Holger Hans Peter Freyther
91a035d2fb oml: Another band aid for reliable BTS init...
Sometimes the operative change for the NSE is getting nacked,
this might be due that we send it before we get the OPSTART ACK
for this object class. Send it from the CELL availability as
a workaround. This init code needs to be changed to make these
dependencies work more reliable.
2010-10-02 04:38:04 +08:00
Holger Hans Peter Freyther
5ed2e3004b ipaccess: Re-Enable the delay between commands...
We still need to keep the delay during startup as the
GPRS code tried to initialize too early.
2010-10-02 02:16:13 +08:00
Holger Hans Peter Freyther
00460d3bc5 bsc_msc_ip: Save the welcome text, add an 'e' to the command. 2010-09-29 20:39:09 +08:00
Holger Hans Peter Freyther
dcb7ad3f2c bsc_msc_ip: Save the bsc-welcome-text message 2010-09-29 20:38:26 +08:00
Holger Hans Peter Freyther
74e7cc4c4a Add missing osmo_bsc_grace.h
This file got lost during a rename.
2010-09-29 18:44:43 +08:00
Holger Hans Peter Freyther
667cba9d28 bsc: Send a USSD command after we have a LU Accept and a new_subscriber 2010-09-26 18:49:03 +08:00
Holger Hans Peter Freyther
6f926f0197 bsc: Add a ussd_welcome_text variable and VTY command
Add a command to set the welcome text for the USSD.
2010-09-26 18:49:02 +08:00
Holger Hans Peter Freyther
c98d67d592 bsc: Mark LUs with a different LAC as needing special care
We want to send a welcome USSD to new subscribers, mark subscribers
with a different LAC than this cell as special and hope someone will
follow up with them.
2010-09-26 18:49:02 +08:00
Holger Hans Peter Freyther
43dbcfe2cf bsc: Rename to osmo_bsc_grace to prepare putting it into master 2010-09-16 00:13:01 +08:00
Holger Hans Peter Freyther
98d949b02f bsc: Rename the RF Ctl interface to match master 2010-09-16 00:01:43 +08:00
Holger Hans Peter Freyther
4f5421a482 bsc_msc: Add minimal code to work with the new SCCP version 2010-08-25 12:22:32 +08:00
Holger Hans Peter Freyther
c8fdf0c4b6 bsc_msc_ip: Set the right log area 2010-08-25 00:55:16 +08:00
Holger Hans Peter Freyther
45473477f6 sccp: Use the libosmo-sccp library and remove the internal copy 2010-08-24 22:38:12 +08:00
Holger Hans Peter Freyther
cd7bed5327 bsc: Remember the MSC connection we use for that connection 2010-08-24 22:26:15 +08:00
Holger Hans Peter Freyther
83594c1f2c oml: Workaround for vty misusage...
Move the OML_NODE below the CONFIG_NODE so that the vty->index
will not be restored when we exit the node. If we do restore the
node we will end up with a double free.
2010-08-11 04:27:25 +08:00
Holger Hans Peter Freyther
7e1524644b mgcp: Speculative mgcp fix...
We really have 32 channels per multiplex... so use the right
number... or at least it seems we do have 32.
2010-08-10 20:24:40 +08:00
Holger Hans Peter Freyther
3ec13ed3a8 OML: Introduce minimal VTY command set to interact with OML managed objects
Conflicts:

	openbsc/include/openbsc/abis_nm.h
	openbsc/src/Makefile.am
	openbsc/src/vty_interface.c
2010-08-10 20:13:38 +08:00
Holger Hans Peter Freyther
11557ecd8c mgcp: Reduce the log level to debug for these calls. 2010-08-08 07:28:54 +08:00
Holger Hans Peter Freyther
c737fbd4af mgcp: Remember if the endpoint was allocated...
Do not use the CI_UNUSED to decide if an endpoint is allocated
but introduce a new flag. This way only the CRCX and free_endp
play with the allocated field.
2010-08-08 07:28:45 +08:00
Holger Hans Peter Freyther
24963554a8 mgcp: Add a callback to inform the client that an endpoint got reallocated 2010-08-08 07:28:36 +08:00
Holger Hans Peter Freyther
c7bd29e6d7 mgcp: Make the CI uint32_t all the way to avoid mismatch
Conflicts:

	openbsc/include/openbsc/mgcp.h
	openbsc/src/nat/bsc_mgcp_utils.c
2010-08-08 07:27:23 +08:00
Holger Hans Peter Freyther
137523ef7c mgcp: Fix discovering the RTCP port with the more strict check.
If we have found the BTS and we receive data on the RTCP port
from the IP of the BTS we will set our RTCP port and forward it
to the network and hope it will be useful.
2010-08-08 07:24:22 +08:00
Holger Hans Peter Freyther
0457b4b6da mgcp: Determine the proto properly... 2010-08-08 07:24:13 +08:00
Holger Hans Peter Freyther
c372b1eb75 bsc_msc: Remove the except code as it is wrong...
Remove wrong code that is luckily not called. We would end up
in a reconnect and attempt to bsc_fd_register the same socket
again. I am removing this part of the code as it is not used
and it would need to know if the fd has ever been registered
or not...
2010-08-08 07:24:06 +08:00
Holger Hans Peter Freyther
55228e31be mgcp: Add instrumentation code to find a possible port leak/bsc-fd corruption 2010-08-08 07:23:58 +08:00
Holger Hans Peter Freyther
db544db73a mgcp: Move the mgcp_free_endp into the right path for the CRCX failure 2010-08-08 07:23:51 +08:00
Holger Hans Peter Freyther
f49d616427 mgcp: Fix a filedescriptor leak in case the bind is failing. 2010-08-08 07:23:44 +08:00
Holger Hans Peter Freyther
1eecb1689b mgcp: Enable the tap after configuring it... 2010-08-08 07:23:36 +08:00
Holger Hans Peter Freyther
f55fc02e87 mgcp: Add a call tap feature to forward audio to another forward port
For debugging it is useful to forward (tee) UDP packets to another
system and use gstreamer to inspect the rtp stream. This is untested
code and might contain bugs.... and of course only tap your own calls.
2010-08-08 07:23:29 +08:00
Holger Hans Peter Freyther
6bb3065e6d mgcp: Patch RTP packets again if that is allowed. 2010-08-08 07:23:22 +08:00
Holger Hans Peter Freyther
3c30a4bb1f mgcp: Get the for network/for bts flag right. 2010-08-08 07:23:15 +08:00
Holger Hans Peter Freyther
636c1cadd8 mgcp: Be more strict on the source addr/source port of the bts
Once we have discovered the bts we will not accept data from
anything else. The call will drop if the BTS is changing the
ip address of the nat anyway.
2010-08-08 07:23:08 +08:00
Holger Hans Peter Freyther
20f3d21665 mgcp: Only discover the bts once, the extra check got lost 2010-08-08 07:22:55 +08:00
Holger Hans Peter Freyther
889fd79511 mgcp: Allow to dynamically allocate ports from a range..
Allow to switch to a dynamic port allocator and not reuse
the ports for a long time... This should help with a crazy
network sending two streams at the same time.
2010-08-08 07:22:40 +08:00
Holger Hans Peter Freyther
7e25253b71 mgcp: Allow to have a different port allocation mode 2010-08-08 07:22:28 +08:00
Holger Hans Peter Freyther
f90c8c9d75 mgcp: Prepare to have different port allocation strategies. 2010-08-08 07:22:06 +08:00
Holger Hans Peter Freyther
08a366f11f mgcp: Fix the signature of the change_cb to not carry the port. 2010-08-08 07:21:58 +08:00
Holger Hans Peter Freyther
c12bfa45a5 mgcp: Separate recv from net/bts and remove autodetection
This allows a more strict check on the source of RTP messages
and we can more easily reject those. For the BTS without an ip
address we will also update the ip address.
2010-08-08 07:21:51 +08:00
Holger Hans Peter Freyther
e320fc115a mgcp: Move the loopback code into the common send as well. 2010-08-08 07:21:43 +08:00
Holger Hans Peter Freyther
5bf8f95701 mgcp: Remove the receive code into a new method. 2010-08-08 07:21:22 +08:00
Holger Hans Peter Freyther
6989b57350 mgcp: Move the selection of the right source port to a new method 2010-08-08 07:19:17 +08:00
Holger Hans Peter Freyther
edd980619c mgcp: Allocate a different port for the networking...
Use the right source port when sending the message.

Conflicts:

	openbsc/include/openbsc/mgcp.h
2010-08-08 07:18:37 +08:00
Holger Hans Peter Freyther
2c362abfb4 mgcp: Rename the base port to bts_base as it will be used for the bts 2010-08-08 07:18:16 +08:00
Holger Hans Peter Freyther
f8e0838f7a mgcp: Move the bfd for rtp/rtcp into the port
Stop using the memset in the mgcp_rtp_end_reset as we
will reset the list pointers and then have a mess..
2010-08-08 07:18:08 +08:00
Holger Hans Peter Freyther
ec2886d1bd mgcp: Make the function internal, only used by the init/config code 2010-08-08 07:18:00 +08:00
Holger Hans Peter Freyther
865ba8b572 mgcp: Rename the bind method to show it is only binding for the bts port 2010-08-08 07:17:52 +08:00
Holger Hans Peter Freyther
9be1b5495f mgcp: Only use early bind for the BTS socket.
Simplify the code by onlt allowing one way to allocate
a socket.
2010-08-08 07:17:42 +08:00
Holger Hans Peter Freyther
7cf7d27d10 mgcp: Attempt to separate the RTP/RTCP port for the Network and for the BTS
We plan to have two different ports for the network and for the
BTS to avoid detecting the BTS and to dynamically allocate the
port to have old data not go to a new socket.

Conflicts:

	openbsc/src/nat/bsc_mgcp_utils.c
2010-08-08 07:17:22 +08:00
Holger Hans Peter Freyther
9002169dcd mgcp: Group the state for bts/net into a struct and have two instances
Group the data that each end (network/bts) have into a struct and use
this struct throughout the sourcecode.

Conflicts:

	openbsc/src/nat/bsc_mgcp_utils.c
2010-08-08 07:16:55 +08:00
Holger Hans Peter Freyther
43242f8947 mgcp: Remove the forwarding mode as it was not used.
Conflicts:

	openbsc/include/openbsc/mgcp.h
2010-08-08 07:16:31 +08:00
Holger Hans Peter Freyther
83961de565 mgcp: Fix the payload_type... it broke in 7cdc62c012 2010-08-08 07:14:58 +08:00
Holger Hans Peter Freyther
19776f70a2 mgcp: Fix the reversed net/bts... which has not cause any issue.. 2010-08-08 07:14:46 +08:00
Holger Hans Peter Freyther
6233ab9859 mgcp: Pass the whole endpoint to the patch method. 2010-08-08 07:14:37 +08:00
Holger Hans Peter Freyther
0037badb02 mgcp: Fix the order of the arguments... 2010-08-08 07:14:07 +08:00
Holger Hans Peter Freyther
054efe1ef2 mgcp: Print the conn mode as well 2010-08-08 07:13:58 +08:00
Holger Hans Peter Freyther
68fc12d89b mgcp: Disable the actual patching... this is a temporary hack 2010-08-08 07:13:50 +08:00
Holger Hans Peter Freyther
7eccf5b0a5 mgcp: Print the system for the duplicate SSRC... 2010-08-08 07:13:40 +08:00
Holger Hans Peter Freyther
6d346d8931 bsc_msc: Fix the naming of this function. 2010-08-08 07:13:20 +08:00
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
Holger Hans Peter Freyther
2ad760fe5f Versioning... 2010-06-13 14:24:11 +08:00
Holger Hans Peter Freyther
fea0aebd36 bsc_msc_ip: Attempt to plug an lchan leak...
If we end up with a channel that has refcount of zero,
has no msc_data attached and the handler has not returned
1 we will just close it.
2010-06-13 14:15:39 +08:00
Holger Hans Peter Freyther
c9b7d74a08 channel: Keep track on when a channel got allocated.
This can help to detect 'stale' channels in a network.
2010-06-13 12:38:35 +08:00
Holger Hans Peter Freyther
42a4e9a52d abis_rsl: Reduce logging to LOGL_DEBUG as it is quite nosiy
The nanoBTS will send us at least one measurement report
after we have decided to close the channel... degrade that
output to a debug message.
2010-06-13 10:09:45 +08:00
Holger Hans Peter Freyther
d4f7a81992 nat: Fix the access-list-name command...
We have added two commands with the same name to the tree..
the second one should have been the BSC...
2010-06-08 11:18:26 +08:00
Holger Hans Peter Freyther
cd4afce470 nat: Add both entries to the tail to keep the order they are inserted 2010-06-08 11:00:09 +08:00
Holger Hans Peter Freyther
299d5aa2a4 nat: Allow to specify multiple entries in the access-list...
Inside the access-list we have a list of entries that have
either one allow or one deny rule... we do not allow to remove
a single rule but one has to remove the whole list, in that case
talloc will handle cleaning all entries.

Right now the matching is O(n*m) as we traverse the list
(multiple times) and run the regexp multiple times. One
way to make it faster would be to concat all regexps into
one.
2010-06-08 10:53:39 +08:00
Holger Hans Peter Freyther
f85e93cd4d nat: Shorten the access-list struct and method names (still way too long) 2010-06-08 10:14:44 +08:00
Holger Hans Peter Freyther
fdfaf9c519 bsc_msc_ip: Possible crash fix on the early assignment code path
The crash happened when we had released the primary channel
for one reason or another but still got the assignment complete
on the secondary. This null checking is some extra caution, with
the previous commit we should fail the msc_data test early in
this method.
2010-06-07 22:32:10 +08:00
Holger Hans Peter Freyther
4d4e6714cd bsc_msc_ip: When closing the SCCP check primary and secondary lchan
When closing a SCCP connection and any of the two lchan's are open,
then close them down properly.

Move the lchan freeing into a new method and call that one from the
SCCP connection close handling. Move the bss scp data varaible to
the top of the context..
2010-06-07 22:28:56 +08:00
Holger Hans Peter Freyther
3806b070bb Version bump... 2010-06-07 15:02:01 +08:00
Holger Hans Peter Freyther
3dd069cfd7 bssap.c: Claim to always do HR AMR right now.
The bssap.c code is sending a multirate config with
only AMR 5.9kb marked as supported, the MSC does not
like if we assign a FR channel and send the GSM 0808
FR AMR mode back to the MSC. So change the code to
not look at the channel type for AMR...
2010-06-07 14:47:25 +08:00
Harald Welte
e1dcbc7622 [GPRS] Change SI13 to NMO_II, as some phones (like G1) dislike NMO_III
I still believe NMO_III is what we want, but as indiciated some phones
absolutely refuse to even connect to the GPRS network in this mode :(
2010-06-07 12:26:12 +08:00
Holger Hans Peter Freyther
6be75737c4 Revert "bsc_init: Set the paging config like in the trace..."
I added this to have the patch in the history, I don't think
that we need to include this but it is good to have it cherry
pickable in the history.

This reverts commit 290a11d0ad.
2010-06-06 21:32:02 +08:00
Holger Hans Peter Freyther
290a11d0ad bsc_init: Set the paging config like in the trace...
The value of this config is not known.... the paging load
needs to be tested again with these parameters...
2010-06-06 21:29:07 +08:00
Holger Hans Peter Freyther
67505f46b7 bsc_init: Use configuration settings from a trace... as default
Use the values but the paging configuration from a trace...
2010-06-06 21:25:33 +08:00
Holger Hans Peter Freyther
8b4898360a [nat] Implement the removal of an access-list. 2010-06-03 01:44:05 +08:00
Holger Hans Peter Freyther
6e495eee4b [nat] Fix the parsing of the access-list regexp...
We need to start at argv[1] for the regexp of
this access-list, also subtract one from number
of items..
2010-06-03 01:38:53 +08:00
Holger Hans Peter Freyther
e6a8a9359d [nat] Fix VTY bug with access-lists...
vty->index does not hold a BSC Config at this point as we are
on the nat level... use the global _nat pointer for now...
2010-06-03 01:34:20 +08:00
Holger Hans Peter Freyther
1d55fd9e2b Mark a new test release.. 2010-06-02 17:36:43 +08:00
Holger Hans Peter Freyther
df342ea82b [nat] Introduce the concept of access-list
One can set one access-list to one BSC and one
access-list to one NAT. The matching of IMSIs
remains the same for now, also applying the
white/blacklist. Access lists can not be deleted
for now and no perf opt is done (e.g. one could
cache the result of the last lookup in the bsc
struct).
2010-06-01 01:03:13 +08:00
Harald Welte
049eb23b73 [GPRS] Make sure SI13 rest octets look like those of the ip.access BSC 2010-05-31 19:20:11 +08:00
Holger Hans Peter Freyther
95eb9dd339 [gprs] Use the defaults coming from a trace file.
* Enable sending RLC3
* Use values from the trace..

This is not intended to be merged to master as this enables the
RLC3 that the comment claims to only work on EGPRS enabled models
and it is changing timers to hex indicating a change where none
happened... This is mostly for testing.
2010-05-31 19:19:10 +08:00
Holger Hans Peter Freyther
ca660ac9ca [nat] Add ip-tos option to the nat.
This is applied to all incoming BSC connections.
2010-05-31 10:36:35 +08:00
Holger Hans Peter Freyther
96d6ed2552 [mgcp] Set the IP_TOS/DSCP on RTP/RTCP IP packets. 2010-05-31 10:22:00 +08:00
Harald Welte
170619fef6 [gprs] NS/BSSGP: Make all timers configurable from VTY 2010-05-22 22:21:36 +08:00
Holger Hans Peter Freyther
09f28ea6b3 [misc] Remove spaces, fix indention. 2010-05-22 22:13:13 +08:00
Sylvain Munaut
1884f89d9b bsc_init: Fix ccch description in SI messages
The previous code just hardcoded RSL_BCCH_CCCH_CONF_1_C, but
we need to inspect the timeslot config to know what to use.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2010-05-22 22:12:48 +08:00
Holger Hans Peter Freyther
0777fb2d32 [mgcp] Only patch RTP packets when they arrived on the RTP port
Do not attempt to patch RTCP packets...
2010-05-22 22:06:13 +08:00
Holger Hans Peter Freyther
35e56453d2 msc: Add msc ip-tos NR option for the BSC
Allow to set the TOS field via the VTY interface. The
SO_PRIORITY was not used as it has no effect on the
packets being sent (in contrast to the documentation).
2010-05-18 03:31:16 +08:00
Holger Hans Peter Freyther
4fcf80a59a [nat] Make the refusal more complicated to support more MSCs
We will need to confirm the connection, then we can send the
GSM48 message, then we need to close the connection... the
embedding in the refusal method was way too easy..
2010-05-16 20:45:16 +08:00
Holger Hans Peter Freyther
31e0bafa10 [sccp] Add method to create a dt1 packet. 2010-05-16 20:45:15 +08:00
Holger Hans Peter Freyther
01ffc204f3 [sccp] Create a method to create RLSD messages. 2010-05-16 20:45:15 +08:00
Holger Hans Peter Freyther
466c40bec2 [sccp] Create a SCCP CC creation routine. 2010-05-16 20:45:15 +08:00
Holger Hans Peter Freyther
60a2f4a7e6 [nat] Make create_sccp_src_ref return the SCCP Connection.
Right now it was not possible to just find a connection, by returning
the connection that is created we will have direct access to it. It
will be used by the local connection handling.
2010-05-16 20:45:15 +08:00
Holger Hans Peter Freyther
797b9f0af0 [nat] Remove parameter that is never accessed directly
The msgb needs to be around when we access the parsed structure
but that needs to be guranteed by the caller handing out the parsed
structure.
2010-05-16 20:45:15 +08:00
Holger Hans Peter Freyther
677f0e7f90 [nat] Add the notion of a "local" connection.
A local connection is only between the MUX and the real BSC. We will
not forward anything to the MSC. This will be needed for the IMSI
filtering as sending a CREF is not liked by every BSC...
2010-05-16 20:45:15 +08:00
Holger Hans Peter Freyther
ddbb5a4e1e [nat] Do not access the con after the removal
In case of a RLC message we will destroy the SCCP connection. This means
that accessing the con and con->bsc will access old memory. Keep the status
local and move the con into an inner scope.
2010-05-16 20:45:15 +08:00
Holger Hans Peter Freyther
75042b80be [nat] Send a GSM48 message within the reject message 2010-05-16 08:15:11 +08:00
Holger Hans Peter Freyther
c32589f395 gsm48: Split LU Reject sending and generation into two. 2010-05-16 02:49:12 +08:00
Holger Hans Peter Freyther
abd0719f23 gsm48: Separate CM Service Reject sending and creation.
Split out the msg generation from the sending, this will
be used by the nat to send a refusal message.
2010-05-16 02:49:12 +08:00
Holger Hans Peter Freyther
5bac62216e [nat] Move the SCCP CREF handling into a new method.
We will need to generate messages with a proper reason
and it is easier to do that from a dedicated method.
2010-05-16 02:49:12 +08:00
Holger Hans Peter Freyther
c93c523872 [bsc_msc_ip] Move the command to the right place
Apparently I could not find the vty_interface_bsc.c when I was
searching for it. Move an extra BSC command into that file.
2010-05-16 02:49:12 +08:00
Holger Hans Peter Freyther
ed1c872352 [bsc_msc_ip] Print SCCP src/dst ref as hex 2010-05-16 02:49:12 +08:00
Holger Hans Peter Freyther
e21bdea501 [nat] Use and print the connection type of a SCCP connection. 2010-05-16 02:49:11 +08:00
Holger Hans Peter Freyther
11c17233fe [nat] Set the connection type/reason as out parameter
We are analyzing each CR message and it is nice to know the
reason these connections were created. Change the nat method.
2010-05-16 02:49:11 +08:00
Holger Hans Peter Freyther
fceee8779e Bump the version. 2010-05-16 01:37:49 +08:00
Holger Hans Peter Freyther
e27740a0e2 [nat] Use the new gsm48 method to parse the MI followed by a classmark. 2010-05-16 01:13:28 +08:00
Holger Hans Peter Freyther
95defd542d gsm48: Add a generic MI from classmark+mi extraction.
This is a generic MI extraction for the MI if it is followed
after a classmark. For the Phase1 Phones the classmark2 is not
four bytes but it might be different. This code can be used
by the CM Service Request handling as well.
2010-05-16 01:07:20 +08:00
Holger Hans Peter Freyther
2d2a43f3d6 [nat] Let IMSI DETACH and other messages pass by. 2010-05-16 01:05:47 +08:00
Holger Hans Peter Freyther
a9aab6a9ca [nat] Print on which BSC config this happend. 2010-05-16 00:45:07 +08:00
Holger Hans Peter Freyther
775b3dc566 [nat] Parse the PAGING RESPONSE inside a CR message as well.
Now we are parsing a CM Service Request, Location Updating Request
and the Paging Response. For all other messages we claim to not
support it and force a refuse.
2010-05-15 23:55:28 +08:00
Holger Hans Peter Freyther
45bb8bfc1a gsm48: Add size checks to the paging response mi parsing.
We go from no size checks to some content checking. We should
refactor the whole classmark2 + mi parsing that is used throughout
the code into one place with proper size checking. This is the
start and requires a new libosmocore as well.
2010-05-15 23:55:28 +08:00
Holger Hans Peter Freyther
57900f0008 [nat] Check proto descriptor and the message type 2010-05-15 23:55:28 +08:00
Holger Hans Peter Freyther
66ac860f62 [nat] Add code to filter the CM Service Request by IMSI.
The code should be shared among the GSM0408 implementation
and this one, and like the LU we are not handling a TMSI
properly as we have no idea where it is coming from.
2010-05-15 23:55:28 +08:00
Holger Hans Peter Freyther
ec82426c5e [nat] Mention where the MSG is coming from. 2010-05-15 19:13:52 +08:00
Holger Hans Peter Freyther
60fa0efcc8 [sccp] Make it optional to send data on a SCCP Connection Refuse
This can be used to send a Location Updating Reject down to the
BSC when it is clear that a subscriber is not allowed.
2010-05-15 01:04:42 +08:00
Holger Hans Peter Freyther
3dfcd4636a [nat] Remove the imsi allow option on the nat level.
For now we have:
1.) bsc imsi deny to deny at the BSC level
2.) bsc imsi allow to allow a SIM at the BSC level
3.) nat imsi deny to deny at the global level
2010-05-15 00:36:54 +08:00
Holger Hans Peter Freyther
50818d0c20 [nat] Separate exit2/exit3 as this can not be shared...
We have tried to send a refuse for arbitary things and ended
up with a segfault... separate the exi2 and exit3 label to have
separate exits and cleanups.
2010-05-15 00:29:50 +08:00
Holger Hans Peter Freyther
317934a2ba [nat] Add a token to the nat config and handle ID GET
This allows to chain a nat with a nat by answering to the
id get code and sending the token.
2010-05-15 00:14:58 +08:00
Holger Hans Peter Freyther
565b355c82 [bsc_msc] Move the id get response into the bsc_msc.c
Create the message in a common place and then it can be used
by tools having an a link or such.
2010-05-15 00:12:19 +08:00
Holger Hans Peter Freyther
be9201a272 [nat] Add a regexp test command to the VTY.
This allows to test the regexp to be used for allo/deny of
the imsi filter.
2010-05-14 23:43:12 +08:00
Holger Hans Peter Freyther
a43c56637d [nat] Fix the regexp of the test and the command line. 2010-05-14 23:07:58 +08:00
Holger Hans Peter Freyther
980c84f0a3 [nat] Fix the imsi deny config write. 2010-05-14 23:06:09 +08:00
Holger Hans Peter Freyther
1ae7b7c372 [nat] We do not want to see the actual matches. 2010-05-14 22:24:36 +08:00
Holger Hans Peter Freyther
e265db68b0 [nat] Allow to set the description for the bsc.
This will allow to add description to each BSC.
2010-05-14 22:06:28 +08:00
Holger Hans Peter Freyther
fdc4a9386f [nat] Implement IMSI filtering... 2010-05-14 19:49:35 +08:00
Holger Hans Peter Freyther
023ac93377 [nat] Fix the size check of the LU Request. 2010-05-14 19:24:06 +08:00
Holger Hans Peter Freyther
fa53aba62c [nat] Make the string -> regexp parsing public
This way it can be used from within a test case to test
the regexps..
2010-05-14 18:38:29 +08:00
Holger Hans Peter Freyther
34c0b245fb nat: Add code to parse the SCCP optional data.
First we have the Complete Layer3 Information, then we have
the IE for the Layer3 information, then the GSM48 hdr, then
the actual content with data. Right now we are parsing the
LU but we are not filtering anything yet.
2010-05-14 08:14:09 +08:00
Holger Hans Peter Freyther
4c4d2d48ec nat: Start to add a test case.. with one CR message. 2010-05-14 08:12:57 +08:00
Holger Hans Peter Freyther
13441a1c50 gsm48: Typo fix. 2010-05-14 08:02:08 +08:00
Holger Hans Peter Freyther
8ff74e8c24 nat: Introduce a nat filter that is working on the CR message.
Currently there is no implementation but the refusal code is
in place and will send a refusal back to the BSC.
2010-05-14 03:47:52 +08:00
Holger Hans Peter Freyther
a202342d64 [sccp] Export function to create SCCP Refuse message. 2010-05-14 03:34:35 +08:00
Holger Hans Peter Freyther
d275cf6407 [bsc_msc_ip] Use A.B.C.D for the VTY code. 2010-05-14 02:37:54 +08:00
Holger Hans Peter Freyther
c00e9ce09f [nat] Use A.B.C.D for the IP address
The VTY code will then be able to validate the IP Address.
2010-05-14 02:36:42 +08:00
Holger Hans Peter Freyther
5d645bf984 [nat] Remove range checks inside the VTY command.
The ranges are enforced by the VTY code.
2010-05-14 02:35:51 +08:00
Holger Hans Peter Freyther
723fb87a6c [mgcp] Clean up VTY code, the ranges are checked by the VTY code. 2010-05-14 02:34:35 +08:00
Holger Hans Peter Freyther
9da4492655 [mgcp] Use A.B.C.D for the ip addresses inside the vty config
Make the vty code parse the ip addresses for us and validate
them for us.
2010-05-14 02:27:50 +08:00
Holger Hans Peter Freyther
1927e93ce5 [mgcp] Improve the language of the comments. 2010-05-14 02:18:59 +08:00
Holger Hans Peter Freyther
7bbd416a52 [mgcp] Include stdlib.h for abs. 2010-05-14 02:14:09 +08:00
Holger Hans Peter Freyther
efd38dd015 [vty] Add power measurements to the one line summary. 2010-05-14 01:58:17 +08:00
Holger Hans Peter Freyther
e8d8811b12 [vty] Add a one line show lchan summary command. 2010-05-14 01:45:52 +08:00
Holger Hans Peter Freyther
39cb9f2a3c [vty] Move "show lchan" into a parameterized method
I want to have a shorter lchan summary but with the same
config parameters. Change the current code to be a method
that takes a dump routine as parameter.
2010-05-14 01:45:45 +08:00
Holger Hans Peter Freyther
0558a5a0dd [vty] Remove unfinished code from the VTY... 2010-05-14 00:58:11 +08:00
Holger Hans Peter Freyther
fcb4468de4 A new day, a new tag 2010-05-14 00:49:14 +08:00
Holger Hans Peter Freyther
9e96b2df12 rach: Allow to set the emergency call bit
Add the rach emergency call allowed (0|1) setting and implement
it by directly manipulating the t2 value. It is the third bit which
is set to 0 when emergency calls are enabled and to one if it is
only enabled for access classes 11 to 15.
2010-05-14 00:39:19 +08:00
Holger Hans Peter Freyther
72952d854c [mgcp] Use tabs here.. 2010-05-14 00:20:32 +08:00
Holger Hans Peter Freyther
637dce99ba ipaccess: Move the RSL delay down to 0 milliseconds.
Set the delay to zero milliseconds to send RSL messages as fast
as possible.
2010-05-14 00:09:05 +08:00
Holger Hans Peter Freyther
641b07ab73 ipaccess: Make sure flashing of the secondary BTS is working
Use the TRX throughout the flash process.
2010-05-13 00:17:17 +08:00
Holger Hans Peter Freyther
6eae31e39f sw_load: Specify the trx_nr for the software load
For the multi TRX setup we will need to specify the right trx->nr
to be able to flash the BTS. For the BS11 case we are ignoring the
additional argument.
2010-05-13 00:17:17 +08:00
Holger Hans Peter Freyther
4647015f69 ipaccess: Send the reset to the BASEBAND_TRANSC and supply TRX
Send the IPA Restart to a given BTS/TRX, change the signal callbacks
to carry the trx instead of the BTS so we have an easy access to the
right TRX and change the ipaccess-config to use that TRX. This is
fixing the restart with a multi TRX setup.

Even if we have the msg->trx, use the gsm_bts_trx_by_nr and get
the TRX from the fom header. This is because the OpenBSC and the
BTS numbering might not match for the multi TRX case.
2010-05-13 00:16:15 +08:00
Holger Hans Peter Freyther
239f95467c ipaccess: Refactor... unite some code 2010-05-12 23:39:51 +08:00
Holger Hans Peter Freyther
cd80c73f37 ipaccess: Use the right trx when performing the test 2010-05-12 23:02:23 +08:00
Holger Hans Peter Freyther
d6f1c4afbb ipaccess: Use the current TRX to set the OML address. 2010-05-12 22:48:28 +08:00
Holger Hans Peter Freyther
0c8af75c94 Increase the version number. 2010-05-12 22:25:40 +08:00
Holger Hans Peter Freyther
e4b33be6fc chan: After sending the GSM04.08 RR Release, reset the subscriber and wait
After we send the SACH DEACTIVATE the BTS will get back to us with a
Release Indication which will trigger the RF Channel Release handling. This
is why we can return here, but we need to put the subscriber reference to
make sure to not end in a infinite loop.

This and the previous change fix the USSD issue for me.
2010-05-12 22:09:24 +08:00
Holger Hans Peter Freyther
cc7461cefc bsc_msc_ip: Assign a dummy gsm_subscriber to send a SACH DEACTIVATE
This is part of fixing USSD delivered to the MS. Currently only MT
services would end up with a GSM Subscriber assigned. The LCHAN code
is using the GSM Subscriber to figure out if a SACH DEACTIVATE should
be send to the MS. Add code to always assign a GSM Subscriber.
2010-05-12 22:09:16 +08:00
Holger Hans Peter Freyther
e174061d17 bssap: Use libosmocore for message creation. 2010-05-12 18:34:20 +08:00
Holger Hans Peter Freyther
6e1c3412ae bssap: Use libosmocore to create GSM0808 Reset 2010-05-12 18:33:15 +08:00
Holger Hans Peter Freyther
bff54b3e00 bssap: Start to libosmocore for gsm0808 message creation. 2010-05-12 18:31:13 +08:00
Holger Hans Peter Freyther
e75eb4ca25 ipaccess: Wait for the BASEBAND_TRANSCEIVER and then bootstrap OML
Currently we are connecting to the BTS and once the OML is established
we are bootstrapping the OML. This does not work for a multi TRX setup
as we will need to use a trx_nr != 0 for it.

Change the code to wait for a message (in this case NM OC_BASEBAND_TRANSC)
to detect the trx_nr used by the BTS and then use that TRX to bootstrap
the network.

I have tested setting the unit id on a single and multi trx system for
the first and second trx.
2010-05-12 17:16:18 +08:00
Holger Hans Peter Freyther
566737a4b8 abis: Pass the abis_om_obj_inst in the nm_state_event.. 2010-05-12 17:16:18 +08:00
Holger Hans Peter Freyther
2b7350240d nat: Have a recycle timer that removes unconfirmed SCCP connections.
The MSC does not respond to a SCCP CR with Paging Response as GSM
payload, when the response comes in 'too late'. Prevent the MUX having
stale connections and start removing old connections every 20 minutes.
2010-05-12 00:58:08 +08:00
Holger Hans Peter Freyther
d76b53c00e nat: When we fail to reallocate... also close down the MGCP part
Give the BSC a chanche to close down MGCP ports as well.
2010-05-12 00:35:07 +08:00
Holger Hans Peter Freyther
9c9ef7796a nat: Store the creation time of a sccp connection.
Generate it when creating the connection but also when
reusing an existing connection.
2010-05-12 00:33:38 +08:00
Holger Hans Peter Freyther
49fcc8fc90 bsc_msc_ip: Use constants for ?/0/1. 2010-05-11 22:54:29 +08:00
Holger Hans Peter Freyther
51a4bcc96a Increase the version... as we have new commands 2010-05-11 20:02:36 +08:00
Holger Hans Peter Freyther
d6238120dd bsc_msc_ip: Add an extra command to show the MSC status. 2010-05-11 20:00:22 +08:00
Holger Hans Peter Freyther
7407aec921 bsc_msc_ip: Move the MSC connection into the structure 2010-05-11 20:00:22 +08:00
Holger Hans Peter Freyther
e575ce69ce nat: Print the MSC status with a new vty command. 2010-05-11 20:00:16 +08:00
Holger Hans Peter Freyther
c1ca0ff091 misc: Make sure PACKAGE_VERSION is getting defined with a useful content.
PACKAGE_VERSION is used by the copyright message.
2010-05-11 19:08:10 +08:00
Holger Hans Peter Freyther
661e68b78f bsc_msc_ip: Add a test mode to send messages to the MSC.
Check if the MSC likes paging responses when it has not
recently send out a paging request.
2010-05-11 19:08:10 +08:00
Holger Hans Peter Freyther
376e146cfb gsm0408: Use counter_inc to increment the counter. 2010-05-11 19:08:10 +08:00
Holger Hans Peter Freyther
eb3ab2f85b bsc_msc: Add a connection timeout for the MSC.
When no one is listening our connection would get stuck
in the SYN_SENT state and we would be there forever.
2010-05-05 22:48:56 +08:00
Holger Hans Peter Freyther
ebc38e4f26 Version bump for testing it on the target 2010-05-05 20:43:11 +08:00
Holger Hans Peter Freyther
e2ab44a439 nat: Using the right fd can be a good idea as well 2010-05-05 20:42:14 +08:00
Holger Hans Peter Freyther
8b3cced773 Another version... another try 2010-05-05 20:37:10 +08:00
Holger Hans Peter Freyther
3d1b0770f4 nat: Fix bad bug, make sure the fd is not overwritten..
The adding of the innocent looking code was actually overwrote
the fd and then stupid things happened. Rename variables to avoid
that. rc,ret should be scratch variables...
2010-05-05 20:33:34 +08:00
Holger Hans Peter Freyther
99743fb7ec Bump the version for TCP_NODELAY. 2010-05-05 19:01:23 +08:00
Holger Hans Peter Freyther
a2a42a7561 bsc_msc_ip: Attempt to disable nagle
Use TCP_NODELAY on the connection to the MSC. We want small
messages to be send immediately.
2010-05-05 19:00:38 +08:00
Holger Hans Peter Freyther
ebd57da87d nat: Use TCP_NODELAY for the connection to the BSC.
We do not want to use NAGLE for the BSC connection.
2010-05-05 18:59:54 +08:00
Holger Hans Peter Freyther
b0ee082bb0 Bump version
Configurable timeout.
2010-05-05 17:52:40 +08:00
Holger Hans Peter Freyther
81f6a4c0bf bsc_msc_ip: Add VTY code for ping/pong timeout. 2010-05-05 17:46:08 +08:00
Holger Hans Peter Freyther
3978de52c1 bsc_msc_ip: Do not send a ping when the timeout is negative 2010-05-05 17:42:32 +08:00
Holger Hans Peter Freyther
7faf692cb7 bsc_msc_ip: Make the ping/pong timeouts configurable
Take the timeouts from the struct.
2010-05-05 17:39:22 +08:00
Holger Hans Peter Freyther
0cf25d5154 nat: Improve log messages. Refer to ip and fd. 2010-05-05 17:03:44 +08:00
Holger Hans Peter Freyther
08db178271 nat: Make ping/pong timeout configurable. 2010-05-05 16:57:38 +08:00
Holger Hans Peter Freyther
936d8c1b64 Work with later libosmocore. 2010-05-03 19:38:01 +08:00
Harald Welte
3170305e56 move gsm48_construct_ra() to libosmocore 2010-05-03 19:36:58 +08:00
Harald Welte
0f3490dd03 'struct gprs_ra_id' is now defined in libosmocore 2010-05-03 19:36:41 +08:00
63 changed files with 2336 additions and 7166 deletions

View File

@@ -4,7 +4,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
SUBDIRS = include src tests
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = openbsc.pc libsccp.pc
pkgconfig_DATA = openbsc.pc
BUILT_SOURCES = $(top_srcdir)/.version
$(top_srcdir)/.version:

View File

@@ -1,7 +1,6 @@
dnl Process this file with autoconf to produce a configure script
AC_INIT
AM_INIT_AUTOMAKE(openbsc, 0.3.99.3onwaves)
AC_INIT(openbsc, 0.3.99.20onwaves)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -16,7 +15,8 @@ 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.3)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.22)
PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.3)
dnl checks for header files
AC_HEADER_STDC
@@ -40,10 +40,8 @@ AM_CONFIG_HEADER(bscconfig.h)
AC_OUTPUT(
openbsc.pc
libsccp.pc
include/openbsc/Makefile
include/vty/Makefile
include/sccp/Makefile
include/Makefile
src/Makefile
tests/Makefile
@@ -51,6 +49,4 @@ AC_OUTPUT(
tests/gsm0408/Makefile
tests/db/Makefile
tests/channel/Makefile
tests/sccp/Makefile
tests/bsc-nat/Makefile
Makefile)

View File

@@ -1,3 +1,3 @@
SUBDIRS = openbsc vty sccp
SUBDIRS = openbsc vty
noinst_HEADERS = mISDNif.h compat_af_isdn.h

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 osmo_bsc_rf.h \
osmo_bsc_grace.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
openbscdir = $(includedir)/openbsc

View File

@@ -55,6 +55,8 @@ struct ipac_bcch_info {
u_int8_t ca_list_si1[16];
};
extern const struct value_string abis_nm_adm_state_names[];
extern const struct value_string abis_nm_obj_class_names[];
extern const struct tlv_definition nm_att_tlvdef;
/* PUBLIC */
@@ -92,7 +94,7 @@ int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1,
int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg);
int abis_nm_event_reports(struct gsm_bts *bts, int on);
int abis_nm_reset_resource(struct gsm_bts *bts);
int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
u_int8_t win_size, int forced,
gsm_cbfn *cbfn, void *cb_data);
int abis_nm_software_load_status(struct gsm_bts *bts);
@@ -148,7 +150,7 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
u_int8_t *attr, int attr_len);
int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr,
int attr_len);
int abis_nm_ipaccess_restart(struct gsm_bts *bts);
int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx);
int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr,
u_int8_t *attr, u_int8_t attr_len);
@@ -164,9 +166,15 @@ enum nm_evt {
EVT_STATECHG_ADM,
};
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state);
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
struct abis_om_obj_inst *obj_inst);
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);
int abis_nm_vty_init(void);
#endif /* _NM_H */

View File

@@ -32,16 +32,20 @@ struct bsc_msc_connection {
int is_authenticated;
const char *ip;
int port;
int prio;
void (*connection_loss) (struct bsc_msc_connection *);
void (*connected) (struct bsc_msc_connection *);
struct timer_list reconnect_timer;
struct timer_list timeout_timer;
};
struct bsc_msc_connection *bsc_msc_create(const char *ip, int port);
struct bsc_msc_connection *bsc_msc_create(const char *ip, int port, int prio);
int bsc_msc_connect(struct bsc_msc_connection *);
void bsc_msc_schedule_connect(struct bsc_msc_connection *);
void bsc_msc_lost(struct bsc_msc_connection *);
struct msgb *bsc_msc_id_get_resp(const char *token);
#endif

View File

@@ -1,20 +0,0 @@
#ifndef BSC_MSC_RF
#define BSC_MSC_RF
#include <osmocore/write_queue.h>
struct gsm_network;
struct bsc_msc_rf {
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 *bsc_msc_rf_create(const char *path, struct gsm_network *net);
#endif

View File

@@ -25,7 +25,7 @@
#include "mgcp.h"
#include <sys/types.h>
#include <sccp/sccp_types.h>
#include <osmocom/sccp/sccp_types.h>
#include <osmocore/select.h>
#include <osmocore/msgb.h>
@@ -42,6 +42,15 @@
struct bsc_nat;
enum {
NAT_CON_TYPE_NONE,
NAT_CON_TYPE_LU,
NAT_CON_TYPE_CM_SERV_REQ,
NAT_CON_TYPE_PAG_RESP,
NAT_CON_TYPE_LOCAL_REJECT,
NAT_CON_TYPE_OTHER,
};
/*
* For the NAT we will need to analyze and later patch
* the received message. This would require us to parse
@@ -116,10 +125,17 @@ struct sccp_connections {
struct sccp_source_reference remote_ref;
int has_remote_ref;
/* status */
int con_type;
int con_local;
/* GSM audio handling. That is 32 * multiplex + ts */
int crcx;
int msc_timeslot;
int bsc_timeslot;
/* timeout handling */
struct timespec creation_time;
};
/**
@@ -146,11 +162,10 @@ struct bsc_config {
unsigned int lac;
int nr;
char *description;
/* imsi white and blacklist */
char *imsi_allow;
regex_t imsi_allow_re;
char *imsi_deny;
regex_t imsi_deny_re;
char *acc_lst_name;
int forbid_paging;
@@ -189,6 +204,24 @@ struct bsc_nat_statistics {
} msc;
};
struct bsc_nat_acc_lst {
struct llist_head list;
/* the name of the list */
const char *name;
struct llist_head fltr_list;
};
struct bsc_nat_acc_lst_entry {
struct llist_head list;
/* the filter */
char *imsi_allow;
regex_t imsi_allow_re;
char *imsi_deny;
regex_t imsi_deny_re;
};
/**
* the structure of the "nat" network
*/
@@ -199,9 +232,13 @@ struct bsc_nat {
/* active BSC connections that need patching */
struct llist_head bsc_connections;
/* access lists */
struct llist_head access_lists;
/* known BSC's */
struct llist_head bsc_configs;
int num_bsc;
int bsc_ip_tos;
/* MGCP config */
struct mgcp_config *mgcp_cfg;
@@ -213,14 +250,18 @@ struct bsc_nat {
char *msc_ip;
int msc_port;
int first_contact;
struct bsc_msc_connection *msc_con;
char *token;
/* timeouts */
int auth_timeout;
int ping_timeout;
int pong_timeout;
struct bsc_endpoint *bsc_endpoints;
/* filter */
char *imsi_allow;
regex_t imsi_allow_re;
char *imsi_deny;
regex_t imsi_deny_re;
char *acc_lst_name;
/* statistics */
struct bsc_nat_statistics stats;
@@ -236,6 +277,8 @@ void bsc_nat_set_msc_ip(struct bsc_nat *bsc, const char *ip);
void sccp_connection_destroy(struct sccp_connections *);
void bsc_close_connection(struct bsc_connection *);
const char *bsc_con_type_to_string(int type);
/**
* parse the given message into the above structure
*/
@@ -248,10 +291,16 @@ int bsc_nat_filter_ipa(int direction, struct msgb *msg, struct bsc_nat_parsed *p
int bsc_nat_vty_init(struct bsc_nat *nat);
struct bsc_connection *bsc_nat_find_bsc(struct bsc_nat *nat, struct msgb *msg, int *_lac);
/**
* Content filtering.
*/
int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg,
struct bsc_nat_parsed *, int *con_type);
/**
* SCCP patching and handling
*/
int create_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed);
struct sccp_connections *create_sccp_src_ref(struct bsc_connection *bsc, struct bsc_nat_parsed *parsed);
int update_sccp_src_ref(struct sccp_connections *sccp, struct bsc_nat_parsed *parsed);
void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed);
struct sccp_connections *patch_sccp_src_ref_to_bsc(struct msgb *, struct bsc_nat_parsed *, struct bsc_nat *);
@@ -273,9 +322,17 @@ void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg);
void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc);
int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60]);
int bsc_mgcp_extract_ci(const char *resp);
uint32_t bsc_mgcp_extract_ci(const char *resp);
int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int id);
/* IMSI allow/deny handling */
void bsc_parse_reg(void *ctx, regex_t *reg, char **imsi, int argc, const char **argv);
struct bsc_nat_acc_lst *bsc_nat_acc_lst_find(struct bsc_nat *nat, const char *name);
struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *name);
void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst);
struct bsc_nat_acc_lst_entry *bsc_nat_acc_lst_entry_create(struct bsc_nat_acc_lst *);
#endif

View File

@@ -45,12 +45,15 @@ int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv,
int h_len);
int send_siemens_mrpci(struct gsm_lchan *lchan, u_int8_t *classmark2_lv);
int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type);
int gsm48_extract_mi(uint8_t *classmark2, int length, char *mi_string, uint8_t *mi_type);
int gsm48_paging_extract_mi(struct gsm48_pag_resp *pag, int length, char *mi_string, u_int8_t *mi_type);
int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr);
int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode);
int gsm48_rx_rr_modif_ack(struct msgb *msg);
int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg);
struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value);
struct msgb *gsm48_create_loc_upd_rej(uint8_t cause);
#endif

View File

@@ -19,4 +19,7 @@ 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);
int gsm0480_send_ussdNotify(struct gsm_lchan *lchan, int level, const char *text);
int gsm0480_send_releaseComplete(struct gsm_lchan *lchan);
#endif

View File

@@ -3,6 +3,8 @@
#include <sys/types.h>
struct bsc_msc_connection;
enum gsm_phys_chan_config {
GSM_PCHAN_NONE,
GSM_PCHAN_CCCH,
@@ -79,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 osmo_bsc_rf;
struct sccp_connection;
/* Real authentication information containing Ki */
@@ -121,6 +125,8 @@ struct bss_sccp_connection_data {
struct sccp_connection *sccp;
int ciphering_handled : 1;
int new_subscriber;
/* Timers... */
/* for assginment command */
@@ -141,6 +147,9 @@ struct bss_sccp_connection_data {
struct llist_head sccp_queue;
unsigned int sccp_queue_size;
/* which msc connection to use? */
struct bsc_msc_connection *msc_con;
/* Active connections */
struct llist_head active_connections;
};
@@ -216,6 +225,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;
@@ -263,6 +273,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];
@@ -286,6 +299,9 @@ struct gsm_lchan {
/* release reason */
u_int8_t release_reason;
/* timestamp */
struct timeval alloc_time;
};
struct gsm_e1_subslot {
@@ -513,10 +529,12 @@ struct gsm_bts {
struct {
struct gsm_nm_state nm_state;
u_int16_t nsei;
uint8_t timer[7];
} nse;
struct {
struct gsm_nm_state nm_state;
u_int16_t bvci;
uint8_t timer[11];
} cell;
struct gsm_bts_gprs_nsvc nsvc[2];
u_int8_t rac;
@@ -529,6 +547,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 */
@@ -662,6 +684,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;
@@ -673,6 +698,13 @@ struct gsm_network {
char *bsc_token;
char *msc_ip;
int msc_port;
int msc_ip_dscp;
struct bsc_msc_connection *msc_con;
int ping_timeout;
int pong_timeout;
struct osmo_bsc_rf *rf;
char *ussd_grace_txt;
char *ussd_welcome_txt;
};
#define SMS_HDR_SIZE 128
@@ -774,14 +806,6 @@ const char *bts_gprs_mode_name(enum bts_gprs_mode mode);
void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
/* A parsed GPRS routing area */
struct gprs_ra_id {
u_int16_t mnc;
u_int16_t mcc;
u_int16_t lac;
u_int8_t rac;
};
int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts);
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan);

View File

@@ -29,6 +29,7 @@
#include <arpa/inet.h>
#define RTP_PORT_DEFAULT 4000
#define RTP_PORT_NET_DEFAULT 16000
/**
* Calculate the RTP audio port for the given multiplex
* and the direction. This allows a semi static endpoint
@@ -74,10 +75,29 @@ struct mgcp_config;
#define MGCP_POLICY_REJECT 5
#define MGCP_POLICY_DEFER 6
typedef int (*mgcp_change)(struct mgcp_config *cfg, int endpoint, int state, int local_rtp);
typedef int (*mgcp_realloc)(struct mgcp_config *cfg, int endpoint);
typedef int (*mgcp_change)(struct mgcp_config *cfg, int endpoint, int state);
typedef int (*mgcp_policy)(struct mgcp_config *cfg, int endpoint, int state, const char *transactio_id);
typedef int (*mgcp_reset)(struct mgcp_config *cfg);
#define PORT_ALLOC_STATIC 0
#define PORT_ALLOC_DYNAMIC 1
/**
* This holds information on how to allocate ports
*/
struct mgcp_port_range {
int mode;
/* pre-allocated from a base? */
int base_port;
/* dynamically allocated */
int range_start;
int range_end;
int last_port;
};
struct mgcp_config {
/* common configuration */
int source_port;
@@ -91,14 +111,10 @@ struct mgcp_config {
char *audio_name;
int audio_payload;
int audio_loop;
int early_bind;
int rtp_base_port;
/* only used in forward mode */
char *forward_ip;
int forward_port;
unsigned int last_call_id;
struct mgcp_port_range bts_ports;
struct mgcp_port_range net_ports;
int endp_dscp;
/* endpoint configuration */
unsigned int number_endpoints;
@@ -111,7 +127,10 @@ struct mgcp_config {
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
mgcp_realloc realloc_cb;
void *data;
uint32_t last_call_id;
};
/* config management */
@@ -119,7 +138,6 @@ struct mgcp_config *mgcp_config_alloc(void);
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg);
int mgcp_vty_init(void);
int mgcp_endpoints_allocate(struct mgcp_config *cfg);
int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
void mgcp_free_endp(struct mgcp_endpoint *endp);
/*
@@ -133,7 +151,7 @@ static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
{
if (timeslot == 0)
timeslot = 1;
return timeslot + (31 * multiplex);
return timeslot + (32 * multiplex);
}

View File

@@ -28,39 +28,86 @@
#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_rtp_end {
/* statistics */
unsigned int packets;
struct in_addr addr;
/* in network byte order */
int rtp_port, rtcp_port;
int payload_type;
/*
* Each end has a socket...
*/
struct bsc_fd rtp;
struct bsc_fd rtcp;
int local_port;
int local_alloc;
};
enum {
MGCP_TAP_BTS_IN,
MGCP_TAP_BTS_OUT,
MGCP_TAP_NET_IN,
MGCP_TAP_NET_OUT,
/* last element */
MGCP_TAP_COUNT
};
struct mgcp_rtp_tap {
int enabled;
struct sockaddr_in forward;
};
struct mgcp_endpoint {
int ci;
int allocated;
uint32_t ci;
char *callid;
char *local_options;
int conn_mode;
int bts_payload_type;
int net_payload_type;
/* the local rtp port we are binding to */
int rtp_port;
/*
* RTP mangling:
* - we get RTP and RTCP to us and need to forward to the BTS
* - we get RTP and RTCP from the BTS and forward to the network
*/
struct bsc_fd local_rtp;
struct bsc_fd local_rtcp;
struct in_addr remote;
struct in_addr bts;
/* in network byte order */
int net_rtp, net_rtcp;
int bts_rtp, bts_rtcp;
int orig_mode;
/* backpointer */
struct mgcp_config *cfg;
/* statistics */
unsigned int in_bts;
unsigned int in_remote;
/* port status for bts/net */
struct mgcp_rtp_end bts_end;
struct mgcp_rtp_end net_end;
/* sequence bits */
struct mgcp_rtp_state net_state;
struct mgcp_rtp_state bts_state;
/* SSRC/seq/ts patching for loop */
int allow_patch;
/* tap for the endpoint */
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
};
#define ENDPOINT_NUMBER(endp) abs(endp - endp->cfg->endpoints)
@@ -74,5 +121,8 @@ int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
struct mgcp_msg_ptr *ptr, int size,
const char **transaction_id, struct mgcp_endpoint **endp);
int mgcp_send_dummy(struct mgcp_endpoint *endp);
int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
int mgcp_free_rtp_port(struct mgcp_rtp_end *end);
#endif

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 OSMO_BSC_GRACE_H
#define OSMO_BSC_GRACE_H
#include "gsm_data.h"
int bsc_grace_allow_new_connection(struct gsm_network *network);
#endif

View File

@@ -0,0 +1,22 @@
#ifndef BSC_MSC_RF
#define BSC_MSC_RF
#include <osmocore/write_queue.h>
struct gsm_network;
struct osmo_bsc_rf {
/* the value of signal.h */
int policy;
struct bsc_fd listen;
struct gsm_network *gsm_network;
};
struct osmo_bsc_rf_conn {
struct write_queue queue;
struct osmo_bsc_rf *rf;
};
struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net);
#endif

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;
@@ -133,7 +141,7 @@ struct scall_signal_data {
};
struct ipacc_ack_signal_data {
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
u_int8_t msg_type;
};
@@ -143,4 +151,8 @@ struct challoc_signal_data {
enum gsm_chan_t type;
};
struct rf_signal_data {
struct gsm_network *net;
};
#endif

View File

@@ -1,2 +0,0 @@
sccp_HEADERS = sccp_types.h sccp.h
sccpdir = $(includedir)/sccp

View File

@@ -1,172 +0,0 @@
/*
* SCCP management code
*
* (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef SCCP_H
#define SCCP_H
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "sccp_types.h"
struct msgb;
struct sccp_system;
enum {
SCCP_CONNECTION_STATE_NONE,
SCCP_CONNECTION_STATE_REQUEST,
SCCP_CONNECTION_STATE_CONFIRM,
SCCP_CONNECTION_STATE_ESTABLISHED,
SCCP_CONNECTION_STATE_RELEASE,
SCCP_CONNECTION_STATE_RELEASE_COMPLETE,
SCCP_CONNECTION_STATE_REFUSED,
SCCP_CONNECTION_STATE_SETUP_ERROR,
};
struct sockaddr_sccp {
sa_family_t sccp_family; /* AF_SCCP in the future??? */
u_int8_t sccp_ssn; /* subssystem number for routing */
/* TODO fill in address indicator... if that is ever needed */
/* not sure about these */
/* u_int8_t sccp_class; */
};
/*
* parsed structure of an address
*/
struct sccp_address {
struct sccp_called_party_address address;
u_int8_t ssn;
u_int8_t poi[2];
};
struct sccp_optional_data {
u_int8_t data_len;
u_int8_t data_start;
};
struct sccp_connection {
/* public */
void *data_ctx;
void (*data_cb)(struct sccp_connection *conn, struct msgb *msg, unsigned int len);
void *state_ctx;
void (*state_cb)(struct sccp_connection *, int old_state);
struct sccp_source_reference source_local_reference;
struct sccp_source_reference destination_local_reference;
int connection_state;
/* private */
/* list of active connections */
struct llist_head list;
struct sccp_system *system;
int incoming;
};
/**
* system functionality to implement on top of any other transport layer:
* call sccp_system_incoming for incoming data (from the network)
* sccp will call outgoing whenever outgoing data exists
*/
int sccp_system_init(void (*outgoing)(struct msgb *data, void *ctx), void *context);
int sccp_system_incoming(struct msgb *data);
/**
* Send data on an existing connection
*/
int sccp_connection_write(struct sccp_connection *connection, struct msgb *data);
int sccp_connection_send_it(struct sccp_connection *connection);
int sccp_connection_close(struct sccp_connection *connection, int cause);
int sccp_connection_free(struct sccp_connection *connection);
/**
* internal..
*/
int sccp_connection_force_free(struct sccp_connection *conn);
/**
* Create a new socket. Set your callbacks and then call bind to open
* the connection.
*/
struct sccp_connection *sccp_connection_socket(void);
/**
* Open the connection and send additional data
*/
int sccp_connection_connect(struct sccp_connection *conn,
const struct sockaddr_sccp *sccp_called,
struct msgb *data);
/**
* mostly for testing purposes only. Set the accept callback.
* TODO: add true routing information... in analogy to socket, bind, accept
*/
int sccp_connection_set_incoming(const struct sockaddr_sccp *sock,
int (*accept_cb)(struct sccp_connection *connection, void *data),
void *user_data);
/**
* Send data in terms of unit data. A fixed address indicator will be used.
*/
int sccp_write(struct msgb *data,
const struct sockaddr_sccp *sock_sender,
const struct sockaddr_sccp *sock_target, int class);
int sccp_set_read(const struct sockaddr_sccp *sock,
int (*read_cb)(struct msgb *msgb, unsigned int, void *user_data),
void *user_data);
/* generic sock addresses */
extern const struct sockaddr_sccp sccp_ssn_bssap;
/* helpers */
u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref);
struct sccp_source_reference sccp_src_ref_from_int(u_int32_t);
/**
* Below this are helper functions and structs for parsing SCCP messages
*/
struct sccp_parse_result {
struct sccp_address called;
struct sccp_address calling;
/* point to the msg packet */
struct sccp_source_reference *source_local_reference;
struct sccp_source_reference *destination_local_reference;
/* data pointer */
int data_len;
};
/*
* helper functions for the nat code
*/
int sccp_determine_msg_type(struct msgb *msg);
int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result);
#endif

View File

@@ -1,420 +0,0 @@
/*
* ITU Q.713 defined types for SCCP
*
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef SCCP_TYPES_H
#define SCCP_TYPES_H
#include <endian.h>
/* Table 1/Q.713 - SCCP message types */
enum sccp_message_types {
SCCP_MSG_TYPE_CR = 1,
SCCP_MSG_TYPE_CC = 2,
SCCP_MSG_TYPE_CREF = 3,
SCCP_MSG_TYPE_RLSD = 4,
SCCP_MSG_TYPE_RLC = 5,
SCCP_MSG_TYPE_DT1 = 6,
SCCP_MSG_TYPE_DT2 = 7,
SCCP_MSG_TYPE_AK = 8,
SCCP_MSG_TYPE_UDT = 9,
SCCP_MSG_TYPE_UDTS = 10,
SCCP_MSG_TYPE_ED = 11,
SCCP_MSG_TYPE_EA = 12,
SCCP_MSG_TYPE_RSR = 13,
SCCP_MSG_TYPE_RSC = 14,
SCCP_MSG_TYPE_ERR = 15,
SCCP_MSG_TYPE_IT = 16,
SCCP_MSG_TYPE_XUDT = 17,
SCCP_MSG_TYPE_XUDTS = 18,
SCCP_MSG_TYPE_LUDT = 19,
SCCP_MSG_TYPE_LUDTS = 20
};
/* Table 2/Q.713 - SCCP parameter name codes */
enum sccp_parameter_name_codes {
SCCP_PNC_END_OF_OPTIONAL = 0,
SCCP_PNC_DESTINATION_LOCAL_REFERENCE = 1,
SCCP_PNC_SOURCE_LOCAL_REFERENCE = 2,
SCCP_PNC_CALLED_PARTY_ADDRESS = 3,
SCCP_PNC_CALLING_PARTY_ADDRESS = 4,
SCCP_PNC_PROTOCOL_CLASS = 5,
SCCP_PNC_SEGMENTING = 6,
SCCP_PNC_RECEIVE_SEQ_NUMBER = 7,
SCCP_PNC_SEQUENCING = 8,
SCCP_PNC_CREDIT = 9,
SCCP_PNC_RELEASE_CAUSE = 10,
SCCP_PNC_RETURN_CAUSE = 11,
SCCP_PNC_RESET_CAUSE = 12,
SCCP_PNC_ERROR_CAUSE = 13,
SCCP_PNC_REFUSAL_CAUSE = 14,
SCCP_PNC_DATA = 15,
SCCP_PNC_SEGMENTATION = 16,
SCCP_PNC_HOP_COUNTER = 17,
SCCP_PNC_IMPORTANCE = 18,
SCCP_PNC_LONG_DATA = 19,
};
/* Figure 3/Q.713 Called/calling party address */
enum {
SCCP_TITLE_IND_NONE = 0,
SCCP_TITLE_IND_NATURE_ONLY = 1,
SCCP_TITLE_IND_TRANSLATION_ONLY = 2,
SCCP_TITLE_IND_TRANS_NUM_ENC = 3,
SCCP_TITLE_IND_TRANS_NUM_ENC_NATURE = 4,
};
enum {
SCCP_CALL_ROUTE_ON_SSN = 1,
SCCP_CALL_ROUTE_ON_GT = 0,
};
struct sccp_called_party_address {
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t point_code_indicator : 1,
ssn_indicator : 1,
global_title_indicator : 4,
routing_indicator : 1,
reserved : 1;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t reserved : 1,
routing_indicator : 1,
global_title_indicator : 4,
ssn_indicator : 1,
point_code_indicator : 1;
#endif
u_int8_t data[0];
} __attribute__((packed));
/* indicator indicates presence in the above order */
/* Figure 6/Q.713 */
struct sccp_signalling_point_code {
u_int8_t lsb;
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t msb : 6,
reserved : 2;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t reserved : 2,
msb : 6;
#endif
} __attribute__((packed));
/* SSN == subsystem number */
enum sccp_subsystem_number {
SCCP_SSN_NOT_KNOWN_OR_USED = 0,
SCCP_SSN_MANAGEMENT = 1,
SCCP_SSN_RESERVED_ITU = 2,
SCCP_SSN_ISDN_USER_PART = 3,
SCCP_SSN_OMAP = 4, /* operation, maint and administration part */
SCCP_SSN_MAP = 5, /* mobile application part */
SCCP_SSN_HLR = 6,
SCCP_SSN_VLR = 7,
SCCP_SSN_MSC = 8,
SCCP_SSN_EIC = 9, /* equipent identifier centre */
SCCP_SSN_AUC = 10, /* authentication centre */
SCCP_SSN_ISDN_SUPPL_SERVICES = 11,
SCCP_SSN_RESERVED_INTL = 12,
SCCP_SSN_ISDN_EDGE_TO_EDGE = 13,
SCCP_SSN_TC_TEST_RESPONDER = 14,
/* From GSM 03.03 8.2 */
SCCP_SSN_BSSAP = 254,
SCCP_SSN_BSSOM = 253,
};
/* Q.713, 3.4.2.3 */
enum {
SCCP_NAI_UNKNOWN = 0,
SCCP_NAI_SUBSCRIBER_NUMBER = 1,
SCCP_NAI_RESERVED_NATIONAL = 2,
SCCP_NAI_NATIONAL_SIGNIFICANT = 3,
SCCP_NAI_INTERNATIONAL = 4,
};
struct sccp_global_title {
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t nature_of_addr_ind : 7,
odd_even : 1;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t odd_even : 1,
nature_of_addr_ind : 7;
#endif
u_int8_t data[0];
} __attribute__((packed));
/* Q.713, 3.3 */
struct sccp_source_reference {
u_int8_t octet1;
u_int8_t octet2;
u_int8_t octet3;
} __attribute__((packed));
/* Q.714, 3.6 */
enum sccp_protocol_class {
SCCP_PROTOCOL_CLASS_0 = 0,
SCCP_PROTOCOL_CLASS_1 = 1,
SCCP_PROTOCOL_CLASS_2 = 2,
SCCP_PROTOCOL_CLASS_3 = 3,
};
/* bits 5-8 when class0, class1 is used */
enum sccp_protocol_options {
SCCP_PROTOCOL_NO_SPECIAL = 0,
SCCP_PROTOCOL_RETURN_MESSAGE = 8,
};
enum sccp_release_cause {
SCCP_RELEASE_CAUSE_END_USER_ORIGINATED = 0,
SCCP_RELEASE_CAUSE_END_USER_CONGESTION = 1,
SCCP_RELEASE_CAUSE_END_USER_FAILURE = 2,
SCCP_RELEASE_CAUSE_SCCP_USER_ORIGINATED = 3,
SCCP_RELEASE_CAUSE_REMOTE_PROCEDURE_ERROR = 4,
SCCP_RELEASE_CAUSE_INCONSISTENT_CONN_DATA = 5,
SCCP_RELEASE_CAUSE_ACCESS_FAILURE = 6,
SCCP_RELEASE_CAUSE_ACCESS_CONGESTION = 7,
SCCP_RELEASE_CAUSE_SUBSYSTEM_FAILURE = 8,
SCCP_RELEASE_CAUSE_SUBSYSTEM_CONGESTION = 9,
SCCP_RELEASE_CAUSE_MTP_FAILURE = 10,
SCCP_RELEASE_CAUSE_NETWORK_CONGESTION = 11,
SCCP_RELEASE_CAUSE_EXPIRATION_RESET = 12,
SCCP_RELEASE_CAUSE_EXPIRATION_INACTIVE = 13,
SCCP_RELEASE_CAUSE_RESERVED = 14,
SCCP_RELEASE_CAUSE_UNQUALIFIED = 15,
SCCP_RELEASE_CAUSE_SCCP_FAILURE = 16,
};
enum sccp_return_cause {
SCCP_RETURN_CAUSE_NO_TRANSLATION_NATURE = 0,
SCCP_RETURN_CAUSE_NO_TRANSLATION = 1,
SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION = 2,
SCCP_RETURN_CAUSE_SUBSYSTEM_FAILURE = 3,
SCCP_RETURN_CAUSE_UNEQUIPPED_USER = 4,
SCCP_RETURN_CAUSE_MTP_FAILURE = 5,
SCCP_RETURN_CAUSE_NETWORK_CONGESTION = 6,
SCCP_RETURN_CAUSE_UNQUALIFIED = 7,
SCCP_RETURN_CAUSE_ERROR_IN_MSG_TRANSPORT = 8,
SCCP_RETURN_CAUSE_ERROR_IN_LOCAL_PROCESSING = 9,
SCCP_RETURN_CAUSE_DEST_CANNOT_PERFORM_REASSEMBLY = 10,
SCCP_RETURN_CAUSE_SCCP_FAILURE = 11,
SCCP_RETURN_CAUSE_HOP_COUNTER_VIOLATION = 12,
SCCP_RETURN_CAUSE_SEGMENTATION_NOT_SUPPORTED= 13,
SCCP_RETURN_CAUSE_SEGMENTATION_FAOLURE = 14
};
enum sccp_reset_cause {
SCCP_RESET_CAUSE_END_USER_ORIGINATED = 0,
SCCP_RESET_CAUSE_SCCP_USER_ORIGINATED = 1,
SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PS = 2,
SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PR = 3,
SCCP_RESET_CAUSE_RPC_OUT_OF_WINDOW = 4,
SCCP_RESET_CAUSE_RPC_INCORRECT_PS = 5,
SCCP_RESET_CAUSE_RPC_GENERAL = 6,
SCCP_RESET_CAUSE_REMOTE_END_USER_OPERATIONAL= 7,
SCCP_RESET_CAUSE_NETWORK_OPERATIONAL = 8,
SCCP_RESET_CAUSE_ACCESS_OPERATIONAL = 9,
SCCP_RESET_CAUSE_NETWORK_CONGESTION = 10,
SCCP_RESET_CAUSE_RESERVED = 11,
};
enum sccp_error_cause {
SCCP_ERROR_LRN_MISMATCH_UNASSIGNED = 0, /* local reference number */
SCCP_ERROR_LRN_MISMATCH_INCONSISTENT = 1,
SCCP_ERROR_POINT_CODE_MISMATCH = 2,
SCCP_ERROR_SERVICE_CLASS_MISMATCH = 3,
SCCP_ERROR_UNQUALIFIED = 4,
};
enum sccp_refusal_cause {
SCCP_REFUSAL_END_USER_ORIGINATED = 0,
SCCP_REFUSAL_END_USER_CONGESTION = 1,
SCCP_REFUSAL_END_USER_FAILURE = 2,
SCCP_REFUSAL_SCCP_USER_ORIGINATED = 3,
SCCP_REFUSAL_DESTINATION_ADDRESS_UKNOWN = 4,
SCCP_REFUSAL_DESTINATION_INACCESSIBLE = 5,
SCCP_REFUSAL_NET_QOS_NON_TRANSIENT = 6,
SCCP_REFUSAL_NET_QOS_TRANSIENT = 7,
SCCP_REFUSAL_ACCESS_FAILURE = 8,
SCCP_REFUSAL_ACCESS_CONGESTION = 9,
SCCP_REFUSAL_SUBSYSTEM_FAILURE = 10,
SCCP_REFUSAL_SUBSYTEM_CONGESTION = 11,
SCCP_REFUSAL_EXPIRATION = 12,
SCCP_REFUSAL_INCOMPATIBLE_USER_DATA = 13,
SCCP_REFUSAL_RESERVED = 14,
SCCP_REFUSAL_UNQUALIFIED = 15,
SCCP_REFUSAL_HOP_COUNTER_VIOLATION = 16,
SCCP_REFUSAL_SCCP_FAILURE = 17,
SCCP_REFUSAL_UNEQUIPPED_USER = 18,
};
/*
* messages... as of Q.713 Chapter 4
*/
struct sccp_connection_request {
/* mandantory */
u_int8_t type;
struct sccp_source_reference source_local_reference;
u_int8_t proto_class;
/* variable */
u_int8_t variable_called;
#if VARIABLE
called_party_address
#endif
/* optional */
u_int8_t optional_start;
#if OPTIONAL
credit 3
callingparty var 4-n
data 3-130
hop_counter 3
importance 3
end_of_optional 1
#endif
u_int8_t data[0];
} __attribute__((packed));
struct sccp_connection_confirm {
/* mandantory */
u_int8_t type;
struct sccp_source_reference destination_local_reference;
struct sccp_source_reference source_local_reference;
u_int8_t proto_class;
/* optional */
u_int8_t optional_start;
/* optional */
#if OPTIONAL
credit 3
called party 4
data 3-130
importance 3
end_of_optional 1
#endif
u_int8_t data[0];
} __attribute__((packed));
struct sccp_connection_refused {
/* mandantory */
u_int8_t type;
struct sccp_source_reference destination_local_reference;
u_int8_t cause;
/* optional */
u_int8_t optional_start;
/* optional */
#if OPTIONAL
called party 4
data 3-130
importance 3
end_of_optional 1
#endif
u_int8_t data[0];
} __attribute__((packed));
struct sccp_connection_released {
/* mandantory */
u_int8_t type;
struct sccp_source_reference destination_local_reference;
struct sccp_source_reference source_local_reference;
u_int8_t release_cause;
/* optional */
u_int8_t optional_start;
#if OPTIONAL
data 3-130
importance 3
end_of_optional 1
#endif
u_int8_t data[0];
} __attribute__((packed));
struct sccp_connection_release_complete {
u_int8_t type;
struct sccp_source_reference destination_local_reference;
struct sccp_source_reference source_local_reference;
} __attribute__((packed));
struct sccp_data_form1 {
/* mandantory */
u_int8_t type;
struct sccp_source_reference destination_local_reference;
u_int8_t segmenting;
/* variable */
u_int8_t variable_start;
#if VARIABLE
data 2-256;
#endif
u_int8_t data[0];
} __attribute__((packed));
struct sccp_data_unitdata {
/* mandantory */
u_int8_t type;
u_int8_t proto_class;
/* variable */
u_int8_t variable_called;
u_int8_t variable_calling;
u_int8_t variable_data;
#if VARIABLE
called party address
calling party address
#endif
u_int8_t data[0];
} __attribute__((packed));
struct sccp_data_it {
/* mandantory */
u_int8_t type;
struct sccp_source_reference destination_local_reference;
struct sccp_source_reference source_local_reference;
u_int8_t proto_class;
u_int8_t sequencing[2];
u_int8_t credit;
} __attribute__((packed));
struct sccp_proto_err {
u_int8_t type;
struct sccp_source_reference destination_local_reference;
u_int8_t error_cause;
};
#endif

View File

@@ -65,6 +65,7 @@ enum node_type {
VIEW_NODE, /* View node. Default mode of vty interface. */
AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
ENABLE_NODE, /* Enable node. */
OML_NODE,
CONFIG_NODE, /* Config node. Default mode of config file. */
SERVICE_NODE, /* Service node. */
DEBUG_NODE, /* Debug node. */
@@ -107,8 +108,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

@@ -1,10 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: OpenBSC SCCP Lib
Description: OpenBSC SCCP Lib
Version: @VERSION@
Libs: -L${libdir} -lsccp
Cflags: -I${includedir}/

View File

@@ -1,18 +1,15 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS)
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOSCCP_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
bscdir = $(libdir)
bsc_LIBRARIES = libsccp.a
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
chan_alloc.c debug.c \
chan_alloc.c debug.c abis_nm_vty.c \
gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c \
input/misdn.c input/ipaccess.c \
@@ -27,16 +24,14 @@ libmsc_a_SOURCES = gsm_subscriber.c db.c \
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
libsccp_a_SOURCES = sccp/sccp.c
bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c
bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c debug.c \
rs232.c bts_siemens_bs11.c
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_ip_LDADD = libbsc.a libvty.a libsccp.a
bsc_msc.c osmo_bsc_rf.c osmo_bsc_grace.c gsm_04_80.c
bsc_msc_ip_LDADD = libbsc.a libvty.a $(LIBOSMOSCCP_LIBS)
ipaccess_find_SOURCES = ipaccess/ipaccess-find.c
@@ -52,8 +47,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

View File

@@ -410,39 +410,59 @@ 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);
static struct value_string obj_class_names[] = {
{ NM_OC_SITE_MANAGER, "SITE MANAGER" },
const struct value_string abis_nm_obj_class_names[] = {
{ NM_OC_SITE_MANAGER, "SITE-MANAGER" },
{ NM_OC_BTS, "BTS" },
{ NM_OC_RADIO_CARRIER, "RADIO CARRIER" },
{ NM_OC_BASEB_TRANSC, "BASEBAND TRANSCEIVER" },
{ NM_OC_RADIO_CARRIER, "RADIO-CARRIER" },
{ NM_OC_BASEB_TRANSC, "BASEBAND-TRANSCEIVER" },
{ NM_OC_CHANNEL, "CHANNEL" },
{ NM_OC_BS11_ADJC, "ADJC" },
{ NM_OC_BS11_HANDOVER, "HANDOVER" },
{ NM_OC_BS11_PWR_CTRL, "POWER CONTROL" },
{ NM_OC_BS11_PWR_CTRL, "POWER-CONTROL" },
{ NM_OC_BS11_BTSE, "BTSE" },
{ NM_OC_BS11_RACK, "RACK" },
{ NM_OC_BS11_TEST, "TEST" },
{ NM_OC_BS11_ENVABTSE, "ENVABTSE" },
{ NM_OC_BS11_BPORT, "BPORT" },
{ NM_OC_GPRS_NSE, "GPRS NSE" },
{ NM_OC_GPRS_CELL, "GPRS CELL" },
{ NM_OC_GPRS_NSVC, "GPRS NSVC" },
{ NM_OC_GPRS_NSE, "GPRS-NSE" },
{ NM_OC_GPRS_CELL, "GPRS-CELL" },
{ NM_OC_GPRS_NSVC, "GPRS-NSVC" },
{ NM_OC_BS11, "SIEMENSHW" },
{ 0, NULL }
};
static const char *obj_class_name(u_int8_t oc)
{
return get_value_string(obj_class_names, oc);
return get_value_string(abis_nm_obj_class_names, oc);
}
const char *nm_opstate_name(u_int8_t os)
@@ -490,18 +510,17 @@ static struct value_string test_names[] = {
{ 0, NULL }
};
const struct value_string abis_nm_adm_state_names[] = {
{ NM_STATE_LOCKED, "Locked" },
{ NM_STATE_UNLOCKED, "Unlocked" },
{ NM_STATE_SHUTDOWN, "Shutdown" },
{ NM_STATE_NULL, "NULL" },
{ 0, NULL }
};
const char *nm_adm_name(u_int8_t adm)
{
switch (adm) {
case 1:
return "Locked";
case 2:
return "Unlocked";
case 3:
return "Shutdown";
default:
return "<not used>";
}
return get_value_string(abis_nm_adm_state_names, adm);
}
int nm_is_running(struct gsm_nm_state *s) {
@@ -678,7 +697,7 @@ static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class,
new_state = *nm_state;
new_state.administrative = adm_state;
rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state);
rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state, obj_inst);
nm_state->administrative = adm_state;
@@ -732,7 +751,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
/* Update the operational state of a given object in our in-memory data
* structures and send an event to the higher layer */
void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst);
rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state);
rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state, &foh->obj_inst);
nm_state->operational = new_state.operational;
nm_state->availability = new_state.availability;
if (nm_state->administrative == 0)
@@ -952,12 +971,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 +1008,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 +1040,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 +1059,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 +1073,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 "
@@ -1139,6 +1179,7 @@ enum sw_state {
struct abis_nm_sw {
struct gsm_bts *bts;
int trx_nr;
gsm_cbfn *cbfn;
void *cb_data;
int forced;
@@ -1276,7 +1317,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 */
@@ -1469,6 +1510,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) {
@@ -1489,6 +1531,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;
@@ -1509,6 +1552,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)
@@ -1530,6 +1574,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) {
@@ -1549,6 +1594,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:
@@ -1562,6 +1608,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");
@@ -1571,6 +1618,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:
@@ -1592,7 +1640,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
}
/* Load the specified software into the BTS */
int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
u_int8_t win_size, int forced,
gsm_cbfn *cbfn, void *cb_data)
{
@@ -1606,6 +1654,7 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
return -EBUSY;
sw->bts = bts;
sw->trx_nr = trx_nr;
switch (bts->type) {
case GSM_BTS_TYPE_BS11:
@@ -1616,8 +1665,8 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
break;
case GSM_BTS_TYPE_NANOBTS:
sw->obj_class = NM_OC_BASEB_TRANSC;
sw->obj_instance[0] = 0x00;
sw->obj_instance[1] = 0x00;
sw->obj_instance[0] = sw->bts->nr;
sw->obj_instance[1] = sw->trx_nr;
sw->obj_instance[2] = 0xff;
break;
case GSM_BTS_TYPE_UNKNOWN:
@@ -2012,7 +2061,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)
@@ -2551,7 +2600,7 @@ static int bs11_swload_cbfn(unsigned int hook, unsigned int event,
fle = fl_dequeue(&bs11_sw->file_list);
if (fle) {
/* start download the next file of our file list */
rc = abis_nm_software_load(bs11_sw->bts, fle->fname,
rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname,
bs11_sw->win_size,
bs11_sw->forced,
&bs11_swload_cbfn, bs11_sw);
@@ -2607,7 +2656,7 @@ int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
return -EINVAL;
/* start download the next file of our file list */
rc = abis_nm_software_load(bts, fle->fname, win_size, forced,
rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced,
bs11_swload_cbfn, bs11_sw);
talloc_free(fle);
return rc;
@@ -2688,6 +2737,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];
@@ -2709,10 +2759,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 *)
@@ -2775,12 +2827,12 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
case NM_MT_IPACC_RSL_CONNECT_NACK:
case NM_MT_IPACC_SET_NVATTR_NACK:
case NM_MT_IPACC_GET_NVATTR_NACK:
signal.bts = msg->trx->bts;
signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr);
signal.msg_type = foh->msg_type;
dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal);
break;
case NM_MT_IPACC_SET_NVATTR_ACK:
signal.bts = msg->trx->bts;
signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr);
signal.msg_type = foh->msg_type;
dispatch_signal(SS_NM, S_NM_IPACC_ACK, &signal);
break;
@@ -2866,9 +2918,16 @@ int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
}
/* restart / reboot an ip.access nanoBTS */
int abis_nm_ipaccess_restart(struct gsm_bts *bts)
int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx)
{
return __simple_cmd(bts, NM_MT_IPACC_RESTART);
struct abis_om_hdr *oh;
struct msgb *msg = nm_msgb_alloc();
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC,
trx->bts->nr, trx->nr, 0xff);
return abis_nm_sendmsg(trx->bts, msg);
}
int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
@@ -3004,3 +3063,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;
}

194
openbsc/src/abis_nm_vty.c Normal file
View File

@@ -0,0 +1,194 @@
/* VTY interface for A-bis OML (Netowrk Management) */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <osmocore/msgb.h>
#include <osmocore/tlv.h>
#include <osmocore/talloc.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/abis_nm.h>
#include <openbsc/vty.h>
#include <vty/command.h>
extern struct gsm_network *bsc_gsmnet;
static struct cmd_node oml_node = {
OML_NODE,
"%s(oml)# ",
1,
};
struct oml_node_state {
struct gsm_bts *bts;
uint8_t obj_class;
uint8_t obj_inst[3];
};
static int dummy_config_write(struct vty *v)
{
return CMD_SUCCESS;
}
/* FIXME: auto-generate those strings from the value_string lists */
#define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)"
#define NM_OBJCLASS_VTY_HELP "FIXME"
DEFUN(oml_class_inst, oml_class_inst_cmd,
"bts <0-255> oml class " NM_OBJCLASS_VTY
" instance <0-255> <0-255> <0-255>",
"BTS related commands\n" "BTS Number\n"
"Manipulate the OML managed objects\n"
"Object Class\n" NM_OBJCLASS_VTY_HELP
"Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n")
{
struct gsm_bts *bts;
struct oml_node_state *oms;
int bts_nr = atoi(argv[0]);
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;
}
oms = talloc_zero(tall_bsc_ctx, struct oml_node_state);
if (!oms)
return CMD_WARNING;
oms->bts = bts;
oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]);
oms->obj_inst[0] = atoi(argv[2]);
oms->obj_inst[1] = atoi(argv[3]);
oms->obj_inst[2] = atoi(argv[4]);
vty->index = oms;
vty->node = OML_NODE;
return CMD_SUCCESS;
}
DEFUN(oml_classnum_inst, oml_classnum_inst_cmd,
"bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>",
"BTS related commands\n" "BTS Number\n"
"Manipulate the OML managed objects\n"
"Object Class\n" "Object Class\n"
"Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n")
{
struct gsm_bts *bts;
struct oml_node_state *oms;
int bts_nr = atoi(argv[0]);
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;
}
oms = talloc_zero(tall_bsc_ctx, struct oml_node_state);
if (!oms)
return CMD_WARNING;
oms->bts = bts;
oms->obj_class = atoi(argv[1]);
oms->obj_inst[0] = atoi(argv[2]);
oms->obj_inst[1] = atoi(argv[3]);
oms->obj_inst[2] = atoi(argv[4]);
vty->index = oms;
vty->node = OML_NODE;
return CMD_SUCCESS;
}
DEFUN(oml_attrib_get, oml_attrib_get_cmd,
"attribute get <0-255>",
"OML Attribute Actions\n" "Get a single OML Attribute\n"
"OML Attribute Number\n")
{
struct oml_node_state *oms = vty->index;
/* FIXME */
return CMD_SUCCESS;
}
DEFUN(oml_attrib_set, oml_attrib_set_cmd,
"attribute set <0-255> .HEX",
"OML Attribute Actions\n" "Set a single OML Attribute\n"
"OML Attribute Number\n")
{
struct oml_node_state *oms = vty->index;
/* FIXME */
return CMD_SUCCESS;
}
DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd,
"change-adm-state (locked|unlocked|shutdown|null)",
"Change the Administrative State\n"
"Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n")
{
struct oml_node_state *oms = vty->index;
enum abis_nm_adm_state state;
state = get_string_value(abis_nm_adm_state_names, argv[0]);
abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0],
oms->obj_inst[1], oms->obj_inst[2], state);
return CMD_SUCCESS;
}
DEFUN(oml_opstart, oml_opstart_cmd,
"opstart", "Send an OPSTART message to the object")
{
struct oml_node_state *oms = vty->index;
abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0],
oms->obj_inst[1], oms->obj_inst[2]);
return CMD_SUCCESS;
}
int abis_nm_vty_init(void)
{
install_element(ENABLE_NODE, &oml_class_inst_cmd);
install_element(ENABLE_NODE, &oml_classnum_inst_cmd);
install_node(&oml_node, dummy_config_write);
install_default(OML_NODE);
install_element(OML_NODE, &oml_attrib_get_cmd);
install_element(OML_NODE, &oml_attrib_set_cmd);
install_element(OML_NODE, &oml_chg_adm_state_cmd);
install_element(OML_NODE, &oml_opstart_cmd);
return 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;
@@ -911,7 +925,7 @@ static int rsl_rx_meas_res(struct msgb *msg)
/* check if this channel is actually active */
/* FIXME: maybe this check should be way more generic/centralized */
if (msg->lchan->state != LCHAN_S_ACTIVE) {
LOGP(DRSL, LOGL_NOTICE, "%s: MEAS RES for inactive channel\n",
LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n",
gsm_lchan_name(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

@@ -481,7 +481,7 @@ static int handle_state_resp(enum abis_bs11_phase state)
* argument, so our swload_cbfn can distinguish
* a safety load from a regular software */
if (file_is_readable(fname_safety))
rc = abis_nm_software_load(g_bts, fname_safety,
rc = abis_nm_software_load(g_bts, 0xff, fname_safety,
win_size, param_forced,
swload_cbfn, g_bts);
else
@@ -697,7 +697,8 @@ int handle_serial_msg(struct msgb *rx_msg)
}
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
struct abis_om_obj_inst *obj_ins)
{
return 0;
}

View File

@@ -33,6 +33,7 @@
#include <openbsc/signal.h>
#include <openbsc/chan_alloc.h>
#include <osmocore/talloc.h>
#include <openbsc/ipaccess.h>
/* global pointer to the gsm network data structure */
extern struct gsm_network *bsc_gsmnet;
@@ -317,14 +318,14 @@ static unsigned char bs11_attr_radio[] =
static unsigned char nanobts_attr_bts[] = {
NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73,
/* interference avg. period in numbers of SACCH multifr */
NM_ATT_INTAVE_PARAM, 0x06,
NM_ATT_INTAVE_PARAM, 0x1f,
/* conn fail based on SACCH error rate */
NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10,
NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8,
NM_ATT_MAX_TA, 0x3f,
NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */
NM_ATT_CCCH_L_T, 10, /* percent */
NM_ATT_CCCH_L_I_P, 1, /* seconds */
NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x20,
NM_ATT_T200, 0x1e, 0x1e, 0x24, 0xa8, 0x34, 0x21, 0xa8,
NM_ATT_MAX_TA, 0x00,
NM_ATT_OVERL_PERIOD, 0x00, 0x01, 5, /* seconds */
NM_ATT_CCCH_L_T, 32, /* percent */
NM_ATT_CCCH_L_I_P, 5, /* seconds */
NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */
NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */
NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */
@@ -345,7 +346,7 @@ static unsigned char nanobts_attr_nse[] = {
3, /* (un)blocking retries */
3, /* reset timer (Tns-reset) */
3, /* reset retries */
30, /* test timer (Tns-test) */
3, /* test timer (Tns-test) */
3, /* alive timer (Tns-alive) */
10, /* alive retrires */
NM_ATT_IPACC_BSSGP_CFG, 0, 11,
@@ -366,29 +367,27 @@ static unsigned char nanobts_attr_cell[] = {
NM_ATT_IPACC_RAC, 0, 1, 1, /* routing area code */
NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2,
5, /* repeat time (50ms) */
3, /* repeat count */
1, /* repeat count */
NM_ATT_IPACC_BVCI, 0, 2, 0x03, 0x9d, /* BVCI 925 */
NM_ATT_IPACC_RLC_CFG, 0, 9,
20, /* T3142 */
5, /* T3169 */
5, /* T3191 */
200, /* T3193 */
5, /* T3195 */
10, /* N3101 */
4, /* N3103 */
8, /* N3105 */
15, /* RLC CV countdown */
NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */
0x14, /* T3142 */
0x05, /* T3169 */
0x05, /* T3191 */
0x14, /* T3193 */
0x05, /* T3195 */
0x0a, /* N3101 */
0x04, /* N3103 */
0x08, /* N3105 */
0x0f, /* RLC CV countdown */
NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x8f, 0xff, /* CS1..CS4 */
NM_ATT_IPACC_RLC_CFG_2, 0, 5,
0x00, 250, /* T downlink TBF extension (0..500) */
0x00, 250, /* T uplink TBF extension (0..500) */
0x00, 0x96, /* T downlink TBF extension (0..500) */
0x00, 0x32, /* T uplink TBF extension (0..500) */
2, /* CS2 */
#if 0
/* EDGE model only, breaks older models.
* Should inquire the BTS capabilities */
NM_ATT_IPACC_RLC_CFG_3, 0, 1,
2, /* MCS2 */
#endif
};
static unsigned char nanobts_attr_nsvc0[] = {
@@ -401,7 +400,8 @@ static unsigned char nanobts_attr_nsvc0[] = {
/* Callback function to be called whenever we get a GSM 12.21 state change event */
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
struct abis_om_obj_inst *obj_inst)
{
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
@@ -470,8 +470,6 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
sizeof(nanobts_attr_nse));
abis_nm_opstart(bts, obj_class, bts->bts_nr,
0xff, 0xff);
abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
0xff, 0xff, NM_STATE_UNLOCKED);
}
break;
case NM_OC_GPRS_CELL:
@@ -486,6 +484,8 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
0, 0xff);
abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
0, 0xff, NM_STATE_UNLOCKED);
abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr,
0xff, 0xff, NM_STATE_UNLOCKED);
}
break;
case NM_OC_GPRS_NSVC:
@@ -493,7 +493,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
bts = nsvc->bts;
if (bts->gprs.mode == BTS_GPRS_NONE)
break;
/* We skip NSVC1 since we only use NSVC0 */
/* We skip NSVC1 since we only use NSVC0 */
if (nsvc->id == 1)
break;
if (new_state->availability == NM_AVSTATE_OFF_LINE) {
@@ -564,10 +564,19 @@ static int sw_activ_rep(struct msgb *mb)
/* Callback function for NACK on the OML NM */
static int oml_msg_nack(u_int8_t mt)
{
int i;
if (mt == NM_MT_SET_BTS_ATTR_NACK) {
LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. "
"Was the bts type and frequency properly specified?\n");
exit(-1);
} else {
LOGP(DNM, LOGL_ERROR, "Got a NACK going to drop the OML links.\n");
for (i = 0; i < bsc_gsmnet->num_bts; ++i) {
struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, i);
if (is_ipaccess_bts(bts))
ipaccess_drop_oml(bts);
}
}
return 0;
@@ -831,6 +840,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
@@ -883,18 +902,22 @@ static void patch_nm_tables(struct gsm_bts *bts)
/* patch NSEI */
nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8;
nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff;
memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer,
ARRAY_SIZE(bts->gprs.nse.timer));
memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer,
ARRAY_SIZE(bts->gprs.cell.timer));
/* patch NSVCI */
nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8;
nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff;
/* patch IP address as SGSN IP */
*(u_int16_t *)(nanobts_attr_nsvc0+8) =
htons(bts->gprs.nsvc[0].remote_port);
*(u_int32_t *)(nanobts_attr_nsvc0+10) =
htonl(bts->gprs.nsvc[0].remote_ip);
*(u_int16_t *)(nanobts_attr_nsvc0+14) =
htons(bts->gprs.nsvc[0].local_port);
patch_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;
@@ -966,6 +989,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;
@@ -974,6 +999,8 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
static int bootstrap_bts(struct gsm_bts *bts)
{
int i, n;
switch (bts->band) {
case GSM_BAND_1800:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
@@ -1010,13 +1037,43 @@ static int bootstrap_bts(struct gsm_bts *bts)
/* Control Channel Description */
bts->si_common.chan_desc.att = 1;
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
/* T3212 is set from vty/config */
/* Set ccch config by looking at ts config */
for (n=0, i=0; i<8; i++)
n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0;
switch (n) {
case 0:
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
break;
case 1:
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC;
break;
case 2:
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC;
break;
case 3:
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC;
break;
case 4:
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC;
break;
default:
LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n");
return -EINVAL;
}
/* 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

@@ -22,6 +22,7 @@
#include <openbsc/bsc_msc.h>
#include <openbsc/debug.h>
#include <openbsc/ipaccess.h>
#include <osmocore/write_queue.h>
#include <osmocore/talloc.h>
@@ -49,18 +50,12 @@ static void connection_loss(struct bsc_msc_connection *con)
con->connection_loss(con);
}
static int bsc_msc_except(struct bsc_fd *bfd)
static void msc_con_timeout(void *_con)
{
struct write_queue *wrt;
struct bsc_msc_connection *con;
struct bsc_msc_connection *con = _con;
LOGP(DMSC, LOGL_ERROR, "Exception on the BFD. Closing down.\n");
wrt = container_of(bfd, struct write_queue, bfd);
con = container_of(wrt, struct bsc_msc_connection, write_queue);
connection_loss(con);
return 0;
LOGP(DMSC, LOGL_ERROR, "MSC Connection timeout.\n");
bsc_msc_lost(con);
}
/* called in the case of a non blocking connect */
@@ -74,13 +69,16 @@ static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
socklen_t len = sizeof(val);
if ((what & BSC_FD_WRITE) == 0) {
LOGP(DMSC, LOGL_ERROR, "Callback but not readable.\n");
LOGP(DMSC, LOGL_ERROR, "Callback but not writable.\n");
return -1;
}
queue = container_of(fd, struct write_queue, bfd);
con = container_of(queue, struct bsc_msc_connection, write_queue);
/* From here on we will either be connected or reconnect */
bsc_del_timer(&con->timeout_timer);
/* check the socket state */
rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
if (rc != 0) {
@@ -153,6 +151,13 @@ int bsc_msc_connect(struct bsc_msc_connection *con)
/* make it non blocking */
setnonblocking(fd);
/* set the socket priority */
ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS,
&con->prio, sizeof(con->prio));
if (ret != 0)
LOGP(DMSC, LOGL_ERROR, "Failed to set prio to %d. %s\n",
con->prio, strerror(errno));
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(con->port);
@@ -165,6 +170,9 @@ int bsc_msc_connect(struct bsc_msc_connection *con)
LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n");
fd->when = BSC_FD_WRITE;
fd->cb = msc_connection_connect;
con->timeout_timer.cb = msc_con_timeout;
con->timeout_timer.data = con;
bsc_schedule_timer(&con->timeout_timer, 20, 0);
} else if (ret < 0) {
perror("Connection failed");
connection_loss(con);
@@ -188,7 +196,7 @@ int bsc_msc_connect(struct bsc_msc_connection *con)
}
struct bsc_msc_connection *bsc_msc_create(const char *ip, int port)
struct bsc_msc_connection *bsc_msc_create(const char *ip, int port, int prio)
{
struct bsc_msc_connection *con;
@@ -200,15 +208,18 @@ struct bsc_msc_connection *bsc_msc_create(const char *ip, int port)
con->ip = ip;
con->port = port;
con->prio = prio;
write_queue_init(&con->write_queue, 100);
con->write_queue.except_cb = bsc_msc_except;
return con;
}
void bsc_msc_lost(struct bsc_msc_connection *con)
{
write_queue_clear(&con->write_queue);
bsc_unregister_fd(&con->write_queue.bfd);
bsc_del_timer(&con->timeout_timer);
if (con->write_queue.bfd.fd >= 0)
bsc_unregister_fd(&con->write_queue.bfd);
connection_loss(con);
}
@@ -227,3 +238,24 @@ void bsc_msc_schedule_connect(struct bsc_msc_connection *con)
con->reconnect_timer.data = con;
bsc_schedule_timer(&con->reconnect_timer, 5, 0);
}
struct msgb *bsc_msc_id_get_resp(const char *token)
{
struct msgb *msg;
if (!token) {
LOGP(DMSC, LOGL_ERROR, "No token specified.\n");
return NULL;
}
msg = msgb_alloc_headroom(4096, 128, "id resp");
if (!msg) {
LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n");
return NULL;
}
msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP);
msgb_l16tv_put(msg, strlen(token) + 1,
IPAC_IDTAG_UNITNAME, (u_int8_t *) token);
return msg;
}

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
@@ -30,6 +30,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#define _GNU_SOURCE
@@ -44,13 +45,15 @@
#include <openbsc/chan_alloc.h>
#include <openbsc/bsc_msc.h>
#include <openbsc/bsc_nat.h>
#include <openbsc/bsc_msc_rf.h>
#include <openbsc/osmo_bsc_rf.h>
#include <openbsc/osmo_bsc_grace.h>
#include <osmocore/select.h>
#include <osmocore/talloc.h>
#include <osmocore/write_queue.h>
#include <osmocore/gsm0808.h>
#include <sccp/sccp.h>
#include <osmocom/sccp/sccp.h>
/* SCCP helper */
#define SCCP_IT_TIMER 60
@@ -64,22 +67,34 @@ static struct in_addr local_addr;
static LLIST_HEAD(active_connections);
static struct write_queue mgcp_agent;
static const char *rf_ctl = NULL;
static int testmode = 0;
extern int ipacc_rtp_direct;
/* msc handling */
static struct bsc_msc_connection *msc_con;
static struct timer_list msc_ping_timeout;
static struct timer_list msc_pong_timeout;
extern int bsc_bootstrap_network(int (*layer4)(struct gsm_network *, int, void *), const char *cfg_file);
extern int bsc_shutdown_net(struct gsm_network *net);
struct llist_head *bsc_sccp_connections()
{
return &active_connections;
}
/*
* Having a subscriber in the lchan is used to indicate that a SACH DEACTIVATE
* should be send. We will just introduce a fake subscriber and bind it to the
* lchan.
*/
static void assign_dummy_subscr(struct gsm_lchan *lchan)
{
if (!lchan->conn.subscr) {
lchan->conn.subscr = subscr_get_or_create(bsc_gsmnet, "2323");
lchan->conn.subscr->lac = 2323;
}
}
struct bss_sccp_connection_data *bss_sccp_create_data()
{
struct bss_sccp_connection_data *data;
@@ -117,19 +132,27 @@ static void sccp_it_fired(void *_data)
bsc_schedule_timer(&data->sccp_it, SCCP_IT_TIMER, 0);
}
static void bss_force_close(struct bss_sccp_connection_data *bss)
/* make sure to stop the T10 timer... bss_sccp_free_data is doing that */
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;
}
}
static void bss_force_close(struct bss_sccp_connection_data *bss)
{
bss_close_lchans(bss);
/* force the close by poking stuff */
if (bss->sccp) {
@@ -221,23 +244,21 @@ void msc_outgoing_sccp_data(struct sccp_connection *conn, struct msgb *msg, unsi
void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
{
struct bss_sccp_connection_data *con_data;
if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
con_data = (struct bss_sccp_connection_data *) conn->data_ctx;
LOGP(DMSC, LOGL_DEBUG, "Freeing sccp conn: %p state: %d\n", conn, conn->connection_state);
if (sccp_get_lchan(conn->data_ctx) != NULL) {
struct gsm_lchan *lchan = sccp_get_lchan(conn->data_ctx);
if (con_data->lchan || con_data->secondary_lchan) {
LOGP(DMSC, LOGL_ERROR, "ERROR: The lchan is still associated\n.");
lchan->msc_data = NULL;
put_subscr_con(&lchan->conn, 0);
bss_close_lchans(con_data);
}
bss_sccp_free_data((struct bss_sccp_connection_data *)conn->data_ctx);
bss_sccp_free_data(con_data);
sccp_connection_free(conn);
return;
} else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) {
struct bss_sccp_connection_data *con_data;
LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn);
con_data = (struct bss_sccp_connection_data *) conn->data_ctx;
@@ -269,10 +290,13 @@ static int open_sccp_connection(struct msgb *layer3)
struct msgb *data;
/* When not connected to a MSC. We will simply close things down. */
if (!msc_con->is_authenticated) {
if (!bsc_gsmnet->msc_con->is_authenticated) {
LOGP(DMSC, LOGL_ERROR, "Not connected to a MSC. Not forwarding data.\n");
use_subscr_con(&layer3->lchan->conn);
put_subscr_con(&layer3->lchan->conn, 0);
return -1;
}
if (!bsc_grace_allow_new_connection(bsc_gsmnet)) {
LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
return -1;
}
@@ -302,6 +326,9 @@ static int open_sccp_connection(struct msgb *layer3)
con_data->lchan = layer3->lchan;
con_data->sccp = sccp_connection;
/* assign the outgoing msc connection */
con_data->msc_con = bsc_gsmnet->msc_con;
sccp_connection->state_cb = msc_outgoing_sccp_state;
sccp_connection->data_cb = msc_outgoing_sccp_data;
sccp_connection->data_ctx = con_data;
@@ -313,6 +340,8 @@ static int open_sccp_connection(struct msgb *layer3)
bsc_schedule_timer(&con_data->sccp_cc_timeout, 10, 0);
/* FIXME: Use transaction for this */
/* assign a dummy subscriber */
assign_dummy_subscr(layer3->lchan);
use_subscr_con(&layer3->lchan->conn);
sccp_connection_connect(sccp_connection, &sccp_ssn_bssap, data);
msgb_free(data);
@@ -344,7 +373,14 @@ static int handle_paging_response(struct msgb *msg)
char mi_string[GSM48_MI_SIZE];
u_int8_t mi_type;
gsm48_paging_extract_mi(msg, mi_string, &mi_type);
struct gsm48_hdr *hdr;
struct gsm48_pag_resp *resp;
hdr = msgb_l3(msg);
resp = (struct gsm48_pag_resp *) &hdr->data[0];
gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*hdr),
mi_string, &mi_type);
LOGP(DMSC, LOGL_DEBUG, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n",
mi_type, mi_string);
@@ -374,7 +410,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;
@@ -397,12 +433,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;
}
@@ -410,21 +448,28 @@ 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;
}
/* assign a dummy subscriber */
assign_dummy_subscr(msg->lchan);
/* swap the channels and release the old */
old_chan = msg->lchan->msc_data->lchan;
msg->lchan->msc_data->lchan = msg->lchan;
msg->lchan->msc_data->secondary_lchan = NULL;
old_chan->msc_data = NULL;
if (old_chan) {
msg->lchan->msc_data->lchan = msg->lchan;
msg->lchan->msc_data->secondary_lchan = NULL;
old_chan->msc_data = NULL;
/* give up the old channel to not do a SACCH deactivate */
if (old_chan->conn.subscr)
subscr_put(old_chan->conn.subscr);
old_chan->conn.subscr = NULL;
put_subscr_con(&old_chan->conn, 1);
/* give up the old channel to not do a SACCH deactivate */
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);
}
/* activate audio on it... */
if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && msg->lchan->tch_mode != GSM48_CMODE_SIGN)
@@ -446,6 +491,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;
}
@@ -454,6 +500,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;
}
@@ -497,6 +544,38 @@ static int handle_modify_ack(struct msgb *msg)
return 1;
}
/*
* Check if the subscriber is coming from our LAC
*/
static void handle_lu(struct msgb *msg)
{
struct gsm48_hdr *gh;
struct gsm48_loc_upd_req *lu;
struct gsm48_loc_area_id lai;
struct gsm_network *net;
if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) {
LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg));
return;
}
if (!msg->lchan->msc_data)
return;
net = msg->trx->bts->network;
gh = msgb_l3(msg);
lu = (struct gsm48_loc_upd_req *) gh->data;
gsm48_generate_lai(&lai, net->country_code, net->network_code,
msg->trx->bts->location_area_code);
if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) {
LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n");
msg->lchan->msc_data->new_subscriber = 1;
}
}
/* Receive a GSM 04.08 Radio Resource (RR) message */
static int gsm0408_rcv_rr(struct msgb *msg)
{
@@ -542,6 +621,8 @@ static int gsm0408_rcv_mm(struct msgb *msg)
case GSM48_MT_MM_CM_SERV_REQ:
case GSM48_MT_MM_IMSI_DETACH_IND:
rc = send_dtap_or_open_connection(msg);
if ((gh->msg_type & 0xbf) == GSM48_MT_MM_LOC_UPD_REQUEST)
handle_lu(msg);
break;
default:
break;
@@ -579,6 +660,13 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
}
bsc_queue_connection_write(lchan_get_sccp(msg->lchan), dtap);
} 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);
}
}
return rc;
@@ -635,7 +723,7 @@ static void print_usage()
static int msc_queue_write(struct msgb *msg, int proto)
{
ipaccess_prepend_header(msg, proto);
if (write_queue_enqueue(&msc_con->write_queue, msg) != 0) {
if (write_queue_enqueue(&bsc_gsmnet->msc_con->write_queue, msg) != 0) {
LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto);
msgb_free(msg);
return -1;
@@ -651,14 +739,14 @@ static int msc_sccp_do_write(struct bsc_fd *fd, struct msgb *msg)
LOGP(DMSC, LOGL_DEBUG, "Sending SCCP to MSC: %u\n", msgb_l2len(msg));
LOGP(DMI, LOGL_DEBUG, "MSC TX %s\n", hexdump(msg->l2h, msgb_l2len(msg)));
ret = write(msc_con->write_queue.bfd.fd, msg->data, msg->len);
ret = write(bsc_gsmnet->msc_con->write_queue.bfd.fd, msg->data, msg->len);
if (ret < msg->len)
perror("MSC: Failed to send SCCP");
return ret;
}
static void msc_sccp_write_ipa(struct msgb *msg, void *data)
static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, void *data)
{
msc_queue_write(msg, IPAC_PROTO_SCCP);
}
@@ -823,9 +911,9 @@ static void initialize_if_needed(void)
struct msgb *msg;
if (!msc_con->is_authenticated) {
if (!bsc_gsmnet->msc_con->is_authenticated) {
/* send a gsm 08.08 reset message from here */
msg = bssmap_create_reset();
msg = gsm0808_create_reset();
if (!msg) {
LOGP(DMSC, LOGL_ERROR, "Failed to create the reset message.\n");
return;
@@ -833,7 +921,7 @@ static void initialize_if_needed(void)
sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0);
msgb_free(msg);
msc_con->is_authenticated = 1;
bsc_gsmnet->msc_con->is_authenticated = 1;
}
}
@@ -845,19 +933,38 @@ static void send_id_get_response(int fd)
return;
}
if (!bsc_gsmnet->bsc_token) {
LOGP(DMSC, LOGL_ERROR, "The bsc token is not set.\n");
msg = bsc_msc_id_get_resp(bsc_gsmnet->bsc_token);
if (!msg)
return;
}
msg = msgb_alloc_headroom(4096, 128, "id resp");
msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP);
msgb_l16tv_put(msg, strlen(bsc_gsmnet->bsc_token) + 1,
IPAC_IDTAG_UNITNAME, (u_int8_t *) bsc_gsmnet->bsc_token);
msc_queue_write(msg, IPAC_PROTO_IPACCESS);
}
/*
* Send some packets to test the MSC.
*/
static void test_msc()
{
struct msgb *msg;
if (!testmode)
return;
static const uint8_t pag_resp[] = {
0x01, 0xf3, 0x26, 0x09, 0x02, 0x02, 0x04, 0x02, 0x42,
0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05, 0x08, 0x00,
0x72, 0xf4, 0x80, 0x10, 0x1c, 0x9c, 0x40, 0x17, 0x10,
0x06, 0x27, 0x02, 0x03, 0x30, 0x18, 0xa0, 0x08, 0x59,
0x51, 0x30, 0x10, 0x30, 0x32, 0x80, 0x06, 0x00
};
msg = msgb_alloc_headroom(4096, 128, "paging response");
if (!msg)
return;
msg->l2h = msgb_put(msg, sizeof(pag_resp));
memcpy(msg->l2h, pag_resp, sizeof(pag_resp));
msc_queue_write(msg, IPAC_PROTO_SCCP);
}
/*
* The connection to the MSC was lost and we will need to free all
* resources and then attempt to reconnect.
@@ -882,7 +989,7 @@ static void msc_connection_was_lost(struct bsc_msc_connection *msc)
static void msc_pong_timeout_cb(void *data)
{
LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n");
bsc_msc_lost(msc_con);
bsc_msc_lost(bsc_gsmnet->msc_con);
}
static void send_ping(void)
@@ -903,17 +1010,26 @@ static void send_ping(void)
static void msc_ping_timeout_cb(void *data)
{
if (bsc_gsmnet->ping_timeout < 0)
return;
send_ping();
/* send another ping in 20 seconds */
bsc_schedule_timer(&msc_ping_timeout, 20, 0);
bsc_schedule_timer(&msc_ping_timeout, bsc_gsmnet->ping_timeout, 0);
/* also start a pong timer */
bsc_schedule_timer(&msc_pong_timeout, 5, 0);
bsc_schedule_timer(&msc_pong_timeout, bsc_gsmnet->pong_timeout, 0);
}
static void msc_connection_connected(struct bsc_msc_connection *con)
{
int ret, on;
on = 1;
ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
if (ret != 0)
LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
msc_ping_timeout_cb(con);
}
@@ -929,7 +1045,7 @@ static int ipaccess_a_fd_cb(struct bsc_fd *bfd)
if (!msg) {
if (error == 0) {
LOGP(DMSC, LOGL_ERROR, "The connection to the MSC was lost.\n");
bsc_msc_lost(msc_con);
bsc_msc_lost(bsc_gsmnet->msc_con);
return -1;
}
@@ -949,6 +1065,7 @@ static int ipaccess_a_fd_cb(struct bsc_fd *bfd)
initialize_if_needed();
else if (msg->l2h[0] == IPAC_MSGT_ID_GET) {
send_id_get_response(bfd->fd);
test_msc();
} else if (msg->l2h[0] == IPAC_MSGT_PONG) {
bsc_del_timer(&msc_pong_timeout);
}
@@ -974,6 +1091,7 @@ static void print_help()
printf(" -l --local=IP. The local address of the MGCP.\n");
printf(" -e --log-level number. Set a global loglevel.\n");
printf(" -r --rf-ctl NAME. A unix domain socket to listen for cmds.\n");
printf(" -t --testmode. A special mode to provoke failures at the MSC.\n");
}
static void handle_options(int argc, char** argv)
@@ -990,10 +1108,11 @@ static void handle_options(int argc, char** argv)
{"local", 1, 0, 'l'},
{"log-level", 1, 0, 'e'},
{"rf-ctl", 1, 0, 'r'},
{"testmode", 0, 0, 't'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hd:sTc:m:l:e:r:",
c = getopt_long(argc, argv, "hd:sTc:m:l:e:r:t",
long_options, &option_index);
if (c == -1)
break;
@@ -1030,6 +1149,9 @@ static void handle_options(int argc, char** argv)
case 'r':
rf_ctl = optarg;
break;
case 't':
testmode = 1;
break;
default:
/* ignore */
break;
@@ -1056,9 +1178,9 @@ static void signal_handler(int signal)
talloc_report_full(tall_bsc_ctx, stderr);
break;
case SIGUSR2:
if (!msc_con || !msc_con->is_connected)
if (!bsc_gsmnet->msc_con)
return;
bsc_msc_lost(msc_con);
bsc_msc_lost(bsc_gsmnet->msc_con);
break;
default:
break;
@@ -1145,6 +1267,7 @@ int main(int argc, char **argv)
}
/* initialize sccp */
sccp_set_log_area(DSCCP);
sccp_system_init(msc_sccp_write_ipa, NULL);
sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL);
sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, NULL);
@@ -1160,9 +1283,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 = osmo_bsc_rf_create(rf_ctl, bsc_gsmnet);
if (!bsc_gsmnet->rf) {
fprintf(stderr, "Failed to create the RF service.\n");
exit(1);
}
@@ -1173,8 +1295,10 @@ int main(int argc, char **argv)
if (msc_address)
msc = msc_address;
msc_con = bsc_msc_create(msc, bsc_gsmnet->msc_port);
if (!msc_con) {
bsc_gsmnet->msc_con = bsc_msc_create(msc,
bsc_gsmnet->msc_port,
bsc_gsmnet->msc_ip_dscp);
if (!bsc_gsmnet->msc_con) {
fprintf(stderr, "Creating a bsc_msc_connection failed.\n");
exit(1);
}
@@ -1182,11 +1306,11 @@ int main(int argc, char **argv)
msc_ping_timeout.cb = msc_ping_timeout_cb;
msc_pong_timeout.cb = msc_pong_timeout_cb;
msc_con->connection_loss = msc_connection_was_lost;
msc_con->connected = msc_connection_connected;
msc_con->write_queue.read_cb = ipaccess_a_fd_cb;
msc_con->write_queue.write_cb = msc_sccp_do_write;
bsc_msc_connect(msc_con);
bsc_gsmnet->msc_con->connection_loss = msc_connection_was_lost;
bsc_gsmnet->msc_con->connected = msc_connection_connected;
bsc_gsmnet->msc_con->write_queue.read_cb = ipaccess_a_fd_cb;
bsc_gsmnet->msc_con->write_queue.write_cb = msc_sccp_do_write;
bsc_msc_connect(bsc_gsmnet->msc_con);
@@ -1194,3 +1318,4 @@ int main(int argc, char **argv)
bsc_select_main(0);
}
}

View File

@@ -1,6 +1,6 @@
/* GSM 08.08 BSSMAP handling */
/* (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
@@ -28,10 +28,11 @@
#include <openbsc/signal.h>
#include <openbsc/paging.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/gsm_04_80.h>
#include <osmocore/gsm0808.h>
#include <sccp/sccp.h>
#include <osmocom/sccp/sccp.h>
#include <arpa/inet.h>
#include <assert.h>
@@ -45,6 +46,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)
{
@@ -66,6 +74,19 @@ static int bssmap_paging_cb(unsigned int hooknum, unsigned int event, struct msg
return 0;
}
/* Send a USSD message */
static void send_welcome_ussd(struct gsm_lchan *lchan)
{
struct gsm_network *network;
network = lchan->ts->trx->bts->network;
if (!network->ussd_welcome_txt)
return;
gsm0480_send_ussdNotify(lchan, 1, network->ussd_welcome_txt);
gsm0480_send_releaseComplete(lchan);
}
static int bssmap_handle_reset_ack(struct gsm_network *net, struct msgb *msg, unsigned int length)
{
LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n");
@@ -124,8 +145,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,11 +190,12 @@ 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);
}
/* send the clear complete message */
resp = bssmap_create_clear_complete();
resp = gsm0808_create_clear_complete();
if (!resp) {
LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n");
return -1;
@@ -258,7 +279,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 +315,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);
}
@@ -312,7 +334,7 @@ static void bssmap_t10_fired(void *_conn)
msc_data = conn->data_ctx;
bssmap_free_secondary(msc_data);
resp = bssmap_create_assignment_failure(
resp = gsm0808_create_assignment_failure(
GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
if (!resp) {
LOGP(DMSC, LOGL_ERROR, "Allocation failure: %p\n", conn);
@@ -451,6 +473,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 +481,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 +636,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;
}
@@ -652,6 +677,7 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length)
struct msgb *gsm48;
u_int8_t *data;
u_int8_t link_id;
int is_lu = 0;
if (!lchan) {
LOGP(DMSC, LOGL_ERROR, "No lchan available\n");
@@ -701,6 +727,7 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length)
gsm48_generate_lai(lai, net->country_code,
net->network_code,
gsm48->trx->bts->location_area_code);
is_lu = 1;
}
}
}
@@ -713,168 +740,23 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length)
link_id |= 0x40;
bts_queue_send(gsm48, link_id);
/* Feature to send a welcome USSD for a new subscriber */
if (is_lu && lchan->msc_data->new_subscriber)
send_welcome_ussd(lchan);
return 0;
}
/* 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_reset(void)
{
struct msgb *msg = msgb_alloc(30, "bssmap: reset");
if (!msg)
return NULL;
msg->l3h = msgb_put(msg, 6);
msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[1] = 0x04;
msg->l3h[2] = 0x30;
msg->l3h[3] = 0x04;
msg->l3h[4] = 0x01;
msg->l3h[5] = 0x20;
return msg;
}
struct msgb *bssmap_create_clear_complete(void)
{
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] = 1;
msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE;
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)
@@ -902,7 +784,9 @@ static u_int8_t chan_mode_to_speech(struct gsm_lchan *lchan)
break;
}
if (lchan->type == GSM_LCHAN_TCH_H)
/* assume to always do AMR HR on any TCH type */
if (lchan->type == GSM_LCHAN_TCH_H ||
lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
mode |= 0x4;
return mode;
@@ -959,80 +843,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;
}
struct msgb *bssmap_create_assignment_failure(u_int8_t cause, u_int8_t *rr_cause)
{
u_int8_t *data;
struct msgb *msg = msgb_alloc(35, "bssmap: ass fail");
if (!msg)
return NULL;
msg->l3h = msgb_put(msg, 6);
msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[1] = 0xff;
msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE;
msg->l3h[3] = GSM0808_IE_CAUSE;
msg->l3h[4] = 1;
msg->l3h[5] = cause;
/* RR cause 3.2.2.22 */
if (rr_cause) {
data = msgb_put(msg, 2);
data[0] = GSM0808_IE_RR_CAUSE;
data[1] = *rr_cause;
}
/* Circuit pool 3.22.45 */
/* Circuit pool list 3.2.2.46 */
/* 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)
@@ -1113,6 +927,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;
}
@@ -1209,7 +1037,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;
@@ -1312,7 +1140,7 @@ void gsm0808_send_assignment_failure(struct gsm_lchan *lchan, u_int8_t cause, u_
bsc_del_timer(&lchan->msc_data->T10);
bssmap_free_secondary(lchan->msc_data);
resp = bssmap_create_assignment_failure(cause, rr_value);
resp = gsm0808_create_assignment_failure(cause, rr_value);
if (!resp) {
LOGP(DMSC, LOGL_ERROR, "Allocation failure: %p\n", lchan_get_sccp(lchan));
return;

View File

@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <openbsc/gsm_data.h>
#include <openbsc/chan_alloc.h>
@@ -33,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 ? */
@@ -287,6 +290,9 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
memset(&lchan->conn, 0, sizeof(lchan->conn));
lchan->conn.lchan = lchan;
lchan->conn.bts = lchan->ts->trx->bts;
/* set the alloc time */
gettimeofday(&lchan->alloc_time, NULL);
} else {
struct challoc_signal_data sig;
sig.bts = bts;
@@ -326,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;
@@ -381,6 +393,11 @@ static void _lchan_handle_release(struct gsm_lchan *lchan)
++lchan->conn.use_count;
gsm48_send_rr_release(lchan);
--lchan->conn.use_count;
/* avoid reentrancy */
subscr_put(lchan->conn.subscr);
lchan->conn.subscr = NULL;
return;
}
/* spoofed? message */
@@ -417,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

@@ -52,6 +52,7 @@
#include <openbsc/transaction.h>
#include <openbsc/ussd.h>
#include <openbsc/silent_call.h>
#include <osmocore/gsm0480.h>
void *tall_locop_ctx;
@@ -175,24 +176,24 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
{
struct gsm_subscriber_connection *conn;
struct gsm_bts *bts = lchan->ts->trx->bts;
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
struct msgb *msg;
counter_inc(bts->network->stats.loc_upd_resp.reject);
msg = gsm48_create_loc_upd_rej(cause);
if (!msg) {
LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n");
return -1;
}
msg->lchan = lchan;
conn = &lchan->conn;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
gh->data[0] = cause;
LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
"LAC=%u BTS=%u\n", conn->subscr ?
subscr_name(conn->subscr) : "unknown",
lchan->ts->trx->bts->location_area_code, lchan->ts->trx->bts->nr);
counter_inc(bts->network->stats.loc_upd_resp.reject);
return gsm48_sendmsg(msg, NULL);
}
@@ -574,19 +575,17 @@ static int gsm48_tx_mm_serv_ack(struct gsm_lchan *lchan)
static int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
enum gsm48_reject_value value)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
struct msgb *msg;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
msg = gsm48_create_mm_serv_rej(value);
if (!msg) {
LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n");
return -1;
}
DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value);
msg->lchan = conn->lchan;
use_subscr_con(conn);
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_CM_SERV_REJ;
gh->data[0] = value;
DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value);
return gsm48_sendmsg(msg, NULL);
}
@@ -609,7 +608,7 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_service_request *req =
(struct gsm48_service_request *)gh->data;
/* unfortunately in Phase1 the classmar2 length is variable */
/* unfortunately in Phase1 the classmark2 length is variable */
u_int8_t classmark2_len = gh->data[1];
u_int8_t *classmark2 = gh->data+2;
u_int8_t mi_len = *(classmark2 + classmark2_len);
@@ -779,13 +778,16 @@ static int gsm48_rx_rr_pag_resp(struct msgb *msg)
{
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_pag_resp *resp;
u_int8_t *classmark2_lv = gh->data + 1;
u_int8_t mi_type;
char mi_string[GSM48_MI_SIZE];
struct gsm_subscriber *subscr = NULL;
int rc = 0;
gsm48_paging_extract_mi(msg, mi_string, &mi_type);
resp = (struct gsm48_pag_resp *) &gh->data[0];
gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh),
mi_string, &mi_type);
DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n",
mi_type, mi_string);

View File

@@ -285,16 +285,30 @@ int send_siemens_mrpci(struct gsm_lchan *lchan,
return rsl_siemens_mrpci(lchan, &mrpci);
}
int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type)
int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type)
{
struct gsm48_hdr *gh = msgb_l3(msg);
u_int8_t *classmark2_lv = gh->data + 1;
u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv;
*mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
/* Check the size for the classmark */
if (length < 1 + *classmark2_lv)
return -1;
u_int8_t *mi_lv = classmark2_lv + *classmark2_lv + 1;
if (length < 2 + *classmark2_lv + mi_lv[0])
return -2;
*mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv);
}
int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length,
char *mi_string, u_int8_t *mi_type)
{
static const uint32_t classmark_offset =
offsetof(struct gsm48_pag_resp, classmark2);
u_int8_t *classmark2_lv = (uint8_t *) &resp->classmark2;
return gsm48_extract_mi(classmark2_lv, length - classmark_offset,
mi_string, mi_type);
}
int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
{
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
@@ -321,7 +335,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
sig_data.bts = msg->lchan->ts->trx->bts;
sig_data.lchan = msg->lchan;
bts->network->stats.paging.completed++;
counter_inc(bts->network->stats.paging.completed);
dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data);
@@ -615,3 +629,36 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
return 0;
}
struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value)
{
struct msgb *msg;
struct gsm48_hdr *gh;
msg = gsm48_msgb_alloc();
if (!msg)
return NULL;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_CM_SERV_REJ;
gh->data[0] = value;
return msg;
}
struct msgb *gsm48_create_loc_upd_rej(uint8_t cause)
{
struct gsm48_hdr *gh;
struct msgb *msg;
msg = gsm48_msgb_alloc();
if (!msg)
return NULL;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
gh->data[0] = cause;
return msg;
}

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
@@ -36,6 +36,7 @@
#include <osmocore/gsm_utils.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/gsm_04_80.h>
#include <osmocore/gsm0480.h>
/* Forward declarations */
static int parse_ussd(u_int8_t *ussd, struct ussd_request *req);
@@ -50,22 +51,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;
}
@@ -298,7 +299,7 @@ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_t
}
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 +327,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

@@ -172,6 +172,10 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
return trx;
}
static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 3, 3, 10 };
static const uint8_t bts_cell_timer_default[] =
{ 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
u_int8_t tsc, u_int8_t bsic)
{
@@ -213,6 +217,10 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
bts->gprs.nsvc[i].bts = bts;
bts->gprs.nsvc[i].id = i;
}
memcpy(&bts->gprs.nse.timer, bts_nse_timer_default,
sizeof(bts->gprs.nse.timer));
memcpy(&bts->gprs.cell.timer, bts_cell_timer_default,
sizeof(bts->gprs.cell.timer));
/* create our primary TRX */
bts->c0 = gsm_bts_trx_alloc(bts);
@@ -226,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;
@@ -300,6 +310,8 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
net->msc_ip = talloc_strdup(net, "127.0.0.1");
net->msc_port = 5000;
net->ping_timeout = 20;
net->pong_timeout = 5;
return net;
}
@@ -454,33 +466,6 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
return get_value_string(auth_policy_names, policy);
}
/* this should not be here but in gsm_04_08... but that creates
in turn a dependency nightmare (abis_nm depending on 04_08, ...) */
static int gsm48_construct_ra(u_int8_t *buf, const struct gprs_ra_id *raid)
{
u_int16_t mcc = raid->mcc;
u_int16_t mnc = raid->mnc;
buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4);
buf[1] = (mcc % 10);
/* I wonder who came up with the stupidity of encoding the MNC
* differently depending on how many digits its decimal number has! */
if (mnc < 100) {
buf[1] |= 0xf0;
buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4);
} else {
buf[1] |= (mnc % 10) << 4;
buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4);
}
*(u_int16_t *)(buf+3) = htons(raid->lac);
buf[5] = raid->rac;
return 6;
}
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts)
{
raid->mcc = bts->network->country_code;

View File

@@ -265,7 +265,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
trx->rsl_link = e1inp_sign_link_create(e1i_ts,
E1INP_SIGN_RSL, trx,
trx->rsl_tei, 0);
trx->rsl_link->ts->sign.delay = 10;
trx->rsl_link->ts->sign.delay = 0;
/* get rid of our old temporary bfd */
memcpy(newbfd, bfd, sizeof(*newbfd));
@@ -607,7 +607,7 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
struct e1inp_driver ipaccess_driver = {
.name = "ip.access",
.want_write = ts_want_write,
.default_delay = 100000,
.default_delay = 0,
};
/* callback of the OML listening filedescriptor */

View File

@@ -1,8 +1,8 @@
/* ip.access nanoBTS configuration tool */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Holger Hans Peter Freyther
* (C) 2009 by On Waves
* (C) 2009,2010 by Holger Hans Peter Freyther
* (C) 2009,2010 by On Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -59,7 +59,7 @@ static int sw_load_state = 0;
static int oml_state = 0;
static int dump_files = 0;
static char *firmware_analysis = NULL;
static int trx_nr = 0;
static int found_trx = 0;
struct sw_load {
u_int8_t file_id[255];
@@ -92,23 +92,23 @@ static int ipacc_msg_nack(u_int8_t mt)
return 0;
}
static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts *bts)
static void check_restart_or_exit(struct gsm_bts_trx *trx)
{
if (restart) {
abis_nm_ipaccess_restart(trx);
} else {
exit(0);
}
}
static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts_trx *trx)
{
if (sw_load_state == 1) {
fprintf(stderr, "The new software is activaed.\n");
if (restart) {
abis_nm_ipaccess_restart(bts);
} else {
exit(0);
}
check_restart_or_exit(trx);
} else if (oml_state == 1) {
fprintf(stderr, "Set the primary OML IP.\n");
if (restart) {
abis_nm_ipaccess_restart(bts);
} else {
exit(0);
}
check_restart_or_exit(trx);
}
return 0;
@@ -203,7 +203,7 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
return ipacc_msg_nack(ipacc_data->msg_type);
case S_NM_IPACC_ACK:
ipacc_data = signal_data;
return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->bts);
return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx);
case S_NM_TEST_REP:
return test_rep(signal_data);
case S_NM_IPACC_RESTART_ACK:
@@ -228,12 +228,12 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
void *data, void *param)
{
struct msgb *msg;
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
if (hook != GSM_HOOK_NM_SWLOAD)
return 0;
bts = (struct gsm_bts *) data;
trx = (struct gsm_bts_trx *) data;
switch (event) {
case NM_MT_LOAD_INIT_ACK:
@@ -272,7 +272,7 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
msg->l2h[1] = msgb_l3len(msg) >> 8;
msg->l2h[2] = msgb_l3len(msg) & 0xff;
printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg));
abis_nm_ipaccess_set_nvattr(bts->c0, msg->l2h, msgb_l2len(msg));
abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg));
msgb_free(msg);
break;
case NM_MT_LOAD_END_NACK:
@@ -286,7 +286,7 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
case NM_MT_ACTIVATE_SW_ACK:
break;
case NM_MT_LOAD_SEG_ACK:
percent = abis_nm_software_load_status(bts);
percent = abis_nm_software_load_status(trx->bts);
if (percent > percent_old)
printf("Software Download Progress: %d%%\n", percent);
percent_old = percent;
@@ -299,13 +299,13 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
return 0;
}
static void bootstrap_om(struct gsm_bts *bts)
static void bootstrap_om(struct gsm_bts_trx *trx)
{
int len;
static u_int8_t buf[1024];
u_int8_t *cur = buf;
printf("OML link established\n");
printf("OML link established using TRX %d\n", trx->nr);
if (unit_id) {
len = strlen(unit_id);
@@ -317,8 +317,7 @@ static void bootstrap_om(struct gsm_bts *bts)
memcpy(buf+3, unit_id, len);
buf[3+len] = 0;
printf("setting Unit ID to '%s'\n", unit_id);
abis_nm_ipaccess_set_nvattr(gsm_bts_trx_by_nr(bts, trx_nr),
buf, 3+len+1);
abis_nm_ipaccess_set_nvattr(trx, buf, 3+len+1);
}
if (prim_oml_ip) {
struct in_addr ia;
@@ -342,7 +341,7 @@ static void bootstrap_om(struct gsm_bts *bts)
*cur++ = 0;
printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
oml_state = 1;
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
abis_nm_ipaccess_set_nvattr(trx, buf, 3+len);
}
if (nv_mask) {
len = 4;
@@ -356,13 +355,12 @@ static void bootstrap_om(struct gsm_bts *bts)
*cur++ = nv_mask >> 8;
printf("setting NV Flags/Mask to 0x%04x/0x%04x\n",
nv_flags, nv_mask);
abis_nm_ipaccess_set_nvattr(gsm_bts_trx_by_nr(bts, trx_nr),
buf, 3+len);
abis_nm_ipaccess_set_nvattr(trx, buf, 3+len);
}
if (restart && !prim_oml_ip && !software) {
printf("restarting BTS\n");
abis_nm_ipaccess_restart(bts);
abis_nm_ipaccess_restart(trx);
}
}
@@ -373,7 +371,6 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
case EVT_E1_TEI_UP:
switch (type) {
case E1INP_SIGN_OML:
bootstrap_om(trx->bts);
break;
case E1INP_SIGN_RSL:
/* FIXME */
@@ -392,22 +389,29 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
}
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
struct abis_om_obj_inst *obj_inst)
{
if (evt == EVT_STATECHG_OPER &&
if (obj_class == NM_OC_BASEB_TRANSC) {
if (!found_trx && obj_inst->trx_nr != 0xff) {
struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc);
bootstrap_om(trx);
found_trx = 1;
}
} else if (evt == EVT_STATECHG_OPER &&
obj_class == NM_OC_RADIO_CARRIER &&
new_state->availability == 3) {
struct gsm_bts_trx *trx = obj;
if (net_listen_testnr) {
u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 };
abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff,
abis_nm_perform_test(trx->bts, 2, 0, trx->nr, 0xff,
net_listen_testnr, 1,
phys_config, sizeof(phys_config));
} else if (software) {
int rc;
printf("Attempting software upload with '%s'\n", software);
rc = abis_nm_software_load(trx->bts, software, 19, 0, swload_cbfn, trx->bts);
rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx);
if (rc < 0) {
fprintf(stderr, "Failed to start software load\n");
exit(-3);
@@ -608,7 +612,6 @@ static void print_help(void)
printf(" -d --software firmware\n");
printf(" -f --firmware firmware Provide firmware information\n");
printf(" -w --write-firmware. This will dump the firmware parts to the filesystem. Use with -f.\n");
printf(" -t --trx NR. The TRX to use for the Unit ID and NVRAM attributes.\n");
}
int main(int argc, char **argv)
@@ -643,11 +646,10 @@ int main(int argc, char **argv)
{ "software", 1, 0, 'd' },
{ "firmware", 1, 0, 'f' },
{ "write-firmware", 0, 0, 'w' },
{ "trx", 1, 0, 't' },
{ 0, 0, 0, 0 },
};
c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:wt:", long_options,
c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:w", long_options,
&option_index);
if (c == -1)
@@ -689,9 +691,6 @@ int main(int argc, char **argv)
case 'w':
dump_files = 1;
break;
case 't':
trx_nr = atoi(optarg);
break;
case 'h':
print_usage();
print_help();

View File

@@ -128,7 +128,7 @@ static int mgcp_rsip_cb(struct mgcp_config *cfg)
return 0;
}
static int mgcp_change_cb(struct mgcp_config *cfg, int endpoint, int state, int local_rtp)
static int mgcp_change_cb(struct mgcp_config *cfg, int endpoint, int state)
{
if (state != MGCP_ENDP_MDCX)
return 0;

View File

@@ -23,6 +23,7 @@
*/
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <endian.h>
#include <errno.h>
@@ -89,48 +90,124 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp)
{
static char buf[] = { DUMMY_LOAD };
return udp_send(endp->local_rtp.fd, &endp->remote,
endp->net_rtp, buf, 1);
return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr,
endp->net_end.rtp_port, buf, 1);
}
static void patch_payload(int payload, char *data, int len)
static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
int payload, struct sockaddr_in *addr, 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 = endp->allow_patch;
LOGP(DMGCP, LOGL_NOTICE,
"The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n",
ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode);
}
/* 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;
}
/*
* There is data coming. We will have to figure out if it
* came from the BTS or the MediaGateway of the MSC. On top
* of that we need to figure out if it was RTP or RTCP.
*
* Currently we do not communicate with the BSC so we have
* no idea where the BTS is listening for RTP and need to
* do the classic routing trick. Wait for the first packet
* from the BTS and then go ahead.
* The below code is for dispatching. We have a dedicated port for
* the data coming from the net and one to discover the BTS.
*/
static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len)
{
char buf[4096];
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
struct mgcp_endpoint *endp;
struct mgcp_config *cfg;
int rc, dest, proto;
if (!tap->enabled)
return 0;
endp = (struct mgcp_endpoint *) fd->data;
cfg = endp->cfg;
return sendto(fd, buf, len, 0,
(struct sockaddr *)&tap->forward, sizeof(tap->forward));
}
static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp,
struct sockaddr_in *addr, char *buf, int rc)
{
struct mgcp_config *cfg = endp->cfg;
/* For loop toggle the destination and then dispatch. */
if (cfg->audio_loop)
dest = !dest;
rc = recvfrom(fd->fd, &buf, sizeof(buf), 0,
(struct sockaddr *) &addr, &slen);
/* Loop based on the conn_mode, maybe undoing the above */
if (endp->conn_mode == MGCP_CONN_LOOPBACK)
dest = !dest;
if (dest == DEST_NETWORK) {
if (is_rtp) {
patch_and_count(endp, &endp->bts_state,
endp->net_end.payload_type,
addr, buf, rc);
forward_data(endp->net_end.rtp.fd,
&endp->taps[MGCP_TAP_NET_OUT], buf, rc);
return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr,
endp->net_end.rtp_port, buf, rc);
} else {
return udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr,
endp->net_end.rtcp_port, buf, rc);
}
} else {
if (is_rtp) {
patch_and_count(endp, &endp->net_state,
endp->bts_end.payload_type,
addr, buf, rc);
forward_data(endp->bts_end.rtp.fd,
&endp->taps[MGCP_TAP_BTS_OUT], buf, rc);
return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr,
endp->bts_end.rtp_port, buf, rc);
} else {
return udp_send(endp->bts_end.rtcp.fd, &endp->bts_end.addr,
endp->bts_end.rtcp_port, buf, rc);
}
}
}
static int recevice_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr,
char *buf, int bufsize)
{
int rc;
socklen_t slen = sizeof(*addr);
rc = recvfrom(fd, buf, bufsize, 0,
(struct sockaddr *) addr, &slen);
if (rc < 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n",
ENDPOINT_NUMBER(endp), errno, strerror(errno));
@@ -138,72 +215,131 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
}
/* do not forward aynthing... maybe there is a packet from the bts */
if (endp->ci == CI_UNUSED) {
LOGP(DMGCP, LOGL_DEBUG, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
if (!endp->allocated)
return -1;
#warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
return rc;
}
static int rtp_data_net(struct bsc_fd *fd, unsigned int what)
{
char buf[4096];
struct sockaddr_in addr;
struct mgcp_endpoint *endp;
int rc, proto;
endp = (struct mgcp_endpoint *) fd->data;
rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf));
if (rc <= 0)
return -1;
if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong address %s on 0x%x\n",
inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp));
return -1;
}
/*
* Figure out where to forward it to. This code assumes that we
* have received the Connection Modify and know who is a legitimate
* partner. According to the spec we could attempt to forward even
* after the Create Connection but we will not as we are not really
* able to tell if this is legitimate.
*/
#warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 &&
(endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port)
? DEST_BTS : DEST_NETWORK;
proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP;
/* We have no idea who called us, maybe it is the BTS. */
if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || cfg->forward_ip)) {
/* it was the BTS... */
if (!cfg->bts_ip
|| memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0
|| memcmp(&addr.sin_addr, &endp->bts, sizeof(endp->bts)) == 0) {
if (fd == &endp->local_rtp) {
endp->bts_rtp = addr.sin_port;
} else {
endp->bts_rtcp = addr.sin_port;
}
endp->bts = addr.sin_addr;
LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n",
ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
inet_ntoa(addr.sin_addr));
}
if (endp->net_end.rtp_port != addr.sin_port &&
endp->net_end.rtcp_port != addr.sin_port) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong source port %d on 0x%x\n",
ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
return -1;
}
/* throw away dummy message */
/* throw away the dummy message */
if (rc == 1 && buf[0] == DUMMY_LOAD) {
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy on 0x%x\n",
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n",
ENDPOINT_NUMBER(endp));
return 0;
}
proto = fd == &endp->net_end.rtp ? PROTO_RTP : PROTO_RTCP;
endp->net_end.packets += 1;
forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc);
return send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, &buf[0], rc);
}
static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr)
{
struct mgcp_config *cfg = endp->cfg;
if (proto == PROTO_RTP && endp->bts_end.rtp_port == 0) {
if (!cfg->bts_ip ||
memcmp(&addr->sin_addr,
&cfg->bts_in, sizeof(cfg->bts_in)) == 0 ||
memcmp(&addr->sin_addr,
&endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) {
endp->bts_end.rtp_port = addr->sin_port;
endp->bts_end.addr = addr->sin_addr;
LOGP(DMGCP, LOGL_NOTICE,
"Found BTS for endpoint: 0x%x on port: %d/%d of %s\n",
ENDPOINT_NUMBER(endp), ntohs(endp->bts_end.rtp_port),
ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr));
}
} else if (proto == PROTO_RTCP && endp->bts_end.rtcp_port == 0) {
if (memcmp(&endp->bts_end.addr, &addr->sin_addr,
sizeof(endp->bts_end.addr)) == 0) {
endp->bts_end.rtcp_port = addr->sin_port;
}
}
}
static int rtp_data_bts(struct bsc_fd *fd, unsigned int what)
{
char buf[4096];
struct sockaddr_in addr;
struct mgcp_endpoint *endp;
struct mgcp_config *cfg;
int rc, proto;
endp = (struct mgcp_endpoint *) fd->data;
cfg = endp->cfg;
rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf));
if (rc <= 0)
return -1;
proto = fd == &endp->bts_end.rtp ? PROTO_RTP : PROTO_RTCP;
/* We have no idea who called us, maybe it is the BTS. */
/* it was the BTS... */
discover_bts(endp, proto, &addr);
if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong bts %s on 0x%x\n",
inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp));
return -1;
}
if (endp->bts_end.rtp_port != addr.sin_port &&
endp->bts_end.rtcp_port != addr.sin_port) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong bts source port %d on 0x%x\n",
ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
return -1;
}
/* throw away the dummy message */
if (rc == 1 && buf[0] == DUMMY_LOAD) {
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n",
ENDPOINT_NUMBER(endp));
return 0;
}
/* do this before the loop handling */
if (dest == DEST_NETWORK)
++endp->in_bts;
else
++endp->in_remote;
endp->bts_end.packets += 1;
/* dispatch */
if (cfg->audio_loop)
dest = !dest;
if (dest == DEST_NETWORK) {
patch_payload(endp->net_payload_type, buf, rc);
return udp_send(fd->fd, &endp->remote,
proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp,
buf, rc);
} else {
patch_payload(endp->bts_payload_type, buf, rc);
return udp_send(fd->fd, &endp->bts,
proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp,
buf, rc);
}
forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc);
return send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, &buf[0], rc);
}
static int create_bind(const char *source_addr, struct bsc_fd *fd, int port)
@@ -224,62 +360,112 @@ static int create_bind(const char *source_addr, struct bsc_fd *fd, int port)
inet_aton(source_addr, &addr.sin_addr);
if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(fd->fd);
fd->fd = -1;
return -1;
}
return 0;
}
static int bind_rtp(struct mgcp_endpoint *endp)
static int set_ip_tos(int fd, int tos)
{
struct mgcp_config *cfg = endp->cfg;
int ret;
ret = setsockopt(fd, IPPROTO_IP, IP_TOS,
&tos, sizeof(tos));
return ret != 0;
}
if (create_bind(cfg->source_addr, &endp->local_rtp, endp->rtp_port) != 0) {
static int bind_rtp(struct mgcp_config *cfg, struct mgcp_rtp_end *rtp_end, int endpno)
{
if (create_bind(cfg->source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n",
cfg->source_addr, endp->rtp_port, ENDPOINT_NUMBER(endp));
cfg->source_addr, rtp_end->local_port, endpno);
goto cleanup0;
}
if (create_bind(cfg->source_addr, &endp->local_rtcp, endp->rtp_port + 1) != 0) {
if (create_bind(cfg->source_addr, &rtp_end->rtcp, rtp_end->local_port + 1) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n",
cfg->source_addr, endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
cfg->source_addr, rtp_end->local_port + 1, endpno);
goto cleanup1;
}
endp->local_rtp.cb = rtp_data_cb;
endp->local_rtp.data = endp;
endp->local_rtp.when = BSC_FD_READ;
if (bsc_register_fd(&endp->local_rtp) != 0) {
set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
rtp_end->rtp.when = BSC_FD_READ;
if (bsc_register_fd(&rtp_end->rtp) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n",
endp->rtp_port, ENDPOINT_NUMBER(endp));
rtp_end->local_port, endpno);
goto cleanup2;
}
endp->local_rtcp.cb = rtp_data_cb;
endp->local_rtcp.data = endp;
endp->local_rtcp.when = BSC_FD_READ;
if (bsc_register_fd(&endp->local_rtcp) != 0) {
rtp_end->rtcp.when = BSC_FD_READ;
if (bsc_register_fd(&rtp_end->rtcp) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n",
endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
rtp_end->local_port + 1, endpno);
goto cleanup3;
}
return 0;
cleanup3:
bsc_unregister_fd(&endp->local_rtp);
bsc_unregister_fd(&rtp_end->rtp);
cleanup2:
close(endp->local_rtcp.fd);
endp->local_rtcp.fd = -1;
close(rtp_end->rtcp.fd);
rtp_end->rtcp.fd = -1;
cleanup1:
close(endp->local_rtp.fd);
endp->local_rtp.fd = -1;
close(rtp_end->rtp.fd);
rtp_end->rtp.fd = -1;
cleanup0:
return -1;
}
int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
{
endp->rtp_port = rtp_port;
return bind_rtp(endp);
if (endp->bts_end.rtp.fd != -1 || endp->bts_end.rtcp.fd != -1) {
LOGP(DMGCP, LOGL_ERROR, "Previous bts-port was still bound on %d\n",
ENDPOINT_NUMBER(endp));
mgcp_free_rtp_port(&endp->bts_end);
}
endp->bts_end.local_port = rtp_port;
endp->bts_end.rtp.cb = rtp_data_bts;
endp->bts_end.rtp.data = endp;
endp->bts_end.rtcp.data = endp;
endp->bts_end.rtcp.cb = rtp_data_bts;
return bind_rtp(endp->cfg, &endp->bts_end, ENDPOINT_NUMBER(endp));
}
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
{
if (endp->net_end.rtp.fd != -1 || endp->net_end.rtcp.fd != -1) {
LOGP(DMGCP, LOGL_ERROR, "Previous net-port was still bound on %d\n",
ENDPOINT_NUMBER(endp));
mgcp_free_rtp_port(&endp->net_end);
}
endp->net_end.local_port = rtp_port;
endp->net_end.rtp.cb = rtp_data_net;
endp->net_end.rtp.data = endp;
endp->net_end.rtcp.data = endp;
endp->net_end.rtcp.cb = rtp_data_net;
return bind_rtp(endp->cfg, &endp->net_end, ENDPOINT_NUMBER(endp));
}
int mgcp_free_rtp_port(struct mgcp_rtp_end *end)
{
if (end->rtp.fd != -1) {
close(end->rtp.fd);
end->rtp.fd = -1;
bsc_unregister_fd(&end->rtp);
}
if (end->rtcp.fd != -1) {
close(end->rtcp.fd);
end->rtcp.fd = -1;
bsc_unregister_fd(&end->rtcp);
}
return 0;
}

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.
*
@@ -79,6 +72,7 @@ enum mgcp_connection_mode {
} \
}
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
struct mgcp_request {
char *name;
@@ -95,7 +89,7 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg);
static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg);
static int generate_call_id(struct mgcp_config *cfg)
static uint32_t generate_call_id(struct mgcp_config *cfg)
{
int i;
@@ -135,7 +129,7 @@ static struct msgb *mgcp_msgb_alloc(void)
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
if (!msg)
LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
return msg;
}
@@ -176,13 +170,13 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
addr = endp->cfg->source_addr;
snprintf(sdp_record, sizeof(sdp_record) - 1,
"I: %d\n\n"
"I: %u\n\n"
"v=0\r\n"
"c=IN IP4 %s\r\n"
"m=audio %d RTP/AVP %d\r\n"
"a=rtpmap:%d %s\r\n",
endp->ci, addr, endp->rtp_port,
endp->bts_payload_type, endp->bts_payload_type,
endp->ci, addr, endp->net_end.local_port,
endp->bts_end.payload_type, endp->bts_end.payload_type,
endp->cfg->audio_name);
return mgcp_create_response_with_data(200, msg, trans_id, sdp_record);
}
@@ -330,11 +324,13 @@ static int verify_call_id(const struct mgcp_endpoint *endp,
}
static int verify_ci(const struct mgcp_endpoint *endp,
const char *ci)
const char *_ci)
{
if (atoi(ci) != endp->ci) {
LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n",
ENDPOINT_NUMBER(endp), endp->ci, ci);
uint32_t ci = strtoul(_ci, NULL, 10);
if (ci != endp->ci) {
LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n",
ENDPOINT_NUMBER(endp), endp->ci, _ci);
return -1;
}
@@ -364,6 +360,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;
@@ -372,6 +370,54 @@ static int parse_conn_mode(const char* msg, int *conn_mode)
return ret;
}
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end,
struct mgcp_port_range *range, int for_net)
{
int i;
if (range->mode == PORT_ALLOC_STATIC) {
end->local_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), range->base_port);
end->local_alloc = PORT_ALLOC_STATIC;
return 0;
}
/* attempt to find a port */
for (i = 0; i < 200; ++i) {
int rc;
if (range->last_port >= range->range_end)
range->last_port = range->range_start;
rc = for_net ?
mgcp_bind_net_rtp_port(endp, range->last_port) :
mgcp_bind_bts_rtp_port(endp, range->last_port);
range->last_port += 2;
if (rc == 0) {
end->local_alloc = PORT_ALLOC_DYNAMIC;
return 0;
}
}
LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x net: %d\n",
ENDPOINT_NUMBER(endp), for_net);
return -1;
}
static int allocate_ports(struct mgcp_endpoint *endp)
{
if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports, 1) != 0)
return -1;
if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports, 0) != 0) {
mgcp_rtp_end_reset(&endp->net_end);
return -1;
}
return 0;
}
static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
{
struct mgcp_msg_ptr data_ptrs[6];
@@ -379,17 +425,18 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
const char *trans_id;
struct mgcp_endpoint *endp;
int error_code = 500;
int port;
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
return create_response(500, "CRCX", trans_id);
if (endp->ci != CI_UNUSED) {
if (endp->allocated) {
if (cfg->force_realloc) {
LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
ENDPOINT_NUMBER(endp));
mgcp_free_endp(endp);
if (cfg->realloc_cb)
cfg->realloc_cb(cfg, ENDPOINT_NUMBER(endp));
} else {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
ENDPOINT_NUMBER(endp));
@@ -414,6 +461,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",
@@ -424,16 +473,13 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
MSG_TOKENIZE_END
/* initialize */
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0;
/* set to zero until we get the info */
memset(&endp->remote, 0, sizeof(endp->remote));
memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr));
/* bind to the port now */
port = rtp_calculate_port(ENDPOINT_NUMBER(endp), cfg->rtp_base_port);
if (cfg->early_bind)
endp->rtp_port = port;
else if (mgcp_bind_rtp_port(endp, port) != 0)
if (allocate_ports(endp) != 0)
goto error2;
/* assign a local call identifier or fail */
@@ -441,7 +487,8 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
if (endp->ci == CI_UNUSED)
goto error2;
endp->bts_payload_type = cfg->audio_payload;
endp->allocated = 1;
endp->bts_end.payload_type = cfg->audio_payload;
/* policy CB */
if (cfg->policy_cb) {
@@ -462,10 +509,11 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
}
}
LOGP(DMGCP, LOGL_NOTICE, "Creating endpoint on: 0x%x CI: %u port: %u\n",
ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
LOGP(DMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
ENDPOINT_NUMBER(endp), endp->ci,
endp->net_end.local_port, endp->bts_end.local_port);
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, endp->rtp_port);
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
return create_response_with_sdp(endp, "CRCX", trans_id);
error:
@@ -475,6 +523,7 @@ error:
return create_response(error_code, "CRCX", trans_id);
error2:
mgcp_free_endp(endp);
LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
return create_response(error_code, "CRCX", trans_id);
}
@@ -518,6 +567,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;
@@ -538,9 +588,9 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
const char *param = (const char *)&msg->l3h[line_start];
if (sscanf(param, "m=audio %d RTP/AVP %d", &port, &payload) == 2) {
endp->net_rtp = htons(port);
endp->net_rtcp = htons(port + 1);
endp->net_payload_type = payload;
endp->net_end.rtp_port = htons(port);
endp->net_end.rtcp_port = htons(port + 1);
endp->net_end.payload_type = payload;
}
break;
}
@@ -549,7 +599,7 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
const char *param = (const char *)&msg->l3h[line_start];
if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) {
inet_aton(ipv4, &endp->remote);
inet_aton(ipv4, &endp->net_end.addr);
}
break;
}
@@ -582,10 +632,10 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
}
/* modify */
LOGP(DMGCP, LOGL_NOTICE, "Modified endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port);
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX);
if (silent)
goto out_silent;
@@ -618,7 +668,7 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
if (found != 0)
return create_response(error_code, "DLCX", trans_id);
if (endp->ci == CI_UNUSED) {
if (!endp->allocated) {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
return create_response(error_code, "DLCX", trans_id);
}
@@ -667,11 +717,11 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
}
/* free the connection */
LOGP(DMGCP, LOGL_NOTICE, "Deleted endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
LOGP(DMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
mgcp_free_endp(endp);
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port);
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX);
if (silent)
goto out_silent;
@@ -711,11 +761,32 @@ struct mgcp_config *mgcp_config_alloc(void)
cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
cfg->audio_name = talloc_strdup(cfg, "GSM-EFR/8000");
cfg->audio_payload = 97;
cfg->rtp_base_port = RTP_PORT_DEFAULT;
cfg->bts_ports.base_port = RTP_PORT_DEFAULT;
cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
return cfg;
}
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
{
if (end->local_alloc == PORT_ALLOC_DYNAMIC)
mgcp_free_rtp_port(end);
end->packets = 0;
memset(&end->addr, 0, sizeof(end->addr));
end->rtp_port = end->rtcp_port = end->local_port = 0;
end->payload_type = -1;
end->local_alloc = -1;
}
static void mgcp_rtp_end_init(struct mgcp_rtp_end *end)
{
mgcp_rtp_end_reset(end);
end->rtp.fd = -1;
end->rtcp.fd = -1;
}
int mgcp_endpoints_allocate(struct mgcp_config *cfg)
{
int i;
@@ -728,12 +799,10 @@ int mgcp_endpoints_allocate(struct mgcp_config *cfg)
return -1;
for (i = 0; i < cfg->number_endpoints; ++i) {
cfg->endpoints[i].local_rtp.fd = -1;
cfg->endpoints[i].local_rtcp.fd = -1;
cfg->endpoints[i].ci = CI_UNUSED;
cfg->endpoints[i].cfg = cfg;
cfg->endpoints[i].net_payload_type = -1;
cfg->endpoints[i].bts_payload_type = -1;
mgcp_rtp_end_init(&cfg->endpoints[i].net_end);
mgcp_rtp_end_init(&cfg->endpoints[i].bts_end);
}
return 0;
@@ -742,7 +811,8 @@ 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;
endp->allocated = 0;
if (endp->callid) {
talloc_free(endp->callid);
@@ -754,14 +824,14 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
endp->local_options = NULL;
}
if (!endp->cfg->early_bind) {
bsc_unregister_fd(&endp->local_rtp);
bsc_unregister_fd(&endp->local_rtcp);
}
mgcp_rtp_end_reset(&endp->bts_end);
mgcp_rtp_end_reset(&endp->net_end);
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
endp->net_payload_type = endp->bts_payload_type = -1;
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;
endp->allow_patch = 0;
memset(&endp->taps, 0, sizeof(endp->taps));
}

View File

@@ -55,18 +55,26 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE);
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
vty_out(vty, " bind early %u%s", !!g_cfg->early_bind, VTY_NEWLINE);
vty_out(vty, " rtp base %u%s", g_cfg->rtp_base_port, VTY_NEWLINE);
if (g_cfg->bts_ports.mode == PORT_ALLOC_STATIC)
vty_out(vty, " rtp bts-base %u%s", g_cfg->bts_ports.base_port, VTY_NEWLINE);
else
vty_out(vty, " rtp bts-range %u %u%s",
g_cfg->bts_ports.range_start, g_cfg->bts_ports.range_end, VTY_NEWLINE);
if (g_cfg->net_ports.mode == PORT_ALLOC_STATIC)
vty_out(vty, " rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE);
else
vty_out(vty, " rtp net-range %u %u%s",
g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, 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)
vty_out(vty, " sdp audio payload name %s%s", g_cfg->audio_name, VTY_NEWLINE);
vty_out(vty, " loop %u%s", !!g_cfg->audio_loop, VTY_NEWLINE);
vty_out(vty, " number endpoints %u%s", g_cfg->number_endpoints - 1, VTY_NEWLINE);
if (g_cfg->forward_ip)
vty_out(vty, " forward audio ip %s%s", g_cfg->forward_ip, VTY_NEWLINE);
if (g_cfg->forward_port != 0)
vty_out(vty, " forward audio port %d%s", g_cfg->forward_port, VTY_NEWLINE);
if (g_cfg->call_agent_addr)
vty_out(vty, " call agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE);
@@ -81,11 +89,13 @@ 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,
ntohs(endp->net_end.rtp_port), ntohs(endp->net_end.rtcp_port),
ntohs(endp->bts_end.rtp_port), ntohs(endp->bts_end.rtcp_port),
inet_ntoa(endp->bts_end.addr),
endp->bts_end.packets, endp->bts_state.lost_no,
endp->net_end.packets, endp->net_state.lost_no,
VTY_NEWLINE);
}
@@ -103,7 +113,7 @@ DEFUN(cfg_mgcp,
DEFUN(cfg_mgcp_local_ip,
cfg_mgcp_local_ip_cmd,
"local ip IP",
"local ip A.B.C.D",
"Set the IP to be used in SDP records")
{
if (g_cfg->local_ip)
@@ -114,7 +124,7 @@ DEFUN(cfg_mgcp_local_ip,
DEFUN(cfg_mgcp_bts_ip,
cfg_mgcp_bts_ip_cmd,
"bts ip IP",
"bts ip A.B.C.D",
"Set the IP of the BTS for RTP forwarding")
{
if (g_cfg->bts_ip)
@@ -126,7 +136,7 @@ DEFUN(cfg_mgcp_bts_ip,
DEFUN(cfg_mgcp_bind_ip,
cfg_mgcp_bind_ip_cmd,
"bind ip IP",
"bind ip A.B.C.D",
"Bind the MGCP to this local addr")
{
if (g_cfg->source_addr)
@@ -141,11 +151,6 @@ DEFUN(cfg_mgcp_bind_port,
"Bind the MGCP to this port")
{
unsigned int port = atoi(argv[0]);
if (port > 65534) {
vty_out(vty, "%% wrong bind port '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->source_port = port;
return CMD_SUCCESS;
}
@@ -155,42 +160,82 @@ DEFUN(cfg_mgcp_bind_early,
"bind early (0|1)",
"Bind all RTP ports early")
{
unsigned int bind = atoi(argv[0]);
if (bind != 0 && bind != 1) {
vty_out(vty, "%% param must be 0 or 1.%s", VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->early_bind = bind == 1;
return CMD_SUCCESS;
vty_out(vty, "bind early is deprecated, remove it from the config.\n");
return CMD_WARNING;
}
DEFUN(cfg_mgcp_rtp_base_port,
cfg_mgcp_rtp_base_port_cmd,
"rtp base <0-65534>",
DEFUN(cfg_mgcp_rtp_bts_base_port,
cfg_mgcp_rtp_bts_base_port_cmd,
"rtp bts-base <0-65534>",
"Base port to use")
{
unsigned int port = atoi(argv[0]);
if (port > 65534) {
vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->rtp_base_port = port;
g_cfg->bts_ports.mode = PORT_ALLOC_STATIC;
g_cfg->bts_ports.base_port = port;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_bts_range,
cfg_mgcp_rtp_bts_range_cmd,
"rtp bts-range <0-65534> <0-65534>",
"Range of ports to allocate for endpoints\n"
"Start of the range of ports\n" "End of the range of ports\n")
{
g_cfg->bts_ports.mode = PORT_ALLOC_DYNAMIC;
g_cfg->bts_ports.range_start = atoi(argv[0]);
g_cfg->bts_ports.range_end = atoi(argv[1]);
g_cfg->bts_ports.last_port = g_cfg->bts_ports.range_start;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_net_range,
cfg_mgcp_rtp_net_range_cmd,
"rtp net-range <0-65534> <0-65534>",
"Range of ports to allocate for endpoints\n"
"Start of the range of ports\n" "End of the range of ports\n")
{
g_cfg->net_ports.mode = PORT_ALLOC_DYNAMIC;
g_cfg->net_ports.range_start = atoi(argv[0]);
g_cfg->net_ports.range_end = atoi(argv[1]);
g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_net_base_port,
cfg_mgcp_rtp_net_base_port_cmd,
"rtp net-base <0-65534>",
"Base port to use for network port\n" "Port\n")
{
unsigned int port = atoi(argv[0]);
g_cfg->net_ports.mode = PORT_ALLOC_STATIC;
g_cfg->net_ports.base_port = port;
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd,
"rtp base <0-65534>", "Base port to use")
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 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>",
"Set the audio codec to use")
{
unsigned int payload = atoi(argv[0]);
if (payload > 255) {
vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->audio_payload = payload;
return CMD_SUCCESS;
}
@@ -225,26 +270,6 @@ DEFUN(cfg_mgcp_number_endp,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_forward_ip,
cfg_mgcp_forward_ip_cmd,
"forward audio ip IP",
"Forward packets from and to the IP. This disables most of the MGCP feature.")
{
if (g_cfg->forward_ip)
talloc_free(g_cfg->forward_ip);
g_cfg->forward_ip = talloc_strdup(g_cfg, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_forward_port,
cfg_mgcp_forward_port_cmd,
"forward audio port <1-15000>",
"Forward packets from and to the port. This disables most of the MGCP feature.")
{
g_cfg->forward_port = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_agent_addr,
cfg_mgcp_agent_addr_cmd,
"call agent ip IP",
@@ -256,12 +281,88 @@ 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;
endp->allow_patch = 1;
return CMD_SUCCESS;
}
DEFUN(tap_call,
tap_call_cmd,
"tap-call ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>",
"Forward data on endpoint to a different system\n"
"The endpoint in hex\n"
"Forward the data coming from the bts\n"
"Forward the data coming from the bts leaving to the network\n"
"Forward the data coming from the net\n"
"Forward the data coming from the net leaving to the bts\n"
"destination IP of the data\n" "destination port\n")
{
struct mgcp_rtp_tap *tap;
struct mgcp_endpoint *endp;
int port = 0;
int endp_no = strtoul(argv[0], NULL, 16);
if (endp_no < 1 || endp_no >= g_cfg->number_endpoints) {
vty_out(vty, "Endpoint number %s/%d is invalid.%s",
argv[0], endp_no, VTY_NEWLINE);
return CMD_WARNING;
}
endp = &g_cfg->endpoints[endp_no];
if (strcmp(argv[1], "bts-in") == 0) {
port = MGCP_TAP_BTS_IN;
} else if (strcmp(argv[1], "bts-out") == 0) {
port = MGCP_TAP_BTS_OUT;
} else if (strcmp(argv[1], "net-in") == 0) {
port = MGCP_TAP_NET_IN;
} else if (strcmp(argv[1], "net-out") == 0) {
port = MGCP_TAP_NET_OUT;
} else {
vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE);
return CMD_WARNING;
}
tap = &endp->taps[port];
memset(&tap->forward, 0, sizeof(tap->forward));
inet_aton(argv[2], &tap->forward.sin_addr);
tap->forward.sin_port = htons(atoi(argv[3]));
tap->enabled = 1;
return CMD_SUCCESS;
}
int mgcp_vty_init(void)
{
install_element(VIEW_NODE, &show_mgcp_cmd);
install_element(ENABLE_NODE, &loop_endp_cmd);
install_element(ENABLE_NODE, &tap_call_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);
@@ -269,12 +370,16 @@ 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_bts_base_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_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);
install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd);
return 0;
}
@@ -304,51 +409,32 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg)
return -1;
}
/*
* This application supports two modes.
* 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX
* 2.) plain forwarding of RTP packets on the endpoints.
* both modes are mutual exclusive
*/
if (g_cfg->forward_ip) {
int port = g_cfg->rtp_base_port;
if (g_cfg->forward_port != 0)
port = g_cfg->forward_port;
if (!g_cfg->early_bind) {
LOGP(DMGCP, LOGL_NOTICE, "Forwarding requires early bind.\n");
return -1;
}
/*
* Store the forward IP and assign a ci. For early bind
* the sockets will be created after this.
*/
for (i = 1; i < g_cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &g_cfg->endpoints[i];
inet_aton(g_cfg->forward_ip, &endp->remote);
endp->ci = CI_UNUSED + 23;
endp->net_rtp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port));
endp->net_rtcp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port) + 1);
}
LOGP(DMGCP, LOGL_NOTICE, "Configured for Audio Forwarding.\n");
}
/* early bind */
if (g_cfg->early_bind) {
for (i = 1; i < g_cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &g_cfg->endpoints[i];
int rtp_port;
for (i = 1; i < g_cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &g_cfg->endpoints[i];
int rtp_port;
rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), g_cfg->rtp_base_port);
if (mgcp_bind_rtp_port(endp, rtp_port) != 0) {
if (g_cfg->bts_ports.mode == PORT_ALLOC_STATIC) {
rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp),
g_cfg->bts_ports.base_port);
if (mgcp_bind_bts_rtp_port(endp, rtp_port) != 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
return -1;
}
endp->bts_end.local_alloc = PORT_ALLOC_STATIC;
}
if (g_cfg->net_ports.mode == PORT_ALLOC_STATIC) {
rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp),
g_cfg->net_ports.base_port);
if (mgcp_bind_net_rtp_port(endp, rtp_port) != 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
return -1;
}
endp->net_end.local_alloc = PORT_ALLOC_STATIC;
}
}
return !!g_cfg->forward_ip;
return 0;
}

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]);
}
}

View File

@@ -1,978 +0,0 @@
/* BSC Multiplexer/NAT */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <openbsc/debug.h>
#include <openbsc/bsc_msc.h>
#include <openbsc/bsc_nat.h>
#include <openbsc/bssap.h>
#include <openbsc/ipaccess.h>
#include <openbsc/abis_nm.h>
#include <openbsc/telnet_interface.h>
#include <osmocore/talloc.h>
#include <vty/vty.h>
#include <sccp/sccp.h>
struct log_target *stderr_target;
static const char *config_file = "bsc-nat.cfg";
static struct in_addr local_addr;
static struct bsc_msc_connection *msc_con;
static struct bsc_fd bsc_listen;
static const char *msc_ip = NULL;
static struct bsc_nat *nat;
static void bsc_send_data(struct bsc_connection *bsc, const u_int8_t *data, unsigned int length, int);
static void msc_send_reset(struct bsc_msc_connection *con);
struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num)
{
struct bsc_config *conf;
llist_for_each_entry(conf, &nat->bsc_configs, entry)
if (conf->nr == num)
return conf;
return NULL;
}
/*
* below are stubs we need to link
*/
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
{
return -1;
}
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
{}
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
{
return -1;
}
static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg)
{
if (write_queue_enqueue(&msc_con->write_queue, msg) != 0) {
LOGP(DINP, LOGL_ERROR, "Failed to enqueue the write.\n");
msgb_free(msg);
}
}
static void send_reset_ack(struct bsc_connection *bsc)
{
static const u_int8_t gsm_reset_ack[] = {
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
0x00, 0x01, 0x31,
};
bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP);
}
static void send_ping(struct bsc_connection *bsc)
{
static const u_int8_t id_ping[] = {
IPAC_MSGT_PING,
};
bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS);
}
static void send_pong(struct bsc_connection *bsc)
{
static const u_int8_t id_pong[] = {
IPAC_MSGT_PONG,
};
bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS);
}
static void bsc_pong_timeout(void *_bsc)
{
struct bsc_connection *bsc = _bsc;
LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr);
bsc_close_connection(bsc);
}
static void bsc_ping_timeout(void *_bsc)
{
struct bsc_connection *bsc = _bsc;
send_ping(bsc);
/* send another ping in 20 seconds */
bsc_schedule_timer(&bsc->ping_timeout, 20, 0);
/* also start a pong timer */
bsc_schedule_timer(&bsc->pong_timeout, 5, 0);
}
static void start_ping_pong(struct bsc_connection *bsc)
{
bsc->pong_timeout.data = bsc;
bsc->pong_timeout.cb = bsc_pong_timeout;
bsc->ping_timeout.data = bsc;
bsc->ping_timeout.cb = bsc_ping_timeout;
bsc_ping_timeout(bsc);
}
static void send_id_ack(struct bsc_connection *bsc)
{
static const u_int8_t id_ack[] = {
IPAC_MSGT_ID_ACK
};
bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS);
}
static void send_id_req(struct bsc_connection *bsc)
{
static const u_int8_t id_req[] = {
IPAC_MSGT_ID_GET,
0x01, IPAC_IDTAG_UNIT,
0x01, IPAC_IDTAG_MACADDR,
0x01, IPAC_IDTAG_LOCATION1,
0x01, IPAC_IDTAG_LOCATION2,
0x01, IPAC_IDTAG_EQUIPVERS,
0x01, IPAC_IDTAG_SWVERSION,
0x01, IPAC_IDTAG_UNITNAME,
0x01, IPAC_IDTAG_SERNR,
};
bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS);
}
static void nat_send_rlsd(struct sccp_connections *conn)
{
struct sccp_connection_released *rel;
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "rlsd");
if (!msg) {
LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n");
return;
}
msg->l2h = msgb_put(msg, sizeof(*rel));
rel = (struct sccp_connection_released *) msg->l2h;
rel->type = SCCP_MSG_TYPE_RLSD;
rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE;
rel->destination_local_reference = conn->remote_ref;
rel->source_local_reference = conn->patched_ref;
ipaccess_prepend_header(msg, IPAC_PROTO_SCCP);
queue_for_msc(msc_con, msg);
}
static void nat_send_rlc(struct sccp_source_reference *src,
struct sccp_source_reference *dst)
{
struct sccp_connection_release_complete *rlc;
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "rlc");
if (!msg) {
LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n");
return;
}
msg->l2h = msgb_put(msg, sizeof(*rlc));
rlc = (struct sccp_connection_release_complete *) msg->l2h;
rlc->type = SCCP_MSG_TYPE_RLC;
rlc->destination_local_reference = *dst;
rlc->source_local_reference = *src;
ipaccess_prepend_header(msg, IPAC_PROTO_SCCP);
queue_for_msc(msc_con, msg);
}
static void send_mgcp_reset(struct bsc_connection *bsc)
{
static const u_int8_t mgcp_reset[] = {
"RSIP 1 13@mgw MGCP 1.0\r\n"
};
bsc_write_mgcp(bsc, mgcp_reset, sizeof mgcp_reset - 1);
}
/*
* Below is the handling of messages coming
* from the MSC and need to be forwarded to
* a real BSC.
*/
static void initialize_msc_if_needed()
{
if (nat->first_contact)
return;
nat->first_contact = 1;
msc_send_reset(msc_con);
}
/*
* Currently we are lacking refcounting so we need to copy each message.
*/
static void bsc_send_data(struct bsc_connection *bsc, const u_int8_t *data, unsigned int length, int proto)
{
struct msgb *msg;
if (length > 4096 - 128) {
LOGP(DINP, LOGL_ERROR, "Can not send message of that size.\n");
return;
}
msg = msgb_alloc_headroom(4096, 128, "to-bsc");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n");
return;
}
msg->l2h = msgb_put(msg, length);
memcpy(msg->data, data, length);
bsc_write(bsc, msg, proto);
}
static int forward_sccp_to_bts(struct msgb *msg)
{
struct sccp_connections *con;
struct bsc_connection *bsc;
struct bsc_nat_parsed *parsed;
int proto;
/* filter, drop, patch the message? */
parsed = bsc_nat_parse(msg);
if (!parsed) {
LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
return -1;
}
if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed))
goto exit;
proto = parsed->ipa_proto;
/* Route and modify the SCCP packet */
if (proto == IPAC_PROTO_SCCP) {
switch (parsed->sccp_type) {
case SCCP_MSG_TYPE_UDT:
/* forward UDT messages to every BSC */
goto send_to_all;
break;
case SCCP_MSG_TYPE_RLSD:
case SCCP_MSG_TYPE_CREF:
case SCCP_MSG_TYPE_DT1:
case SCCP_MSG_TYPE_IT:
con = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) {
counter_inc(nat->stats.sccp.calls);
if (con) {
counter_inc(con->bsc->cfg->stats.sccp.calls);
if (bsc_mgcp_assign(con, msg) != 0)
LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n");
} else
LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n");
}
break;
case SCCP_MSG_TYPE_CC:
con = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
if (!con || update_sccp_src_ref(con, parsed) != 0)
goto exit;
break;
case SCCP_MSG_TYPE_RLC:
LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n");
goto exit;
break;
case SCCP_MSG_TYPE_CR:
/* MSC never opens a SCCP connection, fall through */
default:
goto exit;
}
if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) {
LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n");
/* Exchange src/dest for the reply */
nat_send_rlc(parsed->dest_local_ref, parsed->src_local_ref);
} else if (!con)
LOGP(DNAT, LOGL_ERROR, "Unknown connection for msg type: 0x%x.\n", parsed->sccp_type);
}
talloc_free(parsed);
if (!con)
return -1;
if (!con->bsc->authenticated) {
LOGP(DNAT, LOGL_ERROR, "Selected BSC not authenticated.\n");
return -1;
}
bsc_send_data(con->bsc, msg->l2h, msgb_l2len(msg), proto);
return 0;
send_to_all:
/*
* Filter Paging from the network. We do not want to send a PAGING
* Command to every BSC in our network. We will analys the PAGING
* message and then send it to the authenticated messages...
*/
if (parsed->ipa_proto == IPAC_PROTO_SCCP && parsed->gsm_type == BSS_MAP_MSG_PAGING) {
int lac;
bsc = bsc_nat_find_bsc(nat, msg, &lac);
if (bsc && bsc->cfg->forbid_paging)
LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr);
else if (bsc)
bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto);
else
LOGP(DNAT, LOGL_ERROR, "Could not determine BSC for paging on lac: %d/0x%x\n",
lac, lac);
goto exit;
}
/* currently send this to every BSC connected */
llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) {
if (!bsc->authenticated)
continue;
bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto);
}
exit:
talloc_free(parsed);
return 0;
}
static void msc_connection_was_lost(struct bsc_msc_connection *con)
{
struct bsc_connection *bsc, *tmp;
counter_inc(nat->stats.msc.reconn);
LOGP(DMSC, LOGL_ERROR, "Closing all connections downstream.\n");
llist_for_each_entry_safe(bsc, tmp, &nat->bsc_connections, list_entry)
bsc_close_connection(bsc);
nat->first_contact = 0;
bsc_mgcp_free_endpoints(nat);
bsc_msc_schedule_connect(con);
}
static void msc_send_reset(struct bsc_msc_connection *msc_con)
{
static const u_int8_t reset[] = {
0x00, 0x12, 0xfd,
0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
0x01, 0x20
};
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "08.08 reset");
if (!msg) {
LOGP(DMSC, LOGL_ERROR, "Failed to allocate reset msg.\n");
return;
}
msg->l2h = msgb_put(msg, sizeof(reset));
memcpy(msg->l2h, reset, msgb_l2len(msg));
queue_for_msc(msc_con, msg);
LOGP(DMSC, LOGL_NOTICE, "Scheduled GSM0808 reset msg for the MSC.\n");
}
static int ipaccess_msc_read_cb(struct bsc_fd *bfd)
{
int error;
struct msgb *msg = ipaccess_read_msg(bfd, &error);
struct ipaccess_head *hh;
if (!msg) {
if (error == 0)
LOGP(DNAT, LOGL_FATAL, "The connection the MSC was lost, exiting\n");
else
LOGP(DNAT, LOGL_ERROR, "Failed to parse ip access message: %d\n", error);
bsc_msc_lost(msc_con);
return -1;
}
LOGP(DNAT, LOGL_DEBUG, "MSG from MSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]);
/* handle base message handling */
hh = (struct ipaccess_head *) msg->data;
ipaccess_rcvmsg_base(msg, bfd);
/* initialize the networking. This includes sending a GSM08.08 message */
if (hh->proto == IPAC_PROTO_IPACCESS && msg->l2h[0] == IPAC_MSGT_ID_ACK)
initialize_msc_if_needed();
else if (hh->proto == IPAC_PROTO_SCCP)
forward_sccp_to_bts(msg);
msgb_free(msg);
return 0;
}
static int ipaccess_msc_write_cb(struct bsc_fd *bfd, struct msgb *msg)
{
int rc;
rc = write(bfd->fd, msg->data, msg->len);
if (rc != msg->len) {
LOGP(DNAT, LOGL_ERROR, "Failed to write MSG to MSC.\n");
return -1;
}
return rc;
}
/*
* Below is the handling of messages coming
* from the BSC and need to be forwarded to
* a real BSC.
*/
/*
* Remove the connection from the connections list,
* remove it from the patching of SCCP header lists
* as well. Maybe in the future even close connection..
*/
void bsc_close_connection(struct bsc_connection *connection)
{
struct sccp_connections *sccp_patch, *tmp;
/* stop the timeout timer */
bsc_del_timer(&connection->id_timeout);
bsc_del_timer(&connection->ping_timeout);
bsc_del_timer(&connection->pong_timeout);
/* remove all SCCP connections */
llist_for_each_entry_safe(sccp_patch, tmp, &nat->sccp_connections, list_entry) {
if (sccp_patch->bsc != connection)
continue;
if (sccp_patch->has_remote_ref)
nat_send_rlsd(sccp_patch);
sccp_connection_destroy(sccp_patch);
}
/* close endpoints allocated by this BSC */
bsc_mgcp_clear_endpoints_for(connection);
bsc_unregister_fd(&connection->write_queue.bfd);
close(connection->write_queue.bfd.fd);
write_queue_clear(&connection->write_queue);
llist_del(&connection->list_entry);
talloc_free(connection);
}
static void ipaccess_close_bsc(void *data)
{
struct sockaddr_in sock;
socklen_t len = sizeof(sock);
struct bsc_connection *conn = data;
getpeername(conn->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
LOGP(DNAT, LOGL_ERROR, "BSC on %s didn't respond to identity request. Closing.\n",
inet_ntoa(sock.sin_addr));
bsc_close_connection(conn);
}
static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc)
{
struct bsc_config *conf;
const char* token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME);
if (bsc->cfg) {
LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n",
bsc->write_queue.bfd.fd, bsc->cfg->nr);
return;
}
llist_for_each_entry(conf, &bsc->nat->bsc_configs, entry) {
if (strcmp(conf->token, token) == 0) {
counter_inc(conf->stats.net.reconn);
bsc->authenticated = 1;
bsc->cfg = conf;
bsc_del_timer(&bsc->id_timeout);
LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d lac: %d\n", conf->nr, conf->lac);
start_ping_pong(bsc);
return;
}
}
LOGP(DNAT, LOGL_ERROR, "No bsc found for token %s.\n", token);
}
static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
{
struct sccp_connections *con;
struct bsc_nat_parsed *parsed;
/* Parse and filter messages */
parsed = bsc_nat_parse(msg);
if (!parsed) {
LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
msgb_free(msg);
return -1;
}
if (bsc_nat_filter_ipa(DIR_MSC, msg, parsed))
goto exit;
/*
* check authentication after filtering to not reject auth
* responses coming from the BSC. We have to make sure that
* nothing from the exit path will forward things to the MSC
*/
if (!bsc->authenticated) {
LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated.\n");
msgb_free(msg);
return -1;
}
/* modify the SCCP entries */
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
switch (parsed->sccp_type) {
case SCCP_MSG_TYPE_CR:
if (create_sccp_src_ref(bsc, msg, parsed) != 0)
goto exit2;
con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
break;
case SCCP_MSG_TYPE_RLSD:
case SCCP_MSG_TYPE_CREF:
case SCCP_MSG_TYPE_DT1:
case SCCP_MSG_TYPE_CC:
case SCCP_MSG_TYPE_IT:
con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
break;
case SCCP_MSG_TYPE_RLC:
con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
remove_sccp_src_ref(bsc, msg, parsed);
break;
case SCCP_MSG_TYPE_UDT:
/* simply forward everything */
con = NULL;
break;
default:
LOGP(DNAT, LOGL_ERROR, "Not forwarding to msc sccp type: 0x%x\n", parsed->sccp_type);
con = NULL;
goto exit2;
break;
}
} else if (parsed->ipa_proto == NAT_IPAC_PROTO_MGCP) {
bsc_mgcp_forward(bsc, msg);
goto exit2;
} else {
LOGP(DNAT, LOGL_ERROR, "Not forwarding unknown stream id: 0x%x\n", parsed->ipa_proto);
goto exit2;
}
if (con && con->bsc != bsc) {
LOGP(DNAT, LOGL_ERROR, "The connection belongs to a different BTS: input: %d con: %d\n",
bsc->cfg->nr, con->bsc->cfg->nr);
goto exit2;
}
/* send the non-filtered but maybe modified msg */
queue_for_msc(msc_con, msg);
talloc_free(parsed);
return 0;
exit:
/* if we filter out the reset send an ack to the BSC */
if (parsed->bssap == 0 && parsed->gsm_type == BSS_MAP_MSG_RESET) {
send_reset_ack(bsc);
send_reset_ack(bsc);
} else if (parsed->ipa_proto == IPAC_PROTO_IPACCESS) {
/* do we know who is handling this? */
if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
struct tlv_parsed tvp;
ipaccess_idtag_parse(&tvp,
(unsigned char *) msg->l2h + 2,
msgb_l2len(msg) - 2);
if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME))
ipaccess_auth_bsc(&tvp, bsc);
}
goto exit2;
}
exit2:
talloc_free(parsed);
msgb_free(msg);
return -1;
}
static int ipaccess_bsc_read_cb(struct bsc_fd *bfd)
{
int error;
struct bsc_connection *bsc = bfd->data;
struct msgb *msg = ipaccess_read_msg(bfd, &error);
struct ipaccess_head *hh;
if (!msg) {
if (error == 0)
LOGP(DNAT, LOGL_ERROR,
"The connection to the BSC Nr: %d was lost. Cleaning it\n",
bsc->cfg ? bsc->cfg->nr : -1);
else
LOGP(DNAT, LOGL_ERROR,
"Stream error on BSC Nr: %d. Failed to parse ip access message: %d\n",
bsc->cfg ? bsc->cfg->nr : -1, error);
bsc_close_connection(bsc);
return -1;
}
LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]);
/* Handle messages from the BSC */
hh = (struct ipaccess_head *) msg->data;
/* stop the pong timeout */
if (hh->proto == IPAC_PROTO_IPACCESS) {
if (msg->l2h[0] == IPAC_MSGT_PONG) {
bsc_del_timer(&bsc->pong_timeout);
msgb_free(msg);
return 0;
} else if (msg->l2h[0] == IPAC_MSGT_PING) {
send_pong(bsc);
msgb_free(msg);
return 0;
}
}
/* FIXME: Currently no PONG is sent to the BSC */
/* FIXME: Currently no ID ACK is sent to the BSC */
forward_sccp_to_msc(bsc, msg);
return 0;
}
static int ipaccess_bsc_write_cb(struct bsc_fd *bfd, struct msgb *msg)
{
int rc;
rc = write(bfd->fd, msg->data, msg->len);
if (rc != msg->len)
LOGP(DNAT, LOGL_ERROR, "Failed to write message to the BSC.\n");
return rc;
}
static int ipaccess_listen_bsc_cb(struct bsc_fd *bfd, unsigned int what)
{
struct bsc_connection *bsc;
int ret;
struct sockaddr_in sa;
socklen_t sa_len = sizeof(sa);
if (!(what & BSC_FD_READ))
return 0;
ret = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len);
if (ret < 0) {
perror("accept");
return ret;
}
/* count the reconnect */
counter_inc(nat->stats.bsc.reconn);
/*
* if we are not connected to a msc... just close the socket
*/
if (!msc_con->is_connected) {
LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n");
close(ret);
return 0;
}
/* todo... do something with the connection */
/* todo... use GNUtls to see if we want to trust this as a BTS */
/*
*
*/
bsc = bsc_connection_alloc(nat);
if (!bsc) {
LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n");
close(ret);
return -1;
}
bsc->write_queue.bfd.data = bsc;
bsc->write_queue.bfd.fd = ret;
bsc->write_queue.read_cb = ipaccess_bsc_read_cb;
bsc->write_queue.write_cb = ipaccess_bsc_write_cb;
bsc->write_queue.bfd.when = BSC_FD_READ;
if (bsc_register_fd(&bsc->write_queue.bfd) < 0) {
LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n");
close(ret);
talloc_free(bsc);
return -2;
}
LOGP(DNAT, LOGL_NOTICE, "Registered new BSC\n");
llist_add(&bsc->list_entry, &nat->bsc_connections);
send_id_ack(bsc);
send_id_req(bsc);
send_mgcp_reset(bsc);
/*
* start the hangup timer
*/
bsc->id_timeout.data = bsc;
bsc->id_timeout.cb = ipaccess_close_bsc;
bsc_schedule_timer(&bsc->id_timeout, 2, 0);
return 0;
}
static int listen_for_bsc(struct bsc_fd *bfd, struct in_addr *in_addr, int port)
{
struct sockaddr_in addr;
int ret, on = 1;
bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bfd->cb = ipaccess_listen_bsc_cb;
bfd->when = BSC_FD_READ;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = in_addr->s_addr;
setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
if (ret < 0) {
fprintf(stderr, "Could not bind the BSC socket %s\n",
strerror(errno));
return -EIO;
}
ret = listen(bfd->fd, 1);
if (ret < 0) {
perror("listen");
return ret;
}
ret = bsc_register_fd(bfd);
if (ret < 0) {
perror("register_listen_fd");
return ret;
}
return 0;
}
static void print_usage()
{
printf("Usage: bsc_nat\n");
}
static void print_help()
{
printf(" Some useful help...\n");
printf(" -h --help this text\n");
printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n");
printf(" -s --disable-color\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -m --msc=IP. The address of the MSC.\n");
printf(" -l --local=IP. The local address of this BSC.\n");
}
static void handle_options(int argc, char** argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"debug", 1, 0, 'd'},
{"config-file", 1, 0, 'c'},
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{"msc", 1, 0, 'm'},
{"local", 1, 0, 'l'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hd:sTPc:m:l:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage();
print_help();
exit(0);
case 's':
log_set_use_color(stderr_target, 0);
break;
case 'd':
log_parse_category_mask(stderr_target, optarg);
break;
case 'c':
config_file = strdup(optarg);
break;
case 'T':
log_set_print_timestamp(stderr_target, 1);
break;
case 'm':
msc_ip = optarg;
break;
case 'l':
inet_aton(optarg, &local_addr);
break;
default:
/* ignore */
break;
}
}
}
static void signal_handler(int signal)
{
switch (signal) {
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
talloc_report_full(tall_bsc_ctx, stderr);
break;
default:
break;
}
}
extern void *tall_msgb_ctx;
extern void *tall_ctr_ctx;
static void talloc_init_ctx()
{
tall_bsc_ctx = talloc_named_const(NULL, 0, "nat");
tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter");
}
int main(int argc, char** argv)
{
talloc_init_ctx();
log_init(&log_info);
stderr_target = log_target_create_stderr();
log_add_target(stderr_target);
log_set_all_filter(stderr_target, 1);
nat = bsc_nat_alloc();
if (!nat) {
fprintf(stderr, "Failed to allocate the BSC nat.\n");
return -4;
}
nat->mgcp_cfg = talloc_zero(nat, struct mgcp_config);
if (!nat->mgcp_cfg) {
fprintf(stderr, "Failed to allocate MGCP cfg.\n");
return -5;
}
/* parse options */
local_addr.s_addr = INADDR_ANY;
handle_options(argc, argv);
/* init vty and parse */
bsc_nat_vty_init(nat);
telnet_init(NULL, 4244);
if (mgcp_parse_config(config_file, nat->mgcp_cfg) < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
return -3;
}
/* over rule the VTY config */
if (msc_ip)
bsc_nat_set_msc_ip(nat, msc_ip);
/* seed the PRNG */
srand(time(NULL));
/*
* Setup the MGCP code..
*/
if (bsc_mgcp_nat_init(nat) != 0)
return -4;
/* connect to the MSC */
msc_con = bsc_msc_create(nat->msc_ip, nat->msc_port);
if (!msc_con) {
fprintf(stderr, "Creating a bsc_msc_connection failed.\n");
exit(1);
}
msc_con->connection_loss = msc_connection_was_lost;
msc_con->write_queue.read_cb = ipaccess_msc_read_cb;
msc_con->write_queue.write_cb = ipaccess_msc_write_cb;;
bsc_msc_connect(msc_con);
/* wait for the BSC */
if (listen_for_bsc(&bsc_listen, &local_addr, 5000) < 0) {
fprintf(stderr, "Failed to listen for BSC.\n");
exit(1);
}
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGPIPE, SIG_IGN);
while (1) {
bsc_select_main(0);
}
return 0;
}

View File

@@ -1,192 +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);
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;
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;
}

View File

@@ -1,409 +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/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 int config_write_nat(struct vty *vty)
{
vty_out(vty, "nat%s", VTY_NEWLINE);
if (_nat->imsi_allow)
vty_out(vty, " imsi allow %s%s", _nat->imsi_allow, VTY_NEWLINE);
if (_nat->imsi_deny)
vty_out(vty, " insi deny %s%s", _nat->imsi_deny, 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);
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);
if (bsc->imsi_allow)
vty_out(vty, " imsi allow %s%s", bsc->imsi_allow, VTY_NEWLINE);
if (bsc->imsi_deny)
vty_out(vty, " imsi deny %s%s", bsc->imsi_deny, VTY_NEWLINE);
vty_out(vty, " paging forbidden %d%s", bsc->forbid_paging, 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%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,
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);
vty_out(vty, " imsi_allow: '%s' imsi_deny: '%s'%s",
conf->imsi_allow ? conf->imsi_allow: "any",
conf->imsi_deny ? conf->imsi_deny : "none",
VTY_NEWLINE);
vty_out(vty, " paging forbidden: %d%s",
conf->forbid_paging, 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(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;
}
static void 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);
}
}
DEFUN(cfg_nat_imsi_allow,
cfg_nat_imsi_allow_cmd,
"imsi allow [REGEXP]",
"Allow matching IMSIs to talk to the MSC. "
"The defualt is to allow everyone.")
{
parse_reg(_nat, &_nat->imsi_allow_re, &_nat->imsi_allow, argc, argv);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_imsi_deny,
cfg_nat_imsi_deny_cmd,
"imsi deny [REGEXP]",
"Deny matching IMSIs to talk to the MSC. "
"The defualt is to not deny.")
{
parse_reg(_nat, &_nat->imsi_deny_re, &_nat->imsi_deny, argc, argv);
return CMD_SUCCESS;
}
DEFUN(cfg_nat_msc_ip,
cfg_nat_msc_ip_cmd,
"msc ip IP",
"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;
}
/* 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 < 0 || lac > 0xffff) {
vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s",
lac, VTY_NEWLINE);
return CMD_WARNING;
}
if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
lac, VTY_NEWLINE);
return CMD_WARNING;
}
/* 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_bsc_imsi_allow,
cfg_bsc_imsi_allow_cmd,
"imsi allow [REGEXP]",
"Allow IMSIs with the following network to talk to the MSC."
"The default is to allow everyone)")
{
struct bsc_config *conf = vty->index;
parse_reg(conf, &conf->imsi_allow_re, &conf->imsi_allow, argc, argv);
return CMD_SUCCESS;
}
DEFUN(cfg_bsc_imsi_deny,
cfg_bsc_imsi_deny_cmd,
"imsi deny [REGEXP]",
"Deny IMSIs with the following network to talk to the MSC."
"The default is to not deny anyone.)")
{
struct bsc_config *conf = vty->index;
parse_reg(conf, &conf->imsi_deny_re, &conf->imsi_deny, argc, argv);
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;
}
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);
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_imsi_allow_cmd);
install_element(NAT_NODE, &cfg_nat_imsi_deny_cmd);
install_element(NAT_NODE, &cfg_nat_msc_ip_cmd);
install_element(NAT_NODE, &cfg_nat_msc_port_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_imsi_allow_cmd);
install_element(BSC_NODE, &cfg_bsc_imsi_deny_cmd);
install_element(BSC_NODE, &cfg_bsc_paging_cmd);
mgcp_vty_init();
return 0;
}
/* called by the telnet interface... we have our own init above */
void bsc_vty_init()
{}

View File

@@ -1,232 +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>
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;
}
int create_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, 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));
llist_del(&conn->list_entry);
talloc_free(conn);
return -1;
} else {
bsc_mgcp_dlcx(conn);
return 0;
}
}
conn = talloc_zero(bsc->nat, struct sccp_connections);
if (!conn) {
LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n");
return -1;
}
conn->bsc = bsc;
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 -1;
}
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 0;
}
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

@@ -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/osmo_bsc_grace.h>
#include <openbsc/osmo_bsc_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

@@ -21,9 +21,10 @@
*
*/
#include <openbsc/bsc_msc_rf.h>
#include <openbsc/osmo_bsc_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)
{
@@ -55,18 +57,18 @@ static int lock_each_trx(struct gsm_network *net, int lock)
/*
* Send a '1' when one TRX is online, otherwise send 0
*/
static void handle_query(struct bsc_msc_rf_conn *conn)
static void handle_query(struct osmo_bsc_rf_conn *conn)
{
struct msgb *msg;
struct gsm_bts *bts;
char send = '0';
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 &&
trx->nm_state.operational != NM_STATE_LOCKED) {
send = '1';
send = RF_CMD_ON;
break;
}
}
@@ -90,9 +92,18 @@ static void handle_query(struct bsc_msc_rf_conn *conn)
return;
}
static void send_signal(struct osmo_bsc_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;
struct osmo_bsc_rf_conn *conn = fd->data;
char buf[1];
int rc;
@@ -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]);
@@ -139,8 +155,8 @@ static int rf_write_cmd(struct bsc_fd *fd, struct msgb *msg)
static int rf_ctl_accept(struct bsc_fd *bfd, unsigned int what)
{
struct bsc_msc_rf_conn *conn;
struct bsc_msc_rf *rf = bfd->data;
struct osmo_bsc_rf_conn *conn;
struct osmo_bsc_rf *rf = bfd->data;
struct sockaddr_un addr;
socklen_t len = sizeof(addr);
int fd;
@@ -152,7 +168,7 @@ static int rf_ctl_accept(struct bsc_fd *bfd, unsigned int what)
return -1;
}
conn = talloc_zero(rf, struct bsc_msc_rf_conn);
conn = talloc_zero(rf, struct osmo_bsc_rf_conn);
if (!conn) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate mem.\n");
close(fd);
@@ -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);
@@ -176,17 +192,17 @@ static int rf_ctl_accept(struct bsc_fd *bfd, unsigned int what)
return 0;
}
struct bsc_msc_rf *bsc_msc_rf_create(const char *path, struct gsm_network *net)
struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net)
{
unsigned int namelen;
struct sockaddr_un local;
struct bsc_fd *bfd;
struct bsc_msc_rf *rf;
struct osmo_bsc_rf *rf;
int rc;
rf = talloc_zero(NULL, struct bsc_msc_rf);
rf = talloc_zero(NULL, struct osmo_bsc_rf);
if (!rf) {
LOGP(DINP, LOGL_ERROR, "Failed to create bsc_msc_rf.\n");
LOGP(DINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n");
return NULL;
}
@@ -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

@@ -381,7 +381,7 @@ void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr,
break;
/* Stop paging */
if (bts != _bts)
if (bts != _bts)
_paging_request_stop(bts, subscr, NULL);
} while (1);
}

View File

@@ -316,8 +316,16 @@ static int append_gprs_cell_opt(struct bitvec *bv,
bitvec_set_bit(bv, 1);
bitvec_set_uint(bv, gco->bs_cv_max, 4);
/* hard-code no PAN_{DEC,INC,MAX} */
bitvec_set_bit(bv, 0);
if (0) {
/* hard-code no PAN_{DEC,INC,MAX} */
bitvec_set_bit(bv, 0);
} else {
/* copied from ip.access BSC protocol trace */
bitvec_set_bit(bv, 1);
bitvec_set_uint(bv, 1, 3); /* DEC */
bitvec_set_uint(bv, 1, 3); /* INC */
bitvec_set_uint(bv, 15, 3); /* MAX */
}
if (!gco->ext_info_present) {
/* no extension information */

File diff suppressed because it is too large Load Diff

View File

@@ -397,17 +397,17 @@ static int generate_si6(u_int8_t *output, struct gsm_bts *bts)
static struct gsm48_si13_info si13_default = {
.cell_opts = {
.nmo = GPRS_NMO_III,
.t3168 = 1500,
.t3192 = 500,
.nmo = GPRS_NMO_II,
.t3168 = 2000,
.t3192 = 200,
.drx_timer_max = 3,
.bs_cv_max = 15,
.ext_info_present = 0,
.ext_info_present = 1,
.ext_info = {
/* The values below are just guesses ! */
.egprs_supported = 0,
.use_egprs_p_ch_req = 1,
.bep_period = 4,
.bep_period = 5,
.pfc_supported = 0,
.dtm_supported = 0,
.bss_paging_coordination = 0,
@@ -415,10 +415,10 @@ static struct gsm48_si13_info si13_default = {
},
.pwr_ctrl_pars = {
.alpha = 10, /* a = 1.0 */
.t_avg_w = 25,
.t_avg_t = 25,
.t_avg_w = 16,
.t_avg_t = 16,
.pc_meas_chan = 0, /* downling measured on CCCH */
.n_avg_i = 15,
.n_avg_i = 8,
},
.bcch_change_mark = 1,
.si_change_field = 0,
@@ -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,12 +1949,10 @@ 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;
}
case OML_NODE:
vty->node = ENABLE_NODE;
talloc_free(vty->index);
vty->index = NULL;
break;
default:
vty->node = CONFIG_NODE;
@@ -2373,15 +2370,12 @@ DEFUN(config_exit,
case MGCP_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
case NAT_NODE:
vty->node = CONFIG_NODE;
break;
case OML_NODE:
vty->node = ENABLE_NODE;
talloc_free(vty->index);
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
*
@@ -27,6 +27,7 @@
#include <vty/vty.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <osmocore/linuxlist.h>
#include <openbsc/gsm_data.h>
@@ -44,6 +45,32 @@
static struct gsm_network *gsmnet;
static struct value_string gprs_ns_timer_strs[] = {
{ 0, "tns-block" },
{ 1, "tns-block-retries" },
{ 2, "tns-reset" },
{ 3, "tns-reset-retries" },
{ 4, "tns-test" },
{ 5, "tns-alive" },
{ 6, "tns-alive-retries" },
{ 0, NULL }
};
static struct value_string gprs_bssgp_cfg_strs[] = {
{ 0, "blocking-timer" },
{ 1, "blocking-retries" },
{ 2, "unblocking-retries" },
{ 3, "reset-timer" },
{ 4, "reset-retries" },
{ 5, "suspend-timer" },
{ 6, "suspend-retries" },
{ 7, "resume-timer" },
{ 8, "resume-retries" },
{ 9, "capability-update-timer" },
{ 10, "capability-update-retries" },
{ 0, NULL }
};
struct cmd_node net_node = {
GSMNET_NODE,
"%s(network)#",
@@ -321,10 +348,48 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
config_write_ts_single(vty, &trx->ts[i]);
}
static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts)
{
unsigned int i;
vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode),
VTY_NEWLINE);
if (bts->gprs.mode == BTS_GPRS_NONE)
return;
vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
VTY_NEWLINE);
vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++)
vty_out(vty, " gprs cell timer %s %u%s",
get_value_string(gprs_bssgp_cfg_strs, i),
bts->gprs.cell.timer[i], VTY_NEWLINE);
vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei,
VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++)
vty_out(vty, " gprs ns timer %s %u%s",
get_value_string(gprs_ns_timer_strs, i),
bts->gprs.nse.timer[i], VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
struct gsm_bts_gprs_nsvc *nsvc =
&bts->gprs.nsvc[i];
struct in_addr ia;
ia.s_addr = htonl(nsvc->remote_ip);
vty_out(vty, " gprs nsvc %u nsvci %u%s", i,
nsvc->nsvci, VTY_NEWLINE);
vty_out(vty, " gprs nsvc %u local udp port %u%s", i,
nsvc->local_port, VTY_NEWLINE);
vty_out(vty, " gprs nsvc %u remote udp port %u%s", i,
nsvc->remote_port, VTY_NEWLINE);
vty_out(vty, " gprs nsvc %u remote ip %s%s", i,
inet_ntoa(ia), VTY_NEWLINE);
}
}
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
{
struct gsm_bts_trx *trx;
int i;
vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE);
@@ -359,6 +424,8 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
bts->rach_ldavg_slots, VTY_NEWLINE);
if (bts->si_common.rach_control.cell_bar)
vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
if ((bts->si_common.rach_control.t2 & 0x4) == 0)
vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE);
if (is_ipaccess_bts(bts)) {
vty_out(vty, " ip.access unit_id %u %u%s",
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
@@ -368,35 +435,12 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
}
config_write_bts_gprs(vty, bts);
/* if we have a limit, write it */
if (bts->paging.free_chans_need >= 0)
vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE);
vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode),
VTY_NEWLINE);
if (bts->gprs.mode != BTS_GPRS_NONE) {
vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
VTY_NEWLINE);
vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
VTY_NEWLINE);
vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei,
VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
struct gsm_bts_gprs_nsvc *nsvc =
&bts->gprs.nsvc[i];
struct in_addr ia;
ia.s_addr = htonl(nsvc->remote_ip);
vty_out(vty, " gprs nsvc %u nsvci %u%s", i,
nsvc->nsvci, VTY_NEWLINE);
vty_out(vty, " gprs nsvc %u local udp port %u%s", i,
nsvc->local_port, VTY_NEWLINE);
vty_out(vty, " gprs nsvc %u remote udp port %u%s", i,
nsvc->remote_port, VTY_NEWLINE);
vty_out(vty, " gprs nsvc %u remote ip %s%s", i,
inet_ntoa(ia), VTY_NEWLINE);
}
}
llist_for_each_entry(trx, &bts->trx_list, list)
config_write_trx_single(vty, trx);
@@ -456,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);
@@ -480,6 +525,14 @@ 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-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);
if (gsmnet->ussd_welcome_txt)
vty_out(vty, " bsc-welcome-text %s%s", gsmnet->ussd_welcome_txt, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -677,7 +730,7 @@ static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr,
meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul");
}
static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
{
int idx;
@@ -712,37 +765,41 @@ static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " ");
}
#if 0
TODO: callref and remote callref of call must be resolved to get gsm_trans object
static void call_dump_vty(struct vty *vty, struct gsm_call *call)
static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan)
{
vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s",
call->type, call->state, call->transaction_id, VTY_NEWLINE);
struct gsm_meas_rep *mr;
int idx;
if (call->local_lchan) {
vty_out(vty, "Call Local Channel:%s", VTY_NEWLINE);
lchan_dump_vty(vty, call->local_lchan);
} else
vty_out(vty, "Call has no Local Channel%s", VTY_NEWLINE);
/* we want to report the last measurement report */
idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
lchan->meas_rep_idx, 1);
mr = &lchan->meas_rep[idx];
if (call->remote_lchan) {
vty_out(vty, "Call Remote Channel:%s", VTY_NEWLINE);
lchan_dump_vty(vty, call->remote_lchan);
} else
vty_out(vty, "Call has no Remote Channel%s", VTY_NEWLINE);
if (call->called_subscr) {
vty_out(vty, "Called Subscriber:%s", VTY_NEWLINE);
subscr_dump_vty(vty, call->called_subscr);
} else
vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE);
vty_out(vty, "Lchan: %u Timeslot: %u TRX: %u BTS: %u Type: %s - L1 MS Power: %u dBm "
"RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s",
lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type),
mr->ms_l1.pwr,
rxlev2dbm(mr->dl.full.rx_lev),
rxlev2dbm(mr->ul.full.rx_lev),
VTY_NEWLINE);
}
#endif
DEFUN(show_lchan,
show_lchan_cmd,
"show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
SHOW_STR "Display information about a logical channel\n")
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 *))
{
struct gsm_network *net = gsmnet;
struct gsm_bts *bts;
@@ -787,7 +844,7 @@ DEFUN(show_lchan,
return CMD_WARNING;
}
lchan = &ts->lchan[lchan_nr];
lchan_dump_vty(vty, lchan);
dump_cb(vty, lchan);
return CMD_SUCCESS;
}
for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
@@ -801,7 +858,7 @@ DEFUN(show_lchan,
lchan = &ts->lchan[lchan_nr];
if (lchan->type == GSM_LCHAN_NONE)
continue;
lchan_dump_vty(vty, lchan);
dump_cb(vty, lchan);
}
}
}
@@ -810,6 +867,31 @@ DEFUN(show_lchan,
return CMD_SUCCESS;
}
DEFUN(show_lchan,
show_lchan_cmd,
"show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
SHOW_STR "Display information about a logical channel\n")
{
return lchan_summary(vty, argc, argv, lchan_dump_full_vty);
}
DEFUN(show_lchan_summary,
show_lchan_summary_cmd,
"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);
@@ -1317,7 +1399,7 @@ DEFUN(cfg_net_pag_any_tch,
DEFUN(cfg_net_msc_ip,
cfg_net_msc_ip_cmd,
"msc ip IP",
"msc ip A.B.C.D",
"Set the MSC/MUX IP address.")
{
if (gsmnet->msc_ip)
@@ -1335,6 +1417,68 @@ DEFUN(cfg_net_msc_port,
return CMD_SUCCESS;
}
DEFUN(cfg_net_msc_prio,
cfg_net_msc_prio_cmd,
"msc ip-dscp <0-255>",
"Set the IP_TOS socket attribite")
{
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",
"Set the PING interval, negative for not sending PING")
{
gsmnet->ping_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_pong_time,
cfg_net_pong_time_cmd,
"timeout pong NR",
"Set the time to wait for a PONG.")
{
gsmnet->pong_timeout = atoi(argv[0]);
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;
}
DEFUN(cfg_net_welcome_ussd,
cfg_net_welcome_ussd_cmd,
"bsc-welcome-text .TEXT",
"Set the USSD notification to be sent.\n" "Text to be sent\n")
{
char *data = argv_concat(argv, argc, 1);
if (!data)
return CMD_WARNING;
if (gsmnet->ussd_welcome_txt)
talloc_free(gsmnet->ussd_welcome_txt);
gsmnet->ussd_welcome_txt = talloc_strdup(gsmnet, data);
talloc_free(data);
return CMD_SUCCESS;
}
#define DECLARE_TIMER(number, doc) \
DEFUN(cfg_net_T##number, \
@@ -1366,6 +1510,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,
@@ -1642,6 +1796,20 @@ DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_rach_ec_allowed, cfg_bts_rach_ec_allowed_cmd,
"rach emergency call allowed (0|1)",
"Should this cell allow emergency calls?")
{
struct gsm_bts *bts = vty->index;
if (atoi(argv[0]) == 0)
bts->si_common.rach_control.t2 |= 0x4;
else
bts->si_common.rach_control.t2 &= ~0x4;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd,
"ms max power <0-40>",
"Maximum transmit power of the MS")
@@ -1789,13 +1957,61 @@ DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd,
"paging free FREE_NR",
"Only page when having a certain amount of free slots. -1 to disable")
#define GPRS_TEXT "GPRS Packet Network\n"
#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries)"
#define NS_TIMERS_HELP \
"(un)blocking Timer (Tns-block) timeout\n" \
"(un)blocking Timer (Tns-block) number of retries\n" \
"Reset Timer (Tns-reset) timeout\n" \
"Reset Timer (Tns-reset) number of retries\n" \
"Test Timer (Tns-test) timeout\n" \
DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd,
"gprs ns timer " NS_TIMERS " <0-255>",
GPRS_TEXT "Network Service\n"
"Network Service Timer\n"
NS_TIMERS_HELP "Timer Value\n")
{
struct gsm_bts *bts = vty->index;
int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
int val = atoi(argv[1]);
if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer))
return CMD_WARNING;
bts->gprs.nse.timer[idx] = val;
return CMD_SUCCESS;
}
#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)"
#define BSSGP_TIMERS_HELP ""
DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd,
"gprs cell timer " BSSGP_TIMERS " <0-255>",
GPRS_TEXT "Cell / BSSGP\n"
"Cell/BSSGP Timer\n"
BSSGP_TIMERS_HELP "Timer Value\n")
{
struct gsm_bts *bts = vty->index;
int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]);
int val = atoi(argv[1]);
if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer))
return CMD_WARNING;
bts->gprs.cell.timer[idx] = val;
bts->paging.free_chans_need = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1826,6 +2042,15 @@ DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd,
"paging free FREE_NR",
"Only page when having a certain amount of free slots. -1 to disable")
{
struct gsm_bts *bts = vty->index;
bts->paging.free_chans_need = atoi(argv[0]);
return CMD_SUCCESS;
}
/* per TRX configuration */
DEFUN(cfg_trx,
@@ -2018,6 +2243,8 @@ int bsc_vty_init(struct gsm_network *net)
install_element(VIEW_NODE, &show_trx_cmd);
install_element(VIEW_NODE, &show_ts_cmd);
install_element(VIEW_NODE, &show_lchan_cmd);
install_element(VIEW_NODE, &show_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);
@@ -2066,10 +2293,17 @@ 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_net_welcome_ussd_cmd);
install_element(GSMNET_NODE, &cfg_bts_cmd);
install_node(&bts_node, config_write_bts);
@@ -2090,13 +2324,16 @@ int bsc_vty_init(struct gsm_network *net)
install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd);
install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd);
install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd);
install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd);
install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd);
@@ -2120,6 +2357,8 @@ int bsc_vty_init(struct gsm_network *net)
install_element(TS_NODE, &cfg_ts_pchan_cmd);
install_element(TS_NODE, &cfg_ts_e1_subslot_cmd);
abis_nm_vty_init();
bsc_vty_init_extra(net);
return 0;

View File

@@ -27,9 +27,10 @@
#include <vty/vty.h>
#include <openbsc/gsm_data.h>
#include <openbsc/bsc_msc.h>
#include <openbsc/vty.h>
#include <sccp/sccp.h>
#include <osmocom/sccp/sccp.h>
static struct gsm_network *gsmnet = NULL;
@@ -42,7 +43,7 @@ DEFUN(show_bsc, show_bsc_cmd, "show bsc",
vty_out(vty, "BSC Information%s", VTY_NEWLINE);
llist_for_each_entry(con, bsc_sccp_connections(), active_connections) {
vty_out(vty, " Connection: LCHAN: %p sec LCHAN: %p SCCP src: %d dest: %d%s",
vty_out(vty, " Connection: LCHAN: %p sec LCHAN: 0x%p SCCP src: 0x%x dest: 0x%x%s",
con->lchan, con->secondary_lchan,
con->sccp ? (int) sccp_src_ref_to_int(&con->sccp->source_local_reference) : -1,
con->sccp ? (int) sccp_src_ref_to_int(&con->sccp->destination_local_reference) : -1,
@@ -63,6 +64,24 @@ DEFUN(show_stats,
return CMD_SUCCESS;
}
DEFUN(show_msc,
show_msc_cmd,
"show msc connection",
SHOW_STR "Show the status of the MSC connection.")
{
if (!gsmnet->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",
gsmnet->msc_con->ip, gsmnet->msc_con->port,
gsmnet->msc_con->is_connected, VTY_NEWLINE);
return CMD_SUCCESS;
}
int bsc_vty_init_extra(struct gsm_network *net)
{
gsmnet = net;
@@ -70,6 +89,7 @@ int bsc_vty_init_extra(struct gsm_network *net)
/* get runtime information */
install_element(VIEW_NODE, &show_bsc_cmd);
install_element(VIEW_NODE, &show_stats_cmd);
install_element(VIEW_NODE, &show_msc_cmd);
return 0;
}

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

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)

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,572 +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,
};
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()
{
int rc;
struct bsc_nat *nat;
struct bsc_connection *con;
struct sccp_connections *con_found;
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 = create_sccp_src_ref(con, msg, parsed);
if (rc != 0) {
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 (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();
}
}
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();
return 0;
}
void input_event()
{}
int nm_state_event()
{
return -1;
}
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
{
return -1;
}

View File

@@ -1,8 +0,0 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS)
noinst_PROGRAMS = sccp_test
sccp_test_SOURCES = sccp_test.c
sccp_test_LDADD = $(top_builddir)/src/libsccp.a $(top_builddir)/src/libbsc.a $(LIBOSMOCORE_LIBS)

View File

@@ -1,851 +0,0 @@
/*
* SCCP testing code
*
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009 by On-Waves
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdio.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <osmocore/msgb.h>
#include <sccp/sccp.h>
#define MIN(x, y) ((x) < (y) ? (x) : (y))
/* BSC -> MSC */
static const u_int8_t bssmap_reset[] = {
0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
0x01, 0x20,
};
/* MSC -> BSC reset ack */
static const u_int8_t bssmap_reset_ack[] = {
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
0x00, 0x01, 0x31,
};
/* MSC -> BSC paging, connection less */
static const u_int8_t bssmap_paging[] = {
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10,
0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10,
0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06,
};
/* MSC -> BSC paging, UDT without PC */
static const u_int8_t bssmap_udt[] = {
0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
0x02, 0x42, 0xfe, 0x10, 0x00, 0x0e, 0x52, 0x08,
0x08, 0x29, 0x47, 0x10, 0x02, 0x01, 0x31, 0x97,
0x61, 0x1a, 0x01, 0x06,
};
/* BSC -> MSC connection open */
static const u_int8_t bssmap_cr[] = {
0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02,
0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05,
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3,
0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33,
0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01,
0x31, 0x97, 0x61, 0x00
};
/* MSC -> BSC connection confirm */
static const u_int8_t bssmap_cc[] = {
0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00,
};
/* MSC -> BSC DTAP
*
* we fake a bit and make it BSC -> MSC... so the
* payload does not make any sense..
*/
static const u_int8_t bssmap_dtap[] = {
0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x0f, 0x01, 0x00, 0x0c,
0x03, 0x05, 0x5c, 0x08, 0x11, 0x81, 0x33, 0x66, 0x02, 0x13,
0x45, 0xf4,
};
/* MSC -> BSC clear command */
static const u_int8_t bssmap_clear[] = {
0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x06, 0x00, 0x04, 0x20,
0x04, 0x01, 0x09,
};
/* MSC -> BSC released */
static const u_int8_t bssmap_released[] = {
0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f,
0x02, 0x23, 0x42, 0x00,
};
/* BSC -> MSC released */
static const u_int8_t bssmap_release_complete[] = {
0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03
};
struct test_data {
int length;
const u_int8_t *data;
int payload_start;
int payload_length;
u_int8_t first_byte;
/* in case it should trigger a sccp response */
int write;
const u_int8_t *response;
int response_length;
};
static const struct test_data test_data[] = {
{
.length = ARRAY_SIZE(bssmap_reset),
.data = &bssmap_reset[0],
.payload_start = 12,
.payload_length = ARRAY_SIZE(bssmap_reset) - 12,
.first_byte = 0x0,
},
{
.length = ARRAY_SIZE(bssmap_reset_ack),
.data = &bssmap_reset_ack[0],
.payload_start = 16,
.payload_length = ARRAY_SIZE(bssmap_reset_ack) - 16,
.first_byte = 0x0,
},
{
.length = ARRAY_SIZE(bssmap_paging),
.data = &bssmap_paging[0],
.payload_start = 16,
.payload_length = ARRAY_SIZE(bssmap_paging) - 16,
.first_byte = 0x0,
},
{
.length = ARRAY_SIZE(bssmap_cr),
.data = &bssmap_cr[0],
.payload_start = 12,
/* 0x00 is end of optional data, subtract this byte */
.payload_length = 31,
.first_byte = 0x0,
/* the connection request should trigger a connection confirm */
.write = 1,
.response = &bssmap_cc[0],
.response_length= ARRAY_SIZE(bssmap_cc),
},
{
.length = ARRAY_SIZE(bssmap_dtap),
.data = &bssmap_dtap[0],
.payload_start = 7,
.payload_length = 15,
.first_byte = 0x01,
},
{
.length = ARRAY_SIZE(bssmap_clear),
.data = &bssmap_clear[0],
.payload_start = 7,
.payload_length = 6,
.first_byte = 0x00,
},
{
.length = ARRAY_SIZE(bssmap_released),
.data = &bssmap_released[0],
.payload_length = 2,
.payload_start = 11,
.first_byte = 0x23,
.write = 1,
.response = &bssmap_release_complete[0],
.response_length= ARRAY_SIZE(bssmap_release_complete),
},
};
/* we will send UDTs and verify they look like this */
static const struct test_data send_data[] = {
{
.length = ARRAY_SIZE(bssmap_udt),
.data = &bssmap_udt[0],
.payload_start = 12,
.payload_length = ARRAY_SIZE(bssmap_udt) - 12,
.first_byte = 0x0,
},
{
.length = ARRAY_SIZE(bssmap_reset),
.data = &bssmap_reset[0],
.payload_start = 12,
.payload_length = ARRAY_SIZE(bssmap_reset) - 12,
.first_byte = 0x0,
},
};
struct connection_test {
/* should the connection be refused? */
int refuse;
int with_data;
/* on which side to close the connection? */
int close_side;
int close_cause;
};
/* sccp connection handling we want to test */
static const struct connection_test connection_tests[] = {
{
.refuse = 1,
},
{
.refuse = 1,
.with_data = 1,
},
{
.refuse = 0,
.close_side = 0,
.close_cause = 5,
},
{
.refuse = 0,
.close_side = 0,
.close_cause = 5,
.with_data = 1,
},
{
.refuse = 0,
.close_side = 1,
.close_cause = 5,
},
{
.refuse = 0,
.close_side = 1,
.close_cause = 5,
.with_data = 1,
},
};
struct sccp_parse_header_result {
/* results */
int msg_type;
int wanted_len;
int src_ssn;
int dst_ssn;
int has_src_ref, has_dst_ref;
struct sccp_source_reference src_ref;
struct sccp_source_reference dst_ref;
/* the input */
const u_int8_t *input;
int input_len;
};
static const u_int8_t it_test[] = {
0x10, 0x01, 0x07,
0x94, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00 };
static const u_int8_t proto_err[] = {
0x0f, 0x0c, 0x04, 0x00, 0x00,
};
static const struct sccp_parse_header_result parse_result[] = {
{
.msg_type = SCCP_MSG_TYPE_IT,
.wanted_len = 0,
.src_ssn = -1,
.dst_ssn = -1,
.has_src_ref = 1,
.has_dst_ref = 1,
.src_ref = {
.octet1 = 0x01,
.octet2 = 0x04,
.octet3 = 0x00
},
.dst_ref = {
.octet1 = 0x01,
.octet2 = 0x07,
.octet3 = 0x94,
},
.input = it_test,
.input_len = sizeof(it_test),
},
{
.msg_type = SCCP_MSG_TYPE_ERR,
.wanted_len = 0,
.src_ssn = -1,
.dst_ssn = -1,
.has_src_ref = 0,
.has_dst_ref = 1,
.dst_ref = {
.octet1 = 0x0c,
.octet2 = 0x04,
.octet3 = 0x00,
},
.input = proto_err,
.input_len = sizeof(proto_err),
},
};
/* testing procedure:
* - we will use sccp_write and see what will be set in the
* outgoing callback
* - we will call sccp_system_incoming and see which calls
* are made. And then compare it to the ones we expect. We
* want the payload to arrive, or callbacks to be called.
* - we will use sccp_connection_socket and sccp_connection_write
* and verify state handling of connections
*/
static int current_test;
/*
* test state...
*/
static int called = 0;
static int matched = 0;
static int write_called = 0;
#define FAIL(x, args...) printf("FAILURE in %s:%d: " x, __FILE__, __LINE__, ## args)
/*
* writing these packets and expecting a result
*/
int sccp_read_cb(struct msgb *data, unsigned len, void *context)
{
u_int16_t payload_length = test_data[current_test].payload_length;
const u_int8_t *got, *wanted;
int i;
called = 1;
if (msgb_l3len(data) < len) {
/* this should never be reached */
FAIL("Something horrible happened.. invalid packet..\n");
exit(-1);
}
if (len == 0 || len != payload_length) {
FAIL("length mismatch: got: %d wanted: %d\n", msgb_l3len(data), payload_length);
return -1;
}
if (data->l3h[0] != test_data[current_test].first_byte) {
FAIL("The first bytes of l3 do not match: 0x%x 0x%x\n",
data->l3h[0], test_data[current_test].first_byte);
return -1;
}
got = &data->l3h[0];
wanted = test_data[current_test].data + test_data[current_test].payload_start;
for (i = 0; i < len; ++i) {
if (got[i] != wanted[i]) {
FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
got[i], wanted[i], i);
return -1;
}
}
matched = 1;
return 0;
}
void sccp_write_cb(struct msgb *data, void *ctx)
{
int i = 0;
const u_int8_t *got, *wanted;
if (test_data[current_test].response == NULL) {
FAIL("Didn't expect write callback\n");
goto exit;
} else if (test_data[current_test].response_length != msgb_l2len(data)) {
FAIL("Size does not match. Got: %d Wanted: %d\n",
msgb_l2len(data), test_data[current_test].response_length);
}
got = &data->l2h[0];
wanted = test_data[current_test].response;
for (i = 0; i < msgb_l2len(data); ++i) {
if (got[i] != wanted[i]) {
FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
got[i], wanted[i], i);
goto exit;
}
}
write_called = 1;
exit:
msgb_free(data);
}
void sccp_c_read(struct sccp_connection *connection, struct msgb *msgb, unsigned int len)
{
sccp_read_cb(msgb, len, connection->data_ctx);
}
void sccp_c_state(struct sccp_connection *connection, int old_state)
{
if (connection->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE)
sccp_connection_free(connection);
}
int sccp_accept_cb(struct sccp_connection *connection, void *user_data)
{
called = 1;
unsigned int ref = 0;
ref |= connection->destination_local_reference.octet1 << 24;
ref |= connection->destination_local_reference.octet2 << 16;
ref |= connection->destination_local_reference.octet3 << 8;
ref = ntohl(ref);
connection->data_cb = sccp_c_read;
connection->state_cb = sccp_c_state;
/* accept this */
return 0;
}
static void sccp_udt_write_cb(struct msgb *data, void *context)
{
const u_int8_t *got, *wanted;
int i;
write_called = 1;
if (send_data[current_test].length != msgb_l2len(data)) {
FAIL("Size does not match. Got: %d Wanted: %d\n",
msgb_l2len(data), send_data[current_test].length);
goto exit;
}
got = &data->l2h[0];
wanted = send_data[current_test].data;
for (i = 0; i < msgb_l2len(data); ++i) {
if (got[i] != wanted[i]) {
FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
got[i], wanted[i], i);
goto exit;
}
}
matched = 1;
exit:
msgb_free(data);
}
static void test_sccp_system(void)
{
sccp_system_init(sccp_write_cb, NULL);
sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL);
sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL);
for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) {
unsigned int length = test_data[current_test].length;
struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__);
msg->l2h = msgb_put(msg, length);
memcpy(msg->l2h, test_data[current_test].data, length);
called = matched = write_called = 0;
printf("Testing packet: %d\n", current_test);
sccp_system_incoming(msg);
if (!called || !matched || (test_data[current_test].write != write_called))
FAIL("current test: %d called: %d matched: %d write: %d\n",
current_test, called, matched, write_called);
msgb_free(msg);
}
}
/* test sending of udt */
static void test_sccp_send_udt(void)
{
sccp_system_init(sccp_udt_write_cb, NULL);
sccp_set_read(NULL, NULL, NULL);
sccp_connection_set_incoming(NULL, NULL, NULL);
for (current_test = 0; current_test < ARRAY_SIZE(send_data); ++current_test) {
const struct test_data *test = &send_data[current_test];
struct msgb *msg = msgb_alloc(test->payload_length, __func__);
msg->l3h = msgb_put(msg, test->payload_length);
memcpy(msg->l3h, test->data + test->payload_start, test->payload_length);
matched = write_called = 0;
printf("Testing packet: %d\n", current_test);
sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0);
if (!matched || !write_called)
FAIL("current test: %d matched: %d write: %d\n",
current_test, matched, write_called);
msgb_free(msg);
}
}
/* send udt from one end to another */
static unsigned int test_value = 0x2442;
static int sccp_udt_read(struct msgb *data, unsigned int len, void *context)
{
unsigned int *val;
if (len != 4) {
FAIL("Wrong size: %d\n", msgb_l3len(data));
return -1;
}
val = (unsigned int*)data->l3h;
matched = test_value == *val;
return 0;
}
static void sccp_write_loop(struct msgb *data, void *context)
{
/* send it back to us */
sccp_system_incoming(data);
msgb_free(data);
}
static void test_sccp_udt_communication(void)
{
struct msgb *data;
unsigned int *val;
sccp_system_init(sccp_write_loop, NULL);
sccp_set_read(&sccp_ssn_bssap, sccp_udt_read, NULL);
sccp_connection_set_incoming(NULL, NULL, NULL);
data = msgb_alloc(4, "test data");
data->l3h = &data->data[0];
val = (unsigned int *)msgb_put(data, 4);
*val = test_value;
matched = 0;
sccp_write(data, &sccp_ssn_bssap, &sccp_ssn_bssap, 0);
if (!matched)
FAIL("Talking with us didn't work\n");
msgb_free(data);
}
/* connection testing... open, send, close */
static const struct connection_test *current_con_test;
static struct sccp_connection *outgoing_con;
static struct sccp_connection *incoming_con;
static int outgoing_data, incoming_data, incoming_state, outgoing_state;
static struct msgb *test_data1, *test_data2, *test_data3;
static void sccp_conn_in_state(struct sccp_connection *conn, int old_state)
{
printf("\tincome: %d -> %d\n", old_state, conn->connection_state);
if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
if (conn == incoming_con) {
sccp_connection_free(conn);
incoming_con = NULL;
}
}
}
static void sccp_conn_in_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len)
{
/* compare the data */
++incoming_data;
printf("\tincoming data: %d\n", len);
/* compare the data */
if (len != 4) {
FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len);
return;
}
if (incoming_data == 1) {
if (memcmp(msg->l3h, test_data1->l3h, len) != 0) {
FAIL("Comparing the data failed: %d\n", incoming_data);
incoming_state = 0;
printf("Got: %s\n", hexdump(msg->l3h, len));
printf("Wanted: %s\n", hexdump(test_data1->l3h, len));
}
} else if (incoming_data == 2) {
if (memcmp(msg->l3h, test_data2->l3h, len) != 0) {
FAIL("Comparing the data failed: %d\n", incoming_data);
incoming_state = 0;
printf("Got: %s\n", hexdump(msg->l3h, len));
printf("Wanted: %s\n", hexdump(test_data2->l3h, len));
}
}
/* sending out data */
if (incoming_data == 2) {
printf("\tReturning data3\n");
sccp_connection_write(conn, test_data3);
}
}
static int sccp_conn_accept(struct sccp_connection *conn, void *ctx)
{
printf("\taccept: %p\n", conn);
conn->state_cb = sccp_conn_in_state;
conn->data_cb = sccp_conn_in_data;
if (current_con_test->refuse)
return -1;
incoming_con = conn;
return 0;
}
/* callbacks for the outgoing side */
static void sccp_conn_out_state(struct sccp_connection *conn, int old_state)
{
printf("\toutgoing: %p %d -> %d\n", conn, old_state, conn->connection_state);
if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
if (conn == outgoing_con) {
sccp_connection_free(conn);
outgoing_con = NULL;
}
}
}
static void sccp_conn_out_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len)
{
++outgoing_data;
printf("\toutgoing data: %p %d\n", conn, len);
if (len != 4)
FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len);
if (outgoing_data == 1) {
if (memcmp(msg->l3h, test_data3->l3h, len) != 0) {
FAIL("Comparing the data failed\n");
outgoing_state = 0;
}
}
}
static void do_test_sccp_connection(const struct connection_test *test)
{
int ret;
current_con_test = test;
outgoing_con = incoming_con = 0;
outgoing_con = sccp_connection_socket();
if (!outgoing_con) {
FAIL("Connection is NULL\n");
return;
}
outgoing_con->state_cb = sccp_conn_out_state;
outgoing_con->data_cb = sccp_conn_out_data;
outgoing_data = incoming_data = 0;
incoming_state = outgoing_state = 1;
/* start testing */
if (test->with_data) {
if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, test_data1) != 0)
FAIL("Binding failed\n");
} else {
++incoming_data;
if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, NULL) != 0)
FAIL("Binding failed\n");
}
if (test->refuse) {
if (outgoing_con)
FAIL("Outgoing connection should have been refused.\n");
} else {
if (!incoming_con)
FAIL("Creating incoming didn't work.\n");
printf("\tWriting test data2\n");
sccp_connection_write(outgoing_con, test_data2);
sccp_connection_send_it(outgoing_con);
/* closing connection */
if (test->close_side == 0)
ret = sccp_connection_close(outgoing_con, 0);
else
ret = sccp_connection_close(incoming_con, 0);
if (ret != 0)
FAIL("Closing the connection failed\n");
}
/* outgoing should be gone now */
if (outgoing_con)
FAIL("Outgoing connection was not properly closed\n");
if (incoming_con)
FAIL("Incoming connection was not propery closed.\n");
if (test->refuse == 0) {
if (outgoing_data != 1 || incoming_data != 2) {
FAIL("Data sending failed: %d/%d %d/%d\n",
outgoing_data, 1,
incoming_data, 2);
}
}
if (!incoming_state || !outgoing_state)
FAIL("Failure with the state transition. %d %d\n",
outgoing_state, incoming_state);
}
static void test_sccp_connection(void)
{
sccp_system_init(sccp_write_loop, NULL);
sccp_set_read(NULL, NULL, NULL);
sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_conn_accept, NULL);
test_data1 = msgb_alloc(4, "data1");
test_data1->l3h = msgb_put(test_data1, 4);
*((unsigned int*)test_data1->l3h) = 0x23421122;
test_data2 = msgb_alloc(4, "data2");
test_data2->l3h = msgb_put(test_data2, 4);
*((unsigned int*)test_data2->l3h) = 0x42232211;
test_data3 = msgb_alloc(4, "data3");
test_data3->l3h = msgb_put(test_data3, 4);
*((unsigned int*)test_data3->l3h) = 0x2323ff55;
for (current_test = 0; current_test < ARRAY_SIZE(connection_tests); ++current_test) {
printf("Testing %d refuse: %d with_data: %d\n",
current_test, connection_tests[current_test].refuse,
connection_tests[current_test].with_data);
do_test_sccp_connection(&connection_tests[current_test]);
}
msgb_free(test_data1);
msgb_free(test_data2);
msgb_free(test_data3);
}
/* invalid input */
static void test_sccp_system_crash(void)
{
printf("trying to provoke a crash with invalid input\n");
sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL);
sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL);
for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) {
int original_length = test_data[current_test].length;
int length = original_length + 2;
int i;
printf("Testing packet: %d\n", current_test);
for (i = length; i >= 0; --i) {
unsigned int length = MIN(test_data[current_test].length, i);
struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__);
msg->l2h = msgb_put(msg, length);
memcpy(msg->l2h, test_data[current_test].data, length);
sccp_system_incoming(msg);
msgb_free(msg);
}
}
printf("survived\n");
}
static void test_sccp_parsing(void)
{
for (current_test = 0; current_test < ARRAY_SIZE(parse_result); ++current_test) {
struct msgb *msg;
struct sccp_parse_result result;
msg = msgb_alloc_headroom(1024, 128, "parse-test");
msgb_put(msg, 1);
msg->l2h = msgb_put(msg, parse_result[current_test].input_len);
memcpy(msg->l2h, parse_result[current_test].input, msgb_l2len(msg));
memset(&result, 0, sizeof(result));
if (sccp_parse_header(msg, &result) != 0) {
fprintf(stderr, "Failed to sccp parse test: %d\n", current_test);
} else {
if (parse_result[current_test].wanted_len != result.data_len) {
fprintf(stderr, "Unexpected data length.\n");
abort();
}
if (parse_result[current_test].has_src_ref) {
if (memcmp(result.source_local_reference,
&parse_result[current_test].src_ref,
sizeof(struct sccp_source_reference)) != 0) {
fprintf(stderr, "SRC REF did not match\n");
abort();
}
}
if (parse_result[current_test].has_dst_ref) {
if (memcmp(result.destination_local_reference,
&parse_result[current_test].dst_ref,
sizeof(struct sccp_source_reference)) != 0) {
fprintf(stderr, "DST REF did not match\n");
abort();
}
}
if (parse_result[current_test].src_ssn != -1) {
fprintf(stderr, "Not implemented.\n");
abort();
}
if (parse_result[current_test].dst_ssn != -1) {
fprintf(stderr, "Not implemented.\n");
abort();
}
}
msgb_free(msg);
}
}
int main(int argc, char **argv)
{
test_sccp_system();
test_sccp_send_udt();
test_sccp_udt_communication();
test_sccp_connection();
test_sccp_system_crash();
test_sccp_parsing();
return 0;
}
void db_store_counter() {}