Compare commits

...

131 Commits

Author SHA1 Message Date
Holger Hans Peter Freyther
8ce50ba2f7 rf: Delay execution of commands
Delay executing RF commands up to a second. If many commands
arrive within a second then just execute the last command.
2011-02-14 23:41:42 +01:00
Holger Hans Peter Freyther
3f9b6a6539 rf: Verify that the requested mode is entered and drop OML in error
Verify that the BTS is following our orders, if we think there was
an error we will drop the OML connection.
2011-02-14 23:27:18 +01:00
Holger Hans Peter Freyther
b5bb75fbfd rf: Remember the last command requested on the RF CMD interface 2011-02-14 22:34:47 +01:00
Holger Hans Peter Freyther
af5b88fb07 welcome: Fix the str concat here as well. Start to count at 0. 2010-11-26 00:13:02 +01:00
Holger Hans Peter Freyther
0366e67f78 vty: For ipaccess we will dump if the OML connection is present 2010-11-25 16:32:32 +01:00
Holger Hans Peter Freyther
c3f0aeee30 mid-call: Do not lose the first word of the message 2010-11-22 22:27:39 +01:00
Holger Hans Peter Freyther
2413efa47b mid-call: Make the mid-call behavior the default for switching things off
When switching the RF off we will always go through the grace
period, add a direct off mode to switch it off directly. Make
the query return a 'g' if we are in the process of switching
things over.
2010-11-22 19:32:13 +01:00
Holger Hans Peter Freyther
e6fd64d000 mid-call: Implement a timer to go from grace to off.
Start the timer... switch it off when we do the final
tranistion by a command.
2010-11-22 19:15:38 +01:00
Holger Hans Peter Freyther
00d34cd8c6 mid-call: Rename ussd-grace to mid-call 2010-11-22 18:31:35 +01:00
Holger Hans Peter Freyther
58c5e587a5 mid-call: Introduce a timeout to switch from grace to rf off. 2010-11-22 18:25:02 +01:00
Holger Hans Peter Freyther
13b24827d7 bsc_msc_ip: Clean the header, the defines can come from bssap.h 2010-11-03 15:52:11 +01:00
Holger Hans Peter Freyther
ba482438a0 ussd: Catch up with libosmocore and pass the gsm48_hdr
Conflicts:

	openbsc/src/ussd.c
2010-10-18 09:54:33 +02:00
Holger Hans Peter Freyther
4cc21eaa42 ussd: Move the code libosmocore, increase the version number.
Move the code to libosmocore, update the header file and the
version required in the configure.in.

Conflicts:

	openbsc/configure.in
	openbsc/include/openbsc/gsm_04_80.h
	openbsc/src/gsm_04_80.c
2010-10-18 09:54:33 +02:00
Holger Hans Peter Freyther
e9f8a258a8 bssap.c: Fix unaligned memory access by using the read_data16 2010-10-08 20:11:47 +08:00
Holger Hans Peter Freyther
5797ca85c1 bssap.c: The lac is only 16bit.. no need to read two bytes of garbage
Recreate a read_data16, use it to parse the LAC from the Cell Identifier..
we check that the length is at least 3 byte and read from byte 1..
which means reading four bytes is totally wrong.
2010-10-08 20:08:36 +08:00
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
57 changed files with 1708 additions and 8182 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,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script
AC_INIT(openbsc, 0.3.99.16onwaves)
AC_INIT(openbsc, 0.3.99.20onwaves)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
dnl kernel style compile messages
@@ -15,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.11)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.23)
PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.3)
dnl checks for header files
AC_HEADER_STDC
@@ -39,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
@@ -50,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 */
@@ -170,4 +172,9 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
const char *nm_opstate_name(u_int8_t os);
const char *nm_avail_name(u_int8_t avail);
int nm_is_running(struct gsm_nm_state *s);
void abis_nm_clear_queue(struct gsm_bts *bts);
int abis_nm_vty_init(void);
#endif /* _NM_H */

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>
@@ -322,7 +322,7 @@ 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);

View File

