Compare commits

...

297 Commits

Author SHA1 Message Date
Neels Hofmeyr
8a9f12dc2f mgcp dbg log
Change-Id: I56fda48edaa92abfc6e3886cdfce733bb0686f73
2016-09-29 16:13:08 +02:00
Neels Hofmeyr
0ba1543220 sgsn_ranap_iu_event: handle some events without valid MM context
Change-Id: Ia7e74087d56996104b6d3935b1cf12166ff67f3a
2016-09-29 16:13:08 +02:00
Neels Hofmeyr
e708d74658 IuCS: store assigned rab_id in gsm_subscriber_connection
Change-Id: I7fda4304631fc24bbd1bebe911b8403a942fcf53
2016-09-29 16:13:08 +02:00
Neels Hofmeyr
b4ed0e7b78 bridge calls via mgcpgw
Change-Id: Ie259e30bc532fe9817c96562022ac33443d5747a
2016-09-29 16:13:08 +02:00
Neels Hofmeyr
766acca73e also do call assignment for MT calls, upon Call Confirmed
Change-Id: I863fa73948f61aaffd7f4472f3abc3e44228e31f
2016-09-29 16:13:08 +02:00
Neels Hofmeyr
5f5a6b2113 IuCS: implement msc_call_assignment() for IuCS
Send IuCS RAB Activation upon MNCC_CALL_PROC_REQ.

Implement function msc_call_assignment(): decide between sending A-iface BSSMAP
Assignment Request or IuCS RAB Assignment Request.

Implement iu_rab_act_cs() to send the IuCS RAB Assignment Request. The IP
address and port of the MGCPGW sent in the RAB Assignment are still hardcoded.

The A-interface extension is not implemented yet.

Declare ranap_new_msg_rab_assign_voice() to avoid including
ranap_msg_factory.h, which would require adding ASN1 CFLAGS to Makefile.am.

The mgcpgw_client as well as some more osmo-iuh functions are now linked from
libmsc, hence add some dummy stubs to libiudummy and db_test.c.

Change-Id: Iaae51d1fbbfc28fad1c0b85e161d53d80a420a19
2016-09-29 16:13:08 +02:00
Neels Hofmeyr
e9f82cbe7f cscn: add mgcpgw client (with dummy read cb so far)
Store the mgcpgw client data in struct gsm_network.
Initialize VTY and bind the client.

Change-Id: Ifc4efb1ca44fa34c29bf23b35addb54155296d68
2016-09-29 16:00:44 +02:00
Neels Hofmeyr
7511d4dd5f libmgcp: add mgcpgw client API
Add an API to send MGCP messages to an MGCP GW, from the perspective of
an MSC instructing the GW to setup RTP streams.

Rationale: the mgcp_protocol.h is mostly for the MGCP GW itself, other
implementations forward incoming MGCP messages. So a simpler approach for an
MGCP GW client is useful.

Add general VTY commands that can be used to configure mgcpgw_client.

osmo-cscn is going to use this to route RTP streams (for 3G at first).

Change-Id: I6fe365c4c89207f2172943cc456b508a207b1135
2016-09-29 15:58:38 +02:00
Neels Hofmeyr
3285c7fc15 libmgcp: add value strings for mgcp_connection_mode
Add file mgcp_common.c to implement the value strings for the
mgcp_connection_mode.

Add in a separate file because of the upcoming mgcpgw_client.c implementation,
introducing a file that contains implementations commonly used in MGCP GW as
well as its clients.

Change-Id: I6fe365c4c89207f2172943cc456b508a207b1135
2016-09-29 15:56:33 +02:00
Neels Hofmeyr
775234a8a9 libmgcp: move mgcp_connection_mode to public header
mgcp_connection_mode will be used by the upcoming mgcpgw_client.h API.

Change-Id: I7a3f8905723320d968f1a53c1036904107b4fb2d
2016-09-29 15:49:32 +02:00
Neels Hofmeyr
16bf852609 IuCS: cosmetic prep for msc_call_assignment()
Rename gsm48_cc_tx_call_proc() to gsm48_cc_tx_call_proc_and_assign() to mark
the place where the A-interface will send a BSSAP Assignment Request / where
the IuCS-interface will send a RAB Assignment Request.

Add function msc_call_assignment() to decide between A-iface and IuCS
assignment, to be implemented in subsequent commit.

Change-Id: I0695e233d57d13658793b0e63bb7c3ff224909a0
2016-09-29 13:23:55 +02:00
Neels Hofmeyr
7b05b02968 IuCS: send RANAP CommonID
Add libiu function to send a CommonID message down a UE connection,
iu_tx_common_id(); add also a corresponding stub to libiudummy for linking with
tests.

Add libmsc function msc_tx_common_id() to call the above. Add this mostly to
clearly indicate in msc_ifaces.h that libmsc is calling out of the MSC; also
to do conn->via_iface checking.

Call msc_tx_common_id() after ciphering is established, in
_gsm48_rx_mm_serv_req_sec_cb()'s GSM_SECURITY_SUCCEEDED case.

Change-Id: I576ddd5bbabfc989149debd2f8a9743db6d26043
2016-09-29 13:23:55 +02:00
Neels Hofmeyr
60c272ab2c cosmetic: msc_handler, bsc_handler: drop extra whitespace
The extra ws made me not find these lines when grepping for 'dtap ='.

Change-Id: I45e5c9e7df4704546872aab15adf407298943435
2016-09-29 13:23:55 +02:00
Harald Welte
8480227daa SGSN: Don't indicate GERAN in Iu mode PDP CTX ACT REQ to GGSN 2016-09-29 13:23:55 +02:00
Neels Hofmeyr
5b597738ea cscn: add cmdline error message
Change-Id: I30c13a604160268756b7413f1733f92300eb241b
2016-09-29 13:23:55 +02:00
Neels Hofmeyr
84136eb62d cosmetic: remove legacy comment from gsm0408_loc_upd_rej() 2016-09-29 13:23:55 +02:00
Neels Hofmeyr
a49d02723b cosmetic: make gsm0408_loc_upd_rej() static 2016-09-29 13:23:55 +02:00
Neels Hofmeyr
30e416be28 LU counters: count completion and failure, not messages sent
From a human admin viewpoint it doesn't make sense to count the messages sent:

When we use TMSIs, we first send a LU Accept with a new TMSI, and then expect
the MS to respond with a TMSI Realloc Complete message. When that fails to come
through, the LU actually ends in failure, even though a LU Accept was sent.

In 3G, if a UE sends an Iu Release during LU (e.g. user enables flight mode),
we cancel the LU without sending any reply at all, so nothing would be counted.

Instead, count Location Updating results, i.e. completion and failures.
2016-09-29 13:23:55 +02:00
Neels Hofmeyr
5929d9c952 remove handle_abisip_signal()
Change-Id: I9cf80f9c2c8a53a29e42f000029e680a9922cb41
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
eee41f1b7b gsm0408_clear_request(): actually free the released conn
By having conn->in_release == 1, calling msc_release_connection() has no
effect and thus never frees the conn. So, after all pending requests have
been discarded, also discard and free the unused connection.
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
070ec54b60 add iu.h to gsm_subscriber.c
Change-Id: I398aaa4a7328a58fb0d563725f3bea26482929ef
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
9c1abf5bb7 gsm_04_08.c: iu.h
Change-Id: I624612b5d5cd70770326347634aee2a42ba88945
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
21de1c036e temporary dev: set debug log level almost everywhere
Change-Id: I0d5a36560e7edde27497de57e579f5b1d00eb525
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
6c3a83455d comment on mscsplit, indent comment 2016-09-29 13:23:54 +02:00
Neels Hofmeyr
d8fdf9f14b move to libbsc: lchan_next_meas_rep() -- TODO really?
Change-Id: I4ea799c5fa61f81c404e6ef1b9ac86a8faa1fb49
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
29ce45ba0a move to libbsc: gsm_bts_neighbor() -- TODO really?
Change-Id: I63d4835dc7aabdf176e0ca634a6a4ca527612693
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
c4289bafc2 gsm0408_test: use NULL for root ctx -- TODO really? 2016-09-29 13:23:54 +02:00
Neels Hofmeyr
32a0a43a5b gsm_04_08: remove apply_codec_restrictions() -- TODO really?
This function is wrongly placed on the MSC level.

Unfortunately I cannot remember the very plausible details that hwelte had
back in the days to argue for this change. (Refactoring an old commit that
fails to explain in more detail.)

Change-Id: I82623847e652a59a921d2fb142b77cf22420a746
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
264bc2ff66 move to libmsc: osmo_stats_vty_add_cmds() -- todo MSCSPLIT 2016-09-29 13:23:54 +02:00
Neels Hofmeyr
05022b0ba8 include msc_ifaces.h in gsm_04_08.c
Change-Id: I11be1bdfe6993f89b34319e7d1526c729d6e0cde
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
5d0c8f34c9 complete IuCS paging implementation
Add paging timeout to struct gsm_subscriber. Previously, paging timeout was
implemented only on BSC level, where each request has its own timeout value.
The MSC will still send individual requests to BSC or RNC level, where they
timeout individually. However, the MSC must also have an own timeout to be sure
to discard stale pagings that the BSC or RNC never replied for.

Add handle_paging_resp(), copying the few libmsc relevant parts of
gsm48_handle_paging_resp().
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
05ab605ce4 paging: add todo comments for paging and mscsplit
Change-Id: I7e72c9db2837ea5edf45f6037cb0288a264d492c
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
16c6e5b0f2 paging: actually verify subscriber authorization
Before this, any paging response would be accepted by the CN, without
checking the database whether the subscriber is in fact authorized.

The probability that a subscriber would be able to take unauthorized action
is slim, nevertheless checking authorization status with the database should
happen before we accept a connection.
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
726ec6d460 paging: change subscr_paging_cb() into subscr_rx_paging_response()
Remove one layer of callback indirection in paging. When a paging response
arrives, we always want to first secure the connection, thus a fixed
subscr_rx_paging_response() function is more appropriate and avoids having
to store a cbfn. The actual actions to be taken upon successful paging are
of course still in callback functions stored with each subscriber.

Remove paging_request_stop() call from subscr_paging_dispatch(), which stops
paging on all BTSs, which is not the responsibility of libmsc.

Change-Id: Ic2c785c9cc48b2c2c6557cbe1060d25afa89e38d
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
28b715dfff move subscr auth check to gsm_subscriber.c
add subscr_authorized(), subscr_authorized_imsi()

Change-Id: If2ef06b1229351127c61477ca14653d6ae4cb6bb
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
d9c19a0332 auth log
Change-Id: Icd9f8505388a06ee768d2176cb2b9187953098ef
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
0412f5ef79 iu auth wip
Change-Id: Icc2522252cf15c54f1a1ea5255314a0de8bfba03
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
597ecedce3 Iu auth wip
Change-Id: I44effcca80dc6850178174dc957bcd5608b0ae14
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
8df85ca8a8 cosmetic prep: change int -> bool authorize_subscriber()
Upcoming function subscr_authorized() will flip this to bool, so separate
this change cosmetically.

Change-Id: Iba0184a71afa01141ef06c474cb554e79ad8f5d5
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
733aad4917 subscr_request_channel() -> subscr_request_conn()
Change-Id: Ife8e10b240693a8d369139881774f1892044aa65
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
6fd4ee481a move subscr_request to gsm_subscriber.h
Change-Id: Idbbd39b0e068da17aafa97e315143509c69c50ea
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
eee0960d80 add gsm_encr to subscr_conn
Change-Id: Id5797cd1f1bfa2cca2d3fbabc1981aa75546421b
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
a6ce92b23c osmo-nitb becomes osmo-cscn
Change-Id: I3787050b524954d8a4dd13495c458f3ee293807b
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
5573d6cf5d remove unneccessary linking from some tests
The recent shifts and cuts have made some library linking for bsc, channel and
db tests unnecessary.
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
99ad125c75 msc_release_connection(): don't call gsm0808_clear()
gsm0808_clear() is all about clearing lchans. To be able to link libmsc without
libbsc, don't call it directly.

Change-Id: I149146fc3cb99ef4a21ee2a798231bb070f398cd
2016-09-29 13:23:54 +02:00
Neels Hofmeyr
b108f9da02 cut off libbsc paging, pending paging in libmsc
Temporarily disable all paging to be able to link libmsc without libbsc.
Skip the paging part of channel_test because the paging is now disabled.

In osmo-nitb, paging is done on BSC level and MSC level "at the same time".
When the new CSCN is fully operational, paging will be controlled separately on
the MSC level, and the BSC (RNC) level will be instructed over an IuCS or
A-interface to negotiate paging with the MS (UE). This MSC level paging does
not yet exist and will be added in subsequent commits.

Change-Id: I8b6920ddc54fc3f2876a59664e6722666d8a8a4a
2016-09-29 13:23:52 +02:00
Neels Hofmeyr
4b2cd3a277 libmsc: duplicate gsm0808 / gsm48 functions (towards BSC)
In osmo-nitb, libmsc would directly call the functions on the BSC level, not
always via the bsc_api. When separating libmsc from libbsc, some functions are
missing from the linkage.

Hence duplicate these functions to libmsc, add an msc_ prefix for clarity, also
add a _tx to gsm0808_cipher_mode():

* add msc_gsm0808_tx_cipher_mode() (dummy/stub)
* add msc_gsm48_tx_mm_serv_ack()
* add msc_gsm48_tx_mm_serv_rej()

Call these from libmsc instead of

* gsm0808_cipher_mode()
* gsm48_tx_mm_serv_ack()
* gsm48_tx_mm_serv_rej()

Also add a comment relatd to msc_gsm0808_tx_cipher_mode() in two places.

Change-Id: I5b276853d3af71f5e3f0a031fd17b4fff0580020
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
17395b6c34 Use new msc_tx_dtap() instead of gsm0808_submit_dtap()
Aim: msc_tx_dtap() shall redirect to IuCS or A interfaces depending on subscr
conn.

Change-Id: I30d961f16eb7b9c0ab9cc3f43198098d3f1a909f
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
9df6c1b982 add libiudummy, to avoid linking Iu deps in tests
Change-Id: I4a66c4122011dbc87c6fcb336ab0461b86522c98
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
b8afb85f9b move to libxsc: gsm48_extract_mi(), gsm48_paging_extract_mi() -- TODO move to libfilter instead?
Change-Id: I00ca0caf8224de029f53f4dedb1146e3cf7650ec
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
c575ac11f8 msc_compl_l3(): publish in .h, tweak return value
Use new libmsc enum values for return val, to avoid dependency on libbsc
headers.

Make callable from other scopes: publish in osmo_msc.h and remove 'static' in
osmo_msc.c

Change-Id: If24007445899e9c75553a0dbf843ada3566b3380
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
0fba4dd43c add cscn vty, remove nitb vty
Change-Id: I8f8980d6cfbf26f1b0e0197939833e55dbe521fb
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
2ad8232241 add iucs.[hc]
Change-Id: I88e981f4c31393a98ae8d61176c65c9251a6f28b
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
9de3f511f5 add DIUCS debug log constant
Change-Id: Id347a3024fa495a1ab680db7320648d933a4018b
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
b81419eec2 gsm0408_loc_upd_rej(): remove bts use (used only for debug log)
Change-Id: I3ac38f4b701ad8308470573260fa91a4b04c2f18
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
568798ae37 gsm_04_08, gsm_subscriber: decouple lac from bts
The idea is to not have a direct pointer to a bts struct (into BSC land), but a
LAC to resolve the BSC or RNC depending on the appropriate A or IuCS interface.

subscr_update(): remove bts arg, add lac arg.

Pass conn->lac to gsm48_generate_lai() instead of bts->location_area_code.

Change-Id: I9f2b298a785bf4b2a1b3fcdd91b8256106b2d9de
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
d12e3d7094 subscr_update_expire_lu(): remove bts arg
Change-Id: I26cafd9389aac65e53dc4280a1687c6b8bce3106
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
f2e5bc97cd move t3212 to network level (periodic lu)
Set the T3212 default value in struct gsm_network and take that value when
creating a BTS.

Adjust VTY accordingly.

Change-Id: Ifb730f9d0106fe195adc30459a39290a07313b50
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
145091bcc1 libmsc: iucs dev: disable large parts of the code
Change-Id: I3ef6ca26150b6102a0fa22a88a60d9a442d640b4
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
a4e5b7660b Prepare entry/exit point for MSC -> BSC and MSC -> RNC communication.
Add msc_ifaces.[hc], a_iface.c, with a general msc_tx_dtap() to redirect to
different interfaces depending on the actual subscriber connection.

While iu_tx() is going to be functional fairly soon, the a_tx() is going to be
just a dummy for some time (see comment).

Add via_iface marker to gsm_subscriber_connection with enum values IFACE_A and
IFACE_IU so far.

Add Iu specific fields in a sub-struct: the UE connection pointer and an
indicator for the Integrity Protection status on Iu (to be fully implemented in
later commits).

Add lac member to gsm_subscriber_connection, to allow decoupling from
bts->location_area_code. The conn->lac will actually be set in iu.c in an
upcoming commit ("add iucs.[hc]").

Change-Id: Idf8020a30562426e8f939706bf5c2188d5a09798
2016-09-29 13:20:45 +02:00
Neels Hofmeyr
a91bf7bd94 don't use lchan in libmsc
Change-Id: Ic7ed7faa2bcc7aae799f41ed4abc2c001bfb61b7
2016-09-29 13:20:45 +02:00
Harald Welte
5505bf2630 gsm_04_08.c: Don't set msg->lchan nor msg->dst
the BSC-side of the API behind gsm0808_submit_dtap() is doing
this resolving again anyway.  So let's avoid doing it twice, and avoid
having more dependency of the MSC down into the lchan details.

Conflicts:
	openbsc/src/libmsc/gsm_04_08.c

Change-Id: I14254be68ee1a48e9f1ce968233414d86c6ba9d5
2016-09-29 13:20:44 +02:00
Neels Hofmeyr
c0855729c9 gsm_subscriber_connection: mark BSC specific items
The struct shall be split in two later.

Change-Id: Ib9666225fb9bfec2cf1e364343560571869fe6a7
2016-09-29 13:20:44 +02:00
Neels Hofmeyr
031a1e3523 osmo-nitb: exit when MNCC socket init failed 2016-09-29 13:20:44 +02:00
Neels Hofmeyr
84d8db4616 split bsc_bootstrap_network() in alloc and config
Change-Id: I480a09a31a79766ad07b627dd5238b7e37f3be7a
2016-09-29 13:20:44 +02:00
Neels Hofmeyr
1cd730a3b5 split subscr_con_allocate()/_free() in bsc_ and msc_
Rename current subscr_con_allocate() and subscr_con_free to bsc_*,
and add two separate msc_subscr_con_allocate() and _free().
The msc_subscr_con_free() ignores all lchan members.

In libbsc use bsc_*, in libmsc use msc_*.

Change-Id: I3cf7c7cafdf4672ec7b26058bba8a77159855257
Future: there will be distinct subscr conns for libbsc and libmsc.
2016-09-29 13:20:44 +02:00
Neels Hofmeyr
7abc527d12 move to libxsc: net timezone VTY config
Leave the timezone VTY output in libbsc's config_write_net(), until the BSC/MSC
separation of struct gsm_network is completed.

Change-Id: I9712b2e07b4f1ab8d2e4ad40a8d771e98ed25b20
2016-09-29 13:20:44 +02:00
Neels Hofmeyr
b603030a77 Move timezone settings up to network level
Time zone used to be configurable per-BTS. In the upcoming MSC-split, no BTS
structures will be available on the MSC level. To simplify, drop the ability to
manage several time zones in a core network and place the time zone config on
the network VTY level, i.e. in gsm_network. If we are going to re-add fine
grained time zone settings, it should probably be tied to the LAC.

Adjust time zone VTY config code (to be moved to libxsc in subsequent commit).

Adjust time zone Ctrl Interface code.

Change-Id: I69848887d92990f3d6f969be80f6ef91f6bdbbe8
2016-09-29 13:20:44 +02:00
Neels Hofmeyr
ed81beb9ad reinvent connection_for_subscr() and move to libmsc
Implement connection_for_subscr() from a completely different angle: instead of
looking up lchans in bts structs, look up the subscriber in the global list of
gsm_subscriber_connection. static lchan_find() is thus obsoleted.

All callers of connection_for_subscr() live in libmsc, so move to libmsc.

The move and edit are done in a single commit since the old and new
implementation have nothing in common.

Future: osmo-cscn will use this, without bts being present.

Remove implementation of connection_for_subscr() from channel_test.c -- it is
possible that the abort() in there was intended for a regression test, but
actually it seems the implementation was merely added for linking reasons, and
the abort() added to guard against the NULL return value: no comment nor the
commit log indicate that the abort() is test critical; the addition was the
only change in channel_test.c for that commit; at the same time a
connection_for_subscr() call was added in libmsc.
2016-09-29 13:20:43 +02:00
Neels Hofmeyr
741585fb13 bsc vty: rename show_net_cmd to bsc_show_net_cmd
Future: there will be an MSC-land show-net-cmd, so rename to something with
bsc in its name.
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
3aa96c7e14 move to libxsc: network VTY that isn't BSC-specific
Keep only BSC specific bits of the 'network' VTY node in bsc_vty.c, move more
general VTY commands to xsc_vty.c.

Add arg to xsc_vty_init() to pass a config_write_net() function. Pass a libbsc
specific config_write_net() function.

Future: upcoming omso-cscn will re-use the VTY bits moved to libxsc and pass a
different config_write_net() function.

Change-Id: I871b7b32a0c56fdce983e409cf244ec487d24e71
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
b18b7fb660 move to libxsc: global vty gsm_network pointer
Move gsmnet_from_vty() and the bsc_gsmnet global to xsc_vty.c.

Rename bsc_gsmnet to vty_global_gsm_network and make it static to xsc_vty.c, to
clearly mark the global variable for VTY use only.

Introduce xsc_vty_init() to set vty_global_gsm_network.

Change-Id: I26c5c47de08f899b896813d09612d5cb2f8e42d6
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
82762fb65e tests: drop unused libmsc, unneeded duplicate libbsc linking
Because of libxsc, tests/gsm0408,subscr,trau no longer need libmsc.
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
48b45f547d sms_next_rp_msg_ref(): use direct pointer to next_rp_ref counter
libbsc and libmsc will have separate subscriber connection structs. Hence don't
rely on gsm_subscriber_connection, but work on a direct pointer to the counter
for the next RP reference.

The only very thin function in gsm_04_11_helper.c thus becomes obsolete: drop
the entire file.

Change-Id: I2a2e9ba6a981a385d1f8f07acbe03536ffed0072
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
0f781d10b6 factor out & introduce struct gsm_encr, in xsc.h
Factor out encryption info from struct gsm_lchan as struct gsm_encr, placed in
xsc.h.

Change-Id: I94015fb9dd511c37c1e3058a0963c780b3f700ac
Future: this will be used by libmsc's subscriber connection, for osmo-cscn.
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
d49efe66dd fix build: osmo-bsc_nat: change linking order
Moving gsm48_create* to libxsc affected linking of osmo-bsc_nat, resulting
in an undefined reference to gsm48_extract_mi().

Fix the issue by placing libfilter.a left of libbsc.a.
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
2635aa6cbd move to libxsc: factor out gen of USSD notify and release complete -- TODO subscr_conn
Both libmsc and libbsc need distinct gsm0480_send_ussdNotify() and
gsm0480_send_releaseComplete() functions to account for the distinct subscriber
connection structs.

The current functions live in libmsc, so add the same in libbsc in new file
gsm_04_80_utils.c.

To avoid too much code dup, move the message generation part of
gsm0480_send_ussdNotify() and gsm0480_send_releaseComplete() to new functions
gsm0480_gen_ussdNotify() and gsm0480_gen_releaseComplete(), placed in libxsc.

Change-Id: I33a84e3c28576ced91d2ea24103123431f551173
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
7c5b0cdb79 move to libxsc: gsm48_create_mm_serv_rej(), gsm48_create_loc_upd_rej()
Used by libbsc, libmsc as well as osmo-bsc and osmo-bsc_nat.
2016-09-29 13:15:35 +02:00
Neels Hofmeyr
c0c3d98b13 move to libxsc: net init 3: actual move
Reincarnate gsm_network_init() as the parts not specific to libbsc.
Move from bsc_network_init() those bits that are not BSC specific (and useful
for upcoming osmo-cscn).

Add libxsc to all linkages that use gsm_network_init().

Note: the only requirement to allow linking gsm_network_init() without libbsc
is to keep the call to gsm_net_update_ctype() out of libxsc. The other items
are kept out of libxsc because it makes sense semantically. But the separation
is not strong in that the BSC specific data members are of course still
omnipresent in struct gsm_network. If bsc_network_init() is not called, these
are not initialized properly -- for now no users of uninitialized members
exist.

So this is just a first step towards a sensible split of the BSC and MSC
gsm_network structs. The long term aim should be to have entirely separate
structs with some common general items.
2016-09-29 13:15:34 +02:00
Neels Hofmeyr
da55fbd759 move to libxsc: net init 2: move bsc_network_init decl to osmo_bsc.h
bsc_network_init() is more fit to live in a BSC specific header.

Change-Id: I9edfb1e748bb1cb484fadd48b0406f5b3098e89b
2016-09-29 13:15:34 +02:00
Neels Hofmeyr
0e57e2e370 move to libxsc: net init 1: rename to bsc_network_init
The gsm_network_init() function initializes a whole lot of BSC specific stuff.
Aiming to move some of it to libxsc, first rename it to bsc_network_init().
This will retain the BSC specific stuff when the move is done.

Adjust all callers.

Future: osmo-cscn will call the more generic part and not the BSC specific
part.

Change-Id: I4816ae19374390fc5c64972f7cad2e9ec3d8bcc3
2016-09-29 13:15:34 +02:00
Neels Hofmeyr
2b2455a95f define mncc_recv_cb_t to avoid code dup
Put mncc_recv_cb_t in xsc.h to avoid header include complications: if placing
right above struct gsm_network, one must include gsm_data.h to use
mncc_recv_cb_t as function parameter in a header, which will include
gsm_data_shared.h, which will include xsc.h (future knowledge). Since I will
need to use mncc_recv_cb_t in xsc.h, including gsm_data.h from there would
introduce an #include loop. Avoid that and define mncc_recv_cb_t in xsc.h to
begin with.

Change-Id: I2e64cffa563750ce9f3172ffba6f9cf5b9280e9c
2016-09-29 13:15:34 +02:00
Neels Hofmeyr
7b616794f2 Add empty libxsc
This will gradually soak up code shared by libbsc and libmsc.
2016-09-29 13:15:34 +02:00
Neels Hofmeyr
c6a44bbab9 fix: send SNDCP XID only on GERAN Gb contexts
Add a condition for GERAN Gb.

SNDCP and IuPS were developed on separate branches, and the merge results in
code trying to use an llme on a UTRAN Iu context where the llme is NULL,
leading to stack corruption upon PDP ctx act.

Change-Id: Ibb20d738c1b64d36630ce8eceb54c33ba4f1b003
2016-09-29 03:24:57 +02:00
Neels Hofmeyr
caeb62d7ff vty_test_runner.py: fix nat_msc_test(): socket attach: reduce timeout, retry
In nat_msc_test(), upon socket timeout, retry up to six times. Reduce the
timeout between retries. This should get rid of sporadic test failures that
we've been seeing a lot on jenkins lately.

Raise an exception upon unexpected vty response.

Print more detail to stdout. Since we would actually want as much output as we
can get in a test suite, remove the 'if (verbose)' and just always print the
connection source. unittest is keeping all stdout silent by default anyway.

Change-Id: I2f83eef55592778e54164a90e1eabeb80fb918da
2016-09-28 23:53:24 +02:00
Harald Welte
7e5bb6283d COSMETIC: 'if' is not a function, so there is space before '('
Change-Id: Ic22623dffce998d70a3c67aa6e445de98f558ed7
2016-09-28 00:47:28 +00:00
Neels Hofmeyr
3ea9fece6d cosmetic: bs11: also use ts_is_tch()
Use the recently added ts_is_tch() function instead of an explicit switch to
determine TCH pchan types. This is a cosmetic change since the bs11 does not
support dynamic channels (which was the main motivator behind ts_is_tch()).

Change-Id: Idf8ce51c76a83210fe3d70e18c51bbaffebb8ad5
2016-09-28 00:28:01 +00:00
Neels Hofmeyr
255dbfe655 dyn TS: fix: e1_config.c: switch(pchan) for dyn TS
Add ts_is_tch() in gsm_data_shared.h/.c and use it to replace a switch on the
pchan in e1_config.c.

This patch is not due to an actual observed failure. A general grep for switch
on pchan turned up this instance that doesn't handle dyn TS properly. Hence
this patch is not actually tested with real equipment.

Change-Id: Ide4f156034bab77140d2d9a8c462d68ae6f0d6a6
2016-09-28 00:28:01 +00:00
Neels Hofmeyr
23c3aa37ac dyn TS: fix: abis_om2000: also handle dyn TS as TCH
Add ts2comb() to switch on dyn TS so that dyn TS in TCH mode are also treated
like normal TCH/H or TCH/F pchans. Use ts2comb() instead of pchan2comb().

Change-Id: Iddc51a4409488d91db59228ca66aaab73ce3f1df
2016-09-28 00:28:01 +00:00
Neels Hofmeyr
c3f72f63af dyn TS: fix: ts_subslots() for TCH/F_PDCH in PDCH mode
In gsm_data_shared.c, add ts_pchan() to determine actual pchan type for dynamic
and non-dynamic TS.

Use in ts_subslots() to fix the value returned for TCH/F_PDCH in PDCH mode.
Adjust the assertion in channel_test.c accordingly.

Drop GSM_PCHAN_TCH_F_PDCH, which is now handled in ts_pchan().
Explicitly add GSM_PCHAN_PDCH as zero in subslots_per_pchan[] (cosmetic).
Adjust the comment in subslots_per_pchan[].

The fix for the number of subslots affects only one caller: bts_chan_load() in
chan_alloc.c. Before this, it would always include a TCH/F_PDCH in the
load_counter->total, now it is skipped when in PDCH mode. Whether this is the
way bts_chan_load() should handle dynamic TS is a separate discussion, so far
I'm only making sure that the two dyn TS kinds act in the same way:
TCH/F_TCH/H_PDCH is only counted when in TCH mode, and TCH/F_PDCH should match.

Change-Id: Icd6668667ad2be7ad20866ffd185bf3b8711ccd6
2016-09-28 00:28:01 +00:00
Neels Hofmeyr
2afffd5cf6 Revert "bts: extend bts_chan_load to allow counting tch only"
This reverts commit 308cb0719d.

Problems in this commit:

openbsc/src/libbsc/chan_alloc.c:523:   case GSM_PCHAN_TCH_F_PDCH:
This is actually wrong, GSM_PCHAN_TCH_F_PDCH use ts->flags, not ts->dyn below
(due to historical reasons and could be unified).

560:   if (only_count_tch && !chan_is_tch(ts))
This has exactly one effect: it excludes GSM_PCHAN_TCH_F_PDCH when in PDCH
mode, because for all other PDCH (plain PDCH and TCH/F_TCH/H_PDCH in PDCH mode)
below ts_subslots() returns 0 and skips the for() loop. I consider this a bug
in TCH/F_PDCH, to be fixed in an upcoming commit.

I don't see why we need the only_count_tch argument, because this should
normally only count TCH, weren't it for the TCH/F_PDCH bug.

If dyn TS should be counted differently, we should do this in a different way.

Change-Id: I34dbbaf53a800115e3d03bd44028cad675f3b525
2016-09-28 00:28:01 +00:00
Neels Hofmeyr
e289a2a86a channel_test: test nr of subslots for dyn pchan, with error
Add test_dyn_ts_subslots() and call from main(). Update channel_test.ok.

This includes erratic assert to show a bug for TCH/F_PDCH in PDCH mode: the nr
of subslots should be the same as for a normal PDCH, i.e. zero. This will be
adjusted along with the fix in an upcoming commit.

Change-Id: I09685be3fb3ed1ead4577b772a9fbc31967980d1
2016-09-28 00:28:01 +00:00
Neels Hofmeyr
8d878e8a28 channel test: prepare to add another test function
Move the main() guts to test_request_chan(), so that I can add another test in
an upcoming commit.

Change-Id: I1349d0f416806416080d4667ad697f7db1ea252d
2016-09-28 00:28:01 +00:00
Neels Hofmeyr
57e8a1fee0 cosmetic: comment typo on e1_config.c
Change-Id: I894adf562670abf26665a1eb09592682ab8b31b5
2016-09-27 09:59:39 +00:00
Philipp
3163f336f2 SLHC: Improving slhc (RFC1144) testcase
- Adding Testcases for  UNCOMPRESSED_TCP and TYPE_IP
- Minor cosmetic changes

Change-Id: I555fa3c9b9f78424102f359ef1c27b290fa9c9e9
2016-09-27 05:52:20 +00:00
Neels Hofmeyr
dab3e34d0b log VTY telnet bind only once
After libosmocore 55dc2edc89c1a85187ef8aafc09f7d922383231f which outputs
'telnet at <ip> <port>' from telnet_init_dynif(), there's no need to log the
telnet VTY bind here anymore.

Change-Id: I97a730b28759df1d549a5049f47a3da1c16a3447
2016-09-27 05:00:18 +00:00
Neels Hofmeyr
89d20b60ef vty_test_runner.py: raise exception when MSC socket connection fails
Instead of below error, raise an exception to describe what's happening.

Seen in a jenkins run on https://gerrit.osmocom.org/#/c/945/2:

ERROR: testBSCreload (__main__.TestVTYNAT)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./vty_test_runner.py", line 787, in testBSCreload
    msc = nat_msc_test(self, ip, port)
  File "./vty_test_runner.py", line 1251, in nat_msc_test
    return conn
UnboundLocalError: local variable 'conn' referenced before assignment

Change-Id: Iae26e7345267a21aed0b108b089453832889c9fa
2016-09-26 13:00:35 +02:00
Neels Hofmeyr
23d37c91af cosmetic: vty_test_runner.py: add comment for vim auto settings
Change-Id: I61a0476a0317b011432bb4f6f593cfdcaf1c072b
2016-09-26 13:00:35 +02:00
Neels Hofmeyr
a9f2bb5ab8 mscsplit: directly access gsm_network backpointer from gsm_subscriber_connection
The previous commit added a network backpointer to gsm_subscriber_connection.
Use it wherever it makes sense, to skip the step through the bts structure.

In some places, remove local variables that become unused.

Change-Id: I34537025986713291e14c8212a81539b497befd4
2016-09-26 02:25:46 +02:00
Neels Hofmeyr
5e0b0a658f mscsplit: add gsm_network backpointer to gsm_subscriber_connection
We want to be able to use a network backpointer without having to go through a
gsm_bts struct.

This commit adds the network pointer, the subsequent commit applies direct
access to the network structure from gsm_subscriber_connection.

Change-Id: If8870972f1b3e333c2a4cce97cdc95bdee0382a1
2016-09-26 02:25:46 +02:00
Neels Hofmeyr
663debcb90 mscsplit: abis vty: decouple from global bsc_gsmnet variable
Publish gsmnet_from_vty() in openbsc/vty.h and use in the abis VTY functions.

Change-Id: Ib65a18db06b8bc4fc7d56bf56dd64a52cc1cd253
2016-09-26 02:25:46 +02:00
Neels Hofmeyr
43d86bfc94 mscsplit: bsc_vty_init(): decouple from global bsc_gsmnet
Add an explicit gsm_network pointer instead of using the bsc_gsmnet global.
This allows passing a gsm_network struct from the main() scope, which helps to
decouple libmsc from libbsc.

Change-Id: I9e2c0d9c18d4cebb5efb71565ad84df2bc2e0251
2016-09-26 02:25:46 +02:00
Neels Hofmeyr
c13e687742 mscsplit: talloc_ctx_init(): decouple from global tall_bsc_ctx
Decouple the talloc context allocations from global tall_bsc_ctx pointer.

It appears that talloc_ctx_init() was intended for general use, since it is
located in libcommon. It is currently used only by osmo-nitb; but the upcoming
osmo-cscn will use it as well.

Instead of defining in osmo-nitb main file, add definition in gsm_data.h.

Change-Id: I168106599b788f586be0ff0af4699b9746c1b103
2016-09-26 02:25:46 +02:00
Neels Hofmeyr
77c8d5ffb5 mscsplit: gsm_network_init(): add explicit root talloc ctx
Decouple the root talloc context from libbsc's global talloc_bsc_ctx.

This allows to define the root talloc ctx from a main() scope, which in turn
helps decouple libmsc from libbsc.

Change-Id: I92f6b47b1eeea2e8f3fba66f25d7e708e5659f8a
2016-09-26 02:25:46 +02:00
Neels Hofmeyr
d90fa42dc9 mscsplit: move subscriber conns list into struct gsm_network
Replace the global sub_connections llist with gsm_network.subscr_conns.
Initialize and apply where applicable.

Remove bsc_api_sub_connections(), callers now access gsm_network->subscr_conns
directly.

This allows using the subscr_conns from libmsc without having to link libbsc.

Change-Id: Ice2a7ca04910bcfaaff22539abe68a6349e8631c
2016-09-26 02:25:46 +02:00
Neels Hofmeyr
0ce98c749a mscsplit: bsc_init: don't pass telnet dummy conn
We want to create the telnet for VTY only after reading the config file, and
the dummy_conn was a workaround to be able to do so, but is not needed:
gsmnet_from_vty() used to expect vty->priv to point to a gsm_network struct,
but that is not actually the case anymore. It is using a static pointer to
store the gsm_network struct instead.

Change-Id: I51e7224c5a4cd5baf564bee871cf2fa6e885cda7
2016-09-26 02:25:46 +02:00
Philipp
73f83d533b SNDCP: add V.42bis data compression functionality
- Add compression control for V.42bis Add code to handle compression
   (gprs_sndcp_dcomp.c/h)
 - Add Adjustments in SNDCP
 - Add VTY commands

Change-Id: I6d36cbdf2f5c5f83ca9ba57c70452f02b8582e7e
2016-09-24 03:17:59 +00:00
Philipp
d8b45778de V.42bis: integration and unit test
- Edit previously committed V.42bis implementation to function
   outside IAXmodem.
 - Add unit test to verify the correct function of V.42bis

Change-Id: I689413f2541b6def0625ce6bd96f1f488f05f99d
2016-09-24 03:17:59 +00:00
Philipp
0b11db7e9f V.42bis: add sourcecode from IAXmodem (SPANDSP)
V.42bis is a data compression method found in modems. It has also
been specified for GPRS as data compression algorithm.

The implementation has been taken from IAXmodem:

https://sourceforge.net/p/iaxmodem/code/HEAD/tree/
svn checkout svn://svn.code.sf.net/p/iaxmodem/code/ iaxmodem-code
Revision: r36

Change-Id: Iabedece9f97ca944a1e3f747bb073e532c4e9dca
2016-09-24 03:17:59 +00:00
Philipp
f1f34360fb SNDCP: add RFC1144 header compression functionality
- Add module to handle compression entities
- Add module to control header compression
- Introduce VTY commands for heade compression configuration
- Add changes in sndcp and llc to integrate header compression

Change-Id: Ia00260dc09978844c2865957b4d43000b78b5e43
2016-09-24 03:17:58 +00:00
Philipp
2c7f83762a RFC1144: integration and unit-test
The previously pushed slhc implementation has been modified to compile
and function outside of the kernel. Also debug log messages were added
and datatypes ware matched. The implementation is now ready to be used

Change-Id: I7a638e88a43b3eb9d006751a03ef2570e36613f0
2016-09-24 03:17:58 +00:00
Philipp
b3e116c74d RFC1144: add slhc code from linux kernel
SLHC is an Implementation of RFC1144 TCP/IP header compression. We will need
RFC1144 compression to compress GPRS TCP/IP traffic. The implementation pushed
with this commit was taken from:

git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
commit 29b4817d4018df78086157ea3a55c1d9424a7cfc

Change-Id: Ied69c143678dc4a64cecc671f5c4dfebe19d8519
2016-09-24 03:17:58 +00:00
Philipp
22611be3d9 SNDCP: add SNDCP-XID encoder/decoder and unit test
The SNDCP-XID (or layer-3 xid) is used to exchange layer-3 parameters
such as compression. The encoder encodes a bytestream that is then
sent as regular XID field from LLC.

We will need the SNDCP-XID to negotiate the parameters for our
upcomming GPRS data and header compression features

Change-Id: If2d63fe2550864cafef3156b1dc0629037c49c1e
2016-09-24 03:17:58 +00:00
Alexander Couzens
308cb0719d bts: extend bts_chan_load to allow counting tch only
Change-Id: I86f1d502649747b6b9aefcb39081b14110e8f494
2016-09-23 02:43:12 +00:00
Neels Hofmeyr
7c359eb4b4 cosmetic fixes in libcommon/talloc_ctx.c
Add copyright notice, remove obsolete include, remove unneeded line break.

Change-Id: I4d06a0323aee5a003b06edd179fc61e1936acae5
2016-09-22 21:11:42 +02:00
Neels Hofmeyr
8ce66fd19e cosmetic: transaction.h: 1 comment typo, 1 whitespace
Change-Id: Ia2629f9d9887b50b25c6996531b7ef518fb33335
2016-09-19 11:51:12 +00:00
Neels Hofmeyr
1e918c3d31 debug log for sms: fix/add
One logged the wrong function name. Add others.

Change-Id: Ied5d8e84d5d192c826bc131be8907eaa55190479
2016-09-18 23:40:06 +02:00
Neels Hofmeyr
ffaed9eed2 Sanity fixes for gsm0408_dispatch(): rc, assertions
gsm0408_dispatch() is the main entry point for receiving data from the BSC/RNC
level, so make sure callers pass valid pointers before using them all the way
down the code path (related to CID#93769, a fix before this was refactored).

For unknown/unimplemented packet discriminators, make sure to return error
codes.

Change-Id: Ieec39c74a53ef4dfa971dd935c8c9aa60fef58c1
2016-09-18 23:40:06 +02:00
Neels Hofmeyr
378a492fd9 cosmetic: various comment, whitespace tweaks
Change-Id: I131939cfba4d67d7e2c935341deeb14d09523fee
2016-09-18 23:40:06 +02:00
Neels Hofmeyr
0b607297e6 utils/Makefile.am: remove unused LIBOSMOVTY_CFLAGS
Change-Id: Id1152b105bb7364a06d9720829d39f587242b707
2016-09-18 23:40:04 +02:00
Neels Hofmeyr
d1fdefedf1 vty l3 help: fix typo 'comamnds'; fix english s/his//
Change-Id: I6be52bbb69de8aa0a6d57a3a320661ad85fc2cc4
2016-09-18 23:35:49 +02:00
Neels Hofmeyr
6d82c351b9 remove unused bsc_copyright from bsc_vty.c
Change-Id: I281791c0f57ca75ffe14431a3030811b2d224f0b
2016-09-18 23:35:49 +02:00
Neels Hofmeyr
ab04fb2d50 properly #include <openbsc/gsm_data.h> from gsm_subscriber.h
Don't use quoted, local include, use <> style include.

Cosmetic: also move stdbool.h include to the top to keep osmocom and openbsc
includes grouped.

Change-Id: Iaa3dc36768f96f6b8c91010a2ba389fdc37f1503
2016-09-18 23:35:49 +02:00
Max
292ec58e67 Modify SI 13 field for control_ack_type
Add vty function to explicitly set use of 4xRACH type of ack message for
PACKET CONTROL ACKNOWLEDGMENT. Previous hardcoded value (use RLC/MAC
control block) is used as a default.

This is handy for debugging issues related to Timing Advance in context
of GPRS.

Change-Id: Ie869ac0a82055110f1e3b875e246750c4e113336
Related: OS#1526
2016-09-17 10:00:58 +00:00
Neels Hofmeyr
2867f883a1 log causing rx event for lchan_lookup errors
Add log_name to lchan_lookup() and pass such from the various RSL rx events
that call it to validate the RSL chan_nr.

Change-Id: Id81e7b8b9c27831923f050a78dfc7d650e687033
2016-09-17 09:58:54 +00:00
Neels Hofmeyr
b3d8706bea log: abis_rsl: don't log 'error' when there is no error
The message 'RF Channel Release due error 0' keeps catching my eye because
it says 'error' even though the error code is zero, i.e. no error.
This shall end now.

Change-Id: Ie0b9d62e8ce85a096c963931e0ae5527b8dc490a
2016-09-17 09:58:53 +00:00
Alexander Couzens
aa386d29fd sms: change rp err cause of smpp_try_deliver errors
smpp_try_deliver could fail with rc < 0. In such cases don't send the MS the rp
error sms rejected (cause 21). A rejected message should not be sent again. The
spec 04 11 recommends sending cause 41 Temporary failure in unknown cases.

Add also a log message and rate counter for such cases.

Tweaked-By: Neels Hofmeyr <nhofmeyr@sysmocom.de>
Change-Id: Ia03e50ce2bd9a7d1054cc5a6000fd73bd3497c03
2016-09-17 08:45:07 +00:00
Alexander Huemer
c2f2ad8a5f Build fixes
Some fixes for build environments where dependencies are installed in
distinct directories.

Change-Id: I38808fd2911747b266ee6fde91187a88dd7ae355
2016-09-15 16:02:18 +02:00
Alexander Huemer
7b6673fa06 Consistenly format variables in */Makefile.am files
Change-Id: Ifa21513c007072314097b7bec188579972dc1694
2016-09-15 15:55:02 +02:00
Alexander Couzens
58f446ca08 gprs/gprs_llc: fix null pointer deref in gprs_llc_rcvmsg
Change-Id: I1f7e1d524042134c93a4f3de599c54d442447512
2016-09-05 13:36:01 +00:00
Alexander Couzens
a173566b35 gprs/gsm0408_gprs_force_reattach_oldmsg: check llme before use
Change-Id: I9385655872c4dcf46aa1d18bcc47b84aba2f34f7
2016-09-05 13:36:01 +00:00
Daniel Willmann
21b269f814 IuPS: Change GTP-U endpoint to SGSN in PMM_IDLE and page UE when data arrives
Change-Id: I47b73a40cbdda6b7c31fb2767f74f9f93d84056b
2016-09-02 04:29:36 +02:00
Daniel Willmann
c17cdb40b5 IuPS: Introduce function to change PMM state
This is where IuPS will redirect GTP-U endpoints in a subsequent commit.

Also add comprehensive logging of pmm_state transitions.

Change-Id: I7c2cd1abc1805659b01dffffff31c49fe5161086
2016-09-02 04:29:21 +02:00
Daniel Willmann
fac9758820 IuPS: GMM Attach: reset MM ctx pending_req
Change-Id: I0df0f3d88085939eb617405e2013ad164eed477b
2016-09-02 04:29:15 +02:00
Daniel Willmann
5b2363ebb2 IuPS: sgsn_mm_ctx: add enum gprs_pmm_state field, track PMM state
Iu needs to page to transfer data in PMM-IDLE state.

Change-Id: Id37778cb9a0328a21c8e8246998ecdb43dd687d8
2016-09-02 04:29:01 +02:00
Daniel Willmann
af241727a9 IuPS: RA UPD: make sure to authorize, for Iu Integrity Protection
Change-Id: I2ea2089895f8a8e125ef39d9bef70dafb2b1ce69
2016-09-02 04:28:49 +02:00
Daniel Willmann
1dee2b6e96 IuPS: add GMM Service Request rx and tx
Change-Id: Ib935de22d23a15f449927840d4d59497ce22abbd
2016-09-02 04:28:41 +02:00
Daniel Willmann
3ecfbbba6f IuPS: send Security Mode Command, track the new_key flag.
Change-Id: I0b2593c2df13b79eb36975b0d302e31cfdf8bb09
2016-09-02 04:28:31 +02:00
Daniel Willmann
770f3e32e0 IuPS: dev hack: init hardcoded Ki on ATT REQ
DEVELOPMENT HACK: Our current HLR does not support 3G authentication tokens.  A
new HLR/VLR implementation is being developed. Until it is ready and actual
milenage authentication is properly supported, we are hardcoding a fixed Ki and
use 2G auth.

Change-Id: Ieca45960fa941a3a706c6e479b04b9f2ef89d860
2016-09-02 04:28:16 +02:00
Daniel Willmann
7bc6986f6b IuPS: add Iu response to delete_pdp_conf()
Change-Id: I6d601586101c0a004b2243633fab48db82b44b7c
2016-09-01 23:44:45 +02:00
Daniel Willmann
6b7b319d54 IuPS: add Iu response to create_pdp_conf()
Change-Id: Iad65ca9b77c3166d4df9a58af527e6aef7e589ee
2016-09-01 23:44:45 +02:00
Daniel Willmann
61329d45b8 IuPS: redirect Iu in various places, link Iu in sgsn-test
In gsm48_gmm_sendmsg(), redirect to iu_tx() for both cases of MM context
present or not.

In gsm48_rx_gmm_att_req(), compose an MM context marked as Iu for messages
coming in from a ue_conn_ctx (passed in msg->dst). Also make sure cid is
initialized to avoid introducing a compiler warning.

In gsm48_rx_gmm_ra_upd_req(), look up an Iu MM context based on the presence of
the ue_conn_ctx in msg->dst.

In sgsn-test, add libiu and libasn1c, libosmo-sigtran, libosmo-ranap, which are
now needed for an --enable-iu build.

Change-Id: Ia47ffbfa6fa0f5a0cd76a379c57ef42faa0d80e3
2016-09-01 23:44:45 +02:00
Daniel Willmann
6292c8d44d IuPS: osmo-sgsn: add core IuPS impl, call iu_init()
Add main Iu entry points for IuPS:
* gsm0408_gprs_rcvmsg_iu()
* sgsn_ranap_iu_event()
* sgsn_ranap_rab_ass_resp()

Add main MM context management for IuPS:
* sgsn_mm_ctx_by_ue_ctx()
* sgsn_mm_ctx_alloc_iu()

Call iu_init() from sgsn_main.c.

Add asn_debug impl ("extern" from libasn1c).
Initialize asn_debug VTY command (iu_vty_init()).

osmo-sgsn build: add libiu and libasn1c, libosmo-sigtran, libosmo-ranap

Change-Id: I469ae6ca9ef254d04ee0d2d79bdd65aebcd027b5
2016-09-01 23:41:10 +02:00
Neels Hofmeyr
9bc42ec47b IuPS: add VTY config for asn_debug
Add file iu_vty.c in libiu, and iu_vty_init() to initialize the new VTY
command:

  log
   logging asn1-debug (1|0)

Change-Id: If4e7d0ab3fc2ed0cdf4fb0a3fa077a9e34890918
2016-08-31 11:11:07 +00:00
Harald Welte
7e82ad20fa osmo-nitb: generate backtrace on SIGABRT
As the NITB has an internal SIGABRT handler that prints a talloc report,
let's also print a stack backtrace at the same point.

Change-Id: Ia63aa5c39b26e27c3ee220d755c17d2c1ef636c5
2016-08-31 11:07:35 +00:00
Alexander Couzens
4b95b5401c bsc/netinit: correct mistyped rate counter
Introduced by b847a21fa4

Change-Id: I57c41f98e3826951a5071b005cb640c23d466477
2016-08-30 14:39:43 +02:00
Alexander Couzens
b847a21fa4 libmsc/bsc: split rate counters into bsc and msc group
Tweaked-By: Neels Hofmeyr <nhofmeyr@sysmocom.de>
Change-Id: I7361033cd1eb919ec3c2ea2652f40ab8c75b2f99
2016-08-29 18:56:20 +02:00
Daniel Willmann
d75864f6f5 IuPS: track msg->dst aka ue_conn_ctx, comment
For Iu connections, msg->dst will point to the ue_conn_ctx, and we need to make
sure to keep msg->dst intact when copying from/to msgb and from/to MM context.

Change-Id: I90c7ca6c3655d447aaca958e0086ae6ce6f6045a
2016-08-27 13:29:33 +02:00
Daniel Willmann
746c7896cb gprs_gmm: Fix bit mask when determining update/attach type
Bit 4 is reserved in 3GPP TS 04.08 so exclude it from the type.

In 3GPP TS 24.008 it indicates if a follow-on request is pending by the
MS, but only in Iu mode. According to the spec it is not required to
react to that request with a follow-on proceed so this field can be
ignored for now.

See 3GPP TS 24.008 Ch. 4.4:
"Unless it has specific permission from the network (follow-on proceed)
the mobile station side should await the release of the RR connection
used for a MM specific procedure before a new MM specific procedure or
MM connection establishment is started."

as well as Ch. 4.4.4.6:
"If the network wishes to prolong the RR connection to allow the mobile
station to initiate MM connection establishment (for example if the
mobile station has indicated in the LOCATION UPDATING REQUEST that it
has a follow-on request pending) the network shall send "follow on
proceed" in the LOCATION UPDATING ACCEPT and start timer T3255."

Change-Id: If1dff960c406060e257dafc54132687ffc42ad8f
2016-08-27 13:29:33 +02:00
Neels Hofmeyr
f4daf16c8d cosmetic: gprs_sgsn.c: move pdp.h include to top
Change-Id: I9a9b34d714235462ba72cdb65b7c8c9824dfa9c6
2016-08-27 13:29:33 +02:00
Neels Hofmeyr
bfa8878a07 add libiu
Co-Authored by dwillmann, laforge, nhofmeyr

Change-Id: Iffc26f9c73cb15463948f7435b72ac1747aabdb3
2016-08-27 13:29:30 +02:00
Philipp
4ac3aee711 Adding LLC-XID related modifications in LLC
With this commit the already existing XID mechanism has been
modified to suit the needs for the upcomming SNDCP-XID patches.

This commit should not break anything since it does not alter
the current behaviour (incoming XID is still just echoed, on
GMM-Reset a basic XID message is still echoed)

Change-Id: I65b9d625e72d3d61c99abdc7041773701d694d52
2016-08-27 04:45:55 +00:00
Philipp
3ec03d5048 Moving grs_sndcp.h header file to include
For some reason gprs_sndcp.h is located in src/gprs. This commit moves
gprs_sndcp.h to include/openbsc and fixes the include path in
gprs_sndcp.c and gprs_sndcp_vty.c

Change-Id: If4e4f1252c81d7907c1b4d738c982bb172b128c9
2016-08-27 04:45:55 +00:00
Philipp
a536fc644b Adding LLC-XID encoder / decoder and unit test
The lle-xid encoder/decoder is needed to encode and decode llc
xid parameter messages. We need this to exchange sndcp-parameters
(SNDCP-XID) and also simple parameters such as encryption IOVs

Change-Id: Ia06e4cb08bf9b48c2a4682606d1b1a91d19a9d37
2016-08-27 04:45:54 +00:00
Neels Hofmeyr
d5d39ae2b6 log: rsl notice: tiny tweak for readability
Change-Id: I57c3b7d27d857c96e3fa3dacf7b766bc43100fc3
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
423269f803 log: improve for rsl_lchan_mark_broken()
In rsl_lchan_mark_broken(), call rsl_lchan_set_state() so the state transition
gets logged in the debug log.

Remove logging for the broken channel at the callers, instead log the error
actually in rsl_lchan_mark_broken() itself, with the reason message passed by
the caller anyway. (Removes code dup and ensures it's always logged.)

Change-Id: I54ae9bbd3f193bae7b1bda1fef3e33e62b353bf5
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
baa6c5546e dyn TS: debug log: if still in use, also log lchan type and state
Change-Id: Ifbf31cde24b2d1022b7a472966c17959c96e6dda
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
a0a08d80b8 dyn TS: debug log 'switchover complete' only when there was a switchover
Change-Id: I7ddcb41edce1cd7b22fe91e33bdcaedb21856222
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
d35fc4408c dyn TS: fix OS#1798: on late RF CHAN REL ACK, activate PDCH
Tested by hacking a REL ACK delay of a couple of seconds into osmo-bts' rsl.c
for the first TCH_H lchan:

[[[
diff --git a/include/osmo-bts/rsl.h b/include/osmo-bts/rsl.h
index 093e9cb..b35c3bb 100644
--- a/include/osmo-bts/rsl.h
+++ b/include/osmo-bts/rsl.h
@@ -22,6 +22,7 @@ int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int
 int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause);
 int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
 int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
+int rsl_tx_rf_rel_ack_later(struct gsm_lchan *lchan);
 int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay);

 /* call-back for LAPDm code, called when it wants to send msgs UP */
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 3802e25..1f92b0d 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -491,7 +491,16 @@ static int l1sap_info_rel_cnf(struct gsm_bts_trx *trx,

 	lchan = get_lchan_by_chan_nr(trx, info_act_cnf->chan_nr);

-	rsl_tx_rf_rel_ack(lchan);
+	static int yyy = 0;
+
+	DEBUGP(DRSL, "%s YYYYYYYYYYYYYYYYYYYYY %d %s\n",
+	       gsm_lchan_name(lchan), yyy, gsm_lchant_name(lchan->type));
+
+	if (lchan->type == GSM_LCHAN_TCH_H && !yyy) {
+		yyy ++;
+		rsl_tx_rf_rel_ack_later(lchan);
+	} else
+		rsl_tx_rf_rel_ack(lchan);

 	/* During PDCH DEACT, this marks the deactivation of the PDTCH as
 	 * requested by the PCU. Next up, we disconnect the TS completely and
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 3c97af9..7926f21 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -534,6 +534,22 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
 	return abis_bts_rsl_sendmsg(msg);
 }

+struct osmo_timer_list yyy_timer;
+
+static void yyy_timer_cb(void *data)
+{
+	rsl_tx_rf_rel_ack(data);
+}
+
+int rsl_tx_rf_rel_ack_later(struct gsm_lchan *lchan)
+{
+	yyy_timer.cb = yyy_timer_cb;
+	yyy_timer.data = lchan;
+	osmo_timer_schedule(&yyy_timer, 10, 0);
+	return 0;
+}
+
+
 /* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
 static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
 {
]]]

Change-Id: I87e07e1d54882f8f3d667fa300c6e3679f5c920d
Fixes: OS#1798
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
a2ef7d6477 dyn TS: fix: properly run an lchan activation timeout
Actually schedule an activation timer for the activation part of a dyn TS
switchover. It needs to be restarted because the channel release procedure in
the first part of a switchover actually removes the activation timer.

Change-Id: Ibf50d13ba10298464a8b07e34716763161438990
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
b74a2c8e29 dyn TS: clearly use lchan[0], fixing minor confusion
The dyn_ts_switchover_*() functions made the impression that they act on a
specific lchan of a timeslot. The assumption that we would remember to use e.g.
lchan[1] across a PDCH deactivation is brain damaged to begin with; and
factually we always use lchan[0] anyway (the only case for using lchan[1] would
be when switching to TCH/H, but the channel allocator will always return
lchan[0] for that).

Instead of the brain damaged lchan args, use a ts arg across all
dyn_ts_switchover_*() functions, with one exception: The
dyn_ts_switchover_complete() actually receives an RSL activation ack message on
a specific lchan and needs to evaluate its lchan type. This will always be
lchan[0] as it is now, but we should stick with the lchan the message was sent
for.

For PDCH, a check to use lchan[0] already existed, when composing the ACT
message in rsl_chan_activate_lchan_as_pdch(). Replace with an assertion.

Adjust all callers to pass ts instead of lchan.

In dyn_ts_switchover_start(), there was a dead code check that jumps to
switchover_complete() in case the pchan already matches. This never hits,
because we only call dyn_ts_switchover_start() when pchans mismatch. So avoid
guessing at passing lchan[0] to dyn_ts_switchover_complete() by not calling it
at all but logging an error instead.

In rsl_chan_activate_lchan(), we remember some values before going into
switchover from PDCH. Explicitly store them in lchan[0], because after a PDCH
release we have always and will activate no other than lchan[0].

In dyn_ts_switchover_continue(), move the check for any existing lchan->rqd_ref
further above, and more correctly check all lchans that were so far valid on
the TS, instead of just one.

This partly prepares for a subsequent commit to fix the act_timer use for dyn
TS: with the old lchan arg, we might schedule an activation timer on lchan[1]
but receive an ack on lchan[0] (for PDCH), leading to an act_timer expiry.

Change-Id: I3f5d48a9bdaa49a42a1908d4a03744638c59796a
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
cd150a8f74 dyn TS: fix error recovery: switch to PDCH after lchan error state
Tested by hacking a CHAN ACT ACK delay of a couple of seconds into osmo-bts'
rsl.c for the first TCH_H lchan:

[[[
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 3c97af9..4bfd27a 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -559,6 +559,22 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
 	return abis_bts_rsl_sendmsg(msg);
 }

+struct osmo_timer_list xxx_timer;
+
+static void xxx_timer_cb(void *data)
+{
+	rsl_tx_chan_act_ack(data);
+}
+
+static int rsl_tx_chan_act_ack_later(struct gsm_lchan *lchan)
+{
+	xxx_timer.cb = xxx_timer_cb;
+	xxx_timer.data = lchan;
+	osmo_timer_schedule(&xxx_timer, 10, 0);
+	return 0;
+}
+
+
 /* 8.4.7 sending HANDOver DETection */
 int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay)
 {
@@ -614,6 +630,18 @@ int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause)

 	if (cause)
 		return rsl_tx_chan_act_nack(lchan, cause);
+
+	static int xxx = 0;
+
+	DEBUGP(DRSL, "%s XXXXXXXXXXXXXXXXXXXXX %d %s\n",
+	      gsm_lchan_name(lchan), xxx, gsm_lchant_name(lchan->type));
+
+	if (lchan->type == GSM_LCHAN_TCH_H) {
+		if (!xxx) {
+			xxx ++;
+			return rsl_tx_chan_act_ack_later(lchan);
+		}
+	}
 	return rsl_tx_chan_act_ack(lchan);
 }

]]]

Change-Id: Ie82dec9c9fefc476fdf5b5afdad2246b9d6fe304
2016-08-27 02:23:47 +00:00
Neels Hofmeyr
2ae305de46 dyn TS: move check whether to switch to PDCH to separate function
Prepares for an upcoming commit using the same check in error_timeout_cb().

Change-Id: I8abfa964631040f798212cc3e360f67f9e09b7c5
2016-08-27 02:23:47 +00:00
Alexander Couzens
7130683ffe libmsc: add missing count of sms no receiver when using smpp_first
Change-Id: I20ecb3299d67dbaa7b016620685997db49970ffb
2016-08-27 01:58:19 +00:00
Alexander Couzens
20423ea6cf libbsc/libmsc: convert old osmo counter into rate_ctrgs
rate counters support the export to statsd and can have a delta value.

Change-Id: Ie749cebd53a0bb618d0e23d375885712078bf8dd
2016-08-27 01:58:19 +00:00
Alexander Couzens
4e699a9cbf sgsn: add statistics counter for LLC packets
new counters are:

llc.dl_bytes
llc.ul_bytes
llc.dl_packets
llc.ul_packets

The ip payload bytes are waiting for payload compression
because those data are known then.

Change-Id: I068376d35e84283cb98523cd3097a12c55cdb709
2016-08-27 01:27:43 +00:00
Neels Hofmeyr
76a0ad7fe9 move ts_sublots() to gsm_data_shared.c, it will be used by osmo-bts
Change-Id: I8ba06d7dd6e0ceab3d8d18bb565354d6ed461f7e
2016-08-27 01:23:49 +00:00
Neels Hofmeyr
5486025b18 chan_alloc.c: use ts_subslots() instead of subslots_per_pchan[]
The array will move to gsm_data_shared.c; to prepare, use the function
instead.

Change-Id: Icbea7dbd78abf6144e5291f531a97f96507d8cbf
2016-08-27 01:23:49 +00:00
Neels Hofmeyr
3673380cdb dyn TS: bts_chan_load: use correct nr of subslots for dyn ts
For TCH/F_TCH/H_PDCH dynamic timeslots, the ts->pchan does not lead to a
meaningful value from the subslots_per_pchan[] array. Use the ts_subslots()
function instead, which checks for dyn pchan.

Change-Id: I659acebca82dfb3e305433471be64e9d27439af8
2016-08-27 01:23:49 +00:00
Neels Hofmeyr
723f7c7db3 comment: gsm48_gmm_sendmsg(): add spec reference on encryptable
Change-Id: I54a3bc518bc38e38b78f6e9ea3705e4fbd5ffb98
2016-08-22 22:19:13 +00:00
Holger Hans Peter Freyther
91dfa86c18 ci: Attempt to disable doxygen warnings of dependencies
We do not want to see doxygen warnings when building the
libosmocore dependency.

Change-Id: I4640cb5b91d54641e8e5b2f096c3bca49bfff60e
2016-08-15 17:39:28 +00:00
Max
1f6a9ba7e5 Add web proxy for control interface
Add web application exposing Control Interface over web. All of SET, GET
and TRAP are fully supported.

Notice: TRAP is converted into 'Server-sent events' according to RFC
6202, see also https://www.w3.org/TR/eventsource/ - this requires
corresponding client.

Due to use of special prefix modified version of python
eventsource-client is necessary ATM.

Change-Id: I87d40c80061f8b3d02d656ab8cadabbfb871b461
Related: OS#1646
2016-08-11 06:05:39 +00:00
Max
dbb6392368 Add python functions to get/set ctrl variables
Add get_var and set_var functions which handle requested variable while
checking for proper response and id. Split header handling into separate
function.

Change-Id: I08705963c277bd93a011193dd7451a626d606c21
Related: OS#1646
2016-08-11 06:05:39 +00:00
Max
2a63d01c1e Use random operation id
According to documentation for Control Interface Protocol <id> is "A
numeric identifier, uniquely identifying this particular operation",
hence it's best to be illustrated with random integer - use it as
default.

Fix override of id with previously used python-specific objects' id.

Change-Id: I32236c067360526f4e7ee4bbdba64c5137de696d
Related: OS#1646
2016-08-11 06:05:39 +00:00
Neels Hofmeyr
b6f565c97d gsm_pchan2chan_nr(): fix uninitialized cbits
Commit ec1b5a0e9e introduced an unset cbits
value for the 'special hack for BCCH', where I break out of the switch
without setting cbits. Fix that.

Also remove the comment part that says 'return 0', because I don't return 0.

Change-Id: I54129d921807971eeafc23f80c57666c67b71377
2016-08-10 17:14:53 +02:00
Neels Hofmeyr
2f44693fad gsm_pchan2chan_nr: disable a chan_nr assert in BTS, to not break octphy
In https://gerrit.osmocom.org/589 , msuraev reports an assertion on octphy.
So disable this recently added assertion until we clarify the invocation in
question.

Change-Id: Ia0f7ae5b114e179ab56b98adbae9810e81b4b88f
2016-08-10 17:14:48 +02:00
Harald Welte
158b5d2bdb add .mailmap file for mapping git author name/mail in shortlog
Change-Id: I7ed97fb897895935f942e3eb4fd87a8c138417be
2016-08-08 17:40:28 +00:00
Harald Welte
beca090586 add example config for sysmobts
Many years ago, there was no difference between the libbsc support for
nanobts and sysmobts.  However, this is not the case for a long time
anymore, and there are some specifics in OsmoNITB when it comes to
sysmobts.  Let's have an example config file

Change-Id: I94ae57c9a3cb497ca39d56270fa15ed65d7f147e
2016-08-08 11:29:49 +00:00
Max
3ed214c7b0 Improve code re-use
Introduce explicit __main__ function to facilitate re-use of defined
python functions for ctrl interface.

Change-Id: I9bad8f0dd1d69bd28816bf047d85840e3411bb9c
Related: OS#1646
2016-07-29 18:23:21 +02:00
Neels Hofmeyr
5f0c71b7d5 dyn TS: OS#1778 workaround: disable TCH/F on dyn TS for nitb
To avoid two phones picking mismatching TCH pchans, never pick TCH/F on dynamic
TS in osmo-nitb.

Add gsm_network flag dyn_ts_allow_tch_f, set to true by default in
gsm_network_init().

Set this flag to false in osmo-nitb's main().

See http://osmocom.org/issues/1778

Reasoning about ways to solve this:

* a compile time switch doesn't work because libbsc is first compiled and then
  linked to both osmo-nitb and osmo-bsc.

* we could test net->bsc_api == msc_bsc_api(), but I have the so-called MSC
  split waiting on branch sysmocom/cscn, which will result in msc_bsc_api() not
  being linked in the osmo-bsc binary.

* have a function am_i_nitb() with different implementations in osmo-nitb and
  osmo-bsc, but then we'd need to add implementations to all tests and other
  binaries linking lchan_alloc().

* have a flag in struct bsc_api, but so far there are only function pointers
  there.

Having a "global" flag in gsm_network allows to add a VTY command in case we
decide to keep this feature (#1781), has no linking implications and is nicely
explicit.

Tested that osmo-bsc still picks TCH/F on dyn TS indirectly, since I have no
standalone MSC available: when compiling osmo-nitb with the line that sets
dyn_ts_allow_tch_f = false commented out, TCH/F is picked as described in
OS#1778; and by printf-verifying that dyn_ts_allow_tch_f == true in osmo-bsc
main(), only osmo-nitb should have TCH/F disabled.

Related: OS#1778, OS#1781
Change-Id: If7e4797a72815fc6e2bbef27756ea5df69f4bde7
2016-07-28 17:40:59 +02:00
Neels Hofmeyr
c5e75f3e6a dyn TS: Rename bsc_dyn_pdch.c to bsc_dyn_ts.c
It's no longer just for IPAC style TCH/F_PDCH, but also contains code for
TCH/F_TCH/H_PDCH, so pick a more general name.

Change-Id: Ic19db81eca03fd72738839ee3686b6b4c8b6b437
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
d3b7fa837d dyn TS: split dyn_pdch_init() for new dyn type and rename
Init both TCH/F_PDCH and TCH/F_TCH/H_PDCH via dyn_ts_init(), which
refactors dyn_pdch_init().

Make dyn_ts_switchover_start from abis_rsl.c public in abis_rsl.h, so we can
start the initial switchover to PDCH from dyn_ts_init(); in abis_rsl.h include
gsm_utils.h for enum gsm_phys_chan_config.

Change-Id: I5c0b257ba8ff0e9c9a2268681a84b0681a778368
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
b91e6002a6 dyn TS: implement pchan switchover logic
In struct gsm_lchan, add dyn.rqd_ref and dyn.rqd_ta. These save the Channel
Requested details across the PDCH deactivation dance.

abis_rsl.c: add static functions:

* dyn_ts_switchover*() for the various stages of switchover between pchans.

* pchan_for_lchant() to derive the desired pchan from the lchan type that was
  set during lchan_alloc().

* rsl_chan_activate_lchan_as_pdch() to compose the simpler RSL CHAN ACT message
  without introducing numerous special cases to the normal RSL CHAN ACT code.

In rsl_chan_activate_lchan(), detect and initiate required pchan switchovers if
requested pchan on a dyn TS differs.

In rsl_rx_rf_chan_rel_ack(), initiate or continue pchan switchovers after a
channel was released.

In rsl_rx_chan_act_ack(), notice that a switchover is complete.

In chan_alloc.c, add ts_subslots(): abis_rsl.c will need to know the number of
subslots per pchan, to verify that all lchans are free before dyn TS
switchover. The subslots_per_pchan[] array is static to lchan_alloc.c, and
since we need a non-trivial check for dyn TS anyway, add public ts_subslots()
to lchan_alloc.c, which also checks the current dyn pchan type.

Change-Id: I5c6bce13092a10204113d84678c587c65e35e4fd
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
7af652c0b2 dyn TS: chan act: set chan_nr according to dyn pchan type
Change-Id: Ica5ef2197b3e97d5e895f3e3221295d5d0ef8908
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
fdd9ad7c40 dyn TS: enhance channel allocator for dynamic TS
Change _lc_find_bts() to _lc_dyn_find_bts() with added dyn_as_pchan arg to
pass exactly as which pchan we'd like to allocate on a dynamic TS. Add
_lc_find_bts() as wrapper so non-dynamic-TS callers remain unchanged.

Also add dyn_as_pchan arg to _lc_find_trx() (not renaming to dyn and wrapping
because there is only one caller).

Implement dynamic allocator logic in _lc_find_trx() and lchan_alloc().

A returned dynamic channel still needs to be switched to the proper mode, which
will follow in another commit.

Replace a fixme comment with a normal comment in subslots_per_pchan[], because
handling of dynamic TS is now defined.

Change-Id: I18da7679300c43220d9baa6a304e8df74d366249
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
f58852d117 dyn TS: rsl_lchan_lookup(): add dyn PCHAN
Accept GSM_PCHAN_TCH_F_TCH_H_PDCH for TCH/F and TCH/H if in matching pchan mode
or switching to matching pchan.

Accept RSL_CHAN_OSMO_PDCH chan_nr cbits for GSM_PCHAN_TCH_F_TCH_H_PDCH pchan.

Change-Id: If8f7c118f69e5a9f370bfe25f82f3d5a8de75b51
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
9518ffc299 dyn TS: verify_chan_comb(): handle new dyn TS NM_CHANC_*
Change-Id: I7ce754a48c7f492e921a4450745383bb8dd7225c
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
4673b86f3d dyn TS: rsl *2chan_nr(): handle TCH/F_TCH/H_PDCH
In gsm_lchan2chan_nr() use the current pchan type.

In gsm_lchan_as_pchan2chan_nr(), add the special case of non-standard cbits for
activating PDCH on a TCH/F_TCH/H_PDCH dyn TS. This way, gsm_pchan2chan_nr()
conforms to the standard and does not need access to a ts struct.

Change-Id: If248b9073b9f397110a2003d8e1a04afdc1c0e20
2016-07-28 11:56:51 +02:00
Neels Hofmeyr
d384110d3d dyn TS: gsm_lchan2chan_nr(): decouple from ts->pchan
For upcoming dynamic TS, the pchan choice for RSL De-/Activation is not
trivial. So in order to pass the desired pchan to generate the RSL chan_nr,
introduce gsm_lchan_as_pchan2chan_nr().

To avoid code dup, this requires decoupling the gsm_ts2chan_nr() pchan from the
actual ts struct, so refactor gsm_ts2chan_nr() to gsm_pchan2chan_nr() with
explicit pchan, ts_nr and lchan_nr arguments.

Change-Id: I1a40e8452fe8120d350a27973e56be0b8c8c517f
2016-07-28 11:56:49 +02:00
Neels Hofmeyr
6e999b75fa dyn TS: rename lchan->dyn_pdch to lchan->dyn
This will also be used by the new dynamic TS type, so make the name more
general.

Change-Id: I2451b10519dff3e5cdf503b430574c0984d19000
2016-07-28 11:55:03 +02:00
Neels Hofmeyr
cf7933892a prepare dyn TS: act lchan: fetch the channel mode a bit later
Dyn TS will add a new type of chan activation, which does not need a Channel
Mode IE. Incidentally, the dyn PDCH also doesn't need this IE if it opts for
sending a PDCH ACT instead. So it makes sense to compose the Channel Mode IE
only after the dynamic decisions are done.

Change-Id: I66d88ad6a4ae7bee1e552960fd4e92aff953125c
2016-07-28 11:55:03 +02:00
Neels Hofmeyr
e2eb5cb6a1 error log: rsl_chan_activate_lchan: log channel mode error
Change-Id: I0f403b13ff9897770c0b855bf57a9440717b46e8
2016-07-28 11:55:03 +02:00
Neels Hofmeyr
2e84b60652 cosmetic: dyn_pdch_init(): debug log: use new gsm_ts_and_pchan_name()
Change-Id: I396c2696bdbedb41a1f1fe2183f8eada57dc3413
2016-07-28 11:55:03 +02:00
Neels Hofmeyr
ec1b5a0e9e gsm_ts2chan_nr(): add assertions for lchan_nr
Change-Id: Ibfdef347c85d4a145645a7325cd193ea1b475a54
2016-07-28 11:55:00 +02:00
bhargava
350533cc32 Modify SI 13 field to support 11 bit RACH
System Information 13 field EGPRS PACKET CHANNEL REQUEST is
modified to support 11 bit RACH. Further VTY configuration is added
to enable/disable 11 bit RACH support in EGPRS. By default 11 bit
RACH support is disabled.

Change-Id: I51357bec936c28a26ab9ff5d59e0e30ca3363297
2016-07-28 06:49:03 +00:00
Neels Hofmeyr
e3dc498e01 debug log: fix line endings for abis_rsl_rx_rll logging
This function outputs a debug log without line ending, which should be
completed by a subsequent DEBUGPC(), so complete the started log line where
missing in three of the switch cases.

The three cases do print another log message, but since these don't start on a
new line when RLL is in debug level, the log output for these is hard(er) to
read without this patch.

Change-Id: I355647e77e1b2d8e75ae1a167fe87a507a38d82d
2016-07-28 06:31:35 +00:00
Max
e443145d3e Fix default subscriber regexp
Incorrect regular expression used by default to authorize all
subscribers to implement authorization policy 'accept-all' prevented MS
from camping on the open network.

Change-Id: I20284b3d40ecf4ca1e67d8cd25afb8d5e4ae3025
2016-07-27 14:52:14 +02:00
Neels Hofmeyr
d1c0e3755f log lchan_alloc() result
It is particularly interesting to see whether a given lchan type is allocated
on a dynamic timeslot.

Change-Id: I8a0bca6d9cd583a0988e5ee8f4e6f74f218f4185
2016-07-25 17:35:47 +02:00
Neels Hofmeyr
bbbcfe5b73 error log: abis_rsl.c: log errors in channel_mode_from_lchan()
Change-Id: Ifa416eab76e6c26dc83e979d815ae778d0d7133b
2016-07-25 17:35:47 +02:00
Neels Hofmeyr
745857277c code dup: join [rsl_]lchan_lookup() from libbsc and osmo-bts
lchan_lookup in abis_rsl.c and rsl_lchan_lookup() from osmo-bts rsl.c are the
same code, except for the log context, which is only set in abis_rsl.c.
Factor out the common code to rsl_lchan_lookup() in gsm_data_shared.c.

Openbsc and osmo-bts each define their own DRSL log constant, so add an int *rc
return code argument and keep the logging part in abis_rsl.c's thin lchan_lookup()
wrapper. Incidentally, this also removes code dup for logging.

To avoid duplicate symbols, the rsl_lchan_lookup() implementation needs to be
removed from osmo-bts, so older osmo-bts git revisions will not build with
this.

Change-Id: Ie89bc5bb9110a0e539d37991dedac6f913211b48
2016-07-25 17:35:47 +02:00
Neels Hofmeyr
34b8b5b29b gsm_data_shared: add gsm_ts_and_pchan_name() for dyn ts logging
Change-Id: I9b6be77c9e5fb9dffa2021a2da72293af15a03a0
2016-07-25 17:35:47 +02:00
Neels Hofmeyr
c165876223 dyn TS: add ts->dyn state
Add state fields osmo_bts_trx_ts->dyn.* to record dynamic timeslot state.
Initialize in gsm_bts_trx_alloc().

Change-Id: I0a4049df8500b4f7c864f1355c4e9238932d1b8f
2016-07-25 17:35:47 +02:00
Neels Hofmeyr
f29dd5f15b cosmetic: rsl_rx_chan_act_ack(): use local lchan var in 14 instances
In preparation for an upcoming change.

Change-Id: I9ce71fd7dde42ad7d20f806ac70c150d11450efa
2016-07-25 15:21:24 +00:00
Neels Hofmeyr
8151648ceb cosmetic: act lchan type: use constant instead of 0x00
Change-Id: Idc8afc4e52e189f474077899eef896381ce238f7
2016-07-25 15:21:24 +00:00
Neels Hofmeyr
4007468014 cosmetic: rsl_rx_rf_chan_rel_ack(): use local ts var for brevity
In preparation for an upcoming change.

Change-Id: I11bd59492fa8d5b9392d9f2b511c8fa9585afe6c
2016-07-25 15:21:24 +00:00
Neels Hofmeyr
c6926d064d comments: clarify some dynamic TS comments
A new type of dynamic channel will be introduced soon, so prepare some comments
to name the dynamic TS kind more specifically.

Change-Id: I51fa8c2ebba507299e55a5cb7e67e48a6c8471f7
2016-07-25 15:21:24 +00:00
Neels Hofmeyr
67933a19d6 fix: create_pdp_conf(): unset reject_cause after unknown ran_type
f9f4387686 introduced a check for ran_type,
which potentially leaves reject_cause unset. Fix that.

Change-Id: I0220841ff796f949d00a1415d46b54a3eacc9493
2016-07-25 15:19:50 +00:00
Harald Welte
7c989e7ced remove old copy of documentation that now is in osmo-gsm-manuals.git
We keep some random snippets of documentation here, but manuals are now
generally kept in osmo-gsm-manuals.git.  Particularly the GSUP, OAP and
control interface are documented more extensively there.

To avoid having two sets of (diverging) documentation, let's remove it
from the openbsc.git repository.

Change-Id: I4a4c918587e236a7aa00cf2bb6aa05b090f7229b
2016-07-25 09:58:34 +00:00
Alexander Couzens
14314bd808 sgsn: add statistics counter for GPRS and PDP packets
Changing the test to allow still allocated block from the rate
counters.

Change-Id: Ie30e4c3084ee3a138d6b39bb5000234ac814e65f
2016-07-25 00:15:53 +00:00
Alexander Couzens
b1c227e5ab bs11_config: add brackets to fix warning in argument parsing
Change-Id: I2f02e09a21ca622b03fd966445f533263a499c8d
2016-07-25 00:14:44 +00:00
Neels Hofmeyr
17a6bab150 fix ctrl test: dyn TS: use new GSM_PCHAN_TCH_F_TCH_H_PDCH
Add GSM_PCHAN_TCH_F_TCH_H_PDCH in gsm_pchant_names and gsm_pchant_descs: the
VTY and CTRL can now handle the new pchan type.

Adjust the CTRL iface test to expect the new PCHAN type in the output.

Fixes make check with --enable-external-tests after libosmocore commit
fd80f5a04239c2ab7b561401476dd89f2861748b that adds GSM_PCHAN_TCH_F_TCH_H_PDCH.

Change-Id: I4ad9c972d7f76f7e20cf74d6fc3d1928b644a4f8
2016-07-24 13:35:04 +02:00
Vadim Yanitskiy
d091b8de21 mncc_sock: use osmo_sock_unix_init() from libosmocore
Since the osmo_unixsock_listen() was moved to libosmocore
it would be better to use the library's implementation
instead of reinventing the wheel again.

Change-Id: Iacfc39b6214c24084438f8fe04d03952cdc9ebc2
2016-07-23 19:21:17 +00:00
Alexander Couzens
15fcd10fde gprs_gmm: remove duplicated start of T3395
The timer is already scheduled by gsm48_tx_gsm_deact_pdp_req().

Change-Id: I8203b939d2196f87b11c0f3b2b0ff481e572835c
2016-07-23 15:26:13 +00:00
Dieter Spaar
b572d7c45e SGSN: fix FCS calculation for encrypted frames
Change-Id: I352bc9db0c17fff773788831c4389ec0a5a30af8
Related: OS#1582
2016-07-17 08:44:38 +00:00
Neels Hofmeyr
9ddd8e6267 dyn pdch: don't PDCH ACT if gprs mode is none
Skip PDCH activation if the GPRS mode is 'none' at:

* TCH/F_PDCH init after OML Enable (dyn_pdch_init())
* after TCH/F_PDCH is released, in TCH/F mode
* in the T3111 error timer callback after a TCH/F_PDCH was released in error
  state

Assert the GPRS mode in rsl_ipacc_pdch_activate() to make sure all callers
check the GPRS mode.

Closes: OS#1765

Change-Id: I970e5f9dbcb1c625209e914a4c7696294ed34e62
2016-07-17 07:46:38 +00:00
Neels Hofmeyr
9331df16cf cosmetic: dyn_pdch_init(): flatten if-logic, add comments
Prepare for upcoming addition of heeding gprs mode == none.

Change-Id: Id0fe6f762ac863c4d4053841c7732d011aa8c561
2016-07-17 07:46:38 +00:00
Max
1de159168c SGSN: move cipher application to separate function
Split out generation and application of GEA gamma into separate function
which can be used for both encryption and decryption.

Change-Id: I442f2ead57e40d9bcd24e7f1b261041371595360
Related: OS#1582
2016-07-17 07:15:20 +00:00
Max
82040101eb SGSN: encrypt/decrypt only necessary frames
According to 3GPP TS 24.008 § 4.7.1.2 some GMM frames are not supposed
to be ciphered. Propagate information about the necessity for
encryption between MM <-> LLC to ensure only proper frames are
encrypted/decrypted/dropped.

Change-Id: I0358905e60d1b182f75caec81bfcc72bbbbb2aa1
Related: OS#1582
2016-07-16 23:17:58 +00:00
Max
b997f84443 SGSN: add preliminary support for GPRS encryption
It is already functional enough to allow testing with real
phones. However, note - there are several limitations in the current
implementation:

* only default value for IOV-UI is supported at the moment
* AUTN-based key material is not supported

Related: OS#1582
Change-Id: I8900b906693496e4e6b35be5a86937c58039ed9e
2016-07-16 21:11:10 +00:00
Max
5aa5196fbf SGSN: split GEA key management from TLLI
Move GEA key from TLLI assignment into separate function.

Change-Id: I8a0bc907072dc19cd9535a28b5252dc0f05357cc
Related: OS#1582
2016-07-16 21:04:01 +00:00
Max
4011e728d2 SGSN: use unique AUTH REQ reference
The A&C reference number specified in 3GPP TS 24.008 § 10.5.5.19
identifies particular request sent by network with the related response
sent by MS. The value transparently copied from request to response by
MS: the spec do not specify what exactly should be in there so we use
rand() to decrease chance for collisions.

Note: variable named 'rand' clashes with standard function rand() so it
was renamed.

Change-Id: I3638821a9b4a0532b28dbbb50faa30c4082579f6
Related: OS#1582
2016-07-16 21:03:30 +00:00
Neels Hofmeyr
9759374adb jenkins.sh: add --enable-iu matrix build
Change-Id: Ida76f24d0b801fa609f3a128b3b912572cad4297
2016-07-13 17:50:12 +02:00
Neels Hofmeyr
a3d93ed2f9 jenkins.sh: remove code dup
Have a bash function to build each dependency with the same commands.  There is
a tradeoff: having each dependency build with the same function means you can't
easily tweak one of the dependencies. OTOH having a unified function means a)
more readable script, b) that we're sure not to forget some steps and c) no
need to do the same edit n times.

Set the PKG_CONFIG_PATH globally. Also a tradeoff: if a future addition
wouldn't need the same PKG_CONFIG_PATH, this would make things ugly. But that
is actually quite unlikely, and the readability improvement is substantial.

Use env variables to remember local paths. That means we always are sure to cd
to the same absolute base path, which a 'cd ..' can't guarantee; also, we avoid
possible typos for e.g. "$deps/install".

Change-Id: Ib23f86c6cc1441d882de59bcdde5de87fa4e9fdf
2016-07-13 17:50:10 +02:00
Daniel Willmann
5754206379 osmux: Add negotiation state so race conditions can't disable osmux
Without this commit it is possible that osmux is disabled again on links with
high jitter. This happens when an MGCP response without X-Osmux header is
received before the NAT receives an Osmux dummy frame from the other side.

Ticket: SYS#2628, SYS#2627
Sponsored-by: On-Waves ehf
Change-Id: Id624b0279aee5e2412059a10296ce7896e2d4628
2016-07-11 19:19:05 +00:00
Max
176b62a80c SGSN: prevent starting with inconsistent config
Previously it was possible to start osmo-sgsn with "auth-policy remote"
but without "gsup remote-*" which resulted in broken setup: no MS could
perform GPRS ATTACH. Add consistency check to vty code to fix this.

Related: OS#1582
Change-Id: Ie4296e7d99d7833f7d828b0196435ea81097cf6e
2016-07-11 19:18:17 +00:00
Max
e6052c4cc7 Make random MSISDN assignment optional
Previously if subscriber was automatically created it got assigned
random MSISDN number. Make it optional (defaulting to previous behavior)
by adding following:

* new optional no-extension argument for subscriber-create-on-demand vty
  command
* db unit tests
* vty test

Note: using the db made with new code might result in subscribers with
empty extension. Such subscribers cannot be deleted using old
code. Make sure not to mix db versions or manually fix it by editing
sqlite with external program.

Fixes: OS#1658
Change-Id: Ibbc2e88e4722b08854ebc631485f19ed56443cbb
2016-07-09 19:52:54 +00:00
Alexander Couzens
9f8f9b8021 libbsc: skip channel state LCHAN_S_INACTIVE while handover
The state is directly overwritten by the next function. Because
there isn't any state transition, remove this state.

Change-Id: I7f287692dbd559268fb5e61d81ac19e5dd4827eb
2016-07-09 08:53:16 +00:00
Max
3955025c2a SGSN: move TLLI unassignment into separate function
Change-Id: Ia4df145ab03ebcaad70a13601cff60c488a5de54
Related: OS#1582
2016-07-04 08:42:37 +00:00
Max
93408ae727 SGSN: add vty config for choosing GPRS encryption
Change-Id: I07d65205be1c75d59744426629ed04cf3cd99f79
Related: OS#1582
2016-07-04 08:42:07 +00:00
Max
896c6f8e9e SGSN: force GSUP CN domain to PS
Always set CN domain in outgoing GSUP packets to PS to make it
compatible with osmo-auc.

Change-Id: Ia6ee2e55a41a8ea9e465d7df1b2b3559b553fca8
Related: OS#1582
2016-07-01 15:24:44 +02:00
Max
488902db2a Fix vty tests with subscriber deletion
Use correct vty command for subscriber deletion, adjust assertions
accordingly. The error was cause by inconsistent syntax of vty commands
for subscriber creation and deletion.

Change-Id: I9b9376b4ac0ec066000545167de312ca4460493b
2016-06-29 16:45:14 +00:00
Neels Hofmeyr
dd49beebb1 err log: tweak dyn pdch ack error logging
Rather use gsm_lchan_name().

Change-Id: I0334484eaa6a2c0f25925042c9c3c1a4e3e78ca4
2016-06-24 09:31:54 +00:00
Neels Hofmeyr
3f2212235c dyn PDCH: cosmetic: clarify lchan rel with assertion and comment
Change-Id: If3cc40022e8283daa991fffe4b6baa734303d8a5
2016-06-24 09:31:17 +00:00
Neels Hofmeyr
b0cc64274a debug log: log all lchan state transitions
Change-Id: Ic70aca65b3796c90ba1a88ea67ac7a2ad9190b69
2016-06-23 20:57:44 +00:00
Neels Hofmeyr
efedf80526 vty: show lchan summary: also show lchan->state
Change-Id: If7ae92b8d501b51bbe8a165c223734e169a8bb97
2016-06-23 20:01:25 +00:00
Neels Hofmeyr
82c8f75f71 dyn PDCH: enable PDCH only after release due to error
In rsl_rx_rf_chan_rel_ack(), only activate PDCH when in NONE state.

For the case of REL_ERR state, do the PDCH activation in the error timeout
callback after T3111 is done.

Change-Id: I4c55479b252a12039bb3d8c30a9cbf0199ca410e
2016-06-21 21:33:28 +02:00
Neels Hofmeyr
241bda03b4 typo in sgsn_test
(committing just to test gerrit, if it goes through it's still a valid change)

Change-Id: I3291ea2da99cd7f0e2f340b0e6fd6022d088beb8
2016-06-20 18:26:15 +02:00
Neels Hofmeyr
29048b2a80 rm dup: use channel type names from libosmocore
In gsm_lchant_name(enum gsm_chan_t), use the gsm_chan_t_names value strings
from libosmocore instead of redefining the same strings. The list from
libosmocore is also more complete, including CCCH and PDTCH.

Add a todo comment to move to libosmocore.

In consequence, libosmogsm linkage needs to be added to osmo-bsc_mgcp,
mgcp_test, mgcp_transcoding_test and smpp_mirror, smpp_test.

Change-Id: If65ee7c0619cbc0acb0a15045bd5a969442c93cc
2016-06-18 11:34:21 +00:00
Neels Hofmeyr
f8e02aa4e3 configure: require libgsm for --enable-mgcp-transcoding
Fail in configure if libgsm is not found.

Before this, the --enable-mgcp-transcoding would gladly accept that libgsm is
missing and the build would fail later because of missing linking and undefined
references.

Change-Id: Ic23157cc5b75694f400a176c31f97d71e861ea02
2016-06-17 15:33:22 +00:00
Neels Hofmeyr
349108801f bsc_version.c: update copyright date, add contributor
Change-Id: Ib3f6763448457915d7473ec5039ab406fd4bdb48
2016-06-17 04:52:30 +02:00
Neels Hofmeyr
832afa3f4b dyn PDCH: set lchan->state after PDCH DEACT / before PDCH ACT
Do the PDCH DE/ACT before we set the lchan->state to De-/Activation Requested.
It makes more sense semantically to change PDCH mode while the lchan is still
in NONE status. Thus slightly move some invocations:

PDCH ACT: Free the lchan before PDCH activation. Hence remove the lchan_free()
call from the rsl_rx_pdch_act_ack() code path; it used to do the PDCH
activation first and call lchan_free() in the callback.

PDCH DEACT: Set the (TCH) Activation Requested state only within
rsl_chan_activate_lchan(), after the PDCH deact is complete.

Channel allocator: don't pick channels that have a PDCH PENDING flag set, to
avoid using channels that are still in PDCH switchover (despite their state
being NONE).

The lchan_may_change_pdch() sanity checks are becoming a lot simpler.

Change-Id: I4206dd4808e21c3e59393ea7f5ab4f438afff066
2016-06-16 04:29:40 +02:00
Neels Hofmeyr
285df2ec62 dyn PDCH: add lchan sanity checks in PDCH DE/ACT ACK
Change-Id: I0456cfb88860823c37c14688673e9cbc8d0085d8
2016-06-16 04:29:40 +02:00
Neels Hofmeyr
3201988f7b dyn PDCH: track pending PDCH de-/activation
Set and clear pending flags on the TS according to PDCH de-/activation.

This will allow changing the time we set the channel state to after PDCH
DEACT and before PDCH ACT, in a subsequent commit.

Also add a sanity check on whether we're sending conflicting or superfluous
PDCH de-/activations on the same TS.

Change-Id: Ieae73271df749ded3d90585116aae01f3ad4ee74
2016-06-16 04:29:40 +02:00
Neels Hofmeyr
c1fbdedcd3 dyn PDCH: fix: clear PDCH flags on TS reconnect (e.g. BTS re-connect)
Change-Id: I89a0ef1794f343fdd06a62237f5732e73f4d2704
2016-06-16 04:29:40 +02:00
Neels Hofmeyr
2ebacce4fa dyn PDCH: TS flags: rename one, add three, as enum
Rename TS_F_PDCH_MODE to TS_F_PDCH_ACTIVE, to more accurately reflect the truth
value's meaning.

Add TS_F_PDCH_ACT_PENDING and TS_F_PDCH_DEACT_PENDING for sysmoBTS (and
possibly other BTS implementations) to remember what to do when the PCU replies
with a channel de/activation. Also add TS_F_PDCH_PENDING_MASK to test for both.

Change from #define to an enum.

Note: These flags are also used in the upcoming osmo-bts-sysmo dyn PDCH
commits, so osmo-bts submission depends on this commit.

Change-Id: I391a103ab599648b0c5d4f3ad613a6d7c48834b3
2016-06-16 04:26:33 +02:00
Max
0fcd2e2fec Make random extension range configurable
Previously if subscriber was automatically created it got assigned
random MSISDN number between 20000 and 49999. Make it configurable with
new vty command "subscriber-create-on-demand random" and expand vty
tests to check it.

Change-Id: I040a1d227b0c7a1601dc7c33eccb0007941408a6
Related: OS#1658
2016-06-14 22:20:40 +00:00
Max
e152ffe14d Fix SIGABRT on wrong AMR payload
Previously length check have not considered AMR format which requires
extra byte for in-band length leading to SIGABRT on incorrect payload
from BTS.

Change-Id: I800f756fc803accace8c7e0b4a42b3744fe78bb6
Fixes: OS#1731
2016-06-14 10:20:05 +00:00
Neels Hofmeyr
b8afb5fda2 dyn PDCH: send PDCH ACT for each TCH/F_PDCH on TS Enable
Add dyn_pdch_init() in new file bsc_dyn_pdch.c (new file to avoid linking
issues; bsc_init.c would create undefined references, and putting in a new file
is the easiest solution).

Call dyn_pdch_init() from nm_statechg_event() whenever a TS is enabled.

Revert the |= TS_F_PDCH_MODE chunk from previous commit, since this flag will
now be set after dyn_pdch_init() sent out the PDCH ACT and when subsequently
the PDCH ACT ACK messages are received in rsl_rx_pdch_act_ack().

Change-Id: I0cad93dec59d546b3f3b19e332e0833496031575
2016-06-14 10:18:19 +00:00
Andreas Eversberg
9df268e217 dyn PDCH: Automatically deactivate/activate PDCH on TCH/F+PDCH channel
Handle shared TCH/F+PDCH channels as regular TCH/F channels. Prior to
activation, deactivate PDCH mode.

After deactivation, restore PDCH mode.

Change-Id: I59712b8769cc3959ef114a6e12e77801816fe8b6
2016-06-14 10:18:19 +00:00
Daniel Willmann
3adb23cfc5 add DSUA debug constant
Change-Id: I4a3a8189564345700ea4825983ab39a8411227f4
2016-06-13 09:18:51 +00:00
Neels Hofmeyr
a66852525a dyn PDCH: allow allocating TCH/F on TCH/F_PDCH slots
Remove check for dyn PDCH in _lc_find_trx(), instead call _lc_find_trx() via
_lc_find_bts() several times, so that pure TCH/F is preferred to TCH/F_PDCH.
Add this logic next to the other channel match decisions in chan_alloc().

BTW, the removed check in _lc_find_trx() whether PDCH is active is not
necessary, as described in the added comment for lchan_alloc().

Original patch idea by jolly, but split in two and implemented differently by
nhofmeyr.

Change-Id: I0c728b922656be03588b775638b610a93f8187d5
2016-06-12 15:48:38 +00:00
Andreas Eversberg
0434efa077 dyn PDCH: Fix free slot search for chan_alloc_reverse == true
For chan_alloc_reverse, _lc_find_trx() should return the last free slot instead
of the first.

Original patch by jolly, but split in two by nhofmeyr.

Change-Id: Iff980242b9b5cb39345aaad0350ee368537677cd
2016-06-12 15:48:38 +00:00
Max
ec744655b4 Add talkspurt indicator for gsm_lchan
Add bit which can be set on BTS side to indicate that next RTP frame
should be marked as a beginning of speech.

Change-Id: I355a5ae275a2743b29071924c916c4f68c3b3e80
Related: OS#1562
2016-06-12 15:43:50 +00:00
Max
f5fe31d513 DTX: add data necessary for scheduling
DTXd: to schedule SID repetition we have to know when previous SID was
sent (fn) and if it was UPDATE or FIRST SID (is_update).
DTXu: to properly set Marker bit in outgoing RTP we have to know the
beginning of talkspurt. For codecs without explicit ONSET event we can
do it by setting the flag (ul_sid) upon receiving SID and unsetting it
on speech frames.

Change-Id: I79cbec3b6c6fed5de385f8e202ceaf0b13234778
Related: OS#22, OS#1701
2016-06-10 14:09:07 +02:00
Max
ae3f0718f1 Add DTXd indicator to gsm_lchan
It is necessary for proper reporting of DTXd status during the
measurement period.

Change-Id: I4a033b03fcd0deb4db7a38273b5407511dbf1d6c
Related: OS#1701
2016-06-10 11:34:41 +00:00
Daniel Willmann
35a65edd10 add DRANAP debug constant
Change-Id: I6132198ea86979e0ed84df32f2b7117feba497f2
2016-06-10 10:46:48 +00:00
Neels Hofmeyr
f5713a5c63 lchan_alloc(): on alloc failure, report original type
In lchan_alloc(), there are several decisions to fall back to another type of
channel, followed by setting the channel type to the fall back type. So far,
this was set regardless of allocation success or failure.

If such fall back type is not available, do not modify the local type variable
and thus report an S_CHALLOC_ALLOC_FAIL on the type originally requested
(report is at the end of lchan_alloc()).

Change-Id: Ie3d4cb74f91db0b8c4f5e595a963099de339ad1a
2016-06-07 11:10:40 +00:00
Max
6079528b48 Add warning for unsupported DTX configurations
libosmo-abis do not consider DTX bits while processing TRAU frames. As I
do not have equipment to test it, I'm not sure if/how non-IP BTS will
work in case of DTX - warn users about it.

Change-Id: I94ee69cd309fc343a428ddc66942cd57f2a34c05
Related: OS#22
2016-06-06 11:36:48 +02:00
Neels Hofmeyr
9329e6fb49 gprs_gmm.c: don't transmit NOTEXIST when mmctx is NULL
Add missing mmctx NULL check in gsm0408_rcv_gmm(). gsm48_tx_gmm_status() would
dereference mmctx without checking, so we can't call it if mmctx == NULL.

Follows up on recent e98ba82d2b:
"gprs_gmm.c: Don't try to de-reference NULL mmctx".

Change-Id: If59efbde86c76ffe91a0b33be87273783a2a4a02
2016-06-05 23:36:28 +00:00
Daniel Willmann
3af9660e51 configure.ac: add --enable-iu with deps asn1c, ranap, sigtran
For upcoming 3G support.

Change-Id: I6209423d71f94d5cd0ca9daf065d0a9df521ef02
2016-06-05 23:13:01 +00:00
Neels Hofmeyr
9f5d231f00 debug log: cosmetic fixes
Drop erroneous C from a DEBUGPC, should be on a new line.

Drop underscores from IPAC_PDCH_[DE]ACT: all other log messages for IPAC PDCH
are without underscores -- git grep "P(.*IPAC.PDCH.*ACT"

Change-Id: I8fb7a1c1beabb1f4388517383fd0bdc082d557ca
2016-06-05 23:02:09 +00:00
Neels Hofmeyr
3e62d415ac comment tweak for bsc_handover_start()
Have a comment only in the .c file to remove dup, tweak wording, use doxygen
style.

Change-Id: If054dad877a1ca750cd72be9c9d90bcf087bf741
2016-06-05 23:01:25 +00:00
Max
ddee01fa8f Add regexp authorization policy for IMSI
* extend "auth policy" vty command with new option "regexp"
* add vty command "authorized-regexp" for setting arbitrary POSIX
  regular expression
* add basic vty test
* add optional "regexp" argument to subscriber-create-on-demand vty
  command

With those in place we can now set the regexp against which MS's IMSI
will be matched.

If IMSI match the regexp than MS is allowed to access the network. If
subscriber is already marked as authorized in HLR than it'll be allowed
regardless of IMSI matching.

The same way we can decide whether to create subscribers on-demand
basesd on IMSI regexp match. Similar to authorization this restriction
can be overridden by manually creating subscriber via vty, ctrl
interface or directly in HLR.

Change-Id: I525f4b80676de47d1d422686da2ca012301b0129
Fixes: OS#1647
2016-06-05 09:36:37 +00:00
Max
d7df7ae392 Store last used FN for TCH
It's necessary to properly compute timestamp compensation for RTP
packets in case of DTX (or heavy packet loss).

Related: OS#22
Change-Id: Ib42c6a8614a4b73333a83181488dd4069cac14d7
2016-06-03 13:03:52 +02:00
Daniel Willmann
f9f4387686 gprs: more conditionals for Gb specific actions
Change-Id: I213d21b9ddbf19e56269defcc6aa65aca4947140
2016-06-02 03:01:06 +02:00
Daniel Willmann
7ec8ca422c sgsn_mm_ctx_cleanup_free(): clean up LLME iff present (Gb, not Iu)
Assert that llme is unused for non-Gb (Iu) connections, and clean up otherwise.
Make sure the cleanup is left below the sgsn_mm_ctx_free() call, as the comment
states.

Change-Id: I891ae21afc1f4f60580b822273b5435e0e17d46f
2016-06-02 03:01:04 +02:00
Harald Welte
2b2429eb59 gprs_gmm.c: Perform LLME operations only if we have one
In case the GMM message did not arrive over a Gb interface, there is no
LLME (and thus the associated pointer is NULL).  Don't try to perform
operations on a NULL LLME.

Change-Id: If7f24161cd2826f8ee238d4bc1090adf555cea4e
2016-06-02 03:01:02 +02:00
Harald Welte
dbc72b37ee gprs_gmm.c: Make TLLI handling specific to Gb interface
Soem of the operations we perform in the GMM layer are specific to the
GPRS/EDGE radio access network and its Gb interface.  Let's make them
conditional to that in preparation of supporting an Iu interface.

Change-Id: I3efb7c5087afe8e2331ec17bd9fac5029f4bee6c
2016-06-02 03:00:59 +02:00
Neels Hofmeyr
e98ba82d2b gprs_gmm.c: Don't try to de-reference NULL mmctx
There was a comment in the code that certain GMM messages require a
valid mmctx pointer.  However, nothing actually checked if that pointer
was in fact non-NULL.  We plainly crashed if a MS would send us the
wrong message in the wrong state.

Original patch by Harald Welte, but it broke message validity checking,
resulting in sgsn_test failure. This re-implements the NULL check in a
different way, as explained by in-code comment.

Change-Id: I7908de65bec91599f7042549b832cbbd7ae5a9a8
2016-06-02 03:00:55 +02:00
Harald Welte
49393e128e rename gsm0408_gprs_rcvmsg() to gsm0408_gprs_rcvmsg_gb()
This is the entry point for GMM from Gb.  We will create a new one
for Iu, so let's be explicit rather than implicit.

Change-Id: I93c074bf99db041117c0dc03dc8255879845a875
2016-06-02 03:00:53 +02:00
Daniel Willmann
62ff38447c create_pdp_conf(): factor out PDP context accept dispatch as send_act_pdp_cont_acc()
Change-Id: Ibf60e18707ff4aa2e60291e5595386ddda8d8190
2016-06-01 12:09:51 +00:00
Harald Welte
f97ee04563 prepare sgsn_mm_ctx for Gb and Iu mode (UMTS)
Explicitly mark those sgsn_mm_ctx members that apply for Gb mode and (upcoming)
Iu mode, respectively.

Add some comments in sgsn_mm_ctx.

Change-Id: Ife9b02549f284e2547f16117cf43d7a36948fc4b
Tweaked-By: Neels Hofmeyr <nhofmeyr@sysmocom.de>
2016-06-01 12:09:36 +00:00
Daniel Willmann
97165f386f rename enum gprs_mm_state to gprs_gmm_state
Change-Id: Ibba054d15c55c7ac570e64ff66ea57964be095e3
2016-06-01 11:07:21 +00:00
Harald Welte
e1197481e5 Merge "rename enum gprs_mm_state to gprs_gmm_state" 2016-06-01 10:46:41 +00:00
Harald Welte
a9ca72d907 Merge "add .gitreview" 2016-06-01 06:29:42 +00:00
Alexander Couzens
85f8fdabc3 gprs: use new uint8_t * for kv in gprs_cipher_run()
libosmocore changed in bf990bb8 Update internal GPRS cipher API
from uint_64 to uint8_t*.
Fix a warning.

Change-Id: Ib5bfe1fb05c693347b11ff4faadd3fc2205ebd76
2016-05-31 17:47:52 +02:00
Alexander Couzens
51fbc5f6e0 add .gitreview
A .gitreview file is required to use git review.
More information about git review
https://www.mediawiki.org/wiki/Gerrit/git-review

Change-Id: Ie7cdf16232181d4b8093e61f2d8a3faed9010d4f
2016-05-31 17:43:46 +02:00
Daniel Willmann
0f46f9ca5a rename enum gprs_mm_state to gprs_gmm_state
Change-Id: Ibba054d15c55c7ac570e64ff66ea57964be095e3
2016-05-31 13:53:15 +02:00
Max
69e9c0dfc6 Make si2q scheduling optional
Previously si2quater SI messages were always scheduled. Check for
neighbor configuration and only schedule si2q when necessary. Add
corresponding unit test.

Change-Id: Ibe997803ffb894133fd4d838410fe735791d414f
Fixes: OS#1727
Reviewed-on: https://gerrit.osmocom.org/81
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-31 09:32:12 +00:00
Holger Hans Peter Freyther
82dd983dd8 bsc: Create minimal SI6 rest octets
In GSM R99 SI6 has mandatory SI6 rest octets and so far we did
not include them. Add minimal support to generate the right band
indicator.

Target a slightly older version of the SI6 rest octets as we neither
support MBMS nor Random bit stream but should include the band
indicator.

Change-Id: I417a40eb91f42a3416b4e07bb9fb4d7a01aaa36b
Fixes: OS#1698
Related: OS#1725
Reviewed-on: https://gerrit.osmocom.org/71
Tested-by: Jenkins Builder
Reviewed-by: Max <msuraev@sysmocom.de>
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-30 21:28:40 +00:00
Neels Hofmeyr
307e40648e tweak db debug log: log TMSI as hex
Change-Id: I4564c99c145a35fb592c228c1fa84c61ec425fd3
Reviewed-on: https://gerrit.osmocom.org/94
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-05-27 10:48:04 +00:00
Holger Hans Peter Freyther
5b692d0a5c filter/nat: Fix the context for the imsi assignment
In c09f8a3b7f as part of a cleanup
I accidently changed the talloc context from "con" to "bsc". The
issue occurred at an earlier commit when assigning req.ctx to the
"wrong" context. The allocation needs to be scoped by the struct
nat_sccp_connection and not the connection from BSC to NAT.

Before we have a nat_sccp_connection we scope the copied imsi to
the bsc_connection and then steal it, but for the identity resp
we will always have a nat_sccp_connection and can already use the
right context.

Change-Id: I53789aad2809e19338ad3b2deb72c4757e7bd524
Related: OS#1733
Reviewed-on: https://gerrit.osmocom.org/102
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Reviewed-by: daniel <dwillmann@sysmocom.de>
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-25 19:43:38 +00:00
Holger Hans Peter Freyther
ddf4e1e114 debian: Make upgrading from debian SID easier
Make sure the version number of this sourcepackage is higher than
the one found in Debian SID.

Change-Id: I838632e9e90378a03235c2aebd5bc9ed06627ec8
Reviewed-on: https://gerrit.osmocom.org/113
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-25 19:41:19 +00:00
Harald Welte
ed04fcc179 rtp_proxy.c: Ensure msgb_alloc is large enough for largest AMR frame
In AMR 12.2 (mode 7), the actual RTP payload is 33 bytes.  Howeerver,
as we store the length of the (dynamically-sized) AMR payload in the
first byte, our buffer needs at least 33+1 byte in size.

Change-Id: If1ad5d2d68c85733306c75ea62f67fe8fbc143b3
Reviewed-on: https://gerrit.osmocom.org/91
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-25 12:33:47 +00:00
Neels Hofmeyr
aea28ceb27 gsm04_08_clear_request(): release loc with arg release=0
In gsm04_08_clear_request(), in_release == 1 anyway and
msc_release_connection() would exit immediately without any effect. Don't
confuse the reader by passing release=1 arg.

Change-Id: I5bf9eb4889d32ad5e42ac7d096bf62fa3a493e20
Reviewed-on: https://gerrit.osmocom.org/93
Reviewed-by: Holger Freyther <holger@freyther.de>
Tested-by: Jenkins Builder
2016-05-23 20:20:37 +00:00
Max
ea8e983514 Fix copy-paste error in SI6
Fix error which prevented enabling DTX for half-rate channels.

Change-Id: I7d41df0068783c8fb33ddeeab1d1dcf63c2c259f
Reviewed-on: https://gerrit.osmocom.org/101
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-23 16:24:40 +00:00
Harald Welte
7184bd088e subscr_name(): Handle case for subscr == NULL
subscr_name() was called from several places:
* either without a check for subscr being NULL, which for example
  was causing a segfault if we hand-over a channel before identifying the
  subscriber
* or with an explicit NULL check and the ternary operator (?).

We now simplify the code by checking for the NULL Subscriber in subscr_name()
itself.

Change-Id: Ide09f4a515222eb2ec6c25e7a6a8c5f6cc2ffd4b
Reviewed-on: https://gerrit.osmocom.org/92
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-22 11:51:18 +00:00
Neels Hofmeyr
8495e03588 drop unneccessary duplicate linking: tests/gsm0408
Change-Id: I8b6fb27d1db0157cb7d61f18b03f33c4f3168946
Reviewed-on: https://gerrit.osmocom.org/90
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-22 10:46:42 +00:00
Neels Hofmeyr
5493d87f95 drop unneccessary duplicate linking: osmo-nitb
Change-Id: I430adbb1e0c6382317da282bcf5ef73cf9496f80
Reviewed-on: https://gerrit.osmocom.org/89
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-21 15:54:36 +00:00
Neels Hofmeyr
2fa7d8464f drop unneccessary duplicate/unused linking: ipaccess
Drop unused linking of libmsc, and drop duplicate linking of libbsc.

Change-Id: If2d63adb832c72ff1a22c25a78e06b0c244628d2
Reviewed-on: https://gerrit.osmocom.org/88
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-21 15:20:47 +00:00
Neels Hofmeyr
63081fe34d drop unneccessary duplicate linking: osmo-bsc
Change-Id: Ia227abcaa7b1f808646aadb9f53ee2a669699c51
Reviewed-on: https://gerrit.osmocom.org/87
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-21 15:02:55 +00:00
Max
e21cf38da4 Make extending subscriber creation easier
* rename variable controlling subscriber creation
* use enum for subscriber creation policy
* move check for subscriber creation policy into separate static
  function

Related: OS#1658, OS#1647
Change-Id: I3b10a9a764fd3a7bb96717a990e52caae16266da
Reviewed-on: https://gerrit.osmocom.org/42
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-20 16:32:37 +00:00
Max
8a4d2e756d Use proper measurement for handover
Previously *FULL measurements were always used for handover
decisions. Those are incorrect in case of DTX - check if it was enabled
and use *SUB instead.

Note: *SUB values have higher variance so there might be more "bad"
values compared to *FULL although real quality remains the same.

Change-Id: I95e8e544047a83a256e057a47458678f40a19a15
Related: OS#1701
Reviewed-on: https://gerrit.osmocom.org/66
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-20 16:26:33 +00:00
Max
c08ee71bff Move DTX settings to BTS
* Add per-BTS DTX settings
* Configure Uplink and Downlink DTX separately
* Deprecate global DTX option (it was never tested/used anyway)
* Use libosmocore function for DTX indicator in System
  Information (previously it was incorrectly assigned for half-rate
  channels)

Related: OS#22
Change-Id: I3d55168475ad47044b6238b55846ea22bdd518a4
Reviewed-on: https://gerrit.osmocom.org/40
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-17 16:17:54 +00:00
Max
9a7e25b9c3 Cleanup db test
Move copy-pasted code into separate function to make writing more tests
easier.

Related: OS#1658
Change-Id: I9e39af85718514dd0f081d41c234c9dda77c4b27
Reviewed-on: https://gerrit.osmocom.org/43
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-17 16:14:06 +00:00
Vadim Yanitskiy
a8d8e93086 db.c: implemented incremental migration
In the past, normal migration was possible only if the actual
schema version differed from the version used in DB by 1. For
example, if DB uses an old version 3 and you need to use it
with the code written for version 5, the check_db_revision()
will convert it to 4 and DB will still use incompatible schema
version during Osmo-NITB running time. After next run it will
be converted to version 5.

This patch replaces a set of 'else-if' checks by a 'switch'
without 'break' statements between 'case' labels (waterfall).
It makes you able to migrate from current version to the
latest despite any difference between them.

Change-Id: Ia9c2aa86f96b88ad8a710d0a23879ce219bc82dc
Reviewed-on: https://gerrit.osmocom.org/62
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-15 19:31:18 +00:00
Max
34e2b09278 Ignore extended test leftovers
Change-Id: If9e3522d934611f631cbfde6e6db52251babc37f
Reviewed-on: https://gerrit.osmocom.org/41
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Reviewed-by: Neels Hofmeyr <nhofmeyr@sysmocom.de>
Tested-by: Holger Freyther <holger@freyther.de>
Reviewed-on: https://gerrit.osmocom.org/56
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-12 12:24:23 +00:00
213 changed files with 18660 additions and 3739 deletions

3
.gitreview Normal file
View File

@@ -0,0 +1,3 @@
[gerrit]
host=gerrit.osmocom.org
project=openbsc

12
.mailmap Normal file
View File

@@ -0,0 +1,12 @@
Harald Welte <laforge@gnumonks.org>
Harald Welte <laforge@gnumonks.org> <laflocal@hanuman.gnumonks.org>
Harald Welte <laforge@gnumonks.org> <laflocal@goeller.de.gnumonks.org>
Holger Hans Peter Freyther <holger@moiji-mobile.com> <zecke@selfish.org>
Holger Hans Peter Freyther <holger@moiji-mobile.com> <ich@tamarin.(none)>
Holger Hans Peter Freyther <holgre@moiji-mobile.com> <holger@freyther.de>
Andreas Eversberg <jolly@eversberg.eu>
Andreas Eversberg <jolly@eversberg.eu> <Andreas.Eversberg@versatel.de>
Andreas Eversberg <jolly@eversberg.eu> <root@nuedel.(none)>
Pablo Neira Ayuso <pablo@soleta.eu> <pablo@gnumonks.org>
Max Suraev <msuraev@sysmocom.de>
Tom Tsou <tom.tsou@ettus.com> <tom@tsou.cc>

View File

@@ -2,56 +2,67 @@
set -ex
rm -rf deps/install
mkdir deps || true
export LD_LIBRARY_PATH=$PWD/deps/install/lib
cd deps
osmo-deps.sh libosmocore
base="$PWD"
deps="$base/deps"
inst="$deps/install"
cd libosmocore
mkdir "$deps" || true
rm -rf "$inst"
build_dep() {
project="$1"
branch="$2"
cfg="$3"
set +x
echo
echo
echo
echo " =============================== $project ==============================="
echo
set -x
if [ -z "$project" ]; then
echo "internal failure"
exit 1
fi
cd "$deps"
rm -rf "$project"
osmo-deps.sh "$project"
cd "$project"
if [ -n "$branch" ]; then
git checkout "$branch"
fi
git rev-parse HEAD
autoreconf --install --force
./configure --prefix="$inst" $cfg
$MAKE $PARALLEL_MAKE install
}
build_dep libosmocore "" ac_cv_path_DOXYGEN=false
# All below builds want this PKG_CONFIG_PATH
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
if [ "x$IU" = "x--enable-iu" ]; then
netif_branch="sysmocom/sctp"
sccp_branch="sysmocom/iu"
fi
build_dep libosmo-abis
build_dep libosmo-netif $netif_branch
build_dep libosmo-sccp $sccp_branch
PARALLEL_MAKE="" build_dep libsmpp34
build_dep openggsn
if [ "x$IU" = "x--enable-iu" ]; then
build_dep libasn1c
#build_dep asn1c aper-prefix # only needed for make regen in osmo-iuh
build_dep osmo-iuh
fi
cd "$base"
cd openbsc
autoreconf --install --force
./configure --prefix=$PWD/../install
$MAKE $PARALLEL_MAKE install
cd ../
osmo-deps.sh libosmo-abis
cd libosmo-abis
autoreconf --install --force
PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig ./configure --prefix=$PWD/../install
PKG_CONFIG_PATH=$PWD/..//install/lib/pkgconfig $MAKE $PARALLEL_MAKE install
cd ../
osmo-deps.sh libosmo-netif
cd libosmo-netif
autoreconf --install --force
PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig ./configure --prefix=$PWD/../install
PKG_CONFIG_PATH=$PWD/..//install/lib/pkgconfig $MAKE $PARALLEL_MAKE install
cd ../
osmo-deps.sh libosmo-sccp
cd libosmo-sccp
autoreconf --install --force
PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig ./configure --prefix=$PWD/../install
PKG_CONFIG_PATH=$PWD/..//install/lib/pkgconfig $MAKE $PARALLEL_MAKE install
cd ../
osmo-deps.sh libsmpp34
cd libsmpp34
autoreconf --install --force
./configure --prefix=$PWD/../install
$MAKE install
cd ../
osmo-deps.sh openggsn
cd openggsn
autoreconf --install --force
PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig ./configure --prefix=$PWD/../install
PKG_CONFIG_PATH=$PWD/..//install/lib/pkgconfig $MAKE $PARALLEL_MAKE install
cd ../../openbsc
autoreconf --install --force
PKG_CONFIG_PATH=$PWD/../deps/install/lib/pkgconfig ./configure --enable-osmo-bsc --enable-nat $SMPP $MGCP --enable-vty-tests --enable-external-tests
PKG_CONFIG_PATH=$PWD/../deps/install/lib/pkgconfig $MAKE $PARALLEL_MAKE
PKG_CONFIG_PATH=$PWD/../deps/install/lib/pkgconfig LD_LIBRARY_PATH=$PWD/../deps/install/lib $MAKE check
PKG_CONFIG_PATH=$PWD/../deps/install/lib/pkgconfig LD_LIBRARY_PATH=$PWD/../deps/install/lib $MAKE distcheck
./configure --enable-osmo-bsc --enable-nat $SMPP $MGCP $IU --enable-vty-tests --enable-external-tests
$MAKE $PARALLEL_MAKE
LD_LIBRARY_PATH="$inst/lib" $MAKE check
LD_LIBRARY_PATH="$inst/lib" $MAKE distcheck

10
debian/changelog vendored
View File

@@ -1,4 +1,12 @@
openbsc (0.14.0) UNRELEASED; urgency=low
openbsc (0.15.1) UNRELEASED; urgency=medium
* Move forward toward a new release.
* Prevent SGSN starting with 'auth-policy remote' when no 'gsup remote-*' are configured.
Note: such configs are broken without extra workarounds anyway.
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Tue, 24 May 2016 23:14:31 +0200
openbsc (0.14.0) unstable; urgency=low
* New upstream tag and additional patches.

7
openbsc/.gitignore vendored
View File

@@ -55,6 +55,7 @@ src/gprs/osmo-sgsn
src/gprs/osmo-gbproxy
src/gprs/osmo-gtphub
src/osmo-bsc_nat/osmo-bsc_nat
src/osmo-cscn/osmo-cscn
#tests
tests/testsuite.dir
@@ -81,6 +82,10 @@ tests/subscr/subscr_test
tests/oap/oap_test
tests/gtphub/gtphub_test
tests/mm_auth/mm_auth_test
tests/xid/xid_test
tests/sndcp_xid/sndcp_xid_test
tests/slhc/slhc_test
tests/v42bis/v42bis_test
tests/atconfig
tests/atlocal
@@ -88,7 +93,7 @@ tests/package.m4
tests/testsuite
tests/testsuite.log
gsn_restart
src/openbsc.cfg*
writtenconfig/
gtphub_restart_count

View File

@@ -1,7 +1,16 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
SUBDIRS = doc include src tests
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
$(NULL)
SUBDIRS = \
doc \
include \
src \
tests \
$(NULL)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = openbsc.pc

View File

@@ -47,7 +47,7 @@ fi
AM_CONDITIONAL(BUILD_BSC, test "x$osmo_ac_build_bsc" = "xyes")
AC_SUBST(osmo_ac_build_bsc)
# Enable/disable smpp support in the nitb?
# Enable/disable smpp support in the cscn?
AC_ARG_ENABLE([smpp], [AS_HELP_STRING([--enable-smpp], [Build the SMPP interface])],
[osmo_ac_build_smpp="$enableval"],[osmo_ac_build_smpp="no"])
if test "$osmo_ac_build_smpp" = "yes" ; then
@@ -63,7 +63,7 @@ AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [
AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"])
if test "$osmo_ac_mgcp_transcoding" = "yes" ; then
AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""])
AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""], [AC_MSG_ERROR([--enable-mgcp-transcoding: cannot find usable libgsm])])
AC_SUBST(LIBRARY_GSM)
if test "$osmo_ac_with_g729" = "yes" ; then
PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])])
@@ -73,6 +73,18 @@ fi
AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes")
AC_SUBST(osmo_ac_mgcp_transcoding)
# Enable/disable 3G aka IuPS + IuCS support?
AC_ARG_ENABLE([iu], [AS_HELP_STRING([--enable-iu], [Build 3G support, aka IuPS and IuCS interfaces])],
[osmo_ac_iu="$enableval"],[osmo_ac_iu="no"])
if test "x$osmo_ac_iu" = "xyes" ; then
PKG_CHECK_MODULES(LIBASN1C, libasn1c) # TODO version?
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap) # TODO version?
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran) # TODO version?
AC_DEFINE(BUILD_IU, 1, [Define if we want to build IuPS and IuCS interfaces support])
fi
AM_CONDITIONAL(BUILD_IU, test "x$osmo_ac_iu" = "xyes")
AC_SUBST(osmo_ac_iu)
found_libgtp=yes
PKG_CHECK_MODULES(LIBGTP, libgtp >= 0.92, , found_libgtp=no)
@@ -191,7 +203,9 @@ AC_OUTPUT(
src/libmgcp/Makefile
src/libcommon/Makefile
src/libfilter/Makefile
src/osmo-nitb/Makefile
src/libiu/Makefile
src/libxsc/Makefile
src/osmo-cscn/Makefile
src/osmo-bsc/Makefile
src/osmo-bsc_nat/Makefile
src/osmo-bsc_mgcp/Makefile
@@ -200,6 +214,7 @@ AC_OUTPUT(
src/gprs/Makefile
tests/Makefile
tests/atlocal
tests/libiudummy/Makefile
tests/gsm0408/Makefile
tests/db/Makefile
tests/channel/Makefile
@@ -217,6 +232,10 @@ AC_OUTPUT(
tests/oap/Makefile
tests/gtphub/Makefile
tests/mm_auth/Makefile
tests/xid/Makefile
tests/sndcp_xid/Makefile
tests/slhc/Makefile
tests/v42bis/Makefile
doc/Makefile
doc/examples/Makefile
Makefile)

View File

@@ -1,6 +1,6 @@
#!/usr/bin/python
import sys,os
import sys,os, random
from optparse import OptionParser
import socket
import struct
@@ -10,15 +10,18 @@ verbose = False
def prefix_ipa_ctrl_header(data):
return struct.pack(">HBB", len(data)+1, 0xee, 0) + data
def ipa_ctrl_header(header):
(plen, ipa_proto, osmo_proto) = struct.unpack(">HBB", header)
return None if (ipa_proto != 0xee or osmo_proto != 0) else plen
def remove_ipa_ctrl_header(data):
if (len(data) < 4):
raise BaseException("Answer too short!")
(plen, ipa_proto, osmo_proto) = struct.unpack(">HBB", data[:4])
plen = ipa_ctrl_header(data[:4])
if (None == plen):
raise BaseException("Wrong protocol in answer!")
if (plen + 3 > len(data)):
print "Warning: Wrong payload length (expected %i, got %i)" % (plen, len(data) - 3)
if (ipa_proto != 0xee or osmo_proto != 0):
raise BaseException("Wrong protocol in answer!")
return data[4:plen+3], data[plen+3:]
def connect(host, port):
@@ -36,69 +39,92 @@ def send(sck, data):
data = prefix_ipa_ctrl_header(data)
sck.send(data)
def do_set(var, value, id, sck):
setmsg = "SET %s %s %s" %(options.id, var, value)
def do_set(var, value, op_id, sck):
setmsg = "SET %s %s %s" %(op_id, var, value)
send(sck, setmsg)
def do_get(var, id, sck):
getmsg = "GET %s %s" %(options.id, var)
def do_get(var, op_id, sck):
getmsg = "GET %s %s" %(op_id, var)
send(sck, getmsg)
parser = OptionParser("Usage: %prog [options] var [value]")
parser.add_option("-d", "--host", dest="host",
help="connect to HOST", metavar="HOST")
parser.add_option("-p", "--port", dest="port", type="int",
help="use PORT", metavar="PORT", default=4249)
parser.add_option("-g", "--get", action="store_true",
dest="cmd_get", help="perform GET operation")
parser.add_option("-s", "--set", action="store_true",
dest="cmd_set", help="perform SET operation")
parser.add_option("-i", "--id", dest="id", default="1",
help="set id manually", metavar="ID")
parser.add_option("-v", "--verbose", action="store_true",
dest="verbose", help="be verbose", default=False)
parser.add_option("-m", "--monitor", action="store_true",
dest="monitor", help="monitor the connection for traps", default=False)
def do_set_get(sck, var, value = None):
r = random.randint(1, sys.maxint)
if (value != None):
s = 'SET_REPLY'
do_set(var, value, r, sck)
else:
s = 'GET_REPLY'
do_get(var, r, sck)
(answer, data) = remove_ipa_ctrl_header(sck.recv(4096))
x = answer.split()
if (s == x[0] and str(r) == x[1] and var == x[2]):
return None if ('SET_REPLY' == s and value != x[3]) else x[3]
return None
(options, args) = parser.parse_args()
def set_var(sck, var, val):
return do_set_get(sck, var, val)
verbose = options.verbose
def get_var(sck, var):
return do_set_get(sck, var)
if options.cmd_set and options.cmd_get:
parser.error("Get and set options are mutually exclusive!")
if __name__ == '__main__':
random.seed()
if not (options.cmd_get or options.cmd_set or options.monitor):
parser.error("One of -m, -g, or -s must be set")
parser = OptionParser("Usage: %prog [options] var [value]")
parser.add_option("-d", "--host", dest="host",
help="connect to HOST", metavar="HOST")
parser.add_option("-p", "--port", dest="port", type="int",
help="use PORT", metavar="PORT", default=4249)
parser.add_option("-g", "--get", action="store_true",
dest="cmd_get", help="perform GET operation")
parser.add_option("-s", "--set", action="store_true",
dest="cmd_set", help="perform SET operation")
parser.add_option("-i", "--id", dest="op_id", default=random.randint(1, sys.maxint),
help="set id manually", metavar="ID")
parser.add_option("-v", "--verbose", action="store_true",
dest="verbose", help="be verbose", default=False)
parser.add_option("-m", "--monitor", action="store_true",
dest="monitor", help="monitor the connection for traps", default=False)
if not (options.host):
parser.error("Destination host and port required!")
(options, args) = parser.parse_args()
sock = connect(options.host, options.port)
verbose = options.verbose
if options.cmd_set:
if len(args) < 2:
parser.error("Set requires var and value arguments")
do_set(args[0], ' '.join(args[1:]), options.id, sock)
if options.cmd_set and options.cmd_get:
parser.error("Get and set options are mutually exclusive!")
if options.cmd_get:
if len(args) != 1:
parser.error("Get requires the var argument")
do_get(args[0], options.id, sock)
if not (options.cmd_get or options.cmd_set or options.monitor):
parser.error("One of -m, -g, or -s must be set")
data = sock.recv(1024)
while (len(data)>0):
(answer, data) = remove_ipa_ctrl_header(data)
print "Got message:", answer
if not (options.host):
parser.error("Destination host and port required!")
if options.monitor:
while (True):
data = sock.recv(1024)
if len(data) == 0:
print "Connection is gone."
break
sock = connect(options.host, options.port)
while (len(data)>0):
(answer, data) = remove_ipa_ctrl_header(data)
print "Got message:", answer
if options.cmd_set:
if len(args) < 2:
parser.error("Set requires var and value arguments")
do_set(args[0], ' '.join(args[1:]), options.op_id, sock)
sock.close()
if options.cmd_get:
if len(args) != 1:
parser.error("Get requires the var argument")
do_get(args[0], options.op_id, sock)
data = sock.recv(1024)
while (len(data)>0):
(answer, data) = remove_ipa_ctrl_header(data)
print "Got message:", answer
if options.monitor:
while (True):
data = sock.recv(1024)
if len(data) == 0:
print "Connection is gone."
break
while (len(data)>0):
(answer, data) = remove_ipa_ctrl_header(data)
print "Got message:", answer
sock.close()

147
openbsc/contrib/ctrl2sse.py Executable file
View File

@@ -0,0 +1,147 @@
#!/usr/bin/python2
mod_license = '''
/*
* Copyright (C) 2016 sysmocom s.f.m.c. GmbH
*
* 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 3 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.
*/
'''
import sys, argparse, random, logging, tornado.ioloop, tornado.web, tornado.tcpclient, tornado.httpclient, eventsource, bsc_control
from eventsource import listener, request
'''
N. B: this is not an example of building proper REST API or building secure web application.
It's only purpose is to illustrate conversion of Osmocom's Control Interface to web-friendly API.
Exposing this to Internet while connected to production network might lead to all sorts of mischief and mayhem
from NSA' TAO breaking into your network to zombie apocalypse. Do NOT do that.
'''
token = None
stream = None
url = None
'''
Returns json according to following schema - see http://json-schema.org/documentation.html for details:
{
"title": "Ctrl Schema",
"type": "object",
"properties": {
"variable": {
"type": "string"
},
"varlue": {
"type": "string"
}
},
"required": ["interface", "variable", "value"]
}
Example validation from command-line:
json validate --schema-file=schema.json --document-file=data.json
The interface is represented as string because it might look different for IPv4 vs v6.
'''
def read_header(data):
t_length = bsc_control.ipa_ctrl_header(data)
if (t_length):
stream.read_bytes(t_length - 1, callback = read_trap)
else:
print >> sys.stderr, "protocol error: length missing in %s!" % data
@tornado.gen.coroutine
def read_trap(data):
(t, z, v, p) = data.split()
if (t != 'TRAP' or int(z) != 0):
print >> sys.stderr, "protocol error: TRAP != %s or 0! = %d" % (t, int(z))
else:
yield tornado.httpclient.AsyncHTTPClient().fetch(tornado.httpclient.HTTPRequest(url = "%s/%s/%s" % (url, "ping", token),
method = 'POST',
headers = {'Content-Type': 'application/json'},
body = tornado.escape.json_encode({ 'variable' : v, 'value' : p })))
stream.read_bytes(4, callback = read_header)
@tornado.gen.coroutine
def trap_setup(host, port, target_host, target_port, tk):
global stream
global url
global token
token = tk
url = "http://%s:%s/sse" % (host, port)
stream = yield tornado.tcpclient.TCPClient().connect(target_host, target_port)
stream.read_bytes(4, callback = read_header)
def get_v(s, v):
return { 'variable' : v, 'value' : bsc_control.get_var(s, tornado.escape.native_str(v)) }
class CtrlHandler(tornado.web.RequestHandler):
def initialize(self):
self.skt = bsc_control.connect(self.settings['ctrl_host'], self.settings['ctrl_port'])
def get(self, v):
self.write(get_v(self.skt, v))
def post(self):
self.write(get_v(self.skt, self.get_argument("variable")))
class SetCtrl(CtrlHandler):
def get(self, var, val):
bsc_control.set_var(self.skt, tornado.escape.native_str(var), tornado.escape.native_str(val))
super(SetCtrl, self).get(tornado.escape.native_str(var))
def post(self):
bsc_control.set_var(self.skt, tornado.escape.native_str(self.get_argument("variable")), tornado.escape.native_str(self.get_argument("value")))
super(SetCtrl, self).post()
class Slash(tornado.web.RequestHandler):
def get(self):
self.write('<html><head><title>%s</title></head><body>Using Tornado framework v%s'
'<form action="/get" method="POST">'
'<input type="text" name="variable">'
'<input type="submit" value="GET">'
'</form>'
'<form action="/set" method="POST">'
'<input type="text" name="variable">'
'<input type="text" name="value">'
'<input type="submit" value="SET">'
'</form>'
'</body></html>' % ("Osmocom Control Interface Proxy", tornado.version))
if __name__ == '__main__':
p = argparse.ArgumentParser(description='Osmocom Control Interface proxy.')
p.add_argument('-c', '--control-port', type = int, default = 4252, help = "Target Control Interface port")
p.add_argument('-a', '--control-host', default = 'localhost', help = "Target Control Interface adress")
p.add_argument('-b', '--host', default = 'localhost', help = "Adress to bind proxy's web interface")
p.add_argument('-p', '--port', type = int, default = 6969, help = "Port to bind proxy's web interface")
p.add_argument('-d', '--debug', action='store_true', help = "Activate debugging (default off)")
p.add_argument('-t', '--token', default = 'osmocom', help = "Token to be used by SSE client in URL e. g. http://127.0.0.1:8888/poll/osmocom where 'osmocom' is default token value")
p.add_argument('-k', '--keepalive', type = int, default = 5000, help = "Timeout betwwen keepalive messages, in milliseconds, defaults to 5000")
args = p.parse_args()
random.seed()
tornado.netutil.Resolver.configure('tornado.netutil.ThreadedResolver') # Use non-blocking resolver
logging.basicConfig()
application = tornado.web.Application([
(r"/", Slash),
(r"/get", CtrlHandler),
(r"/get/(.*)", CtrlHandler),
(r"/set", SetCtrl),
(r"/set/(.*)/(.*)", SetCtrl),
(r"/sse/(.*)/(.*)", listener.EventSourceHandler, dict(event_class = listener.JSONIdEvent, keepalive = args.keepalive)),
], debug = args.debug, ctrl_host = args.control_host, ctrl_port = args.control_port)
application.listen(address = args.host, port = args.port)
trap_setup(args.host, args.port, application.settings['ctrl_host'], application.settings['ctrl_port'], args.token)
tornado.ioloop.IOLoop.instance().start()

View File

@@ -1 +1,3 @@
SUBDIRS = examples
SUBDIRS = \
examples \
$(NULL)

View File

@@ -1,21 +0,0 @@
The protocol for the control interface is wrapped inside the ip.access header
with the IPAC_PROTO_OSMO protocol ID (0xee). Inside the ip.access header is
a struct ipaccess_head_ext with protocol ID 0x00 which indicates the control
interface.
After that the actual protocol is text based:
* Getting the value of a variable
-> GET <id> <var>
<- GET_REPLY <id> <var> <val>
or ERROR <id> <reason>
* Setting the value of a variable
-> SET <id> <var> <val>
<- SET_REPLY <id> <var> <val>
or ERROR <id> <reason>
* A value changes which triggers a trap
<- TRAP <var> <val>
<id> needs to be unique within a connection. '0' is not allowed

View File

@@ -1,4 +1,3 @@
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
dist-hook:

View File

@@ -40,7 +40,6 @@ network
timer t3119 0
timer t3122 0
timer t3141 0
dtx-used 0
subscriber-keep-in-ram 0
bts 0
type nanobts
@@ -55,6 +54,8 @@ network
channel allocator ascending
rach tx integer 9
rach max transmission 7
dtx uplink force
dtx downlink
ip.access unit_id 0 0
oml ip.access stream_id 255 line 0
neighbor-list mode manual-si5

View File

@@ -0,0 +1,36 @@
!
! OsmoCSCN configuration saved from vty
!
line vty
no login
!
network
network country code 1
mobile network code 1
short name OsmoCSCN
long name OsmoCSCN
auth policy closed
location updating reject cause 13
encryption a5 0
rrlp mode none
mm info 1
handover 0
handover window rxlev averaging 10
handover window rxqual averaging 1
handover window rxlev neighbor averaging 10
handover power budget interval 6
handover power budget hysteresis 3
handover maximum distance 9999
timer t3101 10
timer t3103 0
timer t3105 0
timer t3107 0
timer t3109 4
timer t3111 0
timer t3113 60
timer t3115 0
timer t3117 0
timer t3119 0
timer t3141 0
cscn
subscriber-create-on-demand

View File

@@ -37,7 +37,6 @@ network
timer t3119 0
timer t3122 0
timer t3141 0
dtx-used 0
subscriber-keep-in-ram 0
bts 0
type rbs2000

View File

@@ -0,0 +1,77 @@
!
! OpenBSC configuration saved from vty
! !
password foo
!
line vty
no login
!
e1_input
e1_line 0 driver ipa
network
network country code 1
mobile network code 1
short name OpenBSC
long name OpenBSC
auth policy closed
location updating reject cause 13
encryption a5 0
neci 1
rrlp mode none
mm info 1
handover 0
handover window rxlev averaging 10
handover window rxqual averaging 1
handover window rxlev neighbor averaging 10
handover power budget interval 6
handover power budget hysteresis 3
handover maximum distance 9999
timer t3101 10
timer t3103 0
timer t3105 0
timer t3107 0
timer t3109 4
timer t3111 0
timer t3113 60
timer t3115 0
timer t3117 0
timer t3119 0
timer t3141 0
bts 0
type sysmobts
band DCS1800
cell_identity 0
location_area_code 1
training_sequence_code 7
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
channel allocator ascending
rach tx integer 9
rach max transmission 7
ip.access unit_id 1801 0
oml ip.access stream_id 255 line 0
gprs mode none
trx 0
rf_locked 0
arfcn 514
nominal power 23
max_power_red 20
rsl e1 tei 0
timeslot 0
phys_chan_config CCCH+SDCCH4
timeslot 1
phys_chan_config SDCCH8
timeslot 2
phys_chan_config TCH/F
timeslot 3
phys_chan_config TCH/F
timeslot 4
phys_chan_config TCH/F
timeslot 5
phys_chan_config TCH/F
timeslot 6
phys_chan_config TCH/F
timeslot 7
phys_chan_config TCH/F

View File

@@ -1,250 +0,0 @@
Osmocom Authentication Protocol (OAP)
1. General
The Osmocom Authentication Protocol employs mutual authentication to register a
client with a server over an IPA connection. Milenage is used as the
authentication algorithm, where client and server have a shared secret.
For example, an SGSN, as OAP client, may use its SGSN ID to register with a MAP
proxy, an OAP server.
1.1. Connection
The protocol expects that a reliable, ordered, packet boundaries preserving
connection is used (e.g. IPA over TCP).
1.2. Using IPA
By default, the following identifiers should be used:
- IPA protocol: 0xee (OSMO)
- IPA OSMO protocol extension: 0x06 (OAP)
2. Procedures
Ideal communication sequence:
Client Server
| |
| Register (ID) |
|----------------------------------->|
| |
| Challenge (RAND+AUTN) |
|<-----------------------------------|
| |
| Challenge Result (XRES) |
|----------------------------------->|
| |
| Register Result |
|<-----------------------------------|
Variation "test setup":
Client Server
| |
| Register (ID) |
|----------------------------------->|
| |
| Register Result |
|<-----------------------------------|
Variation "invalid sequence nr":
Client Server
| |
| Register (ID) |
|----------------------------------->|
| |
| Challenge (RAND+AUTN) |
|<-----------------------------------|
| |
| Sync Request (AUTS) |
|----------------------------------->|
| |
| Challenge (RAND'+AUTN') |
|<-----------------------------------|
| |
| Challenge Result (XRES) |
|----------------------------------->|
| |
| Register Result |
|<-----------------------------------|
2.1. Register
The client sends a REGISTER_REQ message containing an identifier number.
2.2. Challenge
The OAP server (optionally) sends back a CHALLENGE_REQ, containing random bytes
and a milenage authentication token generated from these random bytes, using a
shared secret, to authenticate itself to the OAP client. The server may omit
this challenge entirely, based on its configuration, and immediately reply with
a Register Result response. If the client cannot be registered (e.g. id is
invalid), the server sends a REGISTER_ERR response.
2.3. Challenge Result
When the client has received a Challenge, it may verify the server's
authenticity and validity of the sequence number (included in AUTN), and, if
valid, reply with a CHALLENGE_RES message. This shall contain an XRES
authentication token generated by milenage from the same random bytes received
from the server and the same shared secet. If the client decides to cancel the
registration (e.g. invalid AUTN), it shall not reply to the CHALLENGE_REQ; a
CHALLENGE_ERR message may be sent, but is not mandatory. For example, the
client may directly start with a new REGISTER_REQ message.
2.4. Sync Request
When the client has received a Challenge but sees an invalid sequence number
(embedded in AUTN, according to the milenage algorithm), the client may send a
SYNC_REQ message containing an AUTS synchronisation token.
2.5. Sync Result
If the server has received a valid Sync Request, it shall answer by directly
sending another Challenge (see 2.2.). If an invalid Sync Request is received,
the server shall reply with a REGISTER_ERR message.
2.6. Register Result
The server sends a REGISTER_RES message to indicate that registration has been
successful. If the server cannot register the client (e.g. invalid challenge
response), it shall send a REGISTER_ERR message.
3. Message Format
3.1. General
Every message is based on the following message format
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
The receiver shall be able to receive IEs in any order. Unknown IEs shall be
ignored.
3.2.1. Register Request
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
30 Client ID big endian int (2 oct) M TLV 4
3.2.2. Register Error
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.6. Register Result
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
3.2.3. Challenge
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
20 RAND octet string (16) M TLV 18
23 AUTN octet string (16) M TLV 18
3.2.4. Challenge Error
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.5. Challenge Result
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
21 XRES octet string (8) M TLV 10
3.2.3. Sync Request
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
20 AUTS octet string (16) M TLV 18
3.2.4. Sync Error
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
4. Information Elements
4.1. General
[...]
4.2.1. Message Type
+---------------------------------------------------+
| 8 7 6 5 4 3 2 1 |
| |
| 0 0 0 0 0 1 0 0 - Register Request |
| 0 0 0 0 0 1 0 1 - Register Error |
| 0 0 0 0 0 1 1 0 - Register Result |
| |
| 0 0 0 0 1 0 0 0 - Challenge Request |
| 0 0 0 0 1 0 0 1 - Challenge Error |
| 0 0 0 0 1 0 1 0 - Challenge Result |
| |
| 0 0 0 0 1 1 0 0 - Sync Request |
| 0 0 0 0 1 1 0 1 - Sync Error (not used) |
| 0 0 0 0 1 1 1 0 - Sync Result (not used) |
| |
+---------------------------------------------------+
4.2.2. IE Identifier (informational)
These are the standard values for the IEI.
+---------------------------------------------------------+
| IEI Info Element Type |
| |
| 0x02 Cause GMM cause, 04.08: 10.5.5.14 |
| 0x20 RAND octet string |
| 0x23 AUTN octet string |
| 0x24 XRES octet string |
| 0x25 AUTS octet string |
| 0x30 Client ID big endian int (2 octets) |
+---------------------------------------------------------+
4.2.3. Client ID
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | Client ID IEI | octet 1
+-----------------------------------------------------+
| Length of Client ID IE contents (2) | octet 2
+-----------------------------------------------------+
| Client ID number, most significant byte | octet 3
+-----------------------------------------------------+
| Client ID number, least significant byte | octet 4
+-----------------------------------------------------+
The Client ID number shall be interpreted as an unsigned 16bit integer, where 0
indicates an invalid / unset ID.

View File

@@ -1,468 +0,0 @@
GPRS Subscriber Update Protocol
1. General
This document describes the remote protocol that is used by the SGSN to update
and manage the local subscriber list. The protocol and the messages are
designed after the corresponding MAP messages (see GSM 09.02) with the
following differences:
- The encoding uses TLV structures instead of ASN.1 encodings
- Segmentation is not used
See the specification of the Gr interface (GSM 03.60).
2. Connection
The protocol expects that a reliable, ordered, packet boundaries preserving
connection is used (e.g. IPA over TCP). The remote peer is either a service
that understands the protocol natively or a wrapper service that maps the
messages to/from real MAP messages that can be used to directly communicate
with an HLR.
2.1. Using IPA
By default, the following identifiers should be used:
- IPA protocol: 0xee (OSMO)
- IPA OSMO protocol extension: 0x05
2. Procedures
2.1. Authentication management
The SGSN sends a SEND_AUTHENTICATION_INFO_REQ message containing the MS's IMSI
to the peer. On errors, especially if authentication info is not availabe for
that IMSI, the peer returns a SEND_AUTHENTICATION_INFO_ERR message. Otherwise
the peer returns a SEND_AUTHENTICATION_INFO_RES message. If this message
contains at least one authentication tuple, the SGSN replaces all tuples that
are assigned to the subscriber. If the message doesn't contain any tuple the
SGSN may reject the Attach Request. (see GSM 09.02, 25.5.6)
2.2. Location Updating
The SGSN sends a UPDATE_LOCATION_REQ to the peer. If the request is denied by
the network, the peer returns an UPDATE_LOCATION_ERR message to the SGSN.
Otherwise the peer returns an UPDATE_LOCATION_RES message containing all
information fields that shall be inserted into the subscriber record. If
the 'PDP info complete' information element is set in the message, the SGSN
clears existing PDP information fields in the subscriber record first.
(see GSM 09.02, 19.1.1.8)
[...]
3. Message Format
3.1. General
Every message is based on the following message format
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
If a numeric range is indicated in the 'presence' column, multiple information
elements with the same tag may be used in sequence. The information elements
shall be sent in the given order. Nevertheless after the generic part the
receiver shall be able to received them in any order. Unknown IE shall be
ignored.
3.2.1. Send Authentication Info Request
SGSN -> Network peer
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
3.2.2. Send Authentication Info Error
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.3. Send Authentication Info Response
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
03 Auth tuple 4.2.5 0-5 TLV 36
3.2.4. Update Location Request
SGSN -> Network peer
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
3.2.5. Update Location Error
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.6. Update Location Result
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
08 MSISDN 4.2.10 O TLV 0-9
09 HLR Number 4.2.12 O TLV 0-9
04 PDP info complete 4.2.8 O TLV 2
05 PDP info 4.2.3 1-10 TLV
If the PDP info complete IE is present, the old PDP info list shall be cleared.
3.2.7. Location Cancellation Request
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
06 Cancellation type 4.2.6 M TLV 3
3.2.8. Location Cancellation Result
SGSN -> Network peer
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
3.2.9. Purge MS Request
SGSN -> Network peer
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
09 HLR Number 4.2.12 M TLV 0-9
3.2.10. Purge MS Error
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.11. Purge MS Result
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
07 Freeze P-TMSI 4.2.8 O TLV 2
3.2.12. Insert Subscriber Data Request
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
04 PDP info complete 4.2.8 O TLV 2
05 PDP info 4.2.3 0-10 TLV
If the PDP info complete IE is present, the old PDP info list shall be cleared.
3.2.13. Insert Subscriber Data Error
SGSN -> Network peer
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.14. Insert Subscriber Data Result
SGSN -> Network peer
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
3.2.15. Delete Subscriber Data Request
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
10 PDP context id 4.2.3 0-10 TLV
(no conditional IE)
3.2.16. Delete Subscriber Data Error
SGSN -> Network peer
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.17. Delete Subscriber Data Result
Network peer -> SGSN
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
01 IMSI 4.2.9 M TLV 2-10
4. Information Elements
4.1. General
[...]
4.2.1. Message Type
+---------------------------------------------------+
| 8 7 6 5 4 3 2 1 |
| |
| 0 0 0 0 0 1 0 0 - Update Location Request |
| 0 0 0 0 0 1 0 1 - Update Location Error |
| 0 0 0 0 0 1 1 0 - Update Location Result |
| |
| 0 0 0 0 1 0 0 0 - Send Auth Info Request |
| 0 0 0 0 1 0 0 1 - Send Auth Info Error |
| 0 0 0 0 1 0 1 0 - Send Auth Info Result |
| |
| 0 0 0 0 1 1 0 0 - Purge MS Request |
| 0 0 0 0 1 1 0 1 - Purge MS Error |
| 0 0 0 0 1 1 1 0 - Purge MS Result |
| |
| 0 0 0 1 0 0 0 0 - Insert Subscr. Data Request |
| 0 0 0 1 0 0 0 1 - Insert Subscr. Data Error |
| 0 0 0 1 0 0 1 0 - Insert Subscr. Data Result |
| |
| 0 0 0 1 0 1 0 0 - Delete Subscr. Data Request |
| 0 0 0 1 0 1 0 1 - Delete Subscr. Data Error |
| 0 0 0 1 0 1 1 0 - Delete Subscr. Data Result |
| |
| 0 0 0 1 1 1 0 0 - Location Cancellation Request |
| 0 0 0 1 1 1 0 1 - Location Cancellation Error |
| 0 0 0 1 1 1 1 0 - Location Cancellation Result |
| |
+---------------------------------------------------+
4.2.2. IP Address
The value part is encoded like in the Packet data protocol address IE defined
in GSM 04.08, 10.5.6.4. PDP type organization must be set to 'IETF allocated
address'.
4.2.3. PDP Info
This is a container for information elements describing a single PDP.
IEI Info Element Type Pres. Format Length
PDP Info IEI M V 1
Length of PDP Info IE length, no ext M V 1
10 PDP context id big endian int, 1-N C TLV 3
11 PDP type 4.2.4 C TLV 4
12 Access point name 04.08, 10.5.6.1 C TLV 3-102
13 Quality of Service 4.2.11 O TLV 1-20
The conditional IE are mandantory unless mentioned otherwise.
4.2.4. PDP Type
The PDP type value consists of 2 octets that are encoded like octet 4-5 of the
End User Address defined in GSM 09.60, 7.9.18.
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | PDP type IEI | octet 1
+-----------------------------------------------------+
| Length of PDP type IE contents (2) | octet 2
+-----------------------------------------------------+
| Spare | PDP type org. | octet 3
+-----------------------------------------------------+
| PDP type number | octet 4
+-----------------------------------------------------+
The spare bits are left undefined. While 09.60 defines them as '1 1 1 1', there
are MAP traces where these bits are set to '0 0 0 0'. So the receiver shall
ignore these bits.
Examples:
IPv4: PDP type org: 1 (IETF), PDP type number: 0x21
IPv6: PDP type org: 1 (IETF), PDP type number: 0x57
4.2.5. Auth tuple
This is a container for information elements describing a single authentication
tuple.
IEI Info Element Type Pres. Format Length
Auth. Tuple IEI M V 1
Length of Ath Tuple IE length, no ext M V 1
20 RAND octet string (16) M TLV 18
21 SRES octet string (4) M TLV 6
22 Kc octet string (8) M TLV 10
4.2.6. Cancellation Type
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | Cancellation type IEI | octet 1
+-----------------------------------------------------+
| Length of Cancellation Type IE contents (1) | octet 2
+-----------------------------------------------------+
| Cancellation type number | octet 4
+-----------------------------------------------------+
Where the cancellation type number is:
+---------------------------------------------------+
| 8 7 6 5 4 3 2 1 |
| |
| 0 0 0 0 0 0 0 0 - Update Procedure |
| 0 0 0 0 0 0 0 1 - Subscription Withdraw |
+---------------------------------------------------+
4.2.7. IE Identifier (informational)
These are the standard values for the IEI. See the message definitions for the
IEI that shall be used for the encoding.
+---------------------------------------------------------+
| IEI Info Element Type |
| |
| 0x01 IMSI Mobile identity, 04.08: 10.5.1.4 |
| 0x02 Cause GMM cause, 04.08: 10.5.5.14 |
| 0x03 Auth tuple 4.2.5 |
| 0x04 PDP info compl 4.2.8 |
| 0x05 PDP info 4.2.3 |
| 0x06 Cancel type 4.2.6 |
| 0x07 Freeze P-TMSI 4.2.8 |
| 0x08 MSISDN ISDN-AddressString/octet, 4.2.10 |
| 0x09 HLR Number 4.2.12 |
| 0x10 PDP context id big endian int |
| 0x11 PDP type 4.2.4 |
| 0x12 APN 04.08, 10.5.6.1 |
| 0x13 QoS 4.2.11 |
| 0x20 RAND octet string |
| 0x21 SRES octet string |
| 0x22 Kc octet string |
+---------------------------------------------------------+
4.2.8 Empty field
This is used for flags, if and only if this IE is present, the flag is set.
The semantics depend on the IEI and the context.
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | IEI | octet 1
+-----------------------------------------------------+
| Length of IE contents (0) | octet 2
+-----------------------------------------------------+
4.2.9. IMSI
The IMSI is encoded like in octet 4-N of the Called Party BCD Number defined in GSM 04.08, 10.5.4.7.
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | IMSI IEI | octet 1
+-----------------------------------------------------+
| Length of IMSI IE contents | octet 2
+-----------------------------------------------------+
| Number digit 2 | Number digit 1 | octet 3
+-----------------------------------------------------+
| Number digit 4 | Number digit 3 | octet 4
+-----------------------------------------------------+
: : :
+-----------------------------------------------------+
| see note 1) | octet 2+(N+1)div2
+-----------------------------------------------------+
Note 1) Either '1 1 1 1 | Number digit N' (N odd) or
'Number digit N | Number digit N-1' (N even),
where N is the number of digits.
4.2.10. ISDN-AddressString / MSISDN / Called Party BCD Number
The MSISDN is encoded as an ISDN-AddressString in GSM 09.02 and Called Party
BCD Number in GSM 04.08. It will be stored by the SGSN and then passed as is
to the GGSN during the activation of the primary PDP Context.
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | IEI | octet 1
+-----------------------------------------------------+
| Length of IE contents | octet 2
+-----------------------------------------------------+
| ext | Type of num | Numbering plan | octet 2
+-----------------------------------------------------+
| Number digit 2 | Number digit 1 | octet 3
+-----------------------------------------------------+
| Number digit 4 | Number digit 3 | octet 4
+-----------------------------------------------------+
: : :
+-----------------------------------------------------+
4.2.11 Quality of Service Subscribed Service
This encodes the subscribed QoS of a subscriber. It will be used by the
SGSN during the PDP Context activation. If the length of the QoS data
is 3 (three) octets it is assumed that these are octets 3-5 of the TS
3GPP TS 24.008 Quality of Service Octets. If it is more than three then
then it is assumed that the first octet is the Allocation/Retention
Priority and the reset are encoded as octets 3-N of 24.008.
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | IEI | octet 1
+-----------------------------------------------------+
| Length of IE contents | octet 2
+-----------------------------------------------------+
: : :
+-----------------------------------------------------+
4.2.12. HLR Number encoded as GSM 09.02 ISDN-AddressString
The HLR Number is encoded as an ISDN-AddressString in GSM 09.02. It
will be stored by the SGSN can be used by the CDR module to keep a
record.
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | IEI | octet 1
+-----------------------------------------------------+
| Length of IE contents | octet 2
+-----------------------------------------------------+
| ext | Type of num | Numbering plan | octet 2
+-----------------------------------------------------+
| Number digit 2 | Number digit 1 | octet 3
+-----------------------------------------------------+
| Number digit 4 | Number digit 3 | octet 4
+-----------------------------------------------------+
: : :
+-----------------------------------------------------+

View File

@@ -1,3 +1,8 @@
SUBDIRS = openbsc
SUBDIRS = \
openbsc \
$(NULL)
noinst_HEADERS = mISDNif.h compat_af_isdn.h
noinst_HEADERS = \
mISDNif.h \
compat_af_isdn.h \
$(NULL)

View File

@@ -1,24 +1,98 @@
noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \
gsm_subscriber.h gsm_04_11.h debug.h signal.h \
misdn.h chan_alloc.h paging.h ctrl.h \
trau_mux.h rs232.h openbscdefines.h rtp_proxy.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 socket.h e1_config.h trau_upqueue.h token_auth.h \
handover_decision.h rrlp.h \
crc24.h gprs_llc.h gprs_gmm.h \
gb_proxy.h gprs_sgsn.h sgsn.h \
auth.h osmo_msc.h bsc_msc.h bsc_nat.h \
osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \
osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \
bss.h gsm_data_shared.h ipaccess.h mncc_int.h \
arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \
osmux.h mgcp_transcode.h gprs_utils.h \
gprs_gb_parse.h smpp.h meas_feed.h \
gprs_gsup_client.h bsc_msg_filter.h \
oap.h oap_messages.h \
gtphub.h
noinst_HEADERS = \
abis_nm.h \
abis_om2000.h \
abis_rsl.h \
arfcn_range_encode.h \
auth.h \
bsc_msc.h \
bsc_msg_filter.h \
bsc_nat.h \
bsc_nat_callstats.h \
bsc_nat_sccp.h \
bsc_rll.h \
bss.h \
chan_alloc.h \
crc24.h \
ctrl.h \
db.h \
debug.h \
e1_config.h \
gb_proxy.h \
gprs_gb_parse.h \
gprs_gmm.h \
gprs_gsup_client.h \
gprs_llc.h \
gprs_llc_xid.h \
gprs_sgsn.h \
gprs_sndcp.h \
gprs_sndcp_comp.h \
gprs_sndcp_dcomp.h \
gprs_sndcp_pcomp.h \
gprs_sndcp_xid.h \
gprs_utils.h \
gsm_04_08.h \
gsm_04_11.h \
gsm_04_80.h \
gsm_data.h \
gsm_data_shared.h \
gsm_subscriber.h \
gtphub.h \
handover.h \
handover_decision.h \
ipaccess.h \
iu.h \
iucs.h \
meas_feed.h \
meas_rep.h \
mgcp.h \
mgcp_internal.h \
mgcp_transcode.h \
mgcpgw_client.h \
misdn.h \
mncc.h \
mncc_int.h \
msc_ifaces.h \
nat_rewrite_trie.h \
network_listen.h \
oap.h \
oap_messages.h \
openbscdefines.h \
osmo_bsc.h \
osmo_bsc_grace.h \
osmo_bsc_rf.h \
osmo_msc.h \
osmo_msc_data.h \
osmux.h \
paging.h \
rest_octets.h \
rrlp.h \
rs232.h \
rtp_proxy.h \
sgsn.h \
signal.h \
silent_call.h \
slhc.h \
smpp.h \
sms_queue.h \
socket.h \
system_information.h \
token_auth.h \
transaction.h \
trau_mux.h \
trau_upqueue.h \
ussd.h \
vty.h \
v42bis.h \
v42bis_private.h \
xsc.h \
$(NULL)
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
openbscdir = $(includedir)/openbsc
openbsc_HEADERS = \
bsc_api.h \
gsm_04_08.h \
meas_rep.h \
$(NULL)
openbscdir = \
$(includedir)/openbsc \
$(NULL)

View File

@@ -23,6 +23,7 @@
#define _RSL_H
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/msgb.h>
@@ -106,5 +107,9 @@ int rsl_start_t3109(struct gsm_lchan *lchan);
int rsl_direct_rf_release(struct gsm_lchan *lchan);
void dyn_ts_init(struct gsm_bts_trx_ts *ts);
int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts,
enum gsm_phys_chan_config to_pchan);
#endif /* RSL_MT_H */

View File

@@ -52,6 +52,4 @@ int gsm0808_page(struct gsm_bts *bts, unsigned int page_group,
unsigned int mi_len, uint8_t *mi, int chan_type);
int gsm0808_clear(struct gsm_subscriber_connection *conn);
struct llist_head *bsc_api_sub_connections(struct gsm_network *net);
#endif

View File

@@ -1,11 +1,13 @@
#ifndef _BSS_H_
#define _BSS_H_
struct gsm_network;
#include <openbsc/gsm_data.h>
struct msgb;
/* start and stop network */
extern int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), const char *cfg_file);
extern int bsc_network_alloc(mncc_recv_cb_t mncc_recv);
extern int bsc_network_configure(const char *cfg_file);
extern int bsc_shutdown_net(struct gsm_network *net);
/* register all supported BTS */

View File

@@ -20,6 +20,8 @@
#ifndef _DB_H
#define _DB_H
#include <stdbool.h>
#include "gsm_subscriber.h"
struct gsm_equipment;
@@ -35,13 +37,15 @@ int db_prepare(void);
int db_fini(void);
/* subscriber management */
struct gsm_subscriber *db_create_subscriber(const char *imsi);
struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
uint64_t smax, bool alloc_exten);
struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field,
const char *subscr);
int db_sync_subscriber(struct gsm_subscriber *subscriber);
int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id));
int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber);
int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber);
int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber, uint64_t smin,
uint64_t smax);
int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token);
int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char *imei);
int db_subscriber_delete(struct gsm_subscriber *subscriber);

View File

@@ -29,11 +29,16 @@ enum {
DBSSGP,
DLLC,
DSNDCP,
DSLHC,
DNAT,
DCTRL,
DSMPP,
DFILTER,
DGTPHUB,
DRANAP,
DSUA,
DV42BIS,
DIUCS,
Debug_LastEntry,
};

View File

@@ -4,15 +4,21 @@
#include <osmocom/core/msgb.h>
#include <openbsc/gprs_sgsn.h>
#include <stdbool.h>
int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause);
int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid,
uint8_t cause, uint8_t pco_len, uint8_t *pco_v);
int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp);
int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp);
int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme);
int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
bool drop_cipherable);
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
uint16_t *sai);
int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx);
int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg);
int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg,
struct gprs_llc_llme *llme);
void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *mmctx);
void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *mmctx, int gmm_cause);
void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *mmctx, int gmm_cause);
@@ -24,4 +30,6 @@ int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
time_t gprs_max_time_to_idle(void);
int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp, bool use_x213_nsap);
#endif /* _GPRS_GMM_H */

View File

@@ -2,7 +2,9 @@
#define _GPRS_LLC_H
#include <stdint.h>
#include <stdbool.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_llc_xid.h>
/* Section 4.7 LLC Layer Structure */
enum gprs_llc_sapi {
@@ -155,13 +157,32 @@ struct gprs_llc_llme {
/* Crypto parameters */
enum gprs_ciph_algo algo;
uint8_t kc[8];
uint8_t kc[16];
uint8_t cksn;
/* 3GPP TS 44.064 § 8.9.2: */
uint32_t iov_ui;
/* over which BSSGP BTS ctx do we need to transmit */
uint16_t bvci;
uint16_t nsei;
struct gprs_llc_lle lle[NUM_SAPIS];
/* Copy of the XID fields we have sent with the last
* network originated XID-Request. Since the phone
* may strip the optional fields in the confirmation
* we need to remeber those fields in order to be
* able to create the compression entity. */
struct llist_head *xid;
/* Compression entities */
struct {
/* In these two list_heads we will store the
* data and protocol compression entities,
* together with their compression states */
struct llist_head *proto;
struct llist_head *data;
} comp;
/* Internal management */
uint32_t age_timestamp;
};
@@ -208,16 +229,21 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv);
/* LL-UNITDATA.req */
int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
void *mmctx);
struct sgsn_mm_ctx *mmctx, bool encryptable);
/* Chapter 7.2.1.2 LLGMM-RESET.req */
int gprs_llgmm_reset(struct gprs_llc_llme *llme);
int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi);
int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi,
struct gprs_llc_llme *llme);
/* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */
int gprs_ll_xid_req(struct gprs_llc_lle *lle,
struct gprs_llc_xid_field *l3_xid_field);
/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */
int gprs_llgmm_assign(struct gprs_llc_llme *llme,
uint32_t old_tlli, uint32_t new_tlli,
enum gprs_ciph_algo alg, const uint8_t *kc);
uint32_t old_tlli, uint32_t new_tlli);
int gprs_llgmm_unassign(struct gprs_llc_llme *llme);
int gprs_llc_init(const char *cipher_plugin_path);
int gprs_llc_vty_init(void);
@@ -239,6 +265,7 @@ static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur)
}
/* LLC low level functions */
void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme);
/* parse a GPRS LLC header, also check for invalid frames */
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,

View File

@@ -0,0 +1,57 @@
/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
/* 3GPP TS 44.064 6.4.1.6 Exchange Identification (XID)
command/response parameter field */
struct gprs_llc_xid_field {
struct llist_head list;
uint8_t type; /* See also Table 6: LLC layer parameter
negotiation */
uint8_t *data; /* Payload data (memory is owned by the
* creator of the struct) */
unsigned int data_len; /* Payload length */
};
/* Transform a list with XID fields into a XID message (dst) */
int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen,
const struct llist_head *xid_fields);
/* Transform a XID message (dst) into a list of XID fields */
struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src,
int src_len);
/* Create a duplicate of an XID-Field */
struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx,
const struct gprs_llc_xid_field *xid_field);
/* Copy an llist with xid fields */
struct llist_head *gprs_llc_copy_xid(const void *ctx,
const struct llist_head *xid_fields);
/* Dump a list with XID fields (Debug) */
void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields,
unsigned int logl);

View File

@@ -23,7 +23,7 @@ struct gsm_subscriber;
enum gsm48_gsm_cause;
/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */
enum gprs_mm_state {
enum gprs_gmm_state {
GMM_DEREGISTERED, /* 4.1.3.3.1.1 */
GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */
GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */
@@ -31,6 +31,16 @@ enum gprs_mm_state {
GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */
};
/* TS 23.060 6.1.1 and 6.1.2 Mobility management states A/Gb and Iu mode */
enum gprs_pmm_state {
PMM_DETACHED,
PMM_CONNECTED,
PMM_IDLE,
MM_IDLE = PMM_DETACHED,
MM_READY = PMM_CONNECTED,
MM_STANDBY = PMM_IDLE,
};
enum gprs_mm_ctr {
GMM_CTR_PKTS_SIG_IN,
GMM_CTR_PKTS_SIG_OUT,
@@ -92,13 +102,32 @@ struct sgsn_ggsn_lookup {
uint8_t ti;
};
enum sgsn_ran_type {
/* GPRS/EDGE via Gb */
MM_CTX_T_GERAN_Gb,
/* UMTS via Iu */
MM_CTX_T_UTRAN_Iu,
/* GPRS/EDGE via Iu */
MM_CTX_T_GERAN_Iu,
};
struct service_info {
uint8_t type;
uint16_t pdp_status;
};
struct ue_conn_ctx;
/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */
/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */
struct sgsn_mm_ctx {
struct llist_head list;
enum sgsn_ran_type ran_type;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
enum gprs_mm_state mm_state;
enum gprs_gmm_state mm_state;
enum gprs_pmm_state pmm_state; /* Iu: page when in PMM-IDLE mode */
uint32_t p_tmsi;
uint32_t p_tmsi_old; /* old P-TMSI before new is confirmed */
uint32_t p_tmsi_sig;
@@ -106,10 +135,32 @@ struct sgsn_mm_ctx {
/* Opt: Software Version Numbber / TS 23.195 */
char msisdn[GSM_EXTENSION_LENGTH];
struct gprs_ra_id ra;
uint16_t cell_id;
uint32_t cell_id_age;
uint16_t sac; /* Iu: Service Area Code */
uint32_t sac_age;/* Iu: Service Area Code age */
struct {
uint16_t cell_id; /* Gb only */
uint32_t cell_id_age; /* Gb only */
uint8_t radio_prio_sms;
/* Additional bits not present in the GSM TS */
uint16_t nsei;
uint16_t bvci;
struct gprs_llc_llme *llme;
uint32_t tlli;
uint32_t tlli_new;
} gb;
struct {
int new_key;
uint16_t sac; /* Iu: Service Area Code */
uint32_t sac_age; /* Iu: Service Area Code age */
/* CSG ID */
/* CSG Membership */
/* Access Mode */
/* Seelected CN Operator ID (TS 23.251) */
/* CSG Subscription Data */
/* LIPA Allowed */
/* Voice Support Match Indicator */
struct ue_conn_ctx *ue_ctx;
struct service_info service;
} iu;
/* VLR number */
uint32_t new_sgsn_addr;
/* Authentication Triplet */
@@ -118,30 +169,40 @@ struct sgsn_mm_ctx {
/* Iu: CK, IK, KSI */
/* CKSN */
enum gprs_ciph_algo ciph_algo;
/* Auth & Ciphering Request reference from 3GPP TS 24.008 § 10.5.5.19: */
uint8_t ac_ref_nr_used;
struct {
uint8_t len;
uint8_t buf[50]; /* GSM 04.08 10.5.5.12a, extended in TS 24.008 */
} ms_radio_access_capa;
/* Supported Codecs (SRVCC) */
struct {
uint8_t len;
uint8_t buf[8]; /* GSM 04.08 10.5.5.12, extended in TS 24.008 */
} ms_network_capa;
/* UE Netowrk Capability (E-UTRAN) */
uint16_t drx_parms;
/* Active Time value for PSM */
int mnrg; /* MS reported to HLR? */
int ngaf; /* MS reported to MSC/VLR? */
int ppf; /* paging for GPRS + non-GPRS? */
/* Subscribed Charging Characteristics */
/* Trace Reference */
/* Trace Type */
/* Trigger ID */
/* OMC Identity */
/* SMS Parameters */
int recovery;
uint8_t radio_prio_sms;
/* Access Restriction */
/* GPRS CSI (CAMEL) */
/* MG-CSI (CAMEL) */
/* Subscribed UE-AMBR */
/* UE-AMBR */
/* APN Subscribed */
struct llist_head pdp_list;
/* Additional bits not present in the GSM TS */
struct gprs_llc_llme *llme;
uint32_t tlli;
uint32_t tlli_new;
uint16_t nsei;
uint16_t bvci;
struct rate_ctr_group *ctrg;
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
@@ -176,6 +237,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
const struct gprs_ra_id *raid);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx);
/* look-up by matching TLLI and P-TMSI (think twice before using this) */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
@@ -184,6 +246,8 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
/* Allocate a new SGSN MM context */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
const struct gprs_ra_id *raid);
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx);
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx);
struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
@@ -307,9 +371,6 @@ int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn);
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len);
/* Force re-attachment based on msgb meta data */
int sgsn_force_reattach_oldmsg(struct msgb *oldmsg);
/*
* ctrl interface related work
*/
@@ -406,4 +467,6 @@ int gprs_sndcp_vty_init(void);
struct sgsn_instance;
int sgsn_gtp_init(struct sgsn_instance *sgi);
void sgsn_rate_ctr_init();
#endif /* _GPRS_SGSN_H */

View File

@@ -21,6 +21,16 @@ struct defrag_state {
struct llist_head frag_list;
struct osmo_timer_list timer;
/* Holds state to know which compression mode is used
* when the packet is re-assembled */
uint8_t pcomp;
uint8_t dcomp;
/* Holds the pointers to the compression entity list
* that is used when the re-assembled packet is decompressed */
struct llist_head *proto;
struct llist_head *data;
};
/* See 6.7.1.2 Reassembly */
@@ -50,4 +60,20 @@ struct gprs_sndcp_entity {
extern struct llist_head gprs_sndcp_entities;
/* Set of SNDCP-XID negotiation (See also: TS 144 065,
* Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi);
/* Process SNDCP-XID indication (See also: TS 144 065,
* Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
struct gprs_llc_xid_field *xid_field_response,
struct gprs_llc_lle *lle);
/* Process SNDCP-XID indication
* (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
struct gprs_llc_xid_field *xid_field_request,
struct gprs_llc_lle *lle);
#endif /* INT_SNDCP_H */

View File

@@ -0,0 +1,82 @@
/* GPRS SNDCP header compression entity management tools */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_sndcp_xid.h>
/* Header / Data compression entity */
struct gprs_sndcp_comp {
struct llist_head list;
/* Serves as an ID in case we want to delete this entity later */
unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */
/* Specifies to which NSAPIs the compression entity is assigned */
uint8_t nsapi_len; /* Number of applicable NSAPIs (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
/* Assigned pcomp values */
uint8_t comp_len; /* Number of contained PCOMP / DCOMP values */
uint8_t comp[MAX_COMP]; /* see also: 6.5.1.1.5 and 6.6.1.1.5 */
/* Algorithm parameters */
int algo; /* Algorithm type (see gprs_sndcp_xid.h) */
int compclass; /* See gprs_sndcp_xid.h/c */
void *state; /* Algorithm status and parameters */
};
#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */
#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */
/* Allocate a compression enitiy list */
struct llist_head *gprs_sndcp_comp_alloc(const void *ctx);
/* Free a compression entitiy list */
void gprs_sndcp_comp_free(struct llist_head *comp_entities);
/* Delete a compression entity */
void gprs_sndcp_comp_delete(struct llist_head *comp_entities, unsigned int entity);
/* Create and Add a new compression entity
* (returns a pointer to the compression entity that has just been created) */
struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
struct llist_head *comp_entities,
const struct gprs_sndcp_comp_field
*comp_field);
/* Find which compression entity handles the specified pcomp/dcomp */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
*comp_entities, uint8_t comp);
/* Find which compression entity handles the specified nsapi */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
*comp_entities, uint8_t nsapi);
/* Find a comp_index for a given pcomp/dcomp value */
uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp);
/* Find a pcomp/dcomp value for a given comp_index */
uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp_index);

View File

@@ -0,0 +1,53 @@
/* GPRS SNDCP data compression handler */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_sndcp_comp.h>
/* Note: The decompressed packet may have a maximum size of:
* Return value * MAX_DATADECOMPR_FAC */
#define MAX_DATADECOMPR_FAC 10
/* Note: In unacknowledged mode (SN_UNITDATA), the comression state is reset
* for every NPDU. The compressor needs a reasonably large payload to operate
* effectively (yield positive compression gain). For packets shorter than 100
* byte, no positive compression gain can be expected so we will skip the
* compression for short packets. */
#define MIN_COMPR_PAYLOAD 100
/* Initalize data compression */
int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field);
/* Terminate data compression */
void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity);
/* Expand packet */
int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities);
/* Compress packet */
int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi);

View File

@@ -0,0 +1,46 @@
/* GPRS SNDCP header compression handler */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_sndcp_comp.h>
/* Note: The decompressed packet may have a maximum size of:
* Return value + MAX_DECOMPR_INCR */
#define MAX_HDRDECOMPR_INCR 64
/* Initalize header compression */
int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field);
/* Terminate header compression */
void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity);
/* Expand packet header */
int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities);
/* Compress packet header */
int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi);

View File

@@ -0,0 +1,216 @@
/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#define CURRENT_SNDCP_VERSION 0 /* See 3GPP TS 44.065, clause 8 */
#define MAX_ENTITIES 32 /* 3GPP TS 44.065 reserves 5 bit
* for compression enitity number */
#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */
#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */
#define MAX_ROHC 16 /* Maximum number of ROHC compression profiles */
/* According to: 3GPP TS 44.065, 6.5.1.1 Format of the protocol control
* information compression field (Figure 7) and 3GPP TS 44.065,
* 6.6.1.1 Format of the data compression field (Figure 9) */
struct gprs_sndcp_comp_field {
struct llist_head list;
/* Propose bit (P), see also: 6.5.1.1.2 and 6.6.1.1.2 */
unsigned int p;
/* Entity number, see also: 6.5.1.1.3 and 6.6.1.1.3 */
unsigned int entity;
/* Algorithm identifier, see also: 6.5.1.1.4 and 6.6.1.1.4 */
int algo;
/* Number of contained PCOMP / DCOMP values */
uint8_t comp_len;
/* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */
uint8_t comp[MAX_COMP];
/* Note: Only one of the following struct pointers may,
be used. Unused pointers must be set to NULL! */
struct gprs_sndcp_pcomp_rfc1144_params *rfc1144_params;
struct gprs_sndcp_pcomp_rfc2507_params *rfc2507_params;
struct gprs_sndcp_pcomp_rohc_params *rohc_params;
struct gprs_sndcp_dcomp_v42bis_params *v42bis_params;
struct gprs_sndcp_dcomp_v44_params *v44_params;
};
/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_hdr_comp_algo {
RFC_1144, /* TCP/IP header compression, see also 6.5.2 */
RFC_2507, /* TCP/UDP/IP header compression, see also: 6.5.3 */
ROHC /* Robust Header Compression, see also 6.5.4 */
};
/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_data_comp_algo {
V42BIS, /* V.42bis data compression, see also 6.6.2 */
V44 /* V44 data compression, see also: 6.6.3 */
};
/* According to: 3GPP TS 44.065, 8 SNDCP XID parameters */
enum gprs_sndcp_xid_param_types {
SNDCP_XID_VERSION_NUMBER,
SNDCP_XID_DATA_COMPRESSION, /* See also: subclause 6.6.1 */
SNDCP_XID_PROTOCOL_COMPRESSION, /* See also: subclause 6.5.1 */
};
/* According to: 3GPP TS 44.065, 6.5.2.1 Parameters (Table 5) */
struct gprs_sndcp_pcomp_rfc1144_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int s01; /* (default 15) */
};
/* According to: 3GPP TS 44.065, 6.5.2.2 Assignment of PCOMP values */
enum gprs_sndcp_pcomp_rfc1144_pcomp {
RFC1144_PCOMP1, /* Uncompressed TCP */
RFC1144_PCOMP2, /* Compressed TCP */
RFC1144_PCOMP_NUM /* Number of pcomp values */
};
/* According to: 3GPP TS 44.065, 6.5.3.1 Parameters (Table 6) */
struct gprs_sndcp_pcomp_rfc2507_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int f_max_period; /* (default 256) */
int f_max_time; /* (default 5) */
int max_header; /* (default 168) */
int tcp_space; /* (default 15) */
int non_tcp_space; /* (default 15) */
};
/* According to: 3GPP TS 44.065, 6.5.3.2 Assignment of PCOMP values for RFC2507 */
enum gprs_sndcp_pcomp_rfc2507_pcomp {
RFC2507_PCOMP1, /* Full Header */
RFC2507_PCOMP2, /* Compressed TCP */
RFC2507_PCOMP3, /* Compressed TCP non delta */
RFC2507_PCOMP4, /* Compressed non TCP */
RFC2507_PCOMP5, /* Context state */
RFC2507_PCOMP_NUM /* Number of pcomp values */
};
/* According to: 3GPP TS 44.065, 6.5.4.1 Parameter (Table 10) */
struct gprs_sndcp_pcomp_rohc_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int max_cid; /* (default 15) */
int max_header; /* (default 168) */
uint8_t profile_len; /* (default 1) */
uint16_t profile[MAX_ROHC]; /* (default 0, ROHC uncompressed) */
};
/* According to: 3GPP TS 44.065, 6.5.4.2 Assignment of PCOMP values for ROHC */
enum gprs_sndcp_pcomp_rohc_pcomp {
ROHC_PCOMP1, /* ROHC small CIDs */
ROHC_PCOMP2, /* ROHC large CIDs */
ROHC_PCOMP_NUM /* Number of pcomp values */
};
/* ROHC compression profiles, see also:
http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.xhtml */
enum gprs_sndcp_xid_rohc_profiles {
ROHC_UNCOMPRESSED = 0x0000, /* ROHC uncompressed [RFC5795] */
ROHC_RTP = 0x0001, /* ROHC RTP [RFC3095] */
ROHCV2_RTP = 0x0101, /* ROHCv2 RTP [RFC5225] */
ROHC_UDP = 0x0002, /* ROHC UDP [RFC3095] */
ROHCv2_UDP = 0x0102, /* ROHCv2 UDP [RFC5225] */
ROHC_ESP = 0x0003, /* ROHC ESP [RFC3095] */
ROHCV2_ESP = 0x0103, /* ROHCv2 ESP [RFC5225] */
ROHC_IP = 0x0004, /* ROHC IP [RFC3843] */
ROHCV2_IP = 0x0104, /* ROHCv2 IP [RFC5225] */
ROHC_LLA = 0x0005, /* ROHC LLA [RFC4362] */
ROHC_LLA_WITH_R_MODE = 0x0105, /* ROHC LLA with R-mode [RFC3408] */
ROHC_TCP = 0x0006, /* ROHC TCP [RFC6846] */
ROHC_RTP_UDP_LITE = 0x0007, /* ROHC RTP/UDP-Lite [RFC4019] */
ROHCV2_RTP_UDP_LITE = 0x0107, /* ROHCv2 RTP/UDP-Lite [RFC5225] */
ROHC_UDP_LITE = 0x0008, /* ROHC UDP-Lite [RFC4019] */
ROHCV2_UDP_LITE = 0x0108, /* ROHCv2 UDP-Lite [RFC5225] */
};
/* According to: 3GPP TS 44.065, 6.6.2.1 Parameters (Table 7a) */
struct gprs_sndcp_dcomp_v42bis_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int p0; /* (default 3) */
int p1; /* (default 2048) */
int p2; /* (default 20) */
};
/* According to: 3GPP TS 44.065, 6.6.2.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v42bis_dcomp {
V42BIS_DCOMP1, /* V.42bis enabled */
V42BIS_DCOMP_NUM /* Number of dcomp values */
};
/* According to: 3GPP TS 44.065, 6.6.3.1 Parameters (Table 7c) */
struct gprs_sndcp_dcomp_v44_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int c0; /* (default 10000000) */
int p0; /* (default 3) */
int p1t; /* Refer to subclause 6.6.3.1.4 */
int p1r; /* Refer to subclause 6.6.3.1.5 */
int p3t; /* (default 3 x p1t) */
int p3r; /* (default 3 x p1r) */
};
/* According to: 3GPP TS 44.065, 6.6.3.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v44_dcomp {
V44_DCOMP1, /* Packet method compressed */
V44_DCOMP2, /* Multi packet method compressed */
V44_DCOMP_NUM /* Number of dcomp values */
};
/* Transform a list with compression fields into an SNDCP-XID message (dst) */
int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen,
const struct llist_head *comp_fields);
/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */
struct llist_head *gprs_sndcp_parse_xid(const void *ctx,
const uint8_t * src,
unsigned int src_len,
const struct llist_head *comp_fields_req);
/* Find out to which compression class the specified comp-field belongs
* (header compression or data compression?) */
int gprs_sndcp_get_compression_class(
const struct gprs_sndcp_comp_field *comp_field);
/* Dump a list with SNDCP-XID fields (Debug) */
void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields,
unsigned int logl);

View File

@@ -77,6 +77,8 @@ int decode_bcd_number(char *output, int output_len, const uint8_t *bcd_lv,
int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv);
int gsm48_extract_mi(uint8_t *classmark2, int length, char *mi_string, uint8_t *mi_type);
int gsm48_paging_extract_mi(struct gsm48_pag_resp *pag, int length, char *mi_string, uint8_t *mi_type);
/* TODO MSCSPLIT remove gsm48_handle_paging_resp() */
int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct gsm_subscriber *subscr);
int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode);

View File

@@ -38,5 +38,5 @@ int gsm411_send_sms(struct gsm_subscriber_connection *conn,
struct gsm_sms *sms);
void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn);
uint8_t sms_next_rp_msg_ref(struct gsm_subscriber_connection *conn);
uint8_t sms_next_rp_msg_ref(uint8_t *next_rp_ref);
#endif

View File

@@ -14,7 +14,16 @@ int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
const struct msgb *msg,
const struct ussd_request *request);
int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text);
int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn);
struct msgb *gsm0480_gen_ussdNotify(int level, const char *text);
struct msgb *gsm0480_gen_releaseComplete(void);
int msc_gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn,
int level, const char *text);
int msc_gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn);
/* TODO: move to a bsc_*.h file? */
int bsc_gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn,
int level, const char *text);
int bsc_gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn);
#endif

View File

@@ -2,12 +2,20 @@
#define _GSM_DATA_H
#include <stdint.h>
#include <regex.h>
#include <sys/types.h>
#include <stdbool.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stats.h>
#include <osmocom/crypt/auth.h>
#include <openbsc/rest_octets.h>
#include <openbsc/xsc.h>
#include <openbsc/mgcpgw_client.h>
/** annotations for msgb ownership */
#define __uses
@@ -16,6 +24,7 @@
struct mncc_sock_state;
struct gsm_subscriber_group;
struct ue_conn_ctx;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
@@ -97,7 +106,19 @@ struct neigh_meas_proc {
uint8_t last_seen_nr;
};
/* the per subscriber data for lchan */
enum interface_type {
IFACE_UNKNOWN = -1,
IFACE_A = 0, /* A-interface for 2G */
IFACE_IU = 1 /* Iu-interface for UMTS aka 3G (IuCS or IuPS) */
};
enum integrity_protection_state {
INTEGRITY_PROTECTION_NONE = 0,
INTEGRITY_PROTECTION_IK = 1,
INTEGRITY_PROTECTION_IK_CK = 2,
};
/* active radio connection of a mobile subscriber */
struct gsm_subscriber_connection {
struct llist_head entry;
@@ -125,18 +146,35 @@ struct gsm_subscriber_connection {
int mncc_rtp_connect_pending;
/* bsc structures */
struct osmo_bsc_sccp_con *sccp_con;
struct osmo_bsc_sccp_con *sccp_con; /* BSC */
/* back pointers */
struct gsm_network *network;
int in_release;
struct gsm_lchan *lchan;
struct gsm_lchan *ho_lchan;
struct gsm_bts *bts;
struct gsm_lchan *lchan; /* BSC */
struct gsm_lchan *ho_lchan; /* BSC */
struct gsm_bts *bts; /* BSC */
/* for assignment handling */
struct osmo_timer_list T10;
struct gsm_lchan *secondary_lchan;
struct osmo_timer_list T10; /* BSC */
struct gsm_lchan *secondary_lchan; /* BSC */
uint16_t lac;
struct gsm_encr encr;
/* 2G or 3G? See enum interface_type */
int via_iface;
/* which Iu-CS connection, if any. */
struct {
struct ue_conn_ctx *ue_ctx;
int integrity_protection;
unsigned int mgcp_rtp_endpoint;
uint16_t mgcp_rtp_port_ue;
uint16_t mgcp_rtp_port_cn;
uint8_t rab_id;
} iu;
};
@@ -144,62 +182,103 @@ struct gsm_subscriber_connection {
#include "gsm_data_shared.h"
/* Some statistics of our network */
struct gsmnet_stats {
struct {
struct osmo_counter *total;
struct osmo_counter *no_channel;
} chreq;
struct {
struct osmo_counter *attempted;
struct osmo_counter *no_channel; /* no channel available */
struct osmo_counter *timeout; /* T3103 timeout */
struct osmo_counter *completed; /* HO COMPL received */
struct osmo_counter *failed; /* HO FAIL received */
} handover;
struct {
struct osmo_counter *attach;
struct osmo_counter *normal;
struct osmo_counter *periodic;
struct osmo_counter *detach;
} loc_upd_type;
struct {
struct osmo_counter *reject;
struct osmo_counter *accept;
} loc_upd_resp;
struct {
struct osmo_counter *attempted;
struct osmo_counter *detached;
struct osmo_counter *completed;
struct osmo_counter *expired;
} paging;
struct {
struct osmo_counter *submitted; /* MO SMS submissions */
struct osmo_counter *no_receiver;
struct osmo_counter *delivered; /* MT SMS deliveries */
struct osmo_counter *rp_err_mem;
struct osmo_counter *rp_err_other;
} sms;
struct {
struct osmo_counter *mo_setup;
struct osmo_counter *mo_connect_ack;
struct osmo_counter *mt_setup;
struct osmo_counter *mt_connect;
} call;
struct {
struct osmo_counter *rf_fail;
struct osmo_counter *rll_err;
} chan;
struct {
struct osmo_counter *oml_fail;
struct osmo_counter *rsl_fail;
} bts;
enum {
BSC_CTR_CHREQ_TOTAL,
BSC_CTR_CHREQ_NO_CHANNEL,
BSC_CTR_HANDOVER_ATTEMPTED,
BSC_CTR_HANDOVER_NO_CHANNEL,
BSC_CTR_HANDOVER_TIMEOUT,
BSC_CTR_HANDOVER_COMPLETED,
BSC_CTR_HANDOVER_FAILED,
BSC_CTR_PAGING_ATTEMPTED,
BSC_CTR_PAGING_DETACHED,
BSC_CTR_PAGING_COMPLETED,
BSC_CTR_PAGING_EXPIRED,
BSC_CTR_CHAN_RF_FAIL,
BSC_CTR_CHAN_RLL_ERR,
BSC_CTR_BTS_OML_FAIL,
BSC_CTR_BTS_RSL_FAIL,
};
static const struct rate_ctr_desc bsc_ctr_description[] = {
[BSC_CTR_CHREQ_TOTAL] = {"chreq.total", "Received channel requests."},
[BSC_CTR_CHREQ_NO_CHANNEL] = {"chreq.no_channel", "Sent to MS no channel available."},
[BSC_CTR_HANDOVER_ATTEMPTED] = {"handover.attempted", "Received handover attempts."},
[BSC_CTR_HANDOVER_NO_CHANNEL] = {"handover.no_channel", "Sent no channel available responses."},
[BSC_CTR_HANDOVER_TIMEOUT] = {"handover.timeout", "Count the amount of timeouts of timer T3103."},
[BSC_CTR_HANDOVER_COMPLETED] = {"handover.completed", "Received handover completed."},
[BSC_CTR_HANDOVER_FAILED] = {"handover.failed", "Receive HO FAIL messages."},
[BSC_CTR_PAGING_ATTEMPTED] = {"paging.attempted", "Paging attempts for a MS."},
[BSC_CTR_PAGING_DETACHED] = {"paging.detached", "Counts the amount of paging attempts which couldn't sent out any paging request because no responsible bts found."},
[BSC_CTR_PAGING_COMPLETED] = {"paging.completed", "Paging successful completed."},
[BSC_CTR_PAGING_EXPIRED] = {"paging.expired", "Paging Request expired because of timeout T3113."},
[BSC_CTR_CHAN_RF_FAIL] = {"chan.rf_fail", "Received a RF failure indication from BTS."},
[BSC_CTR_CHAN_RLL_ERR] = {"chan.rll_err", "Received a RLL failure with T200 cause from BTS."},
[BSC_CTR_BTS_OML_FAIL] = {"bts.oml_fail", "Received a TEI down on a OML link."},
[BSC_CTR_BTS_RSL_FAIL] = {"bts.rsl_fail", "Received a TEI down on a OML link."},
};
enum {
MSC_CTR_LOC_UPDATE_TYPE_ATTACH,
MSC_CTR_LOC_UPDATE_TYPE_NORMAL,
MSC_CTR_LOC_UPDATE_TYPE_PERIODIC,
MSC_CTR_LOC_UPDATE_TYPE_DETACH,
MSC_CTR_LOC_UPDATE_FAILED,
MSC_CTR_LOC_UPDATE_COMPLETED,
MSC_CTR_SMS_SUBMITTED,
MSC_CTR_SMS_NO_RECEIVER,
MSC_CTR_SMS_DELIVERED,
MSC_CTR_SMS_RP_ERR_MEM,
MSC_CTR_SMS_RP_ERR_OTHER,
MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR,
MSC_CTR_CALL_MO_SETUP,
MSC_CTR_CALL_MO_CONNECT_ACK,
MSC_CTR_CALL_MT_SETUP,
MSC_CTR_CALL_MT_CONNECT,
};
static const struct rate_ctr_desc msc_ctr_description[] = {
[MSC_CTR_LOC_UPDATE_TYPE_ATTACH] = {"loc_update_type.attach", "Received location update imsi attach requests."},
[MSC_CTR_LOC_UPDATE_TYPE_NORMAL] = {"loc_update_type.normal", "Received location update normal requests."},
[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC] = {"loc_update_type.periodic", "Received location update periodic requests."},
[MSC_CTR_LOC_UPDATE_TYPE_DETACH] = {"loc_update_type.detach", "Received location update detach indication."},
[MSC_CTR_LOC_UPDATE_FAILED] = {"loc_update_resp.failed", "Rejected location updates."},
[MSC_CTR_LOC_UPDATE_COMPLETED] = {"loc_update_resp.completed", "Successful location updates."},
[MSC_CTR_SMS_SUBMITTED] = {"sms.submitted", "Received a RPDU from a MS (MO)."},
[MSC_CTR_SMS_NO_RECEIVER] = {"sms.no_receiver", "Counts SMS which couldn't routed because no receiver found."},
[MSC_CTR_SMS_DELIVERED] = {"sms.delivered", "Global SMS Deliver attempts."},
[MSC_CTR_SMS_RP_ERR_MEM] = {"sms.rp_err_mem", "CAUSE_MT_MEM_EXCEEDED errors of MS responses on a sms deliver attempt."},
[MSC_CTR_SMS_RP_ERR_OTHER] = {"sms.rp_err_other", "Other error of MS responses on a sms delive attempt."},
[MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR] = {"sms.deliver_unknown_error", "Unknown error occured during sms delivery."},
/* FIXME: count also sms delivered */
[MSC_CTR_CALL_MO_SETUP] = {"call.mo_setup", "Received setup requests from a MS to init a MO call."},
[MSC_CTR_CALL_MO_CONNECT_ACK] = {"call.mo_connect_ack", "Received a connect ack from MS of a MO call. Call is now succesful connected up."},
[MSC_CTR_CALL_MT_SETUP] = {"call.mt_setup", "Sent setup requests to the MS (MT)."},
[MSC_CTR_CALL_MT_CONNECT] = {"call.mt_connect", "Sent a connect to the MS (MT)."},
};
static const struct rate_ctr_group_desc bsc_ctrg_desc = {
"bsc",
"base station controller",
OSMO_STATS_CLASS_GLOBAL,
ARRAY_SIZE(bsc_ctr_description),
bsc_ctr_description,
};
static const struct rate_ctr_group_desc msc_ctrg_desc = {
"msc",
"mobile switching center",
OSMO_STATS_CLASS_GLOBAL,
ARRAY_SIZE(msc_ctr_description),
msc_ctr_description,
};
enum gsm_auth_policy {
GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */
GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */
GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */
GSM_AUTH_POLICY_REGEXP, /* accept IMSIs matching given regexp */
};
#define GSM_T3101_DEFAULT 10
@@ -207,13 +286,28 @@ enum gsm_auth_policy {
#define GSM_T3113_DEFAULT 60
#define GSM_T3122_DEFAULT 10
struct gsm_tz {
int override; /* if 0, use system's time zone instead. */
int hr; /* hour */
int mn; /* minute */
int dst; /* daylight savings */
};
struct gsm_network {
/* TODO MSCSPLIT the gsm_network struct is basically a kitchen sink for
* global settings and variables, "madly" mixing BSC and MSC stuff. Split
* this in e.g. struct osmo_bsc and struct osmo_msc, with the things
* these have in common, like country and network code, put in yet
* separate structs and placed as members in osmo_bsc and osmo_msc. */
/* global parameters */
uint16_t country_code;
uint16_t network_code;
char *name_long;
char *name_short;
enum gsm_auth_policy auth_policy;
regex_t authorized_regexp;
char *authorized_reg_str;
enum gsm48_reject_value reject_cause;
int a5_encryption;
int neci;
@@ -235,11 +329,13 @@ struct gsm_network {
unsigned int max_distance; /* TA values */
} handover;
struct gsmnet_stats stats;
struct rate_ctr_group *bsc_ctrs;
struct rate_ctr_group *msc_ctrs;
/* layer 4 */
struct mncc_sock_state *mncc_state;
int (*mncc_recv) (struct gsm_network *net, struct msgb *msg);
mncc_recv_cb_t mncc_recv;
struct llist_head upqueue;
struct llist_head trans_list;
struct bsc_api *bsc_api;
@@ -269,9 +365,6 @@ struct gsm_network {
enum rrlp_mode mode;
} rrlp;
/* enable the DTXu and DTXd for this network */
int dtx_enabled;
enum gsm_chan_t ctype_by_chreq[16];
/* Use a TCH for handling requests of type paging any */
@@ -281,7 +374,10 @@ struct gsm_network {
struct osmo_bsc_data *bsc_data;
/* subscriber related features */
int create_subscriber;
bool auto_create_subscr;
bool auto_assign_exten;
uint64_t ext_min;
uint64_t ext_max;
struct gsm_subscriber_group *subscr_group;
struct gsm_sms_queue *sms_queue;
@@ -290,6 +386,28 @@ struct gsm_network {
/* control interface */
struct ctrl_handle *ctrl;
/* Allow or disallow TCH/F on dynamic TCH/F_TCH/H_PDCH; OS#1778 */
bool dyn_ts_allow_tch_f;
/* TODO: vty for this; related: OS#1781 */
/* all active subscriber connections. */
struct llist_head subscr_conns;
/* if override is nonzero, this timezone data is used for all MM
* contexts. */
/* TODO: in OsmoNITB, tz-override used to be BTS-specific. To enable
* BTS|RNC specific timezone overrides for multi-tz networks in
* OsmoCSCN, this should be tied to the location area code (LAC). */
struct gsm_tz tz;
/* Periodic location update default value */
uint8_t t3212;
struct {
struct mgcpgw_client_conf conf;
struct mgcpgw_client *client;
} mgcpgw;
};
struct osmo_esme;
@@ -336,13 +454,9 @@ struct gsm_sms {
char text[SMS_TEXT_SIZE];
};
struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code,
int (*mncc_recv)(struct gsm_network *, struct msgb *));
int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
extern void talloc_ctx_init(void *ctx_root);
/* Get reference to a neighbor cell on a given BCCH ARFCN */
struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
uint16_t arfcn, uint8_t bsic);
int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
enum gsm_bts_type parse_btstype(const char *arg);
const char *btstype2str(enum gsm_bts_type type);
@@ -426,13 +540,15 @@ int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode);
int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts);
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan);
int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat);
int gsm_bts_model_register(struct gsm_bts_model *model);
struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan);
void subscr_con_free(struct gsm_subscriber_connection *conn);
struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan);
void bsc_subscr_con_free(struct gsm_subscriber_connection *conn);
struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network);
void msc_subscr_con_free(struct gsm_subscriber_connection *conn);
struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net,
enum gsm_bts_type type,

View File

@@ -14,7 +14,7 @@
#include <osmocom/gsm/rxlev_stat.h>
#include <osmocom/gsm/sysinfo.h>
#include <osmocom/gsm/meas_rep.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
@@ -24,6 +24,8 @@
#include <osmocom/gsm/lapdm.h>
#endif
#include <openbsc/xsc.h>
struct osmo_bsc_data;
struct osmo_bsc_sccp_con;
@@ -100,7 +102,6 @@ struct gsm_abis_mo {
struct gsm_bts *bts;
};
#define MAX_A5_KEY_LEN (128/8)
#define A38_XOR_MIN_KEY_LEN 12
#define A38_XOR_MAX_KEY_LEN 16
#define A38_COMP128_KEY_LEN 16
@@ -202,11 +203,7 @@ struct gsm_lchan {
uint8_t bs_power;
uint8_t ms_power;
/* Encryption information */
struct {
uint8_t alg_id;
uint8_t key_len;
uint8_t key[MAX_A5_KEY_LEN];
} encr;
struct gsm_encr encr;
/* AMR bits */
uint8_t mr_ms_lv[7];
@@ -256,6 +253,14 @@ struct gsm_lchan {
struct gsm48_req_ref *rqd_ref;
struct gsm_subscriber_connection *conn;
struct {
/* channel activation type and handover ref */
uint8_t act_type;
uint8_t ho_ref;
struct gsm48_req_ref *rqd_ref;
uint8_t rqd_ta;
} dyn;
#else
/* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23.
* Currently we don't share these headers so this is a magic number. */
@@ -289,8 +294,16 @@ struct gsm_lchan {
struct {
uint8_t buf[16];
uint8_t len;
uint32_t fn;
bool is_update;
} last_sid;
/* set for each SID frame to detect talkspurt for codecs without
explicit ONSET event */
bool ul_sid;
uint8_t last_cmr;
uint32_t last_fn;
/* indicates if DTXd was active during DL measurement period */
bool dtxd_active;
} tch;
/* BTS-side ciphering state (rx only, bi-directional, ...) */
uint8_t ciph_state;
@@ -308,7 +321,8 @@ struct gsm_lchan {
int s;
/* Kind of the release/activation. E.g. RSL or PCU */
int rel_act_kind;
/* RTP header Marker bit to indicate beginning of speech after pause */
bool rtp_tx_marker;
/* power handling */
struct {
uint8_t current;
@@ -317,7 +331,14 @@ struct gsm_lchan {
#endif
};
#define TS_F_PDCH_MODE 0x1000
enum {
TS_F_PDCH_ACTIVE = 0x1000,
TS_F_PDCH_ACT_PENDING = 0x2000,
TS_F_PDCH_DEACT_PENDING = 0x4000,
TS_F_PDCH_PENDING_MASK = 0x6000 /*<
TS_F_PDCH_ACT_PENDING | TS_F_PDCH_DEACT_PENDING */
} gsm_bts_trx_ts_flags;
/* One Timeslot in a TRX */
struct gsm_bts_trx_ts {
struct gsm_bts_trx *trx;
@@ -326,6 +347,12 @@ struct gsm_bts_trx_ts {
enum gsm_phys_chan_config pchan;
struct {
enum gsm_phys_chan_config pchan_is;
enum gsm_phys_chan_config pchan_want;
struct msgb *pending_chan_activ;
} dyn;
unsigned int flags;
struct gsm_abis_mo mo;
struct tlv_parsed nm_attr;
@@ -594,6 +621,10 @@ struct gsm_bts {
/* number of this BTS on given E1 link */
uint8_t bts_nr;
/* DTX features of this BTS */
enum gsm48_dtx_mode dtxu;
bool dtxd;
/* paging state and control */
struct gsm_bts_paging_state paging;
@@ -609,14 +640,6 @@ struct gsm_bts {
/* buffers where we put the pre-computed SI */
sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE];
/* TimeZone hours, mins, and bts specific */
struct {
int hr;
int mn;
int override;
int dst;
} tz;
/* ip.accesss Unit ID's have Site/BTS/TRX layout */
union {
struct {
@@ -664,6 +687,7 @@ struct gsm_bts {
/* Not entirely sure how ip.access specific this is */
struct {
uint8_t supports_egprs_11bit_rach;
enum bts_gprs_mode mode;
struct {
struct gsm_abis_mo mo;
@@ -679,6 +703,7 @@ struct gsm_bts {
struct gsm_bts_gprs_nsvc nsvc[2];
uint8_t rac;
uint8_t net_ctrl_ord;
bool ctrl_ack_type_use_block;
} gprs;
/* RACH NM values */
@@ -759,15 +784,15 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num);
const struct value_string gsm_pchant_names[12];
const struct value_string gsm_pchant_descs[12];
const struct value_string gsm_lchant_names[8];
const struct value_string gsm_pchant_names[13];
const struct value_string gsm_pchant_descs[13];
const char *gsm_pchan_name(enum gsm_phys_chan_config c);
enum gsm_phys_chan_config gsm_pchan_parse(const char *name);
const char *gsm_lchant_name(enum gsm_chan_t c);
const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
char *gsm_trx_name(const struct gsm_bts_trx *trx);
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts);
char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts);
char *gsm_lchan_name_compute(const struct gsm_lchan *lchan);
const char *gsm_lchans_name(enum gsm_lchan_state s);
@@ -792,8 +817,11 @@ gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class,
/* reset the state of all MO in the BTS */
void gsm_bts_mo_reset(struct gsm_bts *bts);
uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr);
uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
uint8_t ts_nr, uint8_t lchan_nr);
uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan);
uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
enum gsm_phys_chan_config as_pchan);
/* return the gsm_lchan for the CBCH (if it exists at all) */
struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts);
@@ -812,5 +840,10 @@ static inline uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts)
return ts->trx->bts->bsic & 7;
}
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
int *rc);
uint8_t ts_subslots(struct gsm_bts_trx_ts *ts);
bool ts_is_tch(struct gsm_bts_trx_ts *ts);
#endif

View File

@@ -1,10 +1,13 @@
#ifndef _GSM_SUBSCR_H
#define _GSM_SUBSCR_H
#include "gsm_data.h"
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <openbsc/gsm_data.h>
#define GSM_NAME_LENGTH 160
#define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */
@@ -68,6 +71,7 @@ struct gsm_subscriber {
/* pending requests */
int is_paging;
struct osmo_timer_list paging_timeout;
struct llist_head requests;
/* GPRS/SGSN related fields */
@@ -87,6 +91,20 @@ enum gsm_subscriber_update_reason {
GSM_SUBSCRIBER_UPDATE_EQUIPMENT,
};
/*
* Struct for pending channel requests. This is managed in the
* llist_head requests of each subscriber. The reference counting
* should work in such a way that a subscriber with a pending request
* remains in memory.
*/
struct subscr_request {
struct llist_head entry;
/* the callback data */
gsm_cbfn *cbfn;
void *param;
};
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr);
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr);
struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp,
@@ -101,7 +119,7 @@ struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp,
unsigned long long id);
struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp,
const char *imsi);
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
int subscr_update(struct gsm_subscriber *s, uint16_t lac, int reason);
struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp,
uint32_t tmsi);
struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp,
@@ -112,14 +130,19 @@ char *subscr_name(struct gsm_subscriber *subscr);
int subscr_purge_inactive(struct gsm_subscriber_group *sgrp);
void subscr_update_from_db(struct gsm_subscriber *subscr);
void subscr_expire(struct gsm_subscriber_group *sgrp);
int subscr_update_expire_lu(struct gsm_subscriber *subscr, struct gsm_bts *bts);
int subscr_update_expire_lu(struct gsm_subscriber *subscr);
bool subscr_authorized_imsi(const struct gsm_network *net, const char *imsi);
bool subscr_authorized(struct gsm_subscriber *subsc);
/*
* Paging handling with authentication
*/
struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr,
int type, gsm_cbfn *cbfn, void *param);
struct subscr_request *subscr_request_conn(struct gsm_subscriber *subscr,
gsm_cbfn *cbfn, void *param);
void subscr_remove_request(struct subscr_request *req);
int subscr_rx_paging_response(struct msgb *msg,
struct gsm_subscriber_connection *conn);
/* internal */
struct gsm_subscriber *subscr_alloc(void);

View File

@@ -3,9 +3,6 @@
struct gsm_subscriber_connection;
/* Hand over the specified logical channel to the specified new BTS.
* This is the main entry point for the actual handover algorithm,
* after it has decided it wants to initiate HO to a specific BTS */
int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts);
/* clear any operation for this connection */

View File

@@ -0,0 +1,63 @@
#pragma once
#include <stdbool.h>
struct sgsn_pdp_ctx;
struct msgb;
struct gprs_ra_id;
struct RANAP_RAB_SetupOrModifiedItemIEs_s;
struct RANAP_GlobalRNC_ID;
struct ue_conn_ctx {
struct llist_head list;
struct osmo_sccp_link *link;
uint32_t conn_id;
int integrity_active;
struct gprs_ra_id ra_id;
};
enum iu_event_type {
IU_EVENT_RAB_ASSIGN,
IU_EVENT_SECURITY_MODE_COMPLETE,
IU_EVENT_IU_RELEASE, /* An actual Iu Release message was received */
IU_EVENT_LINK_INVALIDATED, /* A SUA link was lost or closed down */
/* FIXME: maybe IU_EVENT_IU_RELEASE and IU_EVENT_LINK_INVALIDATED
* should be combined to one generic event that simply means the
* ue_conn_ctx should no longer be used, for whatever reason. */
};
extern const struct value_string iu_event_type_names[];
static inline const char *iu_event_type_str(enum iu_event_type e)
{
return get_value_string(iu_event_type_names, e);
}
/* Implementations of iu_recv_cb_t shall find the ue_conn_ctx in msg->dst. */
typedef int (* iu_recv_cb_t )(struct msgb *msg, struct gprs_ra_id *ra_id,
/* TODO "gprs_" in generic CS+PS domain ^ */
uint16_t *sai);
typedef int (* iu_event_cb_t )(struct ue_conn_ctx *ue_ctx,
enum iu_event_type type, void *data);
typedef int (* iu_rab_ass_resp_cb_t )(struct ue_conn_ctx *ue_ctx, uint8_t rab_id,
struct RANAP_RAB_SetupOrModifiedItemIEs_s *setup_ies);
int iu_init(void *ctx, const char *listen_addr, uint16_t listen_port,
iu_recv_cb_t iu_recv_cb, iu_event_cb_t iu_event_cb);
void iu_link_del(struct osmo_sccp_link *link);
int iu_tx(struct msgb *msg, uint8_t sapi);
int iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac);
int iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac);
int iu_rab_act(struct ue_conn_ctx *ue_ctx, struct msgb *msg);
int iu_rab_deact(struct ue_conn_ctx *ue_ctx, uint8_t rab_id);
int iu_tx_sec_mode_cmd(struct ue_conn_ctx *uectx, struct gsm_auth_tuple *tp,
int send_ck, int new_key);
int iu_tx_common_id(struct ue_conn_ctx *ue_ctx, const char *imsi);
void iu_vty_init(int *asn_debug_p);

View File

@@ -0,0 +1,7 @@
#pragma once
int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg,
uint16_t *lac);
struct gsm_subscriber_connection *subscr_conn_lookup_iu(struct gsm_network *network,
struct ue_conn_ctx *ue);

View File

@@ -170,6 +170,21 @@ enum mgcp_role {
MGCP_BSC_NAT,
};
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 | MGCP_CONN_RECV_SEND,
};
extern const struct value_string mgcp_connection_mode_strs[];
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
{
return get_value_string(mgcp_connection_mode_strs, mode);
}
struct mgcp_config {
int source_port;
char *local_ip;

View File

@@ -28,14 +28,6 @@
#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 | MGCP_CONN_RECV_SEND,
};
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,

View File

@@ -0,0 +1,47 @@
#pragma once
#include <stdint.h>
enum mgcp_connection_mode;
struct msgb;
struct mgcpgw_client;
#define MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
#define MGCPGW_CLIENT_LOCAL_PORT_DEFAULT 0
#define MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
#define MGCPGW_CLIENT_REMOTE_PORT_DEFAULT 2427
typedef void (* mgcp_rx_cb_t )(struct msgb *msg, void *priv);
struct mgcpgw_client_conf {
const char *local_addr;
int local_port;
const char *remote_addr;
int remote_port;
};
void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf);
struct mgcpgw_client *mgcpgw_client_init(void *ctx,
struct mgcpgw_client_conf *conf,
mgcp_rx_cb_t rx_cb, void *rx_cb_priv);
const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp);
uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp);
uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp);
unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client);
int mgcpgw_client_tx_crcx(struct mgcpgw_client *client,
uint16_t rtp_endpoint, unsigned int call_id,
enum mgcp_connection_mode mode);
int mgcpgw_client_tx_mdcx(struct mgcpgw_client *client, uint16_t rtp_endpoint,
const char *rtp_conn_addr, uint16_t rtp_port,
enum mgcp_connection_mode mode);
int mgcpgw_client_tx_str(struct mgcpgw_client *mgcp, const char *fmt, ...);
int mgcpgw_client_tx_buf(struct mgcpgw_client *mgcp, const char *buf, int len);
int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg);
void mgcpgw_client_vty_init(int node, struct mgcpgw_client_conf *conf);
int mgcpgw_client_config_write(struct vty *vty, const char *indent);

View File

@@ -0,0 +1,51 @@
#pragma once
#include <osmocom/core/msgb.h>
#include <openbsc/gsm_data.h>
/* These are the interfaces of the MSC layer towards (from?) the BSC and RNC,
* i.e. in the direction towards the mobile device (MS aka UE).
*
* 2G will use the A-interface,
* 3G aka UMTS will use the Iu-interface (for the MSC, it's IuCS).
*
* To allow linking parts of the MSC code without having to include entire
* infrastructures of external libraries, the core transmitting and receiving
* functions are left unimplemented. For example, a unit test does not need to
* link against external ASN1 libraries if it is never going to encode actual
* outgoing messages. It is up to each building scope to implement real world
* functions or to plug mere dummy implementations.
*
* For example, msc_tx_dtap(conn, msg), depending on conn->via_iface, will call
* either iu_tx() or a_tx() [note: at time of writing, the A-interface is not
* yet implemented]. When you try to link against libmsc, you will find that
* the compiler complains about an undefined reference to iu_tx(). If you,
* however, link against libiu as well as the osmo-iuh libs (etc.), iu_tx() is
* available. A unit test may instead simply implement a dummy iu_tx() function
* and not link against osmo-iuh.
*/
/* Each main linkage must implement this function (see comment above). */
extern int iu_tx(struct msgb *msg, uint8_t sapi);
/* So far this is a dummy implemented in libmsc/a_iface.c. When A-interface
* gets implemented, it should be in a separate lib (like libiu), this function
* should move there, and the following comment should remain here: "
* Each main linkage must implement this function (see comment above).
* " */
extern int a_tx(struct msgb *msg);
int msc_tx_dtap(struct gsm_subscriber_connection *conn,
struct msgb *msg);
int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn);
int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
enum gsm48_reject_value value);
/* TODO: specific to A interface, move this away */
int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
const uint8_t *key, int len, int include_imeisv);
int msc_tx_common_id(struct gsm_subscriber_connection *conn);
int msc_call_assignment(struct gsm_trans *trans);
int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2);

View File

@@ -46,6 +46,11 @@ struct osmo_bsc_sccp_con {
struct bsc_filter_state filter_state;
};
struct gsm_network *bsc_network_init(void *ctx,
uint16_t country_code,
uint16_t network_code,
mncc_recv_cb_t mncc_recv);
struct bsc_api *osmo_bsc_api();
int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg);

View File

@@ -5,7 +5,15 @@
#include "bsc_api.h"
enum {
MSC_CONN_ACCEPT = 0,
MSC_CONN_REJECT = 1,
};
struct bsc_api *msc_bsc_api();
int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
uint16_t chosen_channel);
void msc_release_connection(struct gsm_subscriber_connection *conn);
#endif

View File

@@ -28,6 +28,7 @@ int osmux_used_cid(void);
enum osmux_state {
OSMUX_STATE_DISABLED = 0,
OSMUX_STATE_NEGOTIATING,
OSMUX_STATE_ACTIVATING,
OSMUX_STATE_ENABLED,
};

View File

@@ -12,6 +12,7 @@
int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net);
int rest_octets_si2quater(uint8_t *data, const struct osmo_earfcn_si2q *e,
const uint16_t *u, const uint16_t *sc, size_t u_len);
int rest_octets_si6(uint8_t *data, bool is1800_net);
struct gsm48_si_selection_params {
uint16_t penalty_time:5,
@@ -87,6 +88,8 @@ struct gprs_cell_options {
uint32_t t3192; /* in milliseconds */
uint32_t drx_timer_max;/* in seconds */
uint32_t bs_cv_max;
uint8_t supports_egprs_11bit_rach;
bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */
uint8_t ext_info_present;
struct {

View File

@@ -3,7 +3,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/oap.h>
@@ -20,6 +20,34 @@ enum sgsn_auth_policy {
SGSN_AUTH_POLICY_REMOTE
};
enum sgsn_rate_ctr_keys {
CTR_LLC_DL_BYTES,
CTR_LLC_UL_BYTES,
CTR_LLC_DL_PACKETS,
CTR_LLC_UL_PACKETS,
CTR_GPRS_ATTACH_REQUEST,
CTR_GPRS_ATTACH_ACKED,
CTR_GPRS_ATTACH_REJECTED,
CTR_GPRS_DETACH_REQUEST,
CTR_GPRS_DETACH_ACKED,
CTR_GPRS_ROUTING_AREA_REQUEST,
CTR_GPRS_ROUTING_AREA_ACKED,
CTR_GPRS_ROUTING_AREA_REJECT,
/* PDP single packet counter / GSM 04.08 9.5.1 - 9.5.9 */
CTR_PDP_ACTIVATE_REQUEST,
CTR_PDP_ACTIVATE_REJECT,
CTR_PDP_ACTIVATE_ACCEPT,
CTR_PDP_REQUEST_ACTIVATE, /* unused */
CTR_PDP_REQUEST_ACTIVATE_REJ, /* unused */
CTR_PDP_MODIFY_REQUEST, /* unsued */
CTR_PDP_MODIFY_ACCEPT, /* unused */
CTR_PDP_DL_DEACTIVATE_REQUEST,
CTR_PDP_DL_DEACTIVATE_ACCEPT,
CTR_PDP_UL_DEACTIVATE_REQUEST,
CTR_PDP_UL_DEACTIVATE_ACCEPT,
};
struct sgsn_cdr {
char *filename;
int interval;
@@ -35,6 +63,7 @@ struct sgsn_config {
struct gprs_ns_inst *nsi;
enum sgsn_auth_policy auth_policy;
enum gprs_ciph_algo cipher;
struct llist_head imsi_acl;
struct sockaddr_in gsup_server_addr;
@@ -64,6 +93,22 @@ struct sgsn_config {
int dynamic_lookup;
struct oap_config oap;
/* RFC1144 TCP/IP header compression */
struct {
int active;
int passive;
int s01;
} pcomp_rfc1144;
/* V.42vis data compression */
struct {
int active;
int passive;
int p0;
int p1;
int p2;
} dcomp_v42bis;
};
struct sgsn_instance {
@@ -87,6 +132,8 @@ struct sgsn_instance {
struct llist_head ares_fds;
ares_channel ares_channel;
struct ares_addr_node *ares_servers;
struct rate_ctr_group *rate_ctrs;
};
extern struct sgsn_instance *sgsn;
@@ -107,6 +154,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
uint16_t nsapi,
struct tlv_parsed *tp);
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx);
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen);
/* gprs_sndcp.c */

View File

@@ -0,0 +1,187 @@
#ifndef _SLHC_H
#define _SLHC_H
/*
* Definitions for tcp compression routines.
*
* $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
* - Initial distribution.
*
*
* modified for KA9Q Internet Software Package by
* Katie Stevens (dkstevens@ucdavis.edu)
* University of California, Davis
* Computing Services
* - 01-31-90 initial adaptation
*
* - Feb 1991 Bill_Simpson@um.cc.umich.edu
* variable number of conversation slots
* allow zero or one slots
* separate routines
* status display
*/
/*
* Compressed packet format:
*
* The first octet contains the packet type (top 3 bits), TCP
* 'push' bit, and flags that indicate which of the 4 TCP sequence
* numbers have changed (bottom 5 bits). The next octet is a
* conversation number that associates a saved IP/TCP header with
* the compressed packet. The next two octets are the TCP checksum
* from the original datagram. The next 0 to 15 octets are
* sequence number changes, one change per bit set in the header
* (there may be no changes and there are two special cases where
* the receiver implicitly knows what changed -- see below).
*
* There are 5 numbers which can change (they are always inserted
* in the following order): TCP urgent pointer, window,
* acknowledgment, sequence number and IP ID. (The urgent pointer
* is different from the others in that its value is sent, not the
* change in value.) Since typical use of SLIP links is biased
* toward small packets (see comments on MTU/MSS below), changes
* use a variable length coding with one octet for numbers in the
* range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
* range 256 - 65535 or 0. (If the change in sequence number or
* ack is more than 65535, an uncompressed packet is sent.)
*/
/*
* Packet types (must not conflict with IP protocol version)
*
* The top nibble of the first octet is the packet type. There are
* three possible types: IP (not proto TCP or tcp with one of the
* control flags set); uncompressed TCP (a normal IP/TCP packet but
* with the 8-bit protocol field replaced by an 8-bit connection id --
* this type of packet syncs the sender & receiver); and compressed
* TCP (described above).
*
* LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
* is logically part of the 4-bit "changes" field that follows. Top
* three bits are actual packet type. For backward compatibility
* and in the interest of conserving bits, numbers are chosen so the
* IP protocol version number (4) which normally appears in this nibble
* means "IP packet".
*/
#include <linux/ip.h>
#include <linux/tcp.h>
/* SLIP compression masks for len/vers byte */
#define SL_TYPE_IP 0x40
#define SL_TYPE_UNCOMPRESSED_TCP 0x70
#define SL_TYPE_COMPRESSED_TCP 0x80
#define SL_TYPE_ERROR 0x00
/* Bits in first octet of compressed packet */
#define NEW_C 0x40 /* flag bits for what changed in a packet */
#define NEW_I 0x20
#define NEW_S 0x08
#define NEW_A 0x04
#define NEW_W 0x02
#define NEW_U 0x01
/* reserved, special-case values of above */
#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
#define TCP_PUSH_BIT 0x10
/*
* data type and sizes conversion assumptions:
*
* VJ code KA9Q style generic
* u_char byte_t unsigned char 8 bits
* u_short int16 unsigned short 16 bits
* u_int int16 unsigned short 16 bits
* u_long unsigned long unsigned long 32 bits
* int int32 long 32 bits
*/
typedef __u8 byte_t;
typedef __u32 int32;
/*
* "state" data for each active tcp conversation on the wire. This is
* basically a copy of the entire IP/TCP header from the last packet
* we saw from the conversation together with a small identifier
* the transmit & receive ends of the line use to locate saved header.
*/
struct cstate {
byte_t cs_this; /* connection id number (xmit) */
struct cstate *next; /* next in ring (xmit) */
struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
struct tcphdr cs_tcp;
unsigned char cs_ipopt[64];
unsigned char cs_tcpopt[64];
int cs_hsize;
};
#define NULLSLSTATE (struct cstate *)0
/*
* all the state data for one serial line (we need one of these per line).
*/
struct slcompress {
struct cstate *tstate; /* transmit connection states (array)*/
struct cstate *rstate; /* receive connection states (array)*/
byte_t tslot_limit; /* highest transmit slot id (0-l)*/
byte_t rslot_limit; /* highest receive slot id (0-l)*/
byte_t xmit_oldest; /* oldest xmit in ring */
byte_t xmit_current; /* most recent xmit id */
byte_t recv_current; /* most recent rcvd id */
byte_t flags;
#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */
int32 sls_o_nontcp; /* outbound non-TCP packets */
int32 sls_o_tcp; /* outbound TCP packets */
int32 sls_o_uncompressed; /* outbound uncompressed packets */
int32 sls_o_compressed; /* outbound compressed packets */
int32 sls_o_searches; /* searches for connection state */
int32 sls_o_misses; /* times couldn't find conn. state */
int32 sls_i_uncompressed; /* inbound uncompressed packets */
int32 sls_i_compressed; /* inbound compressed packets */
int32 sls_i_error; /* inbound error packets */
int32 sls_i_tossed; /* inbound packets tossed because of error */
int32 sls_i_runt;
int32 sls_i_badcheck;
};
#define NULLSLCOMPR (struct slcompress *)0
/* In slhc.c: */
struct slcompress *slhc_init(const void *ctx, int rslots, int tslots);
void slhc_free(struct slcompress *comp);
int slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
unsigned char *ocp, unsigned char **cpp, int compress_cid);
int slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize);
int slhc_remember(struct slcompress *comp, unsigned char *icp, int isize);
int slhc_toss(struct slcompress *comp);
void slhc_i_status(struct slcompress *comp);
void slhc_o_status(struct slcompress *comp);
#endif /* _SLHC_H */

View File

@@ -14,7 +14,7 @@ struct gsm_trans {
/* Entry in list of all transactions */
struct llist_head entry;
/* Back pointer to the netweork struct */
/* Back pointer to the network struct */
struct gsm_network *net;
/* The protocol within which we live */
@@ -22,7 +22,7 @@ struct gsm_trans {
/* The current transaction ID */
uint8_t transaction_id;
/* To whom we belong, unique identifier of remote MM entity */
struct gsm_subscriber *subscr;

View File

@@ -0,0 +1,147 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* v42bis.h
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \page v42bis_page V.42bis modem data compression
\section v42bis_page_sec_1 What does it do?
The v.42bis specification defines a data compression scheme, to work in
conjunction with the error correction scheme defined in V.42.
\section v42bis_page_sec_2 How does it work?
*/
#include <stdint.h>
#if !defined(_SPANDSP_V42BIS_H_)
#define _SPANDSP_V42BIS_H_
#define SPAN_DECLARE(x) x
#define V42BIS_MIN_STRING_SIZE 6
#define V42BIS_MAX_STRING_SIZE 250
#define V42BIS_MIN_DICTIONARY_SIZE 512
#define V42BIS_MAX_BITS 12
#define V42BIS_MAX_CODEWORDS 4096 /* 2^V42BIS_MAX_BITS */
#define V42BIS_MAX_OUTPUT_LENGTH 1024
enum
{
V42BIS_P0_NEITHER_DIRECTION = 0,
V42BIS_P0_INITIATOR_RESPONDER,
V42BIS_P0_RESPONDER_INITIATOR,
V42BIS_P0_BOTH_DIRECTIONS
};
enum
{
V42BIS_COMPRESSION_MODE_DYNAMIC = 0,
V42BIS_COMPRESSION_MODE_ALWAYS,
V42BIS_COMPRESSION_MODE_NEVER
};
typedef void (*put_msg_func_t)(void *user_data, const uint8_t *msg, int len);
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
single instance of V.42bis compress/decompression.
*/
typedef struct v42bis_state_s v42bis_state_t;
#if defined(__cplusplus)
extern "C"
{
#endif
/*! Compress a block of octets.
\param s The V.42bis context.
\param buf The data to be compressed.
\param len The length of the data buffer.
\return 0 */
SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in a compression buffer.
\param s The V.42bis context.
\return 0 */
SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s);
/*! Decompress a block of octets.
\param s The V.42bis context.
\param buf The data to be decompressed.
\param len The length of the data buffer.
\return 0 */
SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in the decompression buffer.
\param s The V.42bis context.
\return 0 */
SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s);
/*! Set the compression mode.
\param s The V.42bis context.
\param mode One of the V.42bis compression modes -
V42BIS_COMPRESSION_MODE_DYNAMIC,
V42BIS_COMPRESSION_MODE_ALWAYS,
V42BIS_COMPRESSION_MODE_NEVER */
SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode);
/*! Initialise a V.42bis context.
\param s The V.42bis context.
\param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec.
\param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec.
\param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec.
\param encode_handler Encode callback handler.
\param encode_user_data An opaque pointer passed to the encode callback handler.
\param max_encode_len The maximum length that should be passed to the encode handler.
\param decode_handler Decode callback handler.
\param decode_user_data An opaque pointer passed to the decode callback handler.
\param max_decode_len The maximum length that should be passed to the decode handler.
\return The V.42bis context. */
SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
v42bis_state_t *s,
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
put_msg_func_t encode_handler,
void *encode_user_data,
int max_encode_len,
put_msg_func_t decode_handler,
void *decode_user_data,
int max_decode_len);
/*! Release a V.42bis context.
\param s The V.42bis context.
\return 0 if OK */
SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s);
/*! Free a V.42bis context.
\param s The V.42bis context.
\return 0 if OK */
SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s);
#if defined(__cplusplus)
}
#endif
#endif
/*- End of file ------------------------------------------------------------*/

View File

@@ -0,0 +1,126 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* private/v42bis.h
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(_SPANDSP_PRIVATE_V42BIS_H_)
#define _SPANDSP_PRIVATE_V42BIS_H_
/*!
V.42bis dictionary node.
Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used
as a "no such value" marker in this structure.
*/
typedef struct
{
/*! \brief The value of the octet represented by the current dictionary node */
uint8_t node_octet;
/*! \brief The parent of this node */
uint16_t parent;
/*! \brief The first child of this node */
uint16_t child;
/*! \brief The next node at the same depth */
uint16_t next;
} v42bis_dict_node_t;
/*!
V.42bis compression or decompression. This defines the working state for a single instance
of V.42bis compression or decompression.
*/
typedef struct
{
/*! \brief Compression enabled. */
int v42bis_parm_p0;
/*! \brief Compression mode. */
int compression_mode;
/*! \brief Callback function to handle output data. */
put_msg_func_t handler;
/*! \brief An opaque pointer passed in calls to the data handler. */
void *user_data;
/*! \brief The maximum amount to be passed to the data handler. */
int max_output_len;
/*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */
int transparent;
/*! \brief Next empty dictionary entry */
uint16_t v42bis_parm_c1;
/*! \brief Current codeword size */
uint16_t v42bis_parm_c2;
/*! \brief Threshold for codeword size change */
uint16_t v42bis_parm_c3;
/*! \brief The current update point in the dictionary */
uint16_t update_at;
/*! \brief The last entry matched in the dictionary */
uint16_t last_matched;
/*! \brief The last entry added to the dictionary */
uint16_t last_added;
/*! \brief Total number of codewords in the dictionary */
int v42bis_parm_n2;
/*! \brief Maximum permitted string length */
int v42bis_parm_n7;
/*! \brief The dictionary */
v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
/*! \brief The octet string in progress */
uint8_t string[V42BIS_MAX_STRING_SIZE];
/*! \brief The current length of the octet string in progress */
int string_length;
/*! \brief The amount of the octet string in progress which has already
been flushed out of the buffer */
int flushed_length;
/*! \brief Compression performance metric */
uint16_t compression_performance;
/*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */
uint32_t bit_buffer;
/*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */
int bit_count;
/*! \brief The output composition buffer */
uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH];
/*! \brief The length of the contents of the output composition buffer */
int output_octet_count;
/*! \brief The current value of the escape code */
uint8_t escape_code;
/*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */
int escaped;
} v42bis_comp_state_t;
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
single instance of V.42bis compress/decompression.
*/
struct v42bis_state_s
{
/*! \brief Compression state. */
v42bis_comp_state_t compress;
/*! \brief Decompression state. */
v42bis_comp_state_t decompress;
/*! \brief Error and flow logging control */
};
#endif
/*- End of file ------------------------------------------------------------*/

View File

@@ -37,13 +37,18 @@ enum bsc_vty_node {
SMPP_NODE,
SMPP_ESME_NODE,
GTPHUB_NODE,
CSCN_NODE,
};
extern int bsc_vty_is_config_node(struct vty *vty, int node);
extern void bsc_replace_string(void *ctx, char **dst, const char *newstr);
struct log_info;
int bsc_vty_init(const struct log_info *cat);
int bsc_vty_init(const struct log_info *cat, struct gsm_network *network);
int bsc_vty_init_extra(void);
void cscn_vty_init(struct gsm_network *cscn_network);
struct gsm_network *gsmnet_from_vty(struct vty *vty);
#endif

View File

@@ -0,0 +1,27 @@
#pragma once
#include <stdint.h>
struct msgb;
struct gsm_network;
typedef int (*mncc_recv_cb_t)(struct gsm_network *, struct msgb *);
struct vty;
#define MAX_A5_KEY_LEN (128/8)
struct gsm_encr {
uint8_t alg_id;
uint8_t key_len;
uint8_t key[MAX_A5_KEY_LEN];
};
struct gsm_network *gsm_network_init(void *ctx,
uint16_t country_code,
uint16_t network_code,
mncc_recv_cb_t mncc_recv);
int xsc_vty_init(struct gsm_network *network,
int (* config_write_net )(struct vty *));
struct gsm_network *gsmnet_from_vty(struct vty *v);

View File

@@ -1,13 +1,60 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS)
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libfilter osmo-nitb osmo-bsc_mgcp utils ipaccess gprs
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
# Conditional modules
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# Libraries
SUBDIRS = \
libcommon \
libmgcp \
libbsc \
libmsc \
libtrau \
libfilter \
libxsc \
$(NULL)
# Conditional Libraries
if BUILD_IU
SUBDIRS += \
libiu \
$(NULL)
endif
# Programs
SUBDIRS += \
osmo-cscn \
osmo-bsc_mgcp \
utils \
ipaccess \
gprs \
$(NULL)
# Conditional Programs
if BUILD_NAT
SUBDIRS += osmo-bsc_nat
SUBDIRS += \
osmo-bsc_nat \
$(NULL)
endif
if BUILD_BSC
SUBDIRS += osmo-bsc
SUBDIRS += \
osmo-bsc \
$(NULL)
endif

View File

@@ -1,42 +1,135 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) $(LIBOSMOGB_CFLAGS) $(COVERAGE_CFLAGS) \
$(LIBCARES_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBGTP_CFLAGS)
OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) $(LIBOSMOGB_LIBS)
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
noinst_HEADERS = gprs_sndcp.h
AM_CFLAGS = \
-Wall \
-fno-strict-aliasing \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOGB_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(LIBCARES_CFLAGS) \
$(LIBCRYPTO_CFLAGS) \
$(LIBGTP_CFLAGS) \
$(NULL)
if BUILD_IU
AM_CFLAGS += \
$(LIBASN1C_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBOSMORANAP_CFLAGS) \
$(NULL)
endif
bin_PROGRAMS = osmo-gbproxy
OSMO_LIBS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGB_LIBS) \
$(LIBGTP_LIBS) \
$(NULL)
bin_PROGRAMS = \
osmo-gbproxy \
$(NULL)
if HAVE_LIBGTP
if HAVE_LIBCARES
bin_PROGRAMS += osmo-sgsn osmo-gtphub
bin_PROGRAMS += \
osmo-sgsn \
osmo-gtphub \
$(NULL)
endif
endif
osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c \
gb_proxy_patch.c gb_proxy_tlli.c gb_proxy_peer.c \
gprs_gb_parse.c gprs_llc_parse.c crc24.c gprs_utils.c
osmo_gbproxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) $(LIBCRYPTO_LIBS) -lrt
osmo_gbproxy_SOURCES = \
gb_proxy.c \
gb_proxy_main.c \
gb_proxy_vty.c \
gb_proxy_patch.c \
gb_proxy_tlli.c \
gb_proxy_peer.c \
gprs_gb_parse.c \
gprs_llc_parse.c \
crc24.c \
gprs_utils.c \
$(NULL)
osmo_gbproxy_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(LIBCRYPTO_LIBS) \
-lrt \
$(NULL)
osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \
sgsn_main.c sgsn_vty.c sgsn_libgtp.c \
gprs_llc.c gprs_llc_parse.c gprs_llc_vty.c crc24.c \
sgsn_ctrl.c sgsn_auth.c gprs_subscriber.c \
gprs_utils.c gprs_gsup_client.c \
sgsn_cdr.c sgsn_ares.c \
oap.c oap_messages.c
osmo_sgsn_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
-lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \
$(LIBCRYPTO_LIBS) -lrt
osmo_sgsn_SOURCES = \
gprs_gmm.c \
gprs_sgsn.c \
gprs_sndcp.c \
gprs_sndcp_comp.c \
gprs_sndcp_dcomp.c \
gprs_sndcp_pcomp.c \
gprs_sndcp_vty.c \
gprs_sndcp_xid.c \
sgsn_main.c \
sgsn_vty.c \
sgsn_libgtp.c \
gprs_llc.c \
gprs_llc_parse.c \
gprs_llc_vty.c \
crc24.c \
sgsn_ctrl.c \
sgsn_auth.c \
gprs_subscriber.c \
gprs_utils.c \
gprs_gsup_client.c \
sgsn_cdr.c \
sgsn_ares.c \
slhc.c \
oap.c \
oap_messages.c \
gprs_llc_xid.c \
v42bis.c \
$(NULL)
osmo_sgsn_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBCARES_LIBS) \
$(LIBCRYPTO_LIBS) \
-lrt \
-lgtp \
-lm \
$(NULL)
if BUILD_IU
osmo_sgsn_LDADD += \
$(top_builddir)/src/libiu/libiu.a \
$(LIBOSMOSIGTRAN_LIBS) \
$(LIBOSMORANAP_LIBS) \
$(LIBASN1C_LIBS) \
$(NULL)
endif
osmo_gtphub_SOURCES = gtphub_main.c gtphub.c gtphub_sock.c gtphub_ares.c \
gtphub_vty.c sgsn_ares.c gprs_utils.c
osmo_gtphub_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
-lgtp $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
$(LIBCARES_LIBS) -lrt
osmo_gtphub_SOURCES = \
gtphub_main.c \
gtphub.c \
gtphub_sock.c \
gtphub_ares.c \
gtphub_vty.c \
sgsn_ares.c \
gprs_utils.c \
$(NULL)
osmo_gtphub_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBCARES_LIBS) \
$(LIBGTP_LIBS) \
-lrt \
$(NULL)

View File

@@ -271,8 +271,6 @@ int main(int argc, char **argv)
}
/* start telnet after reading config for vty_get_bind_addr() */
LOGP(DGPRS, LOGL_NOTICE, "VTY at %s %d\n",
vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
if (rc < 0)

File diff suppressed because it is too large Load Diff

View File

@@ -21,11 +21,15 @@
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>
#include <openssl/rand.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/gsm_data.h>
@@ -35,8 +39,312 @@
#include <openbsc/gprs_llc.h>
#include <openbsc/crc24.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc_xid.h>
#include <openbsc/gprs_sndcp_comp.h>
#include <openbsc/gprs_sndcp.h>
static struct gprs_llc_llme *llme_alloc(uint32_t tlli);
static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg,
int command);
static int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi,
int command, enum gprs_llc_u_cmd u_cmd, int pf_bit);
/* BEGIN XID RELATED */
/* Generate XID message */
static int gprs_llc_generate_xid(uint8_t *bytes, int bytes_len,
struct gprs_llc_xid_field *l3_xid_field,
struct gprs_llc_llme *llme)
{
/* Note: Called by gprs_ll_xid_req() */
LLIST_HEAD(xid_fields);
struct gprs_llc_xid_field xid_version;
struct gprs_llc_xid_field xid_n201u;
struct gprs_llc_xid_field xid_n201i;
xid_version.type = GPRS_LLC_XID_T_VERSION;
xid_version.data = (uint8_t *) "\x00";
xid_version.data_len = 1;
xid_n201u.type = GPRS_LLC_XID_T_N201_U;
xid_n201u.data = (uint8_t *) "\x05\xf0";
xid_n201u.data_len = 2;
xid_n201i.type = GPRS_LLC_XID_T_N201_I;
xid_n201i.data = (uint8_t *) "\x05\xf0";
xid_n201i.data_len = 2;
/* Add locally managed XID Fields */
llist_add(&xid_n201i.list, &xid_fields);
llist_add(&xid_n201u.list, &xid_fields);
llist_add(&xid_version.list, &xid_fields);
/* Append layer 3 XID field (if present) */
if (l3_xid_field) {
/* Enforce layer 3 XID type (just to be sure) */
l3_xid_field->type = GPRS_LLC_XID_T_L3_PAR;
/* Add Layer 3 XID field to the list */
llist_add(&l3_xid_field->list, &xid_fields);
}
/* Store generated XID for later reference */
talloc_free(llme->xid);
llme->xid = gprs_llc_copy_xid(llme, &xid_fields);
return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields);
}
/* Generate XID message that will cause the GMM to reset */
static int gprs_llc_generate_xid_for_gmm_reset(uint8_t *bytes,
int bytes_len, uint32_t iov_ui,
struct gprs_llc_llme *llme)
{
/* Called by gprs_llgmm_reset() and
* gprs_llgmm_reset_oldmsg() */
LLIST_HEAD(xid_fields);
struct gprs_llc_xid_field xid_reset;
struct gprs_llc_xid_field xid_iovui;
/* First XID component must be RESET */
xid_reset.type = GPRS_LLC_XID_T_RESET;
xid_reset.data = NULL;
xid_reset.data_len = 0;
/* Add new IOV-UI */
xid_iovui.type = GPRS_LLC_XID_T_IOV_UI;
xid_iovui.data = (uint8_t *) & iov_ui;
xid_iovui.data_len = 4;
/* Add locally managed XID Fields */
llist_add(&xid_iovui.list, &xid_fields);
llist_add(&xid_reset.list, &xid_fields);
/* Store generated XID for later reference */
talloc_free(llme->xid);
llme->xid = gprs_llc_copy_xid(llme, &xid_fields);
return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields);
}
/* Process an incoming XID confirmation */
static int gprs_llc_process_xid_conf(uint8_t *bytes, int bytes_len,
struct gprs_llc_lle *lle)
{
/* Note: This function handles the response of a network originated
* XID-Request. There XID messages reflected by the phone are analyzed
* and processed here. The caller is called by rx_llc_xid(). */
struct llist_head *xid_fields;
struct gprs_llc_xid_field *xid_field;
struct gprs_llc_xid_field *xid_field_request;
struct gprs_llc_xid_field *xid_field_request_l3 = NULL;
/* Pick layer3 XID from the XID request we have sent last */
if (lle->llme->xid) {
llist_for_each_entry(xid_field_request, lle->llme->xid, list) {
if (xid_field_request->type == GPRS_LLC_XID_T_L3_PAR)
xid_field_request_l3 = xid_field_request;
}
}
/* Parse and analyze XID-Response */
xid_fields = gprs_llc_parse_xid(NULL, bytes, bytes_len);
if (xid_fields) {
gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG);
llist_for_each_entry(xid_field, xid_fields, list) {
/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
if (xid_field->type == GPRS_LLC_XID_T_L3_PAR &&
xid_field_request_l3) {
sndcp_sn_xid_conf(xid_field,
xid_field_request_l3, lle);
}
/* Process LLC-XID fields: */
else {
/* FIXME: Do something more useful with the
* echoed XID-Information. Currently we
* just ignore the response completely and
* by doing so we blindly accept any changes
* the MS might have done to the our XID
* inquiry. There is a remainig risk of
* malfunction! */
LOGP(DLLC, LOGL_NOTICE,
"Ignoring XID-Field: XID: type=%d, data_len=%d, data=%s\n",
xid_field->type, xid_field->data_len,
osmo_hexdump_nospc(xid_field->data,
xid_field->data_len));
}
}
talloc_free(xid_fields);
}
/* Flush pending XID fields */
talloc_free(lle->llme->xid);
lle->llme->xid = NULL;
return 0;
}
/* Process an incoming XID indication and generate an appropiate response */
static int gprs_llc_process_xid_ind(uint8_t *bytes_request,
int bytes_request_len,
uint8_t *bytes_response,
int bytes_response_maxlen,
struct gprs_llc_lle *lle)
{
/* Note: This function computes the response that is sent back to the
* phone when a phone originated XID is received. The function is
* called by rx_llc_xid() */
int rc = -EINVAL;
struct llist_head *xid_fields;
struct llist_head *xid_fields_response;
struct gprs_llc_xid_field *xid_field;
struct gprs_llc_xid_field *xid_field_response;
/* Parse and analyze XID-Request */
xid_fields =
gprs_llc_parse_xid(lle->llme, bytes_request, bytes_request_len);
if (xid_fields) {
xid_fields_response = talloc_zero(lle->llme, struct llist_head);
INIT_LLIST_HEAD(xid_fields_response);
gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG);
/* Process LLC-XID fields: */
llist_for_each_entry(xid_field, xid_fields, list) {
if (xid_field->type != GPRS_LLC_XID_T_L3_PAR) {
/* FIXME: Check the incoming XID parameters for
* for validity. Currently we just blindly
* accept all XID fields by just echoing them.
* There is a remaining risk of malfunction
* when a phone submits values which defer from
* the default! */
LOGP(DLLC, LOGL_NOTICE,
"Echoing XID-Field: XID: type=%d, data_len=%d, data=%s\n",
xid_field->type, xid_field->data_len,
osmo_hexdump_nospc(xid_field->data,
xid_field->data_len));
xid_field_response =
gprs_llc_dup_xid_field
(lle->llme, xid_field);
llist_add(&xid_field_response->list,
xid_fields_response);
}
}
/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
llist_for_each_entry(xid_field, xid_fields, list) {
if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) {
xid_field_response =
talloc_zero(lle->llme,
struct gprs_llc_xid_field);
rc = sndcp_sn_xid_ind(xid_field,
xid_field_response, lle);
if (rc == 0)
llist_add(&xid_field_response->list,
xid_fields_response);
else
talloc_free(xid_field_response);
}
}
rc = gprs_llc_compile_xid(bytes_response,
bytes_response_maxlen,
xid_fields_response);
talloc_free(xid_fields_response);
talloc_free(xid_fields);
}
return rc;
}
/* Dispatch XID indications and responses comming from the Phone */
static void rx_llc_xid(struct gprs_llc_lle *lle,
struct gprs_llc_hdr_parsed *gph)
{
uint8_t response[1024];
int response_len;
/* FIXME: 8.5.3.3: check if XID is invalid */
if (gph->is_cmd) {
LOGP(DLLC, LOGL_NOTICE,
"Received XID indication from phone.\n");
struct msgb *resp;
uint8_t *xid;
resp = msgb_alloc_headroom(4096, 1024, "LLC_XID");
response_len =
gprs_llc_process_xid_ind(gph->data, gph->data_len,
response, sizeof(response),
lle);
if (response_len < 0) {
LOGP(DLLC, LOGL_ERROR,
"invalid XID indication received!\n");
} else {
xid = msgb_put(resp, response_len);
memcpy(xid, response, response_len);
}
gprs_llc_tx_xid(lle, resp, 0);
} else {
LOGP(DLLC, LOGL_NOTICE,
"Received XID confirmation from phone.\n");
gprs_llc_process_xid_conf(gph->data, gph->data_len, lle);
/* FIXME: if we had sent a XID reset, send
* LLGMM-RESET.conf to GMM */
}
}
/* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */
int gprs_ll_xid_req(struct gprs_llc_lle *lle,
struct gprs_llc_xid_field *l3_xid_field)
{
/* Note: This functions is calle from gprs_sndcp.c */
uint8_t xid_bytes[1024];;
int xid_bytes_len;
uint8_t *xid;
struct msgb *msg;
/* Generate XID */
xid_bytes_len =
gprs_llc_generate_xid(xid_bytes, sizeof(xid_bytes),
l3_xid_field, lle->llme);
/* Only perform XID sending if the XID message contains something */
if (xid_bytes_len > 0) {
/* Transmit XID bytes */
msg = msgb_alloc_headroom(4096, 1024, "LLC_XID");
xid = msgb_put(msg, xid_bytes_len);
memcpy(xid, xid_bytes, xid_bytes_len);
LOGP(DLLC, LOGL_NOTICE, "Sending XID request to phone...\n");
gprs_llc_tx_xid(lle, msg, 1);
} else {
LOGP(DLLC, LOGL_ERROR,
"XID-Message generation failed, XID not sent!\n");
return -EINVAL;
}
return 0;
}
/* END XID RELATED */
/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
* to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
@@ -48,7 +356,7 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
memset(&dup, 0, sizeof(dup));
/* before we have received some identity from the MS, we might
* not yet have a MMC context (e.g. XID negotiation of primarly
* LLC connection fro GMM sapi). */
* LLC connection from GMM sapi). */
if (mmctx) {
dup.imsi = mmctx->imsi;
dup.drx_parms = mmctx->drx_parms;
@@ -56,8 +364,8 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf;
/* make sure we only send it to the right llme */
OSMO_ASSERT(msgb_tlli(msg) == mmctx->llme->tlli
|| msgb_tlli(msg) == mmctx->llme->old_tlli);
OSMO_ASSERT(msgb_tlli(msg) == mmctx->gb.llme->tlli
|| msgb_tlli(msg) == mmctx->gb.llme->old_tlli);
}
memcpy(&dup.qos_profile, qos_profile_default,
sizeof(qos_profile_default));
@@ -236,17 +544,24 @@ static struct gprs_llc_llme *llme_alloc(uint32_t tlli)
llme->old_tlli = 0xffffffff;
llme->state = GPRS_LLMS_UNASSIGNED;
llme->age_timestamp = GPRS_LLME_RESET_AGE;
llme->cksn = GSM_KEY_SEQ_INVAL;
for (i = 0; i < ARRAY_SIZE(llme->lle); i++)
lle_init(llme, i);
llist_add(&llme->list, &gprs_llc_llmes);
llme->comp.proto = gprs_sndcp_comp_alloc(llme);
llme->comp.data = gprs_sndcp_comp_alloc(llme);
return llme;
}
static void llme_free(struct gprs_llc_llme *llme)
{
gprs_sndcp_comp_free(llme->comp.proto);
gprs_sndcp_comp_free(llme->comp.data);
talloc_free(llme->xid);
llist_del(&llme->list);
talloc_free(llme);
}
@@ -327,6 +642,9 @@ int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command,
/* Identifiers passed down: (BVCI, NSEI) */
rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]);
rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len);
/* Send BSSGP-DL-UNITDATA.req */
return _bssgp_tx_dl_ud(msg, NULL);
}
@@ -343,9 +661,52 @@ static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg,
return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1);
}
/* Transmit a UI frame over the given SAPI */
/* encrypt information field + FCS, if needed! */
static int apply_gea(struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu,
uint32_t oc, uint8_t sapi, uint8_t *fcs, uint8_t *data)
{
uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
if (lle->llme->algo == GPRS_ALGO_GEA0)
return -EINVAL;
/* Compute the 'Input' Paraemeter */
uint32_t fcs_calc, iv = gprs_cipher_gen_input_ui(lle->llme->iov_ui, sapi,
nu, oc);
/* Compute gamma that we need to XOR with the data */
int r = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
lle->llme->kc, iv,
fcs ? GPRS_CIPH_SGSN2MS : GPRS_CIPH_MS2SGSN);
if (r < 0) {
LOGP(DLLC, LOGL_ERROR, "Error producing %s gamma for UI "
"frame: %d\n", get_value_string(gprs_cipher_names,
lle->llme->algo), r);
return -ENOMSG;
}
if (fcs) {
/* Mark frame as encrypted and update FCS */
data[2] |= 0x02;
fcs_calc = gprs_llc_fcs(data, fcs - data);
fcs[0] = fcs_calc & 0xff;
fcs[1] = (fcs_calc >> 8) & 0xff;
fcs[2] = (fcs_calc >> 16) & 0xff;
data += 3;
}
/* XOR the cipher output with the data */
for (r = 0; r < crypt_len; r++)
*(data + r) ^= cipher_out[r];
return 0;
}
/* Transmit a UI frame over the given SAPI:
'encryptable' indicates whether particular message can be encrypted according
to 3GPP TS 24.008 § 4.7.1.2
*/
int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
void *mmctx)
struct sgsn_mm_ctx *mmctx, bool encryptable)
{
struct gprs_llc_lle *lle;
uint8_t *fcs, *llch;
@@ -366,6 +727,8 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
return -EFBIG;
}
gprs_llme_copy_key(mmctx, lle->llme);
/* Update LLE's (BVCI, NSEI) tuple */
lle->llme->bvci = msgb_bvci(msg);
lle->llme->nsei = msgb_nsei(msg);
@@ -403,33 +766,12 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
fcs[1] = (fcs_calc >> 8) & 0xff;
fcs[2] = (fcs_calc >> 16) & 0xff;
/* encrypt information field + FCS, if needed! */
if (lle->llme->algo != GPRS_ALGO_GEA0) {
uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */
uint16_t crypt_len = (fcs + 3) - (llch + 3);
uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
uint32_t iv;
int rc, i;
uint64_t kc = *(uint64_t *)&lle->llme->kc;
/* Compute the 'Input' Paraemeter */
iv = gprs_cipher_gen_input_ui(iov_ui, sapi, nu, oc);
/* Compute the keystream that we need to XOR with the data */
rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
kc, iv, GPRS_CIPH_SGSN2MS);
if (lle->llme->algo != GPRS_ALGO_GEA0 && encryptable) {
int rc = apply_gea(lle, fcs - llch, nu, oc, sapi, fcs, llch);
if (rc < 0) {
LOGP(DLLC, LOGL_ERROR, "Error crypting UI frame: %d\n", rc);
msgb_free(msg);
return rc;
}
/* XOR the cipher output with the information field + FCS */
for (i = 0; i < crypt_len; i++)
*(llch + 3 + i) ^= cipher_out[i];
/* Mark frame as encrypted */
ctrl[1] |= 0x02;
}
/* Identifiers passed down: (BVCI, NSEI) */
@@ -438,54 +780,6 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
return _bssgp_tx_dl_ud(msg, mmctx);
}
/* According to 6.4.1.6 / Figure 11 */
static int msgb_put_xid_par(struct msgb *msg, uint8_t type, uint8_t length, uint8_t *data)
{
uint8_t header_len = 1;
uint8_t *cur;
/* type is a 5-bit field... */
if (type > 0x1f)
return -EINVAL;
if (length > 3)
header_len = 2;
cur = msgb_put(msg, length + header_len);
/* build the header without or with XL bit */
if (length <= 3) {
*cur++ = (type << 2) | (length & 3);
} else {
*cur++ = 0x80 | (type << 2) | (length >> 6);
*cur++ = (length << 2);
}
/* copy over the payload of the parameter*/
memcpy(cur, data, length);
return length + header_len;
}
static void rx_llc_xid(struct gprs_llc_lle *lle,
struct gprs_llc_hdr_parsed *gph)
{
/* FIXME: 8.5.3.3: check if XID is invalid */
if (gph->is_cmd) {
/* FIXME: implement XID negotiation using SNDCP */
struct msgb *resp;
uint8_t *xid;
resp = msgb_alloc_headroom(4096, 1024, "LLC_XID");
xid = msgb_put(resp, gph->data_len);
memcpy(xid, gph->data, gph->data_len);
gprs_llc_tx_xid(lle, resp, 0);
} else {
/* FIXME: if we had sent a XID reset, send
* LLGMM-RESET.conf to GMM */
/* FIXME: implement XID negotiation using SNDCP */
}
}
static int gprs_llc_hdr_rx(struct gprs_llc_hdr_parsed *gph,
struct gprs_llc_lle *lle)
{
@@ -562,6 +856,7 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv)
struct gprs_llc_hdr *lh = (struct gprs_llc_hdr *) msgb_llch(msg);
struct gprs_llc_hdr_parsed llhp;
struct gprs_llc_lle *lle;
bool drop_cipherable = false;
int rc = 0;
/* Identifiers from DOWN: NSEI, BVCI, TLLI */
@@ -595,7 +890,7 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv)
case GPRS_SAPI_SNDCP9:
case GPRS_SAPI_SNDCP11:
/* Ask an upper layer for help. */
return sgsn_force_reattach_oldmsg(msg);
return gsm0408_gprs_force_reattach_oldmsg(msg, NULL);
default:
break;
}
@@ -607,38 +902,24 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv)
/* decrypt information field + FCS, if needed! */
if (llhp.is_encrypted) {
uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */
uint16_t crypt_len = llhp.data_len + 3;
uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
uint32_t iv;
uint64_t kc = *(uint64_t *)&lle->llme->kc;
int rc, i;
if (lle->llme->algo == GPRS_ALGO_GEA0) {
if (lle->llme->algo != GPRS_ALGO_GEA0) {
rc = apply_gea(lle, llhp.data_len + 3, llhp.seq_tx,
lle->oc_ui_recv, lle->sapi, NULL,
llhp.data);
if (rc < 0)
return rc;
llhp.fcs = *(llhp.data + llhp.data_len);
llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8;
llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16;
} else {
LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that "
"has no KC/Algo! Dropping.\n");
return 0;
}
iv = gprs_cipher_gen_input_ui(iov_ui, lle->sapi, llhp.seq_tx,
lle->oc_ui_recv);
rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
kc, iv, GPRS_CIPH_MS2SGSN);
if (rc < 0) {
LOGP(DLLC, LOGL_ERROR, "Error decrypting frame: %d\n",
rc);
return rc;
}
/* XOR the cipher output with the information field + FCS */
for (i = 0; i < crypt_len; i++)
*(llhp.data + i) ^= cipher_out[i];
} else {
if (lle->llme->algo != GPRS_ALGO_GEA0) {
LOGP(DLLC, LOGL_NOTICE, "unencrypted frame for LLC "
"that is supposed to be encrypted. Dropping.\n");
return 0;
}
if (lle->llme->algo != GPRS_ALGO_GEA0 &&
lle->llme->cksn != GSM_KEY_SEQ_INVAL)
drop_cipherable = true;
}
/* We have to do the FCS check _after_ decryption */
@@ -657,13 +938,17 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv)
if (rc < 0)
return rc;
rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_PACKETS]);
rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_BYTES], msg->len);
/* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */
if (llhp.cmd == GPRS_LLC_UI && llhp.data && llhp.data_len) {
msgb_gmmh(msg) = llhp.data;
switch (llhp.sapi) {
case GPRS_SAPI_GMM:
/* send LL_UNITDATA_IND to GMM */
rc = gsm0408_gprs_rcvmsg(msg, lle->llme);
rc = gsm0408_gprs_rcvmsg_gb(msg, lle->llme,
drop_cipherable);
break;
case GPRS_SAPI_SNDCP3:
case GPRS_SAPI_SNDCP5:
@@ -687,18 +972,29 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv)
return rc;
}
/* Propagate crypto parameters MM -> LLME */
void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme)
{
if (!mm)
return;
if (mm->ciph_algo != GPRS_ALGO_GEA0) {
llme->algo = mm->ciph_algo;
if (llme->cksn != mm->auth_triplet.key_seq &&
mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) {
memcpy(llme->kc, mm->auth_triplet.vec.kc,
gprs_cipher_key_length(mm->ciph_algo));
llme->cksn = mm->auth_triplet.key_seq;
}
} else
llme->cksn = GSM_KEY_SEQ_INVAL;
}
/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */
int gprs_llgmm_assign(struct gprs_llc_llme *llme,
uint32_t old_tlli, uint32_t new_tlli,
enum gprs_ciph_algo alg, const uint8_t *kc)
uint32_t old_tlli, uint32_t new_tlli)
{
unsigned int i;
/* Update the crypto parameters */
llme->algo = alg;
if (alg != GPRS_ALGO_GEA0)
memcpy(llme->kc, kc, sizeof(llme->kc));
if (old_tlli == 0xffffffff && new_tlli != 0xffffffff) {
/* TLLI Assignment 8.3.1 */
/* New TLLI shall be assigned and used when (re)transmitting LLC frames */
@@ -745,17 +1041,35 @@ int gprs_llgmm_assign(struct gprs_llc_llme *llme,
return 0;
}
/* TLLI unassignment */
int gprs_llgmm_unassign(struct gprs_llc_llme *llme)
{
return gprs_llgmm_assign(llme, llme->tlli, 0xffffffff);
}
/* Chapter 7.2.1.2 LLGMM-RESET.req */
int gprs_llgmm_reset(struct gprs_llc_llme *llme)
{
struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID");
int random = rand();
struct gprs_llc_lle *lle = &llme->lle[1];
uint8_t xid_bytes[1024];
int xid_bytes_len;
uint8_t *xid;
/* First XID component must be RESET */
msgb_put_xid_par(msg, GPRS_LLC_XID_T_RESET, 0, NULL);
/* randomly select new IOV-UI */
msgb_put_xid_par(msg, GPRS_LLC_XID_T_IOV_UI, 4, (uint8_t *) &random);
LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n");
if (RAND_bytes((uint8_t *) &llme->iov_ui, 4) != 1) {
LOGP(DLLC, LOGL_NOTICE, "RAND_bytes failed for LLC XID reset, "
"falling back to rand()\n");
llme->iov_ui = rand();
}
/* Generate XID message */
xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes,
sizeof(xid_bytes),llme->iov_ui,llme);
if (xid_bytes_len < 0)
return -EINVAL;
xid = msgb_put(msg, xid_bytes_len);
memcpy(xid, xid_bytes, xid_bytes_len);
/* Reset some of the LLC parameters. See GSM 04.64, 8.5.3.1 */
lle->vu_recv = 0;
@@ -767,15 +1081,28 @@ int gprs_llgmm_reset(struct gprs_llc_llme *llme)
return gprs_llc_tx_xid(lle, msg, 1);
}
int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi)
int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi,
struct gprs_llc_llme *llme)
{
struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID");
int random = rand();
uint8_t xid_bytes[1024];
int xid_bytes_len;
uint8_t *xid;
/* First XID component must be RESET */
msgb_put_xid_par(msg, GPRS_LLC_XID_T_RESET, 0, NULL);
/* randomly select new IOV-UI */
msgb_put_xid_par(msg, GPRS_LLC_XID_T_IOV_UI, 4, (uint8_t *) &random);
LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n");
if (RAND_bytes((uint8_t *) &llme->iov_ui, 4) != 1) {
LOGP(DLLC, LOGL_NOTICE, "RAND_bytes failed for LLC XID reset, "
"falling back to rand()\n");
llme->iov_ui = rand();
}
/* Generate XID message */
xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes,
sizeof(xid_bytes),llme->iov_ui,llme);
if (xid_bytes_len < 0)
return -EINVAL;
xid = msgb_put(msg, xid_bytes_len);
memcpy(xid, xid_bytes, xid_bytes_len);
/* FIXME: Start T200, wait for XID response */

View File

@@ -0,0 +1,262 @@
/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc_xid.h>
/* Parse XID parameter field */
static int decode_xid_field(struct gprs_llc_xid_field *xid_field,
const uint8_t *src, uint8_t src_len)
{
uint8_t xl;
uint8_t type;
uint8_t len;
int src_counter = 0;
/* Exit immediately if it is clear that no
* parseable data is present */
if (src_len < 1 || !src)
return -EINVAL;
/* Extract header info */
xl = (*src >> 7) & 1;
type = (*src >> 2) & 0x1F;
/* Extract length field */
len = (*src) & 0x3;
src++;
src_counter++;
if (xl) {
if (src_len < 2)
return -EINVAL;
len = (len << 6) & 0xC0;
len |= ((*src) >> 2) & 0x3F;
src++;
src_counter++;
}
/* Fill out struct */
xid_field->type = type;
xid_field->data_len = len;
if (len > 0) {
if (src_len < src_counter + len)
return -EINVAL;
xid_field->data =
talloc_memdup(xid_field,src,xid_field->data_len);
} else
xid_field->data = NULL;
/* Return consumed length */
return src_counter + len;
}
/* Encode XID parameter field */
static int encode_xid_field(uint8_t *dst, int dst_maxlen,
const struct gprs_llc_xid_field *xid_field)
{
int xl = 0;
/* When the length does not fit into 2 bits,
* we need extended length fields */
if (xid_field->data_len > 3)
xl = 1;
/* Exit immediately if it is clear that no
* encoding result can be stored */
if (dst_maxlen < xid_field->data_len + 1 + xl)
return -EINVAL;
/* There are only 5 bits reserved for the type, exit on exceed */
if (xid_field->type > 31)
return -EINVAL;
/* Encode header */
memset(dst, 0, dst_maxlen);
if (xl)
dst[0] |= 0x80;
dst[0] |= (((xid_field->type) & 0x1F) << 2);
if (xl) {
dst[0] |= (((xid_field->data_len) >> 6) & 0x03);
dst[1] = ((xid_field->data_len) << 2) & 0xFC;
} else
dst[0] |= ((xid_field->data_len) & 0x03);
/* Append payload data */
if (xid_field->data && xid_field->data_len)
memcpy(dst + 1 + xl, xid_field->data, xid_field->data_len);
/* Return generated length */
return xid_field->data_len + 1 + xl;
}
/* Transform a list with XID fields into a XID message (dst) */
int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen,
const struct llist_head *xid_fields)
{
struct gprs_llc_xid_field *xid_field;
int rc;
int byte_counter = 0;
OSMO_ASSERT(xid_fields);
OSMO_ASSERT(dst);
llist_for_each_entry_reverse(xid_field, xid_fields, list) {
/* Encode XID-Field */
rc = encode_xid_field(dst, dst_maxlen, xid_field);
if (rc < 0)
return -EINVAL;
/* Advance pointer and lower maxlen for the
* next encoding round */
dst += rc;
byte_counter += rc;
dst_maxlen -= rc;
}
/* Return generated length */
return byte_counter;
}
/* Transform a XID message (dst) into a list of XID fields */
struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src,
int src_len)
{
struct gprs_llc_xid_field *xid_field;
struct llist_head *xid_fields;
int rc;
int max_loops = src_len;
OSMO_ASSERT(src);
xid_fields = talloc_zero(ctx, struct llist_head);
INIT_LLIST_HEAD(xid_fields);
while (1) {
/* Bail in case decode_xid_field() constantly returns zero */
if (max_loops <= 0) {
talloc_free(xid_fields);
return NULL;
}
/* Decode XID field */
xid_field = talloc_zero(xid_fields, struct gprs_llc_xid_field);
rc = decode_xid_field(xid_field, src, src_len);
/* Immediately stop on error */
if (rc < 0) {
talloc_free(xid_fields);
return NULL;
}
/* Add parsed XID field to list */
llist_add(&xid_field->list, xid_fields);
/* Advance pointer and lower dst_len for the next
* decoding round */
src += rc;
src_len -= rc;
/* We are (scuccessfully) done when no further byes are left */
if (src_len == 0)
return xid_fields;
max_loops--;
}
}
/* Create a duplicate of an XID-Field */
struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx, const struct
gprs_llc_xid_field
*xid_field)
{
struct gprs_llc_xid_field *dup;
OSMO_ASSERT(xid_field);
/* Create a copy of the XID field in memory */
dup = talloc_memdup(ctx, xid_field, sizeof(*xid_field));
dup->data = talloc_memdup(ctx, xid_field->data, xid_field->data_len);
/* Unlink duplicate from source list */
INIT_LLIST_HEAD(&dup->list);
return dup;
}
/* Copy an llist with xid fields */
struct llist_head *gprs_llc_copy_xid(const void *ctx,
const struct llist_head *xid_fields)
{
struct gprs_llc_xid_field *xid_field;
struct llist_head *xid_fields_copy;
OSMO_ASSERT(xid_fields);
xid_fields_copy = talloc_zero(ctx, struct llist_head);
INIT_LLIST_HEAD(xid_fields_copy);
/* Create duplicates and add them to the target list */
llist_for_each_entry(xid_field, xid_fields, list) {
llist_add(&gprs_llc_dup_xid_field(ctx, xid_field)->list,
xid_fields_copy);
}
return xid_fields_copy;
}
/* Dump a list with XID fields (Debug) */
void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields,
unsigned int logl)
{
struct gprs_llc_xid_field *xid_field;
OSMO_ASSERT(xid_fields);
llist_for_each_entry(xid_field, xid_fields, list) {
if (xid_field->data_len) {
OSMO_ASSERT(xid_field->data);
LOGP(DLLC, logl,
"XID: type=%d, data_len=%d, data=%s\n",
xid_field->type, xid_field->data_len,
osmo_hexdump_nospc(xid_field->data,
xid_field->data_len));
} else {
LOGP(DLLC, logl,
"XID: type=%d, data_len=%d, data=NULL\n",
xid_field->type, xid_field->data_len);
}
}
}

View File

@@ -39,6 +39,9 @@
#include <openbsc/gprs_utils.h>
#include <openbsc/signal.h>
#include "openbsc/gprs_llc.h"
#include <openbsc/iu.h>
#include <pdp.h>
#include <time.h>
@@ -90,6 +93,58 @@ static const struct rate_ctr_group_desc pdpctx_ctrg_desc = {
.class_id = OSMO_STATS_CLASS_SUBSCRIBER,
};
static const struct rate_ctr_desc sgsn_ctr_description[] = {
{ "llc.dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" },
{ "llc.ul_bytes", "Count sucessful received LLC bytes (encrypt & fcs correct)" },
{ "llc.dl_packets", "Count sucessful sent LLC packets before giving it to the bssgp layer" },
{ "llc.ul_packets", "Count sucessful received LLC packets (encrypt & fcs correct)" },
{ "gprs.attach_requested", "Received attach requests" },
{ "gprs.attach_accepted", "Sent attach accepts" },
{ "gprs.attach_rejected", "Sent attach rejects" },
{ "gprs.detach_requested", "Received detach requests" },
{ "gprs.detach_acked", "Sent detach acks" },
{ "gprs.routing_area_requested", "Received routing area requests" },
{ "gprs.routing_area_requested", "Sent routing area acks" },
{ "gprs.routing_area_requested", "Sent routing area rejects" },
{ "pdp.activate_requested", "Received activate requests" },
{ "pdp.activate_rejected", "Sent activate rejects" },
{ "pdp.activate_accepted", "Sent activate accepts" },
{ "pdp.request_activated", "unused" },
{ "pdp.request_activate_rejected", "unused" },
{ "pdp.modify_requested", "unused" },
{ "pdp.modify_accepted", "unused" },
{ "pdp.dl_deactivate_requested", "Sent deactivate requests" },
{ "pdp.dl_deactivate_accepted", "Sent deactivate accepted" },
{ "pdp.ul_deactivate_requested", "Received deactivate requests" },
{ "pdp.ul_deactivate_accepted", "Received deactivate accepts" },
};
static const struct rate_ctr_group_desc sgsn_ctrg_desc = {
"sgsn",
"SGSN Overall Statistics",
OSMO_STATS_CLASS_GLOBAL,
ARRAY_SIZE(sgsn_ctr_description),
sgsn_ctr_description,
};
void sgsn_rate_ctr_init() {
sgsn->rate_ctrs = rate_ctr_group_alloc(tall_bsc_ctx, &sgsn_ctrg_desc, 0);
}
/* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if (ctx->ran_type == MM_CTX_T_UTRAN_Iu
&& uectx == ctx->iu.ue_ctx)
return ctx;
}
return NULL;
}
/* look-up a SGSN MM context based on TLLI + RAI */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
const struct gprs_ra_id *raid)
@@ -97,7 +152,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if ((tlli == ctx->tlli || tlli == ctx->tlli_new) &&
if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) &&
gprs_ra_id_equals(raid, &ctx->ra))
return ctx;
}
@@ -165,9 +220,11 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
return NULL;
memcpy(&ctx->ra, raid, sizeof(ctx->ra));
ctx->tlli = tlli;
ctx->ran_type = MM_CTX_T_GERAN_Gb;
ctx->gb.tlli = tlli;
ctx->mm_state = GMM_DEREGISTERED;
ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
ctx->ciph_algo = sgsn->cfg.cipher;
ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
INIT_LLIST_HEAD(&ctx->pdp_list);
@@ -176,6 +233,34 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
return ctx;
}
/* Allocate a new SGSN MM context */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx)
{
struct sgsn_mm_ctx *ctx;
ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
if (!ctx)
return NULL;
ctx->ran_type = MM_CTX_T_UTRAN_Iu;
ctx->iu.ue_ctx = uectx;
ctx->iu.new_key = 1;
ctx->mm_state = GMM_DEREGISTERED;
ctx->pmm_state = PMM_DETACHED;
ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, 0);
/* Need to get RAID from IU conn */
ctx->ra = ctx->iu.ue_ctx->ra_id;
INIT_LLIST_HEAD(&ctx->pdp_list);
llist_add(&ctx->list, &sgsn_mm_ctxts);
return ctx;
}
/* this is a hard _free_ function, it doesn't clean up the PDP contexts
* in libgtp! */
static void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
@@ -196,11 +281,16 @@ static void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm)
{
struct gprs_llc_llme *llme = mm->llme;
uint32_t tlli = mm->tlli;
struct gprs_llc_llme *llme = NULL;
uint32_t tlli = mm->gb.tlli;
struct sgsn_pdp_ctx *pdp, *pdp2;
struct sgsn_signal_data sig_data;
if (mm->ran_type == MM_CTX_T_GERAN_Gb)
llme = mm->gb.llme;
else
OSMO_ASSERT(mm->gb.llme == NULL);
/* Forget about ongoing look-ups */
if (mm->ggsn_lookup) {
LOGMMCTXP(LOGL_NOTICE, mm,
@@ -236,8 +326,10 @@ void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm)
sgsn_mm_ctx_free(mm);
mm = NULL;
/* TLLI unassignment, must be called after sgsn_mm_ctx_free */
gprs_llgmm_assign(llme, tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL);
if (llme) {
/* TLLI unassignment, must be called after sgsn_mm_ctx_free */
gprs_llgmm_assign(llme, tlli, 0xffffffff);
}
}
@@ -290,7 +382,6 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
return pdp;
}
#include <pdp.h>
/*
* This function will not trigger any GSM DEACT PDP ACK messages, so you
* probably want to call sgsn_delete_pdp_ctx() instead if the connection
@@ -307,8 +398,10 @@ void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
/* Force the deactivation of the SNDCP layer */
sndcp_sm_deactivate_ind(&pdp->mm->llme->lle[pdp->sapi], pdp->nsapi);
if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Force the deactivation of the SNDCP layer */
sndcp_sm_deactivate_ind(&pdp->mm->gb.llme->lle[pdp->sapi], pdp->nsapi);
}
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pdp;
@@ -594,11 +687,6 @@ int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
return num;
}
int sgsn_force_reattach_oldmsg(struct msgb *oldmsg)
{
return gsm0408_gprs_force_reattach_oldmsg(oldmsg);
}
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx)
{
OSMO_ASSERT(mmctx != NULL);
@@ -751,7 +839,7 @@ static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme)
struct sgsn_mm_ctx *mmctx = NULL;
llist_for_each_entry(mmctx, &sgsn_mm_ctxts, list) {
if (llme == mmctx->llme) {
if (llme == mmctx->gb.llme) {
gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE);
return;
}
@@ -760,7 +848,7 @@ static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme)
/* No MM context found */
LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n",
llme->tlli);
gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL);
gprs_llgmm_unassign(llme);
}
static void sgsn_llme_check_cb(void *data_)

View File

@@ -22,6 +22,7 @@
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
@@ -33,8 +34,133 @@
#include <openbsc/debug.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sndcp.h>
#include <openbsc/gprs_llc_xid.h>
#include <openbsc/gprs_sndcp_xid.h>
#include <openbsc/gprs_sndcp_pcomp.h>
#include <openbsc/gprs_sndcp_dcomp.h>
#include <openbsc/gprs_sndcp_comp.h>
#include "gprs_sndcp.h"
#define DEBUG_IP_PACKETS 0 /* 0=Disabled, 1=Enabled */
#if DEBUG_IP_PACKETS == 1
/* Calculate TCP/IP checksum */
static uint16_t calc_ip_csum(uint8_t *data, int len)
{
int i;
uint32_t accumulator = 0;
uint16_t *pointer = (uint16_t *) data;
for (i = len; i > 1; i -= 2) {
accumulator += *pointer;
pointer++;
}
if (len % 2)
accumulator += *pointer;
accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff);
accumulator += (accumulator >> 16) & 0xffff;
return (~accumulator);
}
/* Calculate TCP/IP checksum */
static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len)
{
uint8_t *buf;
uint16_t csum;
buf = talloc_zero_size(ctx, len);
memset(buf, 0, len);
memcpy(buf, packet + 12, 8);
buf[9] = packet[9];
buf[11] = (len - 20) & 0xFF;
buf[10] = (len - 20) >> 8 & 0xFF;
memcpy(buf + 12, packet + 20, len - 20);
csum = calc_ip_csum(buf, len - 20 + 12);
talloc_free(buf);
return csum;
}
/* Show some ip packet details */
static void debug_ip_packet(uint8_t *data, int len, int dir, char *info)
{
uint8_t tcp_flags;
char flags_debugmsg[256];
int len_short;
static unsigned int packet_count = 0;
static unsigned int tcp_csum_err_count = 0;
static unsigned int ip_csum_err_count = 0;
packet_count++;
if (len > 80)
len_short = 80;
else
len_short = len;
if (dir)
DEBUGP(DSNDCP, "%s: MS => SGSN: %s\n", info,
osmo_hexdump_nospc(data, len_short));
else
DEBUGP(DSNDCP, "%s: MS <= SGSN: %s\n", info,
osmo_hexdump_nospc(data, len_short));
DEBUGP(DSNDCP, "%s: Length.: %d\n", info, len);
DEBUGP(DSNDCP, "%s: NO.: %d\n", info, packet_count);
if (len < 20) {
DEBUGP(DSNDCP, "%s: Error: Short IP packet!\n", info);
return;
}
if (calc_ip_csum(data, 20) != 0) {
DEBUGP(DSNDCP, "%s: Bad IP-Header checksum!\n", info);
ip_csum_err_count++;
} else
DEBUGP(DSNDCP, "%s: IP-Header checksum ok.\n", info);
if (data[9] == 0x06) {
if (len < 40) {
DEBUGP(DSNDCP, "%s: Error: Short TCP packet!\n", info);
return;
}
DEBUGP(DSNDCP, "%s: Protocol type: TCP\n", info);
tcp_flags = data[33];
if (calc_tcpip_csum(NULL, data, len) != 0) {
DEBUGP(DSNDCP, "%s: Bad TCP checksum!\n", info);
tcp_csum_err_count++;
} else
DEBUGP(DSNDCP, "%s: TCP checksum ok.\n", info);
memset(flags_debugmsg, 0, sizeof(flags_debugmsg));
if (tcp_flags & 1)
strcat(flags_debugmsg, "FIN ");
if (tcp_flags & 2)
strcat(flags_debugmsg, "SYN ");
if (tcp_flags & 4)
strcat(flags_debugmsg, "RST ");
if (tcp_flags & 8)
strcat(flags_debugmsg, "PSH ");
if (tcp_flags & 16)
strcat(flags_debugmsg, "ACK ");
if (tcp_flags & 32)
strcat(flags_debugmsg, "URG ");
DEBUGP(DSNDCP, "%s: FLAGS: %s\n", info, flags_debugmsg);
} else if (data[9] == 0x11) {
DEBUGP(DSNDCP, "%s: Protocol type: UDP\n", info);
} else {
DEBUGP(DSNDCP, "%s: Protocol type: (%02x)\n", info, data[9]);
}
DEBUGP(DSNDCP, "%s: IP-Header checksum errors: %d\n", info,
ip_csum_err_count);
DEBUGP(DSNDCP, "%s: TCP-Checksum errors: %d\n", info,
tcp_csum_err_count);
}
#endif
/* Chapter 7.2: SN-PDU Formats */
struct sndcp_common_hdr {
@@ -77,6 +203,15 @@ struct defrag_queue_entry {
LLIST_HEAD(gprs_sndcp_entities);
/* Check if any compression parameters are set in the sgsn configuration */
static inline int any_pcomp_or_dcomp_active(struct sgsn_instance *sgsn) {
if (sgsn->cfg.pcomp_rfc1144.active || sgsn->cfg.pcomp_rfc1144.passive ||
sgsn->cfg.dcomp_v42bis.active || sgsn->cfg.dcomp_v42bis.passive)
return true;
else
return false;
}
/* Enqueue a fragment into the defragment queue */
static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr,
uint8_t *data, uint32_t data_len)
@@ -143,6 +278,9 @@ static int defrag_segments(struct gprs_sndcp_entity *sne)
struct msgb *msg;
unsigned int seg_nr;
uint8_t *npdu;
int npdu_len;
int rc;
uint8_t *expnd = NULL;
LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u "
"num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi,
@@ -173,16 +311,69 @@ static int defrag_segments(struct gprs_sndcp_entity *sne)
talloc_free(dqe);
}
npdu_len = sne->defrag.tot_len;
/* FIXME: cancel timer */
/* actually send the N-PDU to the SGSN core code, which then
* hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */
return sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
sne->nsapi, msg, sne->defrag.tot_len, npdu);
/* Decompress packet */
#if DEBUG_IP_PACKETS == 1
DEBUGP(DSNDCP, " \n");
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
DEBUGP(DSNDCP, "===================================================\n");
#endif
if (any_pcomp_or_dcomp_active(sgsn)) {
expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC +
MAX_HDRDECOMPR_INCR);
memcpy(expnd, npdu, npdu_len);
/* Apply data decompression */
rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp,
sne->defrag.data);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"Data decompression failed!\n");
talloc_free(expnd);
return -EIO;
}
/* Apply header decompression */
rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp,
sne->defrag.proto);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"TCP/IP Header decompression failed!\n");
talloc_free(expnd);
return -EIO;
}
/* Modify npu length, expnd is handed directly handed
* over to gsn_rx_sndcp_ud_ind(), see below */
npdu_len = rc;
} else
expnd = npdu;
#if DEBUG_IP_PACKETS == 1
debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()");
DEBUGP(DSNDCP, "===================================================\n");
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
DEBUGP(DSNDCP, " \n");
#endif
/* Hand off packet to gtp */
rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
sne->nsapi, msg, npdu_len, expnd);
if (any_pcomp_or_dcomp_active(sgsn))
talloc_free(expnd);
return rc;
}
static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, uint8_t *hdr,
unsigned int len)
static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg,
uint8_t *hdr, unsigned int len)
{
struct sndcp_common_hdr *sch;
struct sndcp_udata_hdr *suh;
@@ -343,7 +534,8 @@ struct sndcp_frag_state {
};
/* returns '1' if there are more fragments to send, '0' if none */
static int sndcp_send_ud_frag(struct sndcp_frag_state *fs)
static int sndcp_send_ud_frag(struct sndcp_frag_state *fs,
uint8_t pcomp, uint8_t dcomp)
{
struct gprs_sndcp_entity *sne = fs->sne;
struct gprs_llc_lle *lle = sne->lle;
@@ -380,8 +572,8 @@ static int sndcp_send_ud_frag(struct sndcp_frag_state *fs)
if (sch->first) {
scomph = (struct sndcp_comp_hdr *)
msgb_put(fmsg, sizeof(*scomph));
scomph->pcomp = 0;
scomph->dcomp = 0;
scomph->pcomp = pcomp;
scomph->dcomp = dcomp;
}
/* append the user-data header */
@@ -417,7 +609,7 @@ static int sndcp_send_ud_frag(struct sndcp_frag_state *fs)
/* set the MORE bit of the SNDCP header accordingly */
sch->more = more;
rc = gprs_llc_tx_ui(fmsg, lle->sapi, 0, fs->mmcontext);
rc = gprs_llc_tx_ui(fmsg, lle->sapi, 0, fs->mmcontext, true);
/* abort in case of error, do not advance frag_nr / next_byte */
if (rc < 0) {
msgb_free(fs->msg);
@@ -446,9 +638,54 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi
struct sndcp_comp_hdr *scomph;
struct sndcp_udata_hdr *suh;
struct sndcp_frag_state fs;
uint8_t pcomp = 0;
uint8_t dcomp = 0;
int rc;
/* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
/* Compress packet */
#if DEBUG_IP_PACKETS == 1
DEBUGP(DSNDCP, " \n");
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
DEBUGP(DSNDCP, "===================================================\n");
debug_ip_packet(msg->data, msg->len, 0, "sndcp_initdata_req()");
#endif
if (any_pcomp_or_dcomp_active(sgsn)) {
/* Apply header compression */
rc = gprs_sndcp_pcomp_compress(msg->data, msg->len, &pcomp,
lle->llme->comp.proto, nsapi);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"TCP/IP Header compression failed!\n");
return -EIO;
}
/* Fixup pointer locations and sizes in message buffer to match
* the new, compressed buffer size */
msgb_get(msg, msg->len);
msgb_put(msg, rc);
/* Apply data compression */
rc = gprs_sndcp_dcomp_compress(msg->data, msg->len, &dcomp,
lle->llme->comp.data, nsapi);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR, "Data compression failed!\n");
return -EIO;
}
/* Fixup pointer locations and sizes in message buffer to match
* the new, compressed buffer size */
msgb_get(msg, msg->len);
msgb_put(msg, rc);
}
#if DEBUG_IP_PACKETS == 1
DEBUGP(DSNDCP, "===================================================\n");
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
DEBUGP(DSNDCP, " \n");
#endif
sne = gprs_sndcp_entity_by_lle(lle, nsapi);
if (!sne) {
LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n");
@@ -469,7 +706,7 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi
/* call function to generate and send fragments until all
* of the N-PDU has been sent */
while (1) {
int rc = sndcp_send_ud_frag(&fs);
int rc = sndcp_send_ud_frag(&fs,pcomp,dcomp);
if (rc == 0)
return 0;
if (rc < 0)
@@ -489,8 +726,8 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi
sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff;
scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph));
scomph->pcomp = 0;
scomph->dcomp = 0;
scomph->pcomp = pcomp;
scomph->dcomp = dcomp;
/* prepend common SNDCP header */
sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch));
@@ -498,7 +735,7 @@ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi
sch->type = 1;
sch->nsapi = nsapi;
return gprs_llc_tx_ui(msg, lle->sapi, 0, mmcontext);
return gprs_llc_tx_ui(msg, lle->sapi, 0, mmcontext, true);
}
/* Section 5.1.2.17 LL-UNITDATA.ind */
@@ -512,6 +749,8 @@ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
uint8_t *npdu;
uint16_t npdu_num __attribute__((unused));
int npdu_len;
int rc;
uint8_t *expnd = NULL;
sch = (struct sndcp_common_hdr *) hdr;
if (sch->first) {
@@ -540,26 +779,81 @@ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
/* FIXME: move this RA_ID up to the LLME or even higher */
bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg));
if (scomph) {
sne->defrag.pcomp = scomph->pcomp;
sne->defrag.dcomp = scomph->dcomp;
sne->defrag.proto = lle->llme->comp.proto;
sne->defrag.data = lle->llme->comp.data;
}
/* any non-first segment is by definition something to defragment
* as is any segment that tells us there are more segments */
if (!sch->first || sch->more)
return defrag_input(sne, msg, hdr, len);
if (scomph && (scomph->pcomp || scomph->dcomp)) {
LOGP(DSNDCP, LOGL_ERROR, "We don't support compression yet\n");
return -EIO;
}
npdu_num = (suh->npdu_high << 8) | suh->npdu_low;
npdu = (uint8_t *)suh + sizeof(*suh);
npdu_len = (msg->data + msg->len) - npdu;
npdu_len = (msg->data + msg->len) - npdu - 3; /* -3 'removes' the FCS */
if (npdu_len <= 0) {
LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len);
return -EIO;
}
/* actually send the N-PDU to the SGSN core code, which then
* hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */
return sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu);
/* Decompress packet */
#if DEBUG_IP_PACKETS == 1
DEBUGP(DSNDCP, " \n");
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
DEBUGP(DSNDCP, "===================================================\n");
#endif
if (any_pcomp_or_dcomp_active(sgsn)) {
expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC +
MAX_HDRDECOMPR_INCR);
memcpy(expnd, npdu, npdu_len);
/* Apply data decompression */
rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp,
sne->defrag.data);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"Data decompression failed!\n");
talloc_free(expnd);
return -EIO;
}
/* Apply header decompression */
rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp,
sne->defrag.proto);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"TCP/IP Header decompression failed!\n");
talloc_free(expnd);
return -EIO;
}
/* Modify npu length, expnd is handed directly handed
* over to gsn_rx_sndcp_ud_ind(), see below */
npdu_len = rc;
} else
expnd = npdu;
#if DEBUG_IP_PACKETS == 1
debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()");
DEBUGP(DSNDCP, "===================================================\n");
DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
DEBUGP(DSNDCP, " \n");
#endif
/* Hand off packet to gtp */
rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli,
sne->nsapi, msg, npdu_len, expnd);
if (any_pcomp_or_dcomp_active(sgsn))
talloc_free(expnd);
return rc;
}
#if 0
@@ -619,3 +913,348 @@ static int sndcp_rx_llc_prim()
case LL_STATUS_IND:
}
#endif
/* Generate SNDCP-XID message */
static int gprs_llc_gen_sndcp_xid(uint8_t *bytes, int bytes_len, uint8_t nsapi)
{
int entity = 0;
LLIST_HEAD(comp_fields);
struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params;
struct gprs_sndcp_comp_field rfc1144_comp_field;
struct gprs_sndcp_dcomp_v42bis_params v42bis_params;
struct gprs_sndcp_comp_field v42bis_comp_field;
memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
/* Setup rfc1144 */
if (sgsn->cfg.pcomp_rfc1144.active) {
rfc1144_params.nsapi[0] = nsapi;
rfc1144_params.nsapi_len = 1;
rfc1144_params.s01 = sgsn->cfg.pcomp_rfc1144.s01;
rfc1144_comp_field.p = 1;
rfc1144_comp_field.entity = entity;
rfc1144_comp_field.algo = RFC_1144;
rfc1144_comp_field.comp[RFC1144_PCOMP1] = 1;
rfc1144_comp_field.comp[RFC1144_PCOMP2] = 2;
rfc1144_comp_field.comp_len = RFC1144_PCOMP_NUM;
rfc1144_comp_field.rfc1144_params = &rfc1144_params;
entity++;
llist_add(&rfc1144_comp_field.list, &comp_fields);
}
/* Setup V.42bis */
if (sgsn->cfg.dcomp_v42bis.active) {
v42bis_params.nsapi[0] = nsapi;
v42bis_params.nsapi_len = 1;
v42bis_params.p0 = sgsn->cfg.dcomp_v42bis.p0;
v42bis_params.p1 = sgsn->cfg.dcomp_v42bis.p1;
v42bis_params.p2 = sgsn->cfg.dcomp_v42bis.p2;
v42bis_comp_field.p = 1;
v42bis_comp_field.entity = entity;
v42bis_comp_field.algo = V42BIS;
v42bis_comp_field.comp[V42BIS_DCOMP1] = 1;
v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM;
v42bis_comp_field.v42bis_params = &v42bis_params;
entity++;
llist_add(&v42bis_comp_field.list, &comp_fields);
}
/* Compile bytestream */
return gprs_sndcp_compile_xid(bytes, bytes_len, &comp_fields);
}
/* Set of SNDCP-XID bnegotiation (See also: TS 144 065,
* Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi)
{
/* Note: The specification requires the SNDCP-User to set of an
* SNDCP xid request. See also 3GPP TS 44.065, 6.8 XID parameter
* negotiation, Figure 11: SNDCP XID negotiation procedure. In
* our case the SNDCP-User is sgsn_libgtp.c, which calls
* sndcp_sn_xid_req directly. */
uint8_t l3params[1024];
int xid_len;
struct gprs_llc_xid_field xid_field_request;
/* Wipe off all compression entities and their states to
* get rid of possible leftovers from a previous session */
gprs_sndcp_comp_free(lle->llme->comp.proto);
gprs_sndcp_comp_free(lle->llme->comp.data);
lle->llme->comp.proto = gprs_sndcp_comp_alloc(lle->llme);
lle->llme->comp.data = gprs_sndcp_comp_alloc(lle->llme);
talloc_free(lle->llme->xid);
lle->llme->xid = NULL;
/* Generate compression parameter bytestream */
xid_len = gprs_llc_gen_sndcp_xid(l3params, sizeof(l3params), nsapi);
/* Send XID with the SNDCP-XID bytetsream included */
if (xid_len > 0) {
xid_field_request.type = GPRS_LLC_XID_T_L3_PAR;
xid_field_request.data = l3params;
xid_field_request.data_len = xid_len;
return gprs_ll_xid_req(lle, &xid_field_request);
}
/* When bytestream can not be generated, proceed without SNDCP-XID */
return gprs_ll_xid_req(lle, NULL);
}
/* Handle header compression entites */
static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field,
struct gprs_llc_lle *lle)
{
/* Note: This functions also transforms the comp_field into its
* echo form (strips comp values, resets propose bit etc...)
* the processed comp_fields can then be sent back as XID-
* Response without further modification. */
/* Delete propose bit */
comp_field->p = 0;
/* Process proposed parameters */
switch (comp_field->algo) {
case RFC_1144:
if (sgsn->cfg.pcomp_rfc1144.passive
&& comp_field->rfc1144_params->nsapi_len > 0) {
DEBUGP(DSNDCP,
"Accepting RFC1144 header compression...\n");
gprs_sndcp_comp_add(lle->llme, lle->llme->comp.proto,
comp_field);
} else {
DEBUGP(DSNDCP,
"Rejecting RFC1144 header compression...\n");
gprs_sndcp_comp_delete(lle->llme->comp.proto,
comp_field->entity);
comp_field->rfc1144_params->nsapi_len = 0;
}
break;
case RFC_2507:
/* RFC 2507 is not yet supported,
* so we set applicable nsapis to zero */
DEBUGP(DSNDCP, "Rejecting RFC2507 header compression...\n");
comp_field->rfc2507_params->nsapi_len = 0;
gprs_sndcp_comp_delete(lle->llme->comp.proto,
comp_field->entity);
break;
case ROHC:
/* ROHC is not yet supported,
* so we set applicable nsapis to zero */
DEBUGP(DSNDCP, "Rejecting ROHC header compression...\n");
comp_field->rohc_params->nsapi_len = 0;
gprs_sndcp_comp_delete(lle->llme->comp.proto,
comp_field->entity);
break;
}
return 0;
}
/* Hanle data compression entites */
static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field,
struct gprs_llc_lle *lle)
{
/* See note in handle_pcomp_entities() */
/* Delete propose bit */
comp_field->p = 0;
/* Process proposed parameters */
switch (comp_field->algo) {
case V42BIS:
if (sgsn->cfg.dcomp_v42bis.passive &&
comp_field->v42bis_params->nsapi_len > 0) {
DEBUGP(DSNDCP,
"Accepting V.42bis data compression...\n");
gprs_sndcp_comp_add(lle->llme, lle->llme->comp.data,
comp_field);
} else {
LOGP(DSNDCP, LOGL_DEBUG,
"Rejecting V.42bis data compression...\n");
gprs_sndcp_comp_delete(lle->llme->comp.data,
comp_field->entity);
comp_field->v42bis_params->nsapi_len = 0;
}
break;
case V44:
/* V44 is not yet supported,
* so we set applicable nsapis to zero */
DEBUGP(DSNDCP, "Rejecting V.44 data compression...\n");
comp_field->v44_params->nsapi_len = 0;
gprs_sndcp_comp_delete(lle->llme->comp.data,
comp_field->entity);
break;
}
return 0;
}
/* Process SNDCP-XID indication
* (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
struct gprs_llc_xid_field *xid_field_response,
struct gprs_llc_lle *lle)
{
/* Note: This function computes the SNDCP-XID response that is sent
* back to the ms when a ms originated XID is received. The
* Input XID fields are directly processed and the result is directly
* handed back. */
int rc;
int compclass;
struct llist_head *comp_fields;
struct gprs_sndcp_comp_field *comp_field;
OSMO_ASSERT(xid_field_indication);
OSMO_ASSERT(xid_field_response);
OSMO_ASSERT(lle);
/* Parse SNDCP-CID XID-Field */
comp_fields = gprs_sndcp_parse_xid(lle->llme,
xid_field_indication->data,
xid_field_indication->data_len,
NULL);
if (!comp_fields)
return -EINVAL;
/* Don't bother with empty indications */
if (llist_empty(comp_fields)) {
xid_field_response->data = NULL;
xid_field_response->data_len = 0;
DEBUGP(DSNDCP,
"SNDCP-XID indication did not contain any parameters!\n");
return 0;
}
/* Handle compression entites */
DEBUGP(DSNDCP, "SNDCP-XID-IND (ms):\n");
gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
llist_for_each_entry(comp_field, comp_fields, list) {
compclass = gprs_sndcp_get_compression_class(comp_field);
if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
rc = handle_pcomp_entities(comp_field, lle);
else if (compclass == SNDCP_XID_DATA_COMPRESSION)
rc = handle_dcomp_entities(comp_field, lle);
else {
gprs_sndcp_comp_delete(lle->llme->comp.proto,
comp_field->entity);
gprs_sndcp_comp_delete(lle->llme->comp.data,
comp_field->entity);
rc = 0;
}
if (rc < 0) {
talloc_free(comp_fields);
return -EINVAL;
}
}
DEBUGP(DSNDCP, "SNDCP-XID-RES (sgsn):\n");
gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
/* Reserve some memory to store the modified SNDCP-XID bytes */
xid_field_response->data =
talloc_zero_size(lle->llme, xid_field_indication->data_len);
/* Set Type flag for response */
xid_field_response->type = GPRS_LLC_XID_T_L3_PAR;
/* Compile modified SNDCP-XID bytes */
rc = gprs_sndcp_compile_xid(xid_field_response->data,
xid_field_indication->data_len,
comp_fields);
if (rc > 0)
xid_field_response->data_len = rc;
else {
talloc_free(xid_field_response->data);
xid_field_response->data = NULL;
xid_field_response->data_len = 0;
return -EINVAL;
}
talloc_free(comp_fields);
return 0;
}
/* Process SNDCP-XID indication
* (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
struct gprs_llc_xid_field *xid_field_request,
struct gprs_llc_lle *lle)
{
/* Note: This function handles an incomming SNDCP-XID confirmiation.
* Since the confirmation fields may lack important parameters we
* will reconstruct these missing fields using the original request
* we have sent. After that we will create (or delete) the
* compression entites */
struct llist_head *comp_fields_req;
struct llist_head *comp_fields_conf;
struct gprs_sndcp_comp_field *comp_field;
int rc;
int compclass;
/* We need both, the confirmation that is sent back by the ms,
* and the original request we have sent. If one of this is missing
* we can not process the confirmation, the caller must check if
* request and confirmation fields are available. */
OSMO_ASSERT(xid_field_conf);
OSMO_ASSERT(xid_field_request);
/* Parse SNDCP-CID XID-Field */
comp_fields_req = gprs_sndcp_parse_xid(lle->llme,
xid_field_request->data,
xid_field_request->data_len,
NULL);
if (!comp_fields_req)
return -EINVAL;
DEBUGP(DSNDCP, "SNDCP-XID-REQ (sgsn):\n");
gprs_sndcp_dump_comp_fields(comp_fields_req, LOGL_DEBUG);
/* Parse SNDCP-CID XID-Field */
comp_fields_conf = gprs_sndcp_parse_xid(lle->llme,
xid_field_conf->data,
xid_field_conf->data_len,
comp_fields_req);
if (!comp_fields_conf)
return -EINVAL;
DEBUGP(DSNDCP, "SNDCP-XID-CONF (ms):\n");
gprs_sndcp_dump_comp_fields(comp_fields_conf, LOGL_DEBUG);
/* Handle compression entites */
llist_for_each_entry(comp_field, comp_fields_conf, list) {
compclass = gprs_sndcp_get_compression_class(comp_field);
if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
rc = handle_pcomp_entities(comp_field, lle);
else if (compclass == SNDCP_XID_DATA_COMPRESSION)
rc = handle_dcomp_entities(comp_field, lle);
else {
gprs_sndcp_comp_delete(lle->llme->comp.proto,
comp_field->entity);
gprs_sndcp_comp_delete(lle->llme->comp.data,
comp_field->entity);
rc = 0;
}
if (rc < 0) {
talloc_free(comp_fields_req);
talloc_free(comp_fields_conf);
return -EINVAL;
}
}
talloc_free(comp_fields_req);
talloc_free(comp_fields_conf);
return 0;
}

View File

@@ -0,0 +1,322 @@
/* GPRS SNDCP header compression entity management tools */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sndcp_xid.h>
#include <openbsc/gprs_sndcp_comp.h>
#include <openbsc/gprs_sndcp_pcomp.h>
#include <openbsc/gprs_sndcp_dcomp.h>
/* Create a new compression entity from a XID-Field */
static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx,
const struct
gprs_sndcp_comp_field
*comp_field)
{
struct gprs_sndcp_comp *comp_entity;
comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp);
/* Copy relevant information from the SNDCP-XID field */
comp_entity->entity = comp_field->entity;
comp_entity->comp_len = comp_field->comp_len;
memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp));
if (comp_field->rfc1144_params) {
comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->rfc1144_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->rfc2507_params) {
comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->rfc2507_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->rohc_params) {
comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len;
memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->v42bis_params) {
comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->v42bis_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->v44_params) {
comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->v42bis_params->nsapi,
sizeof(comp_entity->nsapi));
} else {
/* The caller is expected to check carefully if the all
* data fields required for compression entity creation
* are present. Otherwise we blow an assertion here */
OSMO_ASSERT(false);
}
comp_entity->algo = comp_field->algo;
/* Check if an NSAPI is selected, if not, it does not make sense
* to create the compression entity, since the caller should
* have checked the presence of the NSAPI, we blow an assertion
* in case of missing NSAPIs */
OSMO_ASSERT(comp_entity->nsapi_len > 0);
/* Determine of which class our compression entity will be
* (Protocol or Data compresson ?) */
comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field);
OSMO_ASSERT(comp_entity->compclass != -1);
/* Create an algorithm specific compression context */
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
talloc_free(comp_entity);
comp_entity = NULL;
}
} else {
if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) {
talloc_free(comp_entity);
comp_entity = NULL;
}
}
/* Display info message */
if (comp_entity == NULL) {
LOGP(DSNDCP, LOGL_ERROR,
"Compression entity (%d) creation failed!\n",
comp_entity->entity);
return NULL;
}
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGP(DSNDCP, LOGL_INFO,
"New header compression entity (%d) created.\n",
comp_entity->entity);
} else {
LOGP(DSNDCP, LOGL_INFO,
"New data compression entity (%d) created.\n",
comp_entity->entity);
}
return comp_entity;
}
/* Allocate a compression enitiy list */
struct llist_head *gprs_sndcp_comp_alloc(const void *ctx)
{
struct llist_head *lh;
lh = talloc_zero(ctx, struct llist_head);
INIT_LLIST_HEAD(lh);
return lh;
}
/* Free a compression entitiy list */
void gprs_sndcp_comp_free(struct llist_head *comp_entities)
{
struct gprs_sndcp_comp *comp_entity;
/* We expect the caller to take care of allocating a
* compression entity list properly. Attempting to
* free a non existing list clearly points out
* a malfunction. */
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
/* Free compression entity */
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGP(DSNDCP, LOGL_INFO,
"Deleting header compression entity %d ...\n",
comp_entity->entity);
gprs_sndcp_pcomp_term(comp_entity);
} else {
LOGP(DSNDCP, LOGL_INFO,
"Deleting data compression entity %d ...\n",
comp_entity->entity);
gprs_sndcp_dcomp_term(comp_entity);
}
}
talloc_free(comp_entities);
}
/* Delete a compression entity */
void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
unsigned int entity)
{
struct gprs_sndcp_comp *comp_entity;
struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
if (comp_entity->entity == entity) {
comp_entity_to_delete = comp_entity;
break;
}
}
if (!comp_entity_to_delete)
return;
if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGP(DSNDCP, LOGL_INFO,
"Deleting header compression entity %d ...\n",
comp_entity_to_delete->entity);
gprs_sndcp_pcomp_term(comp_entity_to_delete);
} else {
LOGP(DSNDCP, LOGL_INFO,
"Deleting data compression entity %d ...\n",
comp_entity_to_delete->entity);
}
/* Delete compression entity */
llist_del(&comp_entity_to_delete->list);
talloc_free(comp_entity_to_delete);
}
/* Create and Add a new compression entity
* (returns a pointer to the compression entity that has just been created) */
struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
struct llist_head *comp_entities,
const struct gprs_sndcp_comp_field
*comp_field)
{
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(comp_entities);
OSMO_ASSERT(comp_field);
/* Just to be sure, if the entity is already in
* the list it will be deleted now */
gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
/* Create and add a new entity to the list */
comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
if (!comp_entity)
return NULL;
llist_add(&comp_entity->list, comp_entities);
return comp_entity;
}
/* Find which compression entity handles the specified pcomp/dcomp */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
*comp_entities, uint8_t comp)
{
struct gprs_sndcp_comp *comp_entity;
int i;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
for (i = 0; i < comp_entity->comp_len; i++) {
if (comp_entity->comp[i] == comp)
return comp_entity;
}
}
LOGP(DSNDCP, LOGL_ERROR,
"Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
comp);
return NULL;
}
/* Find which compression entity handles the specified nsapi */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
*comp_entities, uint8_t nsapi)
{
struct gprs_sndcp_comp *comp_entity;
int i;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
for (i = 0; i < comp_entity->nsapi_len; i++) {
if (comp_entity->nsapi[i] == nsapi)
return comp_entity;
}
}
return NULL;
}
/* Find a comp_index for a given pcomp/dcomp value */
uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp)
{
/* Note: This function returns a normalized version of the comp value,
* which matches up with the position of the comp field. Since comp=0
* is reserved for "no compression", the index value starts counting
* at one. The return value is the PCOMPn/DCOMPn value one can find
* in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
int i;
OSMO_ASSERT(comp_entity);
/* A pcomp/dcomp value of zero is reserved for "no comproession",
* So we just bail and return zero in this case */
if (comp == 0)
return 0;
/* Look in the pcomp/dcomp list for the index */
for (i = 0; i < comp_entity->comp_len; i++) {
if (comp_entity->comp[i] == comp)
return i + 1;
}
LOGP(DSNDCP, LOGL_ERROR,
"Could not find a matching comp_index for given pcomp/dcomp value %d\n",
comp);
return 0;
}
/* Find a pcomp/dcomp value for a given comp_index */
uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp_index)
{
OSMO_ASSERT(comp_entity);
/* A comp_index of zero translates to zero right away. */
if (comp_index == 0)
return 0;
if (comp_index > comp_entity->comp_len) {
LOGP(DSNDCP, LOGL_ERROR,
"Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
comp_index);
return 0;
}
/* Look in the pcomp/dcomp list for the comp_index, see
* note in gprs_sndcp_comp_get_idx() */
return comp_entity->comp[comp_index - 1];
}

View File

@@ -0,0 +1,357 @@
/* GPRS SNDCP data compression handler */
/* (C) 2016 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sndcp_xid.h>
#include <openbsc/v42bis.h>
#include <openbsc/v42bis_private.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sndcp_comp.h>
#include <openbsc/gprs_sndcp_dcomp.h>
/* A struct to capture the output data of compressor and decompressor */
struct v42bis_output_buffer {
uint8_t *buf;
uint8_t *buf_pointer;
int len;
};
/* Handler to capture the output data from the compressor */
void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len)
{
struct v42bis_output_buffer *output_buffer =
(struct v42bis_output_buffer *)user_data;
memcpy(output_buffer->buf_pointer, pkt, len);
output_buffer->buf_pointer += len;
output_buffer->len += len;
return;
}
/* Handler to capture the output data from the decompressor */
void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len)
{
struct v42bis_output_buffer *output_buffer =
(struct v42bis_output_buffer *)user_data;
memcpy(output_buffer->buf_pointer, buf, len);
output_buffer->buf_pointer += len;
output_buffer->len += len;
return;
}
/* Initalize data compression */
int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a new data compression
* entity is created by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
OSMO_ASSERT(comp_field);
if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
&& comp_entity->algo == V42BIS) {
comp_entity->state =
v42bis_init(ctx, NULL, comp_field->v42bis_params->p0,
comp_field->v42bis_params->p1,
comp_field->v42bis_params->p2,
&tx_v42bis_frame_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH,
&rx_v42bis_data_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH);
LOGP(DSNDCP, LOGL_INFO,
"V.42bis data compression initalized.\n");
return 0;
}
/* Just in case someone tries to initalize an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Terminate data compression */
void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a data compression
* entity is deleted by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
&& comp_entity->algo == V42BIS) {
if (comp_entity->state) {
v42bis_free((v42bis_state_t *) comp_entity->state);
comp_entity->state = NULL;
}
LOGP(DSNDCP, LOGL_INFO,
"V.42bis data compression terminated.\n");
return;
}
/* Just in case someone tries to terminate an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Perform a full reset of the V.42bis compression state */
static void v42bis_reset(v42bis_state_t *comp)
{
/* This function performs a complete reset of the V.42bis compression
* state by reinitalizing the state withe the previously negotiated
* parameters. */
int p0, p1, p2;
p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0;
p1 = comp->decompress.v42bis_parm_n2;
p2 = comp->decompress.v42bis_parm_n7;
DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n",
comp, p0, p1, p2);
v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH);
}
/* Compress a packet using V.42bis data compression */
static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data,
unsigned int len, v42bis_state_t *comp)
{
/* Note: This implementation may only be used to compress SN_UNITDATA
* packets, since it resets the compression state for each NPDU. */
uint8_t *data_o;
int rc;
int skip = 0;
struct v42bis_output_buffer compressed_data;
/* Don't bother with short packets */
if (len < MIN_COMPR_PAYLOAD)
skip = 1;
/* Skip if compression is not enabled for TX direction */
if (!comp->compress.v42bis_parm_p0)
skip = 1;
/* Skip compression */
if (skip) {
*pcomp_index = 0;
return len;
}
/* Reset V.42bis compression state */
v42bis_reset(comp);
/* Run compressor */
data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC);
compressed_data.buf = data_o;
compressed_data.buf_pointer = data_o;
compressed_data.len = 0;
comp->compress.user_data = (&compressed_data);
rc = v42bis_compress(comp, data, len);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"Data compression failed, skipping...\n");
skip = 1;
}
rc = v42bis_compress_flush(comp);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"Data compression failed, skipping...\n");
skip = 1;
}
/* The compressor might yield negative compression gain, in
* this case, we just decide to send the packat as normal,
* uncompressed payload => skip compresssion */
if (compressed_data.len >= len) {
LOGP(DSNDCP, LOGL_ERROR,
"Data compression ineffective, skipping...\n");
skip = 1;
}
/* Skip compression */
if (skip) {
*pcomp_index = 0;
talloc_free(data_o);
return len;
}
*pcomp_index = 1;
memcpy(data, data_o, compressed_data.len);
talloc_free(data_o);
return compressed_data.len;
}
/* Expand a packet using V.42bis data compression */
static int v42bis_expand_unitdata(uint8_t *data, unsigned int len,
uint8_t pcomp_index, v42bis_state_t *comp)
{
/* Note: This implementation may only be used to compress SN_UNITDATA
* packets, since it resets the compression state for each NPDU. */
int rc;
struct v42bis_output_buffer uncompressed_data;
uint8_t *data_i;
/* Skip when the packet is marked as uncompressed */
if (pcomp_index == 0) {
return len;
}
/* Reset V.42bis compression state */
v42bis_reset(comp);
/* Decompress packet */
data_i = talloc_zero_size(comp, len);
memcpy(data_i, data, len);
uncompressed_data.buf = data;
uncompressed_data.buf_pointer = data;
uncompressed_data.len = 0;
comp->decompress.user_data = (&uncompressed_data);
rc = v42bis_decompress(comp, data_i, len);
talloc_free(data_i);
if (rc < 0)
return -EINVAL;
rc = v42bis_decompress_flush(comp);
if (rc < 0)
return -EINVAL;
return uncompressed_data.len;
}
/* Expand packet */
int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Data compression entity list: comp_entities=%p\n", comp_entities);
LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp);
/* Skip on pcomp=0 */
if (pcomp == 0) {
return len;
}
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
return len;
}
/* Note: Only data compression entities may appear in
* data compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
/* Note: Currently V42BIS is the only compression method we
* support, so the only allowed algorithm is V42BIS */
OSMO_ASSERT(comp_entity->algo == V42BIS);
/* Find pcomp_index */
pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
/* Run decompression algo */
rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state);
LOGP(DSNDCP, LOGL_DEBUG,
"Data expansion done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}
/* Compress packet */
int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(pcomp);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Data compression entity list: comp_entities=%p\n", comp_entities);
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
*pcomp = 0;
return len;
}
/* Note: Only data compression entities may appear in
* data compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
/* Note: Currently V42BIS is the only compression method we
* support, so the only allowed algorithm is V42BIS */
OSMO_ASSERT(comp_entity->algo == V42BIS);
/* Run compression algo */
rc = v42bis_compress_unitdata(&pcomp_index, data, len,
comp_entity->state);
/* Find pcomp value */
*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp);
LOGP(DSNDCP, LOGL_DEBUG,
"Data compression done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}

View File

@@ -0,0 +1,280 @@
/* GPRS SNDCP header compression handler */
/* (C) 2016 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sndcp_xid.h>
#include <openbsc/slhc.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sndcp_comp.h>
#include <openbsc/gprs_sndcp_pcomp.h>
/* Initalize header compression */
int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a new header compression
* entity is created by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
OSMO_ASSERT(comp_field);
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
&& comp_entity->algo == RFC_1144) {
comp_entity->state =
slhc_init(ctx, comp_field->rfc1144_params->s01 + 1,
comp_field->rfc1144_params->s01 + 1);
LOGP(DSNDCP, LOGL_INFO,
"RFC1144 header compression initalized.\n");
return 0;
}
/* Just in case someone tries to initalize an unknown or unsupported
* header compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Terminate header compression */
void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a header compression
* entity is deleted by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
&& comp_entity->algo == RFC_1144) {
if (comp_entity->state) {
slhc_free((struct slcompress *)comp_entity->state);
comp_entity->state = NULL;
}
LOGP(DSNDCP, LOGL_INFO,
"RFC1144 header compression terminated.\n");
return;
}
/* Just in case someone tries to terminate an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Compress a packet using Van Jacobson RFC1144 header compression */
static int rfc1144_compress(uint8_t *pcomp_index, uint8_t *data,
unsigned int len, struct slcompress *comp)
{
uint8_t *comp_ptr;
int compr_len;
uint8_t *data_o;
/* Create a working copy of the incoming data */
data_o = talloc_zero_size(comp, len);
memcpy(data_o, data, len);
/* Run compressor */
compr_len = slhc_compress(comp, data, len, data_o, &comp_ptr, 0);
/* Generate pcomp_index */
if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
*pcomp_index = 2;
data_o[0] &= ~SL_TYPE_COMPRESSED_TCP;
memcpy(data, data_o, compr_len);
} else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) ==
SL_TYPE_UNCOMPRESSED_TCP) {
*pcomp_index = 1;
data_o[0] &= 0x4F;
memcpy(data, data_o, compr_len);
} else
*pcomp_index = 0;
talloc_free(data_o);
return compr_len;
}
/* Expand a packet using Van Jacobson RFC1144 header compression */
static int rfc1144_expand(uint8_t *data, unsigned int len, uint8_t pcomp_index,
struct slcompress *comp)
{
int data_decompressed_len;
int type;
/* Note: this function should never be called with pcomp_index=0,
* since this condition is already filtered
* out by gprs_sndcp_pcomp_expand() */
/* Determine the data type by the PCOMP index */
switch (pcomp_index) {
case 0:
type = SL_TYPE_IP;
case 1:
type = SL_TYPE_UNCOMPRESSED_TCP;
break;
case 2:
type = SL_TYPE_COMPRESSED_TCP;
break;
default:
LOGP(DSNDCP, LOGL_ERROR,
"rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
pcomp_index);
type = SL_TYPE_IP;
break;
}
/* Restore the original version nibble on
* marked uncompressed packets */
if (type == SL_TYPE_UNCOMPRESSED_TCP) {
/* Just in case the phone tags uncompressed tcp-data
* (normally this is handled by pcomp so there is
* no need for tagging the data) */
data[0] &= 0x4F;
data_decompressed_len = slhc_remember(comp, data, len);
return data_decompressed_len;
}
/* Uncompress compressed packets */
else if (type == SL_TYPE_COMPRESSED_TCP) {
data_decompressed_len = slhc_uncompress(comp, data, len);
return data_decompressed_len;
}
/* Regular or unknown packets will not be touched */
return len;
}
/* Expand packet header */
int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Header compression entity list: comp_entities=%p\n",
comp_entities);
LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp);
/* Skip on pcomp=0 */
if (pcomp == 0) {
return len;
}
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
return len;
}
/* Note: Only protocol compression entities may appear in
* protocol compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
/* Note: Currently RFC1144 is the only compression method we
* support, so the only allowed algorithm is RFC1144 */
OSMO_ASSERT(comp_entity->algo == RFC_1144);
/* Find pcomp_index */
pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
/* Run decompression algo */
rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state);
slhc_i_status(comp_entity->state);
slhc_o_status(comp_entity->state);
LOGP(DSNDCP, LOGL_DEBUG,
"Header expansion done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}
/* Compress packet header */
int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(pcomp);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Header compression entity list: comp_entities=%p\n",
comp_entities);
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
*pcomp = 0;
return len;
}
/* Note: Only protocol compression entities may appear in
* protocol compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
/* Note: Currently RFC1144 is the only compression method we
* support, so the only allowed algorithm is RFC1144 */
OSMO_ASSERT(comp_entity->algo == RFC_1144);
/* Run compression algo */
rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state);
slhc_i_status(comp_entity->state);
slhc_o_status(comp_entity->state);
/* Find pcomp value */
*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
LOGP(DSNDCP, LOGL_DEBUG,
"Header compression done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}

View File

@@ -35,8 +35,7 @@
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_llc.h>
#include "gprs_sndcp.h"
#include <openbsc/gprs_sndcp.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>

File diff suppressed because it is too large Load Diff

View File

@@ -165,7 +165,7 @@ static int gprs_subscr_tx_gsup_message(struct gsm_subscriber *subscr,
if (strlen(gsup_msg->imsi) == 0 && subscr)
strncpy(gsup_msg->imsi, subscr->imsi, sizeof(gsup_msg->imsi) - 1);
gsup_msg->cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
osmo_gsup_encode(msg, gsup_msg);
LOGGSUBSCRP(LOGL_INFO, subscr,

View File

@@ -325,8 +325,6 @@ int main(int argc, char **argv)
}
/* start telnet after reading config for vty_get_bind_addr() */
LOGP(DGTPHUB, LOGL_NOTICE, "VTY at %s %d\n",
vty_get_bind_addr(), OSMO_VTY_PORT_GTPHUB);
rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(),
OSMO_VTY_PORT_GTPHUB);
if (rc < 0)

View File

@@ -94,7 +94,7 @@ static void cdr_log_mm(struct sgsn_instance *inst, const char *ev,
mmctx->imsi,
mmctx->imei,
mmctx->msisdn,
mmctx->cell_id,
mmctx->gb.cell_id,
mmctx->ra.lac,
mmctx->hlr,
ev);
@@ -179,7 +179,7 @@ static void cdr_log_pdp(struct sgsn_instance *inst, const char *ev,
pdp->mm ? pdp->mm->imsi : "N/A",
pdp->mm ? pdp->mm->imei : "N/A",
pdp->mm ? pdp->mm->msisdn : "N/A",
pdp->mm ? pdp->mm->cell_id : -1,
pdp->mm ? pdp->mm->gb.cell_id : -1,
pdp->mm ? pdp->mm->ra.lac : -1,
pdp->mm ? pdp->mm->hlr : "N/A",
ev,

View File

@@ -34,6 +34,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include "bscconfig.h"
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
@@ -47,6 +49,12 @@
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gprs_sndcp.h>
#ifdef BUILD_IU
#include <openbsc/iu.h>
#include <osmocom/ranap/ranap_ies_defs.h>
#endif
#include <gtp.h>
#include <pdp.h>
@@ -218,17 +226,16 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
/* SGSN address for user plane */
/* SGSN address for user plane
* Default to the control plane addr for now. If we are connected to a
* hnbgw via IuPS we'll need to send a PDP context update with the
* correct IP address after the RAB Assignment is complete */
pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
/* Assume we are a GERAN system */
pdp->rattype.l = 1;
pdp->rattype.v[0] = 2;
pdp->rattype_given = 1;
/* Include RAI and ULI all the time */
/* Routing Area Identifier with LAC and RAC fixed values, as
* requested in 29.006 7.3.1 */
pdp->rai_given = 1;
pdp->rai.l = 6;
raid = mmctx->ra;
@@ -236,10 +243,24 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
raid.rac = 0xFF;
gsm48_construct_ra(pdp->rai.v, &raid);
pdp->userloc_given = 1;
pdp->userloc.l = 8;
pdp->userloc.v[0] = 0; /* CGI for GERAN */
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->cell_id);
pdp->rattype.l = 1;
pdp->rattype_given = 1;
switch (mmctx->ran_type) {
case MM_CTX_T_GERAN_Gb:
case MM_CTX_T_GERAN_Iu:
pdp->rattype.v[0] = 2;
/* User Location Information */
pdp->userloc_given = 1;
pdp->userloc.l = 8;
pdp->userloc.v[0] = 0; /* CGI for GERAN */
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id);
break;
case MM_CTX_T_UTRAN_Iu:
pdp->rattype.v[0] = 1;
/* FIXME: Optional User Location Information with SAI */
break;
}
/* include the IMEI(SV) */
pdp->imeisv_given = 1;
@@ -304,10 +325,36 @@ static const struct cause_map gtp2sm_cause_map[] = {
{ 0, 0 }
};
static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
{
struct sgsn_signal_data sig_data;
int rc;
struct gprs_llc_lle *lle;
/* Inform others about it */
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pctx;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data);
/* Send PDP CTX ACT to MS */
rc = gsm48_tx_gsm_act_pdp_acc(pctx);
if (rc < 0)
return rc;
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Send SNDCP XID to MS */
lle = &pctx->mm->gb.llme->lle[pctx->sapi];
rc = sndcp_sn_xid_req(lle,pctx->nsapi);
if (rc < 0)
return rc;
}
return 0;
}
/* The GGSN has confirmed the creation of a PDP Context */
static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
{
struct sgsn_signal_data sig_data;
struct sgsn_pdp_ctx *pctx = cbp;
uint8_t reject_cause;
@@ -340,16 +387,23 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
goto reject;
}
/* Activate the SNDCP layer */
sndcp_sm_activate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi);
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Activate the SNDCP layer */
sndcp_sm_activate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi);
return send_act_pdp_cont_acc(pctx);
} else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) {
#ifdef BUILD_IU
/* Activate a radio bearer */
iu_rab_act_ps(pdp->nsapi, pctx, 1);
return 0;
#else
return -ENOTSUP;
#endif
}
/* Inform others about it */
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pctx;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data);
/* Send PDP CTX ACT to MS */
return gsm48_tx_gsm_act_pdp_acc(pctx);
LOGP(DGPRS, LOGL_ERROR, "Unknown ran_type %d\n",
pctx->mm->ran_type);
reject_cause = GSM_CAUSE_PROTO_ERR_UNSPEC;
reject:
/*
@@ -372,6 +426,79 @@ reject:
return EOF;
}
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen)
{
pdp->lib->gsnlu.l = alen;
memcpy(pdp->lib->gsnlu.v, addr, alen);
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
}
#ifdef BUILD_IU
/* Callback for RAB assignment response */
int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies)
{
uint8_t rab_id;
bool require_pdp_update = false;
struct sgsn_pdp_ctx *pdp = NULL;
RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem;
rab_id = item->rAB_ID.buf[0];
pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id);
if (!pdp) {
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id);
return -1;
}
if (item->transportLayerAddress) {
LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf,
item->transportLayerAddress->size));
switch (item->transportLayerAddress->size) {
case 7:
/* It must be IPv4 inside a X213 NSAP */
memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4);
break;
case 4:
/* It must be a raw IPv4 address */
memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4);
break;
case 16:
/* TODO: It must be a raw IPv6 address */
case 19:
/* TODO: It must be IPv6 inside a X213 NSAP */
default:
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown "
"transport layer address size %u\n",
item->transportLayerAddress->size);
return -1;
}
require_pdp_update = true;
}
/* The TEI on the RNC side might have changed, too */
if (item->iuTransportAssociation &&
item->iuTransportAssociation->present == RANAP_IuTransportAssociation_PR_gTP_TEI &&
item->iuTransportAssociation->choice.gTP_TEI.buf &&
item->iuTransportAssociation->choice.gTP_TEI.size >= 4) {
uint32_t tei = osmo_load32be(item->iuTransportAssociation->choice.gTP_TEI.buf);
LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n",
pdp->lib->teid_own, tei);
pdp->lib->teid_own = tei;
require_pdp_update = true;
}
if (require_pdp_update)
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
if (pdp->state != PDP_STATE_CR_CONF) {
send_act_pdp_cont_acc(pdp);
pdp->state = PDP_STATE_CR_CONF;
}
return 0;
}
#endif
/* Confirmation of a PDP Context Delete */
static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
{
@@ -387,8 +514,17 @@ static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_DEACT, &sig_data);
if (pctx->mm) {
/* Deactivate the SNDCP layer */
sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi);
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Deactivate the SNDCP layer */
sndcp_sm_deactivate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi);
} else {
#ifdef BUILD_IU
/* Deactivate radio bearer */
iu_rab_deact(pctx->mm->iu.ue_ctx, 1);
#else
return -ENOTSUP;
#endif
}
/* Confirm deactivation of PDP context to MS */
rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
@@ -517,13 +653,25 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
return -EIO;
}
if (mm->ran_type == MM_CTX_T_UTRAN_Iu) {
#ifdef BUILD_IU
/* Ignore the packet for now and page the UE to get the RAB
* reestablished */
iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac);
return 0;
#else
return -ENOTSUP;
#endif
}
msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP");
ud = msgb_put(msg, len);
memcpy(ud, packet, len);
msgb_tlli(msg) = mm->tlli;
msgb_bvci(msg) = mm->bvci;
msgb_nsei(msg) = mm->nsei;
msgb_tlli(msg) = mm->gb.tlli;
msgb_bvci(msg) = mm->gb.bvci;
msgb_nsei(msg) = mm->gb.nsei;
switch (mm->mm_state) {
case GMM_REGISTERED_SUSPENDED:
@@ -531,12 +679,12 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
memset(&pinfo, 0, sizeof(pinfo));
pinfo.mode = BSSGP_PAGING_PS;
pinfo.scope = BSSGP_PAGING_BVCI;
pinfo.bvci = mm->bvci;
pinfo.bvci = mm->gb.bvci;
pinfo.imsi = mm->imsi;
pinfo.ptmsi = &mm->p_tmsi;
pinfo.drx_params = mm->drx_parms;
pinfo.qos[0] = 0; // FIXME
bssgp_tx_paging(mm->nsei, 0, &pinfo);
bssgp_tx_paging(mm->gb.nsei, 0, &pinfo);
rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]);
/* FIXME: queue the packet we received from GTP */
break;
@@ -544,7 +692,7 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
break;
default:
LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state "
"%u\n", mm->tlli, mm->mm_state);
"%u\n", mm->gb.tlli, mm->mm_state);
msgb_free(msg);
return -1;
}
@@ -557,7 +705,7 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
/* It is easier to have a global count */
pdp->cdr_bytes_out += len;
return sndcp_unitdata_req(msg, &mm->llme->lle[pdp->sapi],
return sndcp_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi],
pdp->nsapi, mm);
}

View File

@@ -21,6 +21,7 @@
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
@@ -55,6 +56,8 @@
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/iu.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/ports.h>
@@ -281,6 +284,26 @@ static struct log_info_cat gprs_categories[] = {
.description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DRANAP] = {
.name = "DRANAP",
.description = "RAN Application Part (RANAP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DSUA] = {
.name = "DSUA",
.description = "SCCP User Adaptation (SUA)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DSLHC] = {
.name = "DSLHC",
.description = "RFC1144 TCP/IP Header compression (SLHC)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DV42BIS] = {
.name = "DV42BIS",
.description = "V.42bis data compression (SNDCP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
}
};
static const struct log_info gprs_log_info = {
@@ -289,6 +312,13 @@ static const struct log_info gprs_log_info = {
.num_cat = ARRAY_SIZE(gprs_categories),
};
/* Implement the extern asn_debug from libasn1c to indicate whether the ASN.1
* binary code decoded and encoded during Iu communication should be logged to
* stderr. See osmocom's libasn1c, asn_internal.h, at "if (asn_debug)":
* http://git.osmocom.org/libasn1c/tree/include/asn1c/asn_internal.h */
int asn_debug = 0;
int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum iu_event_type type, void *data);
int main(int argc, char **argv)
{
@@ -296,6 +326,7 @@ int main(int argc, char **argv)
struct gsm_network dummy_network;
int rc;
srand(time(NULL));
tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
@@ -314,6 +345,9 @@ int main(int argc, char **argv)
osmo_stats_vty_add_cmds(&gprs_log_info);
sgsn_vty_init();
ctrl_vty_init(tall_bsc_ctx);
#ifdef BUILD_IU
iu_vty_init(&asn_debug);
#endif
handle_options(argc, argv);
@@ -330,6 +364,7 @@ int main(int argc, char **argv)
bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi;
gprs_llc_init("/usr/local/lib/osmocom/crypt/");
sgsn_rate_ctr_init();
sgsn_inst_init();
gprs_ns_vty_init(bssgp_nsi);
@@ -347,8 +382,6 @@ int main(int argc, char **argv)
}
/* start telnet after reading config for vty_get_bind_addr() */
LOGP(DGPRS, LOGL_NOTICE, "VTY at %s %d\n",
vty_get_bind_addr(), OSMO_VTY_PORT_SGSN);
rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
vty_get_bind_addr(), OSMO_VTY_PORT_SGSN);
if (rc < 0)
@@ -404,6 +437,10 @@ int main(int argc, char **argv)
}
}
#ifdef BUILD_IU
iu_init(tall_bsc_ctx, "127.0.0.2", 14001, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event);
#endif
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {

View File

@@ -39,7 +39,7 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/misc.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/abis/ipa.h>
#include <pdp.h>
@@ -210,15 +210,19 @@ static int config_write_sgsn(struct vty *vty)
for (server = sgsn->ares_servers; server; server = server->next)
vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
vty_out(vty, " auth-policy %s%s",
get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
VTY_NEWLINE);
if (g_cfg->cipher != GPRS_ALGO_GEA0)
vty_out(vty, " encryption %s%s",
get_value_string(gprs_cipher_names, g_cfg->cipher),
VTY_NEWLINE);
if (g_cfg->gsup_server_addr.sin_addr.s_addr)
vty_out(vty, " gsup remote-ip %s%s",
inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE);
if (g_cfg->gsup_server_port)
vty_out(vty, " gsup remote-port %d%s",
g_cfg->gsup_server_port, VTY_NEWLINE);
vty_out(vty, " auth-policy %s%s",
get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
VTY_NEWLINE);
vty_out(vty, " gsup oap-id %d%s",
(int)g_cfg->oap.client_id, VTY_NEWLINE);
@@ -265,6 +269,34 @@ static int config_write_sgsn(struct vty *vty)
vty_out(vty, " timer t3395 %d%s", g_cfg->timers.T3395, VTY_NEWLINE);
vty_out(vty, " timer t3397 %d%s", g_cfg->timers.T3397, VTY_NEWLINE);
if (g_cfg->pcomp_rfc1144.active) {
vty_out(vty, " compression rfc1144 active slots %d%s",
g_cfg->pcomp_rfc1144.s01 + 1, VTY_NEWLINE);
} else if (g_cfg->pcomp_rfc1144.passive) {
vty_out(vty, " compression rfc1144 passive%s", VTY_NEWLINE);
} else
vty_out(vty, " no compression rfc1144%s", VTY_NEWLINE);
if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 1) {
vty_out(vty,
" compression v42bis active direction sgsn codewords %d strlen %d%s",
g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
VTY_NEWLINE);
} else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 2) {
vty_out(vty,
" compression v42bis active direction ms codewords %d strlen %d%s",
g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
VTY_NEWLINE);
} else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 3) {
vty_out(vty,
" compression v42bis active direction both codewords %d strlen %d%s",
g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
VTY_NEWLINE);
} else if (g_cfg->dcomp_v42bis.passive) {
vty_out(vty, " compression v42bis passive%s", VTY_NEWLINE);
} else
vty_out(vty, " no compression v42bis%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -431,12 +463,12 @@ static void vty_dump_mmctx(struct vty *vty, const char *pfx,
vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s",
pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE);
vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s HLR: %s",
pfx, mm->msisdn, mm->tlli, mm->hlr, VTY_NEWLINE);
pfx, mm->msisdn, mm->gb.tlli, mm->hlr, VTY_NEWLINE);
vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, "
"Cell ID: %u%s", pfx,
get_value_string(gprs_mm_st_strs, mm->mm_state),
mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac,
mm->cell_id, VTY_NEWLINE);
mm->gb.cell_id, VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", mm->ctrg);
@@ -553,6 +585,30 @@ DEFUN(imsi_acl, cfg_imsi_acl_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_encrypt, cfg_encrypt_cmd,
"encryption (GEA0|GEA1|GEA2|GEA3|GEA4)",
"Set encryption algorithm for SGSN\n"
"Use GEA0 (no encryption)\n"
"Use GEA1\nUse GEA2\nUse GEA3\nUse GEA4\n")
{
if (!g_cfg->require_authentication) {
vty_out(vty, "%% unable to use encryption without "
"authentication: adjust auth-policy%s", VTY_NEWLINE);
return CMD_WARNING;
}
enum gprs_ciph_algo c = get_string_value(gprs_cipher_names, argv[0]);
if (!gprs_cipher_supported(c)) {
vty_out(vty, "%% cipher %s is unsupported in current version%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->cipher = c;
return CMD_SUCCESS;
}
DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
"auth-policy (accept-all|closed|acl-only|remote)",
"Autorization Policy of SGSN\n"
@@ -563,6 +619,17 @@ DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
{
int val = get_string_value(sgsn_auth_pol_strs, argv[0]);
OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE);
if (val == SGSN_AUTH_POLICY_REMOTE) {
const char *err = "%% auth-policy remote requires";
if (!g_cfg->gsup_server_addr.sin_addr.s_addr) {
vty_out(vty, "%s 'gsup remote-ip'%s", err, VTY_NEWLINE);
return CMD_WARNING;
}
if (!g_cfg->gsup_server_port) {
vty_out(vty, "%s 'gsup remote-port'%s", err, VTY_NEWLINE);
return CMD_WARNING;
}
}
g_cfg->auth_policy = val;
g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE);
g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE);
@@ -1035,6 +1102,94 @@ DEFUN(cfg_cdr_interval, cfg_cdr_interval_cmd,
return CMD_SUCCESS;
}
#define COMPRESSION_STR "Configure compression\n"
DEFUN(cfg_no_comp_rfc1144, cfg_no_comp_rfc1144_cmd,
"no compression rfc1144",
NO_STR COMPRESSION_STR "disable rfc1144 TCP/IP header compression\n")
{
g_cfg->pcomp_rfc1144.active = 0;
g_cfg->pcomp_rfc1144.passive = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_comp_rfc1144, cfg_comp_rfc1144_cmd,
"compression rfc1144 active slots <1-256>",
COMPRESSION_STR
"RFC1144 Header compresion scheme\n"
"Compression is actively proposed\n"
"Number of compression state slots\n"
"Number of compression state slots\n")
{
g_cfg->pcomp_rfc1144.active = 1;
g_cfg->pcomp_rfc1144.passive = 1;
g_cfg->pcomp_rfc1144.s01 = atoi(argv[0]) - 1;
return CMD_SUCCESS;
}
DEFUN(cfg_comp_rfc1144p, cfg_comp_rfc1144p_cmd,
"compression rfc1144 passive",
COMPRESSION_STR
"RFC1144 Header compresion scheme\n"
"Compression is available on request\n")
{
g_cfg->pcomp_rfc1144.active = 0;
g_cfg->pcomp_rfc1144.passive = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_no_comp_v42bis, cfg_no_comp_v42bis_cmd,
"no compression v42bis",
NO_STR COMPRESSION_STR "disable V.42bis data compression\n")
{
g_cfg->dcomp_v42bis.active = 0;
g_cfg->dcomp_v42bis.passive = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_comp_v42bis, cfg_comp_v42bis_cmd,
"compression v42bis active direction (ms|sgsn|both) codewords <512-65535> strlen <6-250>",
COMPRESSION_STR
"V.42bis data compresion scheme\n"
"Compression is actively proposed\n"
"Direction in which the compression shall be active (p0)\n"
"Compress ms->sgsn direction only\n"
"Compress sgsn->ms direction only\n"
"Both directions\n"
"Number of codewords (p1)\n"
"Number of codewords\n"
"Maximum string length (p2)\n" "Maximum string length\n")
{
g_cfg->dcomp_v42bis.active = 1;
g_cfg->dcomp_v42bis.passive = 1;
switch (argv[0][0]) {
case 'm':
g_cfg->dcomp_v42bis.p0 = 1;
break;
case 's':
g_cfg->dcomp_v42bis.p0 = 2;
break;
case 'b':
g_cfg->dcomp_v42bis.p0 = 3;
break;
}
g_cfg->dcomp_v42bis.p1 = atoi(argv[1]);
g_cfg->dcomp_v42bis.p2 = atoi(argv[2]);
return CMD_SUCCESS;
}
DEFUN(cfg_comp_v42bisp, cfg_comp_v42bisp_cmd,
"compression v42bis passive",
COMPRESSION_STR
"V.42bis data compresion scheme\n"
"Compression is available on request\n")
{
g_cfg->dcomp_v42bis.active = 0;
g_cfg->dcomp_v42bis.passive = 1;
return CMD_SUCCESS;
}
int sgsn_vty_init(void)
{
install_element_ve(&show_sgsn_cmd);
@@ -1060,6 +1215,7 @@ int sgsn_vty_init(void)
install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
install_element(SGSN_NODE, &cfg_auth_policy_cmd);
install_element(SGSN_NODE, &cfg_encrypt_cmd);
install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
install_element(SGSN_NODE, &cfg_gsup_oap_id_cmd);
@@ -1088,6 +1244,12 @@ int sgsn_vty_init(void)
install_element(SGSN_NODE, &cfg_sgsn_T3395_cmd);
install_element(SGSN_NODE, &cfg_sgsn_T3397_cmd);
install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd);
install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd);
install_element(SGSN_NODE, &cfg_comp_rfc1144p_cmd);
install_element(SGSN_NODE, &cfg_no_comp_v42bis_cmd);
install_element(SGSN_NODE, &cfg_comp_v42bis_cmd);
install_element(SGSN_NODE, &cfg_comp_v42bisp_cmd);
return 0;
}

813
openbsc/src/gprs/slhc.c Normal file
View File

@@ -0,0 +1,813 @@
/*
* Routines to compress and uncompress tcp packets (for transmission
* over low speed serial lines).
*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
* - Initial distribution.
*
*
* modified for KA9Q Internet Software Package by
* Katie Stevens (dkstevens@ucdavis.edu)
* University of California, Davis
* Computing Services
* - 01-31-90 initial adaptation (from 1.19)
* PPP.05 02-15-90 [ks]
* PPP.08 05-02-90 [ks] use PPP protocol field to signal compression
* PPP.15 09-90 [ks] improve mbuf handling
* PPP.16 11-02 [karn] substantially rewritten to use NOS facilities
*
* - Feb 1991 Bill_Simpson@um.cc.umich.edu
* variable number of conversation slots
* allow zero or one slots
* separate routines
* status display
* - Jul 1994 Dmitry Gorodchanin
* Fixes for memory leaks.
* - Oct 1994 Dmitry Gorodchanin
* Modularization.
* - Jan 1995 Bjorn Ekwall
* Use ip_fast_csum from ip.h
* - July 1995 Christos A. Polyzols
* Spotted bug in tcp option checking
*
*
* This module is a difficult issue. It's clearly inet code but it's also clearly
* driver code belonging close to PPP and SLIP
*/
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
#include <openbsc/slhc.h>
#include <openbsc/debug.h>
#define ERR_PTR(x) x
static unsigned char *encode(unsigned char *cp, unsigned short n);
static long decode(unsigned char **cpp);
static unsigned char * put16(unsigned char *cp, unsigned short x);
static unsigned short pull16(unsigned char **cpp);
/* Replacement for kernel space function ip_fast_csum() */
static uint16_t ip_fast_csum(uint8_t *iph, int ihl)
{
int i;
uint16_t temp;
uint32_t accumulator = 0xFFFF;
for(i=0;i<ihl*2;i++)
{
temp = ((*iph) << 8)&0xFF00;
iph++;
temp |= (*iph)&0xFF;
iph++;
accumulator+=temp;
if(accumulator>0xFFFF)
{
accumulator++;
accumulator&=0xFFFF;
}
}
return (uint16_t)(htons(~accumulator)&0xFFFF);
}
/* Replacement for kernel space function put_unaligned() */
static void put_unaligned(uint16_t val, void *ptr)
{
memcpy(ptr,&val,sizeof(val));
}
/* Allocate compression data structure
* slots must be in range 0 to 255 (zero meaning no compression)
* Returns pointer to structure or ERR_PTR() on error.
*/
struct slcompress *
slhc_init(const void *ctx, int rslots, int tslots)
{
register short i;
register struct cstate *ts;
struct slcompress *comp;
if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255)
return NULL;
comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress));
if (! comp)
goto out_fail;
if (rslots > 0) {
size_t rsize = rslots * sizeof(struct cstate);
comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize);
if (! comp->rstate)
goto out_free;
comp->rslot_limit = rslots - 1;
}
if (tslots > 0) {
size_t tsize = tslots * sizeof(struct cstate);
comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize);
if (! comp->tstate)
goto out_free2;
comp->tslot_limit = tslots - 1;
}
comp->xmit_oldest = 0;
comp->xmit_current = 255;
comp->recv_current = 255;
/*
* don't accept any packets with implicit index until we get
* one with an explicit index. Otherwise the uncompress code
* will try to use connection 255, which is almost certainly
* out of range
*/
comp->flags |= SLF_TOSS;
if ( tslots > 0 ) {
ts = comp->tstate;
for(i = comp->tslot_limit; i > 0; --i){
ts[i].cs_this = i;
ts[i].next = &(ts[i - 1]);
}
ts[0].next = &(ts[comp->tslot_limit]);
ts[0].cs_this = 0;
}
return comp;
out_free2:
talloc_free(comp->rstate);
out_free:
talloc_free(comp);
out_fail:
return NULL;
}
/* Free a compression data structure */
void
slhc_free(struct slcompress *comp)
{
DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n");
if ( comp == NULLSLCOMPR )
return;
if ( comp->tstate != NULLSLSTATE )
talloc_free(comp->tstate );
if ( comp->rstate != NULLSLSTATE )
talloc_free( comp->rstate );
talloc_free( comp );
}
/* Put a short in host order into a char array in network order */
static inline unsigned char *
put16(unsigned char *cp, unsigned short x)
{
*cp++ = x >> 8;
*cp++ = x;
return cp;
}
/* Encode a number */
static unsigned char *
encode(unsigned char *cp, unsigned short n)
{
if(n >= 256 || n == 0){
*cp++ = 0;
cp = put16(cp,n);
} else {
*cp++ = n;
}
DEBUGP(DSLHC, "encode(): n=%04x\n",n);
return cp;
}
/* Pull a 16-bit integer in host order from buffer in network byte order */
static unsigned short
pull16(unsigned char **cpp)
{
short rval;
rval = *(*cpp)++;
rval <<= 8;
rval |= *(*cpp)++;
return rval;
}
/* Decode a number */
static long
decode(unsigned char **cpp)
{
register int x;
x = *(*cpp)++;
if(x == 0){
return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
} else {
return x & 0xff; /* -1 if PULLCHAR returned error */
}
}
/*
* icp and isize are the original packet.
* ocp is a place to put a copy if necessary.
* cpp is initially a pointer to icp. If the copy is used,
* change it to ocp.
*/
int
slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
unsigned char *ocp, unsigned char **cpp, int compress_cid)
{
register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
register struct cstate *lcs = ocs;
register struct cstate *cs = lcs->next;
register unsigned long deltaS, deltaA;
register short changes = 0;
int hlen;
unsigned char new_seq[16];
register unsigned char *cp = new_seq;
struct iphdr *ip;
struct tcphdr *th, *oth;
__sum16 csum;
/*
* Don't play with runt packets.
*/
if(isize<sizeof(struct iphdr))
return isize;
ip = (struct iphdr *) icp;
/* Bail if this packet isn't TCP, or is an IP fragment */
if (ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) {
/* Send as regular IP */
if(ip->protocol != IPPROTO_TCP)
comp->sls_o_nontcp++;
else
comp->sls_o_tcp++;
DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n");
return isize;
}
/* Extract TCP header */
th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
hlen = ip->ihl*4 + th->doff*4;
/* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
* some other control bit is set). Also uncompressible if
* it's a runt.
*/
if(hlen > isize || th->syn || th->fin || th->rst ||
! (th->ack)){
/* TCP connection stuff; send as regular IP */
comp->sls_o_tcp++;
DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n");
return isize;
}
/*
* Packet is compressible -- we're going to send either a
* COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way,
* we need to locate (or create) the connection state.
*
* States are kept in a circularly linked list with
* xmit_oldest pointing to the end of the list. The
* list is kept in lru order by moving a state to the
* head of the list whenever it is referenced. Since
* the list is short and, empirically, the connection
* we want is almost always near the front, we locate
* states via linear search. If we don't find a state
* for the datagram, the oldest state is (re-)used.
*/
DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n");
for ( ; ; ) {
if( ip->saddr == cs->cs_ip.saddr
&& ip->daddr == cs->cs_ip.daddr
&& th->source == cs->cs_tcp.source
&& th->dest == cs->cs_tcp.dest)
goto found;
/* if current equal oldest, at end of list */
if ( cs == ocs )
break;
lcs = cs;
cs = cs->next;
comp->sls_o_searches++;
}
/*
* Didn't find it -- re-use oldest cstate. Send an
* uncompressed packet that tells the other side what
* connection number we're using for this conversation.
*
* Note that since the state list is circular, the oldest
* state points to the newest and we only need to set
* xmit_oldest to update the lru linkage.
*/
DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n");
comp->sls_o_misses++;
comp->xmit_oldest = lcs->cs_this;
goto uncompressed;
found:
DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n");
/*
* Found it -- move to the front on the connection list.
*/
if(lcs == ocs) {
/* found at most recently used */
} else if (cs == ocs) {
/* found at least recently used */
comp->xmit_oldest = lcs->cs_this;
} else {
/* more than 2 elements */
lcs->next = cs->next;
cs->next = ocs->next;
ocs->next = cs;
}
/*
* Make sure that only what we expect to change changed.
* Check the following:
* IP protocol version, header length & type of service.
* The "Don't fragment" bit.
* The time-to-live field.
* The TCP header length.
* IP options, if any.
* TCP options, if any.
* If any of these things are different between the previous &
* current datagram, we send the current datagram `uncompressed'.
*/
oth = &cs->cs_tcp;
/* Display a little more debug information about which of the
* header fields changed unexpectedly */
if(ip->version != cs->cs_ip.version)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n");
if(ip->ihl != cs->cs_ip.ihl)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n");
if(ip->tos != cs->cs_ip.tos)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n");
if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)))
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n");
if(ip->ttl != cs->cs_ip.ttl)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n");
if(th->doff != cs->cs_tcp.doff)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n",
osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4));
}
if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt,
((th->doff)-5)*4));
}
if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
|| ip->tos != cs->cs_ip.tos
|| (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))
|| ip->ttl != cs->cs_ip.ttl
|| th->doff != cs->cs_tcp.doff
|| (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
|| (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){
DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n");
goto uncompressed;
}
/*
* Figure out which of the changing fields changed. The
* receiver expects changes in the order: urgent, window,
* ack, seq (the order minimizes the number of temporaries
* needed in this section of code).
*/
if(th->urg){
deltaS = ntohs(th->urg_ptr);
DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_U;
} else if(th->urg_ptr != oth->urg_ptr){
/* argh! URG not set but urp changed -- a sensible
* implementation should never do this but RFC793
* doesn't prohibit the change so we have to deal
* with it. */
DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n");
goto uncompressed;
}
if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_W;
}
if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
if(deltaA > 0x0000ffff) {
DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n");
goto uncompressed;
}
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n");
cp = encode(cp,deltaA);
changes |= NEW_A;
}
if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
if(deltaS > 0x0000ffff) {
DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n");
goto uncompressed;
}
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_S;
}
switch(changes){
case 0: /* Nothing changed. If this packet contains data and the
* last one didn't, this is probably a data packet following
* an ack (normal on an interactive connection) and we send
* it compressed. Otherwise it's probably a retransmit,
* retransmitted ack or window probe. Send it uncompressed
* in case the other side missed the compressed version.
*/
if(ip->tot_len != cs->cs_ip.tot_len &&
ntohs(cs->cs_ip.tot_len) == hlen)
break;
DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n");
goto uncompressed;
case SPECIAL_I:
case SPECIAL_D:
/* actual changes match one of our special case encodings --
* send packet uncompressed.
*/
DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n");
goto uncompressed;
case NEW_S|NEW_A:
if(deltaS == deltaA &&
deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
/* special case for echoed terminal traffic */
DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n");
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
changes = SPECIAL_I;
cp = new_seq;
}
break;
case NEW_S:
if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
/* special case for data xfer */
DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n");
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
changes = SPECIAL_D;
cp = new_seq;
}
break;
}
deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
if(deltaS != 1){
DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_I;
}
if(th->psh) {
DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n");
changes |= TCP_PUSH_BIT;
}
/* Grab the cksum before we overwrite it below. Then update our
* state with this packet's header.
*/
csum = th->check;
memcpy(&cs->cs_ip,ip,20);
memcpy(&cs->cs_tcp,th,20);
/* We want to use the original packet as our compressed packet.
* (cp - new_seq) is the number of bytes we need for compressed
* sequence numbers. In addition we need one byte for the change
* mask, one for the connection id and two for the tcp checksum.
* So, (cp - new_seq) + 4 bytes of header are needed.
*/
deltaS = cp - new_seq;
if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
cp = ocp;
*cpp = ocp;
DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n");
*cp++ = changes | NEW_C;
*cp++ = cs->cs_this;
comp->xmit_current = cs->cs_this;
} else {
cp = ocp;
*cpp = ocp;
*cp++ = changes;
}
*(__sum16 *)cp = csum;
cp += 2;
/* deltaS is now the size of the change section of the compressed header */
DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
memcpy(cp,new_seq,deltaS); /* Write list of deltas */
memcpy(cp+deltaS,icp+hlen,isize-hlen);
comp->sls_o_compressed++;
ocp[0] |= SL_TYPE_COMPRESSED_TCP;
return isize - hlen + deltaS + (cp - ocp);
/* Update connection state cs & send uncompressed packet (i.e.,
* a regular ip/tcp packet but with the 'conversation id' we hope
* to use on future compressed packets in the protocol field).
*/
uncompressed:
DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n");
memcpy(&cs->cs_ip,ip,20);
memcpy(&cs->cs_tcp,th,20);
if (ip->ihl > 5)
memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
if (th->doff > 5)
memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
comp->xmit_current = cs->cs_this;
comp->sls_o_uncompressed++;
memcpy(ocp, icp, isize);
*cpp = ocp;
ocp[9] = cs->cs_this;
ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
return isize;
}
int
slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
{
register int changes;
long x;
register struct tcphdr *thp;
register struct iphdr *ip;
register struct cstate *cs;
int len, hdrlen;
unsigned char *cp = icp;
/* We've got a compressed packet; read the change byte */
comp->sls_i_compressed++;
if(isize < 3){
comp->sls_i_error++;
return 0;
}
changes = *cp++;
if(changes & NEW_C){
/* Make sure the state index is in range, then grab the state.
* If we have a good state index, clear the 'discard' flag.
*/
x = *cp++; /* Read conn index */
if(x < 0 || x > comp->rslot_limit)
goto bad;
comp->flags &=~ SLF_TOSS;
comp->recv_current = x;
} else {
/* this packet has an implicit state index. If we've
* had a line error since the last time we got an
* explicit state index, we have to toss the packet. */
if(comp->flags & SLF_TOSS){
comp->sls_i_tossed++;
return 0;
}
}
cs = &comp->rstate[comp->recv_current];
thp = &cs->cs_tcp;
ip = &cs->cs_ip;
thp->check = *(__sum16 *)cp;
cp += 2;
thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
/*
* we can use the same number for the length of the saved header and
* the current one, because the packet wouldn't have been sent
* as compressed unless the options were the same as the previous one
*/
hdrlen = ip->ihl * 4 + thp->doff * 4;
switch(changes & SPECIALS_MASK){
case SPECIAL_I: /* Echoed terminal traffic */
DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n");
{
register short i;
i = ntohs(ip->tot_len) - hdrlen;
thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
thp->seq = htonl( ntohl(thp->seq) + i);
}
break;
case SPECIAL_D: /* Unidirectional data */
DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n");
thp->seq = htonl( ntohl(thp->seq) +
ntohs(ip->tot_len) - hdrlen);
break;
default:
DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n");
if(changes & NEW_U){
thp->urg = 1;
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->urg_ptr = htons(x);
} else
thp->urg = 0;
if(changes & NEW_W){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->window = htons( ntohs(thp->window) + x);
}
if(changes & NEW_A){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
}
if(changes & NEW_S){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->seq = htonl( ntohl(thp->seq) + x);
}
break;
}
if(changes & NEW_I){
if((x = decode(&cp)) == -1) {
goto bad;
}
ip->id = htons (ntohs (ip->id) + x);
} else
ip->id = htons (ntohs (ip->id) + 1);
/*
* At this point, cp points to the first byte of data in the
* packet. Put the reconstructed TCP and IP headers back on the
* packet. Recalculate IP checksum (but not TCP checksum).
*/
len = isize - (cp - icp);
if (len < 0)
goto bad;
len += hdrlen;
ip->tot_len = htons(len);
ip->check = 0;
DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n");
memmove(icp + hdrlen, cp, len - hdrlen);
cp = icp;
memcpy(cp, ip, 20);
cp += 20;
if (ip->ihl > 5) {
memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
cp += (ip->ihl - 5) * 4;
}
put_unaligned(ip_fast_csum(icp, ip->ihl),
&((struct iphdr *)icp)->check);
memcpy(cp, thp, 20);
cp += 20;
if (thp->doff > 5) {
memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
cp += ((thp->doff) - 5) * 4;
}
return len;
bad:
DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n");
comp->sls_i_error++;
return slhc_toss( comp );
}
int
slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
{
register struct cstate *cs;
unsigned ihl;
unsigned char index;
if(isize < 20) {
/* The packet is shorter than a legal IP header */
comp->sls_i_runt++;
DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n");
return slhc_toss( comp );
}
/* Peek at the IP header's IHL field to find its length */
ihl = icp[0] & 0xf;
if(ihl < 20 / 4){
/* The IP header length field is too small */
comp->sls_i_runt++;
DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n");
return slhc_toss( comp );
}
index = icp[9];
icp[9] = IPPROTO_TCP;
if (ip_fast_csum(icp, ihl)) {
/* Bad IP header checksum; discard */
comp->sls_i_badcheck++;
DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n");
return slhc_toss( comp );
}
if(index > comp->rslot_limit) {
comp->sls_i_error++;
DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n");
return slhc_toss(comp);
}
/* Update local state */
cs = &comp->rstate[comp->recv_current = index];
comp->flags &=~ SLF_TOSS;
memcpy(&cs->cs_ip,icp,20);
memcpy(&cs->cs_tcp,icp + ihl*4,20);
if (ihl > 5)
memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
if (cs->cs_tcp.doff > 5)
memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
/* Put headers back on packet
* Neither header checksum is recalculated
*/
comp->sls_i_uncompressed++;
return isize;
}
int
slhc_toss(struct slcompress *comp)
{
DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n");
if ( comp == NULLSLCOMPR )
return 0;
comp->flags |= SLF_TOSS;
return 0;
}
void slhc_i_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
comp->sls_i_compressed,
comp->sls_i_uncompressed,
comp->sls_i_error,
comp->sls_i_tossed);
}
}
void slhc_o_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n",
comp->sls_o_compressed,
comp->sls_o_uncompressed,
comp->sls_o_tcp,
comp->sls_o_nontcp,
comp->sls_o_searches,
comp->sls_o_misses);
}
}

767
openbsc/src/gprs/v42bis.c Normal file
View File

@@ -0,0 +1,767 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* v42bis.c
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED.
Currently it performs the core compression and decompression functions OK.
However, a number of the bells and whistles in V.42bis are incomplete. */
/*! \file */
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <assert.h>
#include <openbsc/v42bis.h>
#include <openbsc/v42bis_private.h>
#include <openbsc/debug.h>
#include <osmocom/core/talloc.h>
#define span_log(x,y,msg, ...) DEBUGP(DV42BIS,msg, ##__VA_ARGS__)
#define span_log_init(x,y,z)
#define span_log_set_protocol(x,y)
#define FALSE 0
#define TRUE 1
/* Fixed parameters from the spec. */
/* Character size (bits) */
#define V42BIS_N3 8
/* Number of characters in the alphabet */
#define V42BIS_N4 256
/* Index number of first dictionary entry used to store a string */
#define V42BIS_N5 (V42BIS_N4 + V42BIS_N6)
/* Number of control codewords */
#define V42BIS_N6 3
/* V.42bis/9.2 */
#define V42BIS_ESC_STEP 51
/* Compreeibility monitoring parameters for assessing automated switches between
transparent and compressed mode */
#define COMPRESSIBILITY_MONITOR (256*V42BIS_N3)
#define COMPRESSIBILITY_MONITOR_HYSTERESIS 11
/* Control code words in compressed mode */
enum
{
V42BIS_ETM = 0, /* Enter transparent mode */
V42BIS_FLUSH = 1, /* Flush data */
V42BIS_STEPUP = 2 /* Step up codeword size */
};
/* Command codes in transparent mode */
enum
{
V42BIS_ECM = 0, /* Enter compression mode */
V42BIS_EID = 1, /* Escape character in data */
V42BIS_RESET = 2 /* Force reinitialisation */
};
static __inline__ void push_octet(v42bis_comp_state_t *s, int octet)
{
s->output_buf[s->output_octet_count++] = (uint8_t) octet;
if (s->output_octet_count >= s->max_output_len)
{
s->handler(s->user_data, s->output_buf, s->output_octet_count);
s->output_octet_count = 0;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len)
{
int i;
int chunk;
i = 0;
while ((s->output_octet_count + len - i) >= s->max_output_len)
{
chunk = s->max_output_len - s->output_octet_count;
memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
s->handler(s->user_data, s->output_buf, s->max_output_len);
s->output_octet_count = 0;
i += chunk;
}
chunk = len - i;
if (chunk > 0)
{
memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
s->output_octet_count += chunk;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code)
{
s->bit_buffer |= code << s->bit_count;
s->bit_count += s->v42bis_parm_c2;
while (s->bit_count >= 8)
{
push_octet(s, s->bit_buffer & 0xFF);
s->bit_buffer >>= 8;
s->bit_count -= 8;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_octet_alignment(v42bis_comp_state_t *s)
{
if ((s->bit_count & 7))
{
s->bit_count += (8 - (s->bit_count & 7));
while (s->bit_count >= 8)
{
push_octet(s, s->bit_buffer & 0xFF);
s->bit_buffer >>= 8;
s->bit_count -= 8;
}
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void flush_octets(v42bis_comp_state_t *s)
{
if (s->output_octet_count > 0)
{
s->handler(s->user_data, s->output_buf, s->output_octet_count);
s->output_octet_count = 0;
}
}
/*- End of function --------------------------------------------------------*/
static void dictionary_init(v42bis_comp_state_t *s)
{
int i;
memset(s->dict, 0, sizeof(s->dict));
for (i = 0; i < V42BIS_N4; i++)
s->dict[i + V42BIS_N6].node_octet = i;
s->v42bis_parm_c1 = V42BIS_N5;
s->v42bis_parm_c2 = V42BIS_N3 + 1;
s->v42bis_parm_c3 = V42BIS_N4 << 1;
s->last_matched = 0;
s->update_at = 0;
s->last_added = 0;
s->bit_buffer = 0;
s->bit_count = 0;
s->flushed_length = 0;
s->string_length = 0;
s->escape_code = 0;
s->transparent = TRUE;
s->escaped = FALSE;
s->compression_performance = COMPRESSIBILITY_MONITOR;
}
/*- End of function --------------------------------------------------------*/
static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
{
uint16_t e;
if (at == 0)
return octet + V42BIS_N6;
e = s->dict[at].child;
while (e)
{
if (s->dict[e].node_octet == octet)
return e;
e = s->dict[e].next;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
{
uint16_t newx;
uint16_t next;
uint16_t e;
newx = s->v42bis_parm_c1;
s->dict[newx].node_octet = octet;
s->dict[newx].parent = at;
s->dict[newx].child = 0;
s->dict[newx].next = s->dict[at].child;
s->dict[at].child = newx;
next = newx;
/* 6.5 Recovering a dictionary entry to use next */
do
{
/* 6.5(a) and (b) */
if (++next == s->v42bis_parm_n2)
next = V42BIS_N5;
}
while (s->dict[next].child);
/* 6.5(c) We need to reuse a leaf node */
if (s->dict[next].parent)
{
/* 6.5(d) Detach the leaf node from its parent, and re-use it */
e = s->dict[next].parent;
if (s->dict[e].child == next)
{
s->dict[e].child = s->dict[next].next;
}
else
{
e = s->dict[e].child;
while (s->dict[e].next != next)
e = s->dict[e].next;
s->dict[e].next = s->dict[next].next;
}
}
s->v42bis_parm_c1 = next;
return newx;
}
/*- End of function --------------------------------------------------------*/
static void send_string(v42bis_comp_state_t *s)
{
push_octets(s, s->string, s->string_length);
s->string_length = 0;
s->flushed_length = 0;
}
/*- End of function --------------------------------------------------------*/
static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code)
{
int i;
uint16_t p;
/* Work out the length */
for (i = 0, p = code; p; i++)
p = s->dict[p].parent;
s->string_length += i;
/* Now expand the known length of string */
i = s->string_length - 1;
for (p = code; p; )
{
s->string[i--] = s->dict[p].node_octet;
p = s->dict[p].parent;
}
}
/*- End of function --------------------------------------------------------*/
static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code)
{
int i;
/* Update compressibility metric */
/* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */
s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR);
if (s->transparent)
{
for (i = 0; i < s->string_length; i++)
{
push_octet(s, s->string[i]);
if (s->string[i] == s->escape_code)
{
push_octet(s, V42BIS_EID);
s->escape_code += V42BIS_ESC_STEP;
}
}
}
else
{
/* Allow for any escape octets in the string */
for (i = 0; i < s->string_length; i++)
{
if (s->string[i] == s->escape_code)
s->escape_code += V42BIS_ESC_STEP;
}
/* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
while (code >= s->v42bis_parm_c3)
{
/* We need to increase the codeword size */
/* 7.4(a) */
push_compressed_code(s, V42BIS_STEPUP);
/* 7.4(b) */
s->v42bis_parm_c2++;
/* 7.4(c) */
s->v42bis_parm_c3 <<= 1;
/* 7.4(d) this might need to be repeated, so we loop */
}
/* 7.5 Transfer - output the last state of the string */
push_compressed_code(s, code);
}
s->string_length = 0;
s->flushed_length = 0;
}
/*- End of function --------------------------------------------------------*/
static void go_compressed(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
if (!s->transparent)
return;
span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n");
/* Switch out of transparent now, between codes. We need to send the octet which did not
match, just before switching. */
if (s->last_matched)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
}
push_octet(s, s->escape_code);
push_octet(s, V42BIS_ECM);
s->bit_buffer = 0;
s->transparent = FALSE;
}
/*- End of function --------------------------------------------------------*/
static void go_transparent(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
if (s->transparent)
return;
span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n");
/* Switch into transparent now, between codes, and the unmatched octet should
go out in transparent mode, just below */
if (s->last_matched)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
}
s->last_added = 0;
push_compressed_code(s, V42BIS_ETM);
push_octet_alignment(s);
s->transparent = TRUE;
}
/*- End of function --------------------------------------------------------*/
static void monitor_for_mode_change(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
switch (s->compression_mode)
{
case V42BIS_COMPRESSION_MODE_DYNAMIC:
/* 7.8 Data compressibility test */
if (s->transparent)
{
if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS)
{
/* 7.8.1 Transition to compressed mode */
go_compressed(ss);
}
}
else
{
if (s->compression_performance > COMPRESSIBILITY_MONITOR)
{
/* 7.8.2 Transition to transparent mode */
go_transparent(ss);
}
}
/* 7.8.3 Reset function - TODO */
break;
case V42BIS_COMPRESSION_MODE_ALWAYS:
if (s->transparent)
go_compressed(ss);
break;
case V42BIS_COMPRESSION_MODE_NEVER:
if (!s->transparent)
go_transparent(ss);
break;
}
}
/*- End of function --------------------------------------------------------*/
static int v42bis_comp_init(v42bis_comp_state_t *s,
int p1,
int p2,
put_msg_func_t handler,
void *user_data,
int max_output_len)
{
memset(s, 0, sizeof(*s));
s->v42bis_parm_n2 = p1;
s->v42bis_parm_n7 = p2;
s->handler = handler;
s->user_data = user_data;
s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH;
s->output_octet_count = 0;
dictionary_init(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
static int comp_exit(v42bis_comp_state_t *s)
{
s->v42bis_parm_n2 = 0;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
v42bis_comp_state_t *s;
int i;
uint16_t code;
s = &ss->compress;
if (!s->v42bis_parm_p0)
{
/* Compression is off - just push the incoming data out */
push_octets(s, buf, len);
return 0;
}
for (i = 0; i < len; )
{
/* 6.4 Add the string to the dictionary */
if (s->update_at)
{
if (match_octet(s, s->update_at, buf[i]) == 0)
s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]);
s->update_at = 0;
}
/* Match string */
while (i < len)
{
code = match_octet(s, s->last_matched, buf[i]);
if (code == 0)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
if (code == s->last_added)
{
s->last_added = 0;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
s->last_matched = code;
/* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
created by the last invocation of the string matching procedure, then the
next character shall be read and appended to the string and this step
repeated. */
s->string[s->string_length++] = buf[i++];
/* 6.4(a) The string must not exceed N7 in length */
if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
{
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
}
monitor_for_mode_change(ss);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
int len;
s = &ss->compress;
if (s->update_at)
return 0;
if (s->last_matched)
{
len = s->string_length;
send_encoded_data(s, s->last_matched);
s->flushed_length += len;
}
if (!s->transparent)
{
s->update_at = s->last_matched;
s->last_matched = 0;
s->flushed_length = 0;
push_compressed_code(s, V42BIS_FLUSH);
push_octet_alignment(s);
}
flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
v42bis_comp_state_t *s;
int i;
int j;
int yyy;
uint16_t code;
uint16_t p;
uint8_t ch;
uint8_t in;
s = &ss->decompress;
if (!s->v42bis_parm_p0)
{
/* Compression is off - just push the incoming data out */
push_octets(s, buf, len);
return 0;
}
for (i = 0; i < len; )
{
if (s->transparent)
{
in = buf[i];
if (s->escaped)
{
/* Command */
s->escaped = FALSE;
switch (in)
{
case V42BIS_ECM:
/* Enter compressed mode */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n");
send_string(s);
s->transparent = FALSE;
s->update_at = s->last_matched;
s->last_matched = 0;
i++;
continue;
case V42BIS_EID:
/* Escape symbol */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n");
in = s->escape_code;
s->escape_code += V42BIS_ESC_STEP;
break;
case V42BIS_RESET:
/* Reset dictionary */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n");
/* TODO: */
send_string(s);
dictionary_init(s);
i++;
continue;
default:
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in);
return -1;
}
}
else if (in == s->escape_code)
{
s->escaped = TRUE;
i++;
continue;
}
yyy = TRUE;
for (j = 0; j < 2 && yyy; j++)
{
if (s->update_at)
{
if (match_octet(s, s->update_at, in) == 0)
s->last_added = add_octet_to_dictionary(s, s->update_at, in);
s->update_at = 0;
}
code = match_octet(s, s->last_matched, in);
if (code == 0)
{
s->update_at = s->last_matched;
send_string(s);
s->last_matched = 0;
}
else if (code == s->last_added)
{
s->last_added = 0;
send_string(s);
s->last_matched = 0;
}
else
{
s->last_matched = code;
s->string[s->string_length++] = in;
if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
{
send_string(s);
s->last_matched = 0;
}
i++;
yyy = FALSE;
}
}
}
else
{
/* Get code from input */
while (s->bit_count < s->v42bis_parm_c2 && i < len)
{
s->bit_buffer |= buf[i++] << s->bit_count;
s->bit_count += 8;
}
if (s->bit_count < s->v42bis_parm_c2)
continue;
code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1);
s->bit_buffer >>= s->v42bis_parm_c2;
s->bit_count -= s->v42bis_parm_c2;
if (code < V42BIS_N6)
{
/* We have a control code. */
switch (code)
{
case V42BIS_ETM:
/* Enter transparent mode */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n");
s->bit_count = 0;
s->transparent = TRUE;
s->last_matched = 0;
s->last_added = 0;
break;
case V42BIS_FLUSH:
/* Flush signal */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n");
s->bit_count = 0;
break;
case V42BIS_STEPUP:
/* Increase code word size */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n");
s->v42bis_parm_c2++;
s->v42bis_parm_c3 <<= 1;
if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3))
return -1;
break;
}
continue;
}
/* Regular codeword */
if (code == s->v42bis_parm_c1)
return -1;
expand_codeword_to_string(s, code);
if (s->update_at)
{
ch = s->string[0];
if ((p = match_octet(s, s->update_at, ch)) == 0)
{
s->last_added = add_octet_to_dictionary(s, s->update_at, ch);
if (code == s->v42bis_parm_c1)
return -1;
}
else if (p == s->last_added)
{
s->last_added = 0;
}
}
s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code;
/* Allow for any escapes which may be in this string */
for (j = 0; j < s->string_length; j++)
{
if (s->string[j] == s->escape_code)
s->escape_code += V42BIS_ESC_STEP;
}
send_string(s);
}
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
int len;
s = &ss->decompress;
len = s->string_length;
send_string(s);
s->flushed_length += len;
flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode)
{
s->compress.compression_mode = mode;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
v42bis_state_t *s,
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
put_msg_func_t encode_handler,
void *encode_user_data,
int max_encode_len,
put_msg_func_t decode_handler,
void *decode_user_data,
int max_decode_len)
{
int ret;
if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE || negotiated_p1 > 65535)
return NULL;
if (negotiated_p2 < V42BIS_MIN_STRING_SIZE || negotiated_p2 > V42BIS_MAX_STRING_SIZE)
return NULL;
if (s == NULL)
{
if ((s = (v42bis_state_t *) talloc_zero_size(ctx,sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "V.42bis");
if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len)))
return NULL;
if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len)))
{
comp_exit(&s->compress);
return NULL;
}
s->compress.v42bis_parm_p0 = negotiated_p0 & 2;
s->decompress.v42bis_parm_p0 = negotiated_p0 & 1;
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s)
{
comp_exit(&s->compress);
comp_exit(&s->decompress);
talloc_free(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@@ -1,31 +1,67 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS)
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS)
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
bin_PROGRAMS = abisip-find ipaccess-config ipaccess-proxy
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
abisip_find_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libmsc/libmsc.a \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS)
abisip_find_SOURCES = abisip-find.c
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c network_listen.c
OSMO_LIBS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(NULL)
bin_PROGRAMS = \
abisip-find \
ipaccess-config \
ipaccess-proxy \
$(NULL)
abisip_find_LDADD = \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(NULL)
abisip_find_SOURCES = \
abisip-find.c \
$(NULL)
ipaccess_config_SOURCES = \
ipaccess-config.c \
ipaccess-firmware.c \
network_listen.c \
$(NULL)
# FIXME: resolve the bogus dependencies patched around here:
ipaccess_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBCRYPT) $(OSMO_LIBS)
ipaccess_config_LDADD = \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libxsc/libxsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBCRYPT) \
$(OSMO_LIBS) \
$(NULL)
ipaccess_proxy_SOURCES = ipaccess-proxy.c
ipaccess_proxy_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libmsc/libmsc.a \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS)
ipaccess_proxy_SOURCES = \
ipaccess-proxy.c \
$(NULL)
ipaccess_proxy_LDADD = \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(NULL)

View File

@@ -38,7 +38,7 @@
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <openbsc/ipaccess.h>
#include <openbsc/gsm_data.h>
#include <openbsc/osmo_bsc.h>
#include <osmocom/abis/e1_input.h>
#include <openbsc/abis_nm.h>
#include <openbsc/signal.h>
@@ -983,7 +983,7 @@ int main(int argc, char **argv)
}
libosmo_abis_init(tall_ctx_config);
bsc_gsmnet = gsm_network_init(1, 1, NULL);
bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, NULL);
if (!bsc_gsmnet)
exit(1);

View File

@@ -1,27 +1,53 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS)
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
noinst_LIBRARIES = libbsc.a
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \
abis_om2000.c abis_om2000_vty.c \
abis_rsl.c bsc_rll.c \
paging.c \
bts_ericsson_rbs2000.c \
bts_ipaccess_nanobts.c \
bts_siemens_bs11.c \
bts_nokia_site.c \
bts_unknown.c \
bts_sysmobts.c \
chan_alloc.c \
handover_decision.c handover_logic.c meas_rep.c \
rest_octets.c system_information.c \
e1_config.c \
bsc_api.c bsc_msc.c bsc_vty.c \
gsm_04_08_utils.c \
bsc_init.c bts_init.c bsc_rf_ctrl.c \
arfcn_range_encode.c bsc_ctrl_commands.c \
bsc_ctrl_lookup.c \
net_init.c
noinst_LIBRARIES = \
libbsc.a \
$(NULL)
libbsc_a_SOURCES = \
abis_nm.c \
abis_nm_vty.c \
abis_om2000.c \
abis_om2000_vty.c \
abis_rsl.c \
bsc_rll.c \
paging.c \
bts_ericsson_rbs2000.c \
bts_ipaccess_nanobts.c \
bts_siemens_bs11.c \
bts_nokia_site.c \
bts_unknown.c \
bts_sysmobts.c \
chan_alloc.c \
handover_decision.c \
handover_logic.c \
meas_rep.c \
rest_octets.c \
system_information.c \
e1_config.c \
bsc_api.c \
bsc_msc.c bsc_vty.c \
gsm_04_08_utils.c \
gsm_04_80_utils.c \
bsc_init.c \
bts_init.c \
bsc_rf_ctrl.c \
arfcn_range_encode.c \
bsc_ctrl_commands.c \
bsc_ctrl_lookup.c \
net_init.c \
bsc_dyn_ts.c \
$(NULL)

View File

@@ -1494,6 +1494,7 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb,
switch (chan_comb) {
case NM_CHANC_TCHHalf:
case NM_CHANC_TCHHalf2:
case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
/* not supported */
*reason = "TCH/H is not supported.";
return -EINVAL;
@@ -1590,6 +1591,7 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb,
case NM_CHANC_TCHHalf:
case NM_CHANC_IPAC_TCHFull_TCHHalf:
case NM_CHANC_IPAC_TCHFull_PDCH:
case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
return 0;
default:
*reason = "TS1 must carry a CBCH, SDCCH or TCH.";
@@ -1621,6 +1623,7 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb,
return 0;
case NM_CHANC_IPAC_PDCH:
case NM_CHANC_IPAC_TCHFull_PDCH:
case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
if (ts->trx->nr == 0)
return 0;
else {

View File

@@ -94,7 +94,7 @@ DEFUN(oml_class_inst, oml_class_inst_cmd,
struct oml_node_state *oms;
int bts_nr = atoi(argv[0]);
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;
@@ -128,7 +128,7 @@ DEFUN(oml_classnum_inst, oml_classnum_inst_cmd,
struct oml_node_state *oms;
int bts_nr = atoi(argv[0]);
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;

View File

@@ -1123,13 +1123,27 @@ static uint8_t pchan2comb(enum gsm_phys_chan_config pchan)
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_H:
case GSM_PCHAN_PDCH:
case GSM_PCHAN_TCH_F_PDCH:
return 8;
default:
return 0;
}
}
static uint8_t ts2comb(struct gsm_bts_trx_ts *ts)
{
switch (ts->pchan) {
case GSM_PCHAN_TCH_F_PDCH:
if (ts->flags & TS_F_PDCH_ACTIVE)
return pchan2comb(GSM_PCHAN_PDCH);
else
return pchan2comb(GSM_PCHAN_TCH_F);
case GSM_PCHAN_TCH_F_TCH_H_PDCH:
return pchan2comb(ts->dyn.pchan_is);
default:
return pchan2comb(ts->pchan);
}
}
static int put_freq_list(uint8_t *buf, uint16_t arfcn)
{
buf[0] = 0x00; /* TX/RX address */
@@ -1179,7 +1193,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ);
msgb_tv_put(msg, OM2K_DEI_COMBINATION, pchan2comb(ts->pchan));
msgb_tv_put(msg, OM2K_DEI_COMBINATION, ts2comb(ts));
msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr);
msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list);
msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn);

View File

@@ -82,7 +82,7 @@ DEFUN(om2k_class_inst, om2k_class_inst_cmd,
struct oml_node_state *oms;
int bts_nr = atoi(argv[0]);
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;
@@ -122,7 +122,7 @@ DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd,
struct oml_node_state *oms;
int bts_nr = atoi(argv[0]);
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;

View File

@@ -35,6 +35,8 @@
#include <openbsc/bsc_rll.h>
#include <openbsc/debug.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/meas_rep.h>
@@ -53,6 +55,9 @@ enum sacch_deact {
static int rsl_send_imm_assignment(struct gsm_lchan *lchan);
static void error_timeout_cb(void *data);
static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts);
static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc);
static void dyn_ts_switchover_complete(struct gsm_lchan *lchan);
static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
struct gsm_meas_rep *resp)
@@ -104,53 +109,23 @@ static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh,
dh->ie_chan = RSL_IE_CHAN_NR;
}
/* determine logical channel based on TRX and channel number IE */
struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr)
/* call rsl_lchan_lookup and set the log context */
static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
const char *log_name)
{
struct gsm_lchan *lchan;
uint8_t ts_nr = chan_nr & 0x07;
uint8_t cbits = chan_nr >> 3;
uint8_t lch_idx;
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
int rc;
struct gsm_lchan *lchan = rsl_lchan_lookup(trx, chan_nr, &rc);
if (cbits == 0x01) {
lch_idx = 0; /* TCH/F */
if (ts->pchan != GSM_PCHAN_TCH_F &&
ts->pchan != GSM_PCHAN_PDCH &&
ts->pchan != GSM_PCHAN_TCH_F_PDCH)
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x1e) == 0x02) {
lch_idx = cbits & 0x1; /* TCH/H */
if (ts->pchan != GSM_PCHAN_TCH_H)
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x1c) == 0x04) {
lch_idx = cbits & 0x3; /* SDCCH/4 */
if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 &&
ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x18) == 0x08) {
lch_idx = cbits & 0x7; /* SDCCH/8 */
if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C &&
ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH)
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
lch_idx = 0;
if (ts->pchan != GSM_PCHAN_CCCH &&
ts->pchan != GSM_PCHAN_CCCH_SDCCH4 &&
ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
/* FIXME: we should not return first sdcch4 !!! */
} else {
LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr);
if (!lchan) {
LOGP(DRSL, LOGL_ERROR, "%sunknown chan_nr=0x%02x\n",
log_name, chan_nr);
return NULL;
}
lchan = &ts->lchan[lch_idx];
if (rc < 0)
LOGP(DRSL, LOGL_ERROR, "%s %smismatching chan_nr=0x%02x\n",
gsm_ts_and_pchan_name(lchan->ts), log_name, chan_nr);
log_set_context(BSC_CTX_LCHAN, lchan);
if (lchan->conn)
log_set_context(BSC_CTX_SUBSCR, lchan->conn->subscr);
@@ -206,10 +181,6 @@ static void lchan_act_tmr_cb(void *data)
{
struct gsm_lchan *lchan = data;
LOGP(DRSL, LOGL_ERROR,
"%s Timeout during activation. Marked as broken.\n",
gsm_lchan_name(lchan));
rsl_lchan_mark_broken(lchan, "activation timeout");
lchan_free(lchan);
}
@@ -218,10 +189,6 @@ static void lchan_deact_tmr_cb(void *data)
{
struct gsm_lchan *lchan = data;
LOGP(DRSL, LOGL_ERROR,
"%s Timeout during deactivation! Marked as broken.\n",
gsm_lchan_name(lchan));
rsl_lchan_mark_broken(lchan, "de-activation timeout");
lchan_free(lchan);
}
@@ -345,10 +312,11 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
memset(cm, 0, sizeof(*cm));
/* FIXME: what to do with data calls ? */
if (lchan->ts->trx->bts->network->dtx_enabled)
cm->dtx_dtu = 0x03;
else
cm->dtx_dtu = 0x00;
cm->dtx_dtu = 0;
if (lchan->ts->trx->bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
cm->dtx_dtu |= RSL_CMOD_DTXu;
if (lchan->ts->trx->bts->dtxd)
cm->dtx_dtu |= RSL_CMOD_DTXd;
/* set TCH Speech/Data */
cm->spd_ind = lchan->rsl_cmode;
@@ -371,6 +339,9 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
case GSM_LCHAN_NONE:
case GSM_LCHAN_UNKNOWN:
default:
LOGP(DRSL, LOGL_ERROR,
"unsupported activation lchan->type %u %s\n",
lchan->type, gsm_lchant_name(lchan->type));
return -EINVAL;
}
@@ -404,6 +375,9 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
cm->chan_rate = RSL_CMOD_SP_NT_6k0;
break;
default:
LOGP(DRSL, LOGL_ERROR,
"unsupported lchan->tch_mode %u\n",
lchan->tch_mode);
return -EINVAL;
}
break;
@@ -433,9 +407,15 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
cm->chan_rate = RSL_CMOD_CSD_T_32000;
break;
default:
LOGP(DRSL, LOGL_ERROR,
"unsupported lchan->csd_mode %u\n",
lchan->csd_mode);
return -EINVAL;
}
default:
LOGP(DRSL, LOGL_ERROR,
"unsupported lchan->tch_mode %u\n",
lchan->tch_mode);
return -EINVAL;
}
@@ -449,6 +429,49 @@ static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg)
lchan->mr_bts_lv + 1);
}
static enum gsm_phys_chan_config pchan_for_lchant(enum gsm_chan_t type)
{
switch (type) {
case GSM_LCHAN_TCH_F:
return GSM_PCHAN_TCH_F;
case GSM_LCHAN_TCH_H:
return GSM_PCHAN_TCH_H;
case GSM_LCHAN_NONE:
case GSM_LCHAN_PDTCH:
/* TODO: so far lchan->type is NONE in PDCH mode. PDTCH is only
* used in osmo-bts. Maybe set PDTCH and drop the NONE case
* here. */
return GSM_PCHAN_PDCH;
default:
return GSM_PCHAN_UNKNOWN;
}
}
/*! Tx simplified channel activation message for non-standard PDCH type. */
static int rsl_chan_activate_lchan_as_pdch(struct gsm_lchan *lchan)
{
struct msgb *msg;
struct abis_rsl_dchan_hdr *dh;
/* This might be called after release of the second lchan of a TCH/H,
* but PDCH activation must always happen on the first lchan. Make sure
* the calling code passes the correct lchan. */
OSMO_ASSERT(lchan == lchan->ts->lchan);
rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
msg = rsl_msgb_alloc();
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
dh->chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH);
msgb_tv_put(msg, RSL_IE_ACT_TYPE, RSL_ACT_OSMO_PDCH);
msg->dst = lchan->ts->trx->rsl_link;
return abis_rsl_sendmsg(msg);
}
/* Chapter 8.4.1 */
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
uint8_t ho_ref)
@@ -459,13 +482,89 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
uint8_t *len;
uint8_t ta;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
struct rsl_ie_chan_mode cm;
struct gsm48_chan_desc cd;
/* If a TCH_F/PDCH TS is in PDCH mode, deactivate PDCH first. */
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
&& (lchan->ts->flags & TS_F_PDCH_ACTIVE)) {
/* store activation type and handover reference */
lchan->dyn.act_type = act_type;
lchan->dyn.ho_ref = ho_ref;
return rsl_ipacc_pdch_activate(lchan->ts, 0);
}
/*
* If necessary, release PDCH on dynamic TS. Note that sending a
* release here is only necessary when in PDCH mode; for TCH types, an
* RSL RF Chan Release is initiated by the BTS when a voice call ends,
* so when we reach this, it will already be released. If a dyn TS is
* in PDCH mode, it is still active and we need to initiate a release
* from the BSC side here.
*
* If pchan_is != pchan_want, the PDCH has already been taken down and
* the switchover now needs to enable the TCH lchan.
*
* To switch a dyn TS between TCH/H and TCH/F, it is sufficient to send
* a chan activ with the new lchan type, because it will already be
* released.
*/
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
&& lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
&& lchan->ts->dyn.pchan_is == lchan->ts->dyn.pchan_want) {
enum gsm_phys_chan_config pchan_want;
pchan_want = pchan_for_lchant(lchan->type);
if (lchan->ts->dyn.pchan_is != pchan_want) {
/*
* Make sure to record on lchan[0] so that we'll find
* it after the PDCH release.
*/
struct gsm_lchan *lchan0 = lchan->ts->lchan;
lchan0->dyn.act_type = act_type,
lchan0->dyn.ho_ref = ho_ref;
lchan0->dyn.rqd_ref = lchan->rqd_ref;
lchan0->dyn.rqd_ta = lchan->rqd_ta;
lchan->rqd_ref = NULL;
lchan->rqd_ta = 0;
DEBUGP(DRSL, "%s saved rqd_ref=%p ta=%u\n",
gsm_lchan_name(lchan0), lchan0->rqd_ref,
lchan0->rqd_ta);
return dyn_ts_switchover_start(lchan->ts, pchan_want);
}
}
DEBUGP(DRSL, "%s Tx RSL Channel Activate with act_type=%s\n",
gsm_ts_and_pchan_name(lchan->ts),
rsl_act_type_name(act_type));
if (act_type == RSL_ACT_OSMO_PDCH) {
if (lchan->ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) {
LOGP(DRSL, LOGL_ERROR,
"%s PDCH channel activation only allowed on %s\n",
gsm_ts_and_pchan_name(lchan->ts),
gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH));
return -EINVAL;
}
return rsl_chan_activate_lchan_as_pdch(lchan);
}
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
&& lchan->ts->dyn.pchan_want == GSM_PCHAN_PDCH) {
LOGP(DRSL, LOGL_ERROR,
"%s Expected PDCH activation kind\n",
gsm_ts_and_pchan_name(lchan->ts));
return -EINVAL;
}
rc = channel_mode_from_lchan(&cm, lchan);
if (rc < 0)
if (rc < 0) {
LOGP(DRSL, LOGL_ERROR,
"%s Cannot find channel mode from lchan type\n",
gsm_ts_and_pchan_name(lchan->ts));
return rc;
}
rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
ta = lchan->rqd_ta;
@@ -479,7 +578,12 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
msg = rsl_msgb_alloc();
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
dh->chan_nr = chan_nr;
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
dh->chan_nr = gsm_lchan_as_pchan2chan_nr(
lchan, lchan->ts->dyn.pchan_want);
else
dh->chan_nr = gsm_lchan2chan_nr(lchan);
msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
@@ -618,6 +722,40 @@ int rsl_deact_sacch(struct gsm_lchan *lchan)
return abis_rsl_sendmsg(msg);
}
static bool dyn_ts_should_switch_to_pdch(struct gsm_bts_trx_ts *ts)
{
int ss;
if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH)
return false;
if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE)
return false;
/* Already in PDCH mode? */
if (ts->dyn.pchan_is == GSM_PCHAN_PDCH)
return false;
/* See if all lchans are released. */
for (ss = 0; ss < ts_subslots(ts); ss++) {
struct gsm_lchan *lc = &ts->lchan[ss];
if (lc->state != LCHAN_S_NONE) {
DEBUGP(DRSL, "%s lchan %u still in use"
" (type=%s,state=%s)\n",
gsm_ts_and_pchan_name(ts), lc->nr,
gsm_lchant_name(lc->type),
gsm_lchans_name(lc->state));
/* An lchan is still used. */
return false;
}
}
/* All channels are released, go to PDCH mode. */
DEBUGP(DRSL, "%s back to PDCH\n",
gsm_ts_and_pchan_name(ts));
return true;
}
static void error_timeout_cb(void *data)
{
struct gsm_lchan *lchan = data;
@@ -630,6 +768,14 @@ static void error_timeout_cb(void *data)
/* go back to the none state */
LOGP(DRSL, LOGL_INFO, "%s is back in operation.\n", gsm_lchan_name(lchan));
rsl_lchan_set_state(lchan, LCHAN_S_NONE);
/* Put PDCH channel back into PDCH mode, if GPRS is enabled */
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
&& lchan->ts->trx->bts->gprs.mode != BTS_GPRS_NONE)
rsl_ipacc_pdch_activate(lchan->ts, 1);
if (dyn_ts_should_switch_to_pdch(lchan->ts))
dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH);
}
static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan);
@@ -646,7 +792,7 @@ static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error,
osmo_timer_del(&lchan->T3109);
if (lchan->state == LCHAN_S_REL_ERR) {
LOGP(DRSL, LOGL_NOTICE, "%s is in error state not sending release.\n",
LOGP(DRSL, LOGL_NOTICE, "%s is in error state, not sending release.\n",
gsm_lchan_name(lchan));
return -1;
}
@@ -659,7 +805,11 @@ static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error,
msg->lchan = lchan;
msg->dst = lchan->ts->trx->rsl_link;
DEBUGP(DRSL, "%s RF Channel Release CMD due error %d\n", gsm_lchan_name(lchan), error);
if (error)
DEBUGP(DRSL, "%s RF Channel Release due to error: %d\n",
gsm_lchan_name(lchan), error);
else
DEBUGP(DRSL, "%s RF Channel Release\n", gsm_lchan_name(lchan));
if (error) {
/*
@@ -714,6 +864,7 @@ static int rsl_rf_chan_release_err(struct gsm_lchan *lchan)
static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan));
@@ -729,13 +880,15 @@ static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan)
* will be sent. So let's "repair" the channel.
*/
if (lchan->state == LCHAN_S_BROKEN) {
int do_free = is_sysmobts_v2(lchan->ts->trx->bts);
int do_free = is_sysmobts_v2(ts->trx->bts);
LOGP(DRSL, LOGL_NOTICE,
"%s CHAN REL ACK for broken channel. %s.\n",
gsm_lchan_name(lchan),
do_free ? "Releasing it" : "Keeping it broken");
if (do_free)
do_lchan_free(lchan);
if (dyn_ts_should_switch_to_pdch(lchan->ts))
dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH);
return 0;
}
@@ -743,8 +896,52 @@ static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan)
LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n",
gsm_lchan_name(lchan),
gsm_lchans_name(lchan->state));
do_lchan_free(lchan);
/*
* Check Osmocom RSL CHAN ACT style dynamic TCH/F_TCH/H_PDCH TS for pending
* transitions in these cases:
*
* a) after PDCH was released due to switchover request, activate TCH.
* BSC initiated this switchover, so dyn.pchan_is != pchan_want and
* lchan->type has been set to the desired GSM_LCHAN_*.
*
* b) Voice call ended and a TCH is released. If the TS is now unused,
* switch to PDCH. Here still dyn.pchan_is == dyn.pchan_want because
* we're only just notified and may decide to switch to PDCH now.
*/
if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
DEBUGP(DRSL, "%s Rx RSL Channel Release ack for lchan %u\n",
gsm_ts_and_pchan_name(ts), lchan->nr);
/* (a) */
if (ts->dyn.pchan_is != ts->dyn.pchan_want)
return dyn_ts_switchover_continue(ts);
/* (b) */
if (dyn_ts_should_switch_to_pdch(ts))
return dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH);
}
/*
* Put a dynamic TCH/F_PDCH channel back to PDCH mode iff it was
* released successfully. If in error, the PDCH ACT will follow after
* T3111 in error_timeout_cb().
*
* Any state other than LCHAN_S_REL_ERR became LCHAN_S_NONE after above
* do_lchan_free(). Assert this, because that's what ensures a PDCH ACT
* on a TCH/F_PDCH TS in all cases.
*
* If GPRS is disabled, always skip the PDCH ACT.
*/
OSMO_ASSERT(lchan->state == LCHAN_S_NONE
|| lchan->state == LCHAN_S_REL_ERR);
if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE)
return 0;
if (ts->pchan == GSM_PCHAN_TCH_F_PDCH
&& lchan->state == LCHAN_S_NONE)
return rsl_ipacc_pdch_activate(ts, 1);
return 0;
}
@@ -922,13 +1119,18 @@ int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id,
int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *reason)
{
lchan->state = LCHAN_S_BROKEN;
LOGP(DRSL, LOGL_ERROR, "%s %s lchan broken: %s\n",
gsm_lchan_name(lchan), gsm_lchant_name(lchan->type), reason);
rsl_lchan_set_state(lchan, LCHAN_S_BROKEN);
lchan->broken_reason = reason;
return 0;
}
int rsl_lchan_set_state(struct gsm_lchan *lchan, int state)
{
DEBUGP(DRSL, "%s state %s -> %s\n",
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state),
gsm_lchans_name(state));
lchan->state = state;
return 0;
}
@@ -937,34 +1139,38 @@ int rsl_lchan_set_state(struct gsm_lchan *lchan, int state)
static int rsl_rx_chan_act_ack(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
/* BTS has confirmed channel activation, we now need
* to assign the activated channel to the MS */
if (rslh->ie_chan != RSL_IE_CHAN_NR)
return -EINVAL;
osmo_timer_del(&msg->lchan->act_timer);
osmo_timer_del(&lchan->act_timer);
if (msg->lchan->state == LCHAN_S_BROKEN) {
if (lchan->state == LCHAN_S_BROKEN) {
LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel.\n",
gsm_lchan_name(msg->lchan));
gsm_lchan_name(lchan));
return 0;
}
if (msg->lchan->state != LCHAN_S_ACT_REQ)
if (lchan->state != LCHAN_S_ACT_REQ)
LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n",
gsm_lchan_name(msg->lchan),
gsm_lchans_name(msg->lchan->state));
rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE);
gsm_lchan_name(lchan),
gsm_lchans_name(lchan->state));
rsl_lchan_set_state(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;
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
dyn_ts_switchover_complete(lchan);
if (lchan->rqd_ref) {
rsl_send_imm_assignment(lchan);
talloc_free(lchan->rqd_ref);
lchan->rqd_ref = NULL;
lchan->rqd_ta = 0;
}
send_lchan_signal(S_LCHAN_ACTIVATE_ACK, msg->lchan, NULL);
send_lchan_signal(S_LCHAN_ACTIVATE_ACK, lchan, NULL);
return 0;
}
@@ -1029,7 +1235,7 @@ static int rsl_rx_conn_fail(struct msgb *msg)
TLVP_LEN(&tp, RSL_IE_CAUSE));
LOGPC(DRSL, LOGL_NOTICE, "\n");
osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail);
rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL]);
return rsl_rf_chan_release_err(msg->lchan);
}
@@ -1048,7 +1254,7 @@ static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr)
int i;
char *name = "";
if (lchan && lchan->conn && lchan->conn->subscr)
if (lchan && lchan->conn)
name = subscr_name(lchan->conn->subscr);
DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr);
@@ -1087,6 +1293,19 @@ static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr)
}
}
static struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
{
struct gsm_meas_rep *meas_rep;
meas_rep = &lchan->meas_rep[lchan->meas_rep_idx];
memset(meas_rep, 0, sizeof(*meas_rep));
meas_rep->lchan = lchan;
lchan->meas_rep_idx = (lchan->meas_rep_idx + 1)
% ARRAY_SIZE(lchan->meas_rep);
return meas_rep;
}
static int rsl_rx_meas_res(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
@@ -1184,6 +1403,63 @@ static int rsl_rx_hando_det(struct msgb *msg)
return 0;
}
static bool lchan_may_change_pdch(struct gsm_lchan *lchan, bool pdch_act)
{
struct gsm_bts_trx_ts *ts;
OSMO_ASSERT(lchan);
ts = lchan->ts;
OSMO_ASSERT(ts);
OSMO_ASSERT(ts->trx);
OSMO_ASSERT(ts->trx->bts);
if (lchan->ts->pchan != GSM_PCHAN_TCH_F_PDCH) {
LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK"
" for channel that is no TCH/F_PDCH\n",
gsm_lchan_name(lchan),
gsm_pchan_name(ts->pchan),
pdch_act? "ACT" : "DEACT");
return false;
}
if (lchan->state != LCHAN_S_NONE) {
LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK"
" in unexpected state: %s\n",
gsm_lchan_name(lchan),
gsm_pchan_name(ts->pchan),
pdch_act? "ACT" : "DEACT",
gsm_lchans_name(lchan->state));
return false;
}
return true;
}
static int rsl_rx_pdch_act_ack(struct msgb *msg)
{
if (!lchan_may_change_pdch(msg->lchan, true))
return -EINVAL;
msg->lchan->ts->flags |= TS_F_PDCH_ACTIVE;
msg->lchan->ts->flags &= ~TS_F_PDCH_ACT_PENDING;
return 0;
}
static int rsl_rx_pdch_deact_ack(struct msgb *msg)
{
if (!lchan_may_change_pdch(msg->lchan, false))
return -EINVAL;
msg->lchan->ts->flags &= ~TS_F_PDCH_ACTIVE;
msg->lchan->ts->flags &= ~TS_F_PDCH_DEACT_PENDING;
rsl_chan_activate_lchan(msg->lchan, msg->lchan->dyn.act_type,
msg->lchan->dyn.ho_ref);
return 0;
}
static int abis_rsl_rx_dchan(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
@@ -1191,7 +1467,8 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
char *ts_name;
struct e1inp_sign_link *sign_link = msg->dst;
msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr);
msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr,
"Abis RSL rx DCHAN: ");
ts_name = gsm_lchan_name(msg->lchan);
switch (rslh->c.msg_type) {
@@ -1221,15 +1498,15 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name);
break;
case RSL_MT_IPAC_PDCH_ACT_ACK:
DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name);
msg->lchan->ts->flags |= TS_F_PDCH_MODE;
DEBUGP(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name);
rc = rsl_rx_pdch_act_ack(msg);
break;
case RSL_MT_IPAC_PDCH_ACT_NACK:
LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name);
break;
case RSL_MT_IPAC_PDCH_DEACT_ACK:
DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name);
msg->lchan->ts->flags &= ~TS_F_PDCH_MODE;
rc = rsl_rx_pdch_deact_ack(msg);
break;
case RSL_MT_IPAC_PDCH_DEACT_NACK:
LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name);
@@ -1406,7 +1683,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra);
chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci);
osmo_counter_inc(bts->network->stats.chreq.total);
rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL]);
/*
* We want LOCATION UPDATES to succeed and will assign a TCH
@@ -1419,14 +1696,22 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
if (!lchan) {
LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n",
msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra);
osmo_counter_inc(bts->network->stats.chreq.no_channel);
rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL]);
/* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */
if (bts->network->T3122)
rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff);
return 0;
}
if (lchan->state != LCHAN_S_NONE)
/*
* Expecting lchan state to be NONE, except for dyn TS in PDCH mode.
* Those are expected to be ACTIVE: the PDCH release will be sent from
* rsl_chan_activate_lchan() below.
*/
if (lchan->state != LCHAN_S_NONE
&& !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
&& lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
&& lchan->state == LCHAN_S_ACTIVE))
LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel "
"in state %s\n", gsm_lchan_name(lchan),
gsm_lchans_name(lchan->state));
@@ -1439,7 +1724,6 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
return -ENOMEM;
}
rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref));
lchan->rqd_ta = rqd_ta;
@@ -1462,7 +1746,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason),
rqd_ref->ra, rqd_ta);
rsl_chan_activate_lchan(lchan, 0x00, 0);
rsl_chan_activate_lchan(lchan, RSL_ACT_INTRA_IMM_ASS, 0);
return 0;
}
@@ -1545,7 +1829,8 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
int rc = 0;
msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr);
msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr,
"Abis RSL rx CCHAN: ");
switch (rslh->c.msg_type) {
case RSL_MT_CHAN_RQD:
@@ -1595,7 +1880,7 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
if (rlm_cause == RLL_CAUSE_T200_EXPIRED) {
osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err);
rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR]);
return rsl_rf_chan_release_err(msg->lchan);
}
@@ -1646,7 +1931,8 @@ static int abis_rsl_rx_rll(struct msgb *msg)
char *ts_name;
uint8_t sapi = rllh->link_id & 7;
msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr);
msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr,
"Abis RSL rx RLL: ");
ts_name = gsm_lchan_name(msg->lchan);
DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi);
@@ -1694,13 +1980,16 @@ static int abis_rsl_rx_rll(struct msgb *msg)
rsl_handle_release(msg->lchan);
break;
case RSL_MT_ERROR_IND:
DEBUGPC(DRLL, "ERROR INDICATION\n");
rc = rsl_rx_rll_err_ind(msg);
break;
case RSL_MT_UNIT_DATA_IND:
DEBUGPC(DRLL, "UNIT DATA INDICATION\n");
LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message "
"type 0x%02x\n", rllh->c.msg_type);
break;
default:
DEBUGPC(DRLL, "UNKNOWN\n");
LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message "
"type 0x%02x\n", rllh->c.msg_type);
}
@@ -1938,17 +2227,33 @@ int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act)
struct abis_rsl_dchan_hdr *dh;
uint8_t msg_type;
if (act)
if (ts->flags & TS_F_PDCH_PENDING_MASK) {
LOGP(DRSL, LOGL_ERROR,
"%s PDCH %s requested, but a PDCH%s%s is still pending\n",
gsm_ts_name(ts),
act ? "ACT" : "DEACT",
ts->flags & TS_F_PDCH_ACT_PENDING? " ACT" : "",
ts->flags & TS_F_PDCH_DEACT_PENDING? " DEACT" : "");
return -EINVAL;
}
if (act){
/* Callers should heed the GPRS mode. */
OSMO_ASSERT(ts->trx->bts->gprs.mode != BTS_GPRS_NONE);
msg_type = RSL_MT_IPAC_PDCH_ACT;
else
ts->flags |= TS_F_PDCH_ACT_PENDING;
} else {
msg_type = RSL_MT_IPAC_PDCH_DEACT;
ts->flags |= TS_F_PDCH_DEACT_PENDING;
}
/* TODO add timeout to cancel PDCH DE/ACT */
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, msg_type);
dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
dh->chan_nr = gsm_ts2chan_nr(ts, 0);
dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_PDCH, ts->nr, 0);
DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_ts_name(ts),
DEBUGP(DRSL, "%s IPAC PDCH %sACT\n", gsm_ts_name(ts),
act ? "" : "DE");
msg->dst = ts->trx->rsl_link;
@@ -2021,7 +2326,8 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
char *ts_name;
int rc = 0;
msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr);
msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr,
"Abis RSL rx IPACC: ");
ts_name = gsm_lchan_name(msg->lchan);
switch (rllh->c.msg_type) {
@@ -2058,6 +2364,203 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
return rc;
}
int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts,
enum gsm_phys_chan_config to_pchan)
{
int ss;
int rc = -EIO;
OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
DEBUGP(DRSL, "%s starting switchover to %s\n",
gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan));
if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
LOGP(DRSL, LOGL_ERROR,
"%s: Attempt to switch dynamic channel to %s,"
" but is already in switchover.\n",
gsm_ts_and_pchan_name(ts),
gsm_pchan_name(to_pchan));
return ts->dyn.pchan_want == to_pchan? 0 : -EAGAIN;
}
if (ts->dyn.pchan_is == to_pchan) {
LOGP(DRSL, LOGL_INFO,
"%s %s Already is in %s mode, cannot switchover.\n",
gsm_ts_name(ts), gsm_pchan_name(ts->pchan),
gsm_pchan_name(to_pchan));
return -EINVAL;
}
/* Paranoia: let's make sure all is indeed released. */
for (ss = 0; ss < ts_subslots(ts); ss++) {
struct gsm_lchan *lc = &ts->lchan[ss];
if (lc->state != LCHAN_S_NONE) {
LOGP(DRSL, LOGL_ERROR,
"%s Attempt to switch dynamic channel to %s,"
" but is not fully released.\n",
gsm_ts_and_pchan_name(ts),
gsm_pchan_name(to_pchan));
return -EAGAIN;
}
}
/* Record that we're busy switching. */
ts->dyn.pchan_want = to_pchan;
/*
* To switch from PDCH, we need to initiate the release from the BSC
* side. dyn_ts_switchover_continue() will be called from
* rsl_rx_rf_chan_rel_ack(). PDCH is always on lchan[0].
*/
if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
rsl_lchan_set_state(ts->lchan, LCHAN_S_REL_REQ);
rc = rsl_rf_chan_release(ts->lchan, 0, SACCH_NONE);
if (rc) {
LOGP(DRSL, LOGL_ERROR,
"%s RSL RF Chan Release failed\n",
gsm_ts_and_pchan_name(ts));
return dyn_ts_switchover_failed(ts, rc);
}
return 0;
}
/*
* To switch from TCH/F and TCH/H pchans, this has been called from
* rsl_rx_rf_chan_rel_ack(), i.e. release is complete. Go ahead and
* activate as new type. This will always be PDCH.
*/
return dyn_ts_switchover_continue(ts);
}
static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts)
{
int rc;
uint8_t act_type;
uint8_t ho_ref;
int ss;
struct gsm_lchan *lchan;
OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
DEBUGP(DRSL, "%s switchover: release complete,"
" activating new pchan type\n",
gsm_ts_and_pchan_name(ts));
if (ts->dyn.pchan_is == ts->dyn.pchan_want) {
LOGP(DRSL, LOGL_ERROR,
"%s Requested to switchover dynamic channel to the"
" same type it is already in.\n",
gsm_ts_and_pchan_name(ts));
return 0;
}
for (ss = 0; ss < ts_subslots(ts); ss++) {
lchan = &ts->lchan[ss];
if (lchan->rqd_ref) {
LOGP(DRSL, LOGL_ERROR,
"%s During dyn TS switchover, expecting no"
" Request Reference to be pending. Discarding!\n",
gsm_lchan_name(lchan));
talloc_free(lchan->rqd_ref);
lchan->rqd_ref = NULL;
}
}
/*
* When switching pchan modes, all lchans are unused. So always
* activate whatever wants to be activated on the first lchan. (We
* wouldn't remember to use lchan[1] across e.g. a PDCH deact anyway)
*/
lchan = ts->lchan;
/*
* For TCH/x, the lchan->type has been set in lchan_alloc(), but it may
* have been lost during channel release due to dynamic switchover.
*
* For PDCH, the lchan->type will actually remain NONE.
* TODO: set GSM_LCHAN_PDTCH?
*/
switch (ts->dyn.pchan_want) {
case GSM_PCHAN_TCH_F:
lchan->type = GSM_LCHAN_TCH_F;
break;
case GSM_PCHAN_TCH_H:
lchan->type = GSM_LCHAN_TCH_H;
break;
case GSM_PCHAN_PDCH:
lchan->type = GSM_LCHAN_NONE;
break;
default:
LOGP(DRSL, LOGL_ERROR,
"%s Invalid target pchan for dynamic TS\n",
gsm_ts_and_pchan_name(ts));
}
act_type = (ts->dyn.pchan_want == GSM_PCHAN_PDCH)
? RSL_ACT_OSMO_PDCH
: lchan->dyn.act_type;
ho_ref = (ts->dyn.pchan_want == GSM_PCHAN_PDCH)
? 0
: lchan->dyn.ho_ref;
/* Fetch the rqd_ref back from before switchover started. */
lchan->rqd_ref = lchan->dyn.rqd_ref;
lchan->rqd_ta = lchan->dyn.rqd_ta;
lchan->dyn.rqd_ref = NULL;
lchan->dyn.rqd_ta = 0;
/* During switchover, we have received a release ack, which means that
* the act_timer has been stopped. Start the timer again so we mark
* this channel broken if the activation ack comes too late. */
lchan->act_timer.cb = lchan_act_tmr_cb;
lchan->act_timer.data = lchan;
osmo_timer_schedule(&lchan->act_timer, 4, 0);
rc = rsl_chan_activate_lchan(lchan, act_type, ho_ref);
if (rc) {
LOGP(DRSL, LOGL_ERROR,
"%s RSL Chan Activate failed\n",
gsm_ts_and_pchan_name(ts));
return dyn_ts_switchover_failed(ts, rc);
}
return 0;
}
static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc)
{
ts->dyn.pchan_want = ts->dyn.pchan_is;
LOGP(DRSL, LOGL_ERROR, "%s Error %d during dynamic channel switchover."
" Going back to previous pchan.\n", gsm_ts_and_pchan_name(ts),
rc);
return rc;
}
static void dyn_ts_switchover_complete(struct gsm_lchan *lchan)
{
enum gsm_phys_chan_config pchan_act;
enum gsm_phys_chan_config pchan_was;
struct gsm_bts_trx_ts *ts = lchan->ts;
OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
pchan_act = pchan_for_lchant(lchan->type);
/*
* Paranoia: do the types match?
* In case of errors: we've received an act ack already, so what to do
* about it? Logging the error should suffice for now.
*/
if (pchan_act != ts->dyn.pchan_want)
LOGP(DRSL, LOGL_ERROR,
"%s Requested transition does not match lchan type %s\n",
gsm_ts_and_pchan_name(ts),
gsm_lchant_name(lchan->type));
pchan_was = ts->dyn.pchan_is;
ts->dyn.pchan_is = ts->dyn.pchan_want = pchan_act;
if (pchan_was != ts->dyn.pchan_is)
LOGP(DRSL, LOGL_INFO, "%s switchover from %s complete.\n",
gsm_ts_and_pchan_name(ts), gsm_pchan_name(pchan_was));
}
/* Entry-point where L2 RSL from BTS enters */
int abis_rsl_rcvmsg(struct msgb *msg)

View File

@@ -39,7 +39,6 @@
#define GSM0808_T10_VALUE 6, 0
static LLIST_HEAD(sub_connections);
static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind);
static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id);
@@ -147,7 +146,7 @@ static void assignment_t10_timeout(void *_conn)
conn->secondary_lchan = NULL;
/* inform them about the failure */
api = conn->bts->network->bsc_api;
api = conn->network->bsc_api;
api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
}
@@ -158,7 +157,7 @@ static void handle_mr_config(struct gsm_subscriber_connection *conn,
struct gsm_lchan *lchan, int full_rate)
{
struct bsc_api *api;
api = conn->bts->network->bsc_api;
api = conn->network->bsc_api;
struct amr_multirate_conf *mr;
struct gsm48_multi_rate_conf *mr_conf;
@@ -239,24 +238,24 @@ static int handle_new_assignment(struct gsm_subscriber_connection *conn, int cha
return 0;
}
struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan)
struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan)
{
struct gsm_subscriber_connection *conn;
struct gsm_network *net = lchan->ts->trx->bts->network;
conn = talloc_zero(lchan->ts->trx->bts->network, struct gsm_subscriber_connection);
conn = talloc_zero(net, struct gsm_subscriber_connection);
if (!conn)
return NULL;
/* Configure the time and start it so it will be closed */
conn->network = net;
conn->lchan = lchan;
conn->bts = lchan->ts->trx->bts;
lchan->conn = conn;
llist_add_tail(&conn->entry, &sub_connections);
llist_add_tail(&conn->entry, &net->subscr_conns);
return conn;
}
/* TODO: move subscriber put here... */
void subscr_con_free(struct gsm_subscriber_connection *conn)
void bsc_subscr_con_free(struct gsm_subscriber_connection *conn)
{
if (!conn)
return;
@@ -387,7 +386,7 @@ static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int ful
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate)
{
struct bsc_api *api;
api = conn->bts->network->bsc_api;
api = conn->network->bsc_api;
if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) {
if (handle_new_assignment(conn, chan_mode, full_rate) != 0)
@@ -424,7 +423,7 @@ static void handle_ass_compl(struct gsm_subscriber_connection *conn,
struct msgb *msg)
{
struct gsm48_hdr *gh;
struct bsc_api *api = conn->bts->network->bsc_api;
struct bsc_api *api = conn->network->bsc_api;
if (conn->secondary_lchan != msg->lchan) {
LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n");
@@ -461,7 +460,7 @@ static void handle_ass_compl(struct gsm_subscriber_connection *conn,
static void handle_ass_fail(struct gsm_subscriber_connection *conn,
struct msgb *msg)
{
struct bsc_api *api = conn->bts->network->bsc_api;
struct bsc_api *api = conn->network->bsc_api;
uint8_t *rr_failure;
struct gsm48_hdr *gh;
@@ -678,7 +677,7 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
} else {
/* allocate a new connection */
rc = BSC_API_CONN_POL_REJECT;
lchan->conn = subscr_con_allocate(msg->lchan);
lchan->conn = bsc_subscr_con_allocate(msg->lchan);
if (!lchan->conn) {
lchan_release(lchan, 1, RSL_REL_NORMAL);
return -1;
@@ -689,7 +688,7 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
if (rc != BSC_API_CONN_POL_ACCEPT) {
lchan->conn->lchan = NULL;
subscr_con_free(lchan->conn);
bsc_subscr_con_free(lchan->conn);
lchan_release(lchan, 1, RSL_REL_NORMAL);
}
}
@@ -751,7 +750,7 @@ static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id
if (!conn)
return;
api = conn->bts->network->bsc_api;
api = conn->network->bsc_api;
if (!api || !api->sapi_n_reject)
return;
@@ -848,7 +847,7 @@ static void handle_release(struct gsm_subscriber_connection *conn,
gsm0808_clear(conn);
if (destruct)
subscr_con_free(conn);
bsc_subscr_con_free(conn);
}
static void handle_chan_ack(struct gsm_subscriber_connection *conn,
@@ -877,7 +876,3 @@ static __attribute__((constructor)) void on_dso_load_bsc(void)
osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL);
}
struct llist_head *bsc_api_sub_connections(struct gsm_network *net)
{
return &sub_connections;
}

View File

@@ -0,0 +1,77 @@
/* Dynamic PDCH initialisation implementation shared across NM and RSL */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/logging.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/abis_rsl.h>
void tchf_pdch_ts_init(struct gsm_bts_trx_ts *ts)
{
int rc;
if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) {
LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':"
" not activating PDCH.\n",
gsm_ts_and_pchan_name(ts));
return;
}
LOGP(DRSL, LOGL_DEBUG, "%s: trying to PDCH ACT\n",
gsm_ts_and_pchan_name(ts));
rc = rsl_ipacc_pdch_activate(ts, 1);
if (rc != 0)
LOGP(DRSL, LOGL_ERROR, "%s %s: PDCH ACT failed\n",
gsm_ts_name(ts), gsm_pchan_name(ts->pchan));
}
void tchf_tchh_pdch_ts_init(struct gsm_bts_trx_ts *ts)
{
if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) {
LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':"
" not activating PDCH.\n",
gsm_ts_and_pchan_name(ts));
return;
}
dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH);
}
void dyn_ts_init(struct gsm_bts_trx_ts *ts)
{
/* Clear all TCH/F_PDCH flags */
ts->flags &= ~(TS_F_PDCH_PENDING_MASK | TS_F_PDCH_ACTIVE);
/* Clear TCH/F_TCH/H_PDCH state */
ts->dyn.pchan_is = ts->dyn.pchan_want = GSM_PCHAN_NONE;
ts->dyn.pending_chan_activ = NULL;
switch (ts->pchan) {
case GSM_PCHAN_TCH_F_PDCH:
tchf_pdch_ts_init(ts);
break;
case GSM_PCHAN_TCH_F_TCH_H_PDCH:
tchf_tchh_pdch_ts_init(ts);
break;
default:
break;
}
}

View File

@@ -36,6 +36,7 @@
#include <openbsc/ipaccess.h>
#include <osmocom/gsm/sysinfo.h>
#include <openbsc/e1_config.h>
#include <openbsc/osmo_bsc.h>
/* global pointer to the gsm network data structure */
extern struct gsm_network *bsc_gsmnet;
@@ -340,9 +341,9 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal,
LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx);
if (isd->link_type == E1INP_SIGN_OML)
osmo_counter_inc(trx->bts->network->stats.bts.oml_fail);
rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL]);
else if (isd->link_type == E1INP_SIGN_RSL)
osmo_counter_inc(trx->bts->network->stats.bts.rsl_fail);
rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL]);
/*
* free all allocated channels. change the nm_state so the
@@ -458,12 +459,6 @@ static int bootstrap_bts(struct gsm_bts *bts)
return -EINVAL;
}
/* 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;
@@ -476,32 +471,30 @@ static int bootstrap_bts(struct gsm_bts *bts)
return 0;
}
int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *),
const char *config_file)
int bsc_network_alloc(mncc_recv_cb_t mncc_recv)
{
struct telnet_connection dummy_conn;
struct gsm_bts *bts;
int rc;
/* initialize our data structures */
bsc_gsmnet = gsm_network_init(1, 1, mncc_recv);
bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, mncc_recv);
if (!bsc_gsmnet)
return -ENOMEM;
bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC");
bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC");
/* our vty command code expects vty->priv to point to a telnet_connection */
dummy_conn.priv = bsc_gsmnet;
rc = vty_read_config_file(config_file, &dummy_conn);
return 0;
}
int bsc_network_configure(const char *config_file)
{
struct gsm_bts *bts;
int rc;
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
/* start telnet after reading config for vty_get_bind_addr() */
LOGP(DNM, LOGL_NOTICE, "VTY at %s %d\n",
vty_get_bind_addr(), OSMO_VTY_PORT_NITB_BSC);
rc = telnet_init_dynif(tall_bsc_ctx, bsc_gsmnet, vty_get_bind_addr(),
OSMO_VTY_PORT_NITB_BSC);
if (rc < 0)

View File

@@ -18,6 +18,7 @@
*/
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <osmocom/vty/command.h>
@@ -27,7 +28,7 @@
#include <osmocom/vty/stats.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0502.h>
#include <arpa/inet.h>
@@ -53,15 +54,13 @@
#include <openbsc/osmo_msc_data.h>
#include <openbsc/osmo_bsc_rf.h>
#include <openbsc/xsc.h>
#include <inttypes.h>
#include "../../bscconfig.h"
#define NETWORK_STR "Configure the GSM network\n"
#define CODE_CMD_STR "Code commands\n"
#define NAME_CMD_STR "Name Commands\n"
#define NAME_STR "Name to use\n"
#define LCHAN_NR_STR "Logical Channel Number\n"
@@ -106,12 +105,6 @@ const struct value_string bts_loc_fix_names[] = {
{ 0, NULL }
};
struct cmd_node net_node = {
GSMNET_NODE,
"%s(config-net)# ",
1,
};
struct cmd_node bts_node = {
BTS_NODE,
"%s(config-net-bts)# ",
@@ -130,21 +123,6 @@ struct cmd_node ts_node = {
1,
};
extern struct gsm_network *bsc_gsmnet;
struct gsm_network *gsmnet_from_vty(struct vty *v)
{
/* In case we read from the config file, the vty->priv cannot
* point to a struct telnet_connection, and thus conn->priv
* will not point to the gsm_network structure */
#if 0
struct telnet_connection *conn = v->priv;
return (struct gsm_network *) conn->priv;
#else
return bsc_gsmnet;
#endif
}
static int dummy_config_write(struct vty *v)
{
return CMD_SUCCESS;
@@ -189,8 +167,15 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
net->name_long, VTY_NEWLINE);
vty_out(vty, " Short network name: '%s'%s",
net->name_short, VTY_NEWLINE);
vty_out(vty, " Authentication policy: %s%s",
gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
vty_out(vty, " Authentication policy: %s",
gsm_auth_policy_name(net->auth_policy));
if (net->authorized_reg_str)
vty_out(vty, ", authorized regexp: %s", net->authorized_reg_str);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " Auto create subscriber: %s%s",
net->auto_create_subscr ? "yes" : "no", VTY_NEWLINE);
vty_out(vty, " Auto assign extension: %s%s",
net->auto_assign_exten ? "yes" : "no", VTY_NEWLINE);
vty_out(vty, " Location updating reject cause: %u%s",
net->reject_cause, VTY_NEWLINE);
vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption,
@@ -220,7 +205,7 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
VTY_NEWLINE);
}
DEFUN(show_net, show_net_cmd, "show network",
DEFUN(bsc_show_net, bsc_show_net_cmd, "show network",
SHOW_STR "Display information about a GSM NETWORK\n")
{
struct gsm_network *net = gsmnet_from_vty(vty);
@@ -273,6 +258,14 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
VTY_NEWLINE);
if (bts->si_common.rach_control.cell_bar)
vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
vty_out(vty, "Uplink DTX: %s%s",
(bts->dtxu != GSM48_DTX_SHALL_BE_USED) ?
"enabled" : "forced", VTY_NEWLINE);
else
vty_out(vty, "Uplink DTX: not enabled%s", VTY_NEWLINE);
vty_out(vty, "Downlink DTX: %senabled%s", bts->dtxd ? "" : "not ",
VTY_NEWLINE);
vty_out(vty, "Channel Description Attachment: %s%s",
(bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE);
vty_out(vty, "Channel Description BS-PA-MFRMS: %u%s",
@@ -432,10 +425,15 @@ static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts)
if (bts->gprs.mode == BTS_GPRS_NONE)
return;
vty_out(vty, " gprs 11bit_rach_support_for_egprs %u%s",
bts->gprs.supports_egprs_11bit_rach, VTY_NEWLINE);
vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
VTY_NEWLINE);
vty_out(vty, " gprs network-control-order nc%u%s",
bts->gprs.net_ctrl_ord, VTY_NEWLINE);
if (!bts->gprs.ctrl_ack_type_use_block)
vty_out(vty, " gprs control-ack-type-rach%s", VTY_NEWLINE);
vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++)
@@ -549,15 +547,13 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
vty_out(vty, " location_area_code %u%s", bts->location_area_code,
VTY_NEWLINE);
if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
vty_out(vty, " dtx uplink%s%s",
(bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "" : " force",
VTY_NEWLINE);
if (bts->dtxd)
vty_out(vty, " dtx downlink%s", VTY_NEWLINE);
vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE);
if (bts->tz.override != 0) {
if (bts->tz.dst)
vty_out(vty, " timezone %d %d %d%s",
bts->tz.hr, bts->tz.mn, bts->tz.dst, VTY_NEWLINE);
else
vty_out(vty, " timezone %d %d%s",
bts->tz.hr, bts->tz.mn, VTY_NEWLINE);
}
vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE);
vty_out(vty, " cell reselection hysteresis %u%s",
bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
@@ -591,13 +587,6 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
(sp->penalty_time*20)+20, VTY_NEWLINE);
}
/* Is periodic LU enabled or disabled? */
if (bts->si_common.chan_desc.t3212 == 0)
vty_out(vty, " no periodic location update%s", VTY_NEWLINE);
else
vty_out(vty, " periodic location update %u%s",
bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE);
vty_out(vty, " radio-link-timeout %d%s",
get_radio_link_timeout(&bts->si_common.cell_options),
VTY_NEWLINE);
@@ -776,6 +765,8 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
if (gsmnet->authorized_reg_str)
vty_out(vty, " authorized-regexp %s%s", gsmnet->authorized_reg_str, VTY_NEWLINE);
vty_out(vty, " location updating reject cause %u%s",
gsmnet->reject_cause, VTY_NEWLINE);
vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
@@ -809,9 +800,22 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, 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, " subscriber-keep-in-ram %d%s",
gsmnet->subscr_group->keep_subscr, VTY_NEWLINE);
if (gsmnet->tz.override != 0) {
if (gsmnet->tz.dst)
vty_out(vty, " timezone %d %d %d%s",
gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst,
VTY_NEWLINE);
else
vty_out(vty, " timezone %d %d%s",
gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE);
}
if (gsmnet->t3212 == 0)
vty_out(vty, " no periodic location update%s", VTY_NEWLINE);
else
vty_out(vty, " periodic location update %u%s",
gsmnet->t3212 * 6, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -900,7 +904,7 @@ static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
gsm_pchan_name(ts->pchan), gsm_ts_tsc(ts));
if (ts->pchan == GSM_PCHAN_TCH_F_PDCH)
vty_out(vty, " (%s mode)",
ts->flags & TS_F_PDCH_MODE ? "PDCH" : "TCH/F");
ts->flags & TS_F_PDCH_ACTIVE ? "PDCH" : "TCH/F");
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " NM State: ");
net_dump_nmstate(vty, &ts->mo.nm_state);
@@ -1101,10 +1105,13 @@ static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan)
lchan->meas_rep_idx, 1);
mr = &lchan->meas_rep[idx];
vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u, Type %s - "
vty_out(vty, "BTS %u, TRX %u, Timeslot %u %s, Lchan %u, Type %s, State %s - "
"L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s",
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
lchan->nr, gsm_lchant_name(lchan->type), mr->ms_l1.pwr,
gsm_pchan_name(lchan->ts->pchan),
lchan->nr,
gsm_lchant_name(lchan->type), gsm_lchans_name(lchan->state),
mr->ms_l1.pwr,
rxlev2dbm(mr->dl.full.rx_lev),
rxlev2dbm(mr->ul.full.rx_lev),
VTY_NEWLINE);
@@ -1320,116 +1327,6 @@ DEFUN(show_paging_group,
return CMD_SUCCESS;
}
DEFUN(cfg_net,
cfg_net_cmd,
"network", NETWORK_STR)
{
vty->index = gsmnet_from_vty(vty);
vty->node = GSMNET_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_net_ncc,
cfg_net_ncc_cmd,
"network country code <1-999>",
"Set the GSM network country code\n"
"Country commands\n"
CODE_CMD_STR
"Network Country Code to use\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->country_code = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_mnc,
cfg_net_mnc_cmd,
"mobile network code <0-999>",
"Set the GSM mobile network code\n"
"Network Commands\n"
CODE_CMD_STR
"Mobile Network Code to use\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->network_code = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_name_short,
cfg_net_name_short_cmd,
"short name NAME",
"Set the short GSM network name\n" NAME_CMD_STR NAME_STR)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
bsc_replace_string(gsmnet, &gsmnet->name_short, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_name_long,
cfg_net_name_long_cmd,
"long name NAME",
"Set the long GSM network name\n" NAME_CMD_STR NAME_STR)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
bsc_replace_string(gsmnet, &gsmnet->name_long, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_auth_policy,
cfg_net_auth_policy_cmd,
"auth policy (closed|accept-all|token)",
"Authentication (not cryptographic)\n"
"Set the GSM network authentication policy\n"
"Require the MS to be activated in HLR\n"
"Accept all MS, whether in HLR or not\n"
"Use SMS-token based authentication\n")
{
enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]);
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->auth_policy = policy;
return CMD_SUCCESS;
}
DEFUN(cfg_net_reject_cause,
cfg_net_reject_cause_cmd,
"location updating reject cause <2-111>",
"Set the reject cause of location updating reject\n"
"Set the reject cause of location updating reject\n"
"Set the reject cause of location updating reject\n"
"Set the reject cause of location updating reject\n"
"Cause Value as Per GSM TS 04.08\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->reject_cause = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_encryption,
cfg_net_encryption_cmd,
"encryption a5 (0|1|2|3)",
"Encryption options\n"
"A5 encryption\n" "A5/0: No encryption\n"
"A5/1: Encryption\n" "A5/2: Export-grade Encryption\n"
"A5/3: 'New' Secure Encryption\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->a5_encryption= atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_neci,
cfg_net_neci_cmd,
"neci (0|1)",
@@ -1443,35 +1340,6 @@ DEFUN(cfg_net_neci,
return CMD_SUCCESS;
}
DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd,
"rrlp mode (none|ms-based|ms-preferred|ass-preferred)",
"Radio Resource Location Protocol\n"
"Set the Radio Resource Location Protocol Mode\n"
"Don't send RRLP request\n"
"Request MS-based location\n"
"Request any location, prefer MS-based\n"
"Request any location, prefer MS-assisted\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd,
"mm info (0|1)",
"Mobility Management\n"
"Send MM INFO after LOC UPD ACCEPT\n"
"Disable\n" "Enable\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->send_mm_info = atoi(argv[0]);
return CMD_SUCCESS;
}
#define HANDOVER_STR "Handover Options\n"
DEFUN(cfg_net_handover, cfg_net_handover_cmd,
@@ -1613,26 +1481,14 @@ DECLARE_TIMER(3119, "Currently not used.\n")
DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n")
DECLARE_TIMER(3141, "Currently not used.\n")
DEFUN(cfg_net_dtx,
cfg_net_dtx_cmd,
"dtx-used (0|1)",
"Enable the usage of DTX.\n"
"DTX is disabled\n" "DTX is enabled\n")
DEFUN_DEPRECATED(cfg_net_dtx,
cfg_net_dtx_cmd,
"dtx-used (0|1)",
".HIDDEN\n""Obsolete\n""Obsolete\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->dtx_enabled = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_net_subscr_keep,
cfg_net_subscr_keep_cmd,
"subscriber-keep-in-ram (0|1)",
"Keep unused subscribers in RAM.\n"
"Delete unused subscribers\n" "Keep unused subscribers\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->subscr_group->keep_subscr = atoi(argv[0]);
return CMD_SUCCESS;
vty_out(vty, "%% 'dtx-used' is now deprecated: use dtx * "
"configuration options of BTS instead%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
/* per-BTS configuration */
@@ -1704,6 +1560,58 @@ DEFUN(cfg_bts_band,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_dtxu, cfg_bts_dtxu_cmd, "dtx uplink [force]",
"Configure discontinuous transmission\n"
"Enable Uplink DTX for this BTS\n"
"MS 'shall' use DTXu instead of 'may' use (might not be supported by "
"older phones).\n")
{
struct gsm_bts *bts = vty->index;
bts->dtxu = (argc > 0) ? GSM48_DTX_SHALL_BE_USED : GSM48_DTX_MAY_BE_USED;
if (!is_ipaccess_bts(bts))
vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
"neither supported nor tested!%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_dtxu, cfg_bts_no_dtxu_cmd, "no dtx uplink",
NO_STR
"Configure discontinuous transmission\n"
"Disable Uplink DTX for this BTS\n")
{
struct gsm_bts *bts = vty->index;
bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_dtxd, cfg_bts_dtxd_cmd, "dtx downlink",
"Configure discontinuous transmission\n"
"Enable Downlink DTX for this BTS\n")
{
struct gsm_bts *bts = vty->index;
bts->dtxd = true;
if (!is_ipaccess_bts(bts))
vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
"neither supported nor tested!%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_dtxd, cfg_bts_no_dtxd_cmd, "no dtx downlink",
NO_STR
"Configure discontinuous transmission\n"
"Disable Downlink DTX for this BTS\n")
{
struct gsm_bts *bts = vty->index;
bts->dtxd = false;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_ci,
cfg_bts_ci_cmd,
"cell_identity <0-65535>",
@@ -1776,67 +1684,6 @@ DEFUN(cfg_bts_bsic,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_timezone,
cfg_bts_timezone_cmd,
"timezone <-19-19> (0|15|30|45)",
"Set the Timezone Offset of this BTS\n"
"Timezone offset (hours)\n"
"Timezone offset (00 minutes)\n"
"Timezone offset (15 minutes)\n"
"Timezone offset (30 minutes)\n"
"Timezone offset (45 minutes)\n"
)
{
struct gsm_bts *bts = vty->index;
int tzhr = atoi(argv[0]);
int tzmn = atoi(argv[1]);
bts->tz.hr = tzhr;
bts->tz.mn = tzmn;
bts->tz.dst = 0;
bts->tz.override = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_timezone_dst,
cfg_bts_timezone_dst_cmd,
"timezone <-19-19> (0|15|30|45) <0-2>",
"Set the Timezone Offset of this BTS\n"
"Timezone offset (hours)\n"
"Timezone offset (00 minutes)\n"
"Timezone offset (15 minutes)\n"
"Timezone offset (30 minutes)\n"
"Timezone offset (45 minutes)\n"
"DST offset (hours)\n"
)
{
struct gsm_bts *bts = vty->index;
int tzhr = atoi(argv[0]);
int tzmn = atoi(argv[1]);
int tzdst = atoi(argv[2]);
bts->tz.hr = tzhr;
bts->tz.mn = tzmn;
bts->tz.dst = tzdst;
bts->tz.override = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_timezone,
cfg_bts_no_timezone_cmd,
"no timezone",
NO_STR
"Disable BTS specific timezone\n")
{
struct gsm_bts *bts = vty->index;
bts->tz.override = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_unit_id,
cfg_bts_unit_id_cmd,
"ip.access unit_id <0-65534> <0-255>",
@@ -2588,6 +2435,40 @@ DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_gprs_ctrl_ack, cfg_bts_gprs_ctrl_ack_cmd,
"gprs control-ack-type-rach", GPRS_TEXT
"Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
"four access bursts format instead of default RLC/MAC control block\n")
{
struct gsm_bts *bts = vty->index;
if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
bts->gprs.ctrl_ack_type_use_block = false;
return CMD_SUCCESS;
}
DEFUN(cfg_no_bts_gprs_ctrl_ack, cfg_no_bts_gprs_ctrl_ack_cmd,
"no gprs control-ack-type-rach", NO_STR GPRS_TEXT
"Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
"four access bursts format instead of default RLC/MAC control block\n")
{
struct gsm_bts *bts = vty->index;
if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
bts->gprs.ctrl_ack_type_use_block = true;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd,
"gprs network-control-order (nc0|nc1|nc2)",
GPRS_TEXT
@@ -2630,6 +2511,32 @@ DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_gprs_11bit_rach_support_for_egprs,
cfg_bts_gprs_11bit_rach_support_for_egprs_cmd,
"gprs 11bit_rach_support_for_egprs (0|1)",
GPRS_TEXT "11 bit RACH options\n"
"Disable 11 bit RACH for EGPRS\n"
"Enable 11 bit RACH for EGPRS")
{
struct gsm_bts *bts = vty->index;
bts->gprs.supports_egprs_11bit_rach = atoi(argv[0]);
if (bts->gprs.supports_egprs_11bit_rach > 1) {
vty_out(vty, "Error in RACH type%s", VTY_NEWLINE);
return CMD_WARNING;
}
if ((bts->gprs.mode == BTS_GPRS_NONE) &&
(bts->gprs.supports_egprs_11bit_rach == 1)) {
vty_out(vty, "Error:gprs mode is none and 11bit rach is"
" enabled%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
#define SI_TEXT "System Information Messages\n"
#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)"
#define SI_TYPE_HELP "System Information Type 1\n" \
@@ -3661,18 +3568,22 @@ DEFUN(cfg_ts_e1_subslot,
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net)
{
vty_out(vty, "Channel Requests : %lu total, %lu no channel%s",
osmo_counter_get(net->stats.chreq.total),
osmo_counter_get(net->stats.chreq.no_channel), VTY_NEWLINE);
net->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL].current,
net->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL].current,
VTY_NEWLINE);
vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s",
osmo_counter_get(net->stats.chan.rf_fail),
osmo_counter_get(net->stats.chan.rll_err), VTY_NEWLINE);
net->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL].current,
net->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR].current,
VTY_NEWLINE);
vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s",
osmo_counter_get(net->stats.paging.attempted),
osmo_counter_get(net->stats.paging.completed),
osmo_counter_get(net->stats.paging.expired), VTY_NEWLINE);
net->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED].current,
net->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED].current,
net->bsc_ctrs->ctr[BSC_CTR_PAGING_EXPIRED].current,
VTY_NEWLINE);
vty_out(vty, "BTS failures : %lu OML, %lu RSL%s",
osmo_counter_get(net->stats.bts.oml_fail),
osmo_counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE);
net->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL].current,
net->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL].current,
VTY_NEWLINE);
}
DEFUN(drop_bts,
@@ -3776,7 +3687,7 @@ DEFUN(smscb_cmd, smscb_cmd_cmd,
uint8_t buf[88];
int rc;
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;
@@ -3827,7 +3738,7 @@ DEFUN(pdch_act, pdch_act_cmd,
int ts_nr = atoi(argv[2]);
int activate;
bts = gsm_bts_num(bsc_gsmnet, bts_nr);
bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
if (!bts) {
vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;
@@ -3864,9 +3775,8 @@ DEFUN(pdch_act, pdch_act_cmd,
}
extern int bsc_vty_init_extra(void);
extern const char *openbsc_copyright;
int bsc_vty_init(const struct log_info *cat)
int bsc_vty_init(const struct log_info *cat, struct gsm_network *network)
{
cfg_ts_pchan_cmd.string =
vty_cmd_string_from_valstr(tall_bsc_ctx,
@@ -3890,8 +3800,9 @@ int bsc_vty_init(const struct log_info *cat)
"BTS Vendor/Type\n",
"\n", "", 0);
xsc_vty_init(network, config_write_net);
install_element_ve(&show_net_cmd);
install_element_ve(&bsc_show_net_cmd);
install_element_ve(&show_bts_cmd);
install_element_ve(&show_trx_cmd);
install_element_ve(&show_ts_cmd);
@@ -3902,21 +3813,8 @@ int bsc_vty_init(const struct log_info *cat)
install_element_ve(&show_paging_group_cmd);
logging_vty_add_cmds(cat);
osmo_stats_vty_add_cmds();
install_element(CONFIG_NODE, &cfg_net_cmd);
install_node(&net_node, config_write_net);
vty_install_default(GSMNET_NODE);
install_element(GSMNET_NODE, &cfg_net_ncc_cmd);
install_element(GSMNET_NODE, &cfg_net_mnc_cmd);
install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
install_element(GSMNET_NODE, &cfg_net_neci_cmd);
install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd);
install_element(GSMNET_NODE, &cfg_net_mm_info_cmd);
install_element(GSMNET_NODE, &cfg_net_handover_cmd);
install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd);
install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd);
@@ -3937,7 +3835,6 @@ int bsc_vty_init(const struct log_info *cat)
install_element(GSMNET_NODE, &cfg_net_T3122_cmd);
install_element(GSMNET_NODE, &cfg_net_T3141_cmd);
install_element(GSMNET_NODE, &cfg_net_dtx_cmd);
install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd);
install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd);
install_element(GSMNET_NODE, &cfg_bts_cmd);
@@ -3948,14 +3845,15 @@ int bsc_vty_init(const struct log_info *cat)
install_element(BTS_NODE, &cfg_no_description_cmd);
install_element(BTS_NODE, &cfg_bts_band_cmd);
install_element(BTS_NODE, &cfg_bts_ci_cmd);
install_element(BTS_NODE, &cfg_bts_dtxu_cmd);
install_element(BTS_NODE, &cfg_bts_dtxd_cmd);
install_element(BTS_NODE, &cfg_bts_no_dtxu_cmd);
install_element(BTS_NODE, &cfg_bts_no_dtxd_cmd);
install_element(BTS_NODE, &cfg_bts_lac_cmd);
install_element(BTS_NODE, &cfg_bts_tsc_cmd);
install_element(BTS_NODE, &cfg_bts_bsic_cmd);
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd);
install_element(BTS_NODE, &cfg_bts_timezone_cmd);
install_element(BTS_NODE, &cfg_bts_timezone_dst_cmd);
install_element(BTS_NODE, &cfg_bts_no_timezone_cmd);
install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd);
install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd);
install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd);
@@ -3986,9 +3884,12 @@ int bsc_vty_init(const struct log_info *cat)
install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd);
install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_11bit_rach_support_for_egprs_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_ctrl_ack_cmd);
install_element(BTS_NODE, &cfg_no_bts_gprs_ctrl_ack_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd);

View File

@@ -369,6 +369,9 @@ static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd)
abis_nm_opstart(trx->bts, obj_class,
trx->bts->bts_nr, trx->nr, ts->nr);
}
if (new_state->operational == NM_OPSTATE_ENABLED
&& new_state->availability == NM_AVSTATE_OK)
dyn_ts_init(ts);
break;
case NM_OC_RADIO_CARRIER:
trx = obj;

View File

@@ -400,15 +400,9 @@ static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts)
if (is_ipaccess_bts(ts->trx->bts))
return;
switch (ts->pchan) {
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_H:
if (ts_is_tch(ts))
abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts,
e1l->e1_ts_ss);
break;
default:
break;
}
}
static void nm_reconfig_trx(struct gsm_bts_trx *trx)

View File

@@ -43,6 +43,20 @@ static int ts_is_usable(struct gsm_bts_trx_ts *ts)
return 0;
}
/* If a TCH/F_PDCH TS is busy changing, it is already taken or not
* yet available. */
if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) {
if (ts->flags & TS_F_PDCH_PENDING_MASK)
return 0;
}
/* If a dynamic channel is busy changing, it is already taken or not
* yet available. */
if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
if (ts->dyn.pchan_is != ts->dyn.pchan_want)
return 0;
}
return 1;
}
@@ -58,43 +72,90 @@ int trx_is_usable(struct gsm_bts_trx *trx)
return 1;
}
static const uint8_t subslots_per_pchan[] = {
[GSM_PCHAN_NONE] = 0,
[GSM_PCHAN_CCCH] = 0,
[GSM_PCHAN_CCCH_SDCCH4] = 4,
[GSM_PCHAN_TCH_F] = 1,
[GSM_PCHAN_TCH_H] = 2,
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
/* FIXME: what about dynamic TCH_F_TCH_H ? */
[GSM_PCHAN_TCH_F_PDCH] = 1,
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
};
static struct gsm_lchan *
_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan,
enum gsm_phys_chan_config dyn_as_pchan)
{
struct gsm_bts_trx_ts *ts;
int j, ss;
int j, start, stop, dir, ss;
int check_subslots;
if (!trx_is_usable(trx))
return NULL;
for (j = 0; j < 8; j++) {
if (trx->bts->chan_alloc_reverse) {
/* check TS 7..0 */
start = 7;
stop = -1;
dir = -1;
} else {
/* check TS 0..7 */
start = 0;
stop = 8;
dir = 1;
}
for (j = start; j != stop; j += dir) {
ts = &trx->ts[j];
if (!ts_is_usable(ts))
continue;
/* ip.access dynamic TCH/F + PDCH combination */
if (ts->pchan == GSM_PCHAN_TCH_F_PDCH &&
pchan == GSM_PCHAN_TCH_F) {
/* we can only consider such a dynamic channel
* if the PDCH is currently inactive */
if (ts->flags & TS_F_PDCH_MODE)
continue;
} else if (ts->pchan != pchan)
if (ts->pchan != pchan)
continue;
/* check if all sub-slots are allocated yet */
for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) {
/*
* Allocation for fully dynamic timeslots
* (does not apply for ip.access style GSM_PCHAN_TCH_F_PDCH)
*
* Note the special nature of a dynamic timeslot in PDCH mode:
* in PDCH mode, typically, lchan->type is GSM_LCHAN_NONE and
* lchan->state is LCHAN_S_NONE -- an otherwise unused slot
* becomes PDCH implicitly. In the same sense, this channel
* allocator will never be asked to find an available PDCH
* slot; only TCH/F or TCH/H will be requested, and PDCH mode
* means that it is available for switchover.
*
* A dynamic timeslot in PDCH mode may be switched to TCH/F or
* TCH/H. If a dyn TS is already in TCH/F or TCH/H mode, it
* means that it is in use and its mode can't be switched.
*
* The logic concerning channels for TCH/F is trivial: there is
* only one channel, so a dynamic TS in TCH/F mode is already
* taken and not available for allocation. For TCH/H, we need
* to check whether a dynamic timeslot is already in TCH/H mode
* and whether one of the two channels is still available.
*/
if (pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
/* The TS's mode is being switched. Not
* available anymore/yet. */
DEBUGP(DRLL, "%s already in switchover\n",
gsm_ts_and_pchan_name(ts));
continue;
}
if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
/* This slot is available. Still check for
* error states to be sure; in all cases the
* first lchan will be used. */
if (ts->lchan->state != LCHAN_S_NONE
&& ts->lchan->state != LCHAN_S_ACTIVE)
continue;
return ts->lchan;
}
if (ts->dyn.pchan_is == dyn_as_pchan) {
/* The requested type matches the dynamic
* timeslot's current mode. A channel may still
* be available (think TCH/H). */
check_subslots = ts_subslots(ts);
} else
/* Otherwise this slot is not applicable. */
continue;
} else {
/* Not a dynamic channel, there is only one pchan kind: */
check_subslots = ts_subslots(ts);
}
/* Is a sub-slot still available? */
for (ss = 0; ss < check_subslots; ss++) {
struct gsm_lchan *lc = &ts->lchan[ss];
if (lc->type == GSM_LCHAN_NONE &&
lc->state == LCHAN_S_NONE)
@@ -106,20 +167,21 @@ _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
}
static struct gsm_lchan *
_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
_lc_dyn_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan,
enum gsm_phys_chan_config dyn_as_pchan)
{
struct gsm_bts_trx *trx;
struct gsm_lchan *lc;
if (bts->chan_alloc_reverse) {
llist_for_each_entry_reverse(trx, &bts->trx_list, list) {
lc = _lc_find_trx(trx, pchan);
lc = _lc_find_trx(trx, pchan, dyn_as_pchan);
if (lc)
return lc;
}
} else {
llist_for_each_entry(trx, &bts->trx_list, list) {
lc = _lc_find_trx(trx, pchan);
lc = _lc_find_trx(trx, pchan, dyn_as_pchan);
if (lc)
return lc;
}
@@ -128,7 +190,21 @@ _lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
return NULL;
}
/* Allocate a logical channel */
static struct gsm_lchan *
_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
{
return _lc_dyn_find_bts(bts, pchan, GSM_PCHAN_NONE);
}
/* Allocate a logical channel.
*
* Dynamic channel types: we always prefer a dedicated TS, and only pick +
* switch a dynamic TS if no pure TS of the requested PCHAN is available.
*
* TCH_F/PDCH: if we pick a PDCH ACT style dynamic TS as TCH/F channel, PDCH
* will be disabled in rsl_chan_activate_lchan(); there is no need to check
* whether PDCH mode is currently active, here.
*/
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
int allow_bigger)
{
@@ -161,13 +237,35 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
if (allow_bigger) {
if (lchan == NULL) {
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H);
type = GSM_LCHAN_TCH_H;
if (lchan)
type = GSM_LCHAN_TCH_H;
}
if (lchan == NULL) {
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
type = GSM_LCHAN_TCH_F;
if (lchan)
type = GSM_LCHAN_TCH_F;
}
/* try dynamic TCH/F_PDCH */
if (lchan == NULL) {
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH);
/* TCH/F_PDCH will be used as TCH/F */
if (lchan)
type = GSM_LCHAN_TCH_F;
}
/* try fully dynamic TCH/F_TCH/H_PDCH */
if (lchan == NULL) {
lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH,
GSM_PCHAN_TCH_H);
if (lchan)
type = GSM_LCHAN_TCH_H;
}
/*
* No need to check fully dynamic channels for TCH/F:
* if no TCH/H was available, neither will be TCH/F.
*/
}
break;
case GSM_LCHAN_TCH_F:
@@ -175,7 +273,33 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
/* If we don't have TCH/F available, fall-back to TCH/H */
if (!lchan) {
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H);
type = GSM_LCHAN_TCH_H;
if (lchan)
type = GSM_LCHAN_TCH_H;
}
/* If we don't have TCH/H either, try dynamic TCH/F_PDCH */
if (!lchan) {
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH);
/* TCH/F_PDCH used as TCH/F -- here, type is already
* set to GSM_LCHAN_TCH_F, but for clarity's sake... */
if (lchan)
type = GSM_LCHAN_TCH_F;
}
/* Try fully dynamic TCH/F_TCH/H_PDCH as TCH/F... */
if (!lchan && bts->network->dyn_ts_allow_tch_f) {
lchan = _lc_dyn_find_bts(bts,
GSM_PCHAN_TCH_F_TCH_H_PDCH,
GSM_PCHAN_TCH_F);
if (lchan)
type = GSM_LCHAN_TCH_F;
}
/* ...and as TCH/H. */
if (!lchan) {
lchan = _lc_dyn_find_bts(bts,
GSM_PCHAN_TCH_F_TCH_H_PDCH,
GSM_PCHAN_TCH_H);
if (lchan)
type = GSM_LCHAN_TCH_H;
}
break;
case GSM_LCHAN_TCH_H:
@@ -183,7 +307,27 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
/* If we don't have TCH/H available, fall-back to TCH/F */
if (!lchan) {
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
type = GSM_LCHAN_TCH_F;
if (lchan)
type = GSM_LCHAN_TCH_F;
}
/* No dedicated TCH/x available -- try fully dynamic
* TCH/F_TCH/H_PDCH */
if (!lchan) {
lchan = _lc_dyn_find_bts(bts,
GSM_PCHAN_TCH_F_TCH_H_PDCH,
GSM_PCHAN_TCH_H);
if (lchan)
type = GSM_LCHAN_TCH_H;
}
/*
* No need to check TCH/F_TCH/H_PDCH channels for TCH/F:
* if no TCH/H was available, neither will be TCH/F.
*/
/* If we don't have TCH/F either, try dynamic TCH/F_PDCH */
if (!lchan) {
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH);
if (lchan)
type = GSM_LCHAN_TCH_F;
}
break;
default:
@@ -193,6 +337,10 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
if (lchan) {
lchan->type = type;
LOGP(DRLL, LOGL_INFO, "%s Allocating lchan=%u as %s\n",
gsm_ts_and_pchan_name(lchan->ts),
lchan->nr, gsm_lchant_name(lchan->type));
/* clear sapis */
memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis));
@@ -202,6 +350,10 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
lchan->broken_reason = "";
} else {
struct challoc_signal_data sig;
LOGP(DRLL, LOGL_ERROR, "Failed to allocate %s channel\n",
gsm_lchant_name(type));
sig.bts = bts;
sig.type = type;
osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig);
@@ -329,39 +481,6 @@ int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mo
return 1;
}
static struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) {
struct gsm_bts_trx *trx;
int ts_no, lchan_no;
llist_for_each_entry(trx, &bts->trx_list, list) {
for (ts_no = 0; ts_no < 8; ++ts_no) {
for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) {
struct gsm_lchan *lchan =
&trx->ts[ts_no].lchan[lchan_no];
if (lchan->conn && subscr == lchan->conn->subscr)
return lchan;
}
}
}
return NULL;
}
struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr)
{
struct gsm_bts *bts;
struct gsm_network *net = subscr->group->net;
struct gsm_lchan *lchan;
llist_for_each_entry(bts, &net->bts_list, list) {
lchan = lchan_find(bts, subscr);
if (lchan)
return lchan->conn;
}
return NULL;
}
void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
{
struct gsm_bts_trx *trx;
@@ -378,12 +497,14 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
struct gsm_bts_trx_ts *ts = &trx->ts[i];
struct load_counter *pl = &cl->pchan[ts->pchan];
int j;
int subslots;
/* skip administratively deactivated timeslots */
if (!nm_is_running(&ts->mo.nm_state))
continue;
for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) {
subslots = ts_subslots(ts);
for (j = 0; j < subslots; j++) {
struct gsm_lchan *lchan = &ts->lchan[j];
pl->total++;

View File

@@ -37,7 +37,7 @@
#define SAPI_OML 62
#define SAPI_RSL 0 /* 63 ? */
/* The e1_reconfig_*() functions below tale the configuration present in the
/* The e1_reconfig_*() functions below take the configuration present in the
* bts/trx/ts data structures and ensure the E1 configuration reflects the
* timeslot/subslot/TEI configuration */
@@ -63,15 +63,10 @@ int e1_reconfig_ts(struct gsm_bts_trx_ts *ts)
return -ENOMEM;
}
switch (ts->pchan) {
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_H:
if (ts_is_tch(ts)) {
e1_ts = &line->ts[e1_link->e1_ts-1];
e1inp_ts_config_trau(e1_ts, line, subch_cb);
subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss);
break;
default:
break;
}
return 0;

View File

@@ -258,30 +258,7 @@ int send_siemens_mrpci(struct gsm_lchan *lchan,
return rsl_siemens_mrpci(lchan, &mrpci);
}
int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type)
{
/* Check the size for the classmark */
if (length < 1 + *classmark2_lv)
return -1;
uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1;
if (length < 2 + *classmark2_lv + mi_lv[0])
return -2;
*mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv);
}
int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length,
char *mi_string, uint8_t *mi_type)
{
static const uint32_t classmark_offset =
offsetof(struct gsm48_pag_resp, classmark2);
uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2;
return gsm48_extract_mi(classmark2_lv, length - classmark_offset,
mi_string, mi_type);
}
/* TODO MSCSPLIT remove gsm48_handle_paging_resp() */
int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn,
struct msgb *msg, struct gsm_subscriber *subscr)
{
@@ -304,7 +281,7 @@ int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn,
subscr = conn->subscr;
}
osmo_counter_inc(bts->network->stats.paging.completed);
rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED]);
/* Stop paging on the bts we received the paging response */
paging_request_stop(conn->bts, subscr, conn, msg);
@@ -634,39 +611,6 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
return 0;
}
struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value)
{
struct msgb *msg;
struct gsm48_hdr *gh;
msg = gsm48_msgb_alloc_name("GSM 04.08 SERV REJ");
if (!msg)
return NULL;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_CM_SERV_REJ;
gh->data[0] = value;
return msg;
}
struct msgb *gsm48_create_loc_upd_rej(uint8_t cause)
{
struct gsm48_hdr *gh;
struct msgb *msg;
msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD REJ");
if (!msg)
return NULL;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
gh->data[0] = cause;
return msg;
}
/* 9.2.5 CM service accept */
int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn)
{

View File

@@ -0,0 +1,40 @@
/* OpenBSC utility functions for 3GPP TS 04.80 */
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/gsm_04_80.h>
#include <openbsc/bsc_api.h>
int bsc_gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int
level, const char *text)
{
struct msgb *msg = gsm0480_gen_ussdNotify(level, text);
if (!msg)
return -1;
return gsm0808_submit_dtap(conn, msg, 0, 0);
}
int bsc_gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn)
{
struct msgb *msg = gsm0480_gen_releaseComplete();
if (!msg)
return -1;
return gsm0808_submit_dtap(conn, msg, 0, 0);
}

View File

@@ -33,6 +33,27 @@
#include <openbsc/handover.h>
#include <osmocom/gsm/gsm_utils.h>
/* Get reference to a neighbor cell on a given BCCH ARFCN */
static struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
uint16_t arfcn, uint8_t bsic)
{
struct gsm_bts *neigh;
/* FIXME: use some better heuristics here to determine which cell
* using this ARFCN really is closest to the target cell. For
* now we simply assume that each ARFCN will only be used by one
* cell */
llist_for_each_entry(neigh, &bts->network->bts_list, list) {
/* FIXME: this is probably returning the same bts again!? */
if (neigh->c0->arfcn == arfcn &&
neigh->bsic == bsic)
return neigh;
}
return NULL;
}
/* issue handover to a cell identified by ARFCN and BSIC */
static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
uint16_t arfcn, uint8_t bsic)
@@ -228,6 +249,7 @@ static int attempt_handover(struct gsm_meas_rep *mr)
static int process_meas_rep(struct gsm_meas_rep *mr)
{
struct gsm_network *net = mr->lchan->ts->trx->bts->network;
enum meas_rep_field dlev, dqual;
int av_rxlev;
/* we currently only do handover for TCH channels */
@@ -239,22 +261,28 @@ static int process_meas_rep(struct gsm_meas_rep *mr)
return 0;
}
if (mr->flags & MEAS_REP_F_DL_DTX) {
dlev = MEAS_REP_DL_RXLEV_SUB;
dqual = MEAS_REP_DL_RXQUAL_SUB;
} else {
dlev = MEAS_REP_DL_RXLEV_FULL;
dqual = MEAS_REP_DL_RXQUAL_FULL;
}
/* parse actual neighbor cell info */
if (mr->num_cell > 0 && mr->num_cell < 7)
process_meas_neigh(mr);
av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL,
av_rxlev = get_meas_rep_avg(mr->lchan, dlev,
net->handover.win_rxlev_avg);
/* Interference HO */
if (rxlev2dbm(av_rxlev) > -85 &&
meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL,
3, 4, 5))
meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5))
return attempt_handover(mr);
/* Bad Quality */
if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL,
3, 4, 5))
if (meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5))
return attempt_handover(mr);
/* Low Level */

View File

@@ -85,9 +85,9 @@ static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
return NULL;
}
/* Hand over the specified logical channel to the specified new BTS.
* This is the main entry point for the actual handover algorithm,
* after it has decided it wants to initiate HO to a specific BTS */
/*! \brief Hand over the specified logical channel to the specified new BTS.
* This is the main entry point for the actual handover algorithm, after the
* decision whether to initiate HO to a specific BTS. */
int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
{
struct gsm_lchan *new_lchan;
@@ -103,7 +103,7 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
old_lchan->ts->trx->bts->nr, bts->nr);
osmo_counter_inc(bts->network->stats.handover.attempted);
rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]);
if (!old_lchan->conn) {
LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n");
@@ -113,7 +113,7 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
new_lchan = lchan_alloc(bts, old_lchan->type, 0);
if (!new_lchan) {
LOGP(DHO, LOGL_NOTICE, "No free channel\n");
osmo_counter_inc(bts->network->stats.handover.no_channel);
rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]);
return -ENOSPC;
}
@@ -188,7 +188,7 @@ static void ho_T3103_cb(void *_ho)
struct gsm_network *net = ho->new_lchan->ts->trx->bts->network;
DEBUGP(DHO, "HO T3103 expired\n");
osmo_counter_inc(net->stats.handover.timeout);
rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]);
ho->new_lchan->conn->ho_lchan = NULL;
ho->new_lchan->conn = NULL;
@@ -265,7 +265,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
osmo_counter_inc(net->stats.handover.completed);
rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED]);
osmo_timer_del(&ho->T3103);
@@ -284,7 +284,6 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
new_lchan->conn->lchan = new_lchan;
ho->old_lchan->conn = NULL;
rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE);
lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END);
handover_free(ho);
@@ -304,7 +303,7 @@ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
return -ENODEV;
}
osmo_counter_inc(net->stats.handover.failed);
rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]);
new_lchan = ho->new_lchan;

View File

@@ -17,18 +17,18 @@
*
*/
#include <openbsc/gsm_data.h>
#include <openbsc/xsc.h>
#include <openbsc/osmo_bsc.h>
#include <openbsc/osmo_msc_data.h>
#include <openbsc/gsm_subscriber.h>
struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code,
int (*mncc_recv)(struct gsm_network *, struct msgb *))
struct gsm_network *bsc_network_init(void *ctx,
uint16_t country_code,
uint16_t network_code,
mncc_recv_cb_t mncc_recv)
{
struct gsm_network *net;
net = talloc_zero(tall_bsc_ctx, struct gsm_network);
if (!net)
return NULL;
net = gsm_network_init(ctx, country_code, network_code, mncc_recv);
net->bsc_data = talloc_zero(net, struct osmo_bsc_data);
if (!net->bsc_data) {
@@ -36,22 +36,11 @@ struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_cod
return NULL;
}
net->subscr_group = talloc_zero(net, struct gsm_subscriber_group);
if (!net->subscr_group) {
talloc_free(net);
return NULL;
}
/* Init back pointer */
net->bsc_data->auto_off_timeout = -1;
net->bsc_data->network = net;
INIT_LLIST_HEAD(&net->bsc_data->mscs);
net->subscr_group->net = net;
net->create_subscriber = 1;
net->country_code = country_code;
net->network_code = network_code;
net->num_bts = 0;
net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
net->T3101 = GSM_T3101_DEFAULT;
@@ -68,42 +57,10 @@ struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_cod
net->handover.pwr_hysteresis = 3;
net->handover.max_distance = 9999;
INIT_LLIST_HEAD(&net->trans_list);
INIT_LLIST_HEAD(&net->upqueue);
INIT_LLIST_HEAD(&net->bts_list);
net->stats.chreq.total = osmo_counter_alloc("net.chreq.total");
net->stats.chreq.no_channel = osmo_counter_alloc("net.chreq.no_channel");
net->stats.handover.attempted = osmo_counter_alloc("net.handover.attempted");
net->stats.handover.no_channel = osmo_counter_alloc("net.handover.no_channel");
net->stats.handover.timeout = osmo_counter_alloc("net.handover.timeout");
net->stats.handover.completed = osmo_counter_alloc("net.handover.completed");
net->stats.handover.failed = osmo_counter_alloc("net.handover.failed");
net->stats.loc_upd_type.attach = osmo_counter_alloc("net.loc_upd_type.attach");
net->stats.loc_upd_type.normal = osmo_counter_alloc("net.loc_upd_type.normal");
net->stats.loc_upd_type.periodic = osmo_counter_alloc("net.loc_upd_type.periodic");
net->stats.loc_upd_type.detach = osmo_counter_alloc("net.imsi_detach.count");
net->stats.loc_upd_resp.reject = osmo_counter_alloc("net.loc_upd_resp.reject");
net->stats.loc_upd_resp.accept = osmo_counter_alloc("net.loc_upd_resp.accept");
net->stats.paging.attempted = osmo_counter_alloc("net.paging.attempted");
net->stats.paging.detached = osmo_counter_alloc("net.paging.detached");
net->stats.paging.completed = osmo_counter_alloc("net.paging.completed");
net->stats.paging.expired = osmo_counter_alloc("net.paging.expired");
net->stats.sms.submitted = osmo_counter_alloc("net.sms.submitted");
net->stats.sms.no_receiver = osmo_counter_alloc("net.sms.no_receiver");
net->stats.sms.delivered = osmo_counter_alloc("net.sms.delivered");
net->stats.sms.rp_err_mem = osmo_counter_alloc("net.sms.rp_err_mem");
net->stats.sms.rp_err_other = osmo_counter_alloc("net.sms.rp_err_other");
net->stats.call.mo_setup = osmo_counter_alloc("net.call.mo_setup");
net->stats.call.mo_connect_ack = osmo_counter_alloc("net.call.mo_connect_ack");
net->stats.call.mt_setup = osmo_counter_alloc("net.call.mt_setup");
net->stats.call.mt_connect = osmo_counter_alloc("net.call.mt_connect");
net->stats.chan.rf_fail = osmo_counter_alloc("net.chan.rf_fail");
net->stats.chan.rll_err = osmo_counter_alloc("net.chan.rll_err");
net->stats.bts.oml_fail = osmo_counter_alloc("net.bts.oml_fail");
net->stats.bts.rsl_fail = osmo_counter_alloc("net.bts.rsl_fail");
net->mncc_recv = mncc_recv;
/* init statistics */
net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0);
gsm_net_update_ctype(net);

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