@@ -6,302 +6,9 @@
#include <stdlib.h>
#include <osmocore/msgb.h>
#include <osmocore/protocol/gsm_08_08.h>
#include <openbsc/gsm_data.h>
/*
* this is from GSM 03.03 CGI but is copied in GSM 08.08
* in § 3.2.2.27 for Cell Identifier List
*/
enum CELL_IDENT {
CELL_IDENT_WHOLE_GLOBAL = 0,
CELL_IDENT_LAC_AND_CI = 1,
CELL_IDENT_CI = 2,
CELL_IDENT_NO_CELL = 3,
CELL_IDENT_LAI_AND_LAC = 4,
CELL_IDENT_LAC = 5,
CELL_IDENT_BSS = 6,
CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
CELL_IDENT_UTRAN_RNC = 9,
CELL_IDENT_UTRAN_LAC_RNC = 10,
};
/* GSM 08.06 § 6.3 */
enum BSSAP_MSG_TYPE {
BSSAP_MSG_BSS_MANAGEMENT = 0x0,
BSSAP_MSG_DTAP = 0x1,
};
struct bssmap_header {
u_int8_t type;
u_int8_t length;
} __attribute__((packed));
struct dtap_header {
u_int8_t type;
u_int8_t link_id;
u_int8_t length;
} __attribute__((packed));
enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_RESERVED_0 = 0,
/* ASSIGNMENT MESSAGES */
BSS_MAP_MSG_ASSIGMENT_RQST = 1,
BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2,
BSS_MAP_MSG_ASSIGMENT_FAILURE = 3,
/* HANDOVER MESSAGES */
BSS_MAP_MSG_HANDOVER_RQST = 16,
BSS_MAP_MSG_HANDOVER_REQUIRED = 17,
BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18,
BSS_MAP_MSG_HANDOVER_CMD = 19,
BSS_MAP_MSG_HANDOVER_COMPLETE = 20,
BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21,
BSS_MAP_MSG_HANDOVER_FAILURE = 22,
BSS_MAP_MSG_HANDOVER_PERFORMED = 23,
BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24,
BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25,
BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26,
BSS_MAP_MSG_HANDOVER_DETECT = 27,
/* RELEASE MESSAGES */
BSS_MAP_MSG_CLEAR_CMD = 32,
BSS_MAP_MSG_CLEAR_COMPLETE = 33,
BSS_MAP_MSG_CLEAR_RQST = 34,
BSS_MAP_MSG_RESERVED_1 = 35,
BSS_MAP_MSG_RESERVED_2 = 36,
BSS_MAP_MSG_SAPI_N_REJECT = 37,
BSS_MAP_MSG_CONFUSION = 38,
/* OTHER CONNECTION RELATED MESSAGES */
BSS_MAP_MSG_SUSPEND = 40,
BSS_MAP_MSG_RESUME = 41,
BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42,
BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43,
BSS_MAP_MSG_LSA_INFORMATION = 44,
BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45,
BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46,
BSS_MAP_MSG_COMMON_ID = 47,
/* GENERAL MESSAGES */
BSS_MAP_MSG_RESET = 48,
BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49,
BSS_MAP_MSG_OVERLOAD = 50,
BSS_MAP_MSG_RESERVED_3 = 51,
BSS_MAP_MSG_RESET_CIRCUIT = 52,
BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53,
BSS_MAP_MSG_MSC_INVOKE_TRACE = 54,
BSS_MAP_MSG_BSS_INVOKE_TRACE = 55,
BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58,
/* TERRESTRIAL RESOURCE MESSAGES */
BSS_MAP_MSG_BLOCK = 64,
BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65,
BSS_MAP_MSG_UNBLOCK = 66,
BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67,
BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68,
BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69,
BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70,
BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71,
BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72,
BSS_MAP_MSG_CHANGE_CIRCUIT = 78,
BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79,
/* RADIO RESOURCE MESSAGES */
BSS_MAP_MSG_RESOURCE_RQST = 80,
BSS_MAP_MSG_RESOURCE_INDICATION = 81,
BSS_MAP_MSG_PAGING = 82,
BSS_MAP_MSG_CIPHER_MODE_CMD = 83,
BSS_MAP_MSG_CLASSMARK_UPDATE = 84,
BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85,
BSS_MAP_MSG_QUEUING_INDICATION = 86,
BSS_MAP_MSG_COMPLETE_LAYER_3 = 87,
BSS_MAP_MSG_CLASSMARK_RQST = 88,
BSS_MAP_MSG_CIPHER_MODE_REJECT = 89,
BSS_MAP_MSG_LOAD_INDICATION = 90,
/* VGCS/VBS */
BSS_MAP_MSG_VGCS_VBS_SETUP = 4,
BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5,
BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6,
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7,
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28,
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29,
BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
BSS_MAP_MSG_UPLINK_RQST = 31,
BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73,
BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74,
BSS_MAP_MSG_UPLINK_REJECT_CMD = 75,
BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76,
BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77,
};
enum GSM0808_IE_CODING {
GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1,
GSM0808_IE_RESERVED_0 = 2,
GSM0808_IE_RESOURCE_AVAILABLE = 3,
GSM0808_IE_CAUSE = 4,
GSM0808_IE_CELL_IDENTIFIER = 5,
GSM0808_IE_PRIORITY = 6,
GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7,
GSM0808_IE_IMSI = 8,
GSM0808_IE_TMSI = 9,
GSM0808_IE_ENCRYPTION_INFORMATION = 10,
GSM0808_IE_CHANNEL_TYPE = 11,
GSM0808_IE_PERIODICITY = 12,
GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13,
GSM0808_IE_NUMBER_OF_MSS = 14,
GSM0808_IE_RESERVED_1 = 15,
GSM0808_IE_RESERVED_2 = 16,
GSM0808_IE_RESERVED_3 = 17,
GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18,
GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19,
GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20,
GSM0808_IE_RR_CAUSE = 21,
GSM0808_IE_RESERVED_4 = 22,
GSM0808_IE_LAYER_3_INFORMATION = 23,
GSM0808_IE_DLCI = 24,
GSM0808_IE_DOWNLINK_DTX_FLAG = 25,
GSM0808_IE_CELL_IDENTIFIER_LIST = 26,
GSM0808_IE_RESPONSE_RQST = 27,
GSM0808_IE_RESOURCE_INDICATION_METHOD = 28,
GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29,
GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30,
GSM0808_IE_DIAGNOSTIC = 31,
GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32,
GSM0808_IE_CHOSEN_CHANNEL = 33,
GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34,
GSM0808_IE_CIPHER_RESPONSE_MODE = 35,
GSM0808_IE_CHANNEL_NEEDED = 36,
GSM0808_IE_TRACE_TYPE = 37,
GSM0808_IE_TRIGGERID = 38,
GSM0808_IE_TRACE_REFERENCE = 39,
GSM0808_IE_TRANSACTIONID = 40,
GSM0808_IE_MOBILE_IDENTITY = 41,
GSM0808_IE_OMCID = 42,
GSM0808_IE_FORWARD_INDICATOR = 43,
GSM0808_IE_CHOSEN_ENCR_ALG = 44,
GSM0808_IE_CIRCUIT_POOL = 45,
GSM0808_IE_CIRCUIT_POOL_LIST = 46,
GSM0808_IE_TIME_INDICATION = 47,
GSM0808_IE_RESOURCE_SITUATION = 48,
GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49,
GSM0808_IE_QUEUEING_INDICATOR = 50,
GSM0808_IE_SPEECH_VERSION = 64,
GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51,
GSM0808_IE_TALKER_FLAG = 53,
GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54,
GSM0808_IE_GROUP_CALL_REFERENCE = 55,
GSM0808_IE_EMLPP_PRIORITY = 56,
GSM0808_IE_CONFIG_EVO_INDI = 57,
GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58,
GSM0808_IE_LSA_IDENTIFIER = 59,
GSM0808_IE_LSA_IDENTIFIER_LIST = 60,
GSM0808_IE_LSA_INFORMATION = 61,
GSM0808_IE_LCS_QOS = 62,
GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63,
GSM0808_IE_LCS_PRIORITY = 67,
GSM0808_IE_LOCATION_TYPE = 68,
GSM0808_IE_LOCATION_ESTIMATE = 69,
GSM0808_IE_POSITIONING_DATA = 70,
GSM0808_IE_LCS_CAUSE = 71,
GSM0808_IE_LCS_CLIENT_TYPE = 72,
GSM0808_IE_APDU = 73,
GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74,
GSM0808_IE_GPS_ASSISTANCE_DATA = 75,
GSM0808_IE_DECIPHERING_KEYS = 76,
GSM0808_IE_RETURN_ERROR_RQST = 77,
GSM0808_IE_RETURN_ERROR_CAUSE = 78,
GSM0808_IE_SEGMENTATION = 79,
GSM0808_IE_SERVICE_HANDOVER = 80,
GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81,
GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
GSM0808_IE_RESERVED_5 = 65,
GSM0808_IE_RESERVED_6 = 66,
};
enum gsm0808_cause {
GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0,
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1,
GSM0808_CAUSE_UPLINK_QUALITY = 2,
GSM0808_CAUSE_UPLINK_STRENGTH = 3,
GSM0808_CAUSE_DOWNLINK_QUALITY = 4,
GSM0808_CAUSE_DOWNLINK_STRENGTH = 5,
GSM0808_CAUSE_DISTANCE = 6,
GSM0808_CAUSE_O_AND_M_INTERVENTION = 7,
GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8,
GSM0808_CAUSE_CALL_CONTROL = 9,
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10,
GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11,
GSM0808_CAUSE_BETTER_CELL = 12,
GSM0808_CAUSE_DIRECTED_RETRY = 13,
GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14,
GSM0808_CAUSE_TRAFFIC = 15,
GSM0808_CAUSE_EQUIPMENT_FAILURE = 32,
GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33,
GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34,
GSM0808_CAUSE_CCCH_OVERLOAD = 35,
GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36,
GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37,
GSM0808_CAUSE_MS_NOT_EQUIPPED = 38,
GSM0808_CAUSE_INVALID_CELL = 39,
GSM0808_CAUSE_TRAFFIC_LOAD = 40,
GSM0808_CAUSE_PREEMPTION = 41,
GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48,
GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49,
GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50,
GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51,
GSM0808_CAUSE_LSA_NOT_ALLOWED = 52,
GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64,
GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80,
GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81,
GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82,
GSM0808_CAUSE_INCORRECT_VALUE = 83,
GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84,
GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85,
GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96,
};
/* GSM 08.08 3.2.2.11 Channel Type */
enum gsm0808_chan_indicator {
GSM0808_CHAN_SPEECH = 1,
GSM0808_CHAN_DATA = 2,
GSM0808_CHAN_SIGN = 3,
};
enum gsm0808_chan_rate_type_data {
GSM0808_DATA_FULL_BM = 0x8,
GSM0808_DATA_HALF_LM = 0x9,
GSM0808_DATA_FULL_RPREF = 0xa,
GSM0808_DATA_HALF_PREF = 0xb,
GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
GSM0808_DATA_MULTI_MASK = 0x20,
GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30,
};
enum gsm0808_chan_rate_type_speech {
GSM0808_SPEECH_FULL_BM = 0x8,
GSM0808_SPEECH_HALF_LM = 0x9,
GSM0808_SPEECH_FULL_PREF= 0xa,
GSM0808_SPEECH_HALF_PREF= 0xb,
GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a,
GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b,
GSM0808_SPEECH_PERM = 0xf,
GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
};
enum gsm0808_permitted_speech {
GSM0808_PERM_FR1 = 0x01,
GSM0808_PERM_FR2 = 0x11,
GSM0808_PERM_FR3 = 0x21,
GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4,
GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4,
GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4,
};
int bssmap_rcvmsg_dt1(struct sccp_connection *conn, struct msgb *msg, unsigned int length);
int bssmap_rcvmsg_udt(struct gsm_network *net, struct msgb *msg, unsigned int length);

View File

@@ -3,20 +3,14 @@
#include <osmocore/msgb.h>
#include <osmocore/protocol/gsm_04_80.h>
#include <osmocore/gsm0480.h>
#define MAX_LEN_USSD_STRING 31
struct ussd_request {
char text[MAX_LEN_USSD_STRING + 1];
u_int8_t transaction_id;
u_int8_t invoke_id;
};
int gsm0480_decode_ussd_request(const struct msgb *msg,
struct ussd_request *request);
int gsm0480_send_ussd_response(const struct msgb *in_msg, const char* response_text,
const struct ussd_request *req);
int gsm0480_send_ussd_reject(const struct msgb *msg,
const struct ussd_request *request);
int gsm0480_send_ussdNotify(struct gsm_lchan *lchan, int level, const char *text);
int gsm0480_send_releaseComplete(struct gsm_lchan *lchan);
#endif

View File

@@ -81,12 +81,14 @@ enum bts_gprs_mode {
BTS_GPRS_EGPRS = 2,
};
#define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3]
struct msgb;
typedef int gsm_cbfn(unsigned int hooknum,
unsigned int event,
struct msgb *msg,
void *data, void *param);
struct osmo_bsc_rf;
struct sccp_connection;
/* Real authentication information containing Ki */
@@ -123,6 +125,8 @@ struct bss_sccp_connection_data {
struct sccp_connection *sccp;
int ciphering_handled : 1;
int new_subscriber;
/* Timers... */
/* for assginment command */
@@ -143,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;
};
@@ -266,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];
@@ -537,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 */
@@ -670,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;
@@ -681,10 +698,14 @@ struct gsm_network {
char *bsc_token;
char *msc_ip;
int msc_port;
int msc_prio;
int msc_ip_dscp;
struct bsc_msc_connection *msc_con;
int ping_timeout;
int pong_timeout;
struct osmo_bsc_rf *rf;
char *mid_call_txt;
int mid_call_timeout;
char *ussd_welcome_txt;
};
#define SMS_HDR_SIZE 128

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,15 +111,10 @@ struct mgcp_config {
char *audio_name;
int audio_payload;
int audio_loop;
int early_bind;
int rtp_base_port;
int endp_tos;
/* 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;
@@ -112,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 */
@@ -120,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);
/*
@@ -134,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,35 @@
#ifndef BSC_MSC_RF
#define BSC_MSC_RF
#include <osmocore/write_queue.h>
#include <osmocore/timer.h>
struct gsm_network;
struct osmo_bsc_rf {
/* the value of signal.h */
int policy;
struct bsc_fd listen;
struct gsm_network *gsm_network;
const char *last_state_command;
/* delay the command */
char last_request;
struct timer_list delay_cmd;
/* verify that RF is up as it should be */
struct timer_list rf_check;
/* some handling for the automatic grace switch */
struct timer_list grace_timeout;
};
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;
@@ -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,177 +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);
struct msgb *sccp_create_refuse(struct sccp_source_reference *src_ref, int cause, uint8_t *data, int length);
struct msgb *sccp_create_cc(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst_ref);
struct msgb *sccp_create_rlsd(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst_ref, int cause);
struct msgb *sccp_create_dt1(struct sccp_source_reference *dst_ref, uint8_t *data, uint8_t len);
/**
* 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 -lrt

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) {
@@ -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 "
@@ -1277,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 */
@@ -1470,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) {
@@ -1490,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;
@@ -1510,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)
@@ -1531,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) {
@@ -1550,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:
@@ -1563,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");
@@ -1572,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:
@@ -2014,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)
@@ -2690,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];
@@ -2711,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 *)
@@ -3013,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;
@@ -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

@@ -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;
@@ -469,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:
@@ -485,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:
@@ -563,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;
@@ -830,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
@@ -892,12 +912,12 @@ static void patch_nm_tables(struct gsm_bts *bts)
nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff;
/* patch IP address as SGSN IP */
*(u_int16_t *)(nanobts_attr_nsvc0+8) =
htons(bts->gprs.nsvc[0].remote_port);
*(u_int32_t *)(nanobts_attr_nsvc0+10) =
htonl(bts->gprs.nsvc[0].remote_ip);
*(u_int16_t *)(nanobts_attr_nsvc0+14) =
htons(bts->gprs.nsvc[0].local_port);
patch_16(nanobts_attr_nsvc0 + 8,
htons(bts->gprs.nsvc[0].remote_port));
patch_32(nanobts_attr_nsvc0 + 10,
htonl(bts->gprs.nsvc[0].remote_ip));
patch_16(nanobts_attr_nsvc0 + 14,
htons(bts->gprs.nsvc[0].local_port));
/* patch BVCI */
nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8;
@@ -969,6 +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;
@@ -1045,7 +1067,13 @@ static int bootstrap_bts(struct gsm_bts *bts)
/* some defaults for our system information */
bts->si_common.cell_options.radio_link_timeout = 7; /* 12 */
bts->si_common.cell_options.dtx = 0; /* MS may use upplink DTX */
/* 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

@@ -58,20 +58,6 @@ static void msc_con_timeout(void *_con)
bsc_msc_lost(con);
}
static int bsc_msc_except(struct bsc_fd *bfd)
{
struct write_queue *wrt;
struct bsc_msc_connection *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;
}
/* called in the case of a non blocking connect */
static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
{
@@ -83,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) {
@@ -107,7 +96,6 @@ static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
con->is_connected = 1;
bsc_del_timer(&con->timeout_timer);
LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC.\n");
if (con->connected)
con->connected(con);
@@ -222,14 +210,16 @@ struct bsc_msc_connection *bsc_msc_create(const char *ip, int port, int prio)
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);
}

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
@@ -45,14 +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
@@ -294,6 +295,11 @@ static int open_sccp_connection(struct msgb *layer3)
return -1;
}
if (!bsc_grace_allow_new_connection(bsc_gsmnet)) {
LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
return -1;
}
LOGP(DMSC, LOGL_DEBUG, "Opening new layer3 connection\n");
sccp_connection = sccp_connection_socket();
if (!sccp_connection) {
@@ -320,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;
@@ -401,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;
@@ -535,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)
{
@@ -580,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;
@@ -703,7 +746,7 @@ static int msc_sccp_do_write(struct bsc_fd *fd, struct msgb *msg)
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);
}
@@ -1135,7 +1178,7 @@ static void signal_handler(int signal)
talloc_report_full(tall_bsc_ctx, stderr);
break;
case SIGUSR2:
if (!bsc_gsmnet->msc_con || !bsc_gsmnet->msc_con->is_connected)
if (!bsc_gsmnet->msc_con)
return;
bsc_msc_lost(bsc_gsmnet->msc_con);
break;
@@ -1224,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);
@@ -1239,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);
}
@@ -1254,7 +1297,7 @@ int main(int argc, char **argv)
bsc_gsmnet->msc_con = bsc_msc_create(msc,
bsc_gsmnet->msc_port,
bsc_gsmnet->msc_prio);
bsc_gsmnet->msc_ip_dscp);
if (!bsc_gsmnet->msc_con) {
fprintf(stderr, "Creating a bsc_msc_connection failed.\n");
exit(1);

View File

@@ -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 uint16_t read_data16(const uint8_t *data)
{
uint16_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_data16(&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;
@@ -259,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;
@@ -507,7 +527,7 @@ static int bssmap_handle_assignm_req(struct sccp_connection *conn,
goto reject;
}
cic = ntohs(*(u_int16_t *)TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
cic = ntohs(read_data16(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)));
timeslot = cic & 0x1f;
multiplex = (cic & ~0x1f) >> 5;
@@ -616,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;
}
@@ -656,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");
@@ -705,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;
}
}
}
@@ -717,138 +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_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)
@@ -935,50 +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;
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)
@@ -1059,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;
}
@@ -1155,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;

View File

@@ -34,6 +34,8 @@
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <osmocore/talloc.h>
static int ts_is_usable(struct gsm_bts_trx_ts *ts)
{
/* FIXME: How does this behave for BS-11 ? */
@@ -330,6 +332,12 @@ void lchan_free(struct gsm_lchan *lchan)
}
for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
lchan->neigh_meas[i].arfcn = 0;
if (lchan->rqd_ref) {
talloc_free(lchan->rqd_ref);
lchan->rqd_ref = NULL;
lchan->rqd_ta = 0;
}
lchan->conn.silent_call = 0;
sig.lchan = lchan;

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;

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,214 +36,29 @@
#include <osmocore/gsm_utils.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/gsm_04_80.h>
/* Forward declarations */
static int parse_ussd(u_int8_t *ussd, struct ussd_request *req);
static int parse_ussd_info_elements(u_int8_t *ussd_ie,
struct ussd_request *req);
static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length,
struct ussd_request *req);
static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length,
struct ussd_request *req);
static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
struct ussd_request *req);
#include <osmocore/gsm0480.h>
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;
}
/* Decode a mobile-originated USSD-request message */
int gsm0480_decode_ussd_request(const struct msgb *msg, struct ussd_request *req)
{
int rc = 0;
u_int8_t *parse_ptr = msgb_l3(msg);
if ((*parse_ptr & 0x0F) == GSM48_PDISC_NC_SS) {
req->transaction_id = *parse_ptr & 0x70;
rc = parse_ussd(parse_ptr+1, req);
}
if (!rc)
DEBUGP(DMM, "Error occurred while parsing received USSD!\n");
return rc;
}
static int parse_ussd(u_int8_t *ussd, struct ussd_request *req)
{
int rc = 1;
u_int8_t msg_type = ussd[0] & 0xBF; /* message-type - section 3.4 */
switch (msg_type) {
case GSM0480_MTYPE_RELEASE_COMPLETE:
DEBUGP(DMM, "USS Release Complete\n");
/* could also parse out the optional Cause/Facility data */
req->text[0] = 0xFF;
break;
case GSM0480_MTYPE_REGISTER:
case GSM0480_MTYPE_FACILITY:
rc &= parse_ussd_info_elements(ussd+1, req);
break;
default:
fprintf(stderr, "Unknown GSM 04.80 message-type field 0x%02x\n",
ussd[0]);
rc = 0;
break;
}
return rc;
}
static int parse_ussd_info_elements(u_int8_t *ussd_ie, struct ussd_request *req)
{
int rc;
/* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
u_int8_t iei = ussd_ie[0];
u_int8_t iei_length = ussd_ie[1];
switch (iei) {
case GSM48_IE_CAUSE:
break;
case GSM0480_IE_FACILITY:
rc = parse_facility_ie(ussd_ie+2, iei_length, req);
break;
case GSM0480_IE_SS_VERSION:
break;
default:
fprintf(stderr, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n",
iei);
rc = 0;
break;
}
return rc;
}
static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length,
struct ussd_request *req)
{
int rc = 1;
u_int8_t offset = 0;
do {
/* Component Type tag - table 3.7 */
u_int8_t component_type = facility_ie[offset];
u_int8_t component_length = facility_ie[offset+1];
switch (component_type) {
case GSM0480_CTYPE_INVOKE:
rc &= parse_ss_invoke(facility_ie+2,
component_length,
req);
break;
case GSM0480_CTYPE_RETURN_RESULT:
break;
case GSM0480_CTYPE_RETURN_ERROR:
break;
case GSM0480_CTYPE_REJECT:
break;
default:
fprintf(stderr, "Unknown GSM 04.80 Facility "
"Component Type 0x%02x\n", component_type);
rc = 0;
break;
}
offset += (component_length+2);
} while (offset < length);
return rc;
}
/* Parse an Invoke component - see table 3.3 */
static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length,
struct ussd_request *req)
{
int rc = 1;
u_int8_t offset;
/* mandatory part */
if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag "
"0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
}
offset = invoke_data[1] + 2;
req->invoke_id = invoke_data[2];
/* optional part */
if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID)
offset += invoke_data[offset+1] + 2; /* skip over it */
/* mandatory part */
if (invoke_data[offset] == GSM0480_OPERATION_CODE) {
u_int8_t operation_code = invoke_data[offset+2];
switch (operation_code) {
case GSM0480_OP_CODE_PROCESS_USS_REQ:
rc = parse_process_uss_req(invoke_data + offset + 3,
length - offset - 3,
req);
break;
default:
fprintf(stderr, "GSM 04.80 operation code 0x%02x "
"is not yet handled\n", operation_code);
rc = 0;
break;
}
} else {
fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag 0x%02x "
"(expecting Operation Code tag)\n",
invoke_data[0]);
rc = 0;
}
return rc;
}
/* Parse the parameters of a Process UnstructuredSS Request */
static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
struct ussd_request *req)
{
int rc = 0;
int num_chars;
u_int8_t dcs;
if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) {
dcs = uss_req_data[4];
if ((dcs == 0x0F) &&
(uss_req_data[5] == ASN1_OCTET_STRING_TAG)) {
num_chars = (uss_req_data[6] * 8) / 7;
/* Prevent a mobile-originated buffer-overrun! */
if (num_chars > MAX_LEN_USSD_STRING)
num_chars = MAX_LEN_USSD_STRING;
gsm_7bit_decode(req->text,
&(uss_req_data[7]), num_chars);
/* append null-terminator */
req->text[num_chars+1] = 0;
rc = 1;
}
}
}
return rc;
}
/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_text,
const struct ussd_request *req)
@@ -298,7 +113,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 +141,43 @@ int gsm0480_send_ussd_reject(const struct msgb *in_msg,
return gsm48_sendmsg(msg, NULL);
}
int gsm0480_send_ussdNotify(struct gsm_lchan *lchan, int level, const char *text)
{
struct gsm48_hdr *gh;
struct msgb *msg;
msg = gsm0480_create_unstructuredSS_Notify(level, text);
if (!msg)
return -1;
gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0);
gsm0480_wrap_facility(msg);
msg->lchan = lchan;
/* And finally pre-pend the L3 header */
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS;
gh->msg_type = GSM0480_MTYPE_REGISTER;
return gsm48_sendmsg(msg, NULL);
}
int gsm0480_send_releaseComplete(struct gsm_lchan *lchan)
{
struct gsm48_hdr *gh;
struct msgb *msg;
msg = gsm48_msgb_alloc();
if (!msg)
return -1;
msg->lchan = lchan;
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS;
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
return gsm48_sendmsg(msg, NULL);
}

View File

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

View File

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

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

@@ -90,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));
@@ -139,74 +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 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;
/* For loop toggle the destination and then dispatch. */
if (cfg->audio_loop)
dest = !dest;
if (dest == DEST_NETWORK) {
if (proto == PROTO_RTP)
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 {
if (proto == PROTO_RTP)
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)
@@ -227,6 +360,8 @@ 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;
}
@@ -241,59 +376,96 @@ static int set_ip_tos(int fd, int tos)
return ret != 0;
}
static int bind_rtp(struct mgcp_endpoint *endp)
static int bind_rtp(struct mgcp_config *cfg, struct mgcp_rtp_end *rtp_end, int endpno)
{
struct mgcp_config *cfg = endp->cfg;
if (create_bind(cfg->source_addr, &endp->local_rtp, endp->rtp_port) != 0) {
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;
}
set_ip_tos(endp->local_rtp.fd, cfg->endp_tos);
set_ip_tos(endp->local_rtcp.fd, cfg->endp_tos);
set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
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) {
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;
@@ -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,19 +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);
vty_out(vty, " rtp ip-tos %d%s", g_cfg->endp_tos, 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);
@@ -82,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);
}
@@ -151,31 +160,76 @@ DEFUN(cfg_mgcp_bind_early,
"bind early (0|1)",
"Bind all RTP ports early")
{
unsigned int bind = atoi(argv[0]);
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]);
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_ip_tos,
cfg_mgcp_rtp_ip_tos_cmd,
"rtp ip-tos <0-255>",
"Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The TOS value.")
DEFUN(cfg_mgcp_rtp_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")
{
int tos = atoi(argv[0]);
g_cfg->endp_tos = tos;
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>",
@@ -216,26 +270,6 @@ DEFUN(cfg_mgcp_number_endp,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_forward_ip,
cfg_mgcp_forward_ip_cmd,
"forward audio ip A.B.C.D",
"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",
@@ -247,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);
@@ -260,13 +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;
}
@@ -296,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]);
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -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->mid_call_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->mid_call_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,11 @@
*
*/
#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 <openbsc/ipaccess.h>
#include <osmocore/talloc.h>
#include <osmocore/protocol/gsm_12_21.h>
@@ -37,6 +39,8 @@
#define RF_CMD_QUERY '?'
#define RF_CMD_OFF '0'
#define RF_CMD_ON '1'
#define RF_CMD_D_OFF 'd'
#define RF_CMD_ON_G 'g'
static int lock_each_trx(struct gsm_network *net, int lock)
{
@@ -52,25 +56,9 @@ static int lock_each_trx(struct gsm_network *net, int lock)
return 0;
}
/*
* Send a '1' when one TRX is online, otherwise send 0
*/
static void handle_query(struct bsc_msc_rf_conn *conn)
static void send_resp(struct osmo_bsc_rf_conn *conn, char send)
{
struct msgb *msg;
struct gsm_bts *bts;
char send = RF_CMD_OFF;
llist_for_each_entry(bts, &conn->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 = RF_CMD_ON;
break;
}
}
}
msg = msgb_alloc(10, "RF Query");
if (!msg) {
@@ -90,9 +78,125 @@ static void handle_query(struct bsc_msc_rf_conn *conn)
return;
}
/*
* Send a
* 'g' when we are in grace mode
* '1' when one TRX is online,
* '0' otherwise
*/
static void handle_query(struct osmo_bsc_rf_conn *conn)
{
struct gsm_bts *bts;
char send = RF_CMD_OFF;
if (conn->rf->policy == S_RF_GRACE)
return send_resp(conn, RF_CMD_ON_G);
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 = RF_CMD_ON;
break;
}
}
}
send_resp(conn, send);
}
static void rf_check_cb(void *_data)
{
struct gsm_bts *bts;
struct osmo_bsc_rf *rf = _data;
llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) {
struct gsm_bts_trx *trx;
/* don't bother to check a booting or missing BTS */
if (!bts->oml_link || !is_ipaccess_bts(bts))
continue;
llist_for_each_entry(trx, &bts->trx_list, list) {
if (trx->nm_state.availability != NM_AVSTATE_OK ||
trx->nm_state.operational != NM_OPSTATE_ENABLED ||
trx->nm_state.administrative != NM_STATE_UNLOCKED) {
LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n");
ipaccess_drop_oml(bts);
break;
}
}
}
}
static void send_signal(struct osmo_bsc_rf *rf, int val)
{
struct rf_signal_data sig;
sig.net = rf->gsm_network;
rf->policy = val;
dispatch_signal(SS_RF, val, &sig);
}
static int switch_rf_off(struct osmo_bsc_rf *rf)
{
lock_each_trx(rf->gsm_network, 1);
send_signal(rf, S_RF_OFF);
return 0;
}
static void grace_timeout(void *_data)
{
struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data;
LOGP(DINP, LOGL_NOTICE, "Grace timeout. Disabling the TRX.\n");
switch_rf_off(rf);
}
static int enter_grace(struct osmo_bsc_rf *rf)
{
rf->grace_timeout.cb = grace_timeout;
rf->grace_timeout.data = rf;
bsc_schedule_timer(&rf->grace_timeout, rf->gsm_network->mid_call_timeout, 0);
LOGP(DINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n",
rf->gsm_network->mid_call_timeout);
send_signal(rf, S_RF_GRACE);
return 0;
}
static void rf_delay_cmd_cb(void *data)
{
struct osmo_bsc_rf *rf = data;
switch (rf->last_request) {
case RF_CMD_D_OFF:
rf->last_state_command = "RF Direct Off";
bsc_del_timer(&rf->rf_check);
bsc_del_timer(&rf->grace_timeout);
switch_rf_off(rf);
break;
case RF_CMD_ON:
rf->last_state_command = "RF Direct On";
bsc_del_timer(&rf->grace_timeout);
lock_each_trx(rf->gsm_network, 0);
send_signal(rf, S_RF_ON);
bsc_schedule_timer(&rf->rf_check, 3, 0);
break;
case RF_CMD_OFF:
rf->last_state_command = "RF Scheduled Off";
bsc_del_timer(&rf->rf_check);
enter_grace(rf);
break;
}
}
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;
@@ -110,13 +214,15 @@ static int rf_read_cmd(struct bsc_fd *fd)
case RF_CMD_QUERY:
handle_query(conn);
break;
case RF_CMD_OFF:
lock_each_trx(conn->gsm_network, 1);
break;
case RF_CMD_D_OFF:
case RF_CMD_ON:
lock_each_trx(conn->gsm_network, 0);
case RF_CMD_OFF:
conn->rf->last_request = buf[0];
if (!bsc_timer_pending(&conn->rf->delay_cmd))
bsc_schedule_timer(&conn->rf->delay_cmd, 1, 0);
break;
default:
conn->rf->last_state_command = "Unknown command";
LOGP(DINP, LOGL_ERROR, "Unknown command %d\n", buf[0]);
break;
}
@@ -139,8 +245,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 +258,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 +271,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 +282,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 +349,16 @@ struct bsc_msc_rf *bsc_msc_rf_create(const char *path, struct gsm_network *net)
}
rf->gsm_network = net;
rf->policy = S_RF_ON;
rf->last_state_command = "";
/* check the rf state */
rf->rf_check.data = rf;
rf->rf_check.cb = rf_check_cb;
/* delay cmd handling */
rf->delay_cmd.data = rf;
rf->delay_cmd.cb = rf_delay_cmd_cb;
return rf;
}

File diff suppressed because it is too large Load Diff

View File

@@ -45,8 +45,11 @@ static int send_own_number(const struct msgb *msg, const struct ussd_request *re
int handle_rcv_ussd(struct msgb *msg)
{
struct ussd_request req;
struct gsm48_hdr *gh;
gsm0480_decode_ussd_request(msg, &req);
memset(&req, 0, sizeof(req));
gh = msgb_l3(msg);
gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req);
if (req.text[0] == 0xFF) /* Release-Complete */
return 0;

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
*
@@ -42,6 +42,7 @@
#include <openbsc/vty.h>
#include <openbsc/ipaccess.h>
#include <openbsc/paging.h>
#include <openbsc/osmo_bsc_rf.h>
static struct gsm_network *gsmnet;
@@ -164,6 +165,10 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
vty_out(vty, "hr: %d ver: %d, ",
net->audio_support[i]->hr, net->audio_support[i]->ver);
vty_out(vty, "%s", VTY_NEWLINE);
/* show rf */
vty_out(vty, " Last RF Command: %s%s",
net->rf->last_state_command, VTY_NEWLINE);
}
DEFUN(show_net, show_net_cmd, "show network",
@@ -227,11 +232,15 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " Paging: %u pending requests, %u free slots%s",
paging_pending_requests_nr(bts),
bts->paging.available_slots, VTY_NEWLINE);
if (!is_ipaccess_bts(bts)) {
if (is_ipaccess_bts(bts)) {
vty_out(vty, " OML Link state: %s.%s",
bts->oml_link ? "connected" : "disconnected", VTY_NEWLINE);
} else {
vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
e1isl_dump_vty(vty, bts->oml_link);
}
/* FIXME: oml_link, chan_desc */
/* FIXME: chan_desc */
memset(&pl, 0, sizeof(pl));
bts_chan_load(&pl, bts);
vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
@@ -500,6 +509,7 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE);
vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE);
vty_out(vty, " ipacc rtp_payload %u%s", gsmnet->rtp_payload, VTY_NEWLINE);
vty_out(vty, " rtp base %u%s", gsmnet->rtp_base_port, VTY_NEWLINE);
@@ -524,9 +534,15 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " bsc_token %s%s", gsmnet->bsc_token, VTY_NEWLINE);
vty_out(vty, " msc ip %s%s", gsmnet->msc_ip, VTY_NEWLINE);
vty_out(vty, " msc port %d%s", gsmnet->msc_port, VTY_NEWLINE);
vty_out(vty, " msc ip-tos %d%s", gsmnet->msc_prio, VTY_NEWLINE);
vty_out(vty, " msc ip-dscp %d%s", gsmnet->msc_ip_dscp, VTY_NEWLINE);
vty_out(vty, " timeout ping %d%s", gsmnet->ping_timeout, VTY_NEWLINE);
vty_out(vty, " timeout pong %d%s", gsmnet->pong_timeout, VTY_NEWLINE);
if (gsmnet->mid_call_txt)
vty_out(vty, " mid-call-text %s%s", gsmnet->mid_call_txt, VTY_NEWLINE);
vty_out(vty, " mid-call-timeout %d%s", gsmnet->mid_call_timeout, VTY_NEWLINE);
if (gsmnet->ussd_welcome_txt)
vty_out(vty, " bsc-welcome-text %s%s", gsmnet->ussd_welcome_txt, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1413,13 +1429,17 @@ DEFUN(cfg_net_msc_port,
DEFUN(cfg_net_msc_prio,
cfg_net_msc_prio_cmd,
"msc ip-tos <0-255>",
"msc ip-dscp <0-255>",
"Set the IP_TOS socket attribite")
{
gsmnet->msc_prio = atoi(argv[0]);
gsmnet->msc_ip_dscp = atoi(argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_net_msc_prio, cfg_net_msc_ip_tos_cmd,
"msc ip-tos <0-255>",
"Set the IP_TOS socket attribite\n" "The DSCP to use.\n")
DEFUN(cfg_net_ping_time,
cfg_net_ping_time_cmd,
"timeout ping NR",
@@ -1438,6 +1458,47 @@ DEFUN(cfg_net_pong_time,
return CMD_SUCCESS;
}
DEFUN(cfg_net_mid_call_text,
cfg_net_mid_call_text_cmd,
"mid-call-text .TEXT",
"Set the USSD notifcation to be send.\n" "Text to be sent\n")
{
char *data = argv_concat(argv, argc, 0);
if (!data)
return CMD_WARNING;
if (gsmnet->mid_call_txt)
talloc_free(gsmnet->mid_call_txt);
gsmnet->mid_call_txt = talloc_strdup(gsmnet, data);
talloc_free(data);
return CMD_SUCCESS;
}
DEFUN(cfg_net_mid_call_timeout,
cfg_net_mid_call_timeout_cmd,
"mid-call-timeout NR",
"Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n")
{
gsmnet->mid_call_timeout = atoi(argv[0]);
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, 0);
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, \
cfg_net_T##number##_cmd, \
@@ -1468,6 +1529,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,
@@ -2241,13 +2312,18 @@ 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_mid_call_text_cmd);
install_element(GSMNET_NODE, &cfg_net_mid_call_timeout_cmd);
install_element(GSMNET_NODE, &cfg_net_welcome_ussd_cmd);
install_element(GSMNET_NODE, &cfg_bts_cmd);
install_node(&bts_node, config_write_bts);
@@ -2301,6 +2377,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

@@ -30,7 +30,7 @@
#include <openbsc/bsc_msc.h>
#include <openbsc/vty.h>
#include <sccp/sccp.h>
#include <osmocom/sccp/sccp.h>
static struct gsm_network *gsmnet = NULL;

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

View File

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

View File

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

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() {}